Transaction Hash:
Block:
22548392 at May-23-2025 09:58:23 PM +UTC
Transaction Fee:
0.000072242 ETH
$0.18
Gas Used:
36,121 Gas / 2 Gwei
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x290ca4DA...3d64d4E6A |
0.161077548559032884 Eth
Nonce: 299614
|
0.161005306559032884 Eth
Nonce: 299615
| 0.000072242 | ||
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 13.133444010127807132 Eth | 13.133464277256554605 Eth | 0.000020267128747473 |
Execution Trace
L1ChugSplashProxy.68747470( )
-
ProxyAdmin.STATICCALL( )
-
L1StandardBridge.68747470( )
File 1 of 3: L1ChugSplashProxy
File 2 of 3: ProxyAdmin
File 3 of 3: L1StandardBridge
// SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.8.0; import { iL1ChugSplashDeployer } from "./interfaces/iL1ChugSplashDeployer.sol"; /** * @title L1ChugSplashProxy * @dev Basic ChugSplash proxy contract for L1. Very close to being a normal proxy but has added * functions `setCode` and `setStorage` for changing the code or storage of the contract. Nifty! * * Note for future developers: do NOT make anything in this contract 'public' unless you know what * you're doing. Anything public can potentially have a function signature that conflicts with a * signature attached to the implementation contract. Public functions SHOULD always have the * 'proxyCallIfNotOwner' modifier unless there's some *really* good reason not to have that * modifier. And there almost certainly is not a good reason to not have that modifier. Beware! */ contract L1ChugSplashProxy { /************* * Constants * *************/ // "Magic" prefix. When prepended to some arbitrary bytecode and used to create a contract, the // appended bytecode will be deployed as given. bytes13 constant internal DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3; // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) bytes32 constant internal IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; // bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) bytes32 constant internal OWNER_KEY = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /*************** * Constructor * ***************/ /** * @param _owner Address of the initial contract owner. */ constructor( address _owner ) { _setOwner(_owner); } /********************** * Function Modifiers * **********************/ /** * Blocks a function from being called when the parent signals that the system should be paused * via an isUpgrading function. */ modifier onlyWhenNotPaused() { address owner = _getOwner(); // We do a low-level call because there's no guarantee that the owner actually *is* an // L1ChugSplashDeployer contract and Solidity will throw errors if we do a normal call and // it turns out that it isn't the right type of contract. (bool success, bytes memory returndata) = owner.staticcall( abi.encodeWithSelector( iL1ChugSplashDeployer.isUpgrading.selector ) ); // If the call was unsuccessful then we assume that there's no "isUpgrading" method and we // can just continue as normal. We also expect that the return value is exactly 32 bytes // long. If this isn't the case then we can safely ignore the result. if (success && returndata.length == 32) { // Although the expected value is a *boolean*, it's safer to decode as a uint256 in the // case that the isUpgrading function returned something other than 0 or 1. But we only // really care about the case where this value is 0 (= false). uint256 ret = abi.decode(returndata, (uint256)); require( ret == 0, "L1ChugSplashProxy: system is currently being upgraded" ); } _; } /** * Makes a proxy call instead of triggering the given function when the caller is either the * owner or the zero address. Caller can only ever be the zero address if this function is * being called off-chain via eth_call, which is totally fine and can be convenient for * client-side tooling. Avoids situations where the proxy and implementation share a sighash * and the proxy function ends up being called instead of the implementation one. * * Note: msg.sender == address(0) can ONLY be triggered off-chain via eth_call. If there's a * way for someone to send a transaction with msg.sender == address(0) in any real context then * we have much bigger problems. Primary reason to include this additional allowed sender is * because the owner address can be changed dynamically and we do not want clients to have to * keep track of the current owner in order to make an eth_call that doesn't trigger the * proxied contract. */ modifier proxyCallIfNotOwner() { if (msg.sender == _getOwner() || msg.sender == address(0)) { _; } else { // This WILL halt the call frame on completion. _doProxyCall(); } } /********************* * Fallback Function * *********************/ fallback() external payable { // Proxy call by default. _doProxyCall(); } /******************** * Public Functions * ********************/ /** * Sets the code that should be running behind this proxy. Note that this scheme is a bit * different from the standard proxy scheme where one would typically deploy the code * separately and then set the implementation address. We're doing it this way because it gives * us a lot more freedom on the client side. Can only be triggered by the contract owner. * @param _code New contract code to run inside this contract. */ function setCode( bytes memory _code ) proxyCallIfNotOwner public { // Get the code hash of the current implementation. address implementation = _getImplementation(); // If the code hash matches the new implementation then we return early. if (keccak256(_code) == _getAccountCodeHash(implementation)) { return; } // Create the deploycode by appending the magic prefix. bytes memory deploycode = abi.encodePacked( DEPLOY_CODE_PREFIX, _code ); // Deploy the code and set the new implementation address. address newImplementation; assembly { newImplementation := create(0x0, add(deploycode, 0x20), mload(deploycode)) } // Check that the code was actually deployed correctly. I'm not sure if you can ever // actually fail this check. Should only happen if the contract creation from above runs // out of gas but this parent execution thread does NOT run out of gas. Seems like we // should be doing this check anyway though. require( _getAccountCodeHash(newImplementation) == keccak256(_code), "L1ChugSplashProxy: code was not correctly deployed." ); _setImplementation(newImplementation); } /** * Modifies some storage slot within the proxy contract. Gives us a lot of power to perform * upgrades in a more transparent way. Only callable by the owner. * @param _key Storage key to modify. * @param _value New value for the storage key. */ function setStorage( bytes32 _key, bytes32 _value ) proxyCallIfNotOwner public { assembly { sstore(_key, _value) } } /** * Changes the owner of the proxy contract. Only callable by the owner. * @param _owner New owner of the proxy contract. */ function setOwner( address _owner ) proxyCallIfNotOwner public { _setOwner(_owner); } /** * Queries the owner of the proxy contract. Can only be called by the owner OR by making an * eth_call and setting the "from" address to address(0). * @return Owner address. */ function getOwner() proxyCallIfNotOwner public returns ( address ) { return _getOwner(); } /** * Queries the implementation address. Can only be called by the owner OR by making an * eth_call and setting the "from" address to address(0). * @return Implementation address. */ function getImplementation() proxyCallIfNotOwner public returns ( address ) { return _getImplementation(); } /********************** * Internal Functions * **********************/ /** * Sets the implementation address. * @param _implementation New implementation address. */ function _setImplementation( address _implementation ) internal { assembly { sstore(IMPLEMENTATION_KEY, _implementation) } } /** * Queries the implementation address. * @return Implementation address. */ function _getImplementation() internal view returns ( address ) { address implementation; assembly { implementation := sload(IMPLEMENTATION_KEY) } return implementation; } /** * Changes the owner of the proxy contract. * @param _owner New owner of the proxy contract. */ function _setOwner( address _owner ) internal { assembly { sstore(OWNER_KEY, _owner) } } /** * Queries the owner of the proxy contract. * @return Owner address. */ function _getOwner() internal view returns ( address ) { address owner; assembly { owner := sload(OWNER_KEY) } return owner; } /** * Gets the code hash for a given account. * @param _account Address of the account to get a code hash for. * @return Code hash for the account. */ function _getAccountCodeHash( address _account ) internal view returns ( bytes32 ) { bytes32 codeHash; assembly { codeHash := extcodehash(_account) } return codeHash; } /** * Performs the proxy call via a delegatecall. */ function _doProxyCall() onlyWhenNotPaused internal { address implementation = _getImplementation(); require( implementation != address(0), "L1ChugSplashProxy: implementation is not set yet" ); assembly { // Copy calldata into memory at 0x0....calldatasize. calldatacopy(0x0, 0x0, calldatasize()) // Perform the delegatecall, make sure to pass all available gas. let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0) // Copy returndata into memory at 0x0....returndatasize. Note that this *will* // overwrite the calldata that we just copied into memory but that doesn't really // matter because we'll be returning in a second anyway. returndatacopy(0x0, 0x0, returndatasize()) // Success == 0 means a revert. We'll revert too and pass the data up. if iszero(success) { revert(0x0, returndatasize()) } // Otherwise we'll just return and pass the data up. return(0x0, returndatasize()) } } } // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.8.0; /** * @title iL1ChugSplashDeployer */ interface iL1ChugSplashDeployer { function isUpgrading() external view returns ( bool ); }
File 2 of 3: ProxyAdmin
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; /** * @custom:legacy * @title AddressManager * @notice AddressManager is a legacy contract that was used in the old version of the Optimism * system to manage a registry of string names to addresses. We now use a more standard * proxy system instead, but this contract is still necessary for backwards compatibility * with several older contracts. */ contract AddressManager is Ownable { /** * @notice Mapping of the hashes of string names to addresses. */ mapping(bytes32 => address) private addresses; /** * @notice Emitted when an address is modified in the registry. * * @param name String name being set in the registry. * @param newAddress Address set for the given name. * @param oldAddress Address that was previously set for the given name. */ event AddressSet(string indexed name, address newAddress, address oldAddress); /** * @notice Changes the address associated with a particular name. * * @param _name String name to associate an address with. * @param _address Address to associate with the name. */ function setAddress(string memory _name, address _address) external onlyOwner { bytes32 nameHash = _getNameHash(_name); address oldAddress = addresses[nameHash]; addresses[nameHash] = _address; emit AddressSet(_name, _address, oldAddress); } /** * @notice Retrieves the address associated with a given name. * * @param _name Name to retrieve an address for. * * @return Address associated with the given name. */ function getAddress(string memory _name) external view returns (address) { return addresses[_getNameHash(_name)]; } /** * @notice Computes the hash of a name. * * @param _name Name to compute a hash for. * * @return Hash of the given name. */ function _getNameHash(string memory _name) internal pure returns (bytes32) { return keccak256(abi.encodePacked(_name)); } } // SPDX-License-Identifier: MIT pragma solidity 0.8.15; /** * @title IL1ChugSplashDeployer */ interface IL1ChugSplashDeployer { function isUpgrading() external view returns (bool); } /** * @custom:legacy * @title L1ChugSplashProxy * @notice Basic ChugSplash proxy contract for L1. Very close to being a normal proxy but has added * functions `setCode` and `setStorage` for changing the code or storage of the contract. * * Note for future developers: do NOT make anything in this contract 'public' unless you * know what you're doing. Anything public can potentially have a function signature that * conflicts with a signature attached to the implementation contract. Public functions * SHOULD always have the `proxyCallIfNotOwner` modifier unless there's some *really* good * reason not to have that modifier. And there almost certainly is not a good reason to not * have that modifier. Beware! */ contract L1ChugSplashProxy { /** * @notice "Magic" prefix. When prepended to some arbitrary bytecode and used to create a * contract, the appended bytecode will be deployed as given. */ bytes13 internal constant DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3; /** * @notice bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) */ bytes32 internal constant IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @notice bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) */ bytes32 internal constant OWNER_KEY = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @notice Blocks a function from being called when the parent signals that the system should * be paused via an isUpgrading function. */ modifier onlyWhenNotPaused() { address owner = _getOwner(); // We do a low-level call because there's no guarantee that the owner actually *is* an // L1ChugSplashDeployer contract and Solidity will throw errors if we do a normal call and // it turns out that it isn't the right type of contract. (bool success, bytes memory returndata) = owner.staticcall( abi.encodeWithSelector(IL1ChugSplashDeployer.isUpgrading.selector) ); // If the call was unsuccessful then we assume that there's no "isUpgrading" method and we // can just continue as normal. We also expect that the return value is exactly 32 bytes // long. If this isn't the case then we can safely ignore the result. if (success && returndata.length == 32) { // Although the expected value is a *boolean*, it's safer to decode as a uint256 in the // case that the isUpgrading function returned something other than 0 or 1. But we only // really care about the case where this value is 0 (= false). uint256 ret = abi.decode(returndata, (uint256)); require(ret == 0, "L1ChugSplashProxy: system is currently being upgraded"); } _; } /** * @notice Makes a proxy call instead of triggering the given function when the caller is * either the owner or the zero address. Caller can only ever be the zero address if * this function is being called off-chain via eth_call, which is totally fine and can * be convenient for client-side tooling. Avoids situations where the proxy and * implementation share a sighash and the proxy function ends up being called instead * of the implementation one. * * Note: msg.sender == address(0) can ONLY be triggered off-chain via eth_call. If * there's a way for someone to send a transaction with msg.sender == address(0) in any * real context then we have much bigger problems. Primary reason to include this * additional allowed sender is because the owner address can be changed dynamically * and we do not want clients to have to keep track of the current owner in order to * make an eth_call that doesn't trigger the proxied contract. */ // slither-disable-next-line incorrect-modifier modifier proxyCallIfNotOwner() { if (msg.sender == _getOwner() || msg.sender == address(0)) { _; } else { // This WILL halt the call frame on completion. _doProxyCall(); } } /** * @param _owner Address of the initial contract owner. */ constructor(address _owner) { _setOwner(_owner); } // slither-disable-next-line locked-ether receive() external payable { // Proxy call by default. _doProxyCall(); } // slither-disable-next-line locked-ether fallback() external payable { // Proxy call by default. _doProxyCall(); } /** * @notice Sets the code that should be running behind this proxy. * * Note: This scheme is a bit different from the standard proxy scheme where one would * typically deploy the code separately and then set the implementation address. We're * doing it this way because it gives us a lot more freedom on the client side. Can * only be triggered by the contract owner. * * @param _code New contract code to run inside this contract. */ function setCode(bytes memory _code) external proxyCallIfNotOwner { // Get the code hash of the current implementation. address implementation = _getImplementation(); // If the code hash matches the new implementation then we return early. if (keccak256(_code) == _getAccountCodeHash(implementation)) { return; } // Create the deploycode by appending the magic prefix. bytes memory deploycode = abi.encodePacked(DEPLOY_CODE_PREFIX, _code); // Deploy the code and set the new implementation address. address newImplementation; assembly { newImplementation := create(0x0, add(deploycode, 0x20), mload(deploycode)) } // Check that the code was actually deployed correctly. I'm not sure if you can ever // actually fail this check. Should only happen if the contract creation from above runs // out of gas but this parent execution thread does NOT run out of gas. Seems like we // should be doing this check anyway though. require( _getAccountCodeHash(newImplementation) == keccak256(_code), "L1ChugSplashProxy: code was not correctly deployed" ); _setImplementation(newImplementation); } /** * @notice Modifies some storage slot within the proxy contract. Gives us a lot of power to * perform upgrades in a more transparent way. Only callable by the owner. * * @param _key Storage key to modify. * @param _value New value for the storage key. */ function setStorage(bytes32 _key, bytes32 _value) external proxyCallIfNotOwner { assembly { sstore(_key, _value) } } /** * @notice Changes the owner of the proxy contract. Only callable by the owner. * * @param _owner New owner of the proxy contract. */ function setOwner(address _owner) external proxyCallIfNotOwner { _setOwner(_owner); } /** * @notice Queries the owner of the proxy contract. Can only be called by the owner OR by * making an eth_call and setting the "from" address to address(0). * * @return Owner address. */ function getOwner() external proxyCallIfNotOwner returns (address) { return _getOwner(); } /** * @notice Queries the implementation address. Can only be called by the owner OR by making an * eth_call and setting the "from" address to address(0). * * @return Implementation address. */ function getImplementation() external proxyCallIfNotOwner returns (address) { return _getImplementation(); } /** * @notice Sets the implementation address. * * @param _implementation New implementation address. */ function _setImplementation(address _implementation) internal { assembly { sstore(IMPLEMENTATION_KEY, _implementation) } } /** * @notice Changes the owner of the proxy contract. * * @param _owner New owner of the proxy contract. */ function _setOwner(address _owner) internal { assembly { sstore(OWNER_KEY, _owner) } } /** * @notice Performs the proxy call via a delegatecall. */ function _doProxyCall() internal onlyWhenNotPaused { address implementation = _getImplementation(); require(implementation != address(0), "L1ChugSplashProxy: implementation is not set yet"); assembly { // Copy calldata into memory at 0x0....calldatasize. calldatacopy(0x0, 0x0, calldatasize()) // Perform the delegatecall, make sure to pass all available gas. let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0) // Copy returndata into memory at 0x0....returndatasize. Note that this *will* // overwrite the calldata that we just copied into memory but that doesn't really // matter because we'll be returning in a second anyway. returndatacopy(0x0, 0x0, returndatasize()) // Success == 0 means a revert. We'll revert too and pass the data up. if iszero(success) { revert(0x0, returndatasize()) } // Otherwise we'll just return and pass the data up. return(0x0, returndatasize()) } } /** * @notice Queries the implementation address. * * @return Implementation address. */ function _getImplementation() internal view returns (address) { address implementation; assembly { implementation := sload(IMPLEMENTATION_KEY) } return implementation; } /** * @notice Queries the owner of the proxy contract. * * @return Owner address. */ function _getOwner() internal view returns (address) { address owner; assembly { owner := sload(OWNER_KEY) } return owner; } /** * @notice Gets the code hash for a given account. * * @param _account Address of the account to get a code hash for. * * @return Code hash for the account. */ function _getAccountCodeHash(address _account) internal view returns (bytes32) { bytes32 codeHash; assembly { codeHash := extcodehash(_account) } return codeHash; } } // SPDX-License-Identifier: MIT pragma solidity 0.8.15; /** * @title Proxy * @notice Proxy is a transparent proxy that passes through the call if the caller is the owner or * if the caller is address(0), meaning that the call originated from an off-chain * simulation. */ contract Proxy { /** * @notice The storage slot that holds the address of the implementation. * bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) */ bytes32 internal constant IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @notice The storage slot that holds the address of the owner. * bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) */ bytes32 internal constant OWNER_KEY = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @notice An event that is emitted each time the implementation is changed. This event is part * of the EIP-1967 specification. * * @param implementation The address of the implementation contract */ event Upgraded(address indexed implementation); /** * @notice An event that is emitted each time the owner is upgraded. This event is part of the * EIP-1967 specification. * * @param previousAdmin The previous owner of the contract * @param newAdmin The new owner of the contract */ event AdminChanged(address previousAdmin, address newAdmin); /** * @notice A modifier that reverts if not called by the owner or by address(0) to allow * eth_call to interact with this proxy without needing to use low-level storage * inspection. We assume that nobody is able to trigger calls from address(0) during * normal EVM execution. */ modifier proxyCallIfNotAdmin() { if (msg.sender == _getAdmin() || msg.sender == address(0)) { _; } else { // This WILL halt the call frame on completion. _doProxyCall(); } } /** * @notice Sets the initial admin during contract deployment. Admin address is stored at the * EIP-1967 admin storage slot so that accidental storage collision with the * implementation is not possible. * * @param _admin Address of the initial contract admin. Admin as the ability to access the * transparent proxy interface. */ constructor(address _admin) { _changeAdmin(_admin); } // slither-disable-next-line locked-ether receive() external payable { // Proxy call by default. _doProxyCall(); } // slither-disable-next-line locked-ether fallback() external payable { // Proxy call by default. _doProxyCall(); } /** * @notice Set the implementation contract address. The code at the given address will execute * when this contract is called. * * @param _implementation Address of the implementation contract. */ function upgradeTo(address _implementation) public virtual proxyCallIfNotAdmin { _setImplementation(_implementation); } /** * @notice Set the implementation and call a function in a single transaction. Useful to ensure * atomic execution of initialization-based upgrades. * * @param _implementation Address of the implementation contract. * @param _data Calldata to delegatecall the new implementation with. */ function upgradeToAndCall(address _implementation, bytes calldata _data) public payable virtual proxyCallIfNotAdmin returns (bytes memory) { _setImplementation(_implementation); (bool success, bytes memory returndata) = _implementation.delegatecall(_data); require(success, "Proxy: delegatecall to new implementation contract failed"); return returndata; } /** * @notice Changes the owner of the proxy contract. Only callable by the owner. * * @param _admin New owner of the proxy contract. */ function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin { _changeAdmin(_admin); } /** * @notice Gets the owner of the proxy contract. * * @return Owner address. */ function admin() public virtual proxyCallIfNotAdmin returns (address) { return _getAdmin(); } /** * @notice Queries the implementation address. * * @return Implementation address. */ function implementation() public virtual proxyCallIfNotAdmin returns (address) { return _getImplementation(); } /** * @notice Sets the implementation address. * * @param _implementation New implementation address. */ function _setImplementation(address _implementation) internal { assembly { sstore(IMPLEMENTATION_KEY, _implementation) } emit Upgraded(_implementation); } /** * @notice Changes the owner of the proxy contract. * * @param _admin New owner of the proxy contract. */ function _changeAdmin(address _admin) internal { address previous = _getAdmin(); assembly { sstore(OWNER_KEY, _admin) } emit AdminChanged(previous, _admin); } /** * @notice Performs the proxy call via a delegatecall. */ function _doProxyCall() internal { address impl = _getImplementation(); require(impl != address(0), "Proxy: implementation not initialized"); assembly { // Copy calldata into memory at 0x0....calldatasize. calldatacopy(0x0, 0x0, calldatasize()) // Perform the delegatecall, make sure to pass all available gas. let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0) // Copy returndata into memory at 0x0....returndatasize. Note that this *will* // overwrite the calldata that we just copied into memory but that doesn't really // matter because we'll be returning in a second anyway. returndatacopy(0x0, 0x0, returndatasize()) // Success == 0 means a revert. We'll revert too and pass the data up. if iszero(success) { revert(0x0, returndatasize()) } // Otherwise we'll just return and pass the data up. return(0x0, returndatasize()) } } /** * @notice Queries the implementation address. * * @return Implementation address. */ function _getImplementation() internal view returns (address) { address impl; assembly { impl := sload(IMPLEMENTATION_KEY) } return impl; } /** * @notice Queries the owner of the proxy contract. * * @return Owner address. */ function _getAdmin() internal view returns (address) { address owner; assembly { owner := sload(OWNER_KEY) } return owner; } } // SPDX-License-Identifier: MIT pragma solidity 0.8.15; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Proxy } from "./Proxy.sol"; import { AddressManager } from "../legacy/AddressManager.sol"; import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol"; /** * @title IStaticERC1967Proxy * @notice IStaticERC1967Proxy is a static version of the ERC1967 proxy interface. */ interface IStaticERC1967Proxy { function implementation() external view returns (address); function admin() external view returns (address); } /** * @title IStaticL1ChugSplashProxy * @notice IStaticL1ChugSplashProxy is a static version of the ChugSplash proxy interface. */ interface IStaticL1ChugSplashProxy { function getImplementation() external view returns (address); function getOwner() external view returns (address); } /** * @title ProxyAdmin * @notice This is an auxiliary contract meant to be assigned as the admin of an ERC1967 Proxy, * based on the OpenZeppelin implementation. It has backwards compatibility logic to work * with the various types of proxies that have been deployed by Optimism in the past. */ contract ProxyAdmin is Ownable { /** * @notice The proxy types that the ProxyAdmin can manage. * * @custom:value ERC1967 Represents an ERC1967 compliant transparent proxy interface. * @custom:value CHUGSPLASH Represents the Chugsplash proxy interface (legacy). * @custom:value RESOLVED Represents the ResolvedDelegate proxy (legacy). */ enum ProxyType { ERC1967, CHUGSPLASH, RESOLVED } /** * @notice A mapping of proxy types, used for backwards compatibility. */ mapping(address => ProxyType) public proxyType; /** * @notice A reverse mapping of addresses to names held in the AddressManager. This must be * manually kept up to date with changes in the AddressManager for this contract * to be able to work as an admin for the ResolvedDelegateProxy type. */ mapping(address => string) public implementationName; /** * @notice The address of the address manager, this is required to manage the * ResolvedDelegateProxy type. */ AddressManager public addressManager; /** * @notice A legacy upgrading indicator used by the old Chugsplash Proxy. */ bool internal upgrading; /** * @param _owner Address of the initial owner of this contract. */ constructor(address _owner) Ownable() { _transferOwnership(_owner); } /** * @notice Sets the proxy type for a given address. Only required for non-standard (legacy) * proxy types. * * @param _address Address of the proxy. * @param _type Type of the proxy. */ function setProxyType(address _address, ProxyType _type) external onlyOwner { proxyType[_address] = _type; } /** * @notice Sets the implementation name for a given address. Only required for * ResolvedDelegateProxy type proxies that have an implementation name. * * @param _address Address of the ResolvedDelegateProxy. * @param _name Name of the implementation for the proxy. */ function setImplementationName(address _address, string memory _name) external onlyOwner { implementationName[_address] = _name; } /** * @notice Set the address of the AddressManager. This is required to manage legacy * ResolvedDelegateProxy type proxy contracts. * * @param _address Address of the AddressManager. */ function setAddressManager(AddressManager _address) external onlyOwner { addressManager = _address; } /** * @custom:legacy * @notice Set an address in the address manager. Since only the owner of the AddressManager * can directly modify addresses and the ProxyAdmin will own the AddressManager, this * gives the owner of the ProxyAdmin the ability to modify addresses directly. * * @param _name Name to set within the AddressManager. * @param _address Address to attach to the given name. */ function setAddress(string memory _name, address _address) external onlyOwner { addressManager.setAddress(_name, _address); } /** * @custom:legacy * @notice Set the upgrading status for the Chugsplash proxy type. * * @param _upgrading Whether or not the system is upgrading. */ function setUpgrading(bool _upgrading) external onlyOwner { upgrading = _upgrading; } /** * @custom:legacy * @notice Legacy function used to tell ChugSplashProxy contracts if an upgrade is happening. * * @return Whether or not there is an upgrade going on. May not actually tell you whether an * upgrade is going on, since we don't currently plan to use this variable for anything * other than a legacy indicator to fix a UX bug in the ChugSplash proxy. */ function isUpgrading() external view returns (bool) { return upgrading; } /** * @notice Returns the implementation of the given proxy address. * * @param _proxy Address of the proxy to get the implementation of. * * @return Address of the implementation of the proxy. */ function getProxyImplementation(address _proxy) external view returns (address) { ProxyType ptype = proxyType[_proxy]; if (ptype == ProxyType.ERC1967) { return IStaticERC1967Proxy(_proxy).implementation(); } else if (ptype == ProxyType.CHUGSPLASH) { return IStaticL1ChugSplashProxy(_proxy).getImplementation(); } else if (ptype == ProxyType.RESOLVED) { return addressManager.getAddress(implementationName[_proxy]); } else { revert("ProxyAdmin: unknown proxy type"); } } /** * @notice Returns the admin of the given proxy address. * * @param _proxy Address of the proxy to get the admin of. * * @return Address of the admin of the proxy. */ function getProxyAdmin(address payable _proxy) external view returns (address) { ProxyType ptype = proxyType[_proxy]; if (ptype == ProxyType.ERC1967) { return IStaticERC1967Proxy(_proxy).admin(); } else if (ptype == ProxyType.CHUGSPLASH) { return IStaticL1ChugSplashProxy(_proxy).getOwner(); } else if (ptype == ProxyType.RESOLVED) { return addressManager.owner(); } else { revert("ProxyAdmin: unknown proxy type"); } } /** * @notice Updates the admin of the given proxy address. * * @param _proxy Address of the proxy to update. * @param _newAdmin Address of the new proxy admin. */ function changeProxyAdmin(address payable _proxy, address _newAdmin) external onlyOwner { ProxyType ptype = proxyType[_proxy]; if (ptype == ProxyType.ERC1967) { Proxy(_proxy).changeAdmin(_newAdmin); } else if (ptype == ProxyType.CHUGSPLASH) { L1ChugSplashProxy(_proxy).setOwner(_newAdmin); } else if (ptype == ProxyType.RESOLVED) { addressManager.transferOwnership(_newAdmin); } else { revert("ProxyAdmin: unknown proxy type"); } } /** * @notice Changes a proxy's implementation contract. * * @param _proxy Address of the proxy to upgrade. * @param _implementation Address of the new implementation address. */ function upgrade(address payable _proxy, address _implementation) public onlyOwner { ProxyType ptype = proxyType[_proxy]; if (ptype == ProxyType.ERC1967) { Proxy(_proxy).upgradeTo(_implementation); } else if (ptype == ProxyType.CHUGSPLASH) { L1ChugSplashProxy(_proxy).setStorage( // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, bytes32(uint256(uint160(_implementation))) ); } else if (ptype == ProxyType.RESOLVED) { string memory name = implementationName[_proxy]; addressManager.setAddress(name, _implementation); } else { // It should not be possible to retrieve a ProxyType value which is not matched by // one of the previous conditions. assert(false); } } /** * @notice Changes a proxy's implementation contract and delegatecalls the new implementation * with some given data. Useful for atomic upgrade-and-initialize calls. * * @param _proxy Address of the proxy to upgrade. * @param _implementation Address of the new implementation address. * @param _data Data to trigger the new implementation with. */ function upgradeAndCall( address payable _proxy, address _implementation, bytes memory _data ) external payable onlyOwner { ProxyType ptype = proxyType[_proxy]; if (ptype == ProxyType.ERC1967) { Proxy(_proxy).upgradeToAndCall{ value: msg.value }(_implementation, _data); } else { // reverts if proxy type is unknown upgrade(_proxy, _implementation); (bool success, ) = _proxy.call{ value: msg.value }(_data); require(success, "ProxyAdmin: call to proxy after upgrade failed"); } } } // 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 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; } }
File 3 of 3: L1StandardBridge
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; // Contracts import { StandardBridge } from "src/universal/StandardBridge.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; // Interfaces import { ISemver } from "interfaces/universal/ISemver.sol"; import { ICrossDomainMessenger } from "interfaces/universal/ICrossDomainMessenger.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; /// @custom:proxied true /// @title L1StandardBridge /// @notice The L1StandardBridge is responsible for transfering ETH and ERC20 tokens between L1 and /// L2. In the case that an ERC20 token is native to L1, it will be escrowed within this /// contract. If the ERC20 token is native to L2, it will be burnt. Before Bedrock, ETH was /// stored within this contract. After Bedrock, ETH is instead stored inside the /// OptimismPortal contract. /// NOTE: this contract is not intended to support all variations of ERC20 tokens. Examples /// of some token types that may not be properly supported by this contract include, but are /// not limited to: tokens with transfer fees, rebasing tokens, and tokens with blocklists. contract L1StandardBridge is StandardBridge, ISemver { /// @custom:legacy /// @notice Emitted whenever a deposit of ETH from L1 into L2 is initiated. /// @param from Address of the depositor. /// @param to Address of the recipient on L2. /// @param amount Amount of ETH deposited. /// @param extraData Extra data attached to the deposit. event ETHDepositInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData); /// @custom:legacy /// @notice Emitted whenever a withdrawal of ETH from L2 to L1 is finalized. /// @param from Address of the withdrawer. /// @param to Address of the recipient on L1. /// @param amount Amount of ETH withdrawn. /// @param extraData Extra data attached to the withdrawal. event ETHWithdrawalFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData); /// @custom:legacy /// @notice Emitted whenever an ERC20 deposit is initiated. /// @param l1Token Address of the token on L1. /// @param l2Token Address of the corresponding token on L2. /// @param from Address of the depositor. /// @param to Address of the recipient on L2. /// @param amount Amount of the ERC20 deposited. /// @param extraData Extra data attached to the deposit. event ERC20DepositInitiated( address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData ); /// @custom:legacy /// @notice Emitted whenever an ERC20 withdrawal is finalized. /// @param l1Token Address of the token on L1. /// @param l2Token Address of the corresponding token on L2. /// @param from Address of the withdrawer. /// @param to Address of the recipient on L1. /// @param amount Amount of the ERC20 withdrawn. /// @param extraData Extra data attached to the withdrawal. event ERC20WithdrawalFinalized( address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData ); /// @notice Semantic version. /// @custom:semver 2.3.0 string public constant version = "2.3.0"; /// @notice Address of the SuperchainConfig contract. ISuperchainConfig public superchainConfig; /// @custom:legacy /// @custom:spacer systemConfig /// @notice Spacer taking up the legacy `systemConfig` slot. address private spacer_51_0_20; /// @notice Constructs the L1StandardBridge contract. constructor() StandardBridge() { _disableInitializers(); } /// @notice Initializer. /// @param _messenger Contract for the CrossDomainMessenger on this network. /// @param _superchainConfig Contract for the SuperchainConfig on this network. function initialize(ICrossDomainMessenger _messenger, ISuperchainConfig _superchainConfig) external initializer { superchainConfig = _superchainConfig; __StandardBridge_init({ _messenger: _messenger, _otherBridge: StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)) }); } /// @inheritdoc StandardBridge function paused() public view override returns (bool) { return superchainConfig.paused(); } /// @notice Allows EOAs to bridge ETH by sending directly to the bridge. receive() external payable override onlyEOA { _initiateETHDeposit(msg.sender, msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes("")); } /// @custom:legacy /// @notice Deposits some amount of ETH into the sender's account on L2. /// @param _minGasLimit Minimum gas limit for the deposit message on L2. /// @param _extraData Optional data to forward to L2. /// Data supplied here will not be used to execute any code on L2 and is /// only emitted as extra data for the convenience of off-chain tooling. function depositETH(uint32 _minGasLimit, bytes calldata _extraData) external payable onlyEOA { _initiateETHDeposit(msg.sender, msg.sender, _minGasLimit, _extraData); } /// @custom:legacy /// @notice Deposits some amount of ETH into a target account on L2. /// Note that if ETH is sent to a contract on L2 and the call fails, then that ETH will /// be locked in the L2StandardBridge. ETH may be recoverable if the call can be /// successfully replayed by increasing the amount of gas supplied to the call. If the /// call will fail for any amount of gas, then the ETH will be locked permanently. /// @param _to Address of the recipient on L2. /// @param _minGasLimit Minimum gas limit for the deposit message on L2. /// @param _extraData Optional data to forward to L2. /// Data supplied here will not be used to execute any code on L2 and is /// only emitted as extra data for the convenience of off-chain tooling. function depositETHTo(address _to, uint32 _minGasLimit, bytes calldata _extraData) external payable { _initiateETHDeposit(msg.sender, _to, _minGasLimit, _extraData); } /// @custom:legacy /// @notice Deposits some amount of ERC20 tokens into the sender's account on L2. /// @param _l1Token Address of the L1 token being deposited. /// @param _l2Token Address of the corresponding token on L2. /// @param _amount Amount of the ERC20 to deposit. /// @param _minGasLimit Minimum gas limit for the deposit message on L2. /// @param _extraData Optional data to forward to L2. /// Data supplied here will not be used to execute any code on L2 and is /// only emitted as extra data for the convenience of off-chain tooling. function depositERC20( address _l1Token, address _l2Token, uint256 _amount, uint32 _minGasLimit, bytes calldata _extraData ) external virtual onlyEOA { _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _extraData); } /// @custom:legacy /// @notice Deposits some amount of ERC20 tokens into a target account on L2. /// @param _l1Token Address of the L1 token being deposited. /// @param _l2Token Address of the corresponding token on L2. /// @param _to Address of the recipient on L2. /// @param _amount Amount of the ERC20 to deposit. /// @param _minGasLimit Minimum gas limit for the deposit message on L2. /// @param _extraData Optional data to forward to L2. /// Data supplied here will not be used to execute any code on L2 and is /// only emitted as extra data for the convenience of off-chain tooling. function depositERC20To( address _l1Token, address _l2Token, address _to, uint256 _amount, uint32 _minGasLimit, bytes calldata _extraData ) external virtual { _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _minGasLimit, _extraData); } /// @custom:legacy /// @notice Finalizes a withdrawal of ETH from L2. /// @param _from Address of the withdrawer on L2. /// @param _to Address of the recipient on L1. /// @param _amount Amount of ETH to withdraw. /// @param _extraData Optional data forwarded from L2. function finalizeETHWithdrawal( address _from, address _to, uint256 _amount, bytes calldata _extraData ) external payable { finalizeBridgeETH(_from, _to, _amount, _extraData); } /// @custom:legacy /// @notice Finalizes a withdrawal of ERC20 tokens from L2. /// @param _l1Token Address of the token on L1. /// @param _l2Token Address of the corresponding token on L2. /// @param _from Address of the withdrawer on L2. /// @param _to Address of the recipient on L1. /// @param _amount Amount of the ERC20 to withdraw. /// @param _extraData Optional data forwarded from L2. function finalizeERC20Withdrawal( address _l1Token, address _l2Token, address _from, address _to, uint256 _amount, bytes calldata _extraData ) external { finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData); } /// @custom:legacy /// @notice Retrieves the access of the corresponding L2 bridge contract. /// @return Address of the corresponding L2 bridge contract. function l2TokenBridge() external view returns (address) { return address(otherBridge); } /// @notice Internal function for initiating an ETH deposit. /// @param _from Address of the sender on L1. /// @param _to Address of the recipient on L2. /// @param _minGasLimit Minimum gas limit for the deposit message on L2. /// @param _extraData Optional data to forward to L2. function _initiateETHDeposit(address _from, address _to, uint32 _minGasLimit, bytes memory _extraData) internal { _initiateBridgeETH(_from, _to, msg.value, _minGasLimit, _extraData); } /// @notice Internal function for initiating an ERC20 deposit. /// @param _l1Token Address of the L1 token being deposited. /// @param _l2Token Address of the corresponding token on L2. /// @param _from Address of the sender on L1. /// @param _to Address of the recipient on L2. /// @param _amount Amount of the ERC20 to deposit. /// @param _minGasLimit Minimum gas limit for the deposit message on L2. /// @param _extraData Optional data to forward to L2. function _initiateERC20Deposit( address _l1Token, address _l2Token, address _from, address _to, uint256 _amount, uint32 _minGasLimit, bytes memory _extraData ) internal { _initiateBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _minGasLimit, _extraData); } /// @inheritdoc StandardBridge /// @notice Emits the legacy ETHDepositInitiated event followed by the ETHBridgeInitiated event. /// This is necessary for backwards compatibility with the legacy bridge. function _emitETHBridgeInitiated( address _from, address _to, uint256 _amount, bytes memory _extraData ) internal override { emit ETHDepositInitiated(_from, _to, _amount, _extraData); super._emitETHBridgeInitiated(_from, _to, _amount, _extraData); } /// @inheritdoc StandardBridge /// @notice Emits the legacy ERC20DepositInitiated event followed by the ERC20BridgeInitiated /// event. This is necessary for backwards compatibility with the legacy bridge. function _emitETHBridgeFinalized( address _from, address _to, uint256 _amount, bytes memory _extraData ) internal override { emit ETHWithdrawalFinalized(_from, _to, _amount, _extraData); super._emitETHBridgeFinalized(_from, _to, _amount, _extraData); } /// @inheritdoc StandardBridge /// @notice Emits the legacy ERC20WithdrawalFinalized event followed by the ERC20BridgeFinalized /// event. This is necessary for backwards compatibility with the legacy bridge. function _emitERC20BridgeInitiated( address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes memory _extraData ) internal override { emit ERC20DepositInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData); super._emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData); } /// @inheritdoc StandardBridge /// @notice Emits the legacy ERC20WithdrawalFinalized event followed by the ERC20BridgeFinalized /// event. This is necessary for backwards compatibility with the legacy bridge. function _emitERC20BridgeFinalized( address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes memory _extraData ) internal override { emit ERC20WithdrawalFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData); super._emitERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData); } } // SPDX-License-Identifier: MIT pragma solidity 0.8.15; // Contracts import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; // Libraries import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; import { EOA } from "src/libraries/EOA.sol"; // Interfaces import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IOptimismMintableERC20 } from "interfaces/universal/IOptimismMintableERC20.sol"; import { ILegacyMintableERC20 } from "interfaces/legacy/ILegacyMintableERC20.sol"; import { ICrossDomainMessenger } from "interfaces/universal/ICrossDomainMessenger.sol"; /// @custom:upgradeable /// @title StandardBridge /// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles /// the core bridging logic, including escrowing tokens that are native to the local chain /// and minting/burning tokens that are native to the remote chain. abstract contract StandardBridge is Initializable { using SafeERC20 for IERC20; /// @notice The L2 gas limit set when eth is depoisited using the receive() function. uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000; /// @custom:legacy /// @custom:spacer messenger /// @notice Spacer for backwards compatibility. bytes30 private spacer_0_2_30; /// @custom:legacy /// @custom:spacer l2TokenBridge /// @notice Spacer for backwards compatibility. address private spacer_1_0_20; /// @notice Mapping that stores deposits for a given pair of local and remote tokens. mapping(address => mapping(address => uint256)) public deposits; /// @notice Messenger contract on this domain. /// @custom:network-specific ICrossDomainMessenger public messenger; /// @notice Corresponding bridge on the other domain. /// @custom:network-specific StandardBridge public otherBridge; /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades. /// A gap size of 45 was chosen here, so that the first slot used in a child contract /// would be a multiple of 50. uint256[45] private __gap; /// @notice Emitted when an ETH bridge is initiated to the other chain. /// @param from Address of the sender. /// @param to Address of the receiver. /// @param amount Amount of ETH sent. /// @param extraData Extra data sent with the transaction. event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData); /// @notice Emitted when an ETH bridge is finalized on this chain. /// @param from Address of the sender. /// @param to Address of the receiver. /// @param amount Amount of ETH sent. /// @param extraData Extra data sent with the transaction. event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData); /// @notice Emitted when an ERC20 bridge is initiated to the other chain. /// @param localToken Address of the ERC20 on this chain. /// @param remoteToken Address of the ERC20 on the remote chain. /// @param from Address of the sender. /// @param to Address of the receiver. /// @param amount Amount of the ERC20 sent. /// @param extraData Extra data sent with the transaction. event ERC20BridgeInitiated( address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 amount, bytes extraData ); /// @notice Emitted when an ERC20 bridge is finalized on this chain. /// @param localToken Address of the ERC20 on this chain. /// @param remoteToken Address of the ERC20 on the remote chain. /// @param from Address of the sender. /// @param to Address of the receiver. /// @param amount Amount of the ERC20 sent. /// @param extraData Extra data sent with the transaction. event ERC20BridgeFinalized( address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 amount, bytes extraData ); /// @notice Only allow EOAs to call the functions. Note that this is not safe against contracts /// calling code within their constructors, but also doesn't really matter since we're /// just trying to prevent users accidentally depositing with smart contract wallets. modifier onlyEOA() { require(EOA.isSenderEOA(), "StandardBridge: function can only be called from an EOA"); _; } /// @notice Ensures that the caller is a cross-chain message from the other bridge. modifier onlyOtherBridge() { require( msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(otherBridge), "StandardBridge: function can only be called from the other bridge" ); _; } /// @notice Initializer. /// @param _messenger Contract for CrossDomainMessenger on this network. /// @param _otherBridge Contract for the other StandardBridge contract. function __StandardBridge_init( ICrossDomainMessenger _messenger, StandardBridge _otherBridge ) internal onlyInitializing { messenger = _messenger; otherBridge = _otherBridge; } /// @notice Allows EOAs to bridge ETH by sending directly to the bridge. /// Must be implemented by contracts that inherit. receive() external payable virtual; /// @notice Getter for messenger contract. /// Public getter is legacy and will be removed in the future. Use `messenger` instead. /// @return Contract of the messenger on this domain. /// @custom:legacy function MESSENGER() external view returns (ICrossDomainMessenger) { return messenger; } /// @notice Getter for the other bridge contract. /// Public getter is legacy and will be removed in the future. Use `otherBridge` instead. /// @return Contract of the bridge on the other network. /// @custom:legacy function OTHER_BRIDGE() external view returns (StandardBridge) { return otherBridge; } /// @notice This function should return true if the contract is paused. /// On L1 this function will check the SuperchainConfig for its paused status. /// On L2 this function should be a no-op. /// @return Whether or not the contract is paused. function paused() public view virtual returns (bool) { return false; } /// @notice Sends ETH to the sender's address on the other chain. /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with. /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// not be triggered with this data, but it will be emitted and can be used /// to identify the transaction. function bridgeETH(uint32 _minGasLimit, bytes calldata _extraData) public payable onlyEOA { _initiateBridgeETH(msg.sender, msg.sender, msg.value, _minGasLimit, _extraData); } /// @notice Sends ETH to a receiver's address on the other chain. Note that if ETH is sent to a /// smart contract and the call fails, the ETH will be temporarily locked in the /// StandardBridge on the other chain until the call is replayed. If the call cannot be /// replayed with any amount of gas (call always reverts), then the ETH will be /// permanently locked in the StandardBridge on the other chain. ETH will also /// be locked if the receiver is the other bridge, because finalizeBridgeETH will revert /// in that case. /// @param _to Address of the receiver. /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with. /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// not be triggered with this data, but it will be emitted and can be used /// to identify the transaction. function bridgeETHTo(address _to, uint32 _minGasLimit, bytes calldata _extraData) public payable { _initiateBridgeETH(msg.sender, _to, msg.value, _minGasLimit, _extraData); } /// @notice Sends ERC20 tokens to the sender's address on the other chain. /// @param _localToken Address of the ERC20 on this chain. /// @param _remoteToken Address of the corresponding token on the remote chain. /// @param _amount Amount of local tokens to deposit. /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with. /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// not be triggered with this data, but it will be emitted and can be used /// to identify the transaction. function bridgeERC20( address _localToken, address _remoteToken, uint256 _amount, uint32 _minGasLimit, bytes calldata _extraData ) public virtual onlyEOA { _initiateBridgeERC20(_localToken, _remoteToken, msg.sender, msg.sender, _amount, _minGasLimit, _extraData); } /// @notice Sends ERC20 tokens to a receiver's address on the other chain. /// @param _localToken Address of the ERC20 on this chain. /// @param _remoteToken Address of the corresponding token on the remote chain. /// @param _to Address of the receiver. /// @param _amount Amount of local tokens to deposit. /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with. /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// not be triggered with this data, but it will be emitted and can be used /// to identify the transaction. function bridgeERC20To( address _localToken, address _remoteToken, address _to, uint256 _amount, uint32 _minGasLimit, bytes calldata _extraData ) public virtual { _initiateBridgeERC20(_localToken, _remoteToken, msg.sender, _to, _amount, _minGasLimit, _extraData); } /// @notice Finalizes an ETH bridge on this chain. Can only be triggered by the other /// StandardBridge contract on the remote chain. /// @param _from Address of the sender. /// @param _to Address of the receiver. /// @param _amount Amount of ETH being bridged. /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// not be triggered with this data, but it will be emitted and can be used /// to identify the transaction. function finalizeBridgeETH( address _from, address _to, uint256 _amount, bytes calldata _extraData ) public payable onlyOtherBridge { require(paused() == false, "StandardBridge: paused"); require(msg.value == _amount, "StandardBridge: amount sent does not match amount required"); require(_to != address(this), "StandardBridge: cannot send to self"); require(_to != address(messenger), "StandardBridge: cannot send to messenger"); // Emit the correct events. By default this will be _amount, but child // contracts may override this function in order to emit legacy events as well. _emitETHBridgeFinalized(_from, _to, _amount, _extraData); bool success = SafeCall.call(_to, gasleft(), _amount, hex""); require(success, "StandardBridge: ETH transfer failed"); } /// @notice Finalizes an ERC20 bridge on this chain. Can only be triggered by the other /// StandardBridge contract on the remote chain. /// @param _localToken Address of the ERC20 on this chain. /// @param _remoteToken Address of the corresponding token on the remote chain. /// @param _from Address of the sender. /// @param _to Address of the receiver. /// @param _amount Amount of the ERC20 being bridged. /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// not be triggered with this data, but it will be emitted and can be used /// to identify the transaction. function finalizeBridgeERC20( address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes calldata _extraData ) public onlyOtherBridge { require(paused() == false, "StandardBridge: paused"); if (_isOptimismMintableERC20(_localToken)) { require( _isCorrectTokenPair(_localToken, _remoteToken), "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token" ); IOptimismMintableERC20(_localToken).mint(_to, _amount); } else { deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] - _amount; IERC20(_localToken).safeTransfer(_to, _amount); } // Emit the correct events. By default this will be ERC20BridgeFinalized, but child // contracts may override this function in order to emit legacy events as well. _emitERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData); } /// @notice Initiates a bridge of ETH through the CrossDomainMessenger. /// @param _from Address of the sender. /// @param _to Address of the receiver. /// @param _amount Amount of ETH being bridged. /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with. /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// not be triggered with this data, but it will be emitted and can be used /// to identify the transaction. function _initiateBridgeETH( address _from, address _to, uint256 _amount, uint32 _minGasLimit, bytes memory _extraData ) internal { require(msg.value == _amount, "StandardBridge: bridging ETH must include sufficient ETH value"); // Emit the correct events. By default this will be _amount, but child // contracts may override this function in order to emit legacy events as well. _emitETHBridgeInitiated(_from, _to, _amount, _extraData); messenger.sendMessage{ value: _amount }({ _target: address(otherBridge), _message: abi.encodeWithSelector(this.finalizeBridgeETH.selector, _from, _to, _amount, _extraData), _minGasLimit: _minGasLimit }); } /// @notice Sends ERC20 tokens to a receiver's address on the other chain. /// @param _localToken Address of the ERC20 on this chain. /// @param _remoteToken Address of the corresponding token on the remote chain. /// @param _to Address of the receiver. /// @param _amount Amount of local tokens to deposit. /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with. /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will /// not be triggered with this data, but it will be emitted and can be used /// to identify the transaction. function _initiateBridgeERC20( address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, uint32 _minGasLimit, bytes memory _extraData ) internal { require(msg.value == 0, "StandardBridge: cannot send value"); if (_isOptimismMintableERC20(_localToken)) { require( _isCorrectTokenPair(_localToken, _remoteToken), "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token" ); IOptimismMintableERC20(_localToken).burn(_from, _amount); } else { IERC20(_localToken).safeTransferFrom(_from, address(this), _amount); deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] + _amount; } // Emit the correct events. By default this will be ERC20BridgeInitiated, but child // contracts may override this function in order to emit legacy events as well. _emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData); messenger.sendMessage({ _target: address(otherBridge), _message: abi.encodeWithSelector( this.finalizeBridgeERC20.selector, // Because this call will be executed on the remote chain, we reverse the order of // the remote and local token addresses relative to their order in the // finalizeBridgeERC20 function. _remoteToken, _localToken, _from, _to, _amount, _extraData ), _minGasLimit: _minGasLimit }); } /// @notice Checks if a given address is an OptimismMintableERC20. Not perfect, but good enough. /// Just the way we like it. /// @param _token Address of the token to check. /// @return True if the token is an OptimismMintableERC20. function _isOptimismMintableERC20(address _token) internal view returns (bool) { return ERC165Checker.supportsInterface(_token, type(ILegacyMintableERC20).interfaceId) || ERC165Checker.supportsInterface(_token, type(IOptimismMintableERC20).interfaceId); } /// @notice Checks if the "other token" is the correct pair token for the OptimismMintableERC20. /// Calls can be saved in the future by combining this logic with /// `_isOptimismMintableERC20`. /// @param _mintableToken OptimismMintableERC20 to check against. /// @param _otherToken Pair token to check. /// @return True if the other token is the correct pair token for the OptimismMintableERC20. function _isCorrectTokenPair(address _mintableToken, address _otherToken) internal view returns (bool) { if (ERC165Checker.supportsInterface(_mintableToken, type(ILegacyMintableERC20).interfaceId)) { return _otherToken == ILegacyMintableERC20(_mintableToken).l1Token(); } else { return _otherToken == IOptimismMintableERC20(_mintableToken).remoteToken(); } } /// @notice Emits the ETHBridgeInitiated event and if necessary the appropriate legacy event /// when an ETH bridge is finalized on this chain. /// @param _from Address of the sender. /// @param _to Address of the receiver. /// @param _amount Amount of ETH sent. /// @param _extraData Extra data sent with the transaction. function _emitETHBridgeInitiated( address _from, address _to, uint256 _amount, bytes memory _extraData ) internal virtual { emit ETHBridgeInitiated(_from, _to, _amount, _extraData); } /// @notice Emits the ETHBridgeFinalized and if necessary the appropriate legacy event when an /// ETH bridge is finalized on this chain. /// @param _from Address of the sender. /// @param _to Address of the receiver. /// @param _amount Amount of ETH sent. /// @param _extraData Extra data sent with the transaction. function _emitETHBridgeFinalized( address _from, address _to, uint256 _amount, bytes memory _extraData ) internal virtual { emit ETHBridgeFinalized(_from, _to, _amount, _extraData); } /// @notice Emits the ERC20BridgeInitiated event and if necessary the appropriate legacy /// event when an ERC20 bridge is initiated to the other chain. /// @param _localToken Address of the ERC20 on this chain. /// @param _remoteToken Address of the ERC20 on the remote chain. /// @param _from Address of the sender. /// @param _to Address of the receiver. /// @param _amount Amount of the ERC20 sent. /// @param _extraData Extra data sent with the transaction. function _emitERC20BridgeInitiated( address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes memory _extraData ) internal virtual { emit ERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData); } /// @notice Emits the ERC20BridgeFinalized event and if necessary the appropriate legacy /// event when an ERC20 bridge is initiated to the other chain. /// @param _localToken Address of the ERC20 on this chain. /// @param _remoteToken Address of the ERC20 on the remote chain. /// @param _from Address of the sender. /// @param _to Address of the receiver. /// @param _amount Amount of the ERC20 sent. /// @param _extraData Extra data sent with the transaction. function _emitERC20BridgeFinalized( address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes memory _extraData ) internal virtual { emit ERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Predeploys /// @notice Contains constant addresses for protocol contracts that are pre-deployed to the L2 system. // This excludes the preinstalls (non-protocol contracts). library Predeploys { /// @notice Number of predeploy-namespace addresses reserved for protocol usage. uint256 internal constant PREDEPLOY_COUNT = 2048; /// @custom:legacy /// @notice Address of the LegacyMessagePasser predeploy. Deprecate. Use the updated /// L2ToL1MessagePasser contract instead. address internal constant LEGACY_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; /// @custom:legacy /// @notice Address of the L1MessageSender predeploy. Deprecated. Use L2CrossDomainMessenger /// or access tx.origin (or msg.sender) in a L1 to L2 transaction instead. /// Not embedded into new OP-Stack chains. address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001; /// @custom:legacy /// @notice Address of the DeployerWhitelist predeploy. No longer active. address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002; /// @notice Address of the canonical WETH contract. address internal constant WETH = 0x4200000000000000000000000000000000000006; /// @notice Address of the L2CrossDomainMessenger predeploy. address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007; /// @notice Address of the GasPriceOracle predeploy. Includes fee information /// and helpers for computing the L1 portion of the transaction fee. address internal constant GAS_PRICE_ORACLE = 0x420000000000000000000000000000000000000F; /// @notice Address of the L2StandardBridge predeploy. address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010; //// @notice Address of the SequencerFeeWallet predeploy. address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011; /// @notice Address of the OptimismMintableERC20Factory predeploy. address internal constant OPTIMISM_MINTABLE_ERC20_FACTORY = 0x4200000000000000000000000000000000000012; /// @custom:legacy /// @notice Address of the L1BlockNumber predeploy. Deprecated. Use the L1Block predeploy /// instead, which exposes more information about the L1 state. address internal constant L1_BLOCK_NUMBER = 0x4200000000000000000000000000000000000013; /// @notice Address of the L2ERC721Bridge predeploy. address internal constant L2_ERC721_BRIDGE = 0x4200000000000000000000000000000000000014; /// @notice Address of the L1Block predeploy. address internal constant L1_BLOCK_ATTRIBUTES = 0x4200000000000000000000000000000000000015; /// @notice Address of the L2ToL1MessagePasser predeploy. address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000016; /// @notice Address of the OptimismMintableERC721Factory predeploy. address internal constant OPTIMISM_MINTABLE_ERC721_FACTORY = 0x4200000000000000000000000000000000000017; /// @notice Address of the ProxyAdmin predeploy. address internal constant PROXY_ADMIN = 0x4200000000000000000000000000000000000018; /// @notice Address of the BaseFeeVault predeploy. address internal constant BASE_FEE_VAULT = 0x4200000000000000000000000000000000000019; /// @notice Address of the L1FeeVault predeploy. address internal constant L1_FEE_VAULT = 0x420000000000000000000000000000000000001A; /// @notice Address of the OperatorFeeVault predeploy. address internal constant OPERATOR_FEE_VAULT = 0x420000000000000000000000000000000000001b; /// @notice Address of the SchemaRegistry predeploy. address internal constant SCHEMA_REGISTRY = 0x4200000000000000000000000000000000000020; /// @notice Address of the EAS predeploy. address internal constant EAS = 0x4200000000000000000000000000000000000021; /// @notice Address of the GovernanceToken predeploy. address internal constant GOVERNANCE_TOKEN = 0x4200000000000000000000000000000000000042; /// @custom:legacy /// @notice Address of the LegacyERC20ETH predeploy. Deprecated. Balances are migrated to the /// state trie as of the Bedrock upgrade. Contract has been locked and write functions /// can no longer be accessed. address internal constant LEGACY_ERC20_ETH = 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000; /// @notice Address of the CrossL2Inbox predeploy. address internal constant CROSS_L2_INBOX = 0x4200000000000000000000000000000000000022; /// @notice Address of the L2ToL2CrossDomainMessenger predeploy. address internal constant L2_TO_L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000023; /// @notice Address of the SuperchainWETH predeploy. address internal constant SUPERCHAIN_WETH = 0x4200000000000000000000000000000000000024; /// @notice Address of the ETHLiquidity predeploy. address internal constant ETH_LIQUIDITY = 0x4200000000000000000000000000000000000025; /// @notice Address of the OptimismSuperchainERC20Factory predeploy. address internal constant OPTIMISM_SUPERCHAIN_ERC20_FACTORY = 0x4200000000000000000000000000000000000026; /// @notice Address of the OptimismSuperchainERC20Beacon predeploy. address internal constant OPTIMISM_SUPERCHAIN_ERC20_BEACON = 0x4200000000000000000000000000000000000027; // TODO: Precalculate the address of the implementation contract /// @notice Arbitrary address of the OptimismSuperchainERC20 implementation contract. address internal constant OPTIMISM_SUPERCHAIN_ERC20 = 0xB9415c6cA93bdC545D4c5177512FCC22EFa38F28; /// @notice Address of the SuperchainTokenBridge predeploy. address internal constant SUPERCHAIN_TOKEN_BRIDGE = 0x4200000000000000000000000000000000000028; /// @notice Returns the name of the predeploy at the given address. function getName(address _addr) internal pure returns (string memory out_) { require(isPredeployNamespace(_addr), "Predeploys: address must be a predeploy"); if (_addr == LEGACY_MESSAGE_PASSER) return "LegacyMessagePasser"; if (_addr == L1_MESSAGE_SENDER) return "L1MessageSender"; if (_addr == DEPLOYER_WHITELIST) return "DeployerWhitelist"; if (_addr == WETH) return "WETH"; if (_addr == L2_CROSS_DOMAIN_MESSENGER) return "L2CrossDomainMessenger"; if (_addr == GAS_PRICE_ORACLE) return "GasPriceOracle"; if (_addr == L2_STANDARD_BRIDGE) return "L2StandardBridge"; if (_addr == SEQUENCER_FEE_WALLET) return "SequencerFeeVault"; if (_addr == OPTIMISM_MINTABLE_ERC20_FACTORY) return "OptimismMintableERC20Factory"; if (_addr == L1_BLOCK_NUMBER) return "L1BlockNumber"; if (_addr == L2_ERC721_BRIDGE) return "L2ERC721Bridge"; if (_addr == L1_BLOCK_ATTRIBUTES) return "L1Block"; if (_addr == L2_TO_L1_MESSAGE_PASSER) return "L2ToL1MessagePasser"; if (_addr == OPTIMISM_MINTABLE_ERC721_FACTORY) return "OptimismMintableERC721Factory"; if (_addr == PROXY_ADMIN) return "ProxyAdmin"; if (_addr == BASE_FEE_VAULT) return "BaseFeeVault"; if (_addr == L1_FEE_VAULT) return "L1FeeVault"; if (_addr == OPERATOR_FEE_VAULT) return "OperatorFeeVault"; if (_addr == SCHEMA_REGISTRY) return "SchemaRegistry"; if (_addr == EAS) return "EAS"; if (_addr == GOVERNANCE_TOKEN) return "GovernanceToken"; if (_addr == LEGACY_ERC20_ETH) return "LegacyERC20ETH"; if (_addr == CROSS_L2_INBOX) return "CrossL2Inbox"; if (_addr == L2_TO_L2_CROSS_DOMAIN_MESSENGER) return "L2ToL2CrossDomainMessenger"; if (_addr == SUPERCHAIN_WETH) return "SuperchainWETH"; if (_addr == ETH_LIQUIDITY) return "ETHLiquidity"; if (_addr == OPTIMISM_SUPERCHAIN_ERC20_FACTORY) return "OptimismSuperchainERC20Factory"; if (_addr == OPTIMISM_SUPERCHAIN_ERC20_BEACON) return "OptimismSuperchainERC20Beacon"; if (_addr == SUPERCHAIN_TOKEN_BRIDGE) return "SuperchainTokenBridge"; revert("Predeploys: unnamed predeploy"); } /// @notice Returns true if the predeploy is not proxied. function notProxied(address _addr) internal pure returns (bool) { return _addr == GOVERNANCE_TOKEN || _addr == WETH; } /// @notice Returns true if the address is a defined predeploy that is embedded into new OP-Stack chains. function isSupportedPredeploy(address _addr, bool _useInterop) internal pure returns (bool) { return _addr == LEGACY_MESSAGE_PASSER || _addr == DEPLOYER_WHITELIST || _addr == WETH || _addr == L2_CROSS_DOMAIN_MESSENGER || _addr == GAS_PRICE_ORACLE || _addr == L2_STANDARD_BRIDGE || _addr == SEQUENCER_FEE_WALLET || _addr == OPTIMISM_MINTABLE_ERC20_FACTORY || _addr == L1_BLOCK_NUMBER || _addr == L2_ERC721_BRIDGE || _addr == L1_BLOCK_ATTRIBUTES || _addr == L2_TO_L1_MESSAGE_PASSER || _addr == OPTIMISM_MINTABLE_ERC721_FACTORY || _addr == PROXY_ADMIN || _addr == BASE_FEE_VAULT || _addr == L1_FEE_VAULT || _addr == OPERATOR_FEE_VAULT || _addr == SCHEMA_REGISTRY || _addr == EAS || _addr == GOVERNANCE_TOKEN || (_useInterop && _addr == CROSS_L2_INBOX) || (_useInterop && _addr == L2_TO_L2_CROSS_DOMAIN_MESSENGER) || (_useInterop && _addr == SUPERCHAIN_WETH) || (_useInterop && _addr == ETH_LIQUIDITY) || (_useInterop && _addr == SUPERCHAIN_TOKEN_BRIDGE); } function isPredeployNamespace(address _addr) internal pure returns (bool) { return uint160(_addr) >> 11 == uint160(0x4200000000000000000000000000000000000000) >> 11; } /// @notice Function to compute the expected address of the predeploy implementation /// in the genesis state. function predeployToCodeNamespace(address _addr) internal pure returns (address) { require( isPredeployNamespace(_addr), "Predeploys: can only derive code-namespace address for predeploy addresses" ); return address( uint160(uint256(uint160(_addr)) & 0xffff | uint256(uint160(0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000))) ); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title ISemver /// @notice ISemver is a simple contract for ensuring that contracts are /// versioned using semantic versioning. interface ISemver { /// @notice Getter for the semantic version of the contract. This is not /// meant to be used onchain but instead meant to be used by offchain /// tooling. /// @return Semver contract version as a string. function version() external view returns (string memory); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ICrossDomainMessenger { event FailedRelayedMessage(bytes32 indexed msgHash); event Initialized(uint8 version); event RelayedMessage(bytes32 indexed msgHash); event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); event SentMessageExtension1(address indexed sender, uint256 value); function MESSAGE_VERSION() external view returns (uint16); function MIN_GAS_CALLDATA_OVERHEAD() external view returns (uint64); function MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR() external view returns (uint64); function MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR() external view returns (uint64); function OTHER_MESSENGER() external view returns (ICrossDomainMessenger); function RELAY_CALL_OVERHEAD() external view returns (uint64); function RELAY_CONSTANT_OVERHEAD() external view returns (uint64); function RELAY_GAS_CHECK_BUFFER() external view returns (uint64); function RELAY_RESERVED_GAS() external view returns (uint64); function TX_BASE_GAS() external view returns (uint64); function FLOOR_CALLDATA_OVERHEAD() external view returns (uint64); function ENCODING_OVERHEAD() external view returns (uint64); function baseGas(bytes memory _message, uint32 _minGasLimit) external pure returns (uint64); function failedMessages(bytes32) external view returns (bool); function messageNonce() external view returns (uint256); function otherMessenger() external view returns (ICrossDomainMessenger); function paused() external view returns (bool); function relayMessage( uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _minGasLimit, bytes memory _message ) external payable; function sendMessage(address _target, bytes memory _message, uint32 _minGasLimit) external payable; function successfulMessages(bytes32) external view returns (bool); function xDomainMessageSender() external view returns (address); function __constructor__() external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ISuperchainConfig { enum UpdateType { GUARDIAN } event ConfigUpdate(UpdateType indexed updateType, bytes data); event Initialized(uint8 version); event Paused(string identifier); event Unpaused(); function GUARDIAN_SLOT() external view returns (bytes32); function PAUSED_SLOT() external view returns (bytes32); function guardian() external view returns (address guardian_); function initialize(address _guardian, bool _paused) external; function pause(string memory _identifier) external; function paused() external view returns (bool paused_); function unpause() external; function version() external view returns (string memory); function __constructor__() external; } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/Address.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. Equivalent to `reinitializer(1)`. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!Address.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. * * `initializer` is equivalent to `reinitializer(1)`, so 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. * * 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. */ 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. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.2) (utils/introspection/ERC165Checker.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Library used to query support of an interface declared via {IERC165}. * * Note that these functions return the actual result of the query: they do not * `revert` if an interface is not supported. It is up to the caller to decide * what to do in these cases. */ library ERC165Checker { // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; /** * @dev Returns true if `account` supports the {IERC165} interface, */ function supportsERC165(address account) internal view returns (bool) { // Any contract that implements ERC165 must explicitly indicate support of // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid return _supportsERC165Interface(account, type(IERC165).interfaceId) && !_supportsERC165Interface(account, _INTERFACE_ID_INVALID); } /** * @dev Returns true if `account` supports the interface defined by * `interfaceId`. Support for {IERC165} itself is queried automatically. * * See {IERC165-supportsInterface}. */ function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { // query support of both ERC165 as per the spec and support of _interfaceId return supportsERC165(account) && _supportsERC165Interface(account, interfaceId); } /** * @dev Returns a boolean array where each value corresponds to the * interfaces passed in and whether they're supported or not. This allows * you to batch check interfaces for a contract where your expectation * is that some interfaces may not be supported. * * See {IERC165-supportsInterface}. * * _Available since v3.4._ */ function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) { // an array of booleans corresponding to interfaceIds and whether they're supported or not bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); // query support of ERC165 itself if (supportsERC165(account)) { // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]); } } return interfaceIdsSupported; } /** * @dev Returns true if `account` supports all the interfaces defined in * `interfaceIds`. Support for {IERC165} itself is queried automatically. * * Batch-querying can lead to gas savings by skipping repeated checks for * {IERC165} support. * * See {IERC165-supportsInterface}. */ function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { // query support of ERC165 itself if (!supportsERC165(account)) { return false; } // query support of each interface in _interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { if (!_supportsERC165Interface(account, interfaceIds[i])) { return false; } } // all interfaces supported return true; } /** * @notice Query if a contract implements an interface, does not check ERC165 support * @param account The address of the contract to query for support of an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract at account indicates support of the interface with * identifier interfaceId, false otherwise * @dev Assumes that account contains a contract that supports ERC165, otherwise * the behavior of this method is undefined. This precondition can be checked * with {supportsERC165}. * Interface identification is specified in ERC-165. */ function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) { // prepare call bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); // perform static call bool success; uint256 returnSize; uint256 returnValue; assembly { success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20) returnSize := returndatasize() returnValue := mload(0x00) } return success && returnSize >= 0x20 && returnValue > 0; } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.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: MIT pragma solidity ^0.8.0; /// @title SafeCall /// @notice Perform low level safe calls library SafeCall { /// @notice Performs a low level call without copying any returndata. /// @dev Passes no calldata to the call context. /// @param _target Address to call /// @param _gas Amount of gas to pass to the call /// @param _value Amount of value to pass to the call function send(address _target, uint256 _gas, uint256 _value) internal returns (bool success_) { assembly { success_ := call( _gas, // gas _target, // recipient _value, // ether value 0, // inloc 0, // inlen 0, // outloc 0 // outlen ) } } /// @notice Perform a low level call with all gas without copying any returndata /// @param _target Address to call /// @param _value Amount of value to pass to the call function send(address _target, uint256 _value) internal returns (bool success_) { success_ = send(_target, gasleft(), _value); } /// @notice Perform a low level call without copying any returndata /// @param _target Address to call /// @param _gas Amount of gas to pass to the call /// @param _value Amount of value to pass to the call /// @param _calldata Calldata to pass to the call function call( address _target, uint256 _gas, uint256 _value, bytes memory _calldata ) internal returns (bool success_) { assembly { success_ := call( _gas, // gas _target, // recipient _value, // ether value add(_calldata, 32), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) } } /// @notice Perform a low level call without copying any returndata /// @param _target Address to call /// @param _value Amount of value to pass to the call /// @param _calldata Calldata to pass to the call function call(address _target, uint256 _value, bytes memory _calldata) internal returns (bool success_) { success_ = call({ _target: _target, _gas: gasleft(), _value: _value, _calldata: _calldata }); } /// @notice Perform a low level call without copying any returndata /// @param _target Address to call /// @param _calldata Calldata to pass to the call function call(address _target, bytes memory _calldata) internal returns (bool success_) { success_ = call({ _target: _target, _gas: gasleft(), _value: 0, _calldata: _calldata }); } /// @notice Helper function to determine if there is sufficient gas remaining within the context /// to guarantee that the minimum gas requirement for a call will be met as well as /// optionally reserving a specified amount of gas for after the call has concluded. /// @param _minGas The minimum amount of gas that may be passed to the target context. /// @param _reservedGas Optional amount of gas to reserve for the caller after the execution /// of the target context. /// @return `true` if there is enough gas remaining to safely supply `_minGas` to the target /// context as well as reserve `_reservedGas` for the caller after the execution of /// the target context. /// @dev !!!!! FOOTGUN ALERT !!!!! /// 1.) The 40_000 base buffer is to account for the worst case of the dynamic cost of the /// `CALL` opcode's `address_access_cost`, `positive_value_cost`, and /// `value_to_empty_account_cost` factors with an added buffer of 5,700 gas. It is /// still possible to self-rekt by initiating a withdrawal with a minimum gas limit /// that does not account for the `memory_expansion_cost` & `code_execution_cost` /// factors of the dynamic cost of the `CALL` opcode. /// 2.) This function should *directly* precede the external call if possible. There is an /// added buffer to account for gas consumed between this check and the call, but it /// is only 5,700 gas. /// 3.) Because EIP-150 ensures that a maximum of 63/64ths of the remaining gas in the call /// frame may be passed to a subcontext, we need to ensure that the gas will not be /// truncated. /// 4.) Use wisely. This function is not a silver bullet. function hasMinGas(uint256 _minGas, uint256 _reservedGas) internal view returns (bool) { bool _hasMinGas; assembly { // Equation: gas × 63 ≥ minGas × 64 + 63(40_000 + reservedGas) _hasMinGas := iszero(lt(mul(gas(), 63), add(mul(_minGas, 64), mul(add(40000, _reservedGas), 63)))) } return _hasMinGas; } /// @notice Perform a low level call without copying any returndata. This function /// will revert if the call cannot be performed with the specified minimum /// gas. /// @param _target Address to call /// @param _minGas The minimum amount of gas that may be passed to the call /// @param _value Amount of value to pass to the call /// @param _calldata Calldata to pass to the call function callWithMinGas( address _target, uint256 _minGas, uint256 _value, bytes memory _calldata ) internal returns (bool) { bool _success; bool _hasMinGas = hasMinGas(_minGas, 0); assembly { // Assertion: gasleft() >= (_minGas * 64) / 63 + 40_000 if iszero(_hasMinGas) { // Store the "Error(string)" selector in scratch space. mstore(0, 0x08c379a0) // Store the pointer to the string length in scratch space. mstore(32, 32) // Store the string. // // SAFETY: // - We pad the beginning of the string with two zero bytes as well as the // length (24) to ensure that we override the free memory pointer at offset // 0x40. This is necessary because the free memory pointer is likely to // be greater than 1 byte when this function is called, but it is incredibly // unlikely that it will be greater than 3 bytes. As for the data within // 0x60, it is ensured that it is 0 due to 0x60 being the zero offset. // - It's fine to clobber the free memory pointer, we're reverting. mstore(88, 0x0000185361666543616c6c3a204e6f7420656e6f75676820676173) // Revert with 'Error("SafeCall: Not enough gas")' revert(28, 100) } // The call will be supplied at least ((_minGas * 64) / 63) gas due to the // above assertion. This ensures that, in all circumstances (except for when the // `_minGas` does not account for the `memory_expansion_cost` and `code_execution_cost` // factors of the dynamic cost of the `CALL` opcode), the call will receive at least // the minimum amount of gas specified. _success := call( gas(), // gas _target, // recipient _value, // ether value add(_calldata, 32), // inloc mload(_calldata), // inlen 0x00, // outloc 0x00 // outlen ) } return _success; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title EOA /// @notice A library for detecting if an address is an EOA. library EOA { /// @notice Returns true if sender address is an EOA. /// @return isEOA_ True if the sender address is an EOA. function isSenderEOA() internal view returns (bool isEOA_) { if (msg.sender == tx.origin) { isEOA_ = true; } else if (address(msg.sender).code.length == 23) { // If the sender is not the origin, check for 7702 delegated EOAs. assembly { let ptr := mload(0x40) mstore(0x40, add(ptr, 0x20)) extcodecopy(caller(), ptr, 0, 0x20) isEOA_ := eq(shr(232, mload(ptr)), 0xEF0100) } } else { // If more or less than 23 bytes of code, not a 7702 delegated EOA. isEOA_ = false; } } } // 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: MIT pragma solidity ^0.8.0; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /// @title IOptimismMintableERC20 /// @notice This interface is available on the OptimismMintableERC20 contract. /// We declare it as a separate interface so that it can be used in /// custom implementations of OptimismMintableERC20. interface IOptimismMintableERC20 is IERC165 { function remoteToken() external view returns (address); function bridge() external returns (address); function mint(address _to, uint256 _amount) external; function burn(address _from, uint256 _amount) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /// @custom:legacy /// @title ILegacyMintableERC20 /// @notice This interface was available on the legacy L2StandardERC20 contract. /// It remains available on the OptimismMintableERC20 contract for /// backwards compatibility. interface ILegacyMintableERC20 is IERC165 { function l1Token() external view returns (address); function mint(address _to, uint256 _amount) external; function burn(address _from, uint256 _amount) external; } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.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 functionCall(target, data, "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"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(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) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(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) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason 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 { // 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 v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // 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); }