-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Quark v2 in QuarkBuilder #83
Conversation
// We need to set Bob as the withdrawer because only he has an account on chain 8453 | ||
cometWithdraw_({ | ||
chainId: 8453, | ||
comet: cometUsdc_(8453), | ||
assetSymbol: "LINK", | ||
amount: 5e18, | ||
withdrawer: address(0xb0b) | ||
}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example of the nonce bug in action. Previously, we were withdrawing using 0xa11ce
as the withdrawer on chain 8453
, even though a nonce for 0xa11ce
wasn't provided for chain 8453. Now, we have to set the withdrawer as 0xb0b
since only he has a nonce on chain 8453.
@@ -182,16 +181,17 @@ library Accounts { | |||
return findAssetPositions(assetAddress, chainAccounts.assetPositionsList); | |||
} | |||
|
|||
function findQuarkState(address account, Accounts.QuarkState[] memory quarkStates) | |||
function findQuarkSecret(address account, Accounts.QuarkSecret[] memory quarkSecrets) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to do it this way? Wouldn't just "next random" work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Synced offline on this. I don't think we resolved this discussion yet, but here's my response:
I considered specifying nonceSecrets
on a non-account basis, but I decided to keep it account-based for a few reasons.
- It conforms to the current interface that the client is passing in.
- It reduces the complexity of the QuarkBuilder having to pick out
nonceSecrets
and keep track of which ones are already assigned to which operations. - It's kind of nice that this account-based approach guarantees there will always be enough
nonceSecrets
to construct the quark operations. It'll be one less thing that could go wrong to worry about in the QuarkBuilder
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBF, if we needed to do two operations on the same chain, this strategy wouldn't work? E.g. let's say we wanted to make two delayed operations on the same chain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have two operations on the same chain for the same account, we wrap those in a multicall operation. So having a single nonceSecret
for an account on each chain is guaranteed to be sufficient.
EDIT: I see your point. If there are two operations that have to be separate, then this wouldn't work. Good point. I can work on the "next random" approach in a follow-up PR to see how complicated it is
src/builder/Actions.sol
Outdated
@@ -58,6 +58,9 @@ library Actions { | |||
uint256 constant SWAP_EXPIRY_BUFFER = 3 days; | |||
uint256 constant TRANSFER_EXPIRY_BUFFER = 7 days; | |||
|
|||
/* replay counts */ | |||
uint256 constant RECURRING_SWAP_REPLAY_COUNT = 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to think 500 is pretty high. We should just discuss as a product question generally
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be easy to change this when we want to.
src/builder/Actions.sol
Outdated
// operations, the `nonce` will be the `nonceSecret` (the hash chain has a length of 1) | ||
bytes32 nonceSecret; | ||
// The number of times an operation can be replayed. For non-replayable operations, this will be 0 | ||
uint256 replayCount; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the backend, we've been more keen to use "totalPlays" which tends to map a little better since it doesn't have a discontinuity at 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted to totalPlays
to be consistent. Both are fine imo.
@@ -245,6 +256,11 @@ library Actions { | |||
address paymentToken; | |||
string paymentTokenSymbol; | |||
uint256 paymentMaxCost; | |||
// The secret used to generate the hash chain for a replayable operation. For non-replayable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we return nonce
and nonceSecret
? Generally we've been on "verify" mode on the client (as opposed to "generate"). That is, we run a check that hash_n(input.nonceSecret, input.totalPlays) == input.nonce
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The actual nonce
is on the QuarkOperation
, so I don't think we need to put it here.
@@ -1487,6 +1544,18 @@ library Actions { | |||
return result; | |||
} | |||
|
|||
function generateNonceFromSecret(bytes32 secret, uint256 replayCount) internal pure returns (bytes32) { | |||
assembly ("memory-safe") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool-zone
pragma solidity ^0.8.27; | ||
|
||
library ReplayableHelper { | ||
function generateNonceFromSecret(bytes32 secret, uint256 replayCount) internal pure returns (bytes32) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason not to use the optimized version above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mainly just because we don't need the optimized version here to save us from stack too deep. Also I thought it's kinda nice to have a more readable version so we can sanity check the optimized version in our tests with a different implementation.
@@ -182,16 +181,17 @@ library Accounts { | |||
return findAssetPositions(assetAddress, chainAccounts.assetPositionsList); | |||
} | |||
|
|||
function findQuarkState(address account, Accounts.QuarkState[] memory quarkStates) | |||
function findQuarkSecret(address account, Accounts.QuarkSecret[] memory quarkSecrets) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBF, if we needed to do two operations on the same chain, this strategy wouldn't work? E.g. let's say we wanted to make two delayed operations on the same chain.
Adds `nonceSecret` and `replayCount` to QuarkBuilder. For replayable operations, the QuarkBuilder will also generate the nonce using the `nonceSecret` and `replayCount` (see `generateNonceFromSecret` function). We also uncover and fix a bug where `0` is being used as the `nonce` for an operation when a nonce is not provided for an account by the client. Now, the code reverts if a `nonceSecret` is not given for an account. We buff up the unit tests to assert the right values are being used for the nonces, since nonces were being checked previously.
Adds `nonceSecret` and `replayCount` to QuarkBuilder. For replayable operations, the QuarkBuilder will also generate the nonce using the `nonceSecret` and `replayCount` (see `generateNonceFromSecret` function). We also uncover and fix a bug where `0` is being used as the `nonce` for an operation when a nonce is not provided for an account by the client. Now, the code reverts if a `nonceSecret` is not given for an account. We buff up the unit tests to assert the right values are being used for the nonces, since nonces were being checked previously.
Adds
nonceSecret
andreplayCount
to QuarkBuilder. For replayable operations, the QuarkBuilder will also generate the nonce using thenonceSecret
andreplayCount
(seegenerateNonceFromSecret
function).We also uncover and fix a bug where
0
is being used as thenonce
for an operation when a nonce is not provided for an account by the client. Now, the code reverts if anonceSecret
is not given for an account. We buff up the unit tests to assert the right values are being used for the nonces, since nonces were being checked previously.