Skip to content

Commit

Permalink
refactor and rename
Browse files Browse the repository at this point in the history
Signed-off-by: bennett <[email protected]>
  • Loading branch information
bmzig committed Sep 19, 2024
1 parent cea0337 commit 0de9303
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 514 deletions.
150 changes: 150 additions & 0 deletions contracts/chain-adapters/ArbitrumForwarderBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { ArbitrumERC20Bridge, ArbitrumInboxLike, ArbitrumERC20GatewayLike } from "../interfaces/ArbitrumBridgeInterfaces.sol";

// solhint-disable-next-line contract-name-camelcase
abstract contract ArbitrumForwarderBase {
// Amount of gas token allocated to pay for the base submission fee. The base submission fee is a parameter unique to
// retryable transactions; the user is charged the base submission fee to cover the storage costs of keeping their
// ticket’s calldata in the retry buffer. (current base submission fee is queryable via
// ArbRetryableTx.getSubmissionPrice). ArbRetryableTicket precompile interface exists at L2 address
// 0x000000000000000000000000000000000000006E.
// @dev This is immutable because we don't know what precision the custom gas token has.
uint256 public immutable L3_MAX_SUBMISSION_COST;

Check warning on line 14 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Variable name must be in mixedCase

// L3 Gas price bid for immediate L3 execution attempt (queryable via standard eth*gasPrice RPC)
uint256 public immutable L3_GAS_PRICE; // The standard is 5 gWei

Check warning on line 17 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Variable name must be in mixedCase

// Native token expected to be sent in L3 message. Should be 0 for all use cases of this constant, which
// includes sending messages from L2 to L3 and sending Custom gas token ERC20's, which won't be the native token
// on the L3 by definition.
uint256 public constant L3_CALL_VALUE = 0;

// Gas limit for L3 execution of a cross chain token transfer sent via the inbox.
uint32 public constant RELAY_TOKENS_L3_GAS_LIMIT = 300_000;
// Gas limit for L3 execution of a message sent via the inbox.
uint32 public constant RELAY_MESSAGE_L3_GAS_LIMIT = 2_000_000;

// This address on L3 receives extra gas token that is left over after relaying a message via the inbox.
address public immutable L3_REFUND_L3_ADDRESS;

Check warning on line 30 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Variable name must be in mixedCase

// This is the address which receives messages and tokens on L3, assumed to be the spoke pool.
address public immutable L3_SPOKE_POOL;

Check warning on line 33 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Variable name must be in mixedCase

// This is the address which has permission to relay root bundles/messages to the L3 spoke pool.
address public immutable CROSS_DOMAIN_ADMIN;

Check warning on line 36 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Variable name must be in mixedCase

// Inbox system contract to send messages to Arbitrum-like L3s. Token bridges use this to send tokens to L3.
// https://github.com/OffchainLabs/nitro-contracts/blob/f7894d3a6d4035ba60f51a7f1334f0f2d4f02dce/src/bridge/Inbox.sol
ArbitrumInboxLike public immutable L2_INBOX;

Check warning on line 40 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Variable name must be in mixedCase

// Router contract to send tokens to Arbitrum. Routes to correct gateway to bridge tokens. Internally this
// contract calls the Inbox.
// Generic gateway: https://github.com/OffchainLabs/token-bridge-contracts/blob/main/contracts/tokenbridge/ethereum/gateway/L1ArbitrumGateway.sol
// Gateway used for communicating with chains that use custom gas tokens:
// https://github.com/OffchainLabs/token-bridge-contracts/blob/main/contracts/tokenbridge/ethereum/gateway/L1ERC20Gateway.sol
ArbitrumERC20GatewayLike public immutable L2_ERC20_GATEWAY_ROUTER;

Check warning on line 47 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Variable name must be in mixedCase

event TokensForwarded(address indexed l2Token, uint256 amount);
event MessageForwarded(address indexed target, bytes message);

error RescueFailed();

/*
* @dev All functions with this modifier must revert if msg.sender != CROSS_DOMAIN_ADMIN, but each L2 may have
* unique aliasing logic, so it is up to the forwarder contract to verify that the sender is valid.
*/
modifier onlyAdmin() {
_requireAdminSender();
_;
}

/**
* @notice Constructs new Adapter.
* @param _l2ArbitrumInbox Inbox helper contract to send messages to Arbitrum-like L3s.
* @param _l2ERC20GatewayRouter ERC20 gateway router contract to send tokens to Arbitrum-like L3s.
* @param _l3RefundL3Address L3 address to receive gas refunds on after a message is relayed.
* @param _l3MaxSubmissionCost Amount of gas token allocated to pay for the base submission fee. The base
* submission fee is a parameter unique to Arbitrum retryable transactions. This value is hardcoded
* and used for all messages sent by this adapter.
* @param _l3SpokePool L3 address of the contract which will receive messages and tokens which are temporarily
* stored in this contract on L2.
* @param _crossDomainAdmin L1 address of the contract which can send root bundles/messages to this forwarder contract.
* In practice, this is the hub pool.
*/
constructor(
ArbitrumInboxLike _l2ArbitrumInbox,
ArbitrumERC20GatewayLike _l2ERC20GatewayRouter,
address _l3RefundL3Address,
uint256 _l3MaxSubmissionCost,
uint256 _l3GasPrice,
address _l3SpokePool,
address _crossDomainAdmin
) {
L2_INBOX = _l2ArbitrumInbox;
L2_ERC20_GATEWAY_ROUTER = _l2ERC20GatewayRouter;
L3_REFUND_L3_ADDRESS = _l3RefundL3Address;
L3_MAX_SUBMISSION_COST = _l3MaxSubmissionCost;
L3_GAS_PRICE = _l3GasPrice;
L3_SPOKE_POOL = _l3SpokePool;
CROSS_DOMAIN_ADMIN = _crossDomainAdmin;
}

// Added so that this function may receive ETH in the event of stuck transactions.
receive() external payable {}

Check warning on line 95 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Code contains empty blocks

/**
* @notice When called by the cross domain admin (i.e. the hub pool), the msg.data should be some function
* recognizable by the L3 spoke pool, such as "relayRootBundle" or "upgradeTo". Therefore, we simply forward
* this message to the L3 spoke pool using the implemented messaging logic of the L2 forwarder
*/
fallback() external payable onlyAdmin {
_relayMessage(L3_SPOKE_POOL, msg.data);
}

/**
* @notice This function can only be called via a rescue adapter. It is used to recover potentially stuck
* funds on this contract.
*/
function adminCall(
address target,
uint256 value,
bytes memory message
) external onlyAdmin {
(bool success, ) = target.call{ value: value }(message);

Check warning on line 115 in contracts/chain-adapters/ArbitrumForwarderBase.sol

View workflow job for this annotation

GitHub Actions / Lint (20)

Avoid to use low level calls
if (!success) revert RescueFailed();
}

/**
* @notice Bridge tokens to an Arbitrum-like L3.
* @notice This contract must hold at least getL2CallValue() amount of ETH or custom gas token
* to send a message via the Inbox successfully, or the message will get stuck.
* @notice relayTokens should only send tokens to L3_SPOKE_POOL, so no access control is required.
* @param l2Token L2 token to deposit.
* @param amount Amount of L2 tokens to deposit and L3 tokens to receive.
*/
function relayTokens(address l2Token, uint256 amount) external payable virtual;

/**
* @notice Relay a message to a contract on L2. Implementation changes on whether the
* target bridge supports a custom gas token or not.
* @notice This contract must hold at least getL2CallValue() amount of the custom gas token
* to send a message via the Inbox successfully, or the message will get stuck.
* @notice This function should be implmented differently based on whether the L2-L3 bridge
* requires custom gas tokens to fund cross-chain transactions.
*/
function _relayMessage(address target, bytes memory message) internal virtual;

// Function to be overridden to accomodate for each L2's unique method of address aliasing.
function _requireAdminSender() internal virtual;

/**
* @notice Returns required amount of gas token to send a message via the Inbox.
* @param l3GasLimit L3 gas limit for the message.
* @return amount of gas token that this contract needs to hold in order for relayMessage to succeed.
*/
function getL2CallValue(uint32 l3GasLimit) public view returns (uint256) {
return L3_MAX_SUBMISSION_COST + L3_GAS_PRICE * l3GasLimit;
}
}
125 changes: 1 addition & 124 deletions contracts/chain-adapters/Arbitrum_Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,130 +7,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../external/interfaces/CCTPInterfaces.sol";
import "../libraries/CircleCCTPAdapter.sol";

