Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 11 from a total of 11 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Teleport | 21849556 | 13 days ago | IN | 0.00040133 ETH | 0.0006086 | ||||
Teleport | 21849434 | 13 days ago | IN | 0.00042833 ETH | 0.00056905 | ||||
Teleport | 21681529 | 36 days ago | IN | 0.00131303 ETH | 0.00412757 | ||||
Teleport | 20626839 | 183 days ago | IN | 0.00029944 ETH | 0.0004861 | ||||
Teleport | 20218143 | 240 days ago | IN | 0.00080828 ETH | 0.00156581 | ||||
Teleport | 20125123 | 253 days ago | IN | 0.00051874 ETH | 0.00095775 | ||||
Teleport | 20125117 | 253 days ago | IN | 0.00047697 ETH | 0.00099702 | ||||
Teleport | 20125114 | 253 days ago | IN | 0.00068111 ETH | 0.00120257 | ||||
Teleport | 20020166 | 268 days ago | IN | 0.00208637 ETH | 0.00552722 | ||||
Teleport | 20019584 | 268 days ago | IN | 0.00302154 ETH | 0.00549959 | ||||
Teleport | 20019578 | 268 days ago | IN | 0.00488876 ETH | 0.00601756 |
Latest 22 internal transactions
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
21849556 | 13 days ago | 0.00032192 ETH | ||||
21849556 | 13 days ago | 0.00007941 ETH | ||||
21849434 | 13 days ago | 0.0003522 ETH | ||||
21849434 | 13 days ago | 0.00007613 ETH | ||||
21681529 | 36 days ago | 0.00051144 ETH | ||||
21681529 | 36 days ago | 0.00080159 ETH | ||||
20626839 | 183 days ago | 0.00020344 ETH | ||||
20626839 | 183 days ago | 0.00009599 ETH | ||||
20218143 | 240 days ago | 0.00022206 ETH | ||||
20218143 | 240 days ago | 0.00058622 ETH | ||||
20125123 | 253 days ago | 0.00024291 ETH | ||||
20125123 | 253 days ago | 0.00027583 ETH | ||||
20125117 | 253 days ago | 0.00020615 ETH | ||||
20125117 | 253 days ago | 0.00027082 ETH | ||||
20125114 | 253 days ago | 0.00021342 ETH | ||||
20125114 | 253 days ago | 0.00046769 ETH | ||||
20020166 | 268 days ago | 0.0007572 ETH | ||||
20020166 | 268 days ago | 0.00132916 ETH | ||||
20019584 | 268 days ago | 0.00050089 ETH | ||||
20019584 | 268 days ago | 0.00252065 ETH | ||||
20019578 | 268 days ago | 0.00236158 ETH | ||||
20019578 | 268 days ago | 0.00252718 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x481D9967...e605fB9C1 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
L1Teleporter
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 20000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; import {AddressAliasHelper} from "@arbitrum/nitro-contracts/src/libraries/AddressAliasHelper.sol"; import {L1GatewayRouter} from "@arbitrum/token-bridge-contracts/contracts/tokenbridge/ethereum/gateway/L1GatewayRouter.sol"; import {IInbox} from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {L2ForwarderPredictor} from "./L2ForwarderPredictor.sol"; import {IL2ForwarderFactory} from "./interfaces/IL2ForwarderFactory.sol"; import {IL1Teleporter} from "./interfaces/IL1Teleporter.sol"; import {IL2Forwarder} from "./interfaces/IL2Forwarder.sol"; import {TeleportationType, toTeleportationType} from "./lib/TeleportationType.sol"; contract L1Teleporter is Pausable, AccessControl, L2ForwarderPredictor, IL1Teleporter { using SafeERC20 for IERC20; /// @notice Accounts with this role can pause and unpause the contract bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); address public constant SKIP_FEE_TOKEN_MAGIC_ADDRESS = address(bytes20(keccak256("SKIP_FEE_TOKEN"))); constructor(address _l2ForwarderFactory, address _l2ForwarderImplementation, address _admin, address _pauser) L2ForwarderPredictor(_l2ForwarderFactory, _l2ForwarderImplementation) { _grantRole(DEFAULT_ADMIN_ROLE, _admin); _grantRole(PAUSER_ROLE, _pauser); } /// @notice Pause the contract function pause() external onlyRole(PAUSER_ROLE) { _pause(); } /// @notice Unpause the contract function unpause() external onlyRole(PAUSER_ROLE) { _unpause(); } /// @inheritdoc IL1Teleporter function teleport(TeleportParams calldata params) external payable whenNotPaused { ( uint256 requiredEth, uint256 requiredFeeToken, TeleportationType teleportationType, RetryableGasCosts memory retryableCosts ) = determineTypeAndFees(params); // ensure we have correct msg.value if (msg.value != requiredEth) revert IncorrectValue(requiredEth, msg.value); // calculate forwarder address from params address l2Forwarder = l2ForwarderAddress(_aliasIfContract(msg.sender), params.l2l3RouterOrInbox, params.to); if (teleportationType == TeleportationType.OnlyCustomFee) { // teleporting an L3's custom fee token to a custom (non-eth) fee L3 // we have to make sure that the amount specified is enough to cover the retryable costs from L2 -> L3 if (params.amount < requiredFeeToken) revert InsufficientFeeToken(requiredFeeToken, params.amount); } else if (teleportationType == TeleportationType.NonFeeTokenToCustomFee) { // teleporting a non-fee token to a custom (non-eth) fee L3 // pull in and send fee tokens through the bridge to predicted forwarder if (requiredFeeToken > 0) { _pullAndBridgeToken({ router: params.l1l2Router, token: params.l3FeeTokenL1Addr, to: l2Forwarder, amount: requiredFeeToken, gasLimit: params.gasParams.l1l2FeeTokenBridgeGasLimit, gasPriceBid: params.gasParams.l2GasPriceBid, maxSubmissionCost: params.gasParams.l1l2FeeTokenBridgeMaxSubmissionCost }); } } _teleportCommon(params, retryableCosts, l2Forwarder); emit Teleported({ sender: msg.sender, l1Token: params.l1Token, l3FeeTokenL1Addr: params.l3FeeTokenL1Addr, l1l2Router: params.l1l2Router, l2l3RouterOrInbox: params.l2l3RouterOrInbox, to: params.to, amount: params.amount }); } /// @inheritdoc IL1Teleporter function buildL2ForwarderParams(TeleportParams calldata params, address caller) public view returns (IL2Forwarder.L2ForwarderParams memory) { address l2Token = L1GatewayRouter(params.l1l2Router).calculateL2TokenAddress(params.l1Token); address l3FeeTokenL2Addr; uint256 maxSubmissionCost; TeleportationType teleportationType = toTeleportationType({token: params.l1Token, feeToken: params.l3FeeTokenL1Addr}); if (teleportationType == TeleportationType.Standard) { l3FeeTokenL2Addr = address(0); } else if (teleportationType == TeleportationType.OnlyCustomFee) { l3FeeTokenL2Addr = l2Token; } else { l3FeeTokenL2Addr = L1GatewayRouter(params.l1l2Router).calculateL2TokenAddress(params.l3FeeTokenL1Addr); maxSubmissionCost = params.gasParams.l2l3TokenBridgeMaxSubmissionCost; } return IL2Forwarder.L2ForwarderParams({ owner: _aliasIfContract(caller), l2Token: l2Token, l3FeeTokenL2Addr: l3FeeTokenL2Addr, routerOrInbox: params.l2l3RouterOrInbox, to: params.to, gasLimit: params.gasParams.l2l3TokenBridgeGasLimit, gasPriceBid: params.gasParams.l3GasPriceBid, maxSubmissionCost: maxSubmissionCost }); } /// @inheritdoc IL1Teleporter function determineTypeAndFees(TeleportParams calldata params) public pure returns ( uint256 ethAmount, uint256 feeTokenAmount, TeleportationType teleportationType, RetryableGasCosts memory costs ) { _requireZeroFeeTokenIfSkipping(params); costs = _calculateRetryableGasCosts(params.gasParams); teleportationType = toTeleportationType({token: params.l1Token, feeToken: params.l3FeeTokenL1Addr}); // all teleportation types require at least these 2 retryables to L2 ethAmount = costs.l1l2TokenBridgeCost + costs.l2ForwarderFactoryCost; // in addition to the above ETH amount, more fee token and/or ETH is required depending on the teleportation type if (teleportationType == TeleportationType.Standard) { // standard type requires 1 retryable to L3 paid for in ETH ethAmount += costs.l2l3TokenBridgeCost; } else if (teleportationType == TeleportationType.OnlyCustomFee) { // only custom fee type requires 1 retryable to L3 paid for in fee token feeTokenAmount = costs.l2l3TokenBridgeCost; } else if (costs.l2l3TokenBridgeCost > 0) { // non-fee token to custom fee type requires: // 1 retryable to L2 paid for in ETH // 1 retryable to L3 paid for in fee token ethAmount += costs.l1l2FeeTokenBridgeCost; feeTokenAmount = costs.l2l3TokenBridgeCost; } } /// @notice Common logic for teleport() /// @dev Pulls in `params.l1Token` and creates 2 retryables: one to bridge tokens to the L2Forwarder, and one to call the L2ForwarderFactory. function _teleportCommon( TeleportParams calldata params, RetryableGasCosts memory retryableCosts, address l2Forwarder ) internal { // send tokens through the bridge to predicted forwarder _pullAndBridgeToken({ router: params.l1l2Router, token: params.l1Token, to: l2Forwarder, amount: params.amount, gasLimit: params.gasParams.l1l2TokenBridgeGasLimit, gasPriceBid: params.gasParams.l2GasPriceBid, maxSubmissionCost: params.gasParams.l1l2TokenBridgeMaxSubmissionCost }); // get inbox address inbox = L1GatewayRouter(params.l1l2Router).inbox(); // call the L2ForwarderFactory IInbox(inbox).createRetryableTicket{value: address(this).balance}({ to: l2ForwarderFactory, l2CallValue: address(this).balance - retryableCosts.l2ForwarderFactoryCost, maxSubmissionCost: params.gasParams.l2ForwarderFactoryMaxSubmissionCost, excessFeeRefundAddress: l2Forwarder, callValueRefundAddress: l2Forwarder, gasLimit: params.gasParams.l2ForwarderFactoryGasLimit, maxFeePerGas: params.gasParams.l2GasPriceBid, data: abi.encodeCall(IL2ForwarderFactory.callForwarder, buildL2ForwarderParams(params, msg.sender)) }); } /// @notice Pull tokens from msg.sender, approve the token's gateway and bridge them to L2. function _pullAndBridgeToken( address router, address token, address to, uint256 amount, uint256 gasLimit, uint256 gasPriceBid, uint256 maxSubmissionCost ) internal { // pull in tokens from caller IERC20(token).safeTransferFrom(msg.sender, address(this), amount); // gateway is user supplied, an attacker can hence approve arbitrary address to spend fund from L1Teleporter // this is fine since L1Teleporter is not expected to hold fund between transactions address gateway = L1GatewayRouter(router).getGateway(token); if (IERC20(token).allowance(address(this), gateway) == 0) { IERC20(token).safeApprove(gateway, type(uint256).max); } // fee on transfer tokens are not supported as the amount would not match L1GatewayRouter(router).outboundTransferCustomRefund{value: gasLimit * gasPriceBid + maxSubmissionCost}({ _token: address(token), _refundTo: to, _to: to, _amount: amount, _maxGas: gasLimit, _gasPriceBid: gasPriceBid, _data: abi.encode(maxSubmissionCost, bytes("")) }); } /// @notice Given some gas parameters, calculate costs for each retryable ticket. /// @param gasParams Gas parameters for each retryable ticket function _calculateRetryableGasCosts(RetryableGasParams calldata gasParams) internal pure returns (RetryableGasCosts memory results) { results.l1l2FeeTokenBridgeCost = gasParams.l1l2FeeTokenBridgeMaxSubmissionCost + (gasParams.l1l2FeeTokenBridgeGasLimit * gasParams.l2GasPriceBid); results.l1l2TokenBridgeCost = gasParams.l1l2TokenBridgeMaxSubmissionCost + (gasParams.l1l2TokenBridgeGasLimit * gasParams.l2GasPriceBid); results.l2ForwarderFactoryCost = gasParams.l2ForwarderFactoryMaxSubmissionCost + (gasParams.l2ForwarderFactoryGasLimit * gasParams.l2GasPriceBid); results.l2l3TokenBridgeCost = gasParams.l2l3TokenBridgeMaxSubmissionCost + (gasParams.l2l3TokenBridgeGasLimit * gasParams.l3GasPriceBid); } /// @dev If the fee token is being skipped, ensure that all fee-related gas parameters are zero function _requireZeroFeeTokenIfSkipping(TeleportParams calldata params) internal pure { if ( params.l3FeeTokenL1Addr == SKIP_FEE_TOKEN_MAGIC_ADDRESS && ( params.gasParams.l2l3TokenBridgeMaxSubmissionCost > 0 || params.gasParams.l2l3TokenBridgeGasLimit > 0 || params.gasParams.l1l2FeeTokenBridgeGasLimit > 0 || params.gasParams.l1l2FeeTokenBridgeMaxSubmissionCost > 0 || params.gasParams.l3GasPriceBid > 0 ) ) { revert NonZeroFeeTokenAmount(); } } /// @dev Alias the address if it has code, otherwise return the address as is function _aliasIfContract(address addr) internal view returns (address) { return addr.code.length > 0 ? AddressAliasHelper.applyL1ToL2Alias(addr) : addr; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControl.sol"; import "../utils/Context.sol"; import "../utils/Strings.sol"; import "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `_msgSender()` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. * * Format of the revert message is described in {_checkRole}. * * _Available since v4.6._ */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(account), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * May emit a {RoleGranted} event. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; library AddressAliasHelper { uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); /// @notice Utility function that converts the address in the L1 that submitted a tx to /// the inbox to the msg.sender viewed in the L2 /// @param l1Address the address in the L1 that triggered the tx to L2 /// @return l2Address L2 address as viewed in msg.sender function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { unchecked { l2Address = address(uint160(l1Address) + OFFSET); } } /// @notice Utility function that converts the msg.sender viewed in the L2 to the /// address in the L1 that submitted a tx to the inbox /// @param l2Address L2 address as viewed in msg.sender /// @return l1Address the address in the L1 that triggered the tx to L2 function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { unchecked { l1Address = address(uint160(l2Address) - OFFSET); } } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; import "../../libraries/Whitelist.sol"; import { ArbitrumEnabledToken } from "../ICustomToken.sol"; import "../L1ArbitrumMessenger.sol"; import "../../libraries/gateway/GatewayRouter.sol"; import "../../arbitrum/gateway/L2GatewayRouter.sol"; import "../../libraries/ERC165.sol"; import "./IL1GatewayRouter.sol"; import "./IL1ArbitrumGateway.sol"; /** * @title Handles deposits from Erhereum into Arbitrum. Tokens are routered to their appropriate L1 gateway (Router itself also conforms to the Gateway itnerface). * @notice Router also serves as an L1-L2 token address oracle. */ contract L1GatewayRouter is WhitelistConsumer, L1ArbitrumMessenger, GatewayRouter, ERC165, IL1GatewayRouter { using Address for address; address public override owner; address public override inbox; modifier onlyOwner() { require(msg.sender == owner, "ONLY_OWNER"); _; } function initialize( address _owner, address _defaultGateway, address, // was _whitelist, now unused address _counterpartGateway, address _inbox ) public { GatewayRouter._initialize(_counterpartGateway, address(0), _defaultGateway); owner = _owner; WhitelistConsumer.whitelist = address(0); inbox = _inbox; } function setDefaultGateway( address newL1DefaultGateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost ) external payable virtual onlyOwner returns (uint256) { return _setDefaultGateway( newL1DefaultGateway, _maxGas, _gasPriceBid, _maxSubmissionCost, msg.value ); } function _setDefaultGateway( address newL1DefaultGateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost, uint256 feeAmount ) internal returns (uint256) { defaultGateway = newL1DefaultGateway; emit DefaultGatewayUpdated(newL1DefaultGateway); address l2NewDefaultGateway; if (newL1DefaultGateway != address(0)) { l2NewDefaultGateway = TokenGateway(newL1DefaultGateway).counterpartGateway(); } bytes memory data = abi.encodeWithSelector( L2GatewayRouter.setDefaultGateway.selector, l2NewDefaultGateway ); return sendTxToL2( inbox, counterpartGateway, msg.sender, feeAmount, 0, L2GasParams({ _maxSubmissionCost: _maxSubmissionCost, _maxGas: _maxGas, _gasPriceBid: _gasPriceBid }), data ); } function setOwner(address newOwner) external onlyOwner { require(newOwner != address(0), "INVALID_OWNER"); // set newOwner to address(1) to disable owner and keep `initialize` safe owner = newOwner; } /** * @notice Allows L1 Token contract to trustlessly register its gateway. (other setGateway method allows excess eth recovery from _maxSubmissionCost and is recommended) * @param _gateway l1 gateway address * @param _maxGas max gas for L2 retryable exrecution * @param _gasPriceBid gas price for L2 retryable ticket * @param _maxSubmissionCost base submission cost L2 retryable tick3et * @return Retryable ticket ID */ function setGateway( address _gateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost ) external payable virtual override returns (uint256) { return setGateway(_gateway, _maxGas, _gasPriceBid, _maxSubmissionCost, msg.sender); } /** * @notice Allows L1 Token contract to trustlessly register its gateway. * @param _gateway l1 gateway address * @param _maxGas max gas for L2 retryable exrecution * @param _gasPriceBid gas price for L2 retryable ticket * @param _maxSubmissionCost base submission cost L2 retryable tick3et * @param _creditBackAddress address for crediting back overpayment of _maxSubmissionCost * @return Retryable ticket ID */ function setGateway( address _gateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost, address _creditBackAddress ) public payable virtual override returns (uint256) { return _setGatewayWithCreditBack( _gateway, _maxGas, _gasPriceBid, _maxSubmissionCost, _creditBackAddress, msg.value ); } function _setGatewayWithCreditBack( address _gateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost, address _creditBackAddress, uint256 feeAmount ) internal returns (uint256) { require( ArbitrumEnabledToken(msg.sender).isArbitrumEnabled() == uint8(0xb1), "NOT_ARB_ENABLED" ); require(_gateway.isContract(), "NOT_TO_CONTRACT"); address currGateway = getGateway(msg.sender); if (currGateway != address(0) && currGateway != defaultGateway) { // if gateway is already set to a non-default gateway, don't allow it to set a different gateway require(currGateway == _gateway, "NO_UPDATE_TO_DIFFERENT_ADDR"); } address[] memory _tokenArr = new address[](1); _tokenArr[0] = address(msg.sender); address[] memory _gatewayArr = new address[](1); _gatewayArr[0] = _gateway; return _setGateways( _tokenArr, _gatewayArr, _maxGas, _gasPriceBid, _maxSubmissionCost, _creditBackAddress, feeAmount ); } function setGateways( address[] memory _token, address[] memory _gateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost ) external payable virtual onlyOwner returns (uint256) { // it is assumed that token and gateway are both contracts // require(_token[i].isContract() && _gateway[i].isContract(), "NOT_CONTRACT"); return _setGateways( _token, _gateway, _maxGas, _gasPriceBid, _maxSubmissionCost, msg.sender, msg.value ); } function _setGateways( address[] memory _token, address[] memory _gateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost, address _creditBackAddress, uint256 feeAmount ) internal returns (uint256) { require(_token.length == _gateway.length, "WRONG_LENGTH"); for (uint256 i = 0; i < _token.length; i++) { l1TokenToGateway[_token[i]] = _gateway[i]; emit GatewaySet(_token[i], _gateway[i]); // overwrite memory so the L2 router receives the L2 address of each gateway if (_gateway[i] != address(0) && _gateway[i] != DISABLED) { // if we are assigning a gateway to the token, the address oracle of the gateway // must return something other than the 0 address // this check helps avoid misconfiguring gateways require( TokenGateway(_gateway[i]).calculateL2TokenAddress(_token[i]) != address(0), "TOKEN_NOT_HANDLED_BY_GATEWAY" ); _gateway[i] = TokenGateway(_gateway[i]).counterpartGateway(); } } bytes memory data = abi.encodeWithSelector( L2GatewayRouter.setGateway.selector, _token, _gateway ); return sendTxToL2( inbox, counterpartGateway, _creditBackAddress, feeAmount, 0, L2GasParams({ _maxSubmissionCost: _maxSubmissionCost, _maxGas: _maxGas, _gasPriceBid: _gasPriceBid }), data ); } function outboundTransfer( address _token, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes calldata _data ) public payable override(GatewayRouter, ITokenGateway) returns (bytes memory) { return super.outboundTransfer(_token, _to, _amount, _maxGas, _gasPriceBid, _data); } /** * @notice Deposit ERC20 token from Ethereum into Arbitrum using the registered or otherwise default gateway * @dev Some legacy gateway might not have the outboundTransferCustomRefund method and will revert, in such case use outboundTransfer instead * 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 _token 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 _token, address _refundTo, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes calldata _data ) public payable override returns (bytes memory) { address gateway = getGateway(_token); bytes memory gatewayData = GatewayMessageHandler.encodeFromRouterToGateway( msg.sender, _data ); emit TransferRouted(_token, msg.sender, _to, gateway); // here we use `IL1ArbitrumGateway` since we don't assume all ITokenGateway implements `outboundTransferCustomRefund` return IL1ArbitrumGateway(gateway).outboundTransferCustomRefund{ value: msg.value }( _token, _refundTo, _to, _amount, _maxGas, _gasPriceBid, gatewayData ); } modifier onlyCounterpartGateway() override { // don't expect messages from L2 router revert("ONLY_COUNTERPART_GATEWAY"); _; } function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) { // registering interfaces that is added after arb-bridge-peripherals >1.0.11 // using function selector instead of single function interfaces to reduce bloat return interfaceId == this.outboundTransferCustomRefund.selector || super.supportsInterface(interfaceId); } }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; import "./IBridge.sol"; import "./IInboxBase.sol"; interface IInbox is IInboxBase { function sendL1FundedUnsignedTransaction( uint256 gasLimit, uint256 maxFeePerGas, uint256 nonce, address to, bytes calldata data ) external payable returns (uint256); function sendL1FundedContractTransaction( uint256 gasLimit, uint256 maxFeePerGas, address to, bytes calldata data ) external payable returns (uint256); /** * @dev This method can only be called upon L1 fork and will not alias the caller * This method will revert if not called from origin */ function sendL1FundedUnsignedTransactionToFork( uint256 gasLimit, uint256 maxFeePerGas, uint256 nonce, address to, bytes calldata data ) external payable returns (uint256); /** * @dev This method can only be called upon L1 fork and will not alias the caller * This method will revert if not called from origin */ function sendUnsignedTransactionToFork( uint256 gasLimit, uint256 maxFeePerGas, uint256 nonce, address to, uint256 value, bytes calldata data ) external returns (uint256); /** * @notice Send a message to initiate L2 withdrawal * @dev This method can only be called upon L1 fork and will not alias the caller * This method will revert if not called from origin */ function sendWithdrawEthToFork( uint256 gasLimit, uint256 maxFeePerGas, uint256 nonce, uint256 value, address withdrawTo ) external returns (uint256); /** * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract * @dev This does not trigger the fallback function when receiving in the L2 side. * Look into retryable tickets if you are interested in this functionality. * @dev This function should not be called inside contract constructors */ function depositEth() 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 all msg.value will deposited to callValueRefundAddress on L2 * @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 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); // ---------- initializer ---------- /** * @dev function to be called one time during the inbox upgrade process * this is used to fix the storage slots */ function postUpgradeInit(IBridge _bridge) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {IL2ForwarderPredictor} from "./interfaces/IL2ForwarderPredictor.sol"; abstract contract L2ForwarderPredictor is IL2ForwarderPredictor { /// @inheritdoc IL2ForwarderPredictor address public immutable l2ForwarderFactory; /// @inheritdoc IL2ForwarderPredictor address public immutable l2ForwarderImplementation; constructor(address _factory, address _implementation) { l2ForwarderFactory = _factory; l2ForwarderImplementation = _implementation; } /// @inheritdoc IL2ForwarderPredictor function l2ForwarderAddress(address owner, address routerOrInbox, address to) public view returns (address) { return Clones.predictDeterministicAddress( l2ForwarderImplementation, _salt(owner, routerOrInbox, to), l2ForwarderFactory ); } /// @notice Creates the salt for an L2Forwarder from its owner, routerOrInbox, and to address function _salt(address owner, address routerOrInbox, address to) internal pure returns (bytes32) { return keccak256(abi.encode(owner, routerOrInbox, to)); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import {IL2ForwarderPredictor} from "./IL2ForwarderPredictor.sol"; import {IL2Forwarder} from "./IL2Forwarder.sol"; /// @title IL2ForwarderFactory /// @notice Creates L2Forwarders and calls them to bridge tokens to L3. /// L2Forwarders are created via CREATE2 / clones. interface IL2ForwarderFactory is IL2ForwarderPredictor { /// @notice Emitted when a new L2Forwarder is created event CreatedL2Forwarder(address indexed l2Forwarder, address indexed owner, address routerOrInbox, address to); /// @notice Emitted when an L2Forwarder is called to bridge tokens to L3 event CalledL2Forwarder(address indexed l2Forwarder, IL2Forwarder.L2ForwarderParams params); /// @notice Thrown when any address other than the aliased L1Teleporter calls callForwarder error OnlyL1Teleporter(); /// @notice Calls an L2Forwarder to bridge tokens to L3. Will create the L2Forwarder first if it doesn't exist. /// @dev Only callable by the aliased L1Teleporter. /// @param params Parameters for the L2Forwarder function callForwarder(IL2Forwarder.L2ForwarderParams memory params) external payable; /// @notice Creates an L2Forwarder for the given parameters. /// @param owner Owner of the L2Forwarder /// @param routerOrInbox Address of the L1GatewayRouter or Inbox /// @param to Address to bridge tokens to /// @dev This method is external to allow fund rescue when `callForwarder` reverts. function createL2Forwarder(address owner, address routerOrInbox, address to) external returns (IL2Forwarder); /// @notice Aliased address of the L1Teleporter contract function aliasedL1Teleporter() external view returns (address); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import {IL2Forwarder} from "./IL2Forwarder.sol"; import {IL2ForwarderPredictor} from "./IL2ForwarderPredictor.sol"; import {TeleportationType} from "../lib/TeleportationType.sol"; /// @title IL1Teleporter /// @notice Initiates L1 -> L3 transfers. /// Creates 2 (or 3) retryables: one to bridge tokens (one to bridge the L3's fee token) to an L2Forwarder, /// and one to call the L2ForwarderFactory. interface IL1Teleporter is IL2ForwarderPredictor { /// @notice Parameters for teleport() /// @param l1Token L1 token being teleported /// @param l3FeeTokenL1Addr L1 address of the L3's fee token, or 0x00 for ETH /// @param l1l2Router L1 to L2 token bridge router /// @param l2l3RouterOrInbox L2 to L3 token bridge router or L2 to L3 Inbox /// @param to L3 address that will receive the tokens /// @param amount Amount of tokens being teleported /// @param gasParams Gas parameters for each retryable ticket struct TeleportParams { address l1Token; address l3FeeTokenL1Addr; address l1l2Router; address l2l3RouterOrInbox; address to; uint256 amount; RetryableGasParams gasParams; } /// @notice Gas parameters for each retryable ticket. struct RetryableGasParams { uint256 l2GasPriceBid; uint256 l3GasPriceBid; uint64 l2ForwarderFactoryGasLimit; uint64 l1l2FeeTokenBridgeGasLimit; uint64 l1l2TokenBridgeGasLimit; uint64 l2l3TokenBridgeGasLimit; uint256 l2ForwarderFactoryMaxSubmissionCost; uint256 l1l2FeeTokenBridgeMaxSubmissionCost; uint256 l1l2TokenBridgeMaxSubmissionCost; uint256 l2l3TokenBridgeMaxSubmissionCost; } /// @notice Total cost for each retryable ticket. struct RetryableGasCosts { uint256 l1l2FeeTokenBridgeCost; uint256 l1l2TokenBridgeCost; uint256 l2ForwarderFactoryCost; uint256 l2l3TokenBridgeCost; } /// @notice Emitted when a teleportation is initiated. /// @param sender L1 address that initiated the teleportation /// @param l1Token L1 token being teleported /// @param l3FeeTokenL1Addr L1 address of the L3's fee token, or 0x00 for ETH /// @param l1l2Router L1 to L2 token bridge router /// @param l2l3RouterOrInbox L2 to L3 token bridge router or Inbox /// @param to L3 address that will receive the tokens /// @param amount Amount of tokens being teleported event Teleported( address indexed sender, address l1Token, address l3FeeTokenL1Addr, address l1l2Router, address l2l3RouterOrInbox, address to, uint256 amount ); /// @notice Thrown when the ETH value sent to teleport() does not equal the total ETH cost of retryables error IncorrectValue(uint256 required, uint256 provided); /// @notice Thrown when TeleportationType is OnlyCustomFee and the amount of fee tokens to send is less than the cost of the retryable to L3 error InsufficientFeeToken(uint256 required, uint256 provided); /// @notice Thrown when the SKIP_FEE_TOKEN magic is passed for l3FeeTokenL1Addr and at least one fee token related gas parameter is nonzero error NonZeroFeeTokenAmount(); /// @notice Start an L1 -> L3 transfer. msg.value sent must equal the total ETH cost of all retryables. /// Call `determineTypeAndFees` to calculate the total cost of retryables in ETH and the L3's fee token. /// If called by an EOA or a contract's constructor, the L2Forwarder will be owned by the caller's address, /// otherwise the L2Forwarder will be owned by the caller's alias. /// @dev 2 retryables will be created: one to send tokens and ETH to the L2Forwarder, and one to call the L2ForwarderFactory. /// If TeleportationType is NonFeeTokenToCustomFeeL3, a third retryable will be created to send the L3's fee token to the L2Forwarder. /// ETH used to pay for the L2 -> L3 retryable is sent through the l2CallValue of the call to the L2ForwarderFactory. /// Contracts that call teleport should have the ability to create retryables in case they need to call rescueFunds on the L2Forwarder. function teleport(TeleportParams calldata params) external payable; /// @notice Given some teleportation parameters, calculate the total cost of retryables in ETH and the L3's fee token. function determineTypeAndFees(TeleportParams calldata params) external view returns ( uint256 ethAmount, uint256 feeTokenAmount, TeleportationType teleportationType, RetryableGasCosts memory costs ); /// @notice Given some teleportation parameters, build the L2ForwarderParams for the L2ForwarderFactory. /// @dev If the caller address has no code, the owner is the caller address, /// otherwise the owner is the caller address's alias. function buildL2ForwarderParams(TeleportParams calldata params, address caller) external view returns (IL2Forwarder.L2ForwarderParams memory); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import {IL2ForwarderPredictor} from "./IL2ForwarderPredictor.sol"; /// @title IL2Forwarder /// @notice L2 contract that receives ERC20 tokens to forward to L3. /// May receive either token and ETH, token and the L3 feeToken, or just feeToken if token == feeToken. /// In case funds cannot be bridged to L3, the owner can call rescue to get their funds back. interface IL2Forwarder { /// @notice Parameters for an L2Forwarder /// @param owner Address of the L2Forwarder owner. Setting this incorrectly could result in loss of funds. /// @param l2Token Address of the L2 token to bridge to L3 /// @param l3FeeTokenL2Addr Address of the L3's fee token, or 0x00 for ETH /// @param routerOrInbox Address of the L2 -> L3 GatewayRouter or Inbox if depositing only custom fee token /// @param to Address of the recipient on L3 /// @param gasLimit Gas limit for the L2 -> L3 retryable /// @param gasPriceBid Gas price for the L2 -> L3 retryable /// @param maxSubmissionCost Max submission fee for the L2 -> L3 retryable. Is ignored for Standard and OnlyCustomFee teleportation types. struct L2ForwarderParams { address owner; address l2Token; address l3FeeTokenL2Addr; address routerOrInbox; address to; uint256 gasLimit; uint256 gasPriceBid; uint256 maxSubmissionCost; } /// @notice Emitted after a successful call to rescue /// @param targets Addresses that were called /// @param values Values that were sent /// @param datas Calldata that was sent event Rescued(address[] targets, uint256[] values, bytes[] datas); /// @notice Emitted after a successful call to bridgeToL3 event BridgedToL3(uint256 tokenAmount, uint256 feeAmount); /// @notice Thrown when initialize is called more than once error AlreadyInitialized(); /// @notice Thrown when a non-owner calls rescue error OnlyOwner(); /// @notice Thrown when the length of targets, values, and datas are not equal in a call to rescue error LengthMismatch(); /// @notice Thrown when an external call in rescue fails error CallFailed(address to, uint256 value, bytes data, bytes returnData); /// @notice Thrown when bridgeToL3 is called by an address other than the L2ForwarderFactory error OnlyL2ForwarderFactory(); /// @notice Thrown when the L2Forwarder has no balance of the token to bridge error ZeroTokenBalance(address token); /// @notice Initialize the L2Forwarder with the owner function initialize(address _owner) external; /// @notice Send tokens + (fee tokens or ETH) through the bridge to a recipient on L3. /// @param params Parameters of the bridge transaction. /// @dev Can only be called by the L2ForwarderFactory. function bridgeToL3(L2ForwarderParams calldata params) external payable; /// @notice Allows the owner of this L2Forwarder to make arbitrary calls. /// If bridgeToL3 cannot succeed, the owner can call this to rescue their tokens and ETH. /// @param targets Addresses to call /// @param values Values to send /// @param datas Calldata to send function rescue(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas) external payable; /// @notice The owner of this L2Forwarder. Authorized to call rescue. function owner() external view returns (address); /// @notice The address of the L2ForwarderFactory function l2ForwarderFactory() external view returns (address); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; enum TeleportationType { Standard, // Teleporting a token to an ETH fee L3 OnlyCustomFee, // Teleporting a L3's custom fee token to a custom (non-eth) fee L3 NonFeeTokenToCustomFee // Teleporting a non-fee token to a custom (non-eth) fee L3 } error InvalidTeleportation(); function toTeleportationType(address token, address feeToken) pure returns (TeleportationType) { if (token == address(0)) { revert InvalidTeleportation(); } else if (feeToken == address(0)) { return TeleportationType.Standard; } else if (token == feeToken) { return TeleportationType.OnlyCustomFee; } else { return TeleportationType.NonFeeTokenToCustomFee; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - 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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; abstract contract WhitelistConsumer { address public whitelist; event WhitelistSourceUpdated(address newSource); modifier onlyWhitelisted() { if (whitelist != address(0)) { require(Whitelist(whitelist).isAllowed(msg.sender), "NOT_WHITELISTED"); } _; } function updateWhitelistSource(address newSource) external { require(msg.sender == whitelist, "NOT_FROM_LIST"); whitelist = newSource; emit WhitelistSourceUpdated(newSource); } } contract Whitelist { address public owner; mapping(address => bool) public isAllowed; event OwnerUpdated(address newOwner); event WhitelistUpgraded(address newWhitelist, address[] targets); constructor() { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner, "ONLY_OWNER"); _; } function setOwner(address newOwner) external onlyOwner { owner = newOwner; emit OwnerUpdated(newOwner); } function setWhitelist(address[] memory user, bool[] memory val) external onlyOwner { require(user.length == val.length, "INVALID_INPUT"); for (uint256 i = 0; i < user.length; i++) { isAllowed[user[i]] = val[i]; } } // set new whitelist to address(0) to disable whitelist function triggerConsumers(address newWhitelist, address[] memory targets) external onlyOwner { for (uint256 i = 0; i < targets.length; i++) { WhitelistConsumer(targets[i]).updateWhitelistSource(newWhitelist); } emit WhitelistUpgraded(newWhitelist, targets); } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; interface ArbitrumEnabledToken { /// @notice should return `0xb1` if token is enabled for arbitrum gateways /// @dev Previous implmentation used to return `uint8(0xa4b1)`, however that causes compile time error in Solidity 0.8. due to type mismatch. /// In current version `uint8(0xb1)` shall be returned, which results in no change as that's the same value as truncated `uint8(0xa4b1)`. function isArbitrumEnabled() external view returns (uint8); } /** * @title Minimum expected interface for L1 custom token (see TestCustomTokenL1.sol for an example implementation) */ interface ICustomToken is ArbitrumEnabledToken { /** * @notice Should make an external call to EthERC20Bridge.registerCustomL2Token */ function registerTokenOnL2( address l2CustomTokenAddress, uint256 maxSubmissionCostForCustomBridge, uint256 maxSubmissionCostForRouter, uint256 maxGasForCustomBridge, uint256 maxGasForRouter, uint256 gasPriceBid, uint256 valueForGateway, uint256 valueForRouter, address creditBackAddress ) external payable; function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); function balanceOf(address account) external view returns (uint256); } interface L1MintableToken is ICustomToken { function bridgeMint(address account, uint256 amount) external; } interface L1ReverseToken is L1MintableToken { function bridgeBurn(address account, uint256 amount) external; }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; import "@arbitrum/nitro-contracts/src/bridge/IInbox.sol"; import "@arbitrum/nitro-contracts/src/bridge/IOutbox.sol"; /// @notice L1 utility contract to assist with L1 <=> L2 interactions /// @dev this is an abstract contract instead of library so the functions can be easily overriden when testing abstract contract L1ArbitrumMessenger { event TxToL2(address indexed _from, address indexed _to, uint256 indexed _seqNum, bytes _data); struct L2GasParams { uint256 _maxSubmissionCost; uint256 _maxGas; uint256 _gasPriceBid; } function sendTxToL2CustomRefund( address _inbox, address _to, address _refundTo, address _user, uint256 _l1CallValue, uint256 _l2CallValue, L2GasParams memory _l2GasParams, bytes memory _data ) internal returns (uint256) { // alternative function entry point when struggling with the stack size return sendTxToL2CustomRefund( _inbox, _to, _refundTo, _user, _l1CallValue, _l2CallValue, _l2GasParams._maxSubmissionCost, _l2GasParams._maxGas, _l2GasParams._gasPriceBid, _data ); } function sendTxToL2( address _inbox, address _to, address _user, uint256 _l1CallValue, uint256 _l2CallValue, L2GasParams memory _l2GasParams, bytes memory _data ) internal returns (uint256) { // alternative function entry point when struggling with the stack size return sendTxToL2( _inbox, _to, _user, _l1CallValue, _l2CallValue, _l2GasParams._maxSubmissionCost, _l2GasParams._maxGas, _l2GasParams._gasPriceBid, _data ); } function sendTxToL2CustomRefund( address _inbox, address _to, address _refundTo, address _user, uint256 _l1CallValue, uint256 _l2CallValue, uint256 _maxSubmissionCost, uint256 _maxGas, uint256 _gasPriceBid, bytes memory _data ) internal returns (uint256) { uint256 seqNum = _createRetryable( _inbox, _to, _refundTo, _user, _l1CallValue, _l2CallValue, _maxSubmissionCost, _maxGas, _gasPriceBid, _data ); emit TxToL2(_user, _to, seqNum, _data); return seqNum; } function sendTxToL2( address _inbox, address _to, address _user, uint256 _l1CallValue, uint256 _l2CallValue, uint256 _maxSubmissionCost, uint256 _maxGas, uint256 _gasPriceBid, bytes memory _data ) internal returns (uint256) { return sendTxToL2CustomRefund( _inbox, _to, _user, _user, _l1CallValue, _l2CallValue, _maxSubmissionCost, _maxGas, _gasPriceBid, _data ); } function getBridge(address _inbox) internal view returns (IBridge) { return IInbox(_inbox).bridge(); } /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies function getL2ToL1Sender(address _inbox) internal view returns (address) { IOutbox outbox = IOutbox(getBridge(_inbox).activeOutbox()); address l2ToL1Sender = outbox.l2ToL1Sender(); require(l2ToL1Sender != address(0), "NO_SENDER"); return l2ToL1Sender; } /** * @notice Calls inbox to create retryable ticket. Default implementation is for standard Eth-based rollup, but it can be overriden to create retryable in ERC20-based rollup. * @param _inbox address of the rollup's inbox * @param _to destination L2 contract address * @param _refundTo refund address for excess fee * @param _user refund address for callvalue * @param _totalFeeAmount amount of fees to pay, in Eth or native token, for retryable's execution * @param _l2CallValue call value for retryable L2 message * @param _maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee * @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution * @param _gasPriceBid price bid for L2 execution * @param _data ABI encoded data of L2 message * @return unique message number of the retryable transaction */ function _createRetryable( address _inbox, address _to, address _refundTo, address _user, uint256 _totalFeeAmount, uint256 _l2CallValue, uint256 _maxSubmissionCost, uint256 _maxGas, uint256 _gasPriceBid, bytes memory _data ) internal virtual returns (uint256) { return IInbox(_inbox).createRetryableTicket{ value: _totalFeeAmount }( _to, _l2CallValue, _maxSubmissionCost, _refundTo, _user, _maxGas, _gasPriceBid, _data ); } } interface IERC20Inbox { function createRetryableTicket( address to, uint256 l2CallValue, uint256 maxSubmissionCost, address excessFeeRefundAddress, address callValueRefundAddress, uint256 gasLimit, uint256 maxFeePerGas, uint256 tokenTotalFeeAmount, bytes calldata data ) external returns (uint256); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; import "../ProxyUtil.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "./TokenGateway.sol"; import "./GatewayMessageHandler.sol"; import "./IGatewayRouter.sol"; /** * @title Common interface for L1 and L2 Gateway Routers */ abstract contract GatewayRouter is TokenGateway, IGatewayRouter { using Address for address; address internal constant ZERO_ADDR = address(0); address internal constant DISABLED = address(1); mapping(address => address) public l1TokenToGateway; address public override defaultGateway; function postUpgradeInit() external { // it is assumed the L2 Arbitrum Gateway contract is behind a Proxy controlled by a proxy admin // this function can only be called by the proxy admin contract address proxyAdmin = ProxyUtil.getProxyAdmin(); require(msg.sender == proxyAdmin, "NOT_FROM_ADMIN"); // this has no other logic since the current upgrade doesn't require this logic } function _initialize( address _counterpartGateway, address _router, address _defaultGateway ) internal { // if you are a router, you can't have a router require(_router == address(0), "BAD_ROUTER"); TokenGateway._initialize(_counterpartGateway, _router); // default gateway can have 0 address defaultGateway = _defaultGateway; } function finalizeInboundTransfer( address, /* _token */ address, /* _from */ address, /* _to */ uint256, /* _amount */ bytes calldata /* _data */ ) external payable virtual override { revert("ONLY_OUTBOUND_ROUTER"); } function outboundTransfer( address _token, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes calldata _data ) public payable virtual override returns (bytes memory) { // this function is kept instead of delegating to outboundTransferCustomRefund to allow // compatibility with older gateways that did not implement outboundTransferCustomRefund address gateway = getGateway(_token); bytes memory gatewayData = GatewayMessageHandler.encodeFromRouterToGateway( msg.sender, _data ); emit TransferRouted(_token, msg.sender, _to, gateway); return ITokenGateway(gateway).outboundTransfer{ value: msg.value }( _token, _to, _amount, _maxGas, _gasPriceBid, gatewayData ); } function getOutboundCalldata( address _token, address _from, address _to, uint256 _amount, bytes memory _data ) public view virtual override returns (bytes memory) { address gateway = getGateway(_token); return TokenGateway(gateway).getOutboundCalldata(_token, _from, _to, _amount, _data); } function getGateway(address _token) public view virtual override returns (address gateway) { gateway = l1TokenToGateway[_token]; if (gateway == ZERO_ADDR) { // if no gateway value set, use default gateway gateway = defaultGateway; } if (gateway == DISABLED || !gateway.isContract()) { // not a valid gateway return ZERO_ADDR; } return gateway; } function calculateL2TokenAddress(address l1ERC20) public view virtual override(TokenGateway, ITokenGateway) returns (address) { address gateway = getGateway(l1ERC20); if (gateway == ZERO_ADDR) { return ZERO_ADDR; } return TokenGateway(gateway).calculateL2TokenAddress(l1ERC20); } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; import "../../libraries/gateway/GatewayRouter.sol"; import "../../ethereum/gateway/L1GatewayRouter.sol"; import "../L2ArbitrumMessenger.sol"; import "../../libraries/AddressAliasHelper.sol"; /** * @title Handles withdrawals from Ethereum into Arbitrum. Tokens are routered to their appropriate L2 gateway (Router itself also conforms to the Gateway interface). * @notice Router also serves as an L2-L1 token address oracle. */ contract L2GatewayRouter is GatewayRouter, L2ArbitrumMessenger { modifier onlyCounterpartGateway() override { require( msg.sender == AddressAliasHelper.applyL1ToL2Alias(counterpartGateway), "ONLY_COUNTERPART_GATEWAY" ); _; } function initialize(address _counterpartGateway, address _defaultGateway) public { GatewayRouter._initialize(_counterpartGateway, address(0), _defaultGateway); } function setGateway(address[] memory _l1Token, address[] memory _gateway) external onlyCounterpartGateway { // counterpart gateway (L1 router) should never allow wrong lengths assert(_l1Token.length == _gateway.length); for (uint256 i = 0; i < _l1Token.length; i++) { l1TokenToGateway[_l1Token[i]] = _gateway[i]; emit GatewaySet(_l1Token[i], _gateway[i]); } } function outboundTransfer( address _l1Token, address _to, uint256 _amount, bytes calldata _data ) public payable returns (bytes memory) { return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data); } function setDefaultGateway(address newL2DefaultGateway) external onlyCounterpartGateway { defaultGateway = newL2DefaultGateway; emit DefaultGatewayUpdated(newL2DefaultGateway); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) // With pragma modification to support ^0.6.11 // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.6/contracts/utils/introspection/ERC165.sol pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; import "../../libraries/gateway/ITokenGateway.sol"; import "../../libraries/IERC165.sol"; /** * @title Handles deposits from Erhereum into Arbitrum. Tokens are routered to their appropriate L1 gateway (Router itself also conforms to the Gateway itnerface). * @notice Router also serves as an L1-L2 token address oracle. */ interface IL1GatewayRouter is ITokenGateway, IERC165 { /** * @notice Deposit ERC20 token from Ethereum into Arbitrum using the registered or otherwise default gateway * @dev Some legacy gateway might not have the outboundTransferCustomRefund method and will revert, in such case use outboundTransfer instead * 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 _token 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 _token, address _refundTo, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes calldata _data ) external payable returns (bytes memory); /** * @notice Allows L1 Token contract to trustlessly register its gateway. * @param _gateway l1 gateway address * @param _maxGas max gas for L2 retryable exrecution * @param _gasPriceBid gas price for L2 retryable ticket * @param _maxSubmissionCost base submission cost L2 retryable tick3et * @param _creditBackAddress address for crediting back overpayment of _maxSubmissionCost * @return Retryable ticket ID */ function setGateway( address _gateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost, address _creditBackAddress ) external payable returns (uint256); /** * @notice Allows L1 Token contract to trustlessly register its gateway. (other setGateway method allows excess eth recovery from _maxSubmissionCost and is recommended) * @param _gateway l1 gateway address * @param _maxGas max gas for L2 retryable exrecution * @param _gasPriceBid gas price for L2 retryable ticket * @param _maxSubmissionCost base submission cost L2 retryable tick3et * @return Retryable ticket ID */ function setGateway( address _gateway, uint256 _maxGas, uint256 _gasPriceBid, uint256 _maxSubmissionCost ) external payable returns (uint256); function owner() external view returns (address); function inbox() external view returns (address); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; import "../../libraries/gateway/ITokenGateway.sol"; import "../../libraries/IERC165.sol"; /** * @title Common interface for gatways on L1 messaging to Arbitrum. */ interface IL1ArbitrumGateway is ITokenGateway, IERC165 { function inbox() external view returns (address); /** * @notice Deposit ERC20 token from Ethereum into Arbitrum. If L2 side hasn't been deployed yet, includes name/symbol/decimals data for initial L2 deploy. Initiate by GatewayRouter. * @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 */ // * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee function outboundTransferCustomRefund( address _l1Token, address _refundTo, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes calldata _data ) external payable returns (bytes memory); }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; import "./IOwnable.sol"; interface IBridge { event MessageDelivered( uint256 indexed messageIndex, bytes32 indexed beforeInboxAcc, address inbox, uint8 kind, address sender, bytes32 messageDataHash, uint256 baseFeeL1, uint64 timestamp ); event BridgeCallTriggered( address indexed outbox, address indexed to, uint256 value, bytes data ); event InboxToggle(address indexed inbox, bool enabled); event OutboxToggle(address indexed outbox, bool enabled); event SequencerInboxUpdated(address newSequencerInbox); event RollupUpdated(address rollup); function allowedDelayedInboxList(uint256) external returns (address); function allowedOutboxList(uint256) external returns (address); /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. function delayedInboxAccs(uint256) external view returns (bytes32); /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. function sequencerInboxAccs(uint256) external view returns (bytes32); function rollup() external view returns (IOwnable); function sequencerInbox() external view returns (address); function activeOutbox() external view returns (address); function allowedDelayedInboxes(address inbox) external view returns (bool); function allowedOutboxes(address outbox) external view returns (bool); function sequencerReportedSubMessageCount() external view returns (uint256); function executeCall( address to, uint256 value, bytes calldata data ) external returns (bool success, bytes memory returnData); function delayedMessageCount() external view returns (uint256); function sequencerMessageCount() external view returns (uint256); // ---------- onlySequencerInbox functions ---------- function enqueueSequencerMessage( bytes32 dataHash, uint256 afterDelayedMessagesRead, uint256 prevMessageCount, uint256 newMessageCount ) external returns ( uint256 seqMessageIndex, bytes32 beforeAcc, bytes32 delayedAcc, bytes32 acc ); /** * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type * This is done through a separate function entrypoint instead of allowing the sequencer inbox * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either * every delayed inbox or every sequencer inbox call. */ function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) external returns (uint256 msgNum); // ---------- onlyRollupOrOwner functions ---------- function setSequencerInbox(address _sequencerInbox) external; function setDelayedInbox(address inbox, bool enabled) external; function setOutbox(address inbox, bool enabled) external; function updateRollupAddress(IOwnable _rollup) external; }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; import "./IBridge.sol"; import "./IDelayedMessageProvider.sol"; import "./ISequencerInbox.sol"; interface IInboxBase is IDelayedMessageProvider { function bridge() external view returns (IBridge); function sequencerInbox() external view returns (ISequencerInbox); function maxDataSize() external view returns (uint256); /** * @notice Send a generic L2 message to the chain * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input * @param messageData Data of the message being sent */ function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256); /** * @notice Send a generic L2 message to the chain * @dev This method can be used to send any type of message that doesn't require L1 validation * @param messageData Data of the message being sent */ function sendL2Message(bytes calldata messageData) external returns (uint256); function sendUnsignedTransaction( uint256 gasLimit, uint256 maxFeePerGas, uint256 nonce, address to, uint256 value, bytes calldata data ) external returns (uint256); function sendContractTransaction( uint256 gasLimit, uint256 maxFeePerGas, address to, uint256 value, bytes calldata data ) external returns (uint256); /** * @notice Get the L1 fee for submitting a retryable * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value * @dev This formula may change in the future, to future proof your code query this method instead of inlining!! * @param dataLength The length of the retryable's calldata, in bytes * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used */ function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) external view returns (uint256); // ---------- onlyRollupOrOwner functions ---------- /// @notice pauses all inbox functionality function pause() external; /// @notice unpauses all inbox functionality function unpause() external; /// @notice add or remove users from allowList function setAllowList(address[] memory user, bool[] memory val) external; /// @notice enable or disable allowList function setAllowListEnabled(bool _allowListEnabled) external; /// @notice check if user is in allowList function isAllowed(address user) external view returns (bool); /// @notice check if allowList is enabled function allowListEnabled() external view returns (bool); function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external; /// @notice returns the current admin function getProxyAdmin() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol) pragma solidity ^0.8.0; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. * * _Available since v3.4._ */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(0, 0x09, 0x37) } require(instance != address(0), "ERC1167: create failed"); } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(0, 0x09, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt ) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import {IL2Forwarder} from "./IL2Forwarder.sol"; /// @title IL2ForwarderPredictor /// @notice Predicts the address of an L2Forwarder based on its parameters interface IL2ForwarderPredictor { /// @notice Address of the L2ForwarderFactory function l2ForwarderFactory() external view returns (address); /// @notice Address of the L2Forwarder implementation function l2ForwarderImplementation() external view returns (address); /// @notice Predicts the address of an L2Forwarder based on its owner, routerOrInbox, and to function l2ForwarderAddress(address owner, address routerOrInbox, address to) external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; import "./IBridge.sol"; interface IOutbox { event SendRootUpdated(bytes32 indexed outputRoot, bytes32 indexed l2BlockHash); event OutBoxTransactionExecuted( address indexed to, address indexed l2Sender, uint256 indexed zero, uint256 transactionIndex ); function initialize(IBridge _bridge) external; function rollup() external view returns (address); // the rollup contract function bridge() external view returns (IBridge); // the bridge contract function spent(uint256) external view returns (bytes32); // packed spent bitmap function roots(bytes32) external view returns (bytes32); // maps root hashes => L2 block hash // solhint-disable-next-line func-name-mixedcase function OUTBOX_VERSION() external view returns (uint128); // the outbox version function updateSendRoot(bytes32 sendRoot, bytes32 l2BlockHash) external; function updateRollupAddress() external; /// @notice When l2ToL1Sender returns a nonzero address, the message was originated by an L2 account /// When the return value is zero, that means this is a system message /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies function l2ToL1Sender() external view returns (address); /// @return l2Block return L2 block when the L2 tx was initiated or 0 if no L2 to L1 transaction is active function l2ToL1Block() external view returns (uint256); /// @return l1Block return L1 block when the L2 tx was initiated or 0 if no L2 to L1 transaction is active function l2ToL1EthBlock() external view returns (uint256); /// @return timestamp return L2 timestamp when the L2 tx was initiated or 0 if no L2 to L1 transaction is active function l2ToL1Timestamp() external view returns (uint256); /// @return outputId returns the unique output identifier of the L2 to L1 tx or 0 if no L2 to L1 transaction is active function l2ToL1OutputId() external view returns (bytes32); /** * @notice Executes a messages in an Outbox entry. * @dev Reverts if dispute period hasn't expired, since the outbox entry * is only created once the rollup confirms the respective assertion. * @dev it is not possible to execute any L2-to-L1 transaction which contains data * to a contract address without any code (as enforced by the Bridge contract). * @param proof Merkle proof of message inclusion in send root * @param index Merkle path to message * @param l2Sender sender if original message (i.e., caller of ArbSys.sendTxToL1) * @param to destination address for L1 contract call * @param l2Block l2 block number at which sendTxToL1 call was made * @param l1Block l1 block number at which sendTxToL1 call was made * @param l2Timestamp l2 Timestamp at which sendTxToL1 call was made * @param value wei in L1 message * @param data abi-encoded L1 message data */ function executeTransaction( bytes32[] calldata proof, uint256 index, address l2Sender, address to, uint256 l2Block, uint256 l1Block, uint256 l2Timestamp, uint256 value, bytes calldata data ) external; /** * @dev function used to simulate the result of a particular function call from the outbox * it is useful for things such as gas estimates. This function includes all costs except for * proof validation (which can be considered offchain as a somewhat of a fixed cost - it's * not really a fixed cost, but can be treated as so with a fixed overhead for gas estimation). * We can't include the cost of proof validation since this is intended to be used to simulate txs * that are included in yet-to-be confirmed merkle roots. The simulation entrypoint could instead pretend * to confirm a pending merkle root, but that would be less practical for integrating with tooling. * It is only possible to trigger it when the msg sender is address zero, which should be impossible * unless under simulation in an eth_call or eth_estimateGas */ function executeTransactionSimulation( uint256 index, address l2Sender, address to, uint256 l2Block, uint256 l1Block, uint256 l2Timestamp, uint256 value, bytes calldata data ) external; /** * @param index Merkle path to message * @return true if the message has been spent */ function isSpent(uint256 index) external view returns (bool); function calculateItemHash( address l2Sender, address to, uint256 l2Block, uint256 l1Block, uint256 l2Timestamp, uint256 value, bytes calldata data ) external pure returns (bytes32); function calculateMerkleRoot( bytes32[] memory proof, uint256 path, bytes32 item ) external pure returns (bytes32); /** * @dev function to be called one time during the outbox upgrade process * this is used to fix the storage slots */ function postUpgradeInit() external; }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; library ProxyUtil { function getProxyAdmin() internal view returns (address admin) { // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/proxy/TransparentUpgradeableProxy.sol#L48 // Storage slot with the admin of the proxy contract. // This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is bytes32 slot = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; assembly { admin := sload(slot) } } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; import "./ITokenGateway.sol"; import "@openzeppelin/contracts/utils/Address.sol"; abstract contract TokenGateway is ITokenGateway { using Address for address; address public counterpartGateway; address public router; // This modifier is overriden in gateways to validate the message sender // For L1 to L2 messages need to be validated against the aliased counterpartGateway // For L2 to L1 messages need to be validated against the bridge and L2ToL1Sender // prettier-ignore modifier onlyCounterpartGateway() virtual; function _initialize(address _counterpartGateway, address _router) internal virtual { // This initializes internal variables of the abstract contract it can be chained together with other functions. // It is virtual so subclasses can override or wrap around this logic. // An example where this is useful is different subclasses that validate the router address differently require(_counterpartGateway != address(0), "INVALID_COUNTERPART"); require(counterpartGateway == address(0), "ALREADY_INIT"); counterpartGateway = _counterpartGateway; router = _router; } function isRouter(address _target) internal view returns (bool isTargetRouter) { return _target == router; } /** * @notice Calculate the address used when bridging an ERC20 token * @dev the L1 and L2 address oracles may not always be in sync. * For example, a custom token may have been registered but not deploy or the contract self destructed. * @param l1ERC20 address of L1 token * @return L2 address of a bridged ERC20 token */ function calculateL2TokenAddress(address l1ERC20) public view virtual override returns (address); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; /// @notice this library manages encoding and decoding of gateway communication library GatewayMessageHandler { // these are for communication from L1 to L2 gateway function encodeToL2GatewayMsg(bytes memory gatewayData, bytes memory callHookData) internal pure returns (bytes memory res) { res = abi.encode(gatewayData, callHookData); } function parseFromL1GatewayMsg(bytes calldata _data) internal pure returns (bytes memory gatewayData, bytes memory callHookData) { // abi decode may revert, but the encoding is done by L1 gateway, so we trust it (gatewayData, callHookData) = abi.decode(_data, (bytes, bytes)); } // these are for communication from L2 to L1 gateway function encodeFromL2GatewayMsg(uint256 exitNum, bytes memory callHookData) internal pure returns (bytes memory res) { res = abi.encode(exitNum, callHookData); } function parseToL1GatewayMsg(bytes calldata _data) internal pure returns (uint256 exitNum, bytes memory callHookData) { // abi decode may revert, but the encoding is done by L1 gateway, so we trust it (exitNum, callHookData) = abi.decode(_data, (uint256, bytes)); } // these are for communication from router to gateway function encodeFromRouterToGateway(address _from, bytes calldata _data) internal pure returns (bytes memory res) { // abi decode may revert, but the encoding is done by L1 gateway, so we trust it return abi.encode(_from, _data); } function parseFromRouterToGateway(bytes calldata _data) internal pure returns (address, bytes memory res) { // abi decode may revert, but the encoding is done by L1 gateway, so we trust it return abi.decode(_data, (address, bytes)); } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; import "../ProxyUtil.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "./TokenGateway.sol"; import "./GatewayMessageHandler.sol"; /** * @title Common interface for L1 and L2 Gateway Routers */ interface IGatewayRouter is ITokenGateway { function defaultGateway() external view returns (address gateway); event TransferRouted( address indexed token, address indexed _userFrom, address indexed _userTo, address gateway ); event GatewaySet(address indexed l1Token, address indexed gateway); event DefaultGatewayUpdated(address newDefaultGateway); function getGateway(address _token) external view returns (address gateway); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; import "@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; /// @notice L2 utility contract to assist with L1 <=> L2 interactions /// @dev this is an abstract contract instead of library so the functions can be easily overriden when testing abstract contract L2ArbitrumMessenger { address internal constant ARB_SYS_ADDRESS = address(100); event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data); function sendTxToL1( uint256 _l1CallValue, address _from, address _to, bytes memory _data ) internal returns (uint256) { uint256 _id = ArbSys(ARB_SYS_ADDRESS).sendTxToL1{ value: _l1CallValue }(_to, _data); emit TxToL1(_from, _to, _id, _data); return _id; } }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2019-2021, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pragma solidity ^0.8.0; library AddressAliasHelper { uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); /// @notice Utility function that converts the address in the L1 that submitted a tx to /// the inbox to the msg.sender viewed in the L2 /// @param l1Address the address in the L1 that triggered the tx to L2 /// @return l2Address L2 address as viewed in msg.sender function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { unchecked { l2Address = address(uint160(l1Address) + offset); } } /// @notice Utility function that converts the msg.sender viewed in the L2 to the /// address in the L1 that submitted a tx to the inbox /// @param l2Address L2 address as viewed in msg.sender /// @return l1Address the address in the L1 that triggered the tx to L2 function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { unchecked { l1Address = address(uint160(l2Address) - offset); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) // With pragma modification to allow interface compatibility with >=0.6.9 <0.9.0 // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.6/contracts/utils/introspection/IERC165.sol // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: Apache-2.0 /* * Copyright 2020, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; interface ITokenGateway { /// @notice event deprecated in favor of DepositInitiated and WithdrawalInitiated // event OutboundTransferInitiated( // address token, // address indexed _from, // address indexed _to, // uint256 indexed _transferId, // uint256 _amount, // bytes _data // ); /// @notice event deprecated in favor of DepositFinalized and WithdrawalFinalized // event InboundTransferFinalized( // address token, // address indexed _from, // address indexed _to, // uint256 indexed _transferId, // uint256 _amount, // bytes _data // ); function outboundTransfer( address _token, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes calldata _data ) external payable returns (bytes memory); function finalizeInboundTransfer( address _token, address _from, address _to, uint256 _amount, bytes calldata _data ) external payable; /** * @notice Calculate the address used when bridging an ERC20 token * @dev the L1 and L2 address oracles may not always be in sync. * For example, a custom token may have been registered but not deploy or the contract self destructed. * @param l1ERC20 address of L1 token * @return L2 address of a bridged ERC20 token */ function calculateL2TokenAddress(address l1ERC20) external view returns (address); function getOutboundCalldata( address _token, address _from, address _to, uint256 _amount, bytes memory _data ) external view returns (bytes memory); }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.4.21 <0.9.0; interface IOwnable { function owner() external view returns (address); }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; interface IDelayedMessageProvider { /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator event InboxMessageDelivered(uint256 indexed messageNum, bytes data); /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator /// same as InboxMessageDelivered but the batch data is available in tx.input event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; pragma experimental ABIEncoderV2; import "../libraries/IGasRefunder.sol"; import "./IDelayedMessageProvider.sol"; import "./IBridge.sol"; interface ISequencerInbox is IDelayedMessageProvider { struct MaxTimeVariation { uint256 delayBlocks; uint256 futureBlocks; uint256 delaySeconds; uint256 futureSeconds; } struct TimeBounds { uint64 minTimestamp; uint64 maxTimestamp; uint64 minBlockNumber; uint64 maxBlockNumber; } enum BatchDataLocation { TxInput, SeparateBatchEvent, NoData } event SequencerBatchDelivered( uint256 indexed batchSequenceNumber, bytes32 indexed beforeAcc, bytes32 indexed afterAcc, bytes32 delayedAcc, uint256 afterDelayedMessagesRead, TimeBounds timeBounds, BatchDataLocation dataLocation ); event OwnerFunctionCalled(uint256 indexed id); /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data); /// @dev a valid keyset was added event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes); /// @dev a keyset was invalidated event InvalidateKeyset(bytes32 indexed keysetHash); function totalDelayedMessagesRead() external view returns (uint256); function bridge() external view returns (IBridge); /// @dev The size of the batch header // solhint-disable-next-line func-name-mixedcase function HEADER_LENGTH() external view returns (uint256); /// @dev If the first batch data byte after the header has this bit set, /// the sequencer inbox has authenticated the data. Currently not used. // solhint-disable-next-line func-name-mixedcase function DATA_AUTHENTICATED_FLAG() external view returns (bytes1); function rollup() external view returns (IOwnable); function isBatchPoster(address) external view returns (bool); function isSequencer(address) external view returns (bool); function maxDataSize() external view returns (uint256); struct DasKeySetInfo { bool isValidKeyset; uint64 creationBlock; } function maxTimeVariation() external view returns ( uint256, uint256, uint256, uint256 ); function dasKeySetInfo(bytes32) external view returns (bool, uint64); /// @notice Remove force inclusion delay after a L1 chainId fork function removeDelayAfterFork() external; /// @notice Force messages from the delayed inbox to be included in the chain /// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and /// maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these /// messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages. /// @param _totalDelayedMessagesRead The total number of messages to read up to /// @param kind The kind of the last message to be included /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included /// @param baseFeeL1 The l1 gas price of the last message to be included /// @param sender The sender of the last message to be included /// @param messageDataHash The messageDataHash of the last message to be included function forceInclusion( uint256 _totalDelayedMessagesRead, uint8 kind, uint64[2] calldata l1BlockAndTime, uint256 baseFeeL1, address sender, bytes32 messageDataHash ) external; function inboxAccs(uint256 index) external view returns (bytes32); function batchCount() external view returns (uint256); function isValidKeysetHash(bytes32 ksHash) external view returns (bool); /// @notice the creation block is intended to still be available after a keyset is deleted function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256); // ---------- BatchPoster functions ---------- function addSequencerL2BatchFromOrigin( uint256 sequenceNumber, bytes calldata data, uint256 afterDelayedMessagesRead, IGasRefunder gasRefunder ) external; function addSequencerL2Batch( uint256 sequenceNumber, bytes calldata data, uint256 afterDelayedMessagesRead, IGasRefunder gasRefunder, uint256 prevMessageCount, uint256 newMessageCount ) external; // ---------- onlyRollupOrOwner functions ---------- /** * @notice Set max delay for sequencer inbox * @param maxTimeVariation_ the maximum time variation parameters */ function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external; /** * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox * @param addr the address * @param isBatchPoster_ if the specified address should be authorized as a batch poster */ function setIsBatchPoster(address addr, bool isBatchPoster_) external; /** * @notice Makes Data Availability Service keyset valid * @param keysetBytes bytes of the serialized keyset */ function setValidKeyset(bytes calldata keysetBytes) external; /** * @notice Invalidates a Data Availability Service keyset * @param ksHash hash of the keyset */ function invalidateKeysetHash(bytes32 ksHash) external; /** * @notice Updates whether an address is authorized to be a sequencer. * @dev The IsSequencer information is used only off-chain by the nitro node to validate sequencer feed signer. * @param addr the address * @param isSequencer_ if the specified address should be authorized as a sequencer */ function setIsSequencer(address addr, bool isSequencer_) external; // ---------- initializer ---------- function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external; function updateRollupAddress() external; }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.4.21 <0.9.0; /** * @title System level functionality * @notice For use by contracts to interact with core L2-specific functionality. * Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. */ interface ArbSys { /** * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) * @return block number as int */ function arbBlockNumber() external view returns (uint256); /** * @notice Get Arbitrum block hash (reverts unless currentBlockNum-256 <= arbBlockNum < currentBlockNum) * @return block hash */ function arbBlockHash(uint256 arbBlockNum) external view returns (bytes32); /** * @notice Gets the rollup's unique chain identifier * @return Chain identifier as int */ function arbChainID() external view returns (uint256); /** * @notice Get internal version number identifying an ArbOS build * @return version number as int */ function arbOSVersion() external view returns (uint256); /** * @notice Returns 0 since Nitro has no concept of storage gas * @return uint 0 */ function getStorageGasAvailable() external view returns (uint256); /** * @notice (deprecated) check if current call is top level (meaning it was triggered by an EoA or a L1 contract) * @dev this call has been deprecated and may be removed in a future release * @return true if current execution frame is not a call by another L2 contract */ function isTopLevelCall() external view returns (bool); /** * @notice map L1 sender contract address to its L2 alias * @param sender sender address * @param unused argument no longer used * @return aliased sender address */ function mapL1SenderContractAddressToL2Alias(address sender, address unused) external pure returns (address); /** * @notice check if the caller (of this caller of this) is an aliased L1 contract address * @return true iff the caller's address is an alias for an L1 contract address */ function wasMyCallersAddressAliased() external view returns (bool); /** * @notice return the address of the caller (of this caller of this), without applying L1 contract address aliasing * @return address of the caller's caller, without applying L1 contract address aliasing */ function myCallersAddressWithoutAliasing() external view returns (address); /** * @notice Send given amount of Eth to dest from sender. * This is a convenience function, which is equivalent to calling sendTxToL1 with empty data. * @param destination recipient address on L1 * @return unique identifier for this L2-to-L1 transaction. */ function withdrawEth(address destination) external payable returns (uint256); /** * @notice Send a transaction to L1 * @dev it is not possible to execute on the L1 any L2-to-L1 transaction which contains data * to a contract address without any code (as enforced by the Bridge contract). * @param destination recipient address on L1 * @param data (optional) calldata for L1 contract call * @return a unique identifier for this L2-to-L1 transaction. */ function sendTxToL1(address destination, bytes calldata data) external payable returns (uint256); /** * @notice Get send Merkle tree state * @return size number of sends in the history * @return root root hash of the send history * @return partials hashes of partial subtrees in the send history tree */ function sendMerkleTreeState() external view returns ( uint256 size, bytes32 root, bytes32[] memory partials ); /** * @notice creates a send txn from L2 to L1 * @param position = (level << 192) + leaf = (0 << 192) + leaf = leaf */ event L2ToL1Tx( address caller, address indexed destination, uint256 indexed hash, uint256 indexed position, uint256 arbBlockNum, uint256 ethBlockNum, uint256 timestamp, uint256 callvalue, bytes data ); /// @dev DEPRECATED in favour of the new L2ToL1Tx event above after the nitro upgrade event L2ToL1Transaction( address caller, address indexed destination, uint256 indexed uniqueId, uint256 indexed batchNumber, uint256 indexInBatch, uint256 arbBlockNum, uint256 ethBlockNum, uint256 timestamp, uint256 callvalue, bytes data ); /** * @notice logs a merkle branch for proof synthesis * @param reserved an index meant only to align the 4th index with L2ToL1Transaction's 4th event * @param hash the merkle hash * @param position = (level << 192) + leaf */ event SendMerkleUpdate( uint256 indexed reserved, bytes32 indexed hash, uint256 indexed position ); error InvalidBlockNumber(uint256 requested, uint256 current); }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; interface IGasRefunder { function onGasSpent( address payable spender, uint256 gasUsed, uint256 calldataSize ) external returns (bool success); } abstract contract GasRefundEnabled { /// @dev this refunds the sender for execution costs of the tx /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded modifier refundsGas(IGasRefunder gasRefunder) { uint256 startGasLeft = gasleft(); _; if (address(gasRefunder) != address(0)) { uint256 calldataSize = msg.data.length; uint256 calldataWords = (calldataSize + 31) / 32; // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost startGasLeft += calldataWords * 6 + (calldataWords**2) / 512; // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input // solhint-disable-next-line avoid-tx-origin if (msg.sender != tx.origin) { // We can't be sure if this calldata came from the top level tx, // so to be safe we tell the gas refunder there was no calldata. calldataSize = 0; } gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize); } } }
{ "remappings": [ "@arbitrum/=node_modules/@arbitrum/", "@offchainlabs/=node_modules/@offchainlabs/", "@openzeppelin/=node_modules/@openzeppelin/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "hardhat/=node_modules/hardhat/" ], "optimizer": { "enabled": true, "runs": 20000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_l2ForwarderFactory","type":"address"},{"internalType":"address","name":"_l2ForwarderImplementation","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_pauser","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"}],"name":"IncorrectValue","type":"error"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"}],"name":"InsufficientFeeToken","type":"error"},{"inputs":[],"name":"InvalidTeleportation","type":"error"},{"inputs":[],"name":"NonZeroFeeTokenAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"l1Token","type":"address"},{"indexed":false,"internalType":"address","name":"l3FeeTokenL1Addr","type":"address"},{"indexed":false,"internalType":"address","name":"l1l2Router","type":"address"},{"indexed":false,"internalType":"address","name":"l2l3RouterOrInbox","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Teleported","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SKIP_FEE_TOKEN_MAGIC_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"l1Token","type":"address"},{"internalType":"address","name":"l3FeeTokenL1Addr","type":"address"},{"internalType":"address","name":"l1l2Router","type":"address"},{"internalType":"address","name":"l2l3RouterOrInbox","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint256","name":"l2GasPriceBid","type":"uint256"},{"internalType":"uint256","name":"l3GasPriceBid","type":"uint256"},{"internalType":"uint64","name":"l2ForwarderFactoryGasLimit","type":"uint64"},{"internalType":"uint64","name":"l1l2FeeTokenBridgeGasLimit","type":"uint64"},{"internalType":"uint64","name":"l1l2TokenBridgeGasLimit","type":"uint64"},{"internalType":"uint64","name":"l2l3TokenBridgeGasLimit","type":"uint64"},{"internalType":"uint256","name":"l2ForwarderFactoryMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l1l2FeeTokenBridgeMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l1l2TokenBridgeMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l2l3TokenBridgeMaxSubmissionCost","type":"uint256"}],"internalType":"struct IL1Teleporter.RetryableGasParams","name":"gasParams","type":"tuple"}],"internalType":"struct IL1Teleporter.TeleportParams","name":"params","type":"tuple"},{"internalType":"address","name":"caller","type":"address"}],"name":"buildL2ForwarderParams","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"l2Token","type":"address"},{"internalType":"address","name":"l3FeeTokenL2Addr","type":"address"},{"internalType":"address","name":"routerOrInbox","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"gasPriceBid","type":"uint256"},{"internalType":"uint256","name":"maxSubmissionCost","type":"uint256"}],"internalType":"struct IL2Forwarder.L2ForwarderParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"l1Token","type":"address"},{"internalType":"address","name":"l3FeeTokenL1Addr","type":"address"},{"internalType":"address","name":"l1l2Router","type":"address"},{"internalType":"address","name":"l2l3RouterOrInbox","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint256","name":"l2GasPriceBid","type":"uint256"},{"internalType":"uint256","name":"l3GasPriceBid","type":"uint256"},{"internalType":"uint64","name":"l2ForwarderFactoryGasLimit","type":"uint64"},{"internalType":"uint64","name":"l1l2FeeTokenBridgeGasLimit","type":"uint64"},{"internalType":"uint64","name":"l1l2TokenBridgeGasLimit","type":"uint64"},{"internalType":"uint64","name":"l2l3TokenBridgeGasLimit","type":"uint64"},{"internalType":"uint256","name":"l2ForwarderFactoryMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l1l2FeeTokenBridgeMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l1l2TokenBridgeMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l2l3TokenBridgeMaxSubmissionCost","type":"uint256"}],"internalType":"struct IL1Teleporter.RetryableGasParams","name":"gasParams","type":"tuple"}],"internalType":"struct IL1Teleporter.TeleportParams","name":"params","type":"tuple"}],"name":"determineTypeAndFees","outputs":[{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"},{"internalType":"enum TeleportationType","name":"teleportationType","type":"uint8"},{"components":[{"internalType":"uint256","name":"l1l2FeeTokenBridgeCost","type":"uint256"},{"internalType":"uint256","name":"l1l2TokenBridgeCost","type":"uint256"},{"internalType":"uint256","name":"l2ForwarderFactoryCost","type":"uint256"},{"internalType":"uint256","name":"l2l3TokenBridgeCost","type":"uint256"}],"internalType":"struct IL1Teleporter.RetryableGasCosts","name":"costs","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"routerOrInbox","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"l2ForwarderAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2ForwarderFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2ForwarderImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"l1Token","type":"address"},{"internalType":"address","name":"l3FeeTokenL1Addr","type":"address"},{"internalType":"address","name":"l1l2Router","type":"address"},{"internalType":"address","name":"l2l3RouterOrInbox","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint256","name":"l2GasPriceBid","type":"uint256"},{"internalType":"uint256","name":"l3GasPriceBid","type":"uint256"},{"internalType":"uint64","name":"l2ForwarderFactoryGasLimit","type":"uint64"},{"internalType":"uint64","name":"l1l2FeeTokenBridgeGasLimit","type":"uint64"},{"internalType":"uint64","name":"l1l2TokenBridgeGasLimit","type":"uint64"},{"internalType":"uint64","name":"l2l3TokenBridgeGasLimit","type":"uint64"},{"internalType":"uint256","name":"l2ForwarderFactoryMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l1l2FeeTokenBridgeMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l1l2TokenBridgeMaxSubmissionCost","type":"uint256"},{"internalType":"uint256","name":"l2l3TokenBridgeMaxSubmissionCost","type":"uint256"}],"internalType":"struct IL1Teleporter.RetryableGasParams","name":"gasParams","type":"tuple"}],"internalType":"struct IL1Teleporter.TeleportParams","name":"params","type":"tuple"}],"name":"teleport","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Deployed Bytecode
0x6080604052600436106101295760003560e01c806363c2f61e116100a5578063a217fddf11610074578063e63ab1e911610059578063e63ab1e914610413578063ec7d4abd14610447578063fbabf0841461047b57600080fd5b8063a217fddf146103de578063d547741f146103f357600080fd5b806363c2f61e1461033b5780638456cb591461034e5780639045f6d41461036357806391d148541461038b57600080fd5b80632f2ff15d116100fc578063377f017a116100e1578063377f017a146102b55780633f4ba83a1461030e5780635c975abb1461032357600080fd5b80632f2ff15d1461027357806336568abe1461029557600080fd5b806301ffc9a71461012e578063082486be146101635780631660853214610204578063248a9ca314610234575b600080fd5b34801561013a57600080fd5b5061014e61014936600461219b565b61049b565b60405190151581526020015b60405180910390f35b34801561016f57600080fd5b5061018361017e366004612218565b610534565b60405161015a919060006101008201905073ffffffffffffffffffffffffffffffffffffffff8084511683528060208501511660208401528060408501511660408401528060608501511660608401528060808501511660808401525060a083015160a083015260c083015160c083015260e083015160e083015292915050565b34801561021057600080fd5b5061022461021f366004612252565b61087a565b60405161015a949392919061229e565b34801561024057600080fd5b5061026561024f366004612317565b6000908152600160208190526040909120015490565b60405190815260200161015a565b34801561027f57600080fd5b5061029361028e366004612330565b610974565b005b3480156102a157600080fd5b506102936102b0366004612330565b61099f565b3480156102c157600080fd5b506102e97f000000000000000000000000791d2abc6c3a459e13b9adf54fb5e97b7af38f8781565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161015a565b34801561031a57600080fd5b50610293610a57565b34801561032f57600080fd5b5060005460ff1661014e565b610293610349366004612252565b610a8c565b34801561035a57600080fd5b50610293610cca565b34801561036f57600080fd5b506102e973ca040eea1dc95e969d9dc07af75147987c83089781565b34801561039757600080fd5b5061014e6103a6366004612330565b600091825260016020908152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b3480156103ea57600080fd5b50610265600081565b3480156103ff57600080fd5b5061029361040e366004612330565b610cfc565b34801561041f57600080fd5b506102657f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b34801561045357600080fd5b506102e97f000000000000000000000000d81257b21327b811799991003c752e6aae9b60d481565b34801561048757600080fd5b506102e9610496366004612355565b610d22565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b00000000000000000000000000000000000000000000000000000000148061052e57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810191909152600061058760608501604086016123a0565b73ffffffffffffffffffffffffffffffffffffffff1663a7e28d486105af60208701876123a0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015610618573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063c91906123bd565b90506000808061066761065260208901896123a0565b61066260408a0160208b016123a0565b610e13565b9050600081600281111561067d5761067d61226f565b0361068b576000925061077f565b600181600281111561069f5761069f61226f565b036106ac5783925061077f565b6106bc60608801604089016123a0565b73ffffffffffffffffffffffffffffffffffffffff1663a7e28d486106e760408a0160208b016123a0565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015610750573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077491906123bd565b92506101e087013591505b60405180610100016040528061079488610ec8565b73ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018860600160208101906107fa91906123a0565b73ffffffffffffffffffffffffffffffffffffffff16815260200161082560a08a0160808b016123a0565b73ffffffffffffffffffffffffffffffffffffffff1681526020016108526101808a016101608b016123da565b67ffffffffffffffff16815260e0890135602082015260400192909252509250505092915050565b60008060006108aa6040518060800160405280600081526020016000815260200160008152602001600081525090565b6108b385610f0a565b6108bf8560c001610ff5565b90506108e16108d160208701876123a0565b61066260408801602089016123a0565b9150806040015181602001516108f79190612433565b9350600082600281111561090d5761090d61226f565b036109285760608101516109219085612433565b935061096d565b600182600281111561093c5761093c61226f565b0361094d578060600151925061096d565b60608101511561096d5780516109639085612433565b9350806060015192505b9193509193565b600082815260016020819052604090912001546109908161110b565b61099a8383611115565b505050565b73ffffffffffffffffffffffffffffffffffffffff81163314610a49576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b610a5382826111d4565b5050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610a818161110b565b610a8961128f565b50565b610a9461130c565b600080600080610aa38561087a565b9350935093509350833414610aed576040517fb25102da00000000000000000000000000000000000000000000000000000000815260048101859052346024820152604401610a40565b6000610b1b610afb33610ec8565b610b0b6080890160608a016123a0565b61049660a08a0160808b016123a0565b90506001836002811115610b3157610b3161226f565b03610b8657838660a001351015610b81576040517fa0d383da0000000000000000000000000000000000000000000000000000000081526004810185905260a08701356024820152604401610a40565b610bf6565b6002836002811115610b9a57610b9a61226f565b03610bf6578315610bf657610bf6610bb860608801604089016123a0565b610bc86040890160208a016123a0565b8387610bdc6101408c016101208d016123da565b67ffffffffffffffff1660c08c01356101a08d013561137b565b610c0186838361160d565b337f762fcae5372ec0a8b89250dca23af574afcad1e9db4425b0a2d9e8f9e8a64ad0610c3060208901896123a0565b610c4060408a0160208b016123a0565b610c5060608b0160408c016123a0565b610c6060808c0160608d016123a0565b610c7060a08d0160808e016123a0565b6040805173ffffffffffffffffffffffffffffffffffffffff96871681529486166020860152928516848401529084166060840152909216608082015260a08a8101359082015290519081900360c00190a2505050505050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610cf48161110b565b610a896118cd565b60008281526001602081905260409091200154610d188161110b565b61099a83836111d4565b6040805173ffffffffffffffffffffffffffffffffffffffff8581166020808401919091528582168385015290841660608084019190915283518084039091018152608083019384905280519101207f000000000000000000000000791d2abc6c3a459e13b9adf54fb5e97b7af38f8760b88301526f5af43d82803e903d91602b57fd5bf3ff60a48301527f000000000000000000000000d81257b21327b811799991003c752e6aae9b60d46094830152733d602d80600a3d3981f3363d3d373d3d3d363d7390925260d88101919091526037608c82012060f8820152605560c3909101206000905b949350505050565b600073ffffffffffffffffffffffffffffffffffffffff8316610e62576040517f83fe0edc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216610e855750600061052e565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610ec05750600161052e565b50600261052e565b6000808273ffffffffffffffffffffffffffffffffffffffff163b11610eee578161052e565b731111000000000000000000000000000000001111820161052e565b73ca040eea1dc95e969d9dc07af75147987c830897610f2f60408301602084016123a0565b73ffffffffffffffffffffffffffffffffffffffff16148015610fbe57506101e0810135151580610f7b57506000610f6f610180830161016084016123da565b67ffffffffffffffff16115b80610fa157506000610f95610140830161012084016123da565b67ffffffffffffffff16115b80610fb057506101a081013515155b80610fbe575060e081013515155b15610a89576040517f870e439500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110206040518060800160405280600081526020016000815260200160008152602001600081525090565b813561103260808401606085016123da565b67ffffffffffffffff166110469190612446565b6110549060e0840135612433565b8152813561106860a08401608085016123da565b67ffffffffffffffff1661107c9190612446565b61108b90610100840135612433565b602082015281356110a260608401604085016123da565b67ffffffffffffffff166110b69190612446565b6110c49060c0840135612433565b604082015260208201356110de60c0840160a085016123da565b67ffffffffffffffff166110f29190612446565b61110190610120840135612433565b6060820152919050565b610a898133611928565b600082815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610a5357600082815260016020818152604080842073ffffffffffffffffffffffffffffffffffffffff8616808652925280842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b600082815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1615610a5357600082815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6112976119e2565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b60005460ff1615611379576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610a40565b565b61139d73ffffffffffffffffffffffffffffffffffffffff8716333087611a4e565b6040517fbda009fe00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87811660048301526000919089169063bda009fe90602401602060405180830381865afa15801561140d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061143191906123bd565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192509088169063dd62ed3e90604401602060405180830381865afa1580156114a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114cb919061245d565b6000036115135761151373ffffffffffffffffffffffffffffffffffffffff8816827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611b30565b73ffffffffffffffffffffffffffffffffffffffff8816634fb1a07b8361153a8688612446565b6115449190612433565b89898a8a8a8a8a6040518060200160405280600081525060405160200161156c9291906124e4565b6040516020818303038152906040526040518963ffffffff1660e01b815260040161159d97969594939291906124fd565b60006040518083038185885af11580156115bb573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611602919081019061258c565b505050505050505050565b61165f61162060608501604086016123a0565b61162d60208601866123a0565b8360a087013561164561016089016101408a016123da565b67ffffffffffffffff1660c08901356101c08a013561137b565b600061167160608501604086016123a0565b73ffffffffffffffffffffffffffffffffffffffff1663fb0e722b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116df91906123bd565b90508073ffffffffffffffffffffffffffffffffffffffff1663679b6ded477f000000000000000000000000791d2abc6c3a459e13b9adf54fb5e97b7af38f8786604001514761172f919061264c565b61018089013587806117496101208d016101008e016123da565b60c08d01356117588e33610534565b60408051825173ffffffffffffffffffffffffffffffffffffffff90811660248301526020840151811660448301529183015182166064820152606083015182166084820152608083015190911660a482015260a082015160c482015260c082015160e482015260e09091015161010482015261012401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f27a01aa600000000000000000000000000000000000000000000000000000000179052517fffffffff0000000000000000000000000000000000000000000000000000000060e08c901b16815261188398979695949392919060040161265f565b60206040518083038185885af11580156118a1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906118c6919061245d565b5050505050565b6118d561130c565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586112e23390565b600082815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16610a535761196881611cb2565b611973836020611cd1565b6040516020016119849291906126d2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252610a4091600401612753565b60005460ff16611379576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610a40565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052611b2a9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611f1b565b50505050565b801580611bd057506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bce919061245d565b155b611c5c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610a40565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905261099a9084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401611aa8565b606061052e73ffffffffffffffffffffffffffffffffffffffff831660145b60606000611ce0836002612446565b611ceb906002612433565b67ffffffffffffffff811115611d0357611d0361255d565b6040519080825280601f01601f191660200182016040528015611d2d576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611d6457611d64612766565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611dc757611dc7612766565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000611e03846002612446565b611e0e906001612433565b90505b6001811115611eab577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110611e4f57611e4f612766565b1a60f81b828281518110611e6557611e65612766565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93611ea481612795565b9050611e11565b508315611f14576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610a40565b9392505050565b6000611f7d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661202a9092919063ffffffff16565b9050805160001480611f9e575080806020019051810190611f9e91906127ca565b61099a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610a40565b6060610e0b8484600085856000808673ffffffffffffffffffffffffffffffffffffffff16858760405161205e91906127ec565b60006040518083038185875af1925050503d806000811461209b576040519150601f19603f3d011682016040523d82523d6000602084013e6120a0565b606091505b50915091506120b1878383876120bc565b979650505050505050565b6060831561215257825160000361214b5773ffffffffffffffffffffffffffffffffffffffff85163b61214b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a40565b5081610e0b565b610e0b83838151156121675781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a409190612753565b6000602082840312156121ad57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611f1457600080fd5b600061020082840312156121f057600080fd5b50919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610a8957600080fd5b600080610220838503121561222c57600080fd5b61223684846121dd565b9150610200830135612247816121f6565b809150509250929050565b6000610200828403121561226557600080fd5b611f1483836121dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8481526020810184905260e08101600384106122e3577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8360408301528251606083015260208301516080830152604083015160a0830152606083015160c083015295945050505050565b60006020828403121561232957600080fd5b5035919050565b6000806040838503121561234357600080fd5b823591506020830135612247816121f6565b60008060006060848603121561236a57600080fd5b8335612375816121f6565b92506020840135612385816121f6565b91506040840135612395816121f6565b809150509250925092565b6000602082840312156123b257600080fd5b8135611f14816121f6565b6000602082840312156123cf57600080fd5b8151611f14816121f6565b6000602082840312156123ec57600080fd5b813567ffffffffffffffff81168114611f1457600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561052e5761052e612404565b808202811582820484141761052e5761052e612404565b60006020828403121561246f57600080fd5b5051919050565b60005b83811015612491578181015183820152602001612479565b50506000910152565b600081518084526124b2816020860160208601612476565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b828152604060208201526000610e0b604083018461249a565b600073ffffffffffffffffffffffffffffffffffffffff808a16835280891660208401528088166040840152508560608301528460808301528360a083015260e060c083015261255060e083018461249a565b9998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561259e57600080fd5b815167ffffffffffffffff808211156125b657600080fd5b818401915084601f8301126125ca57600080fd5b8151818111156125dc576125dc61255d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156126225761262261255d565b8160405282815287602084870101111561263b57600080fd5b6120b1836020830160208801612476565b8181038181111561052e5761052e612404565b600061010073ffffffffffffffffffffffffffffffffffffffff808c1684528a6020850152896040850152808916606085015280881660808501525067ffffffffffffffff861660a08401528460c08401528060e08401526126c38184018561249a565b9b9a5050505050505050505050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161270a816017850160208801612476565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351612747816028840160208801612476565b01602801949350505050565b602081526000611f14602083018461249a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000816127a4576127a4612404565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b6000602082840312156127dc57600080fd5b81518015158114611f1457600080fd5b600082516127fe818460208701612476565b919091019291505056fea264697066735822122063fc28eab6a30cb472864d599575e5a63171bcc79cc80f819029784ced81d42664736f6c63430008170033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.