Skip to content

Commit

Permalink
update comments and refactor aliasing
Browse files Browse the repository at this point in the history
Signed-off-by: bennett <bennett@umaproject.org>
  • Loading branch information
bmzig committed Sep 24, 2024
1 parent 4433749 commit 05dfc6e
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 48 deletions.
14 changes: 2 additions & 12 deletions contracts/Arbitrum_SpokePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pragma solidity ^0.8.19;
import "./SpokePool.sol";
import "./libraries/CircleCCTPAdapter.sol";
import { ArbitrumL2ERC20GatewayLike } from "./interfaces/ArbitrumBridgeInterfaces.sol";
import { AddressUtils } from "./libraries/AddressUtils.sol";

/**
* @notice AVM specific SpokePool. Uses AVM cross-domain-enabled logic to implement admin only access to functions.
Expand Down Expand Up @@ -54,7 +55,7 @@ contract Arbitrum_SpokePool is SpokePool, CircleCCTPAdapter {
}

modifier onlyFromCrossDomainAdmin() {
require(msg.sender == _applyL1ToL2Alias(crossDomainAdmin), "ONLY_COUNTERPART_GATEWAY");
require(msg.sender == AddressUtils._applyL1ToL2Alias(crossDomainAdmin), "ONLY_COUNTERPART_GATEWAY");
_;
}

Expand Down Expand Up @@ -111,17 +112,6 @@ contract Arbitrum_SpokePool is SpokePool, CircleCCTPAdapter {
emit WhitelistedTokens(_l2Token, _l1Token);
}

// L1 addresses are transformed during l1->l2 calls.
// See https://developer.offchainlabs.com/docs/l1_l2_messages#address-aliasing for more information.
// This cannot be pulled directly from Arbitrum contracts because their contracts are not 0.8.X compatible and
// this operation takes advantage of overflows, whose behavior changed in 0.8.0.
function _applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
// Allows overflows as explained above.
unchecked {
l2Address = address(uint160(l1Address) + uint160(0x1111000000000000000000000000000000001111));
}
}

// Apply AVM-specific transformation to cross domain admin address on L1.
function _requireAdminSender() internal override onlyFromCrossDomainAdmin {}
}
12 changes: 2 additions & 10 deletions contracts/ZkSync_SpokePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.0;

import "./SpokePool.sol";
import { AddressUtils } from "./libraries/AddressUtils.sol";

// https://github.com/matter-labs/era-contracts/blob/6391c0d7bf6184d7f6718060e3991ba6f0efe4a7/zksync/contracts/bridge/L2ERC20Bridge.sol#L104
interface ZkBridgeLike {
Expand Down Expand Up @@ -62,7 +63,7 @@ contract ZkSync_SpokePool is SpokePool {
}

modifier onlyFromCrossDomainAdmin() {
require(msg.sender == _applyL1ToL2Alias(crossDomainAdmin), "ONLY_COUNTERPART_GATEWAY");
require(msg.sender == AddressUtils._applyL1ToL2Alias(crossDomainAdmin), "ONLY_COUNTERPART_GATEWAY");
_;
}

Expand Down Expand Up @@ -118,14 +119,5 @@ contract ZkSync_SpokePool is SpokePool {
emit SetZkBridge(address(_zkErc20Bridge), oldErc20Bridge);
}

// L1 addresses are transformed during l1->l2 calls.
// See https://github.com/matter-labs/era-contracts/blob/main/docs/Overview.md#mailboxfacet for more information.
// Another source: https://github.com/matter-labs/era-contracts/blob/41c25aa16d182f757c3fed1463c78a81896f65e6/ethereum/contracts/vendor/AddressAliasHelper.sol#L28
function _applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
unchecked {
l2Address = address(uint160(l1Address) + uint160(0x1111000000000000000000000000000000001111));
}
}

function _requireAdminSender() internal override onlyFromCrossDomainAdmin {}
}
2 changes: 1 addition & 1 deletion contracts/chain-adapters/Arbitrum_AdapterBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ contract Arbitrum_AdapterBase is CircleCCTPAdapter {
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
// maxSubmissionCost to use when creating an L2 retryable ticket: https://github.com/OffchainLabs/arbitrum-classic/blob/551a39b381dcea81e03e7599fcb01fddff4fe96c/packages/arb-bridge-peripherals/contracts/tokenbridge/ethereum/gateway/L1GatewayRouter.sol#L240
bytes memory data = abi.encode(L2_MAX_SUBMISSION_COST, "");

// Note: Legacy routers don't have the outboundTransferCustomRefund method, so default to using
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ import { ITokenMessenger as ICCTPTokenMessenger } from "../external/interfaces/C
import { ArbitrumCustomGasTokenInbox, ArbitrumL1ERC20GatewayLike } from "../interfaces/ArbitrumBridgeInterfaces.sol";
import { ForwarderBase } from "./ForwarderBase.sol";
import { Arbitrum_CustomGasToken_AdapterBase, FunderInterface } from "./Arbitrum_CustomGasToken_AdapterBase.sol";
import { AddressUtils } from "../libraries/AddressUtils.sol";

/**
* @notice Contract containing logic to send messages from Arbitrum to an AVM L3.
* @notice Contract to be deployed on L2 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
* it is deployed on Arbitrum.
* @dev This contract sends messages to L3 when the msg.sender is the `crossDomainAdmin` (which should
* be set to the hub pool). The `crossDomainAdmin` is defined upon initialization.
* @custom:security-contact bugs@across.to
*/

