Skip to content
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

improve(Arbitrum_CustomGasToken_Adapter): Handle non-18 custom gas token precision #589

Merged
merged 7 commits into from
Sep 19, 2024
31 changes: 27 additions & 4 deletions contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@
* @return address of the native token.
*/
function nativeToken() external view returns (address);

/**
* @dev number of decimals used by the native token
* This is set on bridge initialization using nativeToken.decimals()
* If the token does not have decimals() method, we assume it have 0 decimals
*/
function nativeTokenDecimals() external view returns (uint8);
}

/**
Expand Down Expand Up @@ -137,11 +144,12 @@
// 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 Unlike in Arbitrum_Adapter, this is immutable because we don't know what precision the custom gas token has.
// The Arbitrum Inbox requires that this uses 18 decimal precision.
uint256 public immutable L2_MAX_SUBMISSION_COST;

Check warning on line 148 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

Check warning on line 148 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase
nicholaspai marked this conversation as resolved.
Show resolved Hide resolved

// L2 Gas price bid for immediate L2 execution attempt (queryable via standard eth*gasPrice RPC)
uint256 public constant L2_GAS_PRICE = 5e9; // 5 gWei
// The Arbitrum Inbox requires that this is specified in gWei (e.g. 1e9 = 1 gWei)
uint256 public immutable L2_GAS_PRICE;

Check warning on line 152 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

Check warning on line 152 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase
nicholaspai marked this conversation as resolved.
Show resolved Hide resolved

uint256 public constant L2_CALL_VALUE = 0;

Expand All @@ -149,16 +157,16 @@
uint32 public constant RELAY_MESSAGE_L2_GAS_LIMIT = 2_000_000;

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

Check warning on line 160 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

Check warning on line 160 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

ArbitrumL1InboxLike public immutable L1_INBOX;

Check warning on line 162 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

Check warning on line 162 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

ArbitrumL1ERC20GatewayLike public immutable L1_ERC20_GATEWAY_ROUTER;

Check warning on line 164 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

Check warning on line 164 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

// This token is used to pay for l1 to l2 messages if its configured by an Arbitrum orbit chain.
IERC20 public immutable CUSTOM_GAS_TOKEN;

Check warning on line 167 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

Check warning on line 167 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

FunderInterface public immutable CUSTOM_GAS_TOKEN_FUNDER;

Check warning on line 169 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

Check warning on line 169 in contracts/chain-adapters/Arbitrum_CustomGasToken_Adapter.sol

View workflow job for this annotation

GitHub Actions / Solhint (16)

Variable name must be in mixedCase

error InvalidCustomGasToken();
error InsufficientCustomGasToken();
Expand All @@ -169,6 +177,8 @@
* @param _l1ERC20GatewayRouter ERC20 gateway router contract to send tokens to Arbitrum.
* @param _l2RefundL2Address L2 address to receive gas refunds on after a message is relayed.
* @param _l1Usdc USDC address on L1.
* @param _l2MaxSubmissionCost Max gas deducted from user's L2 balance to cover base fee.
* @param _l2GasPrice Gas price bid for L2 execution. Should be set conservatively high to avoid stuck messages.
* @param _cctpTokenMessenger TokenMessenger contract to bridge via CCTP.
* @param _customGasTokenFunder Contract that funds the custom gas token.
* @param _l2MaxSubmissionCost Amount of gas token allocated to pay for the base submission fee. The base
Expand All @@ -182,14 +192,16 @@
IERC20 _l1Usdc,
ICCTPTokenMessenger _cctpTokenMessenger,
FunderInterface _customGasTokenFunder,
uint256 _l2MaxSubmissionCost
uint256 _l2MaxSubmissionCost,
uint256 _l2GasPrice
) CircleCCTPAdapter(_l1Usdc, _cctpTokenMessenger, CircleDomainIds.Arbitrum) {
L1_INBOX = _l1ArbitrumInbox;
L1_ERC20_GATEWAY_ROUTER = _l1ERC20GatewayRouter;
L2_REFUND_L2_ADDRESS = _l2RefundL2Address;
CUSTOM_GAS_TOKEN = IERC20(L1_INBOX.bridge().nativeToken());
if (address(CUSTOM_GAS_TOKEN) == address(0)) revert InvalidCustomGasToken();
L2_MAX_SUBMISSION_COST = _l2MaxSubmissionCost;
L2_GAS_PRICE = _l2GasPrice;
CUSTOM_GAS_TOKEN_FUNDER = _customGasTokenFunder;
}

Expand All @@ -212,6 +224,7 @@
RELAY_MESSAGE_L2_GAS_LIMIT, // maxGas Max gas deducted from user's L2 balance to cover L2 execution
L2_GAS_PRICE, // gasPriceBid price bid for L2 execution
requiredL1TokenTotalFeeAmount, // tokenTotalFeeAmount amount of fees to be deposited in native token.
// This should be in the precision of the custom gas token.
message // data ABI encoded data of L2 message
);
emit MessageRelayed(target, message);
Expand Down Expand Up @@ -246,6 +259,7 @@
// Must use Inbox to bridge custom gas token.
// Source: https://github.com/OffchainLabs/token-bridge-contracts/blob/5bdf33259d2d9ae52ddc69bc5a9cbc558c4c40c7/contracts/tokenbridge/ethereum/gateway/L1OrbitERC20Gateway.sol#L33
if (l1Token == address(CUSTOM_GAS_TOKEN)) {
// amount and requiredL1TokenTotalFeeAmount are in the precision of the custom gas token.
uint256 amountToBridge = amount + requiredL1TokenTotalFeeAmount;
CUSTOM_GAS_TOKEN.safeIncreaseAllowance(address(L1_INBOX), amountToBridge);
L1_INBOX.createRetryableTicket(
Expand Down Expand Up @@ -283,11 +297,12 @@

/**
* @notice Returns required amount of gas token to send a message via the Inbox.
* @dev Should return a value in the same precision as the gas token's precision.
* @param l2GasLimit L2 gas limit for the message.
* @return amount of gas token that this contract needs to hold in order for relayMessage to succeed.
*/
function getL1CallValue(uint32 l2GasLimit) public view returns (uint256) {
return L2_MAX_SUBMISSION_COST + L2_GAS_PRICE * l2GasLimit;
return _from18ToNativeDecimals(L2_MAX_SUBMISSION_COST + L2_GAS_PRICE * l2GasLimit);
Copy link
Member Author

Choose a reason for hiding this comment

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

the benefit of putting the scaling logic into this public function is the deployer can quickly sanity check post-deployment that the L1CallValue will be set correctly with the right precision

}

function _pullCustomGas(uint32 l2GasLimit) internal returns (uint256) {
Expand All @@ -296,4 +311,12 @@
if (CUSTOM_GAS_TOKEN.balanceOf(address(this)) < requiredL1CallValue) revert InsufficientCustomGasToken();
return requiredL1CallValue;
}

function _from18ToNativeDecimals(uint256 amount) internal view returns (uint256) {
uint8 nativeTokenDecimals = L1_INBOX.bridge().nativeTokenDecimals();
if (nativeTokenDecimals < 18) {
return amount / 10**(18 - nativeTokenDecimals);
}
return amount;
}
}
Loading