Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/tokenbridge/arbitrum/L2AtomicTokenBridgeFactory.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.4;
import {L2GatewayRouter} from "./gateway/L2GatewayRouter.sol";
import {L2ERC20Gateway} from "./gateway/L2ERC20Gateway.sol";
import {L2CustomGateway} from "./gateway/L2CustomGateway.sol";
import {L2WethGateway} from "./gateway/L2WethGateway.sol";
import {StandardArbERC20} from "./StandardArbERC20.sol";
import {IUpgradeExecutor} from "@offchainlabs/upgrade-executor/src/IUpgradeExecutor.sol";
import {CreationCodeHelper} from "../libraries/CreationCodeHelper.sol";
import {BeaconProxyFactory} from "../libraries/ClonableBeaconProxy.sol";
import {aeWETH} from "../libraries/aeWETH.sol";
import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import {
TransparentUpgradeableProxy,
ITransparentUpgradeableProxy
} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
/**
* @title Layer2 token bridge creator
* @notice This contract is used to deploy token bridge on L2 chain.
* @dev L1AtomicTokenBridgeCreator shall call `deployL2Contracts` using retryable and that will result in deployment of canonical token bridge contracts.
*/
contract L2AtomicTokenBridgeFactory {
error L2AtomicTokenBridgeFactory_AlreadyExists();
// In order to avoid having uninitialized logic contracts, `initialize` function will be called
// on all logic contracts which don't have initializers disabled. This dummy non-zero address
// will be provided to those initializers, as values written to the logic contract's storage
// are not used.
address private constant ADDRESS_DEAD = address(0x000000000000000000000000000000000000dEaD);
function deployL2Contracts(
L2RuntimeCode calldata l2Code,
address l1Router,
address l1StandardGateway,
address l1CustomGateway,
address l1WethGateway,
address l1Weth,
address l2StandardGatewayCanonicalAddress,
address rollupOwner,
address aliasedL1UpgradeExecutor
) external {
// Create proxyAdmin which will be used for all contracts. Revert if canonical deployment already exists
{
address proxyAdminAddress = Create2.computeAddress(
_getL2Salt(OrbitSalts.L2_PROXY_ADMIN),
keccak256(type(ProxyAdmin).creationCode),
address(this)
);
if (proxyAdminAddress.code.length > 0) {
revert L2AtomicTokenBridgeFactory_AlreadyExists();
}
}
address proxyAdmin = address(new ProxyAdmin{salt: _getL2Salt(OrbitSalts.L2_PROXY_ADMIN)}());
// deploy router/gateways/executor
address upgradeExecutor = _deployUpgradeExecutor(
l2Code.upgradeExecutor, rollupOwner, proxyAdmin, aliasedL1UpgradeExecutor
);
address router =
_deployRouter(l2Code.router, l1Router, l2StandardGatewayCanonicalAddress, proxyAdmin);
_deployStandardGateway(
l2Code.standardGateway, l1StandardGateway, router, proxyAdmin, upgradeExecutor
);
_deployCustomGateway(l2Code.customGateway, l1CustomGateway, router, proxyAdmin);
// fee token based creator will provide address(0) as WETH is not used in ERC20-based chains
if (l1WethGateway != address(0)) {
_deployWethGateway(
l2Code.wethGateway, l2Code.aeWeth, l1WethGateway, l1Weth, router, proxyAdmin
);
}
// deploy multicall
Create2.deploy(
0,
_getL2Salt(OrbitSalts.L2_MULTICALL),
CreationCodeHelper.getCreationCodeFor(l2Code.multicall)
);
// transfer ownership to L2 upgradeExecutor
ProxyAdmin(proxyAdmin).transferOwnership(upgradeExecutor);
}
function _deployUpgradeExecutor(
bytes calldata runtimeCode,
address rollupOwner,
address proxyAdmin,
address aliasedL1UpgradeExecutor
) internal returns (address) {
// canonical L2 upgrade executor with dummy logic
address canonicalUpgradeExecutor = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_EXECUTOR);
// Create UpgradeExecutor logic and upgrade to it.
address upExecutorLogic = Create2.deploy(
0,
_getL2Salt(OrbitSalts.L2_EXECUTOR),
CreationCodeHelper.getCreationCodeFor(runtimeCode)
);
ProxyAdmin(proxyAdmin).upgrade(
ITransparentUpgradeableProxy(canonicalUpgradeExecutor), upExecutorLogic
);
// init logic contract with dummy values
address[] memory empty = new address[](0);
IUpgradeExecutor(upExecutorLogic).initialize(ADDRESS_DEAD, empty);
// init upgrade executor
address[] memory executors = new address[](2);
executors[0] = rollupOwner;
executors[1] = aliasedL1UpgradeExecutor;
IUpgradeExecutor(canonicalUpgradeExecutor).initialize(canonicalUpgradeExecutor, executors);
return canonicalUpgradeExecutor;
}
function _deployRouter(
bytes calldata runtimeCode,
address l1Router,
address l2StandardGatewayCanonicalAddress,
address proxyAdmin
) internal returns (address) {
// canonical L2 router with dummy logic
address canonicalRouter = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_ROUTER);
// create L2 router logic and upgrade
address routerLogic = Create2.deploy(
0, _getL2Salt(OrbitSalts.L2_ROUTER), CreationCodeHelper.getCreationCodeFor(runtimeCode)
);
ProxyAdmin(proxyAdmin).upgrade(ITransparentUpgradeableProxy(canonicalRouter), routerLogic);
// init logic contract with dummy values.
L2GatewayRouter(routerLogic).initialize(ADDRESS_DEAD, ADDRESS_DEAD);
// init
L2GatewayRouter(canonicalRouter).initialize(l1Router, l2StandardGatewayCanonicalAddress);
return canonicalRouter;
}
function _deployStandardGateway(
bytes calldata runtimeCode,
address l1StandardGateway,
address router,
address proxyAdmin,
address upgradeExecutor
) internal {
// canonical L2 standard gateway with dummy logic
address canonicalStdGateway = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_STANDARD_GATEWAY);
// create L2 standard gateway logic and upgrade
address stdGatewayLogic = Create2.deploy(
0,
_getL2Salt(OrbitSalts.L2_STANDARD_GATEWAY),
CreationCodeHelper.getCreationCodeFor(runtimeCode)
);
ProxyAdmin(proxyAdmin).upgrade(
ITransparentUpgradeableProxy(canonicalStdGateway), stdGatewayLogic
);
// init logic contract with dummy values
L2ERC20Gateway(stdGatewayLogic).initialize(ADDRESS_DEAD, ADDRESS_DEAD, ADDRESS_DEAD);
// create beacon
StandardArbERC20 standardArbERC20 =
new StandardArbERC20{salt: _getL2Salt(OrbitSalts.BEACON_PROXY_FACTORY)}();
UpgradeableBeacon beacon = new UpgradeableBeacon{
salt: _getL2Salt(OrbitSalts.BEACON_PROXY_FACTORY)
}(address(standardArbERC20));
BeaconProxyFactory beaconProxyFactory =
new BeaconProxyFactory{salt: _getL2Salt(OrbitSalts.BEACON_PROXY_FACTORY)}();
// init contracts
beaconProxyFactory.initialize(address(beacon));
L2ERC20Gateway(canonicalStdGateway).initialize(
l1StandardGateway, router, address(beaconProxyFactory)
);
// make L2 executor the beacon owner
beacon.transferOwnership(upgradeExecutor);
}
function _deployCustomGateway(
bytes calldata runtimeCode,
address l1CustomGateway,
address router,
address proxyAdmin
) internal {
// canonical L2 custom gateway with dummy logic
address canonicalCustomGateway = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_CUSTOM_GATEWAY);
// create L2 custom gateway logic and upgrade
address customGatewayLogicAddress = Create2.deploy(
0,
_getL2Salt(OrbitSalts.L2_CUSTOM_GATEWAY),
CreationCodeHelper.getCreationCodeFor(runtimeCode)
);
ProxyAdmin(proxyAdmin).upgrade(
ITransparentUpgradeableProxy(canonicalCustomGateway), customGatewayLogicAddress
);
// init logic contract with dummy values
L2CustomGateway(customGatewayLogicAddress).initialize(ADDRESS_DEAD, ADDRESS_DEAD);
// init
L2CustomGateway(canonicalCustomGateway).initialize(l1CustomGateway, router);
}
function _deployWethGateway(
bytes calldata wethGatewayRuntimeCode,
bytes calldata aeWethRuntimeCode,
address l1WethGateway,
address l1Weth,
address router,
address proxyAdmin
) internal {
// canonical L2 WETH with dummy logic
address canonicalL2Weth = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_WETH);
// Create L2WETH logic and upgrade
address l2WethLogic = Create2.deploy(
0,
_getL2Salt(OrbitSalts.L2_WETH),
CreationCodeHelper.getCreationCodeFor(aeWethRuntimeCode)
);
ProxyAdmin(proxyAdmin).upgrade(ITransparentUpgradeableProxy(canonicalL2Weth), l2WethLogic);
// canonical L2 WETH gateway with dummy logic
address canonicalL2WethGateway = _deploySeedProxy(proxyAdmin, OrbitSalts.L2_WETH_GATEWAY);
// create L2WETH gateway logic and upgrade
address l2WethGatewayLogic = Create2.deploy(
0,
_getL2Salt(OrbitSalts.L2_WETH_GATEWAY),
CreationCodeHelper.getCreationCodeFor(wethGatewayRuntimeCode)
);
ProxyAdmin(proxyAdmin).upgrade(
ITransparentUpgradeableProxy(canonicalL2WethGateway), l2WethGatewayLogic
);
// init logic contract with dummy values
L2WethGateway(payable(l2WethGatewayLogic)).initialize(
ADDRESS_DEAD, ADDRESS_DEAD, ADDRESS_DEAD, ADDRESS_DEAD
);
// init gateway
L2WethGateway(payable(canonicalL2WethGateway)).initialize(
l1WethGateway, router, l1Weth, address(canonicalL2Weth)
);
// init logic contract with dummy values
aeWETH(payable(l2WethLogic)).initialize("", "", 0, ADDRESS_DEAD, ADDRESS_DEAD);
// init L2Weth
aeWETH(payable(canonicalL2Weth)).initialize(
"WETH", "WETH", 18, canonicalL2WethGateway, l1Weth
);
}
/**
* In addition to hard-coded prefix, salt for L2 contracts depends on msg.sender and the chainId. Deploying L2 token bridge contracts is
* permissionless. By making msg.sender part of the salt we know exactly which set of contracts is the "canonical" one for given chain,
* deployed by L1TokenBridgeRetryableSender via retryable ticket.
*/
function _getL2Salt(bytes memory prefix) internal view returns (bytes32) {
return keccak256(abi.encodePacked(prefix, block.chainid, msg.sender));
}
/**
* Deploys a proxy with address(this) as logic in order to get deterministic address
* the proxy is salted using a salt derived from the prefix, the chainId and the sender
*/
function _deploySeedProxy(address proxyAdmin, bytes memory prefix) internal returns (address) {
return address(
new TransparentUpgradeableProxy{salt: _getL2Salt(prefix)}(
address(this), proxyAdmin, bytes("")
)
);
}
}
/**
* Placeholder for bytecode of token bridge contracts which is sent from L1 to L2 through retryable ticket.
*/
struct L2RuntimeCode {
bytes router;
bytes standardGateway;
bytes customGateway;
bytes wethGateway;
bytes aeWeth;
bytes upgradeExecutor;
bytes multicall;
}
/**
* Collection of salts used in CREATE2 deployment of L2 token bridge contracts.
* Logic contracts are deployed using the same salt as the proxy, it's fine as they have different code
*/
library OrbitSalts {
bytes internal constant L1_ROUTER = bytes("L1R");
bytes internal constant L1_STANDARD_GATEWAY = bytes("L1SGW");
bytes internal constant L1_CUSTOM_GATEWAY = bytes("L1CGW");
bytes internal constant L1_WETH_GATEWAY = bytes("L1WGW");
bytes internal constant L2_PROXY_ADMIN = bytes("L2PA");
bytes internal constant L2_ROUTER = bytes("L2R");
bytes internal constant L2_STANDARD_GATEWAY = bytes("L2SGW");
bytes internal constant L2_CUSTOM_GATEWAY = bytes("L2CGW");
bytes internal constant L2_WETH_GATEWAY = bytes("L2WGW");
bytes internal constant L2_WETH = bytes("L2W");
bytes internal constant BEACON_PROXY_FACTORY = bytes("L2BPF");
bytes internal constant L2_EXECUTOR = bytes("L2E");
bytes internal constant L2_MULTICALL = bytes("L2MC");
}
"
},
"contracts/tokenbridge/arbitrum/gateway/L2GatewayRouter.sol": {
"content": "// 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);
}
}
"
},
"contracts/tokenbridge/arbitrum/gateway/L2ERC20Gateway.sol": {
"content": "// 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 "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "./L2ArbitrumGateway.sol";
import "../StandardArbERC20.sol";
import "../../libraries/ClonableBeaconProxy.sol";
contract L2ERC20Gateway is L2ArbitrumGateway {
address public beaconProxyFactory;
function initialize(
address _l1Counterpart,
address _router,
address _beaconProxyFactory
) public {
L2ArbitrumGateway._initialize(_l1Counterpart, _router);
require(_beaconProxyFactory != address(0), "INVALID_BEACON");
beaconProxyFactory = _beaconProxyFactory;
}
/**
* @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)
{
// this method is marked virtual to be overriden in subclasses used in testing
return
BeaconProxyFactory(beaconProxyFactory).calculateExpectedAddress(
address(this),
getUserSalt(l1ERC20)
);
}
function cloneableProxyHash() public view returns (bytes32) {
return BeaconProxyFactory(beaconProxyFactory).cloneableProxyHash();
}
function getUserSalt(address l1ERC20) public pure returns (bytes32) {
return keccak256(abi.encode(l1ERC20));
}
/**
* @notice internal utility function used to deploy ERC20 tokens with the beacon proxy pattern.
* @dev the transparent proxy implementation by OpenZeppelin can't be used if we want to be able to
* upgrade the token logic.
* @param l1ERC20 L1 address of ERC20
* @param expectedL2Address L2 address of ERC20
* @param deployData encoded symbol/name/decimal data for initial deploy
*/
function handleNoContract(
address l1ERC20,
address expectedL2Address,
address _from,
address, /* _to */
uint256 _amount,
bytes memory deployData
) internal override returns (bool shouldHalt) {
bytes32 userSalt = getUserSalt(l1ERC20);
address createdContract = BeaconProxyFactory(beaconProxyFactory).createProxy(userSalt);
StandardArbERC20(createdContract).bridgeInit(l1ERC20, deployData);
if (createdContract == expectedL2Address) {
return false;
} else {
// trigger withdrawal then halt
// this codepath should only be hit if the system is setup incorrectly
// this withdrawal is for error recovery, not composing with L2 dapps, so we ignore the return value
triggerWithdrawal(l1ERC20, address(this), _from, _amount, "");
return true;
}
}
}
"
},
"contracts/tokenbridge/arbitrum/gateway/L2CustomGateway.sol": {
"content": "// 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 "./L2ArbitrumGateway.sol";
import "../../libraries/gateway/ICustomGateway.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract L2CustomGateway is L2ArbitrumGateway, ICustomGateway {
// stores addresses of L2 tokens to be used
mapping(address => address) public override l1ToL2Token;
function initialize(address _l1Counterpart, address _router) public {
L2ArbitrumGateway._initialize(_l1Counterpart, _router);
}
/**
* @notice internal utility function used to handle when no contract is deployed at expected address
*/
function handleNoContract(
address _l1Token,
address, /* expectedL2Address */
address _from,
address, /* _to */
uint256 _amount,
bytes memory /* gatewayData */
) internal override returns (bool shouldHalt) {
// it is assumed that the custom token is deployed in the L2 before deposits are made
// trigger withdrawal
// we don't need the return value from triggerWithdrawal since this is forcing a withdrawal back to the L1
// instead of composing with a L2 dapp
triggerWithdrawal(_l1Token, address(this), _from, _amount, "");
return true;
}
/**
* @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 override returns (address) {
return l1ToL2Token[l1ERC20];
}
function registerTokenFromL1(address[] calldata l1Address, address[] calldata l2Address)
external
onlyCounterpartGateway
{
// we assume both arrays are the same length, safe since its encoded by the L1
for (uint256 i = 0; i < l1Address.length; i++) {
// here we don't check if l2Address is a contract and instead deal with that behaviour
// in `handleNoContract` this way we keep the l1 and l2 address oracles in sync
l1ToL2Token[l1Address[i]] = l2Address[i];
emit TokenSet(l1Address[i], l2Address[i]);
}
}
}
"
},
"contracts/tokenbridge/arbitrum/gateway/L2WethGateway.sol": {
"content": "// 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 "./L2ArbitrumGateway.sol";
import "../../libraries/IWETH9.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract L2WethGateway is L2ArbitrumGateway {
using SafeERC20 for IERC20;
address public l1Weth;
address public l2Weth;
function initialize(
address _l1Counterpart,
address _router,
address _l1Weth,
address _l2Weth
) public {
L2ArbitrumGateway._initialize(_l1Counterpart, _router);
require(_l1Weth != address(0), "INVALID_L1WETH");
require(_l2Weth != address(0), "INVALID_L2WETH");
l1Weth = _l1Weth;
l2Weth = _l2Weth;
}
/**
* @notice internal utility function used to handle when no contract is deployed at expected address
* @param l1ERC20 L1 address of ERC20
*/
function handleNoContract(
address l1ERC20,
address, /* expectedL2Address */
address _from,
address, /* _to */
uint256 _amount,
bytes memory /* deployData */
) internal override returns (bool shouldHalt) {
// it is assumed that the custom token is deployed in the L2 before deposits are made
// trigger withdrawal
// this codepath should only be hit if the system is setup incorrectly
// this withdrawal is for error recovery, not composing with L2 dapps, so we ignore the return value
triggerWithdrawal(l1ERC20, address(this), _from, _amount, "");
return true;
}
/**
* @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 override returns (address) {
if (l1ERC20 != l1Weth) {
// invalid L1 weth address
return address(0);
}
return l2Weth;
}
function inboundEscrowTransfer(
address _l2TokenAddress,
address _dest,
uint256 _amount
) internal override {
IWETH9(_l2TokenAddress).deposit{ value: _amount }();
IERC20(_l2TokenAddress).safeTransfer(_dest, _amount);
}
function createOutboundTx(
address _from,
uint256 _tokenAmount,
bytes memory _outboundCalldata
) internal override returns (uint256) {
// exitNum incremented after being included in _outboundCalldata
exitNum++;
return
sendTxToL1(
// we send the amount of weth withdrawn as callvalue to the L1 gateway
_tokenAmount,
_from,
counterpartGateway,
_outboundCalldata
);
}
receive() external payable {}
}
"
},
"contracts/tokenbridge/arbitrum/StandardArbERC20.sol": {
"content": "// 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/Cloneable.sol";
import "../libraries/L2GatewayToken.sol";
import "../libraries/BytesParser.sol";
import "./IArbToken.sol";
/**
* @title Standard (i.e., non-custom) contract deployed by L2Gateway.sol as L2 ERC20. Includes standard ERC20 interface plus additional methods for deposits/withdraws
*/
contract StandardArbERC20 is IArbToken, L2GatewayToken, Cloneable {
struct ERC20Getters {
bool ignoreDecimals;
bool ignoreName;
bool ignoreSymbol;
}
ERC20Getters private availableGetters;
/**
* @notice initialize the token
* @dev the L2 bridge assumes this does not fail or revert
* @param _l1Address L1 address of ERC20
* @param _data encoded symbol/name/decimal data for initial deploy
*/
function bridgeInit(address _l1Address, bytes memory _data) public virtual {
(bytes memory name_, bytes memory symbol_, bytes memory decimals_) = abi.decode(
_data,
(bytes, bytes, bytes)
);
// what if decode reverts? shouldn't as this is encoded by L1 contract
/*
* if parsing fails, the type's default value gets assigned
* the parsing can fail for different reasons:
* 1. method not available in L1 (empty input)
* 2. data type is encoded differently in the L1 (trying to abi decode the wrong data type)
* currently (1) returns a parser fails and (2) reverts as there is no `abi.tryDecode`
* https://github.com/ethereum/solidity/issues/10381
*/
(bool parseNameSuccess, string memory parsedName) = BytesParser.toString(name_);
(bool parseSymbolSuccess, string memory parsedSymbol) = BytesParser.toString(symbol_);
(bool parseDecimalSuccess, uint8 parsedDecimals) = BytesParser.toUint8(decimals_);
L2GatewayToken._initialize(
parsedName,
parsedSymbol,
parsedDecimals,
msg.sender, // _l2Gateway,
_l1Address // _l1Counterpart
);
// here we assume that (2) would have reverted, so if the parser failed its because the getter isn't available in the L1.
// instead of storing on a struct, we could instead set a magic number, at something like `type(uint8).max` or random string
// to be more general we instead use an extra storage slot
availableGetters = ERC20Getters({
ignoreName: !parseNameSuccess,
ignoreSymbol: !parseSymbolSuccess,
ignoreDecimals: !parseDecimalSuccess
});
}
function decimals() public view override returns (uint8) {
// no revert message just as in the L1 if you called and the function is not implemented
if (availableGetters.ignoreDecimals) revert();
return super.decimals();
}
function name() public view override returns (string memory) {
// no revert message just as in the L1 if you called and the function is not implemented
if (availableGetters.ignoreName) revert();
return super.name();
}
function symbol() public view override returns (string memory) {
// no revert message just as in the L1 if you called and the function is not implemented
if (availableGetters.ignoreSymbol) revert();
return super.symbol();
}
}
"
},
"node_modules/@offchainlabs/upgrade-executor/src/IUpgradeExecutor.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.6.2 <0.9.0;
interface IUpgradeExecutor {
function initialize(address admin, address[] memory executors) external;
function execute(address upgrade, bytes memory upgradeCallData) external payable;
function executeCall(address target, bytes memory targetCallData) external payable;
}
"
},
"contracts/tokenbridge/libraries/CreationCodeHelper.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
library CreationCodeHelper {
/**
* @notice Generate a creation code that results with a contract with `code` as deployed code.
* Generated creation code shall match the one generated by Solidity compiler with an empty constructor.
* @dev Prepended constructor bytecode consists of:
* - 608060405234801561001057600080fd5b50 - store free memory pointer, then check no callvalue is provided
* - 61xxxx - push 2 bytes of `code` length
* - 806100206000396000f3fe - copy deployed code to memory and return the location of it
* @param runtimeCode Deployed bytecode to which constructor bytecode will be prepended
* @return Creation code of a new contract
*/
function getCreationCodeFor(bytes memory runtimeCode) internal pure returns (bytes memory) {
return abi.encodePacked(
hex"608060405234801561001057600080fd5b50",
hex"61",
uint16(runtimeCode.length),
hex"806100206000396000f3fe",
runtimeCode
);
}
}
"
},
"contracts/tokenbridge/libraries/ClonableBeaconProxy.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.0 <0.9.0;
import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
interface ProxySetter {
function beacon() external view returns (address);
}
contract ClonableBeaconProxy is BeaconProxy {
constructor() BeaconProxy(ProxySetter(msg.sender).beacon(), "") {}
}
contract BeaconProxyFactory is ProxySetter {
bytes32 public constant cloneableProxyHash = keccak256(type(ClonableBeaconProxy).creationCode);
/**
* @notice utility function used in ClonableBeaconProxy.
* @dev this method makes it possible to use ClonableBeaconProxy.creationCode without encoding constructor parameters
* @return the beacon to be used by the proxy contract.
*/
address public override beacon;
function initialize(address _beacon) external {
require(_beacon != address(0), "INVALID_BEACON");
require(beacon == address(0), "ALREADY_INIT");
beacon = _beacon;
}
function getSalt(address user, bytes32 userSalt) public pure returns (bytes32) {
return keccak256(abi.encode(user, userSalt));
}
function createProxy(bytes32 userSalt) external returns (address) {
// deployment will fail and this function will revert if contract `salt` is not unique
bytes32 salt = getSalt(msg.sender, userSalt);
address createdContract = address(new ClonableBeaconProxy{ salt: salt }());
return createdContract;
}
function calculateExpectedAddress(address user, bytes32 userSalt)
public
view
returns (address)
{
bytes32 salt = getSalt(user, userSalt);
return Create2.computeAddress(salt, cloneableProxyHash, address(this));
}
function calculateExpectedAddress(bytes32 salt) public view returns (address) {
return Create2.computeAddress(salt, cloneableProxyHash, address(this));
}
}
"
},
"contracts/tokenbridge/libraries/aeWETH.sol": {
"content": "// 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 "./L2GatewayToken.sol";
import "./IWETH9.sol";
/// @title Arbitrum extended WETH
contract aeWETH is L2GatewayToken, IWETH9 {
function initialize(
string memory name_,
string memory symbol_,
uint8 decimals_,
address l2Gateway_,
address l1Address_
) external {
L2GatewayToken._initialize(name_, symbol_, decimals_, l2Gateway_, l1Address_);
}
function bridgeMint(
address, /* account */
uint256 /* amount */
) external virtual override {
// we want weth to always be fully collaterized
revert("NO_BRIDGE_MINT");
}
function bridgeBurn(address account, uint256 amount) external virtual override onlyGateway {
_burn(account, amount);
(bool success, ) = msg.sender.call{ value: amount }("");
require(success, "FAIL_TRANSFER");
}
function deposit() external payable override {
depositTo(msg.sender);
}
function withdraw(uint256 amount) external override {
withdrawTo(msg.sender, amount);
}
function depositTo(address account) public payable {
_mint(account, msg.value);
}
function withdrawTo(address account, uint256 amount) public {
_burn(msg.sender, amount);
(bool success, ) = account.call{ value: amount }("");
require(success, "FAIL_TRANSFER");
}
receive() external payable {
depositTo(msg.sender);
}
}
"
},
"node_modules/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)
pragma solidity ^0.8.0;
import "./IBeacon.sol";
import "../../access/Ownable.sol";
import "../../utils/Address.sol";
/**
* @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
* implementation contract, which is where they will delegate all function calls.
*
* An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
*/
contract UpgradeableBeacon is IBeacon, Ownable {
address private _implementation;
/**
* @dev Emitted when the implementation returned by the beacon is changed.
*/
event Upgraded(address indexed implementation);
/**
* @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
* beacon.
*/
constructor(address implementation_) {
_setImplementation(implementation_);
}
/**
* @dev Returns the current implementation address.
*/
function implementation() public view virtual override returns (address) {
return _implementation;
}
/**
* @dev Upgrades the beacon to a new implementation.
*
* Emits an {Upgraded} event.
*
* Requirements:
*
* - msg.sender must be the owner of the contract.
* - `newImplementation` must be a contract.
*/
function upgradeTo(address newImplementation) public virtual onlyOwner {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Sets the implementation contract address for this beacon
*
* Requirements:
*
* - `newImplementation` must be a contract.
*/
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
_implementation = newImplementation;
}
}
"
},
"node_modules/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.3) (proxy/transparent/ProxyAdmin.sol)
pragma solidity ^0.8.0;
import "./TransparentUpgradeableProxy.sol";
import "../../access/Ownable.sol";
/**
* @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
* explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
*/
contract ProxyAdmin is Ownable {
/**
* @dev Returns the current implementation of `proxy`.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function getProxyImplementation(ITransparentUpgradeableProxy proxy) public view virtual returns (address) {
// We need to manually run the static call since the getter cannot be flagged as view
// bytes4(keccak256("implementation()")) == 0x5c60da1b
(bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
require(success);
return abi.decode(returndata, (address));
}
/**
* @dev Returns the current admin of `proxy`.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function getProxyAdmin(ITransparentUpgradeableProxy proxy) public view virtual returns (address) {
// We need to manually run the static call since the getter cannot be flagged as view
// bytes4(keccak256("admin()")) == 0xf851a440
(bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
require(success);
return abi.decode(returndata, (address));
}
/**
* @dev Changes the admin of `proxy` to `newAdmin`.
*
* Requirements:
*
* - This contract must be the current admin of `proxy`.
*/
function changeProxyAdmin(ITransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
proxy.changeAdmin(newAdmin);
}
/**
* @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function upgrade(ITransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
proxy.upgradeTo(implementation);
}
/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
* {TransparentUpgradeableProxy-upgradeToAndCall}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function upgradeAndCall(
ITransparentUpgradeableProxy proxy,
address implementation,
bytes memory data
) public payable virtual onlyOwner {
proxy.upgradeToAndCall{value: msg.value}(implementation, data);
}
}
"
},
"node_modules/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.3) (proxy/transparent/TransparentUpgradeableProxy.sol)
pragma solidity ^0.8.0;
import "../ERC1967/ERC1967Proxy.sol";
/**
* @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
* does not implement this interface directly, and some of its functions are implemented by an internal dispatch
* mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
* include them in the ABI so this interface must be used to interact with it.
*/
interface ITransparentUpgradeableProxy is IERC1967 {
function admin() external view returns (address);
function implementation() external view returns (address);
function changeAdmin(address) external;
function upgradeTo(address) external;
function upgradeToAndCall(address, bytes memory) external payable;
}
/**
* @dev This contract implements a proxy that is upgradeable by an admin.
*
* To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
* clashing], which can potentially be used in an attack, this contract uses the
* https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
* things that go hand in hand:
*
* 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
* that call matches one of the admin functions exposed by the proxy itself.
* 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
* implementation. If the admin tries to call a function on the implementation it will fail with an error that says
* "admin cannot fallback to proxy target".
*
* These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
* the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
* to sudden errors when trying to call a function from the proxy implementation.
*
* Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
* you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
*
* NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
* inherit from that interface, and instead the admin functions are implicitly implemented using a custom dispatch
* mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
* fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
* implementation.
*
* WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the compiler
* will not check that there are no selector conflicts, due to the note above. A selector clash between any new function
* and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This could
* render the admin operations inaccessible, which could prevent upgradeability. Transparency may also be compromised.
*/
contract TransparentUpgradeableProxy is ERC1967Proxy {
/**
* @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
* optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
*/
constructor(
address _logic,
address admin_,
bytes memory _data
) payable ERC1967Proxy(_logic, _data) {
_changeAdmin(admin_);
}
/**
* @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
*
* CAUTION: This modifier is deprecated, as it could cause issues if the modified function has arguments, and the
* implementation provides a function with the same selector.
*/
modifier ifAdmin() {
if (msg.sender == _getAdmin()) {
_;
} else {
_fallback();
}
}
/**
* @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior
*/
function _fallback() internal virtual override {
if (msg.sender == _getAdmin()) {
bytes memory ret;
bytes4 selector = msg.sig;
if (selector == ITransparentUpgradeableProxy.upgradeTo.selector) {
ret = _dispatchUpgradeTo();
} else if (selector == ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
ret = _dispatchUpgradeToAndCall();
} else if (selector == ITransparentUpgradeableProxy.changeAdmin.selector) {
ret = _dispatchChangeAdmin();
} else if (selector == ITransparentUpgradeableProxy.admin.selector) {
ret = _dispatchAdmin();
} else if (selector == ITransparentUpgradeableProxy.implementation.selector) {
ret = _dispatchImplementation();
} else {
revert("TransparentUpgradeableProxy: admin cannot fallback to proxy target");
}
assembly {
return(add(ret, 0x20), mload(ret))
}
} else {
super._fallback();
}
}
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function _dispatchAdmin() private returns (bytes memory) {
_requireZeroValue();
address admin = _getAdmin();
return abi.encode(admin);
}
/**
* @dev Returns the current implementation.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/
function _dispatchImplementation() private returns (bytes memory) {
_requireZeroValue();
address implementation = _implementation();
return abi.encode(implementation);
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _dispatchChangeAdmin() private returns (bytes memory) {
_requireZeroValue();
address newAdmin = abi.decode(msg.data[4:], (address));
_changeAdmin(newAdmin);
return "";
}
/**
* @dev Upgrade the implementation of the proxy.
*/
function _dispatchUpgradeTo() private returns (bytes memory) {
_requireZeroValue();
address newImplementation = abi.decode(msg.data[4:], (address));
_upgradeToAndCall(newImplementation, bytes(""), false);
return "";
}
/**
* @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
* by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
* proxied contract.
*/
function _dispatchUpgradeToAndCall() private returns (bytes memory) {
(address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
_upgradeToAndCall(newImplementation, data, true);
return "";
}
/**
* @dev Returns the current admin.
*/
function _admin() internal view virtual returns (address) {
return _getAdmin();
}
/**
* @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to
* emulate some proxy functions being non-payable while still allowing value to pass through.
*/
function _requireZeroValue() private {
require(msg.value == 0);
}
}
"
},
"node_modules/@openzeppelin/contracts/utils/Create2.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Create2.sol)
pragma solidity ^0.8.0;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(
uint256 amount,
bytes32 salt,
bytes memory bytecode
) internal returns (address addr) {
require(address(this).balance >= amount, "Create2: insufficient balance");
require(bytecode.length != 0, "Create2: bytecode length is zero");
/// @solidity memory-safe-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(
bytes32 salt,
bytes32 bytecodeHash,
address deployer
) internal pure returns (address addr) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40) // Get free memory pointer
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
// |-------------------|---------------------------------------------------------------------------|
// | bytecodeHash | CCCCCCCCCCCCC...CC |
// | salt | BBBBBBBBBBBBB...BB |
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
// | 0xFF | FF |
// |-------------------|---------------------------------------------------------------------------|
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
mstore8(start, 0xff)
addr := keccak256(start, 85)
}
}
}
"
},
"contracts/tokenbridge/libraries/gateway/GatewayRouter.sol": {
"content": "// 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);
}
}
"
},
"contracts/tokenbridge/ethereum/gateway/L1GatewayRouter.sol": {
"content": "// 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 ad
Submitted on: 2025-10-10 21:35:10
Comments
Log in to comment.
No comments yet.