// solhint-disable-next-line contract-name-camelcase
contract Arbitrum_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_AdapterBase, ForwarderBase {
contract Arbitrum_CustomGasToken_L2Adapter is Arbitrum_CustomGasToken_AdapterBase, ForwarderBase {
using SafeERC20 for IERC20;

modifier onlyFromCrossDomainAdmin() {
require(msg.sender == _applyL1ToL2Alias(crossDomainAdmin), "ONLY_CROSS_DOMAIN_ADMIN");
require(msg.sender == AddressUtils._applyL1ToL2Alias(crossDomainAdmin), "ONLY_CROSS_DOMAIN_ADMIN");
_;
}

Expand All @@ -41,6 +44,7 @@ contract Arbitrum_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_Adapter
* submission fee is a parameter unique to Arbitrum retryable transactions. This value is hardcoded
* and used for all messages sent by this adapter.
* @param _l3GasPrice Gas price bid for L3 execution. Should be set conservatively high to avoid stuck messages.
* @dev Both `crossDomainAdmin` and `l3SpokePool` are defined on initialization of the proxy.
*/
constructor(
ArbitrumCustomGasTokenInbox _l2ArbitrumInbox,
Expand Down Expand Up @@ -79,6 +83,9 @@ contract Arbitrum_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_Adapter
address,
uint256 amount
) external payable override {
// The second field is hardcoded as address(0) since AVM gateway routers do not bridge a source token
// to a specified destination token, but instead derives the L2 token in the gateway router. Therefore,
// the second argument is not used by the adapter's bridging logic.
_relayTokens(l2Token, address(0), amount, l3SpokePool);
emit TokensForwarded(l2Token, amount);
}
Expand All @@ -96,11 +103,4 @@ contract Arbitrum_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_Adapter
}

function _requireAdminSender() internal virtual override onlyFromCrossDomainAdmin {}

function _applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
// Allows overflows as explained above.
unchecked {
l2Address = address(uint160(l1Address) + uint160(0x1111000000000000000000000000000000001111));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CircleCCTPAdapter, CircleDomainIds } from "../libraries/CircleCCTPAdapt
import { ArbitrumInboxLike, ArbitrumL1ERC20GatewayLike } from "../interfaces/ArbitrumBridgeInterfaces.sol";
import { ForwarderBase } from "./ForwarderBase.sol";
import { Arbitrum_AdapterBase } from "./Arbitrum_AdapterBase.sol";
import { AddressUtils } from "../libraries/AddressUtils.sol";

/**
* @notice Contract containing logic to send messages from L2 to AVM L3s.
Expand All @@ -17,11 +18,11 @@ import { Arbitrum_AdapterBase } from "./Arbitrum_AdapterBase.sol";
*/

// solhint-disable-next-line contract-name-camelcase
contract Arbitrum_L2_Forwarder is ForwarderBase, Arbitrum_AdapterBase {
contract Arbitrum_L2Adapter is ForwarderBase, Arbitrum_AdapterBase {
using SafeERC20 for IERC20;

modifier onlyFromCrossDomainAdmin() {
require(msg.sender == _applyL1ToL2Alias(crossDomainAdmin), "ONLY_CROSS_DOMAIN_ADMIN");
require(msg.sender == AddressUtils._applyL1ToL2Alias(crossDomainAdmin), "ONLY_CROSS_DOMAIN_ADMIN");
_;
}

Expand Down Expand Up @@ -91,11 +92,4 @@ contract Arbitrum_L2_Forwarder is ForwarderBase, Arbitrum_AdapterBase {
}

function _requireAdminSender() internal virtual override onlyFromCrossDomainAdmin {}

function _applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
// Allows overflows as explained above.
unchecked {
l2Address = address(uint160(l1Address) + uint160(0x1111000000000000000000000000000000001111));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { Arbitrum_CustomGasToken_L2_Forwarder, ICCTPTokenMessenger, FunderInterface } from "./Arbitrum_CustomGasToken_L2_Forwarder.sol";
import { Arbitrum_CustomGasToken_L2Adapter, ICCTPTokenMessenger, FunderInterface } from "./Arbitrum_CustomGasToken_L2Adapter.sol";
import { ArbitrumCustomGasTokenInbox, ArbitrumL1ERC20GatewayLike } from "../interfaces/ArbitrumBridgeInterfaces.sol";
import { LibOptimismUpgradeable } from "@openzeppelin/contracts-upgradeable/crosschain/optimism/LibOptimismUpgradeable.sol";
import { Lib_PredeployAddresses } from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
Expand All @@ -13,11 +13,13 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s
* @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.
* @dev This contract differs from the Arbitrum counterpart since we must derive the L1 msg.sender by
* calling the L2 messenger instead of aliasing.
* @custom:security-contact bugs@across.to
*/

// solhint-disable-next-line contract-name-camelcase
contract Ovm_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_L2_Forwarder {
contract Ovm_CustomGasToken_L2Adapter is Arbitrum_CustomGasToken_L2Adapter {
using SafeERC20 for IERC20;

address public constant MESSENGER = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
Expand Down Expand Up @@ -51,7 +53,7 @@ contract Ovm_CustomGasToken_L2_Forwarder is Arbitrum_CustomGasToken_L2_Forwarder
uint256 _l3MaxSubmissionCost,
uint256 _l3GasPrice
)
Arbitrum_CustomGasToken_L2_Forwarder(
Arbitrum_CustomGasToken_L2Adapter(
_l2ArbitrumInbox,
_l2ERC20GatewayRouter,
_l3RefundL3Address,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { Arbitrum_L2_Forwarder, ITokenMessenger } from "./Arbitrum_L2_Forwarder.sol";
import { Arbitrum_L2Adapter, ITokenMessenger } from "./Arbitrum_L2Adapter.sol";
import { ArbitrumInboxLike, ArbitrumL1ERC20GatewayLike } from "../interfaces/ArbitrumBridgeInterfaces.sol";
import { LibOptimismUpgradeable } from "@openzeppelin/contracts-upgradeable/crosschain/optimism/LibOptimismUpgradeable.sol";
import { Lib_PredeployAddresses } from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
Expand All @@ -13,11 +13,13 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s
* @dev This contract is very similar to the Arbitrum_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.
* @dev This contract differs from the Arbitrum counterpart since we must derive the L1 msg.sender by
* calling the L2 messenger instead of aliasing.
* @custom:security-contact bugs@across.to
*/

// solhint-disable-next-line contract-name-camelcase
contract Ovm_L2_Forwarder is Arbitrum_L2_Forwarder {
contract Ovm_L2Adapter is Arbitrum_L2Adapter {
using SafeERC20 for IERC20;

address public constant MESSENGER = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
Expand Down Expand Up @@ -47,7 +49,7 @@ contract Ovm_L2_Forwarder is Arbitrum_L2_Forwarder {
uint256 _l3MaxSubmissionCost,
uint256 _l3GasPrice
)
Arbitrum_L2_Forwarder(
Arbitrum_L2Adapter(
_l2ArbitrumInbox,
_l2ERC20GatewayRouter,
_l3RefundL3Address,
Expand Down
17 changes: 17 additions & 0 deletions contracts/libraries/AddressUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/**
* @title AddressUtils
* @notice This library contains internal functions for manipulating addresses.
*/
library AddressUtils {
// L1 addresses are transformed during l1->l2 calls.
// This cannot be pulled directly from Arbitrum contracts because their contracts are not 0.8.X compatible and
// this operation takes advantage of overflows, whose behavior changed in 0.8.0.
function _applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
unchecked {
l2Address = address(uint160(l1Address) + uint160(0x1111000000000000000000000000000000001111));
}
}
}

0 comments on commit 05dfc6e

Please sign in to comment.