Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
Cross-Chain Transactions
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.
Contract Name:
L1AtomicTokenBridgeCreator
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 100 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.4;
import {
L1TokenBridgeRetryableSender,
L1DeploymentAddresses,
L2DeploymentAddresses,
RetryableParams,
L2TemplateAddresses,
IERC20Inbox,
IERC20,
ERC20,
SafeERC20
} from "./L1TokenBridgeRetryableSender.sol";
import {L1GatewayRouter} from "./gateway/L1GatewayRouter.sol";
import {L1ERC20Gateway} from "./gateway/L1ERC20Gateway.sol";
import {L1CustomGateway} from "./gateway/L1CustomGateway.sol";
import {L1WethGateway} from "./gateway/L1WethGateway.sol";
import {L1OrbitGatewayRouter} from "./gateway/L1OrbitGatewayRouter.sol";
import {L1OrbitERC20Gateway} from "./gateway/L1OrbitERC20Gateway.sol";
import {L1OrbitCustomGateway} from "./gateway/L1OrbitCustomGateway.sol";
import {
L2AtomicTokenBridgeFactory,
OrbitSalts,
L2RuntimeCode,
ProxyAdmin
} from "../arbitrum/L2AtomicTokenBridgeFactory.sol";
import {CreationCodeHelper} from "../libraries/CreationCodeHelper.sol";
import {
IUpgradeExecutor,
UpgradeExecutor
} from "@offchainlabs/upgrade-executor/src/UpgradeExecutor.sol";
import {AddressAliasHelper} from "../libraries/AddressAliasHelper.sol";
import {IInbox, IBridge, IOwnable} from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol";
import {ArbMulticall2} from "../../rpc-utils/MulticallV2.sol";
import {BeaconProxyFactory, ClonableBeaconProxy} from "../libraries/ClonableBeaconProxy.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {
Initializable,
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {TransparentUpgradeableProxy} from
"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IAccessControlUpgradeable} from
"@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol";
/**
* @title Layer1 token bridge creator
* @notice This contract is used to deploy token bridge on custom Orbit chains.
* @dev Throughout the contract terms L1 and L2 are used, but those can be considered as parent (N) chain and child (N+1) chain.
*/
contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
using SafeERC20 for IERC20;
error L1AtomicTokenBridgeCreator_OnlyRollupOwner();
error L1AtomicTokenBridgeCreator_TemplatesNotSet();
error L1AtomicTokenBridgeCreator_RollupOwnershipMisconfig();
error L1AtomicTokenBridgeCreator_ProxyAdminNotFound();
error L1AtomicTokenBridgeCreator_L2FactoryCannotBeChanged();
event OrbitTokenBridgeCreated(
address indexed inbox,
address indexed owner,
L1DeploymentAddresses l1Deployment,
L2DeploymentAddresses l2Deployment,
address proxyAdmin,
address upgradeExecutor
);
event OrbitTokenBridgeTemplatesUpdated();
event OrbitTokenBridgeDeploymentSet(
address indexed inbox, L1DeploymentAddresses l1, L2DeploymentAddresses l2
);
struct L1Templates {
L1GatewayRouter routerTemplate;
L1ERC20Gateway standardGatewayTemplate;
L1CustomGateway customGatewayTemplate;
L1WethGateway wethGatewayTemplate;
L1OrbitGatewayRouter feeTokenBasedRouterTemplate;
L1OrbitERC20Gateway feeTokenBasedStandardGatewayTemplate;
L1OrbitCustomGateway feeTokenBasedCustomGatewayTemplate;
IUpgradeExecutor upgradeExecutor;
}
// use separate mapping to allow appending to the struct in the future
// and workaround some stack too deep issues
mapping(address => L1DeploymentAddresses) public inboxToL1Deployment;
mapping(address => L2DeploymentAddresses) public inboxToL2Deployment;
// Hard-code gas to make sure gas limit is big enough for L2 factory deployment to succeed.
// If retryable would've reverted due to too low gas limit, nonce 0 would be burned and
// canonical address for L2 factory would've been unobtainable
uint256 public gasLimitForL2FactoryDeployment;
// contract which creates retryables for deploying L2 side of token bridge
L1TokenBridgeRetryableSender public retryableSender;
// L1 logic contracts shared by all token bridges
L1Templates public l1Templates;
// L2 contracts deployed to L1 as bytecode placeholders
address public l2TokenBridgeFactoryTemplate;
address public l2RouterTemplate;
address public l2StandardGatewayTemplate;
address public l2CustomGatewayTemplate;
address public l2WethGatewayTemplate;
address public l2WethTemplate;
address public l2MulticallTemplate;
// WETH address on L1
address public l1Weth;
// Multicall2 address on L1, this should NOT be ArbMulticall2
address public l1Multicall;
// immutable canonical address for L2 factory
// other canonical addresses (dependent on L2 template implementations) can be fetched through `_predictL2***Address` functions
address public canonicalL2FactoryAddress;
constructor() {
_disableInitializers();
}
function initialize(L1TokenBridgeRetryableSender _retryableSender) public initializer {
__Ownable_init();
// store retryable sender and initialize it. This contract will be set as owner
retryableSender = _retryableSender;
retryableSender.initialize();
canonicalL2FactoryAddress =
_computeAddressAtNonce0(AddressAliasHelper.applyL1ToL2Alias(address(this)));
}
/**
* @notice Set addresses of L1 logic contracts and L2 contracts which are deployed on L1.
* @dev L2 contracts are deployed to L1 as bytecode placeholders - that bytecode will be part of retryable
* payload used to deploy contracts on L2 side.
*/
function setTemplates(
L1Templates calldata _l1Templates,
address _l2TokenBridgeFactoryTemplate,
address _l2RouterTemplate,
address _l2StandardGatewayTemplate,
address _l2CustomGatewayTemplate,
address _l2WethGatewayTemplate,
address _l2WethTemplate,
address _l2MulticallTemplate,
address _l1Weth,
address _l1Multicall,
uint256 _gasLimitForL2FactoryDeployment
) external onlyOwner {
l1Templates = _l1Templates;
if (
l2TokenBridgeFactoryTemplate != address(0)
&& l2TokenBridgeFactoryTemplate != _l2TokenBridgeFactoryTemplate
) {
revert L1AtomicTokenBridgeCreator_L2FactoryCannotBeChanged();
}
l2TokenBridgeFactoryTemplate = _l2TokenBridgeFactoryTemplate;
l2RouterTemplate = _l2RouterTemplate;
l2StandardGatewayTemplate = _l2StandardGatewayTemplate;
l2CustomGatewayTemplate = _l2CustomGatewayTemplate;
l2WethGatewayTemplate = _l2WethGatewayTemplate;
l2WethTemplate = _l2WethTemplate;
l2MulticallTemplate = _l2MulticallTemplate;
l1Weth = _l1Weth;
l1Multicall = _l1Multicall;
gasLimitForL2FactoryDeployment = _gasLimitForL2FactoryDeployment;
emit OrbitTokenBridgeTemplatesUpdated();
}
/**
* @notice Deploy and initialize token bridge, both L1 and L2 sides, as part of a single TX.
* @dev This is a single entrypoint of L1 token bridge creator. Function deploys L1 side of token bridge and then uses
* 2 retryable tickets to deploy L2 side. 1st retryable deploys L2 factory. And then 'retryable sender' contract
* is called to issue 2nd retryable which deploys and inits the rest of the contracts. L2 chain is determined
* by `inbox` parameter.
*
* In addition to deploying token bridge contracts, L2 factory will also deploy UpgradeExector on L2 side.
* L2 UpgradeExecutor will set 2 accounts to have EXECUTOR role - rollupOwner and alias of L1UpgradeExecutor.
* 'rollupOwner' can be either EOA or a contract. If it is a contract, address will be aliased before sending to L2
* in order to be usable.
*
* Warning: Due to asynchronous communication between parent and child chain, always check child chain contracts are
* fully deployed and initialized before sending tokens to the bridge. Otherwise tokens might be permanently lost.
*/
function createTokenBridge(
address inbox,
address rollupOwner,
uint256 maxGasForContracts,
uint256 gasPriceBid
) external payable {
// templates have to be in place
if (address(l1Templates.routerTemplate) == address(0)) {
revert L1AtomicTokenBridgeCreator_TemplatesNotSet();
}
// Check that the rollupOwner account has EXECUTOR role
// on the upgrade executor which is the owner of the rollup
address upgradeExecutor = IInbox(inbox).bridge().rollup().owner();
if (
!IAccessControlUpgradeable(upgradeExecutor).hasRole(
UpgradeExecutor(upgradeExecutor).EXECUTOR_ROLE(), rollupOwner
)
) {
revert L1AtomicTokenBridgeCreator_RollupOwnershipMisconfig();
}
// we allow resending l2 deployment calls
// this is useful to recover from expired or out-of-order retryables
// in case of resend, we assume L1 contracts already exist and we just need to deploy L2 contracts
// deployment mappings should not be updated in case of resend
bool isResend = (inboxToL1Deployment[inbox].router != address(0));
address feeToken = _getFeeToken(inbox);
// store L2 addresses before deployments
L1DeploymentAddresses memory l1Deployment;
L2DeploymentAddresses memory l2Deployment;
// if resend, we use the existing l1 deployment
if (isResend) {
l1Deployment = inboxToL1Deployment[inbox];
}
{
// store L2 addresses which are proxies
uint256 chainId = IRollupCore(address(IInbox(inbox).bridge().rollup())).chainId();
l2Deployment.router = _getProxyAddress(OrbitSalts.L2_ROUTER, chainId);
l2Deployment.standardGateway = _getProxyAddress(OrbitSalts.L2_STANDARD_GATEWAY, chainId);
l2Deployment.customGateway = _getProxyAddress(OrbitSalts.L2_CUSTOM_GATEWAY, chainId);
if (feeToken == address(0)) {
l2Deployment.wethGateway = _getProxyAddress(OrbitSalts.L2_WETH_GATEWAY, chainId);
l2Deployment.weth = _getProxyAddress(OrbitSalts.L2_WETH, chainId);
}
l2Deployment.upgradeExecutor = _getProxyAddress(OrbitSalts.L2_EXECUTOR, chainId);
// store L2 addresses which are not proxies
l2Deployment.proxyAdmin = _predictL2ProxyAdminAddress(chainId);
l2Deployment.beaconProxyFactory = _predictL2BeaconProxyFactoryAddress(chainId);
l2Deployment.multicall = _predictL2Multicall(chainId);
}
// deploy L1 side of token bridge
// get existing proxy admin and upgrade executor
address proxyAdmin = IInboxProxyAdmin(inbox).getProxyAdmin();
if (proxyAdmin == address(0)) {
revert L1AtomicTokenBridgeCreator_ProxyAdminNotFound();
}
// if resend, we assume L1 contracts already exist
if (!isResend) {
// l1 router deployment block
{
address routerTemplate = feeToken != address(0)
? address(l1Templates.feeTokenBasedRouterTemplate)
: address(l1Templates.routerTemplate);
l1Deployment.router = _deployProxyWithSalt(
_getL1Salt(OrbitSalts.L1_ROUTER, inbox), routerTemplate, proxyAdmin
);
}
// l1 standard gateway deployment block
{
address template = feeToken != address(0)
? address(l1Templates.feeTokenBasedStandardGatewayTemplate)
: address(l1Templates.standardGatewayTemplate);
L1ERC20Gateway standardGateway = L1ERC20Gateway(
_deployProxyWithSalt(
_getL1Salt(OrbitSalts.L1_STANDARD_GATEWAY, inbox), template, proxyAdmin
)
);
standardGateway.initialize(
l2Deployment.standardGateway,
l1Deployment.router,
inbox,
keccak256(type(ClonableBeaconProxy).creationCode),
l2Deployment.beaconProxyFactory
);
l1Deployment.standardGateway = address(standardGateway);
}
// l1 custom gateway deployment block
{
address template = feeToken != address(0)
? address(l1Templates.feeTokenBasedCustomGatewayTemplate)
: address(l1Templates.customGatewayTemplate);
L1CustomGateway customGateway = L1CustomGateway(
_deployProxyWithSalt(
_getL1Salt(OrbitSalts.L1_CUSTOM_GATEWAY, inbox), template, proxyAdmin
)
);
customGateway.initialize(
l2Deployment.customGateway, l1Deployment.router, inbox, upgradeExecutor
);
l1Deployment.customGateway = address(customGateway);
}
// l1 weth gateway deployment block
if (feeToken == address(0)) {
L1WethGateway wethGateway = L1WethGateway(
payable(
_deployProxyWithSalt(
_getL1Salt(OrbitSalts.L1_WETH_GATEWAY, inbox),
address(l1Templates.wethGatewayTemplate),
proxyAdmin
)
)
);
wethGateway.initialize(
l2Deployment.wethGateway, l1Deployment.router, inbox, l1Weth, l2Deployment.weth
);
l1Deployment.wethGateway = address(wethGateway);
l1Deployment.weth = l1Weth;
}
// init router
L1GatewayRouter(l1Deployment.router).initialize(
upgradeExecutor,
l1Deployment.standardGateway,
address(0),
l2Deployment.router,
inbox
);
}
// deploy factory and then L2 contracts through L2 factory, using 2 retryables calls
// we do not care if it is a resend or not, if the L2 deployment already exists it will simply fail on L2
_deployL2Factory(inbox, gasPriceBid, feeToken);
RetryableParams memory retryableParams = RetryableParams(
inbox,
canonicalL2FactoryAddress,
msg.sender,
msg.sender,
maxGasForContracts,
gasPriceBid,
0
);
if (feeToken != address(0)) {
// transfer fee tokens to inbox to pay for 2nd retryable
retryableParams.feeTokenTotalFeeAmount =
_getScaledAmount(feeToken, maxGasForContracts * gasPriceBid);
IERC20(feeToken).safeTransferFrom(
msg.sender, inbox, retryableParams.feeTokenTotalFeeAmount
);
}
L2TemplateAddresses memory l2TemplateAddress = L2TemplateAddresses(
l2RouterTemplate,
l2StandardGatewayTemplate,
l2CustomGatewayTemplate,
feeToken != address(0) ? address(0) : l2WethGatewayTemplate,
feeToken != address(0) ? address(0) : l2WethTemplate,
address(l1Templates.upgradeExecutor),
l2MulticallTemplate
);
// alias rollup owner if it is a contract
address l2RollupOwner = rollupOwner.code.length == 0
? rollupOwner
: AddressAliasHelper.applyL1ToL2Alias(rollupOwner);
// sweep the balance to send the retryable and refund the difference
// it is known that any eth previously in this contract can be extracted
// tho it is not expected that this contract will have any eth
_sendRetryableToCreateContracts(
retryableParams,
l2TemplateAddress,
l1Deployment,
l2Deployment,
l2RollupOwner,
upgradeExecutor
);
// deployment mappings should not be updated in case of resend
if (!isResend) {
emit OrbitTokenBridgeCreated(
inbox, rollupOwner, l1Deployment, l2Deployment, proxyAdmin, upgradeExecutor
);
inboxToL1Deployment[inbox] = l1Deployment;
inboxToL2Deployment[inbox] = l2Deployment;
}
}
function _sendRetryableToCreateContracts(
RetryableParams memory retryableParams,
L2TemplateAddresses memory l2TemplateAddress,
L1DeploymentAddresses memory l1Deployment,
L2DeploymentAddresses memory l2Deployment,
address l2RollupOwner,
address upgradeExecutor
) internal {
retryableSender.sendRetryable{
value: retryableParams.feeTokenTotalFeeAmount > 0 ? 0 : address(this).balance
}(
retryableParams,
l2TemplateAddress,
l1Deployment,
l2Deployment.standardGateway,
l2RollupOwner,
msg.sender,
upgradeExecutor
);
}
/**
* @notice Rollup owner can override deployment
*/
function setDeployment(
address inbox,
L1DeploymentAddresses memory l1Deployment,
L2DeploymentAddresses memory l2Deployment
) external {
if (msg.sender != IInbox(inbox).bridge().rollup().owner()) {
revert L1AtomicTokenBridgeCreator_OnlyRollupOwner();
}
inboxToL1Deployment[inbox] = l1Deployment;
inboxToL2Deployment[inbox] = l2Deployment;
emit OrbitTokenBridgeDeploymentSet(inbox, l1Deployment, l2Deployment);
}
/**
* @notice Get the L1 router address for a given inbox
* @dev This is kept since its cheaper than accessing the mapping getter
* and is useful enough for most onchain purposes
*/
function getRouter(address inbox) public view returns (address) {
return inboxToL1Deployment[inbox].router;
}
function _deployL2Factory(address inbox, uint256 gasPriceBid, address feeToken) internal {
// encode L2 factory bytecode
bytes memory deploymentData =
CreationCodeHelper.getCreationCodeFor(l2TokenBridgeFactoryTemplate.code);
if (feeToken != address(0)) {
// transfer fee tokens to inbox to pay for 1st retryable
uint256 retryableFee = gasLimitForL2FactoryDeployment * gasPriceBid;
uint256 scaledRetryableFee = _getScaledAmount(feeToken, retryableFee);
IERC20(feeToken).safeTransferFrom(msg.sender, inbox, scaledRetryableFee);
IERC20Inbox(inbox).createRetryableTicket(
address(0),
0,
0,
msg.sender,
msg.sender,
gasLimitForL2FactoryDeployment,
gasPriceBid,
scaledRetryableFee,
deploymentData
);
} else {
uint256 maxSubmissionCost =
IInbox(inbox).calculateRetryableSubmissionFee(deploymentData.length, 0);
uint256 retryableFee = maxSubmissionCost + gasLimitForL2FactoryDeployment * gasPriceBid;
IInbox(inbox).createRetryableTicket{value: retryableFee}(
address(0),
0,
maxSubmissionCost,
msg.sender,
msg.sender,
gasLimitForL2FactoryDeployment,
gasPriceBid,
deploymentData
);
}
}
function _predictL2ProxyAdminAddress(uint256 chainId) internal view returns (address) {
return Create2.computeAddress(
_getL2Salt(OrbitSalts.L2_PROXY_ADMIN, chainId),
keccak256(type(ProxyAdmin).creationCode),
canonicalL2FactoryAddress
);
}
function _predictL2BeaconProxyFactoryAddress(uint256 chainId) internal view returns (address) {
return Create2.computeAddress(
_getL2Salt(OrbitSalts.BEACON_PROXY_FACTORY, chainId),
keccak256(type(BeaconProxyFactory).creationCode),
canonicalL2FactoryAddress
);
}
function _predictL2Multicall(uint256 chainId) internal view returns (address) {
return Create2.computeAddress(
_getL2Salt(OrbitSalts.L2_MULTICALL, chainId),
keccak256(CreationCodeHelper.getCreationCodeFor(l2MulticallTemplate.code)),
canonicalL2FactoryAddress
);
}
function _getFeeToken(address inbox) internal view returns (address) {
address bridge = address(IInbox(inbox).bridge());
try IERC20Bridge(bridge).nativeToken() returns (address feeToken) {
return feeToken;
} catch {
return address(0);
}
}
/**
* @notice Compute address of contract deployed using CREATE opcode at nonce 0
* @dev The contract address is derived by RLP encoding the deployer's address and the nonce using the Keccak-256 hashing algorithm.
* More formally: keccak256(rlp.encode([origin, nonce])[12:]
*
* First part of the function implementation does RLP encoding of [origin, nonce].
* - nonce's prefix is encoded depending on its size -> 0x80 + lenInBytes(nonce)
* - origin is 20 bytes long so its encoded prefix is 0x80 + 0x14 = 0x94
* - prefix of the whole list is 0xc0 + lenInBytes(RLP(list))
* After we have RLP encoding in place last step is to hash it, take last 20 bytes and cast is to an address.
*
* This function is an codesize optimized version to only calculate the address for nonce 0.
* @return computed address
*/
function _computeAddressAtNonce0(address origin) internal pure returns (address) {
return address(
uint160(
uint256(
keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), origin, bytes1(0x80)))
)
)
);
}
/**
* @notice L2 contracts are deployed as proxy with dummy seed logic contracts using CREATE2. That enables
* us to upfront calculate the expected canonical addresses. This proxy should be upgraded to the
* intended logic implementation immediately.
*/
function _getProxyAddress(bytes memory prefix, uint256 chainId)
internal
view
returns (address)
{
return Create2.computeAddress(
_getL2Salt(prefix, chainId),
keccak256(
abi.encodePacked(
type(TransparentUpgradeableProxy).creationCode,
abi.encode(
canonicalL2FactoryAddress, _predictL2ProxyAdminAddress(chainId), bytes("")
)
)
),
canonicalL2FactoryAddress
);
}
/**
* @notice We want to have exactly one set of canonical token bridge contracts for every rollup. For that
* reason we make rollup's inbox address part of the salt. It prevents deploying more than one
* token bridge.
*/
function _getL1Salt(bytes memory prefix, address inbox) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(prefix, inbox));
}
/**
* @notice Salt for L2 token bridge contracts depends on the chainId and caller's address. Canonical token bridge
* will be deployed by retryable ticket which is created by `retryableSender` contract. That
* means `retryableSender`'s alias will be used on L2 side to calculate the salt for deploying
* L2 contracts, in addition to chainId (_getL2Salt function in L2AtomicTokenBridgeFactory).
*/
function _getL2Salt(bytes memory prefix, uint256 chainId) internal view returns (bytes32) {
return keccak256(
abi.encodePacked(
prefix, chainId, AddressAliasHelper.applyL1ToL2Alias(address(retryableSender))
)
);
}
/**
* @notice Internal method to deploy TransparentUpgradeableProxy with CREATE2 opcode.
*/
function _deployProxyWithSalt(bytes32 salt, address logic, address admin)
internal
returns (address)
{
return address(new TransparentUpgradeableProxy{salt: salt}(logic, admin, bytes("")));
}
/**
* @notice Scale amount to the fee token's decimals. Ie. amount of 1e18 will be scaled to 1e6 if fee token has 6 decimals like USDC.
*/
function _getScaledAmount(address feeToken, uint256 amount) internal view returns (uint256) {
uint8 decimals = ERC20(feeToken).decimals();
if (decimals == 18) {
return amount;
}
if (decimals < 18) {
uint256 scaledAmount = amount / (10 ** (18 - decimals));
// round up if necessary
if (scaledAmount * (10 ** (18 - decimals)) < amount) {
scaledAmount++;
}
return scaledAmount;
}
return amount * (10 ** (decimals - 18));
}
}
interface IERC20Bridge {
function nativeToken() external view returns (address);
}
interface IInboxProxyAdmin {
function getProxyAdmin() external view returns (address);
}
interface IRollupCore {
function chainId() external view returns (uint256);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.4;
import {IInbox} from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol";
import {
L2AtomicTokenBridgeFactory,
L2RuntimeCode,
ProxyAdmin
} from "../arbitrum/L2AtomicTokenBridgeFactory.sol";
import {AddressAliasHelper} from "../libraries/AddressAliasHelper.sol";
import {
Initializable,
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {TransparentUpgradeableProxy} from
"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IERC20, ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title Token Bridge Retryable Ticket Sender
* @notice This contract is intended to simply send out retryable ticket to deploy L2 side of the token bridge.
* Ticket data is prepared by L1AtomicTokenBridgeCreator. Retryable ticket issuance is done separately
* from L1 creator in order to have different senders for deployment of L2 factory vs. deployment of
* rest of L2 contracts. Having same sender can lead to edge cases where retryables are executed out
* order - that would prevent us from having canonical set of L2 addresses.
*
*/
contract L1TokenBridgeRetryableSender is Initializable, OwnableUpgradeable {
error L1TokenBridgeRetryableSender_RefundFailed();
error L1TokenBridgeRetryableSender_EthReceivedForFeeToken();
function initialize() public initializer {
__Ownable_init();
}
/**
* @notice Creates retryable which deploys L2 side of the token bridge.
* @dev Function will build retryable data, calculate submission cost and retryable value, create retryable
* and then refund the remaining funds to original delpoyer if excess eth was sent.
*/
function sendRetryable(
RetryableParams calldata retryableParams,
L2TemplateAddresses calldata l2,
L1DeploymentAddresses calldata l1,
address l2StandardGatewayAddress,
address rollupOwner,
address deployer,
address l1UpgradeExecutor
) external payable onlyOwner {
bool isUsingFeeToken = retryableParams.feeTokenTotalFeeAmount > 0;
address aliasedL1UpgradeExecutor = AddressAliasHelper.applyL1ToL2Alias(l1UpgradeExecutor);
if (!isUsingFeeToken) {
_sendRetryableUsingEth(
retryableParams,
l2,
l1,
l2StandardGatewayAddress,
rollupOwner,
deployer,
aliasedL1UpgradeExecutor
);
} else {
if (msg.value > 0) revert L1TokenBridgeRetryableSender_EthReceivedForFeeToken();
_sendRetryableUsingFeeToken(
retryableParams,
l2,
l1,
l2StandardGatewayAddress,
rollupOwner,
aliasedL1UpgradeExecutor
);
}
}
function _sendRetryableUsingEth(
RetryableParams calldata retryableParams,
L2TemplateAddresses calldata l2,
L1DeploymentAddresses calldata l1,
address l2StandardGatewayAddress,
address rollupOwner,
address deployer,
address aliasedL1UpgradeExecutor
) internal {
bytes memory data = abi.encodeCall(
L2AtomicTokenBridgeFactory.deployL2Contracts,
(
L2RuntimeCode(
l2.routerTemplate.code,
l2.standardGatewayTemplate.code,
l2.customGatewayTemplate.code,
l2.wethGatewayTemplate.code,
l2.wethTemplate.code,
l2.upgradeExecutorTemplate.code,
l2.multicallTemplate.code
),
l1.router,
l1.standardGateway,
l1.customGateway,
l1.wethGateway,
l1.weth,
l2StandardGatewayAddress,
rollupOwner,
aliasedL1UpgradeExecutor
)
);
uint256 maxSubmissionCost =
IInbox(retryableParams.inbox).calculateRetryableSubmissionFee(data.length, 0);
uint256 retryableValue =
maxSubmissionCost + retryableParams.maxGas * retryableParams.gasPriceBid;
_createRetryableUsingEth(retryableParams, maxSubmissionCost, retryableValue, data);
// refund excess value to the deployer
// it is known that any eth previously in this contract can be extracted
// tho it is not expected that this contract will have any eth
(bool success,) = deployer.call{value: address(this).balance}("");
if (!success) revert L1TokenBridgeRetryableSender_RefundFailed();
}
function _sendRetryableUsingFeeToken(
RetryableParams calldata retryableParams,
L2TemplateAddresses calldata l2,
L1DeploymentAddresses calldata l1,
address l2StandardGatewayAddress,
address rollupOwner,
address aliasedL1UpgradeExecutor
) internal {
bytes memory data = abi.encodeCall(
L2AtomicTokenBridgeFactory.deployL2Contracts,
(
L2RuntimeCode(
l2.routerTemplate.code,
l2.standardGatewayTemplate.code,
l2.customGatewayTemplate.code,
"",
"",
l2.upgradeExecutorTemplate.code,
l2.multicallTemplate.code
),
l1.router,
l1.standardGateway,
l1.customGateway,
address(0),
address(0),
l2StandardGatewayAddress,
rollupOwner,
aliasedL1UpgradeExecutor
)
);
_createRetryableUsingFeeToken(retryableParams, data);
}
function _createRetryableUsingEth(
RetryableParams calldata retryableParams,
uint256 maxSubmissionCost,
uint256 value,
bytes memory data
) internal {
IInbox(retryableParams.inbox).createRetryableTicket{value: value}(
retryableParams.target,
0,
maxSubmissionCost,
retryableParams.excessFeeRefundAddress,
retryableParams.callValueRefundAddress,
retryableParams.maxGas,
retryableParams.gasPriceBid,
data
);
}
function _createRetryableUsingFeeToken(
RetryableParams calldata retryableParams,
bytes memory data
) internal {
IERC20Inbox(retryableParams.inbox).createRetryableTicket(
retryableParams.target,
0,
0,
retryableParams.excessFeeRefundAddress,
retryableParams.callValueRefundAddress,
retryableParams.maxGas,
retryableParams.gasPriceBid,
retryableParams.feeTokenTotalFeeAmount,
data
);
}
}
/**
* retryableParams needed to send retryable ticket
*/
struct RetryableParams {
address inbox;
address target;
address excessFeeRefundAddress;
address callValueRefundAddress;
uint256 maxGas;
uint256 gasPriceBid;
uint256 feeTokenTotalFeeAmount;
}
/**
* Addresses of L2 templates deployed on L1
*/
struct L2TemplateAddresses {
address routerTemplate;
address standardGatewayTemplate;
address customGatewayTemplate;
address wethGatewayTemplate;
address wethTemplate;
address upgradeExecutorTemplate;
address multicallTemplate;
}
/**
* L1 side of token bridge addresses
*/
struct L1DeploymentAddresses {
address router;
address standardGateway;
address customGateway;
address wethGateway;
address weth;
}
struct L2DeploymentAddresses {
address router;
address standardGateway;
address customGateway;
address wethGateway;
address weth;
address proxyAdmin;
address beaconProxyFactory;
address upgradeExecutor;
address multicall;
}
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 "../../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);
}
}// 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 "./L1ArbitrumExtendedGateway.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "../../libraries/Whitelist.sol";
/**
* @title Layer 1 Gateway contract for bridging standard ERC20s
* @notice This contract handles token deposits, holds the escrowed tokens on layer 1, and (ultimately) finalizes withdrawals.
* @dev Any ERC20 that requires non-standard functionality should use a separate gateway.
* Messages to layer 2 use the inbox's createRetryableTicket method.
*/
contract L1ERC20Gateway is L1ArbitrumExtendedGateway {
// used for create2 address calculation
bytes32 public cloneableProxyHash;
// We don't use the solidity creationCode as it breaks when upgrading contracts
// keccak256(type(ClonableBeaconProxy).creationCode);
address public l2BeaconProxyFactory;
// whitelist not used anymore
address public whitelist;
// start of inline reentrancy guard
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.2/contracts/utils/ReentrancyGuard.sol
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
// end of inline reentrancy guard
function outboundTransferCustomRefund(
address _l1Token,
address _refundTo,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) public payable virtual override nonReentrant returns (bytes memory res) {
return
super.outboundTransferCustomRefund(
_l1Token,
_refundTo,
_to,
_amount,
_maxGas,
_gasPriceBid,
_data
);
}
function finalizeInboundTransfer(
address _token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) public payable override nonReentrant {
// the superclass checks onlyCounterpartGateway
super.finalizeInboundTransfer(_token, _from, _to, _amount, _data);
}
function initialize(
address _l2Counterpart,
address _router,
address _inbox,
bytes32 _cloneableProxyHash,
address _l2BeaconProxyFactory
) public {
L1ArbitrumGateway._initialize(_l2Counterpart, _router, _inbox);
require(_cloneableProxyHash != bytes32(0), "INVALID_PROXYHASH");
require(_l2BeaconProxyFactory != address(0), "INVALID_BEACON");
cloneableProxyHash = _cloneableProxyHash;
l2BeaconProxyFactory = _l2BeaconProxyFactory;
// disable whitelist by default
whitelist = address(0);
// reentrancy guard
_status = _NOT_ENTERED;
}
/**
* @notice utility function used to perform external read-only calls.
* @dev the result is returned even if the call failed or was directed at an EOA,
* it is cheaper to have the L2 consumer identify and deal with this.
* @return result bytes, even if the call failed.
*/
function callStatic(address targetContract, bytes4 targetFunction)
internal
view
returns (bytes memory)
{
(
,
/* bool success */
bytes memory res
) = targetContract.staticcall(abi.encodeWithSelector(targetFunction));
return res;
}
function getOutboundCalldata(
address _token,
address _from,
address _to,
uint256 _amount,
bytes memory _data
) public view override returns (bytes memory outboundCalldata) {
// TODO: cheaper to make static calls or save isDeployed to storage?
bytes memory deployData = abi.encode(
callStatic(_token, ERC20.name.selector),
callStatic(_token, ERC20.symbol.selector),
callStatic(_token, ERC20.decimals.selector)
);
outboundCalldata = abi.encodeWithSelector(
ITokenGateway.finalizeInboundTransfer.selector,
_token,
_from,
_to,
_amount,
GatewayMessageHandler.encodeToL2GatewayMsg(deployData, _data)
);
return outboundCalldata;
}
function calculateL2TokenAddress(address l1ERC20)
public
view
override(ITokenGateway, TokenGateway)
returns (address)
{
bytes32 salt = getSalt(l1ERC20);
return Create2.computeAddress(salt, cloneableProxyHash, l2BeaconProxyFactory);
}
function getSalt(address l1ERC20) internal view returns (bytes32) {
// TODO: use a library
return keccak256(abi.encode(counterpartGateway, keccak256(abi.encode(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 { ArbitrumEnabledToken } from "../ICustomToken.sol";
import "./L1ArbitrumExtendedGateway.sol";
import "../../arbitrum/gateway/L2CustomGateway.sol";
import "../../libraries/gateway/ICustomGateway.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../../libraries/Whitelist.sol";
/**
* @title Gatway for "custom" bridging functionality
* @notice Handles some (but not all!) custom Gateway needs.
*/
contract L1CustomGateway is L1ArbitrumExtendedGateway, ICustomGateway {
using Address for address;
// stores addresses of L2 tokens to be used
mapping(address => address) public override l1ToL2Token;
// owner is able to force add custom mappings
address public owner;
// whitelist not used anymore
address public whitelist;
// start of inline reentrancy guard
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.2/contracts/utils/ReentrancyGuard.sol
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
modifier onlyOwner() {
require(msg.sender == owner, "ONLY_OWNER");
_;
}
function outboundTransferCustomRefund(
address _l1Token,
address _refundTo,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) public payable override nonReentrant returns (bytes memory res) {
return
super.outboundTransferCustomRefund(
_l1Token,
_refundTo,
_to,
_amount,
_maxGas,
_gasPriceBid,
_data
);
}
function finalizeInboundTransfer(
address _token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) public payable override nonReentrant {
// the superclass checks onlyCounterpartGateway
super.finalizeInboundTransfer(_token, _from, _to, _amount, _data);
}
function initialize(
address _l1Counterpart,
address _l1Router,
address _inbox,
address _owner
) public {
L1ArbitrumGateway._initialize(_l1Counterpart, _l1Router, _inbox);
owner = _owner;
// disable whitelist by default
whitelist = address(0);
// reentrancy guard
_status = _NOT_ENTERED;
}
/**
* @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(ITokenGateway, TokenGateway)
returns (address)
{
return l1ToL2Token[l1ERC20];
}
/**
* @notice Allows L1 Token contract to trustlessly register its custom L2 counterpart. (other registerTokenToL2 method allows excess eth recovery from _maxSubmissionCost and is recommended)
* @param _l2Address counterpart address of L1 token
* @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 registerTokenToL2(
address _l2Address,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost
) external payable virtual returns (uint256) {
return registerTokenToL2(_l2Address, _maxGas, _gasPriceBid, _maxSubmissionCost, msg.sender);
}
/**
* @notice Allows L1 Token contract to trustlessly register its custom L2 counterpart.
* param _l2Address counterpart address of L1 token
* 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 registerTokenToL2(
address _l2Address,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
address _creditBackAddress
) public payable virtual returns (uint256) {
return
_registerTokenToL2(
_l2Address,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
_creditBackAddress,
msg.value
);
}
function _registerTokenToL2(
address _l2Address,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
address _creditBackAddress,
uint256 _feeAmount
) internal returns (uint256) {
{
require(
ArbitrumEnabledToken(msg.sender).isArbitrumEnabled() == uint8(0xb1),
"NOT_ARB_ENABLED"
);
address currL2Addr = l1ToL2Token[msg.sender];
if (currL2Addr != address(0)) {
// if token is already set, don't allow it to set a different L2 address
require(currL2Addr == _l2Address, "NO_UPDATE_TO_DIFFERENT_ADDR");
}
}
l1ToL2Token[msg.sender] = _l2Address;
address[] memory l1Addresses = new address[](1);
address[] memory l2Addresses = new address[](1);
l1Addresses[0] = msg.sender;
l2Addresses[0] = _l2Address;
emit TokenSet(l1Addresses[0], l2Addresses[0]);
bytes memory _data = abi.encodeWithSelector(
L2CustomGateway.registerTokenFromL1.selector,
l1Addresses,
l2Addresses
);
return
sendTxToL2(
inbox,
counterpartGateway,
_creditBackAddress,
_feeAmount,
0,
_maxSubmissionCost,
_maxGas,
_gasPriceBid,
_data
);
}
function setOwner(address newOwner) external onlyOwner {
require(newOwner != address(0), "INVALID_OWNER");
owner = newOwner;
}
/**
* @notice Allows owner to force register a custom L1/L2 token pair.
* @dev _l1Addresses[i] counterpart is assumed to be _l2Addresses[i]
* @param _l1Addresses array of L1 addresses
* @param _l2Addresses array of L2 addresses
* @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 forceRegisterTokenToL2(
address[] calldata _l1Addresses,
address[] calldata _l2Addresses,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost
) external payable virtual onlyOwner returns (uint256) {
return
_forceRegisterTokenToL2(
_l1Addresses,
_l2Addresses,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
msg.value
);
}
function _forceRegisterTokenToL2(
address[] calldata _l1Addresses,
address[] calldata _l2Addresses,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256 _feeAmount
) internal returns (uint256) {
require(_l1Addresses.length == _l2Addresses.length, "INVALID_LENGTHS");
for (uint256 i = 0; i < _l1Addresses.length; i++) {
// here we assume the owner checked both addresses offchain before force registering
// require(address(_l1Addresses[i]).isContract(), "MUST_BE_CONTRACT");
l1ToL2Token[_l1Addresses[i]] = _l2Addresses[i];
emit TokenSet(_l1Addresses[i], _l2Addresses[i]);
}
bytes memory _data = abi.encodeWithSelector(
L2CustomGateway.registerTokenFromL1.selector,
_l1Addresses,
_l2Addresses
);
return
sendTxToL2(
inbox,
counterpartGateway,
msg.sender,
_feeAmount,
0,
_maxSubmissionCost,
_maxGas,
_gasPriceBid,
_data
);
}
}// 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 "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../libraries/IWETH9.sol";
import "../../test/TestWETH9.sol";
import "./L1ArbitrumExtendedGateway.sol";
contract L1WethGateway is L1ArbitrumExtendedGateway {
using SafeERC20 for IERC20;
address public l1Weth;
address public l2Weth;
function initialize(
address _l2Counterpart,
address _l1Router,
address _inbox,
address _l1Weth,
address _l2Weth
) public {
L1ArbitrumGateway._initialize(_l2Counterpart, _l1Router, _inbox);
require(_l1Weth != address(0), "INVALID_L1WETH");
require(_l2Weth != address(0), "INVALID_L2WETH");
l1Weth = _l1Weth;
l2Weth = _l2Weth;
}
function createOutboundTxCustomRefund(
address _refundTo,
address _from,
uint256 _tokenAmount,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
bytes memory _outboundCalldata
) internal override returns (uint256) {
return
sendTxToL2CustomRefund(
inbox,
counterpartGateway,
_refundTo,
_from,
// msg.value does not include weth withdrawn from user, we need to add in that amount
msg.value + _tokenAmount,
// send token amount to L2 as call value
_tokenAmount,
L2GasParams({
_maxSubmissionCost: _maxSubmissionCost,
_maxGas: _maxGas,
_gasPriceBid: _gasPriceBid
}),
_outboundCalldata
);
}
function outboundEscrowTransfer(
address _l1Token,
address _from,
uint256 _amount
) internal override returns (uint256) {
IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);
IWETH9(_l1Token).withdraw(_amount);
// the weth token doesn't contain any special behaviour that changes the amount
// when doing transfers / withdrawals. so we don't check the balanceOf
return _amount;
}
function inboundEscrowTransfer(
address _l1Token,
address _dest,
uint256 _amount
) internal override {
IWETH9(_l1Token).deposit{ value: _amount }();
IERC20(_l1Token).safeTransfer(_dest, _amount);
}
/**
* @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(ITokenGateway, TokenGateway)
returns (address)
{
if (l1ERC20 != l1Weth) {
// invalid L1 weth address
return address(0);
}
return l2Weth;
}
/**
* @notice Temporary disable the ability to trade exits
*/
function setRedirectedExit(
uint256 _exitNum,
address _initialDestination,
address _newDestination,
bytes memory _newData
) internal override {
revert("TRADABLE_EXIT_TEMP_DISABLED");
}
receive() external payable {}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import { L1GatewayRouter } from "./L1GatewayRouter.sol";
import { IERC20Inbox } from "../L1ArbitrumMessenger.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20Bridge } from "../../libraries/IERC20Bridge.sol";
/**
* @title Handles deposits from L1 into L2 in ERC20-based rollups where custom token is used to pay for fees. Tokens are routed to their appropriate L1 gateway.
* @notice Router itself also conforms to the Gateway interface. Router also serves as an L1-L2 token address oracle.
*/
contract L1OrbitGatewayRouter is L1GatewayRouter {
using SafeERC20 for IERC20;
/**
* @notice Allows owner to register the default gateway.
* @param newL1DefaultGateway default gateway address
* @param _maxGas max gas for L2 retryable execution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost for L2 retryable ticket
* @param _feeAmount total amount of fees in native token to cover for retryable ticket costs. This amount will be transferred from user to bridge.
* @return Retryable ticket ID
*/
function setDefaultGateway(
address newL1DefaultGateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256 _feeAmount
) external onlyOwner returns (uint256) {
return
_setDefaultGateway(
newL1DefaultGateway,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
_feeAmount
);
}
/**
* @notice Allows L1 Token contract to trustlessly register its gateway.
* @dev Other setGateway method allows excess eth recovery from _maxSubmissionCost and is recommended.
* @param _gateway l1 gateway address
* @param _maxGas max gas for L2 retryable execution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost for L2 retryable ticket
* @param _feeAmount total amount of fees in native token to cover for retryable ticket costs. This amount will be transferred from user to bridge.
* @return Retryable ticket ID
*/
function setGateway(
address _gateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256 _feeAmount
) external returns (uint256) {
return
setGateway(_gateway, _maxGas, _gasPriceBid, _maxSubmissionCost, msg.sender, _feeAmount);
}
/**
* @notice Allows L1 Token contract to trustlessly register its gateway.
* @dev Other setGateway method allows excess eth recovery from _maxSubmissionCost and is recommended.
* @param _gateway l1 gateway address
* @param _maxGas max gas for L2 retryable execution
* @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
* @param _feeAmount total amount of fees in native token to cover for retryable ticket costs. This amount will be transferred from user to bridge.
* @return Retryable ticket ID
*/
function setGateway(
address _gateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
address _creditBackAddress,
uint256 _feeAmount
) public returns (uint256) {
return
_setGatewayWithCreditBack(
_gateway,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
_creditBackAddress,
_feeAmount
);
}
/**
* @notice Allows owner to register gateways for specific tokens.
* @param _token list of L1 token addresses
* @param _gateway list of L1 gateway addresses
* @param _maxGas max gas for L2 retryable execution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost for L2 retryable ticket
* @param _feeAmount total amount of fees in native token to cover for retryable ticket costs. This amount will be transferred from user to bridge.
* @return Retryable ticket ID
*/
function setGateways(
address[] memory _token,
address[] memory _gateway,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256 _feeAmount
) external onlyOwner returns (uint256) {
return
_setGateways(
_token,
_gateway,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
msg.sender,
_feeAmount
);
}
function _createRetryable(
address _inbox,
address _to,
address _refundTo,
address _user,
uint256 _totalFeeAmount,
uint256 _l2CallValue,
uint256 _maxSubmissionCost,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes memory _data
) internal override returns (uint256) {
{
// Transfer native token amount needed to pay for retryable fees to the inbox.
// Fee tokens will be transferred from msg.sender
address nativeFeeToken = IERC20Bridge(address(getBridge(_inbox))).nativeToken();
uint256 inboxNativeTokenBalance = IERC20(nativeFeeToken).balanceOf(_inbox);
if (inboxNativeTokenBalance < _totalFeeAmount) {
uint256 diff = _totalFeeAmount - inboxNativeTokenBalance;
IERC20(nativeFeeToken).safeTransferFrom(msg.sender, _inbox, diff);
}
}
return
IERC20Inbox(_inbox).createRetryableTicket(
_to,
_l2CallValue,
_maxSubmissionCost,
_refundTo,
_user,
_maxGas,
_gasPriceBid,
_totalFeeAmount,
_data
);
}
/**
* @notice Revert 'setGateway' entrypoint which doesn't have total amount of token fees as an argument.
*/
function setGateway(
address,
uint256,
uint256,
uint256,
address
) public payable override returns (uint256) {
revert("NOT_SUPPORTED_IN_ORBIT");
}
/**
* @notice Revert 'setDefaultGateway' entrypoint which doesn't have total amount of token fees as an argument.
*/
function setDefaultGateway(
address,
uint256,
uint256,
uint256
) external payable override onlyOwner returns (uint256) {
revert("NOT_SUPPORTED_IN_ORBIT");
}
/**
* @notice Revert 'setGateway' entrypoint which doesn't have total amount of token fees as an argument.
*/
function setGateway(
address,
uint256,
uint256,
uint256
) external payable override returns (uint256) {
revert("NOT_SUPPORTED_IN_ORBIT");
}
/**
* @notice Revert 'setGateways' entrypoint which doesn't have total amount of token fees as an argument.
*/
function setGateways(
address[] memory,
address[] memory,
uint256,
uint256,
uint256
) external payable override onlyOwner returns (uint256) {
revert("NOT_SUPPORTED_IN_ORBIT");
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import { L1ERC20Gateway, IERC20 } from "./L1ERC20Gateway.sol";
import { IERC20Inbox } from "../L1ArbitrumMessenger.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20Bridge } from "../../libraries/IERC20Bridge.sol";
/**
* @title Layer 1 Gateway contract for bridging standard ERC20s in ERC20-based rollup
* @notice This contract handles token deposits, holds the escrowed tokens on layer 1, and (ultimately) finalizes withdrawals.
* @dev Any ERC20 that requires non-standard functionality should use a separate gateway.
* Messages to layer 2 use the inbox's createRetryableTicket method.
*/
contract L1OrbitERC20Gateway is L1ERC20Gateway {
using SafeERC20 for IERC20;
function outboundTransferCustomRefund(
address _l1Token,
address _refundTo,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) public payable override returns (bytes memory res) {
// fees are paid in native token, so there is no use for ether
require(msg.value == 0, "NO_VALUE");
// We don't allow bridging of native token to avoid having multiple representations of it
// on child chain. Native token can be bridged directly through inbox using depositERC20().
require(_l1Token != _getNativeFeeToken(), "NOT_ALLOWED_TO_BRIDGE_FEE_TOKEN");
return
super.outboundTransferCustomRefund(
_l1Token,
_refundTo,
_to,
_amount,
_maxGas,
_gasPriceBid,
_data
);
}
function _parseUserEncodedData(bytes memory data)
internal
pure
override
returns (
uint256 maxSubmissionCost,
bytes memory callHookData,
uint256 tokenTotalFeeAmount
)
{
(maxSubmissionCost, callHookData, tokenTotalFeeAmount) = abi.decode(
data,
(uint256, bytes, uint256)
);
}
function _initiateDeposit(
address _refundTo,
address _from,
uint256, // _amount, this info is already contained in _data
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256 tokenTotalFeeAmount,
bytes memory _data
) internal override returns (uint256) {
return
sendTxToL2CustomRefund(
inbox,
counterpartGateway,
_refundTo,
_from,
tokenTotalFeeAmount,
0,
L2GasParams({
_maxSubmissionCost: _maxSubmissionCost,
_maxGas: _maxGas,
_gasPriceBid: _gasPriceBid
}),
_data
);
}
function _createRetryable(
address _inbox,
address _to,
address _refundTo,
address _user,
uint256 _totalFeeAmount,
uint256 _l2CallValue,
uint256 _maxSubmissionCost,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes memory _data
) internal override returns (uint256) {
{
// Transfer native token amount needed to pay for retryable fees to the inbox.
// Fee tokens will be transferred from user who initiated the action - that's `_user` account in
// case call was routed by router, or msg.sender in case gateway's entrypoint was called directly.
address nativeFeeToken = _getNativeFeeToken();
uint256 inboxNativeTokenBalance = IERC20(nativeFeeToken).balanceOf(_inbox);
if (inboxNativeTokenBalance < _totalFeeAmount) {
address transferFrom = isRouter(msg.sender) ? _user : msg.sender;
IERC20(nativeFeeToken).safeTransferFrom(
transferFrom,
_inbox,
_totalFeeAmount - inboxNativeTokenBalance
);
}
}
return
IERC20Inbox(_inbox).createRetryableTicket(
_to,
_l2CallValue,
_maxSubmissionCost,
_refundTo,
_user,
_maxGas,
_gasPriceBid,
_totalFeeAmount,
_data
);
}
/**
* @notice get rollup's native token that's used to pay for fees
*/
function _getNativeFeeToken() internal view returns (address) {
address bridge = address(getBridge(inbox));
return IERC20Bridge(bridge).nativeToken();
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import { L1CustomGateway } from "./L1CustomGateway.sol";
import { IERC20Inbox } from "../L1ArbitrumMessenger.sol";
import { IERC20Bridge } from "../../libraries/IERC20Bridge.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title Gateway for "custom" bridging functionality in an ERC20-based rollup.
* @notice Adds new entrypoints that have `_feeAmount` as parameter, while entrypoints without that parameter are reverted.
*/
contract L1OrbitCustomGateway is L1CustomGateway {
using SafeERC20 for IERC20;
/**
* @notice Allows L1 Token contract to trustlessly register its custom L2 counterpart, in an ERC20-based rollup. Retryable costs are paid in native token.
* @param _l2Address counterpart address of L1 token
* @param _maxGas max gas for L2 retryable execution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost for L2 retryable ticket
* @param _feeAmount total amount of fees in native token to cover for retryable ticket costs. This amount will be transferred from user to bridge.
* @return Retryable ticket ID
*/
function registerTokenToL2(
address _l2Address,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256 _feeAmount
) external returns (uint256) {
return
registerTokenToL2(
_l2Address,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
msg.sender,
_feeAmount
);
}
/**
* @notice Allows L1 Token contract to trustlessly register its custom L2 counterpart, in an ERC20-based rollup. Retryable costs are paid in native token.
* @param _l2Address counterpart address of L1 token
* @param _maxGas max gas for L2 retryable execution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost for L2 retryable ticket
* @param _creditBackAddress address for crediting back overpayment of _maxSubmissionCost
* @param _feeAmount total amount of fees in native token to cover for retryable ticket costs. This amount will be transferred from user to bridge.
* @return Retryable ticket ID
*/
function registerTokenToL2(
address _l2Address,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
address _creditBackAddress,
uint256 _feeAmount
) public returns (uint256) {
return
_registerTokenToL2(
_l2Address,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
_creditBackAddress,
_feeAmount
);
}
/**
* @notice Allows owner to force register a custom L1/L2 token pair.
* @dev _l1Addresses[i] counterpart is assumed to be _l2Addresses[i]
* @param _l1Addresses array of L1 addresses
* @param _l2Addresses array of L2 addresses
* @param _maxGas max gas for L2 retryable execution
* @param _gasPriceBid gas price for L2 retryable ticket
* @param _maxSubmissionCost base submission cost for L2 retryable ticket
* @param _feeAmount total amount of fees in native token to cover for retryable ticket costs. This amount will be transferred from user to bridge.
* @return Retryable ticket ID
*/
function forceRegisterTokenToL2(
address[] calldata _l1Addresses,
address[] calldata _l2Addresses,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256 _feeAmount
) external onlyOwner returns (uint256) {
return
_forceRegisterTokenToL2(
_l1Addresses,
_l2Addresses,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
_feeAmount
);
}
/**
* @notice Revert 'registerTokenToL2' entrypoint which doesn't have total amount of token fees as an argument.
*/
function registerTokenToL2(
address,
uint256,
uint256,
uint256,
address
) public payable override returns (uint256) {
revert("NOT_SUPPORTED_IN_ORBIT");
}
/**
* @notice Revert 'registerTokenToL2' entrypoint which doesn't have total amount of token fees as an argument.
*/
function registerTokenToL2(
address,
uint256,
uint256,
uint256
) external payable override returns (uint256) {
revert("NOT_SUPPORTED_IN_ORBIT");
}
/**
* @notice Revert 'forceRegisterTokenToL2' entrypoint which doesn't have total amount of token fees as an argument.
*/
function forceRegisterTokenToL2(
address[] calldata,
address[] calldata,
uint256,
uint256,
uint256
) external payable override onlyOwner returns (uint256) {
revert("NOT_SUPPORTED_IN_ORBIT");
}
function _parseUserEncodedData(bytes memory data)
internal
pure
override
returns (
uint256 maxSubmissionCost,
bytes memory callHookData,
uint256 tokenTotalFeeAmount
)
{
(maxSubmissionCost, callHookData, tokenTotalFeeAmount) = abi.decode(
data,
(uint256, bytes, uint256)
);
}
function _initiateDeposit(
address _refundTo,
address _from,
uint256, // _amount, this info is already contained in _data
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256 tokenTotalFeeAmount,
bytes memory _data
) internal override returns (uint256) {
return
sendTxToL2CustomRefund(
inbox,
counterpartGateway,
_refundTo,
_from,
tokenTotalFeeAmount,
0,
L2GasParams({
_maxSubmissionCost: _maxSubmissionCost,
_maxGas: _maxGas,
_gasPriceBid: _gasPriceBid
}),
_data
);
}
function _createRetryable(
address _inbox,
address _to,
address _refundTo,
address _user,
uint256 _totalFeeAmount,
uint256 _l2CallValue,
uint256 _maxSubmissionCost,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes memory _data
) internal override returns (uint256) {
{
// Transfer native token amount needed to pay for retryable fees to the inbox.
// Fee tokens will be transferred from user who initiated the action - that's `_user` account in
// case call was routed by router, or msg.sender in case gateway's entrypoint was called directly.
address nativeFeeToken = IERC20Bridge(address(getBridge(_inbox))).nativeToken();
uint256 inboxNativeTokenBalance = IERC20(nativeFeeToken).balanceOf(_inbox);
if (inboxNativeTokenBalance < _totalFeeAmount) {
address transferFrom = isRouter(msg.sender) ? _user : msg.sender;
IERC20(nativeFeeToken).safeTransferFrom(
transferFrom,
_inbox,
_totalFeeAmount - inboxNativeTokenBalance
);
}
}
return
IERC20Inbox(_inbox).createRetryableTicket(
_to,
_l2CallValue,
_maxSubmissionCost,
_refundTo,
_user,
_maxGas,
_gasPriceBid,
_totalFeeAmount,
_data
);
}
}// 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");
}// 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
);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.16;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./IUpgradeExecutor.sol";
/// @title A root contract from which it execute upgrades
/// @notice Does not contain upgrade logic itself, only the means to call upgrade contracts and execute them
/// @dev We use these upgrade contracts as they allow multiple actions to take place in an upgrade
/// and for these actions to interact. However because we are delegatecalling into these upgrade
/// contracts, it's important that these upgrade contract do not touch or modify contract state.
contract UpgradeExecutor is
Initializable,
AccessControlUpgradeable,
ReentrancyGuard,
IUpgradeExecutor
{
using Address for address;
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
/// @notice Emitted when an upgrade execution occurs
event UpgradeExecuted(address indexed upgrade, uint256 value, bytes data);
/// @notice Emitted when target call occurs
event TargetCallExecuted(address indexed target, uint256 value, bytes data);
constructor() {
_disableInitializers();
}
/// @notice Initialise the upgrade executor
/// @param admin The admin who can update other roles, and itself - ADMIN_ROLE
/// @param executors Can call the execute function - EXECUTOR_ROLE
function initialize(address admin, address[] memory executors) public initializer {
require(admin != address(0), "UpgradeExecutor: zero admin");
__AccessControl_init();
_setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
_setRoleAdmin(EXECUTOR_ROLE, ADMIN_ROLE);
_setupRole(ADMIN_ROLE, admin);
for (uint256 i = 0; i < executors.length; ++i) {
_setupRole(EXECUTOR_ROLE, executors[i]);
}
}
/// @notice Execute an upgrade by delegate calling an upgrade contract
/// @dev Only executor can call this. Since we're using a delegatecall here the Upgrade contract
/// will have access to the state of this contract - including the roles. Only upgrade contracts
/// that do not touch local state should be used.
function execute(address upgrade, bytes memory upgradeCallData)
public
payable
onlyRole(EXECUTOR_ROLE)
nonReentrant
{
// OZ Address library check if the address is a contract and bubble up inner revert reason
address(upgrade).functionDelegateCall(
upgradeCallData, "UpgradeExecutor: inner delegate call failed without reason"
);
emit UpgradeExecuted(upgrade, msg.value, upgradeCallData);
}
/// @notice Execute an upgrade by directly calling target contract
/// @dev Only executor can call this.
function executeCall(address target, bytes memory targetCallData)
public
payable
onlyRole(EXECUTOR_ROLE)
nonReentrant
{
// OZ Address library check if the address is a contract and bubble up inner revert reason
address(target).functionCallWithValue(
targetCallData, msg.value, "UpgradeExecutor: inner call failed without reason"
);
emit TargetCallExecuted(target, msg.value, targetCallData);
}
}// 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);
}
}
}// 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 // solhint-disable-next-line compiler-version pragma solidity >=0.5.0; pragma experimental ABIEncoderV2; import "@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; // implementation from https://github.com/makerdao/multicall/blob/1e1b44362640820bef92d0ccf5eeee25d9b41474/src/Multicall2.sol MIT License /// @title Multicall2 - Aggregate results from multiple read-only function calls /// @author Michael Elliot <[email protected]> /// @author Joshua Levine <[email protected]> /// @author Nick Johnson <[email protected]> contract Multicall2 { struct Call { address target; bytes callData; } struct Result { bool success; bytes returnData; } function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) { blockNumber = block.number; returnData = new bytes[](calls.length); for (uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); require(success, "Multicall aggregate: call failed"); returnData[i] = ret; } } function blockAndAggregate(Call[] memory calls) public returns ( uint256 blockNumber, bytes32 blockHash, Result[] memory returnData ) { (blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls); } function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { blockHash = blockhash(blockNumber); } function getBlockNumber() public view returns (uint256 blockNumber) { blockNumber = block.number; } function getCurrentBlockCoinbase() public view returns (address coinbase) { coinbase = block.coinbase; } function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { difficulty = block.difficulty; } function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { gaslimit = block.gaslimit; } function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { timestamp = block.timestamp; } function getEthBalance(address addr) public view returns (uint256 balance) { balance = addr.balance; } function getLastBlockHash() public view returns (bytes32 blockHash) { blockHash = blockhash(block.number - 1); } function tryAggregateGasRation(bool requireSuccess, Call[] memory calls) public returns (Result[] memory returnData) { returnData = new Result[](calls.length); uint256 gasPerCall = gasleft() / calls.length; for (uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call{ gas: gasleft() > gasPerCall ? gasPerCall : gasleft() }(calls[i].callData); if (requireSuccess) { require(success, "Multicall2 aggregate: call failed"); } returnData[i] = Result(success, ret); } } function tryAggregate(bool requireSuccess, Call[] memory calls) public returns (Result[] memory returnData) { returnData = new Result[](calls.length); for (uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); if (requireSuccess) { require(success, "Multicall2 aggregate: call failed"); } returnData[i] = Result(success, ret); } } function tryBlockAndAggregate(bool requireSuccess, Call[] memory calls) public returns ( uint256 blockNumber, bytes32 blockHash, Result[] memory returnData ) { blockNumber = block.number; blockHash = blockhash(block.number); returnData = tryAggregate(requireSuccess, calls); } } /// @title Arbitrum Multicall2 - Multicall2 contracts with L1 and L2 block numbers contract ArbMulticall2 { struct Call { address target; bytes callData; } struct Result { bool success; bytes returnData; } function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) { blockNumber = ArbSys(address(100)).arbBlockNumber(); returnData = new bytes[](calls.length); for (uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); require(success, "Multicall aggregate: call failed"); returnData[i] = ret; } } function blockAndAggregate(Call[] memory calls) public returns ( uint256 blockNumber, bytes32 blockHash, Result[] memory returnData ) { (blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls); } function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { blockHash = blockhash(blockNumber); } function getBlockNumber() public view returns (uint256 blockNumber) { blockNumber = ArbSys(address(100)).arbBlockNumber(); } function getL1BlockNumber() public view returns (uint256 l1BlockNumber) { l1BlockNumber = block.number; } function getCurrentBlockCoinbase() public view returns (address coinbase) { coinbase = block.coinbase; } function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { difficulty = block.difficulty; } function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { gaslimit = block.gaslimit; } function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { timestamp = block.timestamp; } function getEthBalance(address addr) public view returns (uint256 balance) { balance = addr.balance; } function getLastBlockHash() public view returns (bytes32 blockHash) { blockHash = blockhash(block.number - 1); } function tryAggregateGasRation(bool requireSuccess, Call[] memory calls) public returns (Result[] memory returnData) { returnData = new Result[](calls.length); uint256 gasPerCall = gasleft() / calls.length; for (uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call{ gas: gasleft() > gasPerCall ? gasPerCall : gasleft() }(calls[i].callData); if (requireSuccess) { require(success, "Multicall2 aggregate: call failed"); } returnData[i] = Result(success, ret); } } function tryAggregate(bool requireSuccess, Call[] memory calls) public returns (Result[] memory returnData) { returnData = new Result[](calls.length); for (uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); if (requireSuccess) { require(success, "Multicall2 aggregate: call failed"); } returnData[i] = Result(success, ret); } } function tryBlockAndAggregate(bool requireSuccess, Call[] memory calls) public returns ( uint256 blockNumber, bytes32 blockHash, Result[] memory returnData ) { blockNumber = ArbSys(address(100)).arbBlockNumber(); blockHash = blockhash(block.number); returnData = tryAggregate(requireSuccess, calls); } }
// 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));
}
}// 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)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// 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);
}
}// 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 IAccessControlUpgradeable {
/**
* @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.8.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-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;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
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));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
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");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
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");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// 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);
}// 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/ITransferAndCall.sol";
import "./L1ArbitrumGateway.sol";
interface ITradeableExitReceiver {
function onExitTransfer(
address sender,
uint256 exitNum,
bytes calldata data
) external returns (bool);
}
abstract contract L1ArbitrumExtendedGateway is L1ArbitrumGateway {
using Address for address;
struct ExitData {
bool isExit;
address _newTo;
bytes _newData;
}
mapping(bytes32 => ExitData) public redirectedExits;
event WithdrawRedirected(
address indexed from,
address indexed to,
uint256 indexed exitNum,
bytes newData,
bytes data,
bool madeExternalCall
);
/**
* @notice Allows a user to redirect their right to claim a withdrawal to another address.
* @dev This method also allows you to make an arbitrary call after the transfer.
* This does not validate if the exit was already triggered. It is assumed the `_exitNum` is
* validated off-chain to ensure this was not yet triggered.
* @param _exitNum Sequentially increasing exit counter determined by the L2 bridge
* @param _initialDestination address the L2 withdrawal call initially set as the destination.
* @param _newDestination address the L1 will now call instead of the previously set destination
* @param _newData data to be used in inboundEscrowAndCall
* @param _data optional data for external call upon transfering the exit
*/
function transferExitAndCall(
uint256 _exitNum,
address _initialDestination,
address _newDestination,
bytes calldata _newData,
bytes calldata _data
) external {
// the initial data doesn't make a difference when transfering you exit
// since the L2 bridge gives a unique exit ID to each exit
(address expectedSender, ) = getExternalCall(_exitNum, _initialDestination, "");
// if you want to transfer your exit, you must be the current destination
require(msg.sender == expectedSender, "NOT_EXPECTED_SENDER");
// the inboundEscrowAndCall functionality has been disabled, so no data is allowed
require(_newData.length == 0, "NO_DATA_ALLOWED");
setRedirectedExit(_exitNum, _initialDestination, _newDestination, _newData);
if (_data.length > 0) {
require(_newDestination.isContract(), "TO_NOT_CONTRACT");
bool success = ITradeableExitReceiver(_newDestination).onExitTransfer(
expectedSender,
_exitNum,
_data
);
require(success, "TRANSFER_HOOK_FAIL");
}
emit WithdrawRedirected(
expectedSender,
_newDestination,
_exitNum,
_newData,
_data,
_data.length > 0
);
}
/// @notice this does not verify if the external call was already done
function getExternalCall(
uint256 _exitNum,
address _initialDestination,
bytes memory _initialData
) public view virtual override returns (address target, bytes memory data) {
// this function is virtual so that subclasses can override it with custom logic where necessary
bytes32 withdrawData = encodeWithdrawal(_exitNum, _initialDestination);
ExitData storage exit = redirectedExits[withdrawData];
// here we don't authenticate `_initialData`. we could hash it into `withdrawData` but would increase gas costs
// this is safe because if the exit isn't overriden, the _initialData coming from L2 is trusted
// but if the exit is traded, all we care about is the latest user calldata
if (exit.isExit) {
return (exit._newTo, exit._newData);
} else {
return (_initialDestination, _initialData);
}
}
function setRedirectedExit(
uint256 _exitNum,
address _initialDestination,
address _newDestination,
bytes memory _newData
) internal virtual {
bytes32 withdrawData = encodeWithdrawal(_exitNum, _initialDestination);
redirectedExits[withdrawData] = ExitData(true, _newDestination, _newData);
}
function encodeWithdrawal(uint256 _exitNum, address _initialDestination)
public
pure
returns (bytes32)
{
// here we assume the L2 bridge gives a unique exitNum to each exit
return keccak256(abi.encode(_exitNum, _initialDestination));
}
}// 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]);
}
}
}// 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 "./ITokenGateway.sol";
interface ICustomGateway {
function l1ToL2Token(address _l1Token) external view returns (address _l2Token);
event TokenSet(address indexed l1Address, address indexed l2Address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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
* ====
*
* [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://diligence.consensys.net/posts/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.5.11/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.6.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
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
interface IWETH9 {
function deposit() external payable;
function withdraw(uint256 _amount) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "../libraries/IWETH9.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
contract TestWETH9 is ERC20, IWETH9 {
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {}
function deposit() external payable override {
_mint(msg.sender, msg.value);
}
function withdraw(uint256 _amount) external override {
_burn(msg.sender, _amount);
payable(address(msg.sender)).transfer(_amount);
}
}// SPDX-License-Identifier: Apache-2.0
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
interface IERC20Bridge {
/**
* @dev token that is escrowed in bridge on L1 side and minted on L2 as native currency. Also fees are paid in this token.
*/
function nativeToken() 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.
*/
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;
}
}
}// 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 {}
}// 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();
}
}// 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;
}// 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);
}
}// 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;
}
}// 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);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.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:
*
* ```
* 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}:
*
* ```
* 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.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
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(IAccessControlUpgradeable).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 ",
StringsUpgradeable.toHexString(account),
" is missing role ",
StringsUpgradeable.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());
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// 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);
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)
pragma solidity ^0.8.0;
import "./IBeacon.sol";
import "../Proxy.sol";
import "../ERC1967/ERC1967Upgrade.sol";
/**
* @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
*
* The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
* conflict with the storage layout of the implementation behind the proxy.
*
* _Available since v3.4._
*/
contract BeaconProxy is Proxy, ERC1967Upgrade {
/**
* @dev Initializes the proxy with `beacon`.
*
* If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
* will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
* constructor.
*
* Requirements:
*
* - `beacon` must be a contract with the interface {IBeacon}.
*/
constructor(address beacon, bytes memory data) payable {
_upgradeBeaconToAndCall(beacon, data, false);
}
/**
* @dev Returns the current beacon address.
*/
function _beacon() internal view virtual returns (address) {
return _getBeacon();
}
/**
* @dev Returns the current implementation address of the associated beacon.
*/
function _implementation() internal view virtual override returns (address) {
return IBeacon(_getBeacon()).implementation();
}
/**
* @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
*
* If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
*
* Requirements:
*
* - `beacon` must be a contract.
* - The implementation returned by `beacon` must be a contract.
*/
function _setBeacon(address beacon, bytes memory data) internal virtual {
_upgradeBeaconToAndCall(beacon, data, false);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
pragma solidity ^0.8.0;
import "../Proxy.sol";
import "./ERC1967Upgrade.sol";
/**
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
* implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
/**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
*
* If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
* function call, and allows initializing the storage of the proxy like a Solidity constructor.
*/
constructor(address _logic, bytes memory _data) payable {
_upgradeToAndCall(_logic, _data, false);
}
/**
* @dev Returns the current implementation address.
*/
function _implementation() internal view virtual override returns (address impl) {
return ERC1967Upgrade._getImplementation();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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.
*/
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].
*/
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);
}// 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: 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);
}// SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity >0.6.0 <0.9.0;
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
interface ITransferAndCall is IERC20Upgradeable {
function transferAndCall(
address to,
uint256 value,
bytes memory data
) external returns (bool success);
event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
}
/**
* @notice note that implementation of ITransferAndCallReceiver is not expected to return a success bool
*/
interface ITransferAndCallReceiver {
function onTokenTransfer(
address _sender,
uint256 _value,
bytes memory _data
) 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 "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../L1ArbitrumMessenger.sol";
import "./IL1ArbitrumGateway.sol";
import "../../libraries/ProxyUtil.sol";
import "../../libraries/gateway/GatewayMessageHandler.sol";
import "../../libraries/gateway/TokenGateway.sol";
import "../../libraries/ITransferAndCall.sol";
import "../../libraries/ERC165.sol";
/**
* @title Common interface for gatways on L1 messaging to Arbitrum.
*/
abstract contract L1ArbitrumGateway is
L1ArbitrumMessenger,
TokenGateway,
ERC165,
IL1ArbitrumGateway
{
using SafeERC20 for IERC20;
using Address for address;
address public override inbox;
event DepositInitiated(
address l1Token,
address indexed _from,
address indexed _to,
uint256 indexed _sequenceNumber,
uint256 _amount
);
event WithdrawalFinalized(
address l1Token,
address indexed _from,
address indexed _to,
uint256 indexed _exitNum,
uint256 _amount
);
modifier onlyCounterpartGateway() override {
address _inbox = inbox;
// a message coming from the counterpart gateway was executed by the bridge
address bridge = address(super.getBridge(_inbox));
require(msg.sender == bridge, "NOT_FROM_BRIDGE");
// and the outbox reports that the L2 address of the sender is the counterpart gateway
address l2ToL1Sender = super.getL2ToL1Sender(_inbox);
require(l2ToL1Sender == counterpartGateway, "ONLY_COUNTERPART_GATEWAY");
_;
}
function postUpgradeInit() external {
// it is assumed the L1 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 _l2Counterpart,
address _router,
address _inbox
) internal {
TokenGateway._initialize(_l2Counterpart, _router);
// L1 gateway must have a router
require(_router != address(0), "BAD_ROUTER");
require(_inbox != address(0), "BAD_INBOX");
inbox = _inbox;
}
/**
* @notice Finalizes a withdrawal via Outbox message; callable only by L2Gateway.outboundTransfer
* @param _token L1 address of token being withdrawn from
* @param _from initiator of withdrawal
* @param _to address the L2 withdrawal call set as the destination.
* @param _amount Token amount being withdrawn
* @param _data encoded exitNum (Sequentially increasing exit counter determined by the L2Gateway) and additinal hook data
*/
function finalizeInboundTransfer(
address _token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) public payable virtual override onlyCounterpartGateway {
// this function is marked as virtual so superclasses can override it to add modifiers
(uint256 exitNum, bytes memory callHookData) = GatewayMessageHandler.parseToL1GatewayMsg(
_data
);
if (callHookData.length != 0) {
// callHookData should always be 0 since inboundEscrowAndCall is disabled
callHookData = bytes("");
}
// we ignore the returned data since the callHook feature is now disabled
(_to, ) = getExternalCall(exitNum, _to, callHookData);
inboundEscrowTransfer(_token, _to, _amount);
emit WithdrawalFinalized(_token, _from, _to, exitNum, _amount);
}
function getExternalCall(
uint256, /* _exitNum */
address _initialDestination,
bytes memory _initialData
) public view virtual returns (address target, bytes memory data) {
// this method is virtual so the destination of a call can be changed
// using tradeable exits in a subclass (L1ArbitrumExtendedGateway)
target = _initialDestination;
data = _initialData;
}
function inboundEscrowTransfer(
address _l1Token,
address _dest,
uint256 _amount
) internal virtual {
// this method is virtual since different subclasses can handle escrow differently
IERC20(_l1Token).safeTransfer(_dest, _amount);
}
/**
* @dev Only excess gas is refunded to the _refundTo account, l2 call value is always returned to the _to account
*/
function createOutboundTxCustomRefund(
address _refundTo,
address _from,
uint256, /* _tokenAmount */
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
bytes memory _outboundCalldata
) internal virtual returns (uint256) {
// We make this function virtual since outboundTransfer logic is the same for many gateways
// but sometimes (ie weth) you construct the outgoing message differently.
// msg.value is sent, but 0 is set to the L2 call value
// the eth sent is used to pay for the tx's gas
return
sendTxToL2CustomRefund(
inbox,
counterpartGateway,
_refundTo,
_from,
msg.value, // we forward the L1 call value to the inbox
0, // l2 call value 0 by default
L2GasParams({
_maxSubmissionCost: _maxSubmissionCost,
_maxGas: _maxGas,
_gasPriceBid: _gasPriceBid
}),
_outboundCalldata
);
}
/**
* @notice DEPRECATED - look at createOutboundTxCustomRefund instead
*/
function createOutboundTx(
address _from,
uint256 _tokenAmount,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
bytes memory _outboundCalldata
) internal returns (uint256) {
return
createOutboundTxCustomRefund(
_from,
_from,
_tokenAmount,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
_outboundCalldata
);
}
/**
* @notice DEPRECATED - look at outboundTransferCustomRefund instead
*/
function outboundTransfer(
address _l1Token,
address _to,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
bytes calldata _data
) public payable override returns (bytes memory res) {
return
outboundTransferCustomRefund(_l1Token, _to, _to, _amount, _maxGas, _gasPriceBid, _data);
}
/**
* @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
) public payable virtual override returns (bytes memory res) {
require(isRouter(msg.sender), "NOT_FROM_ROUTER");
// This function is set as public and virtual so that subclasses can override
// it and add custom validation for callers (ie only whitelisted users)
address _from;
uint256 seqNum;
bytes memory extraData;
{
uint256 _maxSubmissionCost;
uint256 tokenTotalFeeAmount;
if (super.isRouter(msg.sender)) {
// router encoded
(_from, extraData) = GatewayMessageHandler.parseFromRouterToGateway(_data);
} else {
_from = msg.sender;
extraData = _data;
}
// unpack user encoded data
(_maxSubmissionCost, extraData, tokenTotalFeeAmount) = _parseUserEncodedData(extraData);
// the inboundEscrowAndCall functionality has been disabled, so no data is allowed
require(extraData.length == 0, "EXTRA_DATA_DISABLED");
require(_l1Token.isContract(), "L1_NOT_CONTRACT");
address l2Token = calculateL2TokenAddress(_l1Token);
require(l2Token != address(0), "NO_L2_TOKEN_SET");
_amount = outboundEscrowTransfer(_l1Token, _from, _amount);
// we override the res field to save on the stack
res = getOutboundCalldata(_l1Token, _from, _to, _amount, extraData);
seqNum = _initiateDeposit(
_refundTo,
_from,
_amount,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
tokenTotalFeeAmount,
res
);
}
emit DepositInitiated(_l1Token, _from, _to, seqNum, _amount);
return abi.encode(seqNum);
}
function outboundEscrowTransfer(
address _l1Token,
address _from,
uint256 _amount
) internal virtual returns (uint256 amountReceived) {
// this method is virtual since different subclasses can handle escrow differently
// user funds are escrowed on the gateway using this function
uint256 prevBalance = IERC20(_l1Token).balanceOf(address(this));
IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount);
uint256 postBalance = IERC20(_l1Token).balanceOf(address(this));
return postBalance - prevBalance;
}
function getOutboundCalldata(
address _l1Token,
address _from,
address _to,
uint256 _amount,
bytes memory _data
) public view virtual override returns (bytes memory outboundCalldata) {
// this function is public so users can query how much calldata will be sent to the L2
// before execution
// it is virtual since different gateway subclasses can build this calldata differently
// ( ie the standard ERC20 gateway queries for a tokens name/symbol/decimals )
bytes memory emptyBytes = "";
outboundCalldata = abi.encodeWithSelector(
ITokenGateway.finalizeInboundTransfer.selector,
_l1Token,
_from,
_to,
_amount,
GatewayMessageHandler.encodeToL2GatewayMsg(emptyBytes, _data)
);
return outboundCalldata;
}
function supportsInterface(bytes4 interfaceId)
public
view
virtual
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);
}
/**
* @notice Parse data that was encoded by user and passed into the outbound TX entrypoint
* @dev In case of standard ETH-based rollup, format of encoded data is expected to be:
* - maxSubmissionCost (uint256)
* - callHookData (bytes)
* In case of ERC20-based rollup, format of encoded data is expected to be:
* - maxSubmissionCost (uint256)
* - tokenTotalFeeAmount (uint256)
* - callHookData (bytes)
* @param data data encoded by user
* @return maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
* @return callHookData Calldata for extra call in inboundEscrowAndCall on L2
* @return tokenTotalFeeAmount Amount of fees to be deposited in native token to cover for retryable ticket cost (used only in ERC20-based rollups, otherwise 0)
*/
function _parseUserEncodedData(bytes memory data)
internal
pure
virtual
returns (
uint256 maxSubmissionCost,
bytes memory callHookData,
uint256 tokenTotalFeeAmount
)
{
(maxSubmissionCost, callHookData) = abi.decode(data, (uint256, bytes));
}
/**
* @notice Intermediate internal function that passes on parameters needed to trigger creation of retryable ticket.
* @param _refundTo Account, or its L2 alias if it have code in L1, to be credited with excess gas refund in L2
* @param _from Initiator of deposit
* @param _amount Token amount being deposited
* @param _maxGas Max gas deducted from user's L2 balance to cover L2 execution
* @param _gasPriceBid Gas price for L2 execution
* @param _maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
* @param _data encoded data from router and user
* @return res abi encoded inbox sequence number
*/
function _initiateDeposit(
address _refundTo,
address _from,
uint256 _amount,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost,
uint256, // tokenTotalFeeAmount - amount of fees to be deposited in native token to cover for retryable ticket cost (used only in ERC20-based rollups)
bytes memory _data
) internal virtual returns (uint256) {
return
createOutboundTxCustomRefund(
_refundTo,
_from,
_amount,
_maxGas,
_gasPriceBid,
_maxSubmissionCost,
_data
);
}
}// 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/utils/Address.sol";
import "../../libraries/AddressAliasHelper.sol";
import "../../libraries/BytesLib.sol";
import "../../libraries/ProxyUtil.sol";
import "../IArbToken.sol";
import "../L2ArbitrumMessenger.sol";
import "../../libraries/gateway/GatewayMessageHandler.sol";
import "../../libraries/gateway/TokenGateway.sol";
/**
* @title Common interface for gatways on Arbitrum messaging to L1.
*/
abstract contract L2ArbitrumGateway is L2ArbitrumMessenger, TokenGateway {
using Address for address;
uint256 public exitNum;
event DepositFinalized(
address indexed l1Token,
address indexed _from,
address indexed _to,
uint256 _amount
);
event WithdrawalInitiated(
address l1Token,
address indexed _from,
address indexed _to,
uint256 indexed _l2ToL1Id,
uint256 _exitNum,
uint256 _amount
);
modifier onlyCounterpartGateway() override {
require(
msg.sender == AddressAliasHelper.applyL1ToL2Alias(counterpartGateway),
"ONLY_COUNTERPART_GATEWAY"
);
_;
}
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 _l1Counterpart, address _router) internal override {
TokenGateway._initialize(_l1Counterpart, _router);
// L1 gateway must have a router
require(_router != address(0), "BAD_ROUTER");
}
function createOutboundTx(
address _from,
uint256, /* _tokenAmount */
bytes memory _outboundCalldata
) internal virtual returns (uint256) {
// We make this function virtual since outboundTransfer logic is the same for many gateways
// but sometimes (ie weth) you construct the outgoing message differently.
// exitNum incremented after being included in _outboundCalldata
exitNum++;
return
sendTxToL1(
// default to sending no callvalue to the L1
0,
_from,
counterpartGateway,
_outboundCalldata
);
}
function getOutboundCalldata(
address _token,
address _from,
address _to,
uint256 _amount,
bytes memory _data
) public view override returns (bytes memory outboundCalldata) {
outboundCalldata = abi.encodeWithSelector(
ITokenGateway.finalizeInboundTransfer.selector,
_token,
_from,
_to,
_amount,
GatewayMessageHandler.encodeFromL2GatewayMsg(exitNum, _data)
);
return outboundCalldata;
}
function outboundTransfer(
address _l1Token,
address _to,
uint256 _amount,
bytes calldata _data
) public payable returns (bytes memory) {
return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data);
}
/**
* @notice Initiates a token withdrawal from Arbitrum to Ethereum
* @param _l1Token l1 address of token
* @param _to destination address
* @param _amount amount of tokens withdrawn
* @return res encoded unique identifier for withdrawal
*/
function outboundTransfer(
address _l1Token,
address _to,
uint256 _amount,
uint256, /* _maxGas */
uint256, /* _gasPriceBid */
bytes calldata _data
) public payable override returns (bytes memory res) {
// This function is set as public and virtual so that subclasses can override
// it and add custom validation for callers (ie only whitelisted users)
// the function is marked as payable to conform to the inheritance setup
// this particular code path shouldn't have a msg.value > 0
// TODO: remove this invariant for execution markets
require(msg.value == 0, "NO_VALUE");
address _from;
bytes memory _extraData;
{
if (isRouter(msg.sender)) {
(_from, _extraData) = GatewayMessageHandler.parseFromRouterToGateway(_data);
} else {
_from = msg.sender;
_extraData = _data;
}
}
// the inboundEscrowAndCall functionality has been disabled, so no data is allowed
require(_extraData.length == 0, "EXTRA_DATA_DISABLED");
uint256 id;
{
address l2Token = calculateL2TokenAddress(_l1Token);
require(l2Token.isContract(), "TOKEN_NOT_DEPLOYED");
require(IArbToken(l2Token).l1Address() == _l1Token, "NOT_EXPECTED_L1_TOKEN");
_amount = outboundEscrowTransfer(l2Token, _from, _amount);
id = triggerWithdrawal(_l1Token, _from, _to, _amount, _extraData);
}
return abi.encode(id);
}
function triggerWithdrawal(
address _l1Token,
address _from,
address _to,
uint256 _amount,
bytes memory _data
) internal returns (uint256) {
// exit number used for tradeable exits
uint256 currExitNum = exitNum;
// unique id used to identify the L2 to L1 tx
uint256 id = createOutboundTx(
_from,
_amount,
getOutboundCalldata(_l1Token, _from, _to, _amount, _data)
);
emit WithdrawalInitiated(_l1Token, _from, _to, id, currExitNum, _amount);
return id;
}
function outboundEscrowTransfer(
address _l2Token,
address _from,
uint256 _amount
) internal virtual returns (uint256 amountBurnt) {
// this method is virtual since different subclasses can handle escrow differently
// user funds are escrowed on the gateway using this function
// burns L2 tokens in order to release escrowed L1 tokens
IArbToken(_l2Token).bridgeBurn(_from, _amount);
// by default we assume that the amount we send to bridgeBurn is the amount burnt
// this might not be the case for every token
return _amount;
}
function inboundEscrowTransfer(
address _l2Address,
address _dest,
uint256 _amount
) internal virtual {
// this method is virtual since different subclasses can handle escrow differently
IArbToken(_l2Address).bridgeMint(_dest, _amount);
}
/**
* @notice Mint on L2 upon L1 deposit.
* If token not yet deployed and symbol/name/decimal data is included, deploys StandardArbERC20
* @dev Callable only by the L1ERC20Gateway.outboundTransfer method. For initial deployments of a token the L1 L1ERC20Gateway
* is expected to include the deployData. If not a L1 withdrawal is automatically triggered for the user
* @param _token L1 address of ERC20
* @param _from account that initiated the deposit in the L1
* @param _to account to be credited with the tokens in the L2 (can be the user's L2 account or a contract)
* @param _amount token amount to be minted to the user
* @param _data encoded symbol/name/decimal data for deploy, in addition to any additional callhook data
*/
function finalizeInboundTransfer(
address _token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external payable override onlyCounterpartGateway {
(bytes memory gatewayData, bytes memory callHookData) = GatewayMessageHandler
.parseFromL1GatewayMsg(_data);
if (callHookData.length != 0) {
// callHookData should always be 0 since inboundEscrowAndCall is disabled
callHookData = bytes("");
}
address expectedAddress = calculateL2TokenAddress(_token);
if (!expectedAddress.isContract()) {
bool shouldHalt = handleNoContract(
_token,
expectedAddress,
_from,
_to,
_amount,
gatewayData
);
if (shouldHalt) return;
}
// ignores gatewayData if token already deployed
{
// validate if L1 address supplied matches that of the expected L2 address
(bool success, bytes memory _l1AddressData) = expectedAddress.staticcall(
abi.encodeWithSelector(IArbToken.l1Address.selector)
);
bool shouldWithdraw;
if (!success || _l1AddressData.length < 32) {
shouldWithdraw = true;
} else {
// we do this in the else branch since we want to avoid reverts
// and `toAddress` reverts if _l1AddressData has a short length
// `_l1AddressData` should be 12 bytes of padding then 20 bytes for the address
address expectedL1Address = BytesLib.toAddress(_l1AddressData, 12);
if (expectedL1Address != _token) {
shouldWithdraw = true;
}
}
if (shouldWithdraw) {
// 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(_token, address(this), _from, _amount, "");
return;
}
}
inboundEscrowTransfer(expectedAddress, _to, _amount);
emit DepositFinalized(_token, _from, _to, _amount);
return;
}
// returns if function should halt after
function handleNoContract(
address _l1Token,
address expectedL2Address,
address _from,
address _to,
uint256 _amount,
bytes memory gatewayData
) internal virtual returns (bool shouldHalt);
}// SPDX-License-Identifier: Apache-2.0
/*
* Copyright 2019-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 "./ICloneable.sol";
contract Cloneable is ICloneable {
string private constant NOT_CLONE = "NOT_CLONE";
bool private isMasterCopy;
constructor() {
isMasterCopy = true;
}
function isMaster() external view override returns (bool) {
return isMasterCopy;
}
function safeSelfDestruct(address payable dest) internal {
require(!isMasterCopy, NOT_CLONE);
selfdestruct(dest);
}
}// 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 "./aeERC20.sol";
import "./BytesParser.sol";
import "../arbitrum/IArbToken.sol";
/**
* @title Standard (i.e., non-custom) contract used as a base for different L2 Gateways
*/
abstract contract L2GatewayToken is aeERC20, IArbToken {
address public l2Gateway;
address public override l1Address;
modifier onlyGateway() {
require(msg.sender == l2Gateway, "ONLY_GATEWAY");
_;
}
/**
* @notice initialize the token
* @dev the L2 bridge assumes this does not fail or revert
* @param name_ ERC20 token name
* @param symbol_ ERC20 token symbol
* @param decimals_ ERC20 decimals
* @param l2Gateway_ L2 gateway this token communicates with
* @param l1Counterpart_ L1 address of ERC20
*/
function _initialize(
string memory name_,
string memory symbol_,
uint8 decimals_,
address l2Gateway_,
address l1Counterpart_
) internal virtual {
require(l2Gateway_ != address(0), "INVALID_GATEWAY");
require(l2Gateway == address(0), "ALREADY_INIT");
l2Gateway = l2Gateway_;
l1Address = l1Counterpart_;
aeERC20._initialize(name_, symbol_, decimals_);
}
/**
* @notice Mint tokens on L2. Callable path is L1Gateway depositToken (which handles L1 escrow), which triggers L2Gateway, which calls this
* @param account recipient of tokens
* @param amount amount of tokens minted
*/
function bridgeMint(address account, uint256 amount) external virtual override onlyGateway {
_mint(account, amount);
}
/**
* @notice Burn tokens on L2.
* @dev only the token bridge can call this
* @param account owner of tokens
* @param amount amount of tokens burnt
*/
function bridgeBurn(address account, uint256 amount) external virtual override onlyGateway {
_burn(account, amount);
}
}// 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 "./BytesLib.sol";
library BytesParser {
using BytesLib for bytes;
function toUint8(bytes memory input) internal pure returns (bool success, uint8 res) {
if (input.length != 32) {
return (false, 0);
}
// TODO: try catch to handle error
uint256 inputNum = abi.decode(input, (uint256));
if (inputNum > type(uint8).max) {
return (false, 0);
}
res = uint8(inputNum);
success = true;
}
function toString(bytes memory input) internal pure returns (bool success, string memory res) {
if (input.length == 0) {
success = false;
// return default value of string
} else if (input.length == 32) {
// TODO: can validate anything other than length and being null terminated?
if (input[31] != bytes1(0x00)) return (false, res);
else success = true;
// here we assume its a null terminated Bytes32 string
// https://github.com/ethereum/solidity/blob/5852972ec148bc041909400affc778dee66d384d/test/libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol#L89
// https://github.com/Arachnid/solidity-stringutils
uint256 len = 32;
while (len > 0 && input[len - 1] == bytes1(0x00)) {
len--;
}
bytes memory inputTruncated = new bytes(len);
for (uint8 i = 0; i < len; i++) {
inputTruncated[i] = input[i];
}
// we can't just do `res := input` because of the null values in the end
// TODO: can we instead use a bitwise AND? build it dynamically with the length
assembly {
res := inputTruncated
}
} else {
// TODO: try catch to handle error
success = true;
res = abi.decode(input, (string));
}
}
}// 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.
*/
/**
* @title Minimum expected interface for L2 token that interacts with the L2 token bridge (this is the interface necessary
* for a custom token that interacts with the bridge, see TestArbCustomToken.sol for an example implementation).
*/
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
interface IArbToken {
/**
* @notice should increase token supply by amount, and should (probably) only be callable by the L1 bridge.
*/
function bridgeMint(address account, uint256 amount) external;
/**
* @notice should decrease token supply by amount, and should (probably) only be callable by the L1 bridge.
*/
function bridgeBurn(address account, uint256 amount) external;
/**
* @return address of layer 1 token
*/
function l1Address() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/MathUpgradeable.sol";
/**
* @dev String operations.
*/
library StringsUpgradeable {
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 = MathUpgradeable.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 `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, MathUpgradeable.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);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.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 ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// 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;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
pragma solidity ^0.8.0;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback() external payable virtual {
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive() external payable virtual {
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overridden should call `super._beforeFallback()`.
*/
function _beforeFallback() internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.3) (proxy/ERC1967/ERC1967Upgrade.sol)
pragma solidity ^0.8.2;
import "../beacon/IBeacon.sol";
import "../../interfaces/IERC1967.sol";
import "../../interfaces/draft-IERC1822.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*
* @custom:oz-upgrades-unsafe-allow delegatecall
*/
abstract contract ERC1967Upgrade is IERC1967 {
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallUUPS(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
// Upgrades from old implementations will perform a rollback test. This test requires the new
// implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
// this special case will break upgrade paths from old UUPS implementation to new ones.
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
require(
Address.isContract(IBeacon(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(
address newBeacon,
bytes memory data,
bool forceCall
) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
* ====
*
* [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://diligence.consensys.net/posts/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.5.11/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 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.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @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: MIT /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity ^0.8.0; /* solhint-disable no-inline-assembly */ library BytesLib { function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= (_start + 20), "Read out of bounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= (_start + 1), "Read out of bounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= (_start + 32), "Read out of bounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= (_start + 32), "Read out of bounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } } /* solhint-enable no-inline-assembly */
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright 2019, 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 ICloneable {
function isMaster() 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.
*/
pragma solidity ^0.8.0;
import "../libraries/draft-ERC20PermitUpgradeable.sol";
import "./TransferAndCallToken.sol";
/// @title Arbitrum extended ERC20
/// @notice The recommended ERC20 implementation for Layer 2 tokens
/// @dev This implements the ERC20 standard with transferAndCall extenstion/affordances
contract aeERC20 is ERC20PermitUpgradeable, TransferAndCallToken {
using AddressUpgradeable for address;
constructor() initializer {
// this is expected to be used as the logic contract behind a proxy
// override the constructor if you don't wish to use the initialize method
}
function _initialize(
string memory name_,
string memory symbol_,
uint8 decimals_
) internal initializer {
__ERC20Permit_init(name_);
__ERC20_init(name_, symbol_);
_setupDecimals(decimals_);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
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) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 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 10, 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 * 8) < value ? 1 : 0);
}
}
}// 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 IERC165Upgradeable {
/**
* @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;
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);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.3) (interfaces/IERC1967.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*
* _Available since v4.9._
*/
interface IERC1967 {
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol)
pragma solidity ^0.8.0;
import "./ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/**
* @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* This implementation is based on OpenZeppelin implementation (https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.8.3/contracts/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol).
* The only difference is that we are importing our own `ERC20Upgradeable` implementation instead of the OpenZeppelin's one.
*
*/
abstract contract ERC20PermitUpgradeable is
Initializable,
ERC20Upgradeable,
IERC20PermitUpgradeable,
EIP712Upgradeable
{
using CountersUpgradeable for CountersUpgradeable.Counter;
mapping(address => CountersUpgradeable.Counter) private _nonces;
// solhint-disable-next-line var-name-mixedcase
bytes32 private constant _PERMIT_TYPEHASH =
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
/**
* @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
* However, to ensure consistency with the upgradeable transpiler, we will continue
* to reserve a slot.
* @custom:oz-renamed-from _PERMIT_TYPEHASH
*/
// solhint-disable-next-line var-name-mixedcase
bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
/**
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
*
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
*/
function __ERC20Permit_init(string memory name) internal onlyInitializing {
__EIP712_init_unchained(name, "1");
}
function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {}
/**
* @dev See {IERC20Permit-permit}.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override {
require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
bytes32 structHash = keccak256(
abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)
);
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSAUpgradeable.recover(hash, v, r, s);
require(signer == owner, "ERC20Permit: invalid signature");
_approve(owner, spender, value);
}
/**
* @dev See {IERC20Permit-nonces}.
*/
function nonces(address owner) public view virtual override returns (uint256) {
return _nonces[owner].current();
}
/**
* @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4();
}
/**
* @dev "Consume a nonce": return the current value and increment.
*
* _Available since v4.1._
*/
function _useNonce(address owner) internal virtual returns (uint256 current) {
CountersUpgradeable.Counter storage nonce = _nonces[owner];
current = nonce.current();
nonce.increment();
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity >0.6.0 <0.9.0;
import "./ERC20Upgradeable.sol";
import "./ITransferAndCall.sol";
// Implementation from https://github.com/smartcontractkit/LinkToken/blob/master/contracts/v0.6/TransferAndCallToken.sol
/**
* @notice based on Implementation from https://github.com/smartcontractkit/LinkToken/blob/master/contracts/v0.6/ERC677Token.sol
* The implementation doesn't return a bool on onTokenTransfer. This is similar to the proposed 677 standard, but still incompatible - thus we don't refer to it as such.
*/
abstract contract TransferAndCallToken is ERC20Upgradeable, ITransferAndCall {
/**
* @dev transfer token to a contract address with additional data if the recipient is a contact.
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
* @param _data The extra data to be passed to the receiving contract.
*/
function transferAndCall(
address _to,
uint256 _value,
bytes memory _data
) public virtual override returns (bool success) {
super.transfer(_to, _value);
emit Transfer(msg.sender, _to, _value, _data);
if (isContract(_to)) {
contractFallback(_to, _value, _data);
}
return true;
}
// PRIVATE
function contractFallback(
address _to,
uint256 _value,
bytes memory _data
) private {
ITransferAndCallReceiver receiver = ITransferAndCallReceiver(_to);
receiver.onTokenTransfer(msg.sender, _value, _data);
}
function isContract(address _addr) private view returns (bool hasCode) {
uint256 length;
assembly {
length := extcodesize(_addr)
}
return length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is based on OpenZeppelin implementation (https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.8.3/contracts/token/ERC20/ERC20Upgradeable.sol)
* with a small modification. OZ implementation removed `decimals` storage variable when they did upgrade to Solidity 0.8. Since we're already using older OZ implementation, here we're
* adding back the `decimals` storage variable along with the appropriate getter and setter. That way we avoid changes in storage layout that would've happen due to OZ removing token
* decimals storage variable.
*
*/
contract ERC20Upgradeable is
Initializable,
ContextUpgradeable,
IERC20Upgradeable,
IERC20MetadataUpgradeable
{
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_)
internal
onlyInitializing
{
_name = name_;
_symbol = symbol_;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender)
public
view
virtual
override
returns (uint256)
{
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue)
public
virtual
returns (bool)
{
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal virtual {
_decimals = decimals_;
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[44] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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.
*/
interface IERC20PermitUpgradeable {
/**
* @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].
*/
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.8.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../StringsUpgradeable.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSAUpgradeable {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.0;
import "./ECDSAUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*
* @custom:storage-size 52
*/
abstract contract EIP712Upgradeable is Initializable {
/* solhint-disable var-name-mixedcase */
bytes32 private _HASHED_NAME;
bytes32 private _HASHED_VERSION;
bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
__EIP712_init_unchained(name, version);
}
function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev The hash of the name parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712NameHash() internal virtual view returns (bytes32) {
return _HASHED_NAME;
}
/**
* @dev The hash of the version parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712VersionHash() internal virtual view returns (bytes32) {
return _HASHED_VERSION;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
pragma solidity ^0.8.0;
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
*/
library CountersUpgradeable {
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@arbitrum/=node_modules/@arbitrum/",
"@offchainlabs/=node_modules/@offchainlabs/",
"@arbitrum/=node_modules/@arbitrum/",
"@ensdomains/=node_modules/@ensdomains/",
"@offchainlabs/=node_modules/@offchainlabs/",
"eth-gas-reporter/=node_modules/eth-gas-reporter/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"nitro-contracts/=lib/nitro-contracts/src/"
],
"optimizer": {
"enabled": true,
"runs": 100
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"L1AtomicTokenBridgeCreator_L2FactoryCannotBeChanged","type":"error"},{"inputs":[],"name":"L1AtomicTokenBridgeCreator_OnlyRollupOwner","type":"error"},{"inputs":[],"name":"L1AtomicTokenBridgeCreator_ProxyAdminNotFound","type":"error"},{"inputs":[],"name":"L1AtomicTokenBridgeCreator_RollupOwnershipMisconfig","type":"error"},{"inputs":[],"name":"L1AtomicTokenBridgeCreator_TemplatesNotSet","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"inbox","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"standardGateway","type":"address"},{"internalType":"address","name":"customGateway","type":"address"},{"internalType":"address","name":"wethGateway","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"indexed":false,"internalType":"struct L1DeploymentAddresses","name":"l1Deployment","type":"tuple"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"standardGateway","type":"address"},{"internalType":"address","name":"customGateway","type":"address"},{"internalType":"address","name":"wethGateway","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"beaconProxyFactory","type":"address"},{"internalType":"address","name":"upgradeExecutor","type":"address"},{"internalType":"address","name":"multicall","type":"address"}],"indexed":false,"internalType":"struct L2DeploymentAddresses","name":"l2Deployment","type":"tuple"},{"indexed":false,"internalType":"address","name":"proxyAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"upgradeExecutor","type":"address"}],"name":"OrbitTokenBridgeCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"inbox","type":"address"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"standardGateway","type":"address"},{"internalType":"address","name":"customGateway","type":"address"},{"internalType":"address","name":"wethGateway","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"indexed":false,"internalType":"struct L1DeploymentAddresses","name":"l1","type":"tuple"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"standardGateway","type":"address"},{"internalType":"address","name":"customGateway","type":"address"},{"internalType":"address","name":"wethGateway","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"beaconProxyFactory","type":"address"},{"internalType":"address","name":"upgradeExecutor","type":"address"},{"internalType":"address","name":"multicall","type":"address"}],"indexed":false,"internalType":"struct L2DeploymentAddresses","name":"l2","type":"tuple"}],"name":"OrbitTokenBridgeDeploymentSet","type":"event"},{"anonymous":false,"inputs":[],"name":"OrbitTokenBridgeTemplatesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"canonicalL2FactoryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"inbox","type":"address"},{"internalType":"address","name":"rollupOwner","type":"address"},{"internalType":"uint256","name":"maxGasForContracts","type":"uint256"},{"internalType":"uint256","name":"gasPriceBid","type":"uint256"}],"name":"createTokenBridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"gasLimitForL2FactoryDeployment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"inbox","type":"address"}],"name":"getRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"inboxToL1Deployment","outputs":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"standardGateway","type":"address"},{"internalType":"address","name":"customGateway","type":"address"},{"internalType":"address","name":"wethGateway","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"inboxToL2Deployment","outputs":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"standardGateway","type":"address"},{"internalType":"address","name":"customGateway","type":"address"},{"internalType":"address","name":"wethGateway","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"beaconProxyFactory","type":"address"},{"internalType":"address","name":"upgradeExecutor","type":"address"},{"internalType":"address","name":"multicall","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract L1TokenBridgeRetryableSender","name":"_retryableSender","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"l1Multicall","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l1Templates","outputs":[{"internalType":"contract L1GatewayRouter","name":"routerTemplate","type":"address"},{"internalType":"contract L1ERC20Gateway","name":"standardGatewayTemplate","type":"address"},{"internalType":"contract L1CustomGateway","name":"customGatewayTemplate","type":"address"},{"internalType":"contract L1WethGateway","name":"wethGatewayTemplate","type":"address"},{"internalType":"contract L1OrbitGatewayRouter","name":"feeTokenBasedRouterTemplate","type":"address"},{"internalType":"contract L1OrbitERC20Gateway","name":"feeTokenBasedStandardGatewayTemplate","type":"address"},{"internalType":"contract L1OrbitCustomGateway","name":"feeTokenBasedCustomGatewayTemplate","type":"address"},{"internalType":"contract IUpgradeExecutor","name":"upgradeExecutor","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l1Weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2CustomGatewayTemplate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2MulticallTemplate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2RouterTemplate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2StandardGatewayTemplate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2TokenBridgeFactoryTemplate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2WethGatewayTemplate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l2WethTemplate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"retryableSender","outputs":[{"internalType":"contract L1TokenBridgeRetryableSender","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"inbox","type":"address"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"standardGateway","type":"address"},{"internalType":"address","name":"customGateway","type":"address"},{"internalType":"address","name":"wethGateway","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"internalType":"struct L1DeploymentAddresses","name":"l1Deployment","type":"tuple"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"standardGateway","type":"address"},{"internalType":"address","name":"customGateway","type":"address"},{"internalType":"address","name":"wethGateway","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"beaconProxyFactory","type":"address"},{"internalType":"address","name":"upgradeExecutor","type":"address"},{"internalType":"address","name":"multicall","type":"address"}],"internalType":"struct L2DeploymentAddresses","name":"l2Deployment","type":"tuple"}],"name":"setDeployment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"contract L1GatewayRouter","name":"routerTemplate","type":"address"},{"internalType":"contract L1ERC20Gateway","name":"standardGatewayTemplate","type":"address"},{"internalType":"contract L1CustomGateway","name":"customGatewayTemplate","type":"address"},{"internalType":"contract L1WethGateway","name":"wethGatewayTemplate","type":"address"},{"internalType":"contract L1OrbitGatewayRouter","name":"feeTokenBasedRouterTemplate","type":"address"},{"internalType":"contract L1OrbitERC20Gateway","name":"feeTokenBasedStandardGatewayTemplate","type":"address"},{"internalType":"contract L1OrbitCustomGateway","name":"feeTokenBasedCustomGatewayTemplate","type":"address"},{"internalType":"contract IUpgradeExecutor","name":"upgradeExecutor","type":"address"}],"internalType":"struct L1AtomicTokenBridgeCreator.L1Templates","name":"_l1Templates","type":"tuple"},{"internalType":"address","name":"_l2TokenBridgeFactoryTemplate","type":"address"},{"internalType":"address","name":"_l2RouterTemplate","type":"address"},{"internalType":"address","name":"_l2StandardGatewayTemplate","type":"address"},{"internalType":"address","name":"_l2CustomGatewayTemplate","type":"address"},{"internalType":"address","name":"_l2WethGatewayTemplate","type":"address"},{"internalType":"address","name":"_l2WethTemplate","type":"address"},{"internalType":"address","name":"_l2MulticallTemplate","type":"address"},{"internalType":"address","name":"_l1Weth","type":"address"},{"internalType":"address","name":"_l1Multicall","type":"address"},{"internalType":"uint256","name":"_gasLimitForL2FactoryDeployment","type":"uint256"}],"name":"setTemplates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b506200001c62000022565b620000e4565b600054610100900460ff16156200008f5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161015620000e2576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b615f9880620000f46000396000f3fe6080604052600436106200015b5760003560e01c8063888139d411620000c5578063bfd3e5181162000078578063bfd3e518146200050a578063c4d66de8146200052c578063d7eee6ca1462000551578063d9ce0ef91462000573578063f2fde38b14620005e5578063fd40ad85146200060a57600080fd5b8063888139d414620003bd5780638c99e31c14620003e45780638da5cb5b14620004065780639095765e1462000426578063a5595da91462000448578063b1460a7114620004e857600080fd5b806346052706116200011e5780634605270614620002275780634c1496711462000305578063715018a6146200032c57806381fb918414620003445780638277742b14620003695780638369166d146200038057600080fd5b8063146bf4b114620001605780631aeef2e2146200019f57806336dddb9714620001c1578063381c9d9914620001e3578063410831861462000205575b600080fd5b3480156200016d57600080fd5b5060785462000182906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b348015620001ac57600080fd5b5060715462000182906001600160a01b031681565b348015620001ce57600080fd5b5060685462000182906001600160a01b031681565b348015620001f057600080fd5b5060725462000182906001600160a01b031681565b3480156200021257600080fd5b5060745462000182906001600160a01b031681565b3480156200023457600080fd5b50620002aa620002463660046200286c565b6066602052600090815260409020805460018201546002830154600384015460048501546005860154600687015460078801546008909801546001600160a01b03978816989688169795861696948616959384169492841693918216928216911689565b604080516001600160a01b039a8b168152988a1660208a01529689169688019690965293871660608701529186166080860152851660a0850152841660c0840152831660e08301529091166101008201526101200162000196565b3480156200031257600080fd5b506200032a62000324366004620029c3565b6200062c565b005b3480156200033957600080fd5b506200032a6200091c565b3480156200035157600080fd5b506200032a6200036336600462002a88565b62000934565b6200032a6200037a36600462002b7c565b62000a58565b3480156200038d57600080fd5b50620001826200039f3660046200286c565b6001600160a01b039081166000908152606560205260409020541690565b348015620003ca57600080fd5b50620003d560675481565b60405190815260200162000196565b348015620003f157600080fd5b5060775462000182906001600160a01b031681565b3480156200041357600080fd5b506033546001600160a01b031662000182565b3480156200043357600080fd5b5060755462000182906001600160a01b031681565b3480156200045557600080fd5b50606954606a54606b54606c54606d54606e54606f5460705462000495976001600160a01b03908116978116968116958116948116938116928116911688565b604080516001600160a01b03998a16815297891660208901529588169587019590955292861660608601529085166080850152841660a0840152831660c083015290911660e08201526101000162000196565b348015620004f557600080fd5b5060795462000182906001600160a01b031681565b3480156200051757600080fd5b50607a5462000182906001600160a01b031681565b3480156200053957600080fd5b506200032a6200054b3660046200286c565b620019f0565b3480156200055e57600080fd5b5060735462000182906001600160a01b031681565b3480156200058057600080fd5b50620005d2620005923660046200286c565b606560205260009081526040902080546001820154600283015460038401546004909401546001600160a01b039384169492841693918216928216911685565b6040516200019695949392919062002bc7565b348015620005f257600080fd5b506200032a620006043660046200286c565b62001c0e565b3480156200061757600080fd5b5060765462000182906001600160a01b031681565b826001600160a01b031663e78cea926040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200066b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000691919062002bf9565b6001600160a01b031663cb23bcb56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620006cf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006f5919062002bf9565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000733573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000759919062002bf9565b6001600160a01b0316336001600160a01b0316146200078b57604051630dbb2a6f60e11b815260040160405180910390fd5b6001600160a01b038381166000818152606560209081526040808320875181546001600160a01b031990811691881691909117825588840151600180840180548416928a169290921790915589840151600280850180548516928b16929092179091556060808c0151600380870180548716928d16929092179091556080808e0151600497880180548816918e1691909117905560668952988790208c5181548716908d16178155978c015193880180548616948c16949094179093558a86015191870180548516928b169290921790915589015190850180548316918916919091179055938701519083018054851691871691909117905560a0860151600583018054851691871691909117905560c0860151600683018054851691871691909117905560e08601516007830180548516918716919091179055610100860151600890920180549093169190941617905590517e3661d67ef6fa28d5937e796b7701a68fbf54c16d9434eb705715ebc28f424b906200090f908590859062002cfc565b60405180910390a2505050565b6200092662001c8d565b62000932600062001ce9565b565b6200093e62001c8d565b8a60696200094d828262002d4b565b50506071546001600160a01b0316158015906200097857506071546001600160a01b038b8116911614155b156200099757604051633157c93960e01b815260040160405180910390fd5b607180546001600160a01b03199081166001600160a01b038d8116919091179092556072805482168c84161790556073805482168b84161790556074805482168a84161790556075805482168984161790556076805482168884161790556077805482168784161790556078805482168684161790556079805490911691841691909117905560678190556040517f8a040c53d83c1e62b7c4c7c88774e55775a95bdaadc2c0ef9f63b8d1a118ffdc90600090a15050505050505050505050565b6069546001600160a01b031662000a82576040516376a1604d60e11b815260040160405180910390fd5b6000846001600160a01b031663e78cea926040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000ac3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ae9919062002bf9565b6001600160a01b031663cb23bcb56040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000b27573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000b4d919062002bf9565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000b8b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000bb1919062002bf9565b9050806001600160a01b03166391d14854826001600160a01b03166307bd02656040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000c01573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000c27919062002e2d565b6040516001600160e01b031960e084901b16815260048101919091526001600160a01b0387166024820152604401602060405180830381865afa15801562000c73573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000c99919062002e47565b62000cb757604051632625e8ed60e11b815260040160405180910390fd5b6001600160a01b0385811660009081526065602052604081205490911615159062000ce28762001d3b565b6040805160a080820183526000808352602080840182905283850182905260608085018390526080808601849052865161012081018852848152928301849052958201839052810182905293840181905290830181905260c0830181905260e08301819052610100830152919250831562000dbb576001600160a01b03808a16600090815260656020908152604091829020825160a08101845281548516815260018201548516928101929092526002810154841692820192909252600382015483166060820152600490910154909116608082015291505b6000896001600160a01b031663e78cea926040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000dfc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000e22919062002bf9565b6001600160a01b031663cb23bcb56040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000e60573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000e86919062002bf9565b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000ec4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000eea919062002e2d565b905062000f136040518060400160405280600381526020016226192960e91b8152508262001e17565b6001600160a01b031682526040805180820190915260058152644c3253475760d81b602082015262000f46908262001e17565b82602001906001600160a01b031690816001600160a01b03168152505062000f8c604051806040016040528060058152602001644c3243475760d81b8152508262001e17565b6001600160a01b03908116604084015284166200100f5762000fcc604051806040016040528060058152602001644c3257475760d81b8152508262001e17565b6001600160a01b031660608301526040805180820190915260038152624c325760e81b602082015262001000908262001e17565b6001600160a01b031660808301525b62001036604051806040016040528060038152602001624c324560e81b8152508262001e17565b6001600160a01b031660e08301526200104f8162001ed6565b6001600160a01b031660a0830152620010688162001f43565b6001600160a01b031660c0830152620010818162001f83565b8261010001906001600160a01b031690816001600160a01b031681525050506000896001600160a01b0316638b3240a06040518163ffffffff1660e01b8152600401602060405180830381865afa158015620010e1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001107919062002bf9565b90506001600160a01b038116620011315760405163eac0860960e01b815260040160405180910390fd5b846200153f5760006001600160a01b0385166200115a576069546001600160a01b031662001167565b606d546001600160a01b03165b90506200119c62001194604051806040016040528060038152602001622618a960e91b8152508d62002002565b828462002037565b6001600160a01b039081168552600091508516620011c657606a546001600160a01b0316620011d3565b606e546001600160a01b03165b905060006200120c62001204604051806040016040528060058152602001644c3153475760d81b8152508e62002002565b838562002037565b9050806001600160a01b031663a01893bf856020015187600001518f604051806020016200123a90620027ff565b6020820181038252601f19601f82011660405250805190602001208960c001516040518663ffffffff1660e01b8152600401620012a69594939291906001600160a01b039586168152938516602085015291841660408401526060830152909116608082015260a00190565b600060405180830381600087803b158015620012c157600080fd5b505af1158015620012d6573d6000803e3d6000fd5b5050506001600160a01b039182166020870152506000915085166200130757606b546001600160a01b031662001314565b606f546001600160a01b03165b905060006200134562001204604051806040016040528060058152602001644c3143475760d81b8152508e62002002565b60408086015187519151637c643b2f60e11b81526001600160a01b03918216600482015291811660248301528e811660448301528a811660648301529192509082169063f8c8765e90608401600060405180830381600087803b158015620013ac57600080fd5b505af1158015620013c1573d6000803e3d6000fd5b5050506001600160a01b0391821660408701525085169050620014c7576000620014206200140d604051806040016040528060058152602001644c3157475760d81b8152508d62002002565b606c546001600160a01b03168462002037565b9050806001600160a01b0316631459457a846060015186600001518e607860009054906101000a90046001600160a01b031688608001516040518663ffffffff1660e01b81526004016200147995949392919062002bc7565b600060405180830381600087803b1580156200149457600080fd5b505af1158015620014a9573d6000803e3d6000fd5b5050506001600160a01b039182166060860152506078541660808401525b82600001516001600160a01b0316631459457a878560200151600086600001518f6040518663ffffffff1660e01b81526004016200150a95949392919062002bc7565b600060405180830381600087803b1580156200152557600080fd5b505af11580156200153a573d6000803e3d6000fd5b505050505b6200154c8a888662002092565b6040805160e0810182526001600160a01b03808d168252607a5481166020830152339282018390526060820192909252608081018a905260a08101899052600060c082015290851615620015d257620015b185620015ab8a8c62002e81565b620022d6565b60c08201819052620015d2906001600160a01b0387169033908e9062002403565b6040805160e0810182526072546001600160a01b039081168252607354811660208301526074548116928201929092526000916060820190881662001623576075546001600160a01b031662001626565b60005b6001600160a01b0316815260200160006001600160a01b0316886001600160a01b03160362001661576076546001600160a01b031662001664565b60005b6001600160a01b0390811682526070548116602083015260775481166040909201919091529091506000908c163b15620016aa5761111161111160901b018c01620016ac565b8b5b9050620016be83838888858e6200245f565b87620019e1578b6001600160a01b03168d6001600160a01b03167f9a9203aa9ddcf21d8523e422e009214f0447efca13201ecdd802d8663092de7e8888888e6040516200170f949392919062002ea3565b60405180910390a385606560008f6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555090505084606660008f6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060c08201518160060160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160080160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050505b50505050505050505050505050565b600054610100900460ff161580801562001a115750600054600160ff909116105b8062001a2d5750303b15801562001a2d575060005460ff166001145b62001a965760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562001aba576000805461ff0019166101001790555b62001ac4620024f3565b606880546001600160a01b0319166001600160a01b0384169081179091556040805163204a7f0760e21b81529051638129fc1c9160048082019260009290919082900301818387803b15801562001b1a57600080fd5b505af115801562001b2f573d6000803e3d6000fd5b5050505062001ba362001b4a3061111161111160901b010190565b604051606b60f91b6020820152602560fa1b60218201526001600160601b0319606083901b166022820152600160ff1b603682015260009060370160408051601f19818403018152919052805160209091012092915050565b607a80546001600160a01b0319166001600160a01b0392909216919091179055801562001c0a576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b62001c1862001c8d565b6001600160a01b03811662001c7f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162001a8d565b62001c8a8162001ce9565b50565b6033546001600160a01b03163314620009325760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162001a8d565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600080826001600160a01b031663e78cea926040518163ffffffff1660e01b8152600401602060405180830381865afa15801562001d7d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001da3919062002bf9565b9050806001600160a01b031663e1758bd86040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801562001e02575060408051601f3d908101601f1916820190925262001dff9181019062002bf9565b60015b62001e105750600092915050565b9392505050565b600062001ecd62001e29848462002527565b60405162001e3a602082016200280d565b601f1982820381018352601f90910116604052607a546001600160a01b031662001e648662001ed6565b604080516020808201835260008252915162001e839493920162002f3c565b60408051601f198184030181529082905262001ea3929160200162002f73565b60408051601f198184030181529190528051602090910120607a546001600160a01b031662002561565b90505b92915050565b600062001ed062001f04604051806040016040528060048152602001634c32504160e01b8152508462002527565b60405162001f15602082016200281b565b601f1982820381018352601f90910116604052805160209190910120607a546001600160a01b031662002561565b600062001ed062001f7260405180604001604052806005815260200164261921282360d91b8152508462002527565b60405162001f156020820162002829565b600062001ed062001fb1604051806040016040528060048152602001634c324d4360e01b8152508462002527565b607754604080516001600160a01b03909216803b838101602090810190935280845262001fe8939290916000918401903c6200258b565b8051602090910120607a546001600160a01b031662002561565b600082826040516020016200201992919062002fa6565b60405160208183030381529060405280519060200120905092915050565b6000838383604051806020016040528060008152506040516200205a906200280d565b620020689392919062002f3c565b8190604051809103906000f590508015801562002089573d6000803e3d6000fd5b50949350505050565b6071546040805160206001600160a01b03909316803b8083018501909352828252600093620020c8939185918401903c6200258b565b90506001600160a01b03821615620021a457600083606754620020ec919062002e81565b90506000620020fc8483620022d6565b9050620021156001600160a01b03851633888462002403565b606754604051632a4f421360e11b81526001600160a01b0388169163549e842691620021559160009182918291339182918e908b908e9060040162002fda565b6020604051808303816000875af115801562002175573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200219b919062002e2d565b505050620022d0565b805160405163a66b327d60e01b81526000916001600160a01b0387169163a66b327d91620021df918590600401918252602082015260400190565b602060405180830381865afa158015620021fd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002223919062002e2d565b905060008460675462002237919062002e81565b6200224390836200303f565b60675460405163679b6ded60e01b81529192506001600160a01b0388169163679b6ded918491620022869160009182918991339182918f908e9060040162003055565b60206040518083038185885af1158015620022a5573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190620022cc919062002e2d565b5050505b50505050565b600080836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002318573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200233e9190620030b2565b90508060ff1660120362002356578291505062001ed0565b60128160ff161015620023d557600062002372826012620030d7565b6200237f90600a620031f0565b6200238b908562003201565b9050836200239b836012620030d7565b620023a890600a620031f0565b620023b4908362002e81565b1015620023cb5780620023c78162003224565b9150505b915062001ed09050565b620023e2601282620030d7565b620023ef90600a620031f0565b620023fb908462002e81565b949350505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052620022d0908590620025b9565b60685460c08701516001600160a01b0390911690635fc788d69062002485574762002488565b60005b88888888602001518833896040518963ffffffff1660e01b8152600401620024b7979695949392919062003240565b6000604051808303818588803b158015620024d157600080fd5b505af1158015620024e6573d6000803e3d6000fd5b5050505050505050505050565b600054610100900460ff166200251d5760405162461bcd60e51b815260040162001a8d906200333f565b6200093262002697565b606854600090839083906200254d906001600160a01b031661111161111160901b010190565b60405160200162002019939291906200338a565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b6060815182604051602001620025a3929190620033c0565b6040516020818303038152906040529050919050565b600062002610826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316620026cc9092919063ffffffff16565b80519091501562002692578080602001905181019062002631919062002e47565b620026925760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162001a8d565b505050565b600054610100900460ff16620026c15760405162461bcd60e51b815260040162001a8d906200333f565b620009323362001ce9565b6060620023fb848460008585600080866001600160a01b03168587604051620026f6919062003430565b60006040518083038185875af1925050503d806000811462002735576040519150601f19603f3d011682016040523d82523d6000602084013e6200273a565b606091505b50915091506200274d8783838762002758565b979650505050505050565b60608315620027cc578251600003620027c4576001600160a01b0385163b620027c45760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162001a8d565b5081620023fb565b620023fb8383815115620027e35781518083602001fd5b8060405162461bcd60e51b815260040162001a8d91906200344e565b610881806200346483390190565b610ebb8062003ce583390190565b61070f8062004ba083390190565b610cb480620052af83390190565b6001600160a01b03169052565b6001600160a01b038116811462001c8a57600080fd5b8035620028678162002844565b919050565b6000602082840312156200287f57600080fd5b813562001e108162002844565b604051610120810167ffffffffffffffff81118282101715620028bf57634e487b7160e01b600052604160045260246000fd5b60405290565b60405160a0810167ffffffffffffffff81118282101715620028bf57634e487b7160e01b600052604160045260246000fd5b600061012082840312156200290b57600080fd5b620029156200288c565b905062002922826200285a565b815262002932602083016200285a565b602082015262002945604083016200285a565b604082015262002958606083016200285a565b60608201526200296b608083016200285a565b60808201526200297e60a083016200285a565b60a08201526200299160c083016200285a565b60c0820152620029a460e083016200285a565b60e0820152610100620029b98184016200285a565b9082015292915050565b60008060008385036101e0811215620029db57600080fd5b8435620029e88162002844565b935060a0601f1982011215620029fd57600080fd5b5062002a08620028c5565b602085013562002a188162002844565b8152604085013562002a2a8162002844565b6020820152606085013562002a3f8162002844565b6040820152608085013562002a548162002844565b606082015260a085013562002a698162002844565b6080820152915062002a7f8560c08601620028f7565b90509250925092565b60008060008060008060008060008060008b8d0361024081121562002aac57600080fd5b6101008082121562002abd57600080fd5b8d9c508c0135905062002ad08162002844565b99506101208c013562002ae38162002844565b98506101408c013562002af68162002844565b97506101608c013562002b098162002844565b96506101808c013562002b1c8162002844565b95506101a08c013562002b2f8162002844565b945062002b406101c08d016200285a565b935062002b516101e08d016200285a565b925062002b626102008d016200285a565b91506102208c013590509295989b509295989b9093969950565b6000806000806080858703121562002b9357600080fd5b843562002ba08162002844565b9350602085013562002bb28162002844565b93969395505050506040820135916060013590565b6001600160a01b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b60006020828403121562002c0c57600080fd5b815162001e108162002844565b80516001600160a01b03908116835260208083015182169084015260408083015182169084015260608083015182169084015260809182015116910152565b60018060a01b0380825116835280602083015116602084015280604083015116604084015250606081015162002c92606084018262002837565b50608081015162002ca7608084018262002837565b5060a081015162002cbc60a084018262002837565b5060c081015162002cd160c084018262002837565b5060e081015162002ce660e084018262002837565b5061010080820151620022d08285018262002837565b6101c0810162002d0d828562002c19565b62001e1060a083018462002c58565b80546001600160a01b0319166001600160a01b0392909216919091179055565b6000813562001ed08162002844565b813562002d588162002844565b62002d64818362002d1c565b50602082013562002d758162002844565b62002d84816001840162002d1c565b5062002da162002d976040840162002d3c565b6002830162002d1c565b62002dbd62002db36060840162002d3c565b6003830162002d1c565b62002dd962002dcf6080840162002d3c565b6004830162002d1c565b62002df562002deb60a0840162002d3c565b6005830162002d1c565b62002e1162002e0760c0840162002d3c565b6006830162002d1c565b62001c0a62002e2360e0840162002d3c565b6007830162002d1c565b60006020828403121562002e4057600080fd5b5051919050565b60006020828403121562002e5a57600080fd5b8151801515811462001e1057600080fd5b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161562002e9e5762002e9e62002e6b565b500290565b610200810162002eb4828762002c19565b62002ec360a083018662002c58565b6001600160a01b039384166101c0830152919092166101e09092019190915292915050565b60005b8381101562002f0557818101518382015260200162002eeb565b50506000910152565b6000815180845262002f2881602086016020860162002ee8565b601f01601f19169290920160200192915050565b6001600160a01b0384811682528316602082015260606040820181905260009062002f6a9083018462002f0e565b95945050505050565b6000835162002f8781846020880162002ee8565b83519083019062002f9d81836020880162002ee8565b01949350505050565b6000835162002fba81846020880162002ee8565b60609390931b6001600160601b0319169190920190815260140192915050565b600061012060018060a01b03808d1684528b60208501528a6040850152808a1660608501528089166080850152508660a08401528560c08401528460e0840152806101008401526200302f8184018562002f0e565b9c9b505050505050505050505050565b8082018082111562001ed05762001ed062002e6b565b600061010060018060a01b03808c1684528a602085015289604085015280891660608501528088166080850152508560a08401528460c08401528060e0840152620030a38184018562002f0e565b9b9a5050505050505050505050565b600060208284031215620030c557600080fd5b815160ff8116811462001e1057600080fd5b60ff828116828216039081111562001ed05762001ed062002e6b565b600181815b808511156200313457816000190482111562003118576200311862002e6b565b808516156200312657918102915b93841c9390800290620030f8565b509250929050565b6000826200314d5750600162001ed0565b816200315c5750600062001ed0565b81600181146200317557600281146200318057620031a0565b600191505062001ed0565b60ff84111562003194576200319462002e6b565b50506001821b62001ed0565b5060208310610133831016604e8410600b8410161715620031c5575081810a62001ed0565b620031d18383620030f3565b8060001904821115620031e857620031e862002e6b565b029392505050565b600062001ecd60ff8416836200313c565b6000826200321f57634e487b7160e01b600052601260045260246000fd5b500490565b60006001820162003239576200323962002e6b565b5060010190565b87516001600160a01b0390811682526020808a01518216818401526040808b01518316818501526060808c01518416818601526080808d01518187015260a08d8101518188015260c0808f0151818901528d51871660e0890152948d01518616610100880152928c01518516610120870152908b015184166101408601528a015183166101608501528901518216610180840152880151166101a08201526102e08101620032f36101c083018862002c19565b6200330361026083018762002837565b6200331361028083018662002837565b620033236102a083018562002837565b620033336102c083018462002837565b98975050505050505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600084516200339e81846020890162002ee8565b919091019283525060601b6001600160601b0319166020820152603401919050565b710608060405234801561001057600080fd5b560741b8152606160f81b601282015260f083901b6001600160f01b03191660138201526a4030801030001cb00079ff60a91b601582015281516000906200342281602080860190870162002ee8565b919091016020019392505050565b600082516200344481846020870162002ee8565b9190910192915050565b60208152600062001ecd602083018462002f0e56fe608060405234801561001057600080fd5b50336001600160a01b03166359659e906040518163ffffffff1660e01b8152600401602060405180830381865afa15801561004f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610073919061046c565b604051806020016040528060008152506100958282600061009c60201b60201c565b5050610508565b6100a583610167565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a26000825111806100e65750805b1561016257610160836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610150919061046c565b8361030a60201b6100291760201c565b505b505050565b61017a8161033660201b6100551760201c565b6101d95760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b61024d816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561021a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023e919061046c565b61033660201b6100551760201c565b6102b25760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b60648201526084016101d0565b806102e97fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5060001b61034560201b6100641760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b606061032f838360405180606001604052806027815260200161085a60279139610348565b9392505050565b6001600160a01b03163b151590565b90565b6060600080856001600160a01b03168560405161036591906104b9565b600060405180830381855af49150503d80600081146103a0576040519150601f19603f3d011682016040523d82523d6000602084013e6103a5565b606091505b5090925090506103b7868383876103c1565b9695505050505050565b60608315610430578251600003610429576001600160a01b0385163b6104295760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101d0565b508161043a565b61043a8383610442565b949350505050565b8151156104525781518083602001fd5b8060405162461bcd60e51b81526004016101d091906104d5565b60006020828403121561047e57600080fd5b81516001600160a01b038116811461032f57600080fd5b60005b838110156104b0578181015183820152602001610498565b50506000910152565b600082516104cb818460208701610495565b9190910192915050565b60208152600082518060208401526104f4816040850160208701610495565b601f01601f19169190910160400192915050565b610343806105176000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610067565b610100565b565b606061004e83836040518060600160405280602781526020016102e760279139610124565b9392505050565b6001600160a01b03163b151590565b90565b600061009a7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fb919061024a565b905090565b3660008037600080366000845af43d6000803e80801561011f573d6000f35b3d6000fd5b6060600080856001600160a01b0316856040516101419190610297565b600060405180830381855af49150503d806000811461017c576040519150601f19603f3d011682016040523d82523d6000602084013e610181565b606091505b50915091506101928683838761019c565b9695505050505050565b6060831561020e578251600003610207576101b685610055565b6102075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610218565b6102188383610220565b949350505050565b8151156102305781518083602001fd5b8060405162461bcd60e51b81526004016101fe91906102b3565b60006020828403121561025c57600080fd5b81516001600160a01b038116811461004e57600080fd5b60005b8381101561028e578181015183820152602001610276565b50506000910152565b600082516102a9818460208701610273565b9190910192915050565b60208152600082518060208401526102d2816040850160208701610273565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e9bed491ce4cc7495def60dc616a13f39ccd912637e0c8ba02d45400506de9c064736f6c63430008100033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564608060405260405162000ebb38038062000ebb833981016040819052620000269162000497565b828162000036828260006200004d565b50620000449050826200008a565b505050620005ca565b6200005883620000e5565b600082511180620000665750805b1562000085576200008383836200012760201b620001691760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000b562000156565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000e2816200018f565b50565b620000f08162000244565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200014f838360405180606001604052806027815260200162000e9460279139620002f8565b9392505050565b60006200018060008051602062000e7483398151915260001b6200037760201b620001951760201c565b546001600160a01b0316919050565b6001600160a01b038116620001fa5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200022360008051602062000e7483398151915260001b6200037760201b620001951760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6200025a816200037a60201b620001981760201c565b620002be5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001f1565b80620002237f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200037760201b620001951760201c565b6060600080856001600160a01b03168560405162000317919062000577565b600060405180830381855af49150503d806000811462000354576040519150601f19603f3d011682016040523d82523d6000602084013e62000359565b606091505b5090925090506200036d8683838762000389565b9695505050505050565b90565b6001600160a01b03163b151590565b60608315620003fd578251600003620003f5576001600160a01b0385163b620003f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001f1565b508162000409565b62000409838362000411565b949350505050565b815115620004225781518083602001fd5b8060405162461bcd60e51b8152600401620001f1919062000595565b80516001600160a01b03811681146200045657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200048e57818101518382015260200162000474565b50506000910152565b600080600060608486031215620004ad57600080fd5b620004b8846200043e565b9250620004c8602085016200043e565b60408501519092506001600160401b0380821115620004e657600080fd5b818601915086601f830112620004fb57600080fd5b8151818111156200051057620005106200045b565b604051601f8201601f19908116603f011681019083821181831017156200053b576200053b6200045b565b816040528281528960208487010111156200055557600080fd5b6200056883602083016020880162000471565b80955050505050509250925092565b600082516200058b81846020870162000471565b9190910192915050565b6020815260008251806020840152620005b681604085016020870162000471565b601f01601f19169190910160400192915050565b61089a80620005da6000396000f3fe60806040523661001357610011610017565b005b6100115b61001f6101a7565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a576100536101da565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a57610053610231565b63070d7c6960e41b6001600160e01b031982160161009a57610053610277565b621eb96f60e61b6001600160e01b03198216016100b9576100536102a8565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102e8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102fc565b565b606061018e838360405180606001604052806027815260200161083e6027913961030c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101e4610384565b60006101f33660048184610691565b81019061020091906106d7565b905061021d8160405180602001604052806000815250600061038f565b505060408051602081019091526000815290565b60606000806102433660048184610691565b8101906102509190610708565b915091506102608282600161038f565b604051806020016040528060008152509250505090565b6060610281610384565b60006102903660048184610691565b81019061029d91906106d7565b905061021d816103bb565b60606102b2610384565b60006102bc6101a7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102f2610384565b60006102bc610412565b610167610307610412565b610421565b6060600080856001600160a01b03168560405161032991906107ee565b600060405180830381855af49150503d8060008114610364576040519150601f19603f3d011682016040523d82523d6000602084013e610369565b606091505b509150915061037a86838387610445565b9695505050505050565b341561016757600080fd5b610398836104c4565b6000825111806103a55750805b156103b6576103b48383610169565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e46101a7565b604080516001600160a01b03928316815291841660208301520160405180910390a161040f81610504565b50565b600061041c6105ad565b905090565b3660008037600080366000845af43d6000803e808015610440573d6000f35b3d6000fd5b606083156104b25782516000036104ab5761045f85610198565b6104ab5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b50816104bc565b6104bc83836105d5565b949350505050565b6104cd816105ff565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105695760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101cb565b8151156105e55781518083602001fd5b8060405162461bcd60e51b815260040161014e919061080a565b61060881610198565b61066a5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61058c565b600080858511156106a157600080fd5b838611156106ae57600080fd5b5050820193919092039150565b80356001600160a01b03811681146106d257600080fd5b919050565b6000602082840312156106e957600080fd5b61018e826106bb565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561071b57600080fd5b610724836106bb565b9150602083013567ffffffffffffffff8082111561074157600080fd5b818501915085601f83011261075557600080fd5b813581811115610767576107676106f2565b604051601f8201601f19908116603f0116810190838211818310171561078f5761078f6106f2565b816040528281528860208487010111156107a857600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107e55781810151838201526020016107cd565b50506000910152565b600082516108008184602087016107ca565b9190910192915050565b60208152600082518060208401526108298160408501602087016107ca565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122072007c277cc5d8471be1434c6b2d5b70fb7c2f6f77a0697617733c4d4ae5b76964736f6c63430008100033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6106918061007e6000396000f3fe60806040526004361061006b5760003560e01c8063204e1c7a14610070578063715018a6146100a65780637eff275e146100bd5780638da5cb5b146100dd5780639623609d146100fb57806399a88ec41461010e578063f2fde38b1461012e578063f3b7dead1461014e575b600080fd5b34801561007c57600080fd5b5061009061008b366004610483565b61016e565b60405161009d91906104a7565b60405180910390f35b3480156100b257600080fd5b506100bb6101ff565b005b3480156100c957600080fd5b506100bb6100d83660046104bb565b610213565b3480156100e957600080fd5b506000546001600160a01b0316610090565b6100bb61010936600461050a565b61027d565b34801561011a57600080fd5b506100bb6101293660046104bb565b6102ec565b34801561013a57600080fd5b506100bb610149366004610483565b610320565b34801561015a57600080fd5b50610090610169366004610483565b61039e565b6000806000836001600160a01b031660405161019490635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101cf576040519150601f19603f3d011682016040523d82523d6000602084013e6101d4565b606091505b5091509150816101e357600080fd5b808060200190518101906101f791906105e0565b949350505050565b6102076103c4565b610211600061041e565b565b61021b6103c4565b6040516308f2839760e41b81526001600160a01b03831690638f283970906102479084906004016104a7565b600060405180830381600087803b15801561026157600080fd5b505af1158015610275573d6000803e3d6000fd5b505050505050565b6102856103c4565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906102b590869086906004016105fd565b6000604051808303818588803b1580156102ce57600080fd5b505af11580156102e2573d6000803e3d6000fd5b5050505050505050565b6102f46103c4565b604051631b2ce7f360e11b81526001600160a01b03831690633659cfe6906102479084906004016104a7565b6103286103c4565b6001600160a01b0381166103925760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61039b8161041e565b50565b6000806000836001600160a01b0316604051610194906303e1469160e61b815260040190565b6000546001600160a01b031633146102115760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610389565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038116811461039b57600080fd5b60006020828403121561049557600080fd5b81356104a08161046e565b9392505050565b6001600160a01b0391909116815260200190565b600080604083850312156104ce57600080fd5b82356104d98161046e565b915060208301356104e98161046e565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561051f57600080fd5b833561052a8161046e565b9250602084013561053a8161046e565b9150604084013567ffffffffffffffff8082111561055757600080fd5b818601915086601f83011261056b57600080fd5b81358181111561057d5761057d6104f4565b604051601f8201601f19908116603f011681019083821181831017156105a5576105a56104f4565b816040528281528960208487010111156105be57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000602082840312156105f257600080fd5b81516104a08161046e565b60018060a01b038316815260006020604081840152835180604085015260005b818110156106395785810183015185820160600152820161061d565b506000606082860101526060601f19601f83011685010192505050939250505056fea26469706673582212208a2d9d6b4833462246845f0898b3da66a9086b54eb9fb2d870a9780af5c177f664736f6c63430008100033608060405234801561001057600080fd5b50610c94806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620000765760003560e01c806329a5c5cf146200007b578063396a5f9514620000af57806359659e9014620000c657806397881f8d14620000da578063b3e3bf4214620000f3578063c4d66de8146200010a578063e75b21411462000123575b600080fd5b620000926200008c36600462000354565b6200013a565b6040516001600160a01b0390911681526020015b60405180910390f35b62000092620000c036600462000354565b62000186565b60005462000092906001600160a01b031681565b620000e4620001c7565b604051908152602001620000a6565b620000e4620001043660046200038b565b620001f6565b620001216200011b366004620003b8565b62000233565b005b62000092620001343660046200038b565b620002ec565b600080620001493384620001f6565b90506000816040516200015c9062000346565b8190604051809103906000f59050801580156200017d573d6000803e3d6000fd5b50949350505050565b6000620001c182604051806020016200019f9062000346565b6020820181038252601f19601f8201166040525080519060200120306200031c565b92915050565b604051620001d86020820162000346565b6020820181038252601f19601f820116604052508051906020012081565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b6001600160a01b038116620002805760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa122a0a1a7a760911b60448201526064015b60405180910390fd5b6000546001600160a01b031615620002ca5760405162461bcd60e51b815260206004820152600c60248201526b1053149150511657d253925560a21b604482015260640162000277565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600080620002fb8484620001f6565b90506200031481604051806020016200019f9062000346565b949350505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61088180620003de83390190565b6000602082840312156200036757600080fd5b5035919050565b80356001600160a01b03811681146200038657600080fd5b919050565b600080604083850312156200039f57600080fd5b620003aa836200036e565b946020939093013593505050565b600060208284031215620003cb57600080fd5b620003d6826200036e565b939250505056fe608060405234801561001057600080fd5b50336001600160a01b03166359659e906040518163ffffffff1660e01b8152600401602060405180830381865afa15801561004f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610073919061046c565b604051806020016040528060008152506100958282600061009c60201b60201c565b5050610508565b6100a583610167565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a26000825111806100e65750805b1561016257610160836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610150919061046c565b8361030a60201b6100291760201c565b505b505050565b61017a8161033660201b6100551760201c565b6101d95760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b61024d816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561021a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023e919061046c565b61033660201b6100551760201c565b6102b25760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b60648201526084016101d0565b806102e97fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5060001b61034560201b6100641760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b606061032f838360405180606001604052806027815260200161085a60279139610348565b9392505050565b6001600160a01b03163b151590565b90565b6060600080856001600160a01b03168560405161036591906104b9565b600060405180830381855af49150503d80600081146103a0576040519150601f19603f3d011682016040523d82523d6000602084013e6103a5565b606091505b5090925090506103b7868383876103c1565b9695505050505050565b60608315610430578251600003610429576001600160a01b0385163b6104295760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101d0565b508161043a565b61043a8383610442565b949350505050565b8151156104525781518083602001fd5b8060405162461bcd60e51b81526004016101d091906104d5565b60006020828403121561047e57600080fd5b81516001600160a01b038116811461032f57600080fd5b60005b838110156104b0578181015183820152602001610498565b50506000910152565b600082516104cb818460208701610495565b9190910192915050565b60208152600082518060208401526104f4816040850160208701610495565b601f01601f19169190910160400192915050565b610343806105176000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610067565b610100565b565b606061004e83836040518060600160405280602781526020016102e760279139610124565b9392505050565b6001600160a01b03163b151590565b90565b600061009a7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fb919061024a565b905090565b3660008037600080366000845af43d6000803e80801561011f573d6000f35b3d6000fd5b6060600080856001600160a01b0316856040516101419190610297565b600060405180830381855af49150503d806000811461017c576040519150601f19603f3d011682016040523d82523d6000602084013e610181565b606091505b50915091506101928683838761019c565b9695505050505050565b6060831561020e578251600003610207576101b685610055565b6102075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610218565b6102188383610220565b949350505050565b8151156102305781518083602001fd5b8060405162461bcd60e51b81526004016101fe91906102b3565b60006020828403121561025c57600080fd5b81516001600160a01b038116811461004e57600080fd5b60005b8381101561028e578181015183820152602001610276565b50506000910152565b600082516102a9818460208701610273565b9190910192915050565b60208152600082518060208401526102d2816040850160208701610273565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e9bed491ce4cc7495def60dc616a13f39ccd912637e0c8ba02d45400506de9c064736f6c63430008100033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220237fb73aef7b871fb5f0c1ef15f58a63a1b7502356cc319c2d83ad0717d2cd7264736f6c63430008100033a2646970667358221220100146539135ca00d5aa5d840df4fd04406bdb7e1c768608b390f1f0b9aa743364736f6c63430008100033
Deployed Bytecode
0x6080604052600436106200015b5760003560e01c8063888139d411620000c5578063bfd3e5181162000078578063bfd3e518146200050a578063c4d66de8146200052c578063d7eee6ca1462000551578063d9ce0ef91462000573578063f2fde38b14620005e5578063fd40ad85146200060a57600080fd5b8063888139d414620003bd5780638c99e31c14620003e45780638da5cb5b14620004065780639095765e1462000426578063a5595da91462000448578063b1460a7114620004e857600080fd5b806346052706116200011e5780634605270614620002275780634c1496711462000305578063715018a6146200032c57806381fb918414620003445780638277742b14620003695780638369166d146200038057600080fd5b8063146bf4b114620001605780631aeef2e2146200019f57806336dddb9714620001c1578063381c9d9914620001e3578063410831861462000205575b600080fd5b3480156200016d57600080fd5b5060785462000182906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b348015620001ac57600080fd5b5060715462000182906001600160a01b031681565b348015620001ce57600080fd5b5060685462000182906001600160a01b031681565b348015620001f057600080fd5b5060725462000182906001600160a01b031681565b3480156200021257600080fd5b5060745462000182906001600160a01b031681565b3480156200023457600080fd5b50620002aa620002463660046200286c565b6066602052600090815260409020805460018201546002830154600384015460048501546005860154600687015460078801546008909801546001600160a01b03978816989688169795861696948616959384169492841693918216928216911689565b604080516001600160a01b039a8b168152988a1660208a01529689169688019690965293871660608701529186166080860152851660a0850152841660c0840152831660e08301529091166101008201526101200162000196565b3480156200031257600080fd5b506200032a62000324366004620029c3565b6200062c565b005b3480156200033957600080fd5b506200032a6200091c565b3480156200035157600080fd5b506200032a6200036336600462002a88565b62000934565b6200032a6200037a36600462002b7c565b62000a58565b3480156200038d57600080fd5b50620001826200039f3660046200286c565b6001600160a01b039081166000908152606560205260409020541690565b348015620003ca57600080fd5b50620003d560675481565b60405190815260200162000196565b348015620003f157600080fd5b5060775462000182906001600160a01b031681565b3480156200041357600080fd5b506033546001600160a01b031662000182565b3480156200043357600080fd5b5060755462000182906001600160a01b031681565b3480156200045557600080fd5b50606954606a54606b54606c54606d54606e54606f5460705462000495976001600160a01b03908116978116968116958116948116938116928116911688565b604080516001600160a01b03998a16815297891660208901529588169587019590955292861660608601529085166080850152841660a0840152831660c083015290911660e08201526101000162000196565b348015620004f557600080fd5b5060795462000182906001600160a01b031681565b3480156200051757600080fd5b50607a5462000182906001600160a01b031681565b3480156200053957600080fd5b506200032a6200054b3660046200286c565b620019f0565b3480156200055e57600080fd5b5060735462000182906001600160a01b031681565b3480156200058057600080fd5b50620005d2620005923660046200286c565b606560205260009081526040902080546001820154600283015460038401546004909401546001600160a01b039384169492841693918216928216911685565b6040516200019695949392919062002bc7565b348015620005f257600080fd5b506200032a620006043660046200286c565b62001c0e565b3480156200061757600080fd5b5060765462000182906001600160a01b031681565b826001600160a01b031663e78cea926040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200066b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000691919062002bf9565b6001600160a01b031663cb23bcb56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620006cf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006f5919062002bf9565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000733573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000759919062002bf9565b6001600160a01b0316336001600160a01b0316146200078b57604051630dbb2a6f60e11b815260040160405180910390fd5b6001600160a01b038381166000818152606560209081526040808320875181546001600160a01b031990811691881691909117825588840151600180840180548416928a169290921790915589840151600280850180548516928b16929092179091556060808c0151600380870180548716928d16929092179091556080808e0151600497880180548816918e1691909117905560668952988790208c5181548716908d16178155978c015193880180548616948c16949094179093558a86015191870180548516928b169290921790915589015190850180548316918916919091179055938701519083018054851691871691909117905560a0860151600583018054851691871691909117905560c0860151600683018054851691871691909117905560e08601516007830180548516918716919091179055610100860151600890920180549093169190941617905590517e3661d67ef6fa28d5937e796b7701a68fbf54c16d9434eb705715ebc28f424b906200090f908590859062002cfc565b60405180910390a2505050565b6200092662001c8d565b62000932600062001ce9565b565b6200093e62001c8d565b8a60696200094d828262002d4b565b50506071546001600160a01b0316158015906200097857506071546001600160a01b038b8116911614155b156200099757604051633157c93960e01b815260040160405180910390fd5b607180546001600160a01b03199081166001600160a01b038d8116919091179092556072805482168c84161790556073805482168b84161790556074805482168a84161790556075805482168984161790556076805482168884161790556077805482168784161790556078805482168684161790556079805490911691841691909117905560678190556040517f8a040c53d83c1e62b7c4c7c88774e55775a95bdaadc2c0ef9f63b8d1a118ffdc90600090a15050505050505050505050565b6069546001600160a01b031662000a82576040516376a1604d60e11b815260040160405180910390fd5b6000846001600160a01b031663e78cea926040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000ac3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ae9919062002bf9565b6001600160a01b031663cb23bcb56040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000b27573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000b4d919062002bf9565b6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000b8b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000bb1919062002bf9565b9050806001600160a01b03166391d14854826001600160a01b03166307bd02656040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000c01573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000c27919062002e2d565b6040516001600160e01b031960e084901b16815260048101919091526001600160a01b0387166024820152604401602060405180830381865afa15801562000c73573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000c99919062002e47565b62000cb757604051632625e8ed60e11b815260040160405180910390fd5b6001600160a01b0385811660009081526065602052604081205490911615159062000ce28762001d3b565b6040805160a080820183526000808352602080840182905283850182905260608085018390526080808601849052865161012081018852848152928301849052958201839052810182905293840181905290830181905260c0830181905260e08301819052610100830152919250831562000dbb576001600160a01b03808a16600090815260656020908152604091829020825160a08101845281548516815260018201548516928101929092526002810154841692820192909252600382015483166060820152600490910154909116608082015291505b6000896001600160a01b031663e78cea926040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000dfc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000e22919062002bf9565b6001600160a01b031663cb23bcb56040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000e60573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000e86919062002bf9565b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000ec4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000eea919062002e2d565b905062000f136040518060400160405280600381526020016226192960e91b8152508262001e17565b6001600160a01b031682526040805180820190915260058152644c3253475760d81b602082015262000f46908262001e17565b82602001906001600160a01b031690816001600160a01b03168152505062000f8c604051806040016040528060058152602001644c3243475760d81b8152508262001e17565b6001600160a01b03908116604084015284166200100f5762000fcc604051806040016040528060058152602001644c3257475760d81b8152508262001e17565b6001600160a01b031660608301526040805180820190915260038152624c325760e81b602082015262001000908262001e17565b6001600160a01b031660808301525b62001036604051806040016040528060038152602001624c324560e81b8152508262001e17565b6001600160a01b031660e08301526200104f8162001ed6565b6001600160a01b031660a0830152620010688162001f43565b6001600160a01b031660c0830152620010818162001f83565b8261010001906001600160a01b031690816001600160a01b031681525050506000896001600160a01b0316638b3240a06040518163ffffffff1660e01b8152600401602060405180830381865afa158015620010e1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001107919062002bf9565b90506001600160a01b038116620011315760405163eac0860960e01b815260040160405180910390fd5b846200153f5760006001600160a01b0385166200115a576069546001600160a01b031662001167565b606d546001600160a01b03165b90506200119c62001194604051806040016040528060038152602001622618a960e91b8152508d62002002565b828462002037565b6001600160a01b039081168552600091508516620011c657606a546001600160a01b0316620011d3565b606e546001600160a01b03165b905060006200120c62001204604051806040016040528060058152602001644c3153475760d81b8152508e62002002565b838562002037565b9050806001600160a01b031663a01893bf856020015187600001518f604051806020016200123a90620027ff565b6020820181038252601f19601f82011660405250805190602001208960c001516040518663ffffffff1660e01b8152600401620012a69594939291906001600160a01b039586168152938516602085015291841660408401526060830152909116608082015260a00190565b600060405180830381600087803b158015620012c157600080fd5b505af1158015620012d6573d6000803e3d6000fd5b5050506001600160a01b039182166020870152506000915085166200130757606b546001600160a01b031662001314565b606f546001600160a01b03165b905060006200134562001204604051806040016040528060058152602001644c3143475760d81b8152508e62002002565b60408086015187519151637c643b2f60e11b81526001600160a01b03918216600482015291811660248301528e811660448301528a811660648301529192509082169063f8c8765e90608401600060405180830381600087803b158015620013ac57600080fd5b505af1158015620013c1573d6000803e3d6000fd5b5050506001600160a01b0391821660408701525085169050620014c7576000620014206200140d604051806040016040528060058152602001644c3157475760d81b8152508d62002002565b606c546001600160a01b03168462002037565b9050806001600160a01b0316631459457a846060015186600001518e607860009054906101000a90046001600160a01b031688608001516040518663ffffffff1660e01b81526004016200147995949392919062002bc7565b600060405180830381600087803b1580156200149457600080fd5b505af1158015620014a9573d6000803e3d6000fd5b5050506001600160a01b039182166060860152506078541660808401525b82600001516001600160a01b0316631459457a878560200151600086600001518f6040518663ffffffff1660e01b81526004016200150a95949392919062002bc7565b600060405180830381600087803b1580156200152557600080fd5b505af11580156200153a573d6000803e3d6000fd5b505050505b6200154c8a888662002092565b6040805160e0810182526001600160a01b03808d168252607a5481166020830152339282018390526060820192909252608081018a905260a08101899052600060c082015290851615620015d257620015b185620015ab8a8c62002e81565b620022d6565b60c08201819052620015d2906001600160a01b0387169033908e9062002403565b6040805160e0810182526072546001600160a01b039081168252607354811660208301526074548116928201929092526000916060820190881662001623576075546001600160a01b031662001626565b60005b6001600160a01b0316815260200160006001600160a01b0316886001600160a01b03160362001661576076546001600160a01b031662001664565b60005b6001600160a01b0390811682526070548116602083015260775481166040909201919091529091506000908c163b15620016aa5761111161111160901b018c01620016ac565b8b5b9050620016be83838888858e6200245f565b87620019e1578b6001600160a01b03168d6001600160a01b03167f9a9203aa9ddcf21d8523e422e009214f0447efca13201ecdd802d8663092de7e8888888e6040516200170f949392919062002ea3565b60405180910390a385606560008f6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555090505084606660008f6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060c08201518160060160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160080160006101000a8154816001600160a01b0302191690836001600160a01b031602179055509050505b50505050505050505050505050565b600054610100900460ff161580801562001a115750600054600160ff909116105b8062001a2d5750303b15801562001a2d575060005460ff166001145b62001a965760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562001aba576000805461ff0019166101001790555b62001ac4620024f3565b606880546001600160a01b0319166001600160a01b0384169081179091556040805163204a7f0760e21b81529051638129fc1c9160048082019260009290919082900301818387803b15801562001b1a57600080fd5b505af115801562001b2f573d6000803e3d6000fd5b5050505062001ba362001b4a3061111161111160901b010190565b604051606b60f91b6020820152602560fa1b60218201526001600160601b0319606083901b166022820152600160ff1b603682015260009060370160408051601f19818403018152919052805160209091012092915050565b607a80546001600160a01b0319166001600160a01b0392909216919091179055801562001c0a576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b62001c1862001c8d565b6001600160a01b03811662001c7f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162001a8d565b62001c8a8162001ce9565b50565b6033546001600160a01b03163314620009325760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162001a8d565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600080826001600160a01b031663e78cea926040518163ffffffff1660e01b8152600401602060405180830381865afa15801562001d7d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001da3919062002bf9565b9050806001600160a01b031663e1758bd86040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801562001e02575060408051601f3d908101601f1916820190925262001dff9181019062002bf9565b60015b62001e105750600092915050565b9392505050565b600062001ecd62001e29848462002527565b60405162001e3a602082016200280d565b601f1982820381018352601f90910116604052607a546001600160a01b031662001e648662001ed6565b604080516020808201835260008252915162001e839493920162002f3c565b60408051601f198184030181529082905262001ea3929160200162002f73565b60408051601f198184030181529190528051602090910120607a546001600160a01b031662002561565b90505b92915050565b600062001ed062001f04604051806040016040528060048152602001634c32504160e01b8152508462002527565b60405162001f15602082016200281b565b601f1982820381018352601f90910116604052805160209190910120607a546001600160a01b031662002561565b600062001ed062001f7260405180604001604052806005815260200164261921282360d91b8152508462002527565b60405162001f156020820162002829565b600062001ed062001fb1604051806040016040528060048152602001634c324d4360e01b8152508462002527565b607754604080516001600160a01b03909216803b838101602090810190935280845262001fe8939290916000918401903c6200258b565b8051602090910120607a546001600160a01b031662002561565b600082826040516020016200201992919062002fa6565b60405160208183030381529060405280519060200120905092915050565b6000838383604051806020016040528060008152506040516200205a906200280d565b620020689392919062002f3c565b8190604051809103906000f590508015801562002089573d6000803e3d6000fd5b50949350505050565b6071546040805160206001600160a01b03909316803b8083018501909352828252600093620020c8939185918401903c6200258b565b90506001600160a01b03821615620021a457600083606754620020ec919062002e81565b90506000620020fc8483620022d6565b9050620021156001600160a01b03851633888462002403565b606754604051632a4f421360e11b81526001600160a01b0388169163549e842691620021559160009182918291339182918e908b908e9060040162002fda565b6020604051808303816000875af115801562002175573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200219b919062002e2d565b505050620022d0565b805160405163a66b327d60e01b81526000916001600160a01b0387169163a66b327d91620021df918590600401918252602082015260400190565b602060405180830381865afa158015620021fd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002223919062002e2d565b905060008460675462002237919062002e81565b6200224390836200303f565b60675460405163679b6ded60e01b81529192506001600160a01b0388169163679b6ded918491620022869160009182918991339182918f908e9060040162003055565b60206040518083038185885af1158015620022a5573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190620022cc919062002e2d565b5050505b50505050565b600080836001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562002318573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200233e9190620030b2565b90508060ff1660120362002356578291505062001ed0565b60128160ff161015620023d557600062002372826012620030d7565b6200237f90600a620031f0565b6200238b908562003201565b9050836200239b836012620030d7565b620023a890600a620031f0565b620023b4908362002e81565b1015620023cb5780620023c78162003224565b9150505b915062001ed09050565b620023e2601282620030d7565b620023ef90600a620031f0565b620023fb908462002e81565b949350505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052620022d0908590620025b9565b60685460c08701516001600160a01b0390911690635fc788d69062002485574762002488565b60005b88888888602001518833896040518963ffffffff1660e01b8152600401620024b7979695949392919062003240565b6000604051808303818588803b158015620024d157600080fd5b505af1158015620024e6573d6000803e3d6000fd5b5050505050505050505050565b600054610100900460ff166200251d5760405162461bcd60e51b815260040162001a8d906200333f565b6200093262002697565b606854600090839083906200254d906001600160a01b031661111161111160901b010190565b60405160200162002019939291906200338a565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b6060815182604051602001620025a3929190620033c0565b6040516020818303038152906040529050919050565b600062002610826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316620026cc9092919063ffffffff16565b80519091501562002692578080602001905181019062002631919062002e47565b620026925760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162001a8d565b505050565b600054610100900460ff16620026c15760405162461bcd60e51b815260040162001a8d906200333f565b620009323362001ce9565b6060620023fb848460008585600080866001600160a01b03168587604051620026f6919062003430565b60006040518083038185875af1925050503d806000811462002735576040519150601f19603f3d011682016040523d82523d6000602084013e6200273a565b606091505b50915091506200274d8783838762002758565b979650505050505050565b60608315620027cc578251600003620027c4576001600160a01b0385163b620027c45760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162001a8d565b5081620023fb565b620023fb8383815115620027e35781518083602001fd5b8060405162461bcd60e51b815260040162001a8d91906200344e565b610881806200346483390190565b610ebb8062003ce583390190565b61070f8062004ba083390190565b610cb480620052af83390190565b6001600160a01b03169052565b6001600160a01b038116811462001c8a57600080fd5b8035620028678162002844565b919050565b6000602082840312156200287f57600080fd5b813562001e108162002844565b604051610120810167ffffffffffffffff81118282101715620028bf57634e487b7160e01b600052604160045260246000fd5b60405290565b60405160a0810167ffffffffffffffff81118282101715620028bf57634e487b7160e01b600052604160045260246000fd5b600061012082840312156200290b57600080fd5b620029156200288c565b905062002922826200285a565b815262002932602083016200285a565b602082015262002945604083016200285a565b604082015262002958606083016200285a565b60608201526200296b608083016200285a565b60808201526200297e60a083016200285a565b60a08201526200299160c083016200285a565b60c0820152620029a460e083016200285a565b60e0820152610100620029b98184016200285a565b9082015292915050565b60008060008385036101e0811215620029db57600080fd5b8435620029e88162002844565b935060a0601f1982011215620029fd57600080fd5b5062002a08620028c5565b602085013562002a188162002844565b8152604085013562002a2a8162002844565b6020820152606085013562002a3f8162002844565b6040820152608085013562002a548162002844565b606082015260a085013562002a698162002844565b6080820152915062002a7f8560c08601620028f7565b90509250925092565b60008060008060008060008060008060008b8d0361024081121562002aac57600080fd5b6101008082121562002abd57600080fd5b8d9c508c0135905062002ad08162002844565b99506101208c013562002ae38162002844565b98506101408c013562002af68162002844565b97506101608c013562002b098162002844565b96506101808c013562002b1c8162002844565b95506101a08c013562002b2f8162002844565b945062002b406101c08d016200285a565b935062002b516101e08d016200285a565b925062002b626102008d016200285a565b91506102208c013590509295989b509295989b9093969950565b6000806000806080858703121562002b9357600080fd5b843562002ba08162002844565b9350602085013562002bb28162002844565b93969395505050506040820135916060013590565b6001600160a01b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b60006020828403121562002c0c57600080fd5b815162001e108162002844565b80516001600160a01b03908116835260208083015182169084015260408083015182169084015260608083015182169084015260809182015116910152565b60018060a01b0380825116835280602083015116602084015280604083015116604084015250606081015162002c92606084018262002837565b50608081015162002ca7608084018262002837565b5060a081015162002cbc60a084018262002837565b5060c081015162002cd160c084018262002837565b5060e081015162002ce660e084018262002837565b5061010080820151620022d08285018262002837565b6101c0810162002d0d828562002c19565b62001e1060a083018462002c58565b80546001600160a01b0319166001600160a01b0392909216919091179055565b6000813562001ed08162002844565b813562002d588162002844565b62002d64818362002d1c565b50602082013562002d758162002844565b62002d84816001840162002d1c565b5062002da162002d976040840162002d3c565b6002830162002d1c565b62002dbd62002db36060840162002d3c565b6003830162002d1c565b62002dd962002dcf6080840162002d3c565b6004830162002d1c565b62002df562002deb60a0840162002d3c565b6005830162002d1c565b62002e1162002e0760c0840162002d3c565b6006830162002d1c565b62001c0a62002e2360e0840162002d3c565b6007830162002d1c565b60006020828403121562002e4057600080fd5b5051919050565b60006020828403121562002e5a57600080fd5b8151801515811462001e1057600080fd5b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161562002e9e5762002e9e62002e6b565b500290565b610200810162002eb4828762002c19565b62002ec360a083018662002c58565b6001600160a01b039384166101c0830152919092166101e09092019190915292915050565b60005b8381101562002f0557818101518382015260200162002eeb565b50506000910152565b6000815180845262002f2881602086016020860162002ee8565b601f01601f19169290920160200192915050565b6001600160a01b0384811682528316602082015260606040820181905260009062002f6a9083018462002f0e565b95945050505050565b6000835162002f8781846020880162002ee8565b83519083019062002f9d81836020880162002ee8565b01949350505050565b6000835162002fba81846020880162002ee8565b60609390931b6001600160601b0319169190920190815260140192915050565b600061012060018060a01b03808d1684528b60208501528a6040850152808a1660608501528089166080850152508660a08401528560c08401528460e0840152806101008401526200302f8184018562002f0e565b9c9b505050505050505050505050565b8082018082111562001ed05762001ed062002e6b565b600061010060018060a01b03808c1684528a602085015289604085015280891660608501528088166080850152508560a08401528460c08401528060e0840152620030a38184018562002f0e565b9b9a5050505050505050505050565b600060208284031215620030c557600080fd5b815160ff8116811462001e1057600080fd5b60ff828116828216039081111562001ed05762001ed062002e6b565b600181815b808511156200313457816000190482111562003118576200311862002e6b565b808516156200312657918102915b93841c9390800290620030f8565b509250929050565b6000826200314d5750600162001ed0565b816200315c5750600062001ed0565b81600181146200317557600281146200318057620031a0565b600191505062001ed0565b60ff84111562003194576200319462002e6b565b50506001821b62001ed0565b5060208310610133831016604e8410600b8410161715620031c5575081810a62001ed0565b620031d18383620030f3565b8060001904821115620031e857620031e862002e6b565b029392505050565b600062001ecd60ff8416836200313c565b6000826200321f57634e487b7160e01b600052601260045260246000fd5b500490565b60006001820162003239576200323962002e6b565b5060010190565b87516001600160a01b0390811682526020808a01518216818401526040808b01518316818501526060808c01518416818601526080808d01518187015260a08d8101518188015260c0808f0151818901528d51871660e0890152948d01518616610100880152928c01518516610120870152908b015184166101408601528a015183166101608501528901518216610180840152880151166101a08201526102e08101620032f36101c083018862002c19565b6200330361026083018762002837565b6200331361028083018662002837565b620033236102a083018562002837565b620033336102c083018462002837565b98975050505050505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600084516200339e81846020890162002ee8565b919091019283525060601b6001600160601b0319166020820152603401919050565b710608060405234801561001057600080fd5b560741b8152606160f81b601282015260f083901b6001600160f01b03191660138201526a4030801030001cb00079ff60a91b601582015281516000906200342281602080860190870162002ee8565b919091016020019392505050565b600082516200344481846020870162002ee8565b9190910192915050565b60208152600062001ecd602083018462002f0e56fe608060405234801561001057600080fd5b50336001600160a01b03166359659e906040518163ffffffff1660e01b8152600401602060405180830381865afa15801561004f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610073919061046c565b604051806020016040528060008152506100958282600061009c60201b60201c565b5050610508565b6100a583610167565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a26000825111806100e65750805b1561016257610160836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610150919061046c565b8361030a60201b6100291760201c565b505b505050565b61017a8161033660201b6100551760201c565b6101d95760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b61024d816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561021a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023e919061046c565b61033660201b6100551760201c565b6102b25760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b60648201526084016101d0565b806102e97fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5060001b61034560201b6100641760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b606061032f838360405180606001604052806027815260200161085a60279139610348565b9392505050565b6001600160a01b03163b151590565b90565b6060600080856001600160a01b03168560405161036591906104b9565b600060405180830381855af49150503d80600081146103a0576040519150601f19603f3d011682016040523d82523d6000602084013e6103a5565b606091505b5090925090506103b7868383876103c1565b9695505050505050565b60608315610430578251600003610429576001600160a01b0385163b6104295760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101d0565b508161043a565b61043a8383610442565b949350505050565b8151156104525781518083602001fd5b8060405162461bcd60e51b81526004016101d091906104d5565b60006020828403121561047e57600080fd5b81516001600160a01b038116811461032f57600080fd5b60005b838110156104b0578181015183820152602001610498565b50506000910152565b600082516104cb818460208701610495565b9190910192915050565b60208152600082518060208401526104f4816040850160208701610495565b601f01601f19169190910160400192915050565b610343806105176000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610067565b610100565b565b606061004e83836040518060600160405280602781526020016102e760279139610124565b9392505050565b6001600160a01b03163b151590565b90565b600061009a7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fb919061024a565b905090565b3660008037600080366000845af43d6000803e80801561011f573d6000f35b3d6000fd5b6060600080856001600160a01b0316856040516101419190610297565b600060405180830381855af49150503d806000811461017c576040519150601f19603f3d011682016040523d82523d6000602084013e610181565b606091505b50915091506101928683838761019c565b9695505050505050565b6060831561020e578251600003610207576101b685610055565b6102075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610218565b6102188383610220565b949350505050565b8151156102305781518083602001fd5b8060405162461bcd60e51b81526004016101fe91906102b3565b60006020828403121561025c57600080fd5b81516001600160a01b038116811461004e57600080fd5b60005b8381101561028e578181015183820152602001610276565b50506000910152565b600082516102a9818460208701610273565b9190910192915050565b60208152600082518060208401526102d2816040850160208701610273565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e9bed491ce4cc7495def60dc616a13f39ccd912637e0c8ba02d45400506de9c064736f6c63430008100033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564608060405260405162000ebb38038062000ebb833981016040819052620000269162000497565b828162000036828260006200004d565b50620000449050826200008a565b505050620005ca565b6200005883620000e5565b600082511180620000665750805b1562000085576200008383836200012760201b620001691760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000b562000156565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000e2816200018f565b50565b620000f08162000244565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200014f838360405180606001604052806027815260200162000e9460279139620002f8565b9392505050565b60006200018060008051602062000e7483398151915260001b6200037760201b620001951760201c565b546001600160a01b0316919050565b6001600160a01b038116620001fa5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200022360008051602062000e7483398151915260001b6200037760201b620001951760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6200025a816200037a60201b620001981760201c565b620002be5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001f1565b80620002237f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200037760201b620001951760201c565b6060600080856001600160a01b03168560405162000317919062000577565b600060405180830381855af49150503d806000811462000354576040519150601f19603f3d011682016040523d82523d6000602084013e62000359565b606091505b5090925090506200036d8683838762000389565b9695505050505050565b90565b6001600160a01b03163b151590565b60608315620003fd578251600003620003f5576001600160a01b0385163b620003f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001f1565b508162000409565b62000409838362000411565b949350505050565b815115620004225781518083602001fd5b8060405162461bcd60e51b8152600401620001f1919062000595565b80516001600160a01b03811681146200045657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200048e57818101518382015260200162000474565b50506000910152565b600080600060608486031215620004ad57600080fd5b620004b8846200043e565b9250620004c8602085016200043e565b60408501519092506001600160401b0380821115620004e657600080fd5b818601915086601f830112620004fb57600080fd5b8151818111156200051057620005106200045b565b604051601f8201601f19908116603f011681019083821181831017156200053b576200053b6200045b565b816040528281528960208487010111156200055557600080fd5b6200056883602083016020880162000471565b80955050505050509250925092565b600082516200058b81846020870162000471565b9190910192915050565b6020815260008251806020840152620005b681604085016020870162000471565b601f01601f19169190910160400192915050565b61089a80620005da6000396000f3fe60806040523661001357610011610017565b005b6100115b61001f6101a7565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a576100536101da565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a57610053610231565b63070d7c6960e41b6001600160e01b031982160161009a57610053610277565b621eb96f60e61b6001600160e01b03198216016100b9576100536102a8565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102e8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102fc565b565b606061018e838360405180606001604052806027815260200161083e6027913961030c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101e4610384565b60006101f33660048184610691565b81019061020091906106d7565b905061021d8160405180602001604052806000815250600061038f565b505060408051602081019091526000815290565b60606000806102433660048184610691565b8101906102509190610708565b915091506102608282600161038f565b604051806020016040528060008152509250505090565b6060610281610384565b60006102903660048184610691565b81019061029d91906106d7565b905061021d816103bb565b60606102b2610384565b60006102bc6101a7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102f2610384565b60006102bc610412565b610167610307610412565b610421565b6060600080856001600160a01b03168560405161032991906107ee565b600060405180830381855af49150503d8060008114610364576040519150601f19603f3d011682016040523d82523d6000602084013e610369565b606091505b509150915061037a86838387610445565b9695505050505050565b341561016757600080fd5b610398836104c4565b6000825111806103a55750805b156103b6576103b48383610169565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e46101a7565b604080516001600160a01b03928316815291841660208301520160405180910390a161040f81610504565b50565b600061041c6105ad565b905090565b3660008037600080366000845af43d6000803e808015610440573d6000f35b3d6000fd5b606083156104b25782516000036104ab5761045f85610198565b6104ab5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b50816104bc565b6104bc83836105d5565b949350505050565b6104cd816105ff565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105695760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101cb565b8151156105e55781518083602001fd5b8060405162461bcd60e51b815260040161014e919061080a565b61060881610198565b61066a5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61058c565b600080858511156106a157600080fd5b838611156106ae57600080fd5b5050820193919092039150565b80356001600160a01b03811681146106d257600080fd5b919050565b6000602082840312156106e957600080fd5b61018e826106bb565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561071b57600080fd5b610724836106bb565b9150602083013567ffffffffffffffff8082111561074157600080fd5b818501915085601f83011261075557600080fd5b813581811115610767576107676106f2565b604051601f8201601f19908116603f0116810190838211818310171561078f5761078f6106f2565b816040528281528860208487010111156107a857600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107e55781810151838201526020016107cd565b50506000910152565b600082516108008184602087016107ca565b9190910192915050565b60208152600082518060208401526108298160408501602087016107ca565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122072007c277cc5d8471be1434c6b2d5b70fb7c2f6f77a0697617733c4d4ae5b76964736f6c63430008100033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6106918061007e6000396000f3fe60806040526004361061006b5760003560e01c8063204e1c7a14610070578063715018a6146100a65780637eff275e146100bd5780638da5cb5b146100dd5780639623609d146100fb57806399a88ec41461010e578063f2fde38b1461012e578063f3b7dead1461014e575b600080fd5b34801561007c57600080fd5b5061009061008b366004610483565b61016e565b60405161009d91906104a7565b60405180910390f35b3480156100b257600080fd5b506100bb6101ff565b005b3480156100c957600080fd5b506100bb6100d83660046104bb565b610213565b3480156100e957600080fd5b506000546001600160a01b0316610090565b6100bb61010936600461050a565b61027d565b34801561011a57600080fd5b506100bb6101293660046104bb565b6102ec565b34801561013a57600080fd5b506100bb610149366004610483565b610320565b34801561015a57600080fd5b50610090610169366004610483565b61039e565b6000806000836001600160a01b031660405161019490635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101cf576040519150601f19603f3d011682016040523d82523d6000602084013e6101d4565b606091505b5091509150816101e357600080fd5b808060200190518101906101f791906105e0565b949350505050565b6102076103c4565b610211600061041e565b565b61021b6103c4565b6040516308f2839760e41b81526001600160a01b03831690638f283970906102479084906004016104a7565b600060405180830381600087803b15801561026157600080fd5b505af1158015610275573d6000803e3d6000fd5b505050505050565b6102856103c4565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906102b590869086906004016105fd565b6000604051808303818588803b1580156102ce57600080fd5b505af11580156102e2573d6000803e3d6000fd5b5050505050505050565b6102f46103c4565b604051631b2ce7f360e11b81526001600160a01b03831690633659cfe6906102479084906004016104a7565b6103286103c4565b6001600160a01b0381166103925760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61039b8161041e565b50565b6000806000836001600160a01b0316604051610194906303e1469160e61b815260040190565b6000546001600160a01b031633146102115760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610389565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038116811461039b57600080fd5b60006020828403121561049557600080fd5b81356104a08161046e565b9392505050565b6001600160a01b0391909116815260200190565b600080604083850312156104ce57600080fd5b82356104d98161046e565b915060208301356104e98161046e565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561051f57600080fd5b833561052a8161046e565b9250602084013561053a8161046e565b9150604084013567ffffffffffffffff8082111561055757600080fd5b818601915086601f83011261056b57600080fd5b81358181111561057d5761057d6104f4565b604051601f8201601f19908116603f011681019083821181831017156105a5576105a56104f4565b816040528281528960208487010111156105be57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000602082840312156105f257600080fd5b81516104a08161046e565b60018060a01b038316815260006020604081840152835180604085015260005b818110156106395785810183015185820160600152820161061d565b506000606082860101526060601f19601f83011685010192505050939250505056fea26469706673582212208a2d9d6b4833462246845f0898b3da66a9086b54eb9fb2d870a9780af5c177f664736f6c63430008100033608060405234801561001057600080fd5b50610c94806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620000765760003560e01c806329a5c5cf146200007b578063396a5f9514620000af57806359659e9014620000c657806397881f8d14620000da578063b3e3bf4214620000f3578063c4d66de8146200010a578063e75b21411462000123575b600080fd5b620000926200008c36600462000354565b6200013a565b6040516001600160a01b0390911681526020015b60405180910390f35b62000092620000c036600462000354565b62000186565b60005462000092906001600160a01b031681565b620000e4620001c7565b604051908152602001620000a6565b620000e4620001043660046200038b565b620001f6565b620001216200011b366004620003b8565b62000233565b005b62000092620001343660046200038b565b620002ec565b600080620001493384620001f6565b90506000816040516200015c9062000346565b8190604051809103906000f59050801580156200017d573d6000803e3d6000fd5b50949350505050565b6000620001c182604051806020016200019f9062000346565b6020820181038252601f19601f8201166040525080519060200120306200031c565b92915050565b604051620001d86020820162000346565b6020820181038252601f19601f820116604052508051906020012081565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b6001600160a01b038116620002805760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa122a0a1a7a760911b60448201526064015b60405180910390fd5b6000546001600160a01b031615620002ca5760405162461bcd60e51b815260206004820152600c60248201526b1053149150511657d253925560a21b604482015260640162000277565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600080620002fb8484620001f6565b90506200031481604051806020016200019f9062000346565b949350505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61088180620003de83390190565b6000602082840312156200036757600080fd5b5035919050565b80356001600160a01b03811681146200038657600080fd5b919050565b600080604083850312156200039f57600080fd5b620003aa836200036e565b946020939093013593505050565b600060208284031215620003cb57600080fd5b620003d6826200036e565b939250505056fe608060405234801561001057600080fd5b50336001600160a01b03166359659e906040518163ffffffff1660e01b8152600401602060405180830381865afa15801561004f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610073919061046c565b604051806020016040528060008152506100958282600061009c60201b60201c565b5050610508565b6100a583610167565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a26000825111806100e65750805b1561016257610160836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610150919061046c565b8361030a60201b6100291760201c565b505b505050565b61017a8161033660201b6100551760201c565b6101d95760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b61024d816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561021a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023e919061046c565b61033660201b6100551760201c565b6102b25760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b60648201526084016101d0565b806102e97fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5060001b61034560201b6100641760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b606061032f838360405180606001604052806027815260200161085a60279139610348565b9392505050565b6001600160a01b03163b151590565b90565b6060600080856001600160a01b03168560405161036591906104b9565b600060405180830381855af49150503d80600081146103a0576040519150601f19603f3d011682016040523d82523d6000602084013e6103a5565b606091505b5090925090506103b7868383876103c1565b9695505050505050565b60608315610430578251600003610429576001600160a01b0385163b6104295760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101d0565b508161043a565b61043a8383610442565b949350505050565b8151156104525781518083602001fd5b8060405162461bcd60e51b81526004016101d091906104d5565b60006020828403121561047e57600080fd5b81516001600160a01b038116811461032f57600080fd5b60005b838110156104b0578181015183820152602001610498565b50506000910152565b600082516104cb818460208701610495565b9190910192915050565b60208152600082518060208401526104f4816040850160208701610495565b601f01601f19169190910160400192915050565b610343806105176000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610067565b610100565b565b606061004e83836040518060600160405280602781526020016102e760279139610124565b9392505050565b6001600160a01b03163b151590565b90565b600061009a7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fb919061024a565b905090565b3660008037600080366000845af43d6000803e80801561011f573d6000f35b3d6000fd5b6060600080856001600160a01b0316856040516101419190610297565b600060405180830381855af49150503d806000811461017c576040519150601f19603f3d011682016040523d82523d6000602084013e610181565b606091505b50915091506101928683838761019c565b9695505050505050565b6060831561020e578251600003610207576101b685610055565b6102075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610218565b6102188383610220565b949350505050565b8151156102305781518083602001fd5b8060405162461bcd60e51b81526004016101fe91906102b3565b60006020828403121561025c57600080fd5b81516001600160a01b038116811461004e57600080fd5b60005b8381101561028e578181015183820152602001610276565b50506000910152565b600082516102a9818460208701610273565b9190910192915050565b60208152600082518060208401526102d2816040850160208701610273565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e9bed491ce4cc7495def60dc616a13f39ccd912637e0c8ba02d45400506de9c064736f6c63430008100033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220237fb73aef7b871fb5f0c1ef15f58a63a1b7502356cc319c2d83ad0717d2cd7264736f6c63430008100033a2646970667358221220100146539135ca00d5aa5d840df4fd04406bdb7e1c768608b390f1f0b9aa743364736f6c63430008100033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.