/**
* @notice Interface for Arbitrum's L1 Inbox contract used to send messages to Arbitrum.
* @custom:security-contact [email protected]
*/
interface ArbitrumL1InboxLike {
/**
* @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
* @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
* @dev Caller must set msg.value equal to at least `maxSubmissionCost + maxGas * gasPriceBid`.
* all msg.value will deposited to callValueRefundAddress on L2
* @dev More details can be found here: https://developer.arbitrum.io/arbos/l1-to-l2-messaging
* @param to destination L2 contract address
* @param l2CallValue call value for retryable L2 message
* @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
* @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
* @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
* @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param data ABI encoded data of L2 message
* @return unique message number of the retryable transaction
*/
function createRetryableTicket(
address to,
uint256 l2CallValue,
uint256 maxSubmissionCost,
address excessFeeRefundAddress,
address callValueRefundAddress,
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable returns (uint256);

/**
* @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
* @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed
* funds come from the deposit alone, rather than falling back on the user's L2 balance
* @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress).
* createRetryableTicket method is the recommended standard.
* @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
* @param to destination L2 contract address
* @param l2CallValue call value for retryable L2 message
* @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
* @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
* @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
* @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param data ABI encoded data of L2 message
* @return unique message number of the retryable transaction
*/
function unsafeCreateRetryableTicket(
address to,
uint256 l2CallValue,
uint256 maxSubmissionCost,
address excessFeeRefundAddress,
address callValueRefundAddress,
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable returns (uint256);
}

/**
* @notice Layer 1 Gateway contract for bridging standard ERC20s to Arbitrum.
*/
interface ArbitrumL1ERC20GatewayLike {
/**
* @notice Deprecated in favor of outboundTransferCustomRefund but still used in custom bridges
* like the DAI bridge.
* @dev Refunded to aliased L2 address of sender if sender has code on L1, otherwise to to sender's EOA on L2.
* @param _l1Token L1 address of ERC20
* @param _to Account to be credited with the tokens in the L2 (can be the user's L2 account or a contract),
* not subject to L2 aliasing. This account, or its L2 alias if it have code in L1, will also be able to
* cancel the retryable ticket and receive callvalue refund
* @param _amount Token Amount
* @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
* @param _gasPriceBid Gas price for L2 execution
* @param _data encoded data from router and user
* @return res abi encoded inbox sequence number
*/
function outboundTransfer(
address _l1Token,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) external payable returns (bytes memory);

/**
* @notice Deposit ERC20 token from Ethereum into Arbitrum.
* @dev L2 address alias will not be applied to the following types of addresses on L1:
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* @param _l1Token L1 address of ERC20
* @param _refundTo Account, or its L2 alias if it have code in L1, to be credited with excess gas refund in L2
* @param _to Account to be credited with the tokens in the L2 (can be the user's L2 account or a contract),
* not subject to L2 aliasing. This account, or its L2 alias if it have code in L1, will also be able to
* cancel the retryable ticket and receive callvalue refund
* @param _amount Token Amount
* @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
* @param _gasPriceBid Gas price for L2 execution
* @param _data encoded data from router and user
* @return res abi encoded inbox sequence number
*/
function outboundTransferCustomRefund(
address _l1Token,
address _refundTo,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) external payable returns (bytes memory);

/**
* @notice get ERC20 gateway for token.
* @param _token ERC20 address.
* @return address of ERC20 gateway.
*/
function getGateway(address _token) external view returns (address);
}
import { ArbitrumInboxLike as ArbitrumL1InboxLike, ArbitrumERC20GatewayLike as ArbitrumL1ERC20GatewayLike } from "../interfaces/ArbitrumBridgeInterfaces.sol";

/**
* @notice Contract containing logic to send messages from L1 to Arbitrum.
Expand Down
Loading

0 comments on commit 0de9303

Please sign in to comment.