L2AtomicTokenBridgeFactory
0xe83b11faa5693e68388785feca3595c6805dfb9a
Verification
Verified
v0.8.16+commit.07a7930e
Type
Contract
23,530 bytes
ABI entries
2
0 read · 1 write
License
none
Contract information
- Address
- 0xe83b11faa5693e68388785feca3595c6805dfb9a
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.16+commit.07a7930e
- Optimization
- Enabled
- Creator
- 0x71EAa46F24…5D94ee1771
- Creation tx
- 0xf32efeb304…290f097b69
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (0)
No read functions
The ABI is unavailable or exposes no view functions.
ABI
[
{
"inputs": [],
"name": "L2AtomicTokenBridgeFactory_AlreadyExists",
"type": "error"
},
{
"inputs": [
{
"components": [
{
"internalType": "bytes",
"name": "router",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "standardGateway",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "customGateway",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "wethGateway",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "aeWeth",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "upgradeExecutor",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "multicall",
"type": "bytes"
}
],
"internalType": "struct L2RuntimeCode",
"name": "l2Code",
"type": "tuple"
},
{
"internalType": "address",
"name": "l1Router",
"type": "address"
},
{
"internalType": "address",
"name": "l1StandardGateway",
"type": "address"
},
{
"internalType": "address",
"name": "l1CustomGateway",
"type": "address"
},
{
"internalType": "address",
"name": "l1WethGateway",
"type": "address"
},
{
"internalType": "address",
"name": "l1Weth",
"type": "address"
},
{
"internalType": "address",
"name": "l2StandardGatewayCanonicalAddress",
"type": "address"
},
{
"internalType": "address",
"name": "rollupOwner",
"type": "address"
},
{
"internalType": "address",
"name": "aliasedL1UpgradeExecutor",
"type": "address"
}
],
"name": "deployL2Contracts",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// 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");
}
Chain explorer4069msChain node105ms