-
Notifications
You must be signed in to change notification settings - Fork 68
feat: L3 withdrawal helpers #605
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
Conversation
…ract Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
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.
Looks great -- just a few comments
} else { | ||
// Check that the Ethereum counterpart of the L2 token is stored on this contract. | ||
// Tokens will only be bridged if they are whitelisted by the spoke pool. | ||
address ethereumTokenToBridge = spokePool.whitelistedTokens(l2TokenAddress); |
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 don't love the dependency on the spoke pool, especially since we may end up wanting the spoke pool to call into this contract.
But I can't really think of a way to get this list elsewhere without adding a lot of complexity or additional args that wouldn't apply on non-arbitrum chains... maybe we could add a bytes arg that can be used to supply extra context when needed, but that makes the interface sort of convoluted.
Any ideas?
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.
@mrice32 The update for the Arbitrum withdrawal adapter seems quite clean, but the issue remains in the Optimism one. It seems tricky to avoid in the short term without introducing ownership to additional contracts, which adds complexity.
Do you think it could make sense for the adapter to have a public function that allows it to import state from the SpokePool? This is a bit clunky and a little bit more complex, but would at least avoid the adapter calling into the SpokePool as part of a withdrawl.
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 public function would be on the spoke pool? Is that different than what's happening now?
Maybe I'm missing the point.
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.
We use a predeploy for l2Eth and the wrapped native token is immutable in the spoke pool, but I did make it so that we query l1Gas every time in case that does end up changing in the future: 4db2572
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
|
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.
Couple first pass comments
* token mapping is correct so that the bridge call itself can succeed. | ||
*/ | ||
abstract contract WithdrawalAdapterBase is CircleCCTPAdapter, MultiCaller { | ||
using SafeERC20 for IERC20; |
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.
Prefer naming this contract WithdrawHelper or L3WithdrawHelper as we've typically used Adapter to contain code that gets delegate called and otherwise acts as a library
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.
* which has a special SNX bridge (and thus this adapter will NOT work for Optimism). | ||
* @custom:security-contact [email protected] | ||
*/ | ||
contract Ovm_WithdrawalAdapter is WithdrawalAdapterBase { |
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 this be upgradeable? If not then is the process for upgrading this and then updating the withdrawTo address in the L3 spoke pool easy?
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.
After discussing w/ @bmzig:
- We're probably OK w/o upgradability here. To change this implementation we'd need to deploy a new instance and call
setHubPool()
on the L3 Spoke, which should be fine. - We are probably lacking a way to rescue any tokens from this contract in case of an implementation problem in
withdrawToken()
. We probably want to support an L1 -> L2 message initiated by the HubPool that instructs this contract to withdraw tokens to an L2 address. We could use this to withdraw tokens to the relevant SpokePool on the same chain and evacuate the tokens via there.
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.
This, along with a basic unit test, was added here da1bc9a
Next time don't open PR for review until names are ready to be merged, otherwise just leave in draft mode |
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.
One tiny comment but otherwise no blockers from me.
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.
One fairly minor comment, otherwise looking good.
@@ -35,10 +44,28 @@ abstract contract WithdrawalAdapterBase is CircleCCTPAdapter, MultiCaller { | |||
ITokenMessenger _cctpTokenMessenger, | |||
uint32 _destinationCircleDomainId, | |||
address _l2TokenGateway, | |||
address _tokenRecipient | |||
address _tokenRecipient, | |||
address _hubPool |
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 think this needs a corresponding param update in the natspec above. Also on the naming, I think the SpokePool contracts typically use crossDomainAdmin
to specify the owner/admin and hubPool
to specify the recipient on mainnet for withdrawals, so this seems a bit inconsistent with that in the sense that hubPool
has permission to execute admin functions here.
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.
Good catch. I added the natspec to reflect the new parameter. 2056784. Also, I agree with renaming hubPool
, so I just changed the functions to match the same ones as the spoke pool (i.e. use crossDomainAdmin
).
address l2Token, | ||
address to, | ||
uint256 amount | ||
) external onlyHubPool { |
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'm unsure, but does external
make sense when this would be called from another contract via a delegatecall from this contract? It's a little bit inception-y so I'm not immediately sure. @bmzig do you know?
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 just ran a quick test to verify that we can delegatecall these, and it looks to be alright.
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
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.
LGTM!
There will be shared logic between L2 token retrievers which need to bridge funds from L3 back to L1 and L2 spoke pool logic. We may be able to factor this out for each chain and create another adapter-like contract which can be
delegatecall
-ed by both L2 token retriever and spoke pool contracts.