Skip to content

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

Merged
merged 36 commits into from
Oct 7, 2024
Merged

feat: L3 withdrawal helpers #605

merged 36 commits into from
Oct 7, 2024

Conversation

bmzig
Copy link
Contributor

@bmzig bmzig commented Sep 13, 2024

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.

@bmzig bmzig changed the title [WIP] refactor: export bridging logic to l2 adapter contracts feat: add an L2 token retriever contract which calls exported spoke pool functions Sep 17, 2024
Copy link
Contributor

@mrice32 mrice32 left a 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);
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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

@bmzig bmzig marked this pull request as ready for review September 20, 2024 16:53
@bmzig bmzig changed the base branch from master to bz/ArbitrumForwarders September 23, 2024 13:50
@bmzig bmzig changed the base branch from bz/ArbitrumForwarders to bz/l2ForwarderInterface September 23, 2024 14:57
@bmzig bmzig marked this pull request as ready for review October 2, 2024 18:29
@bmzig
Copy link
Contributor Author

bmzig commented Oct 2, 2024

WithdrawalAdapter name is TBD

Copy link
Member

@nicholaspai nicholaspai left a 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;
Copy link
Member

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

Copy link
Contributor Author

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 {
Copy link
Member

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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

@nicholaspai
Copy link
Member

WithdrawalAdapter name is TBD

Next time don't open PR for review until names are ready to be merged, otherwise just leave in draft mode

Copy link
Contributor

@pxrl pxrl left a 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.

@bmzig bmzig changed the title feat: add an L2 token retriever contract which calls exported spoke pool functions feat: L3 withdrawal helpers Oct 3, 2024
Copy link
Contributor

@pxrl pxrl left a 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
Copy link
Contributor

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.

Copy link
Contributor Author

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 {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@bmzig bmzig requested review from mrice32 and pxrl October 4, 2024 13:20
Signed-off-by: bennett <[email protected]>
@bmzig bmzig requested a review from pxrl October 4, 2024 20:42
Signed-off-by: bennett <[email protected]>
Copy link
Contributor

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@bmzig bmzig merged commit abec4e5 into master Oct 7, 2024
9 checks passed
@bmzig bmzig deleted the bz/withdrawalAdapter branch October 7, 2024 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants