-
Notifications
You must be signed in to change notification settings - Fork 50
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
feat: add Arbitrum forwarder contracts #610
Conversation
address _l3SpokePool, | ||
address _crossDomainAdmin | ||
) | ||
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, CircleDomainIds.UNINITIALIZED) |
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 is speculation on how CCTP would work on L3s. We may just want to remove this on the forwarders.
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 see a reason it would work differently on L3s, so it seems reasonable just to leave this here as uninitialized and use it if/when we need it.
Maybe we've talked about this before, but is there a reason we shouldn't allow the domain id to be passed in rather than hardcoded, so it can be enabled without changing the code?
Signed-off-by: bennett <[email protected]>
There are some refactors I need to do to hopefully cut down a lot on the code duplication, but I think this is the general idea. |
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! A few comments
address _l3SpokePool, | ||
address _crossDomainAdmin | ||
) | ||
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, CircleDomainIds.UNINITIALIZED) |
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 see a reason it would work differently on L3s, so it seems reasonable just to leave this here as uninitialized and use it if/when we need it.
Maybe we've talked about this before, but is there a reason we shouldn't allow the domain id to be passed in rather than hardcoded, so it can be enabled without changing the code?
*/ | ||
|
||
// solhint-disable-next-line contract-name-camelcase | ||
contract Arbitrum_CustomGasToken_L2_Forwarder is ArbitrumForwarderInterface, CircleCCTPAdapter { |
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.
Is the code in this contract identical to the custom gas arbitrum adapter?
Thoughts on sharing the code via a library or inheritance?
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.
There was a big refactor here ea2b6ca which should share code via inheritance.
*/ | ||
|
||
// solhint-disable-next-line contract-name-camelcase | ||
contract Arbitrum_L2_Forwarder is ArbitrumForwarderInterface, CircleCCTPAdapter { |
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.
Same question here, but for the regular arbitrum adapter
*/ | ||
|
||
// solhint-disable-next-line contract-name-camelcase | ||
contract Ovm_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_L2_Forwarder { |
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.
+1, great job on keeping these super simple
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
@@ -7,7 +7,8 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |||
import "../external/interfaces/CCTPInterfaces.sol"; | |||
import "../libraries/CircleCCTPAdapter.sol"; | |||
import { ArbitrumInboxLike as ArbitrumL1InboxLike, ArbitrumL1ERC20GatewayLike } from "../interfaces/ArbitrumBridgeInterfaces.sol"; |
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 change was only just introduced back in this (not yer merged) PR: https://github.com/across-protocol/contracts/pull/609/files#diff-9986d5b9b57b18f73b5c629869f6d8a6cfdbfa978964dd76de7f4f37d4b5d561
Is it worth backporting the change to that PR, or is the intermediate step necessary?
IERC20(l1Token).safeIncreaseAllowance(erc20Gateway, amount); | ||
|
||
// `outboundTransfer` expects that the caller includes a bytes message as the last param that includes the | ||
// maxSubmissionCost to use when creating an L2 retryable ticket: https://github.com/OffchainLabs/arbitrum/blob/e98d14873dd77513b569771f47b5e05b72402c5e/packages/arb-bridge-peripherals/contracts/tokenbridge/ethereum/gateway/L1GatewayRouter.sol#L232 |
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 seems like a dead link now. I think the correct reference for that contract is: https://github.com/OffchainLabs/arbitrum-classic/blob/551a39b381dcea81e03e7599fcb01fddff4fe96c/packages/arb-bridge-peripherals/contracts/tokenbridge/ethereum/gateway/L1GatewayRouter.sol#L240
We are however referencing their legacy contracts though. They seem to be maintaining compatibility moving forward but I wonder if we should try to reference the current implementations instead.
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.
Nice catch 05dfc6e
function _applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { | ||
// Allows overflows as explained above. | ||
unchecked { | ||
l2Address = address(uint160(l1Address) + uint160(0x1111000000000000000000000000000000001111)); | ||
} | ||
} |
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 have this in the SpokePool as well. Should we factor it out to an "Arbitrum Utils" library for inlining at build time?
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.
Same comment as paul. If we add it as an internal library function
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.
Refactored: 05dfc6e
*/ | ||
|
||
// solhint-disable-next-line contract-name-camelcase | ||
contract Ovm_L2_Forwarder is Arbitrum_L2_Forwarder { |
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 keep getting lost in these naming conventions (i.e. L2 forwarder, l3 adapter, ...) and it gets a bit tricker when chain names are overlaid like this. Is there a concrete naming scheme to be followed? wdyt about building a lexicon that explains how to interpret the names?
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 we should stick with the Adapter{base} naming scheme:
- chainId makes it clear which chain the contract should be deployed on, or L1 if omitted
- Adapter already is intuitively understood by us as something that sends message to a higher level
- Base contracts are meant to be inherited
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 really like the inheritance tree of this PR but I think the names are clunky. Can we think of a better naming scheme? There are too many underscores for one.
Signed-off-by: bennett <[email protected]>
_l1Usdc, | ||
_cctpTokenMessenger, | ||
_circleDomainId, | ||
_l2MaxSubmissionCost, |
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.
Note for future work: anytime you make changes to existing contract code where its understood that you're refactoring, you should leave a comment saying something like "I changed this code here"
import { Arbitrum_CustomGasToken_AdapterBase, FunderInterface } from "./Arbitrum_CustomGasToken_AdapterBase.sol"; | ||
|
||
/** | ||
* @notice Contract containing logic to send messages from Arbitrum to an AVM L3. |
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.
In all contracts, its helpful to note which layer this contract is supposed to be deployed on. In this case, this contract is supposed to be deployed on L2, right?
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.
Yes, it is L2: 05dfc6e
*/ | ||
|
||
// solhint-disable-next-line contract-name-camelcase | ||
contract Arbitrum_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_AdapterBase, ForwarderBase { |
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.
alternative naming option:
- Arbitrum_CustomGasToken_L2Adapter
Makes it more clear this behaves like an Adapter, but its on L1. Also I think the forwarder logic maybe is implied if this extends ForwarderBase
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.
|
||
/** | ||
* @notice Constructs new L2 Forwarder. | ||
* @dev We normally cannot define a constructor for proxies, but this is an exception since all |
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 would note here in a @dev note that the cross domain admin will be sit upon initilization of the ForwarderBase, which should be the 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.
/** | ||
* @notice Contract containing logic to send messages from Arbitrum to an AVM L3. | ||
* @dev This contract is very similar to Arbitrum_CustomGasToken_Adapter. It is meant to bridge | ||
* tokens and send messages over a bridge which uses a custom gas token, except this contract assumes |
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 would also note that this contract is designed to be controlled by a cross domain admin which should be the 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.
address, | ||
uint256 amount | ||
) external payable override { | ||
_relayTokens(l2Token, address(0), amount, l3SpokePool); |
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.
you should comment why address(0) can be hardcoded 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.
*/ | ||
|
||
// solhint-disable-next-line contract-name-camelcase | ||
contract Arbitrum_L2_Forwarder is ForwarderBase, Arbitrum_AdapterBase { |
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.
alternative name could be Arbitrum_L2Adapter
for similar reasons to 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.
*/ | ||
|
||
// solhint-disable-next-line contract-name-camelcase | ||
contract Ovm_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_L2_Forwarder { |
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.
alternative name would be Ovm_CustomGasToken_L2_Adapter
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.
* @notice Contract containing logic to send messages from an OVM L2 to an AVM L3. | ||
* @dev This contract is very similar to Arbitrum_CustomGasToken_Adapter. It is meant to bridge | ||
* tokens and send messages over a bridge which uses a custom gas token, except this contract makes | ||
* the assumption that it is deployed on an OpStack L2. |
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.
Can you explain a bit more how this differs from Arbitrum_L2 equivalent? Specifically comment on how the onlyFromCrossDomainAdmin-esque function has changed
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.
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Signed-off-by: bennett <[email protected]>
Closing in favor of #625 since we no longer wish to inherit specific adapter code, but instead delegatecall external contracts which contain bridging logic. |
This contains L2 forwarder contracts based on the interface #609, under the assumption that the L2 on which it is deployed is Arbitrum. It mostly follows the same format as the Arbitrum_CustomGasToken_Adapter and Arbitrum_Adapter contracts.