Spend less on fees, more on crypto. Buy crypto easily with MoonPay Balance. 20M+ users trust MoonPay worldwide.
Ready to onboard to Ethereum? With MetaMask Portfolio, you're in control.
Don’t invest unless you’re prepared to lose all the money you invest.
Ready to simplify your web3 experience? Try the all-in-one web3 app trusted by millions worldwide.
Available on 9 networks: Ethereum mainnet, Linea, Polygon, Optimism, BNB Chain, zkSync Era, Base, Avalanche.
Everyday giveaways up to 100 ETH, Lucky Spins. Deposit BONUS 300% and Cashbacks!
5000+ Slots & Live Casino Games, 50+cryptos. Register with Etherscan and get 760% deposit bonus. Win Big$, withdraw it fast.
Slots, Roulette, Poker & more - Proud sponsors of UFC, Everton & StakeF1 team!
5000+ Slots & Live Casino Games, 50+cryptos. Register with Etherscan and get 760% deposit bonus. Win Big$, withdraw it fast.
Anonymous play on awesome games - sign up now for 25 free jackpot spins - worth $100s!
100s of games, generous bonuses, 20+ years of trusted gaming. Join CryptoWins & start winning today!
Overview
ETH Balance
Eth Value
$2,991.01 (@ $3,300.22/ETH)Token Holdings
Could not find any matches!
- ERC-20 Tokens (98)6 APEApeCoin (APE)$6.90@1.1512,303.3521881 BALBalancer (BAL)$30,881.41@2.5115,737.47819202 LINKChainLink To... (LINK)$354,093.26@22.500.14336662 cbETHCoinbase Wra... (cbETH)$511.49@3,567.71311,668.0836382 FRAXFrax (FRAX)$309,888.77@0.9943130.75700113 frxETHFrax Ether (frxETH)$429,118.96@3,281.80485,486.45428816 FXSFrax Share (FXS)$17,556.65@3.201,000 MTVMultiVAC (MTV)$0.76@0.000816,906,725.386064 ORBSOrbs (ORBS)$458,944.22@0.027110,996,185,716.4629 PEPEPepe (PEPE)$195,402.22@0.0054.28558908 POLSPolkastarter... (POLS)$22.81@0.42025,951.74509783 RAIRai Reflex I... (RAI)$17,736.20@2.9850.52096248 YieldETHReal Yield E... (YieldE...)$182,409.47@3,610.573.34146404 rETHRocket Pool ... (rETH)$12,320.08@3,687.033,811,817,120.28229 SHIBSHIBA INU (SHIB)$81,687.24@0.00868.79386351 sFRAXStaked FRAX (sFRAX)$946.99@1.0920.59076493 sfrxETHStaked Frax ... (sfrxET...)$75,464.50@3,664.96840.0451083 stETHstETH (stETH)$148.31@3,287.97406.6977164 UNIUniswap (UNI)$5,596.16@13.760.76 AAVEAave Token (AAVE)$246.46@324.291,491,504.44901987 BUSDBUSD (BUSD)$1,487,058.27@0.997732,039.18816805 DAIDai Stableco... (DAI)$731,382.55@0.99914,550,035.22276 EURCEURC (EURC)$4,736,586.67@1.04179,716.40333571 KNCKyber Networ... (KNC)$42,504.87@0.53324.07252132 MKRMaker (MKR)$6,370.36@1,564.232,997,327.945884 USDTTether USD (USDT)$2,991,258.36@0.99832,967,344.090102 USDCUSDC (USDC)$32,932,036.06@0.9989299.83054262 WBTCWrapped BTC (WBTC)$28,539,969.86@95,187.006,911.25755026 WETHWrapped Ethe... (WETH)$22,808,654.47@3,300.21771,373.03271622 wstETHWrapped liqu... (wstETH)$5,365,097.88@3,907.48750 $stLink rewards on stlink.fiERC-20: ! st... ($stLin...)4,000 Earn $UNI airdrops at https://www.uniswaplabs.comERC-20: # un... (Earn $...)7,500 $ Evmosia.comERC-20: $ E... ($ Evm...)5,000 $ rEthLP.comERC-20: $ rE... ($ rEth...)48,000 Earn $TUSD airdrops at https://www.tenorusd.orgERC-20: $ te... (Earn $...)4.75ERC20 ***2.47301434 BFUKUERC-20: Baby... (BFUKU)1,356.38951554 BGBGBigMouthFrog50.00292838 BlueSparrowBlueSparrowToken$1.24@0.0248247,339,202.9321 CHKNChickencoin$17.93@0.0018.2325072 COLONColon$0.01@0.000469.42 EVILDoctor Evil$0.00@0.000132.75729711 DOGERC-20: Dog (DOG)0 FPIFrax Price Index$0.00@1.12100,000,000 GNCATERC-20: GanN... (GNCAT)28,888 GUAGUA1,000 NeiroERC-20: Neir... (Neiro)707,090 PSYOPToken.netERC-20: PSYO... (PSYOPT...)1 YieldUSDERC-20: Real... (YieldU...)5.334 REKTERC-20: Rekt... (REKT)1,000,000,000,000 Pepe Reward TokenERC-20: rPep... (Pepe R...)7.39469374 SIMPERC-20: Simp... (SIMP)12,269 SQUID2.0Squid Game 2.010 WDOGEWrapped DOGE$3.13@0.31272,663,740.0463059 WMAIERC-20: Wrap... (WMAI)42,456,122.11 YUMYum$662,442.45@0.01560.155555 HQGERC-20: 环球股 (HQG)1 APUApu Apustaja$0.00@0.00060.5 CRICrypto International338,479.44785677 OXOpen Exchange Token$1,371.17@0.00411.2 TokenERC-20 TOKEN*[Suspicious]200 TokenERC-20 TOKEN*[Suspicious]2,382.23 TokenERC-20 TOKEN*[Suspicious]3,999.99 TokenERC-20 TOKEN*[Suspicious]525,235,396 TokenERC-20 TOKEN*[Suspicious]1.7 TokenERC-20 TOKEN*[Suspicious]1.5 TokenERC-20 TOKEN*[Suspicious]8,271 TokenERC-20 TOKEN*[Suspicious]3,999.99 TokenERC-20 TOKEN*[Suspicious]0.7 TokenERC-20 TOKEN*[Suspicious]9,000 TokenERC-20 TOKEN*[Suspicious]245.44 TokenERC-20 TOKEN*[Suspicious]9,283 TokenERC-20 TOKEN*[Suspicious]5,000 TokenERC-20 TOKEN*[Suspicious]5,000 TokenERC-20 TOKEN*[Suspicious]400 TokenERC-20 TOKEN*[Suspicious]407 TokenERC-20 TOKEN*[Suspicious]100,000 TokenERC-20 TOKEN*[Suspicious]4,214 TokenERC-20 TOKEN*[Suspicious]7,000 TokenERC-20 TOKEN*[Suspicious]7,000 TokenERC-20 TOKEN*[Suspicious]98,127 TokenERC-20 TOKEN*[Suspicious]350 TokenERC-20 TOKEN*[Suspicious]700 TokenERC-20 TOKEN*[Unsafe]7,000 TokenERC-20 TOKEN*[Unsafe]7,000 TokenERC-20 TOKEN*[Unsafe]9,283 TokenERC-20 TOKEN*[Spam]132.84 TokenERC-20 TOKEN*[Spam]75,539,184.44 TokenERC-20 TOKEN*[Spam]100 TokenERC-20 TOKEN*[Spam]6,000 TokenERC-20 TOKEN*[Spam]9,283 TokenERC-20 TOKEN*[Spam]12.5 TokenERC-20 TOKEN*[Spam]7,000 TokenERC-20 TOKEN*[Spam]50,000 TokenERC-20 TOKEN*[Spam]300 TokenERC-20 TOKEN*[Spam]5,555 TokenERC-20 TOKEN*[Spam]50,000 TokenERC-20 TOKEN*[Spam]NFT Tokens (78)zsteth.com : airdrop recipient# zsteth.com : airdrop recipientERC-1155pepenewyear.com'PEPE Mysterybox NFTERC-1155claim rewards on apylink.comapylink.comERC-1155claim rewards on apyusd.netapyusd.netERC-1155nft-dai.comDAI Mysterybox NFTERC-1155ether-origin.comether-origin.comERC-1155EventERC-1155Official Authorize credentialFRAX: Airdrop NFT VoucherERC-1155claim rewards on fraxprotocol.comfraxprotocol.comERC-1155claim rewards on get-clink.netget-clink.netERC-1155claim rewards on getaave.orggetaave.orgERC-1155LidowstETH.comLidowstETH.comERC-1155claim rewards on pepetoken.netpepetoken.netERC-1155claim rewards on pooledeth.netpooledeth.netERC-1155Puffer Mystery Box NFT pufether.orgPuffer Mystery Box NFT pufether.orgERC-1155Reward Club [QEfkfA1G]Reward Club [w7cIVAag]ERC-1155Official Authorize credentialRocket Pool: Airdrop NFT VoucherERC-1155claim rewards on stakedeth.iostakedeth.ioERC-1155The Nft Mint Club OfficialsThe Nft Mint Club OfficialsERC-1155claim rewards on univ3portal.netuniv3portal.netERC-1155claim rewards on univ4labs.orguniv4labs.orgERC-1155NWBTCwbtc-nft.org or OS/collection/nwbtc to Redeem and Swap your NFT for WBTCERC-1155claim rewards on wbtcnetwork.netwbtcnetwork.netERC-1155claim rewards on wsteth.netwsteth.netERC-1155Let's Go!🎁ERC-1155ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]ERC-1155 TOKEN*[Spam]
More Info
Private Name Tags
ContractCreator
Multichain Info
3 addresses found via- Transactions
- Internal Transactions
- Token Transfers (ERC-20)
- NFT Transfers
- Contract
- Events
- Analytics
- Multichain Portfolio
- Cards New
Advanced Filter- Filter by Tx Type:
- Tx
- Internal Tx
- ERC-20
- NFTs
Latest 25 from a total of 82,732 transactions
Transaction Hash MethodBlockFromToExecute 21463825 2024-12-23 7:54:59 6 mins ago 1734940499 IN 0 ETH$0.00 0.00181124 4.49192457 Execute 21463754 2024-12-23 7:39:59 21 mins ago 1734939599 IN 0 ETH$0.00 0.00248736 5.31593116 Execute 21463658 2024-12-23 7:20:11 41 mins ago 1734938411 IN 0 ETH$0.00 0.00237988 4.99989469 Execute 21463640 2024-12-23 7:16:35 44 mins ago 1734938195 IN 0 ETH$0.00 0.00187165 4.63971992 Execute 21463633 2024-12-23 7:15:11 46 mins ago 1734938111 IN 0 ETH$0.00 0.00277895 5.13530221 Execute 21463608 2024-12-23 7:10:11 51 mins ago 1734937811 IN 0 ETH$0.00 0.00243557 5.98172179 Execute 21463584 2024-12-23 7:05:23 56 mins ago 1734937523 IN 0 ETH$0.00 0.00333857 5.02743156 Execute 21463580 2024-12-23 7:04:35 56 mins ago 1734937475 IN 0 ETH$0.00 0.00286752 4.78879369 Execute 21463574 2024-12-23 7:03:23 58 mins ago 1734937403 IN 0 ETH$0.00 0.0030676 5.04511622 Execute 21463569 2024-12-23 7:02:23 1 hrs ago 1734937343 IN 0 ETH$0.00 0.00242314 5.1707688 Execute 21463563 2024-12-23 7:01:11 1 hr ago 1734937271 IN 0 ETH$0.00 0.00201207 4.98898819 Execute 21463557 2024-12-23 6:59:59 1 hr ago 1734937199 IN 0 ETH$0.00 0.00207191 5.13966513 Execute 21463460 2024-12-23 6:40:35 1 hr ago 1734936035 IN 0 ETH$0.00 0.00229385 5.68862308 Execute 21463450 2024-12-23 6:38:35 1 hr ago 1734935915 IN 0 ETH$0.00 0.00281337 5.23691642 Execute 21463436 2024-12-23 6:35:47 1 hr ago 1734935747 IN 0 ETH$0.00 0.00210402 5.21704524 Execute 21463431 2024-12-23 6:34:47 1 hr ago 1734935687 IN 0 ETH$0.00 0.00221769 4.73312645 Execute 21463427 2024-12-23 6:33:59 1 hr ago 1734935639 IN 0 ETH$0.00 0.00311765 5.20515175 Execute 21463422 2024-12-23 6:32:59 1 hr ago 1734935579 IN 0 ETH$0.00 0.0022478 4.79768083 Execute 21463413 2024-12-23 6:31:11 1 hr ago 1734935471 IN 0 ETH$0.00 0.00181574 4.50304181 Execute 21463407 2024-12-23 6:29:59 1 hr ago 1734935399 IN 0 ETH$0.00 0.00254587 4.77142148 Execute 21463359 2024-12-23 6:20:11 1 hr ago 1734934811 IN 0 ETH$0.00 0.00152933 3.75764621 Execute 21463334 2024-12-23 6:15:11 1 hr ago 1734934511 IN 0 ETH$0.00 0.00253542 5.4180577 Execute 21463308 2024-12-23 6:09:59 1 hr ago 1734934199 IN 0 ETH$0.00 0.00215796 4.57219929 Execute 21463283 2024-12-23 6:04:59 1 hr ago 1734933899 IN 0 ETH$0.00 0.00187748 4.60956654 Execute 21463234 2024-12-23 5:55:11 2 hrs ago 1734933311 IN 0 ETH$0.00 0.00191782 4.75704813 Latest 25 internal transactions (View All)
Advanced mode:Parent Transaction Hash Block FromTo21448506 2024-12-21 4:29:59 2 days ago 1734755399 0 ETH$0.00 21448506 2024-12-21 4:29:59 2 days ago 1734755399 Contract Creation 0 ETH$0.00 21446727 2024-12-20 22:30:11 2 days ago 1734733811 0 ETH$0.00 21446727 2024-12-20 22:30:11 2 days ago 1734733811 Contract Creation 0 ETH$0.00 21446727 2024-12-20 22:30:11 2 days ago 1734733811 0 ETH$0.00 21446727 2024-12-20 22:30:11 2 days ago 1734733811 Contract Creation 0 ETH$0.00 21444931 2024-12-20 16:29:59 2 days ago 1734712199 0 ETH$0.00 21444931 2024-12-20 16:29:59 2 days ago 1734712199 Contract Creation 0 ETH$0.00 21432406 2024-12-18 22:30:11 4 days ago 1734561011 0 ETH$0.00 21432406 2024-12-18 22:30:11 4 days ago 1734561011 Contract Creation 0 ETH$0.00 21428820 2024-12-18 10:28:23 4 days ago 1734517703 0 ETH$0.00 21428820 2024-12-18 10:28:23 4 days ago 1734517703 Contract Creation 0 ETH$0.00 21425241 2024-12-17 22:28:11 5 days ago 1734474491 0 ETH$0.00 21425241 2024-12-17 22:28:11 5 days ago 1734474491 Contract Creation 0 ETH$0.00 21418071 2024-12-16 22:28:11 6 days ago 1734388091 0 ETH$0.00 21418071 2024-12-16 22:28:11 6 days ago 1734388091 Contract Creation 0 ETH$0.00 21418071 2024-12-16 22:28:11 6 days ago 1734388091 0 ETH$0.00 21418071 2024-12-16 22:28:11 6 days ago 1734388091 Contract Creation 0 ETH$0.00 21416275 2024-12-16 16:28:23 6 days ago 1734366503 0 ETH$0.00 21416275 2024-12-16 16:28:23 6 days ago 1734366503 Contract Creation 0 ETH$0.00 21409110 2024-12-15 16:28:11 7 days ago 1734280091 0 ETH$0.00 21409110 2024-12-15 16:28:11 7 days ago 1734280091 Contract Creation 0 ETH$0.00 21405533 2024-12-15 4:28:11 8 days ago 1734236891 0 ETH$0.00 21405533 2024-12-15 4:28:11 8 days ago 1734236891 Contract Creation 0 ETH$0.00 21396573 2024-12-13 22:28:11 9 days ago 1734128891 0 ETH$0.00 Loading...LoadingContract Name:AxelarGatewayProxyMultisig
Compiler Versionv0.8.9+commit.e5eed63a
Contract Source Code (Solidity Multiple files format)
// Dependency file: src/interfaces/IAxelarGateway.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; interface IAxelarGateway { /**********\ |* Events *| \**********/ event Executed(bytes32 indexed commandId); event TokenDeployed(string symbol, address tokenAddresses); event TokenFrozen(string indexed symbol); event TokenUnfrozen(string indexed symbol); event AllTokensFrozen(); event AllTokensUnfrozen(); event AccountBlacklisted(address indexed account); event AccountWhitelisted(address indexed account); event Upgraded(address indexed implementation); /***********\ |* Getters *| \***********/ function allTokensFrozen() external view returns (bool); function implementation() external view returns (address); function tokenAddresses(string memory symbol) external view returns (address); function tokenFrozen(string memory symbol) external view returns (bool); function isCommandExecuted(bytes32 commandId) external view returns (bool); /*******************\ |* Admin Functions *| \*******************/ function freezeToken(string memory symbol) external; function unfreezeToken(string memory symbol) external; function freezeAllTokens() external; function unfreezeAllTokens() external; function upgrade(address newImplementation, bytes calldata setupParams) external; /**********************\ |* External Functions *| \**********************/ function setup(bytes calldata params) external; function execute(bytes calldata input) external; } // Dependency file: src/EternalStorage.sol // pragma solidity >=0.8.0 <0.9.0; /** * @title EternalStorage * @dev This contract holds all the necessary state variables to carry out the storage of any contract. */ contract EternalStorage { mapping(bytes32 => uint256) private _uintStorage; mapping(bytes32 => string) private _stringStorage; mapping(bytes32 => address) private _addressStorage; mapping(bytes32 => bytes) private _bytesStorage; mapping(bytes32 => bool) private _boolStorage; mapping(bytes32 => int256) private _intStorage; // *** Getter Methods *** function getUint(bytes32 key) public view returns (uint256) { return _uintStorage[key]; } function getString(bytes32 key) public view returns (string memory) { return _stringStorage[key]; } function getAddress(bytes32 key) public view returns (address) { return _addressStorage[key]; } function getBytes(bytes32 key) public view returns (bytes memory) { return _bytesStorage[key]; } function getBool(bytes32 key) public view returns (bool) { return _boolStorage[key]; } function getInt(bytes32 key) public view returns (int256) { return _intStorage[key]; } // *** Setter Methods *** function _setUint(bytes32 key, uint256 value) internal { _uintStorage[key] = value; } function _setString(bytes32 key, string memory value) internal { _stringStorage[key] = value; } function _setAddress(bytes32 key, address value) internal { _addressStorage[key] = value; } function _setBytes(bytes32 key, bytes memory value) internal { _bytesStorage[key] = value; } function _setBool(bytes32 key, bool value) internal { _boolStorage[key] = value; } function _setInt(bytes32 key, int256 value) internal { _intStorage[key] = value; } // *** Delete Methods *** function _deleteUint(bytes32 key) internal { delete _uintStorage[key]; } function _deleteString(bytes32 key) internal { delete _stringStorage[key]; } function _deleteAddress(bytes32 key) internal { delete _addressStorage[key]; } function _deleteBytes(bytes32 key) internal { delete _bytesStorage[key]; } function _deleteBool(bytes32 key) internal { delete _boolStorage[key]; } function _deleteInt(bytes32 key) internal { delete _intStorage[key]; } } // Dependency file: src/AxelarGatewayProxy.sol // pragma solidity >=0.8.0 <0.9.0; // import { EternalStorage } from 'src/EternalStorage.sol'; contract AxelarGatewayProxy is EternalStorage { /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`. bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc); fallback() external payable { address implementation = getAddress(KEY_IMPLEMENTATION); assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } receive() external payable { revert('NO_ETHER'); } } // Dependency file: src/interfaces/IAxelarGatewayMultisig.sol // pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGateway } from 'src/interfaces/IAxelarGateway.sol'; interface IAxelarGatewayMultisig is IAxelarGateway { event OwnershipTransferred(address[] preOwners, uint256 prevThreshold, address[] newOwners, uint256 newThreshold); event OperatorshipTransferred(address[] preOperators, uint256 prevThreshold, address[] newOperators, uint256 newThreshold); function owners() external view returns (address[] memory); function operators() external view returns (address[] memory); } // Dependency file: src/ECDSA.sol // pragma solidity >=0.8.0 <0.9.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address signer) { // Check the signature length require(signature.length == 65, 'INV_LEN'); // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, 'INV_S'); require(v == 27 || v == 28, 'INV_V'); // If the signature is valid (and not malleable), return the signer address require((signer = ecrecover(hash, v, r, s)) != address(0), 'INV_SIG'); } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked('\x19Ethereum Signed Message:\n32', hash)); } } // Dependency file: src/interfaces/IERC20.sol // pragma solidity >=0.8.0 <0.9.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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 `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @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); } // Dependency file: src/Context.sol // pragma solidity >=0.8.0 <0.9.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 GSN 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 payable) { return payable(msg.sender); } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // Dependency file: src/ERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { IERC20 } from 'src/interfaces/IERC20.sol'; // import { Context } from 'src/Context.sol'; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { mapping(address => uint256) public override balanceOf; mapping(address => mapping(address => uint256)) public override allowance; uint256 public override totalSupply; string public name; string public symbol; uint8 public immutable decimals; /** * @dev Sets the values for {name}, {symbol}, and {decimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { name = name_; symbol = symbol_; decimals = decimals_; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), allowance[sender][_msgSender()] - amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] - subtractedValue); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), 'ZERO_ADDR'); require(recipient != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(sender, recipient, amount); balanceOf[sender] -= amount; balanceOf[recipient] += amount; emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(address(0), account, amount); totalSupply += amount; balanceOf[account] += amount; emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(account, address(0), amount); balanceOf[account] -= amount; totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), 'ZERO_ADDR'); require(spender != address(0), 'ZERO_ADDR'); allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } // Dependency file: src/Ownable.sol // pragma solidity >=0.8.0 <0.9.0; abstract contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } modifier onlyOwner() { require(owner == msg.sender, 'NOT_OWNER'); _; } function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), 'ZERO_ADDR'); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } } // Dependency file: src/Burner.sol // pragma solidity >=0.8.0 <0.9.0; // import { BurnableMintableCappedERC20 } from 'src/BurnableMintableCappedERC20.sol'; contract Burner { constructor(address tokenAddress, bytes32 salt) { BurnableMintableCappedERC20(tokenAddress).burn(salt); selfdestruct(payable(address(0))); } } // Dependency file: src/BurnableMintableCappedERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { ERC20 } from 'src/ERC20.sol'; // import { Ownable } from 'src/Ownable.sol'; // import { Burner } from 'src/Burner.sol'; // import { EternalStorage } from 'src/EternalStorage.sol'; contract BurnableMintableCappedERC20 is ERC20, Ownable { uint256 public cap; bytes32 private constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen'); bytes32 private constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen'); event Frozen(address indexed owner); event Unfrozen(address indexed owner); constructor( string memory name, string memory symbol, uint8 decimals, uint256 capacity ) ERC20(name, symbol, decimals) Ownable() { cap = capacity; } function depositAddress(bytes32 salt) public view returns (address) { // This would be easier, cheaper, simpler, and result in globally consistent deposit addresses for any salt (all chains, all tokens). // return address(uint160(uint256(keccak256(abi.encodePacked(bytes32(0x000000000000000000000000000000000000000000000000000000000000dead), salt))))); /* Convert a hash which is bytes32 to an address which is 20-byte long according to https://docs.soliditylang.org/en/v0.8.1/control-structures.html?highlight=create2#salted-contract-creations-create2 */ return address( uint160( uint256( keccak256( abi.encodePacked( bytes1(0xff), owner, salt, keccak256(abi.encodePacked(type(Burner).creationCode, abi.encode(address(this)), salt)) ) ) ) ) ); } function mint(address account, uint256 amount) public onlyOwner { uint256 capacity = cap; require(capacity == 0 || totalSupply + amount <= capacity, 'CAP_EXCEEDED'); _mint(account, amount); } function burn(bytes32 salt) public onlyOwner { address account = depositAddress(salt); _burn(account, balanceOf[account]); } function _beforeTokenTransfer( address, address, uint256 ) internal view override { require(!EternalStorage(owner).getBool(KEY_ALL_TOKENS_FROZEN), 'IS_FROZEN'); require(!EternalStorage(owner).getBool(keccak256(abi.encodePacked(PREFIX_TOKEN_FROZEN, symbol))), 'IS_FROZEN'); } } // Dependency file: src/AdminMultisigBase.sol // pragma solidity >=0.8.0 <0.9.0; // import { EternalStorage } from 'src/EternalStorage.sol'; contract AdminMultisigBase is EternalStorage { // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_ADMIN_EPOCH = keccak256('admin-epoch'); bytes32 internal constant PREFIX_ADMIN = keccak256('admin'); bytes32 internal constant PREFIX_ADMIN_COUNT = keccak256('admin-count'); bytes32 internal constant PREFIX_ADMIN_THRESHOLD = keccak256('admin-threshold'); bytes32 internal constant PREFIX_ADMIN_VOTE_COUNTS = keccak256('admin-vote-counts'); bytes32 internal constant PREFIX_ADMIN_VOTED = keccak256('admin-voted'); bytes32 internal constant PREFIX_IS_ADMIN = keccak256('is-admin'); modifier onlyAdmin() { uint256 adminEpoch = _adminEpoch(); require(_isAdmin(adminEpoch, msg.sender), 'NOT_ADMIN'); bytes32 topic = keccak256(msg.data); // Check that admin has not voted, then record that they have voted. require(!_hasVoted(adminEpoch, topic, msg.sender), 'VOTED'); _setHasVoted(adminEpoch, topic, msg.sender, true); // Determine the new vote count and update it. uint256 adminVoteCount = _getVoteCount(adminEpoch, topic) + uint256(1); _setVoteCount(adminEpoch, topic, adminVoteCount); // Do not proceed with operation execution if insufficient votes. if (adminVoteCount < _getAdminThreshold(adminEpoch)) return; _; // Clear vote count and voted booleans. _setVoteCount(adminEpoch, topic, uint256(0)); uint256 adminCount = _getAdminCount(adminEpoch); for (uint256 i; i < adminCount; i++) { _setHasVoted(adminEpoch, topic, _getAdmin(adminEpoch, i), false); } } /********************\ |* Pure Key Getters *| \********************/ function _getAdminKey(uint256 adminEpoch, uint256 index) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN, adminEpoch, index)); } function _getAdminCountKey(uint256 adminEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_COUNT, adminEpoch)); } function _getAdminThresholdKey(uint256 adminEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_THRESHOLD, adminEpoch)); } function _getAdminVoteCountsKey(uint256 adminEpoch, bytes32 topic) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_VOTE_COUNTS, adminEpoch, topic)); } function _getAdminVotedKey( uint256 adminEpoch, bytes32 topic, address account ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_VOTED, adminEpoch, topic, account)); } function _getIsAdminKey(uint256 adminEpoch, address account) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_IS_ADMIN, adminEpoch, account)); } /***********\ |* Getters *| \***********/ function _adminEpoch() internal view returns (uint256) { return getUint(KEY_ADMIN_EPOCH); } function _getAdmin(uint256 adminEpoch, uint256 index) internal view returns (address) { return getAddress(_getAdminKey(adminEpoch, index)); } function _getAdminCount(uint256 adminEpoch) internal view returns (uint256) { return getUint(_getAdminCountKey(adminEpoch)); } function _getAdminThreshold(uint256 adminEpoch) internal view returns (uint256) { return getUint(_getAdminThresholdKey(adminEpoch)); } function _getVoteCount(uint256 adminEpoch, bytes32 topic) internal view returns (uint256) { return getUint(_getAdminVoteCountsKey(adminEpoch, topic)); } function _hasVoted( uint256 adminEpoch, bytes32 topic, address account ) internal view returns (bool) { return getBool(_getAdminVotedKey(adminEpoch, topic, account)); } function _isAdmin(uint256 adminEpoch, address account) internal view returns (bool) { return getBool(_getIsAdminKey(adminEpoch, account)); } /***********\ |* Setters *| \***********/ function _setAdminEpoch(uint256 adminEpoch) internal { _setUint(KEY_ADMIN_EPOCH, adminEpoch); } function _setAdmin( uint256 adminEpoch, uint256 index, address account ) internal { _setAddress(_getAdminKey(adminEpoch, index), account); } function _setAdminCount(uint256 adminEpoch, uint256 adminCount) internal { _setUint(_getAdminCountKey(adminEpoch), adminCount); } function _setAdmins( uint256 adminEpoch, address[] memory accounts, uint256 threshold ) internal { uint256 adminLength = accounts.length; require(adminLength >= threshold, 'INV_ADMINS'); require(threshold > uint256(0), 'INV_ADMIN_THLD'); _setAdminThreshold(adminEpoch, threshold); _setAdminCount(adminEpoch, adminLength); for (uint256 i; i < adminLength; i++) { address account = accounts[i]; // Check that the account wasn't already set as an admin for this epoch. require(!_isAdmin(adminEpoch, account), 'DUP_ADMIN'); // Set this account as the i-th admin in this epoch (needed to we can clear topic votes in `onlyAdmin`). _setAdmin(adminEpoch, i, account); _setIsAdmin(adminEpoch, account, true); } } function _setAdminThreshold(uint256 adminEpoch, uint256 adminThreshold) internal { _setUint(_getAdminThresholdKey(adminEpoch), adminThreshold); } function _setVoteCount( uint256 adminEpoch, bytes32 topic, uint256 voteCount ) internal { _setUint(_getAdminVoteCountsKey(adminEpoch, topic), voteCount); } function _setHasVoted( uint256 adminEpoch, bytes32 topic, address account, bool voted ) internal { _setBool(_getAdminVotedKey(adminEpoch, topic, account), voted); } function _setIsAdmin( uint256 adminEpoch, address account, bool isAdmin ) internal { _setBool(_getIsAdminKey(adminEpoch, account), isAdmin); } } // Dependency file: src/AxelarGateway.sol // pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGateway } from 'src/interfaces/IAxelarGateway.sol'; // import { BurnableMintableCappedERC20 } from 'src/BurnableMintableCappedERC20.sol'; // import { AdminMultisigBase } from 'src/AdminMultisigBase.sol'; abstract contract AxelarGateway is IAxelarGateway, AdminMultisigBase { /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`. bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc); // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen'); bytes32 internal constant PREFIX_COMMAND_EXECUTED = keccak256('command-executed'); bytes32 internal constant PREFIX_TOKEN_ADDRESS = keccak256('token-address'); bytes32 internal constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen'); bytes32 internal constant SELECTOR_BURN_TOKEN = keccak256('burnToken'); bytes32 internal constant SELECTOR_DEPLOY_TOKEN = keccak256('deployToken'); bytes32 internal constant SELECTOR_MINT_TOKEN = keccak256('mintToken'); bytes32 internal constant SELECTOR_TRANSFER_OPERATORSHIP = keccak256('transferOperatorship'); bytes32 internal constant SELECTOR_TRANSFER_OWNERSHIP = keccak256('transferOwnership'); uint8 internal constant OLD_KEY_RETENTION = 16; modifier onlySelf() { require(msg.sender == address(this), 'NOT_SELF'); _; } /***********\ |* Getters *| \***********/ function allTokensFrozen() public view override returns (bool) { return getBool(KEY_ALL_TOKENS_FROZEN); } function implementation() public view override returns (address) { return getAddress(KEY_IMPLEMENTATION); } function tokenAddresses(string memory symbol) public view override returns (address) { return getAddress(_getTokenAddressKey(symbol)); } function tokenFrozen(string memory symbol) public view override returns (bool) { return getBool(_getFreezeTokenKey(symbol)); } function isCommandExecuted(bytes32 commandId) public view override returns (bool) { return getBool(_getIsCommandExecutedKey(commandId)); } /*******************\ |* Admin Functions *| \*******************/ function freezeToken(string memory symbol) external override onlyAdmin { _setBool(_getFreezeTokenKey(symbol), true); emit TokenFrozen(symbol); } function unfreezeToken(string memory symbol) external override onlyAdmin { _setBool(_getFreezeTokenKey(symbol), false); emit TokenUnfrozen(symbol); } function freezeAllTokens() external override onlyAdmin { _setBool(KEY_ALL_TOKENS_FROZEN, true); emit AllTokensFrozen(); } function unfreezeAllTokens() external override onlyAdmin { _setBool(KEY_ALL_TOKENS_FROZEN, false); emit AllTokensUnfrozen(); } function upgrade(address newImplementation, bytes calldata setupParams) external override onlyAdmin { emit Upgraded(newImplementation); // AUDIT: If `newImplementation.setup` performs `selfdestruct`, it will result in the loss of _this_ implementation (thereby losing the gateway) // if `upgrade` is entered within the context of _this_ implementation itself. (bool success, ) = newImplementation.delegatecall( abi.encodeWithSelector(IAxelarGateway.setup.selector, setupParams) ); require(success, 'SETUP_FAILED'); _setImplementation(newImplementation); } /**********************\ |* Internal Functions *| \**********************/ function _deployToken( string memory name, string memory symbol, uint8 decimals, uint256 cap ) internal { require(tokenAddresses(symbol) == address(0), 'TOKEN_EXIST'); bytes32 salt = keccak256(abi.encodePacked(symbol)); address token = address(new BurnableMintableCappedERC20{ salt: salt }(name, symbol, decimals, cap)); _setTokenAddress(symbol, token); emit TokenDeployed(symbol, token); } function _mintToken( string memory symbol, address account, uint256 amount ) internal { address tokenAddress = tokenAddresses(symbol); require(tokenAddress != address(0), 'TOKEN_NOT_EXIST'); BurnableMintableCappedERC20(tokenAddress).mint(account, amount); } function _burnToken(string memory symbol, bytes32 salt) internal { address tokenAddress = tokenAddresses(symbol); require(tokenAddress != address(0), 'TOKEN_NOT_EXIST'); BurnableMintableCappedERC20(tokenAddress).burn(salt); } /********************\ |* Pure Key Getters *| \********************/ function _getFreezeTokenKey(string memory symbol) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_TOKEN_FROZEN, symbol)); } function _getTokenAddressKey(string memory symbol) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_TOKEN_ADDRESS, symbol)); } function _getIsCommandExecutedKey(bytes32 commandId) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_COMMAND_EXECUTED, commandId)); } /********************\ |* Internal Getters *| \********************/ function _getChainID() internal view returns (uint256 id) { assembly { id := chainid() } } /********************\ |* Internal Setters *| \********************/ function _setTokenAddress(string memory symbol, address tokenAddr) internal { _setAddress(_getTokenAddressKey(symbol), tokenAddr); } function _setCommandExecuted(bytes32 commandId, bool executed) internal { _setBool(_getIsCommandExecutedKey(commandId), executed); } function _setImplementation(address newImplementation) internal { _setAddress(KEY_IMPLEMENTATION, newImplementation); } } // Dependency file: src/AxelarGatewayMultisig.sol // pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGatewayMultisig } from 'src/interfaces/IAxelarGatewayMultisig.sol'; // import { ECDSA } from 'src/ECDSA.sol'; // import { AxelarGateway } from 'src/AxelarGateway.sol'; contract AxelarGatewayMultisig is IAxelarGatewayMultisig, AxelarGateway { // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_OWNER_EPOCH = keccak256('owner-epoch'); bytes32 internal constant PREFIX_OWNER = keccak256('owner'); bytes32 internal constant PREFIX_OWNER_COUNT = keccak256('owner-count'); bytes32 internal constant PREFIX_OWNER_THRESHOLD = keccak256('owner-threshold'); bytes32 internal constant PREFIX_IS_OWNER = keccak256('is-owner'); bytes32 internal constant KEY_OPERATOR_EPOCH = keccak256('operator-epoch'); bytes32 internal constant PREFIX_OPERATOR = keccak256('operator'); bytes32 internal constant PREFIX_OPERATOR_COUNT = keccak256('operator-count'); bytes32 internal constant PREFIX_OPERATOR_THRESHOLD = keccak256('operator-threshold'); bytes32 internal constant PREFIX_IS_OPERATOR = keccak256('is-operator'); function _containsDuplicates(address[] memory accounts) internal pure returns (bool) { uint256 count = accounts.length; for (uint256 i; i < count; ++i) { for (uint256 j = i + 1; j < count; ++j) { if (accounts[i] == accounts[j]) return true; } } return false; } /************************\ |* Owners Functionality *| \************************/ /********************\ |* Pure Key Getters *| \********************/ function _getOwnerKey(uint256 ownerEpoch, uint256 index) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OWNER, ownerEpoch, index)); } function _getOwnerCountKey(uint256 ownerEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OWNER_COUNT, ownerEpoch)); } function _getOwnerThresholdKey(uint256 ownerEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OWNER_THRESHOLD, ownerEpoch)); } function _getIsOwnerKey(uint256 ownerEpoch, address account) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_IS_OWNER, ownerEpoch, account)); } /***********\ |* Getters *| \***********/ function _ownerEpoch() internal view returns (uint256) { return getUint(KEY_OWNER_EPOCH); } function _getOwner(uint256 ownerEpoch, uint256 index) internal view returns (address) { return getAddress(_getOwnerKey(ownerEpoch, index)); } function _getOwnerCount(uint256 ownerEpoch) internal view returns (uint256) { return getUint(_getOwnerCountKey(ownerEpoch)); } function _getOwnerThreshold(uint256 ownerEpoch) internal view returns (uint256) { return getUint(_getOwnerThresholdKey(ownerEpoch)); } function _isOwner(uint256 ownerEpoch, address account) internal view returns (bool) { return getBool(_getIsOwnerKey(ownerEpoch, account)); } /// @dev Returns true if a sufficient quantity of `accounts` are owners in the same `ownerEpoch`, within the last `OLD_KEY_RETENTION + 1` owner epochs. function _areValidRecentOwners(address[] memory accounts) internal view returns (bool) { uint256 ownerEpoch = _ownerEpoch(); uint256 recentEpochs = OLD_KEY_RETENTION + uint256(1); uint256 lowerBoundOwnerEpoch = ownerEpoch > recentEpochs ? ownerEpoch - recentEpochs : uint256(0); while (ownerEpoch > lowerBoundOwnerEpoch) { if (_areValidOwnersInEpoch(ownerEpoch--, accounts)) return true; } return false; } /// @dev Returns true if a sufficient quantity of `accounts` are owners in the `ownerEpoch`. function _areValidOwnersInEpoch(uint256 ownerEpoch, address[] memory accounts) internal view returns (bool) { if (_containsDuplicates(accounts)) return false; uint256 threshold = _getOwnerThreshold(ownerEpoch); uint256 validSignerCount; for (uint256 i; i < accounts.length; i++) { if (_isOwner(ownerEpoch, accounts[i]) && ++validSignerCount >= threshold) return true; } return false; } /// @dev Returns the array of owners within the current `ownerEpoch`. function owners() public view override returns (address[] memory results) { uint256 ownerEpoch = _ownerEpoch(); uint256 ownerCount = _getOwnerCount(ownerEpoch); results = new address[](ownerCount); for (uint256 i; i < ownerCount; i++) { results[i] = _getOwner(ownerEpoch, i); } } /***********\ |* Setters *| \***********/ function _setOwnerEpoch(uint256 ownerEpoch) internal { _setUint(KEY_OWNER_EPOCH, ownerEpoch); } function _setOwner( uint256 ownerEpoch, uint256 index, address account ) internal { require(account != address(0), 'ZERO_ADDR'); _setAddress(_getOwnerKey(ownerEpoch, index), account); } function _setOwnerCount(uint256 ownerEpoch, uint256 ownerCount) internal { _setUint(_getOwnerCountKey(ownerEpoch), ownerCount); } function _setOwners( uint256 ownerEpoch, address[] memory accounts, uint256 threshold ) internal { uint256 accountLength = accounts.length; require(accountLength >= threshold, 'INV_OWNERS'); require(threshold > uint256(0), 'INV_OWNER_THLD'); _setOwnerThreshold(ownerEpoch, threshold); _setOwnerCount(ownerEpoch, accountLength); for (uint256 i; i < accountLength; i++) { address account = accounts[i]; // Check that the account wasn't already set as an owner for this ownerEpoch. require(!_isOwner(ownerEpoch, account), 'DUP_OWNER'); // Set this account as the i-th owner in this ownerEpoch (needed to we can get all the owners for `owners`). _setOwner(ownerEpoch, i, account); _setIsOwner(ownerEpoch, account, true); } } function _setOwnerThreshold(uint256 ownerEpoch, uint256 ownerThreshold) internal { _setUint(_getOwnerThresholdKey(ownerEpoch), ownerThreshold); } function _setIsOwner( uint256 ownerEpoch, address account, bool isOwner ) internal { _setBool(_getIsOwnerKey(ownerEpoch, account), isOwner); } /**************************\ |* Operator Functionality *| \**************************/ /********************\ |* Pure Key Getters *| \********************/ function _getOperatorKey(uint256 operatorEpoch, uint256 index) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OPERATOR, operatorEpoch, index)); } function _getOperatorCountKey(uint256 operatorEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OPERATOR_COUNT, operatorEpoch)); } function _getOperatorThresholdKey(uint256 operatorEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OPERATOR_THRESHOLD, operatorEpoch)); } function _getIsOperatorKey(uint256 operatorEpoch, address account) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_IS_OPERATOR, operatorEpoch, account)); } /***********\ |* Getters *| \***********/ function _operatorEpoch() internal view returns (uint256) { return getUint(KEY_OPERATOR_EPOCH); } function _getOperator(uint256 operatorEpoch, uint256 index) internal view returns (address) { return getAddress(_getOperatorKey(operatorEpoch, index)); } function _getOperatorCount(uint256 operatorEpoch) internal view returns (uint256) { return getUint(_getOperatorCountKey(operatorEpoch)); } function _getOperatorThreshold(uint256 operatorEpoch) internal view returns (uint256) { return getUint(_getOperatorThresholdKey(operatorEpoch)); } function _isOperator(uint256 operatorEpoch, address account) internal view returns (bool) { return getBool(_getIsOperatorKey(operatorEpoch, account)); } /// @dev Returns true if a sufficient quantity of `accounts` are operator in the same `operatorEpoch`, within the last `OLD_KEY_RETENTION + 1` operator epochs. function _areValidRecentOperators(address[] memory accounts) internal view returns (bool) { uint256 operatorEpoch = _operatorEpoch(); uint256 recentEpochs = OLD_KEY_RETENTION + uint256(1); uint256 lowerBoundOperatorEpoch = operatorEpoch > recentEpochs ? operatorEpoch - recentEpochs : uint256(0); while (operatorEpoch > lowerBoundOperatorEpoch) { if (_areValidOperatorsInEpoch(operatorEpoch--, accounts)) return true; } return false; } /// @dev Returns true if a sufficient quantity of `accounts` are operator in the `operatorEpoch`. function _areValidOperatorsInEpoch(uint256 operatorEpoch, address[] memory accounts) internal view returns (bool) { if (_containsDuplicates(accounts)) return false; uint256 threshold = _getOperatorThreshold(operatorEpoch); uint256 validSignerCount; for (uint256 i; i < accounts.length; i++) { if (_isOperator(operatorEpoch, accounts[i]) && ++validSignerCount >= threshold) return true; } return false; } /// @dev Returns the array of operators within the current `operatorEpoch`. function operators() public view override returns (address[] memory results) { uint256 operatorEpoch = _operatorEpoch(); uint256 operatorCount = _getOperatorCount(operatorEpoch); results = new address[](operatorCount); for (uint256 i; i < operatorCount; i++) { results[i] = _getOperator(operatorEpoch, i); } } /***********\ |* Setters *| \***********/ function _setOperatorEpoch(uint256 operatorEpoch) internal { _setUint(KEY_OPERATOR_EPOCH, operatorEpoch); } function _setOperator( uint256 operatorEpoch, uint256 index, address account ) internal { // AUDIT: Should have `require(account != address(0), 'ZERO_ADDR');` like Singlesig? _setAddress(_getOperatorKey(operatorEpoch, index), account); } function _setOperatorCount(uint256 operatorEpoch, uint256 operatorCount) internal { _setUint(_getOperatorCountKey(operatorEpoch), operatorCount); } function _setOperators( uint256 operatorEpoch, address[] memory accounts, uint256 threshold ) internal { uint256 accountLength = accounts.length; require(accountLength >= threshold, 'INV_OPERATORS'); require(threshold > uint256(0), 'INV_OPERATOR_THLD'); _setOperatorThreshold(operatorEpoch, threshold); _setOperatorCount(operatorEpoch, accountLength); for (uint256 i; i < accountLength; i++) { address account = accounts[i]; // Check that the account wasn't already set as an operator for this operatorEpoch. require(!_isOperator(operatorEpoch, account), 'DUP_OPERATOR'); // Set this account as the i-th operator in this operatorEpoch (needed to we can get all the operators for `operators`). _setOperator(operatorEpoch, i, account); _setIsOperator(operatorEpoch, account, true); } } function _setOperatorThreshold(uint256 operatorEpoch, uint256 operatorThreshold) internal { _setUint(_getOperatorThresholdKey(operatorEpoch), operatorThreshold); } function _setIsOperator( uint256 operatorEpoch, address account, bool isOperator ) internal { _setBool(_getIsOperatorKey(operatorEpoch, account), isOperator); } /**********************\ |* Self Functionality *| \**********************/ function deployToken(bytes calldata params) external onlySelf { (string memory name, string memory symbol, uint8 decimals, uint256 cap) = abi.decode( params, (string, string, uint8, uint256) ); _deployToken(name, symbol, decimals, cap); } function mintToken(bytes calldata params) external onlySelf { (string memory symbol, address account, uint256 amount) = abi.decode(params, (string, address, uint256)); _mintToken(symbol, account, amount); } function burnToken(bytes calldata params) external onlySelf { (string memory symbol, bytes32 salt) = abi.decode(params, (string, bytes32)); _burnToken(symbol, salt); } function transferOwnership(bytes calldata params) external onlySelf { (address[] memory newOwners, uint256 newThreshold) = abi.decode(params, (address[], uint256)); uint256 ownerEpoch = _ownerEpoch(); emit OwnershipTransferred(owners(), _getOwnerThreshold(ownerEpoch), newOwners, newThreshold); _setOwnerEpoch(++ownerEpoch); _setOwners(ownerEpoch, newOwners, newThreshold); } function transferOperatorship(bytes calldata params) external onlySelf { (address[] memory newOperators, uint256 newThreshold) = abi.decode(params, (address[], uint256)); uint256 ownerEpoch = _ownerEpoch(); emit OperatorshipTransferred(operators(), _getOperatorThreshold(ownerEpoch), newOperators, newThreshold); uint256 operatorEpoch = _operatorEpoch(); _setOperatorEpoch(++operatorEpoch); _setOperators(operatorEpoch, newOperators, newThreshold); } /**************************\ |* External Functionality *| \**************************/ function setup(bytes calldata params) external override { // Prevent setup from being called on a non-proxy (the implementation). require(implementation() != address(0), 'NOT_PROXY'); ( address[] memory adminAddresses, uint256 adminThreshold, address[] memory ownerAddresses, uint256 ownerThreshold, address[] memory operatorAddresses, uint256 operatorThreshold ) = abi.decode(params, (address[], uint256, address[], uint256, address[], uint256)); uint256 adminEpoch = _adminEpoch() + uint256(1); _setAdminEpoch(adminEpoch); _setAdmins(adminEpoch, adminAddresses, adminThreshold); uint256 ownerEpoch = _ownerEpoch() + uint256(1); _setOwnerEpoch(ownerEpoch); _setOwners(ownerEpoch, ownerAddresses, ownerThreshold); uint256 operatorEpoch = _operatorEpoch() + uint256(1); _setOperatorEpoch(operatorEpoch); _setOperators(operatorEpoch, operatorAddresses, operatorThreshold); emit OwnershipTransferred(new address[](uint256(0)), uint256(0), ownerAddresses, ownerThreshold); emit OperatorshipTransferred(new address[](uint256(0)), uint256(0), operatorAddresses, operatorThreshold); } function execute(bytes calldata input) external override { (bytes memory data, bytes[] memory signatures) = abi.decode(input, (bytes, bytes[])); _execute(data, signatures); } function _execute(bytes memory data, bytes[] memory signatures) internal { uint256 signatureCount = signatures.length; address[] memory signers = new address[](signatureCount); for (uint256 i; i < signatureCount; i++) { signers[i] = ECDSA.recover(ECDSA.toEthSignedMessageHash(keccak256(data)), signatures[i]); } (uint256 chainId, bytes32[] memory commandIds, string[] memory commands, bytes[] memory params) = abi.decode( data, (uint256, bytes32[], string[], bytes[]) ); require(chainId == _getChainID(), 'INV_CHAIN'); uint256 commandsLength = commandIds.length; require(commandsLength == commands.length && commandsLength == params.length, 'INV_CMDS'); bool areValidCurrentOwners = _areValidOwnersInEpoch(_ownerEpoch(), signers); bool areValidRecentOwners = areValidCurrentOwners || _areValidRecentOwners(signers); bool areValidRecentOperators = _areValidRecentOperators(signers); for (uint256 i; i < commandsLength; i++) { bytes32 commandId = commandIds[i]; if (isCommandExecuted(commandId)) continue; /* Ignore if duplicate commandId received */ bytes4 commandSelector; bytes32 commandHash = keccak256(abi.encodePacked(commands[i])); if (commandHash == SELECTOR_DEPLOY_TOKEN) { if (!areValidRecentOwners) continue; commandSelector = AxelarGatewayMultisig.deployToken.selector; } else if (commandHash == SELECTOR_MINT_TOKEN) { if (!areValidRecentOperators && !areValidRecentOwners) continue; commandSelector = AxelarGatewayMultisig.mintToken.selector; } else if (commandHash == SELECTOR_BURN_TOKEN) { if (!areValidRecentOperators && !areValidRecentOwners) continue; commandSelector = AxelarGatewayMultisig.burnToken.selector; } else if (commandHash == SELECTOR_TRANSFER_OWNERSHIP) { if (!areValidCurrentOwners) continue; commandSelector = AxelarGatewayMultisig.transferOwnership.selector; } else if (commandHash == SELECTOR_TRANSFER_OPERATORSHIP) { if (!areValidCurrentOwners) continue; commandSelector = AxelarGatewayMultisig.transferOperatorship.selector; } else { continue; /* Ignore if unknown command received */ } // Prevent a re-entrancy from executing this command before it can be marked as successful. _setCommandExecuted(commandId, true); (bool success, ) = address(this).call(abi.encodeWithSelector(commandSelector, params[i])); _setCommandExecuted(commandId, success); if (success) { emit Executed(commandId); } } } } // Root file: src/AxelarGatewayProxyMultisig.sol pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGateway } from 'src/interfaces/IAxelarGateway.sol'; // import { AxelarGatewayProxy } from 'src/AxelarGatewayProxy.sol'; // import { AxelarGatewayMultisig } from 'src/AxelarGatewayMultisig.sol'; contract AxelarGatewayProxyMultisig is AxelarGatewayProxy { constructor(bytes memory params) { // AUDIT: constructor contains entire AxelarGatewayMultisig bytecode. Consider passing in an AxelarGatewayMultisig address. address gateway = address(new AxelarGatewayMultisig()); _setAddress(KEY_IMPLEMENTATION, gateway); (bool success, ) = gateway.delegatecall(abi.encodeWithSelector(IAxelarGateway.setup.selector, params)); require(success, 'SETUP_FAILED'); } function setup(bytes calldata params) external {} }
// Dependency file: src/EternalStorage.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; /** * @title EternalStorage * @dev This contract holds all the necessary state variables to carry out the storage of any contract. */ contract EternalStorage { mapping(bytes32 => uint256) private _uintStorage; mapping(bytes32 => string) private _stringStorage; mapping(bytes32 => address) private _addressStorage; mapping(bytes32 => bytes) private _bytesStorage; mapping(bytes32 => bool) private _boolStorage; mapping(bytes32 => int256) private _intStorage; // *** Getter Methods *** function getUint(bytes32 key) public view returns (uint256) { return _uintStorage[key]; } function getString(bytes32 key) public view returns (string memory) { return _stringStorage[key]; } function getAddress(bytes32 key) public view returns (address) { return _addressStorage[key]; } function getBytes(bytes32 key) public view returns (bytes memory) { return _bytesStorage[key]; } function getBool(bytes32 key) public view returns (bool) { return _boolStorage[key]; } function getInt(bytes32 key) public view returns (int256) { return _intStorage[key]; } // *** Setter Methods *** function _setUint(bytes32 key, uint256 value) internal { _uintStorage[key] = value; } function _setString(bytes32 key, string memory value) internal { _stringStorage[key] = value; } function _setAddress(bytes32 key, address value) internal { _addressStorage[key] = value; } function _setBytes(bytes32 key, bytes memory value) internal { _bytesStorage[key] = value; } function _setBool(bytes32 key, bool value) internal { _boolStorage[key] = value; } function _setInt(bytes32 key, int256 value) internal { _intStorage[key] = value; } // *** Delete Methods *** function _deleteUint(bytes32 key) internal { delete _uintStorage[key]; } function _deleteString(bytes32 key) internal { delete _stringStorage[key]; } function _deleteAddress(bytes32 key) internal { delete _addressStorage[key]; } function _deleteBytes(bytes32 key) internal { delete _bytesStorage[key]; } function _deleteBool(bytes32 key) internal { delete _boolStorage[key]; } function _deleteInt(bytes32 key) internal { delete _intStorage[key]; } } // Root file: src/AdminMultisigBase.sol pragma solidity >=0.8.0 <0.9.0; // import { EternalStorage } from 'src/EternalStorage.sol'; contract AdminMultisigBase is EternalStorage { // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_ADMIN_EPOCH = keccak256('admin-epoch'); bytes32 internal constant PREFIX_ADMIN = keccak256('admin'); bytes32 internal constant PREFIX_ADMIN_COUNT = keccak256('admin-count'); bytes32 internal constant PREFIX_ADMIN_THRESHOLD = keccak256('admin-threshold'); bytes32 internal constant PREFIX_ADMIN_VOTE_COUNTS = keccak256('admin-vote-counts'); bytes32 internal constant PREFIX_ADMIN_VOTED = keccak256('admin-voted'); bytes32 internal constant PREFIX_IS_ADMIN = keccak256('is-admin'); modifier onlyAdmin() { uint256 adminEpoch = _adminEpoch(); require(_isAdmin(adminEpoch, msg.sender), 'NOT_ADMIN'); bytes32 topic = keccak256(msg.data); // Check that admin has not voted, then record that they have voted. require(!_hasVoted(adminEpoch, topic, msg.sender), 'VOTED'); _setHasVoted(adminEpoch, topic, msg.sender, true); // Determine the new vote count and update it. uint256 adminVoteCount = _getVoteCount(adminEpoch, topic) + uint256(1); _setVoteCount(adminEpoch, topic, adminVoteCount); // Do not proceed with operation execution if insufficient votes. if (adminVoteCount < _getAdminThreshold(adminEpoch)) return; _; // Clear vote count and voted booleans. _setVoteCount(adminEpoch, topic, uint256(0)); uint256 adminCount = _getAdminCount(adminEpoch); for (uint256 i; i < adminCount; i++) { _setHasVoted(adminEpoch, topic, _getAdmin(adminEpoch, i), false); } } /********************\ |* Pure Key Getters *| \********************/ function _getAdminKey(uint256 adminEpoch, uint256 index) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN, adminEpoch, index)); } function _getAdminCountKey(uint256 adminEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_COUNT, adminEpoch)); } function _getAdminThresholdKey(uint256 adminEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_THRESHOLD, adminEpoch)); } function _getAdminVoteCountsKey(uint256 adminEpoch, bytes32 topic) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_VOTE_COUNTS, adminEpoch, topic)); } function _getAdminVotedKey( uint256 adminEpoch, bytes32 topic, address account ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_VOTED, adminEpoch, topic, account)); } function _getIsAdminKey(uint256 adminEpoch, address account) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_IS_ADMIN, adminEpoch, account)); } /***********\ |* Getters *| \***********/ function _adminEpoch() internal view returns (uint256) { return getUint(KEY_ADMIN_EPOCH); } function _getAdmin(uint256 adminEpoch, uint256 index) internal view returns (address) { return getAddress(_getAdminKey(adminEpoch, index)); } function _getAdminCount(uint256 adminEpoch) internal view returns (uint256) { return getUint(_getAdminCountKey(adminEpoch)); } function _getAdminThreshold(uint256 adminEpoch) internal view returns (uint256) { return getUint(_getAdminThresholdKey(adminEpoch)); } function _getVoteCount(uint256 adminEpoch, bytes32 topic) internal view returns (uint256) { return getUint(_getAdminVoteCountsKey(adminEpoch, topic)); } function _hasVoted( uint256 adminEpoch, bytes32 topic, address account ) internal view returns (bool) { return getBool(_getAdminVotedKey(adminEpoch, topic, account)); } function _isAdmin(uint256 adminEpoch, address account) internal view returns (bool) { return getBool(_getIsAdminKey(adminEpoch, account)); } /***********\ |* Setters *| \***********/ function _setAdminEpoch(uint256 adminEpoch) internal { _setUint(KEY_ADMIN_EPOCH, adminEpoch); } function _setAdmin( uint256 adminEpoch, uint256 index, address account ) internal { _setAddress(_getAdminKey(adminEpoch, index), account); } function _setAdminCount(uint256 adminEpoch, uint256 adminCount) internal { _setUint(_getAdminCountKey(adminEpoch), adminCount); } function _setAdmins( uint256 adminEpoch, address[] memory accounts, uint256 threshold ) internal { uint256 adminLength = accounts.length; require(adminLength >= threshold, 'INV_ADMINS'); require(threshold > uint256(0), 'INV_ADMIN_THLD'); _setAdminThreshold(adminEpoch, threshold); _setAdminCount(adminEpoch, adminLength); for (uint256 i; i < adminLength; i++) { address account = accounts[i]; // Check that the account wasn't already set as an admin for this epoch. require(!_isAdmin(adminEpoch, account), 'DUP_ADMIN'); // Set this account as the i-th admin in this epoch (needed to we can clear topic votes in `onlyAdmin`). _setAdmin(adminEpoch, i, account); _setIsAdmin(adminEpoch, account, true); } } function _setAdminThreshold(uint256 adminEpoch, uint256 adminThreshold) internal { _setUint(_getAdminThresholdKey(adminEpoch), adminThreshold); } function _setVoteCount( uint256 adminEpoch, bytes32 topic, uint256 voteCount ) internal { _setUint(_getAdminVoteCountsKey(adminEpoch, topic), voteCount); } function _setHasVoted( uint256 adminEpoch, bytes32 topic, address account, bool voted ) internal { _setBool(_getAdminVotedKey(adminEpoch, topic, account), voted); } function _setIsAdmin( uint256 adminEpoch, address account, bool isAdmin ) internal { _setBool(_getIsAdminKey(adminEpoch, account), isAdmin); } }
// Dependency file: src/interfaces/IAxelarGateway.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; interface IAxelarGateway { /**********\ |* Events *| \**********/ event Executed(bytes32 indexed commandId); event TokenDeployed(string symbol, address tokenAddresses); event TokenFrozen(string indexed symbol); event TokenUnfrozen(string indexed symbol); event AllTokensFrozen(); event AllTokensUnfrozen(); event AccountBlacklisted(address indexed account); event AccountWhitelisted(address indexed account); event Upgraded(address indexed implementation); /***********\ |* Getters *| \***********/ function allTokensFrozen() external view returns (bool); function implementation() external view returns (address); function tokenAddresses(string memory symbol) external view returns (address); function tokenFrozen(string memory symbol) external view returns (bool); function isCommandExecuted(bytes32 commandId) external view returns (bool); /*******************\ |* Admin Functions *| \*******************/ function freezeToken(string memory symbol) external; function unfreezeToken(string memory symbol) external; function freezeAllTokens() external; function unfreezeAllTokens() external; function upgrade(address newImplementation, bytes calldata setupParams) external; /**********************\ |* External Functions *| \**********************/ function setup(bytes calldata params) external; function execute(bytes calldata input) external; } // Dependency file: src/interfaces/IERC20.sol // pragma solidity >=0.8.0 <0.9.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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 `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @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); } // Dependency file: src/Context.sol // pragma solidity >=0.8.0 <0.9.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 GSN 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 payable) { return payable(msg.sender); } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // Dependency file: src/ERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { IERC20 } from 'src/interfaces/IERC20.sol'; // import { Context } from 'src/Context.sol'; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { mapping(address => uint256) public override balanceOf; mapping(address => mapping(address => uint256)) public override allowance; uint256 public override totalSupply; string public name; string public symbol; uint8 public immutable decimals; /** * @dev Sets the values for {name}, {symbol}, and {decimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { name = name_; symbol = symbol_; decimals = decimals_; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), allowance[sender][_msgSender()] - amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] - subtractedValue); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), 'ZERO_ADDR'); require(recipient != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(sender, recipient, amount); balanceOf[sender] -= amount; balanceOf[recipient] += amount; emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(address(0), account, amount); totalSupply += amount; balanceOf[account] += amount; emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(account, address(0), amount); balanceOf[account] -= amount; totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), 'ZERO_ADDR'); require(spender != address(0), 'ZERO_ADDR'); allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } // Dependency file: src/Ownable.sol // pragma solidity >=0.8.0 <0.9.0; abstract contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } modifier onlyOwner() { require(owner == msg.sender, 'NOT_OWNER'); _; } function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), 'ZERO_ADDR'); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } } // Dependency file: src/Burner.sol // pragma solidity >=0.8.0 <0.9.0; // import { BurnableMintableCappedERC20 } from 'src/BurnableMintableCappedERC20.sol'; contract Burner { constructor(address tokenAddress, bytes32 salt) { BurnableMintableCappedERC20(tokenAddress).burn(salt); selfdestruct(payable(address(0))); } } // Dependency file: src/EternalStorage.sol // pragma solidity >=0.8.0 <0.9.0; /** * @title EternalStorage * @dev This contract holds all the necessary state variables to carry out the storage of any contract. */ contract EternalStorage { mapping(bytes32 => uint256) private _uintStorage; mapping(bytes32 => string) private _stringStorage; mapping(bytes32 => address) private _addressStorage; mapping(bytes32 => bytes) private _bytesStorage; mapping(bytes32 => bool) private _boolStorage; mapping(bytes32 => int256) private _intStorage; // *** Getter Methods *** function getUint(bytes32 key) public view returns (uint256) { return _uintStorage[key]; } function getString(bytes32 key) public view returns (string memory) { return _stringStorage[key]; } function getAddress(bytes32 key) public view returns (address) { return _addressStorage[key]; } function getBytes(bytes32 key) public view returns (bytes memory) { return _bytesStorage[key]; } function getBool(bytes32 key) public view returns (bool) { return _boolStorage[key]; } function getInt(bytes32 key) public view returns (int256) { return _intStorage[key]; } // *** Setter Methods *** function _setUint(bytes32 key, uint256 value) internal { _uintStorage[key] = value; } function _setString(bytes32 key, string memory value) internal { _stringStorage[key] = value; } function _setAddress(bytes32 key, address value) internal { _addressStorage[key] = value; } function _setBytes(bytes32 key, bytes memory value) internal { _bytesStorage[key] = value; } function _setBool(bytes32 key, bool value) internal { _boolStorage[key] = value; } function _setInt(bytes32 key, int256 value) internal { _intStorage[key] = value; } // *** Delete Methods *** function _deleteUint(bytes32 key) internal { delete _uintStorage[key]; } function _deleteString(bytes32 key) internal { delete _stringStorage[key]; } function _deleteAddress(bytes32 key) internal { delete _addressStorage[key]; } function _deleteBytes(bytes32 key) internal { delete _bytesStorage[key]; } function _deleteBool(bytes32 key) internal { delete _boolStorage[key]; } function _deleteInt(bytes32 key) internal { delete _intStorage[key]; } } // Dependency file: src/BurnableMintableCappedERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { ERC20 } from 'src/ERC20.sol'; // import { Ownable } from 'src/Ownable.sol'; // import { Burner } from 'src/Burner.sol'; // import { EternalStorage } from 'src/EternalStorage.sol'; contract BurnableMintableCappedERC20 is ERC20, Ownable { uint256 public cap; bytes32 private constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen'); bytes32 private constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen'); event Frozen(address indexed owner); event Unfrozen(address indexed owner); constructor( string memory name, string memory symbol, uint8 decimals, uint256 capacity ) ERC20(name, symbol, decimals) Ownable() { cap = capacity; } function depositAddress(bytes32 salt) public view returns (address) { // This would be easier, cheaper, simpler, and result in globally consistent deposit addresses for any salt (all chains, all tokens). // return address(uint160(uint256(keccak256(abi.encodePacked(bytes32(0x000000000000000000000000000000000000000000000000000000000000dead), salt))))); /* Convert a hash which is bytes32 to an address which is 20-byte long according to https://docs.soliditylang.org/en/v0.8.1/control-structures.html?highlight=create2#salted-contract-creations-create2 */ return address( uint160( uint256( keccak256( abi.encodePacked( bytes1(0xff), owner, salt, keccak256(abi.encodePacked(type(Burner).creationCode, abi.encode(address(this)), salt)) ) ) ) ) ); } function mint(address account, uint256 amount) public onlyOwner { uint256 capacity = cap; require(capacity == 0 || totalSupply + amount <= capacity, 'CAP_EXCEEDED'); _mint(account, amount); } function burn(bytes32 salt) public onlyOwner { address account = depositAddress(salt); _burn(account, balanceOf[account]); } function _beforeTokenTransfer( address, address, uint256 ) internal view override { require(!EternalStorage(owner).getBool(KEY_ALL_TOKENS_FROZEN), 'IS_FROZEN'); require(!EternalStorage(owner).getBool(keccak256(abi.encodePacked(PREFIX_TOKEN_FROZEN, symbol))), 'IS_FROZEN'); } } // Dependency file: src/AdminMultisigBase.sol // pragma solidity >=0.8.0 <0.9.0; // import { EternalStorage } from 'src/EternalStorage.sol'; contract AdminMultisigBase is EternalStorage { // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_ADMIN_EPOCH = keccak256('admin-epoch'); bytes32 internal constant PREFIX_ADMIN = keccak256('admin'); bytes32 internal constant PREFIX_ADMIN_COUNT = keccak256('admin-count'); bytes32 internal constant PREFIX_ADMIN_THRESHOLD = keccak256('admin-threshold'); bytes32 internal constant PREFIX_ADMIN_VOTE_COUNTS = keccak256('admin-vote-counts'); bytes32 internal constant PREFIX_ADMIN_VOTED = keccak256('admin-voted'); bytes32 internal constant PREFIX_IS_ADMIN = keccak256('is-admin'); modifier onlyAdmin() { uint256 adminEpoch = _adminEpoch(); require(_isAdmin(adminEpoch, msg.sender), 'NOT_ADMIN'); bytes32 topic = keccak256(msg.data); // Check that admin has not voted, then record that they have voted. require(!_hasVoted(adminEpoch, topic, msg.sender), 'VOTED'); _setHasVoted(adminEpoch, topic, msg.sender, true); // Determine the new vote count and update it. uint256 adminVoteCount = _getVoteCount(adminEpoch, topic) + uint256(1); _setVoteCount(adminEpoch, topic, adminVoteCount); // Do not proceed with operation execution if insufficient votes. if (adminVoteCount < _getAdminThreshold(adminEpoch)) return; _; // Clear vote count and voted booleans. _setVoteCount(adminEpoch, topic, uint256(0)); uint256 adminCount = _getAdminCount(adminEpoch); for (uint256 i; i < adminCount; i++) { _setHasVoted(adminEpoch, topic, _getAdmin(adminEpoch, i), false); } } /********************\ |* Pure Key Getters *| \********************/ function _getAdminKey(uint256 adminEpoch, uint256 index) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN, adminEpoch, index)); } function _getAdminCountKey(uint256 adminEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_COUNT, adminEpoch)); } function _getAdminThresholdKey(uint256 adminEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_THRESHOLD, adminEpoch)); } function _getAdminVoteCountsKey(uint256 adminEpoch, bytes32 topic) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_VOTE_COUNTS, adminEpoch, topic)); } function _getAdminVotedKey( uint256 adminEpoch, bytes32 topic, address account ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_VOTED, adminEpoch, topic, account)); } function _getIsAdminKey(uint256 adminEpoch, address account) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_IS_ADMIN, adminEpoch, account)); } /***********\ |* Getters *| \***********/ function _adminEpoch() internal view returns (uint256) { return getUint(KEY_ADMIN_EPOCH); } function _getAdmin(uint256 adminEpoch, uint256 index) internal view returns (address) { return getAddress(_getAdminKey(adminEpoch, index)); } function _getAdminCount(uint256 adminEpoch) internal view returns (uint256) { return getUint(_getAdminCountKey(adminEpoch)); } function _getAdminThreshold(uint256 adminEpoch) internal view returns (uint256) { return getUint(_getAdminThresholdKey(adminEpoch)); } function _getVoteCount(uint256 adminEpoch, bytes32 topic) internal view returns (uint256) { return getUint(_getAdminVoteCountsKey(adminEpoch, topic)); } function _hasVoted( uint256 adminEpoch, bytes32 topic, address account ) internal view returns (bool) { return getBool(_getAdminVotedKey(adminEpoch, topic, account)); } function _isAdmin(uint256 adminEpoch, address account) internal view returns (bool) { return getBool(_getIsAdminKey(adminEpoch, account)); } /***********\ |* Setters *| \***********/ function _setAdminEpoch(uint256 adminEpoch) internal { _setUint(KEY_ADMIN_EPOCH, adminEpoch); } function _setAdmin( uint256 adminEpoch, uint256 index, address account ) internal { _setAddress(_getAdminKey(adminEpoch, index), account); } function _setAdminCount(uint256 adminEpoch, uint256 adminCount) internal { _setUint(_getAdminCountKey(adminEpoch), adminCount); } function _setAdmins( uint256 adminEpoch, address[] memory accounts, uint256 threshold ) internal { uint256 adminLength = accounts.length; require(adminLength >= threshold, 'INV_ADMINS'); require(threshold > uint256(0), 'INV_ADMIN_THLD'); _setAdminThreshold(adminEpoch, threshold); _setAdminCount(adminEpoch, adminLength); for (uint256 i; i < adminLength; i++) { address account = accounts[i]; // Check that the account wasn't already set as an admin for this epoch. require(!_isAdmin(adminEpoch, account), 'DUP_ADMIN'); // Set this account as the i-th admin in this epoch (needed to we can clear topic votes in `onlyAdmin`). _setAdmin(adminEpoch, i, account); _setIsAdmin(adminEpoch, account, true); } } function _setAdminThreshold(uint256 adminEpoch, uint256 adminThreshold) internal { _setUint(_getAdminThresholdKey(adminEpoch), adminThreshold); } function _setVoteCount( uint256 adminEpoch, bytes32 topic, uint256 voteCount ) internal { _setUint(_getAdminVoteCountsKey(adminEpoch, topic), voteCount); } function _setHasVoted( uint256 adminEpoch, bytes32 topic, address account, bool voted ) internal { _setBool(_getAdminVotedKey(adminEpoch, topic, account), voted); } function _setIsAdmin( uint256 adminEpoch, address account, bool isAdmin ) internal { _setBool(_getIsAdminKey(adminEpoch, account), isAdmin); } } // Root file: src/AxelarGateway.sol pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGateway } from 'src/interfaces/IAxelarGateway.sol'; // import { BurnableMintableCappedERC20 } from 'src/BurnableMintableCappedERC20.sol'; // import { AdminMultisigBase } from 'src/AdminMultisigBase.sol'; abstract contract AxelarGateway is IAxelarGateway, AdminMultisigBase { /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`. bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc); // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen'); bytes32 internal constant PREFIX_COMMAND_EXECUTED = keccak256('command-executed'); bytes32 internal constant PREFIX_TOKEN_ADDRESS = keccak256('token-address'); bytes32 internal constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen'); bytes32 internal constant SELECTOR_BURN_TOKEN = keccak256('burnToken'); bytes32 internal constant SELECTOR_DEPLOY_TOKEN = keccak256('deployToken'); bytes32 internal constant SELECTOR_MINT_TOKEN = keccak256('mintToken'); bytes32 internal constant SELECTOR_TRANSFER_OPERATORSHIP = keccak256('transferOperatorship'); bytes32 internal constant SELECTOR_TRANSFER_OWNERSHIP = keccak256('transferOwnership'); uint8 internal constant OLD_KEY_RETENTION = 16; modifier onlySelf() { require(msg.sender == address(this), 'NOT_SELF'); _; } /***********\ |* Getters *| \***********/ function allTokensFrozen() public view override returns (bool) { return getBool(KEY_ALL_TOKENS_FROZEN); } function implementation() public view override returns (address) { return getAddress(KEY_IMPLEMENTATION); } function tokenAddresses(string memory symbol) public view override returns (address) { return getAddress(_getTokenAddressKey(symbol)); } function tokenFrozen(string memory symbol) public view override returns (bool) { return getBool(_getFreezeTokenKey(symbol)); } function isCommandExecuted(bytes32 commandId) public view override returns (bool) { return getBool(_getIsCommandExecutedKey(commandId)); } /*******************\ |* Admin Functions *| \*******************/ function freezeToken(string memory symbol) external override onlyAdmin { _setBool(_getFreezeTokenKey(symbol), true); emit TokenFrozen(symbol); } function unfreezeToken(string memory symbol) external override onlyAdmin { _setBool(_getFreezeTokenKey(symbol), false); emit TokenUnfrozen(symbol); } function freezeAllTokens() external override onlyAdmin { _setBool(KEY_ALL_TOKENS_FROZEN, true); emit AllTokensFrozen(); } function unfreezeAllTokens() external override onlyAdmin { _setBool(KEY_ALL_TOKENS_FROZEN, false); emit AllTokensUnfrozen(); } function upgrade(address newImplementation, bytes calldata setupParams) external override onlyAdmin { emit Upgraded(newImplementation); // AUDIT: If `newImplementation.setup` performs `selfdestruct`, it will result in the loss of _this_ implementation (thereby losing the gateway) // if `upgrade` is entered within the context of _this_ implementation itself. (bool success, ) = newImplementation.delegatecall( abi.encodeWithSelector(IAxelarGateway.setup.selector, setupParams) ); require(success, 'SETUP_FAILED'); _setImplementation(newImplementation); } /**********************\ |* Internal Functions *| \**********************/ function _deployToken( string memory name, string memory symbol, uint8 decimals, uint256 cap ) internal { require(tokenAddresses(symbol) == address(0), 'TOKEN_EXIST'); bytes32 salt = keccak256(abi.encodePacked(symbol)); address token = address(new BurnableMintableCappedERC20{ salt: salt }(name, symbol, decimals, cap)); _setTokenAddress(symbol, token); emit TokenDeployed(symbol, token); } function _mintToken( string memory symbol, address account, uint256 amount ) internal { address tokenAddress = tokenAddresses(symbol); require(tokenAddress != address(0), 'TOKEN_NOT_EXIST'); BurnableMintableCappedERC20(tokenAddress).mint(account, amount); } function _burnToken(string memory symbol, bytes32 salt) internal { address tokenAddress = tokenAddresses(symbol); require(tokenAddress != address(0), 'TOKEN_NOT_EXIST'); BurnableMintableCappedERC20(tokenAddress).burn(salt); } /********************\ |* Pure Key Getters *| \********************/ function _getFreezeTokenKey(string memory symbol) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_TOKEN_FROZEN, symbol)); } function _getTokenAddressKey(string memory symbol) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_TOKEN_ADDRESS, symbol)); } function _getIsCommandExecutedKey(bytes32 commandId) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_COMMAND_EXECUTED, commandId)); } /********************\ |* Internal Getters *| \********************/ function _getChainID() internal view returns (uint256 id) { assembly { id := chainid() } } /********************\ |* Internal Setters *| \********************/ function _setTokenAddress(string memory symbol, address tokenAddr) internal { _setAddress(_getTokenAddressKey(symbol), tokenAddr); } function _setCommandExecuted(bytes32 commandId, bool executed) internal { _setBool(_getIsCommandExecutedKey(commandId), executed); } function _setImplementation(address newImplementation) internal { _setAddress(KEY_IMPLEMENTATION, newImplementation); } }
// Dependency file: src/interfaces/IAxelarGateway.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; interface IAxelarGateway { /**********\ |* Events *| \**********/ event Executed(bytes32 indexed commandId); event TokenDeployed(string symbol, address tokenAddresses); event TokenFrozen(string indexed symbol); event TokenUnfrozen(string indexed symbol); event AllTokensFrozen(); event AllTokensUnfrozen(); event AccountBlacklisted(address indexed account); event AccountWhitelisted(address indexed account); event Upgraded(address indexed implementation); /***********\ |* Getters *| \***********/ function allTokensFrozen() external view returns (bool); function implementation() external view returns (address); function tokenAddresses(string memory symbol) external view returns (address); function tokenFrozen(string memory symbol) external view returns (bool); function isCommandExecuted(bytes32 commandId) external view returns (bool); /*******************\ |* Admin Functions *| \*******************/ function freezeToken(string memory symbol) external; function unfreezeToken(string memory symbol) external; function freezeAllTokens() external; function unfreezeAllTokens() external; function upgrade(address newImplementation, bytes calldata setupParams) external; /**********************\ |* External Functions *| \**********************/ function setup(bytes calldata params) external; function execute(bytes calldata input) external; } // Dependency file: src/interfaces/IAxelarGatewayMultisig.sol // pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGateway } from 'src/interfaces/IAxelarGateway.sol'; interface IAxelarGatewayMultisig is IAxelarGateway { event OwnershipTransferred(address[] preOwners, uint256 prevThreshold, address[] newOwners, uint256 newThreshold); event OperatorshipTransferred(address[] preOperators, uint256 prevThreshold, address[] newOperators, uint256 newThreshold); function owners() external view returns (address[] memory); function operators() external view returns (address[] memory); } // Dependency file: src/ECDSA.sol // pragma solidity >=0.8.0 <0.9.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address signer) { // Check the signature length require(signature.length == 65, 'INV_LEN'); // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, 'INV_S'); require(v == 27 || v == 28, 'INV_V'); // If the signature is valid (and not malleable), return the signer address require((signer = ecrecover(hash, v, r, s)) != address(0), 'INV_SIG'); } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked('\x19Ethereum Signed Message:\n32', hash)); } } // Dependency file: src/interfaces/IERC20.sol // pragma solidity >=0.8.0 <0.9.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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 `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @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); } // Dependency file: src/Context.sol // pragma solidity >=0.8.0 <0.9.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 GSN 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 payable) { return payable(msg.sender); } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // Dependency file: src/ERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { IERC20 } from 'src/interfaces/IERC20.sol'; // import { Context } from 'src/Context.sol'; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { mapping(address => uint256) public override balanceOf; mapping(address => mapping(address => uint256)) public override allowance; uint256 public override totalSupply; string public name; string public symbol; uint8 public immutable decimals; /** * @dev Sets the values for {name}, {symbol}, and {decimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { name = name_; symbol = symbol_; decimals = decimals_; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), allowance[sender][_msgSender()] - amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] - subtractedValue); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), 'ZERO_ADDR'); require(recipient != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(sender, recipient, amount); balanceOf[sender] -= amount; balanceOf[recipient] += amount; emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(address(0), account, amount); totalSupply += amount; balanceOf[account] += amount; emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(account, address(0), amount); balanceOf[account] -= amount; totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), 'ZERO_ADDR'); require(spender != address(0), 'ZERO_ADDR'); allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } // Dependency file: src/Ownable.sol // pragma solidity >=0.8.0 <0.9.0; abstract contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } modifier onlyOwner() { require(owner == msg.sender, 'NOT_OWNER'); _; } function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), 'ZERO_ADDR'); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } } // Dependency file: src/Burner.sol // pragma solidity >=0.8.0 <0.9.0; // import { BurnableMintableCappedERC20 } from 'src/BurnableMintableCappedERC20.sol'; contract Burner { constructor(address tokenAddress, bytes32 salt) { BurnableMintableCappedERC20(tokenAddress).burn(salt); selfdestruct(payable(address(0))); } } // Dependency file: src/EternalStorage.sol // pragma solidity >=0.8.0 <0.9.0; /** * @title EternalStorage * @dev This contract holds all the necessary state variables to carry out the storage of any contract. */ contract EternalStorage { mapping(bytes32 => uint256) private _uintStorage; mapping(bytes32 => string) private _stringStorage; mapping(bytes32 => address) private _addressStorage; mapping(bytes32 => bytes) private _bytesStorage; mapping(bytes32 => bool) private _boolStorage; mapping(bytes32 => int256) private _intStorage; // *** Getter Methods *** function getUint(bytes32 key) public view returns (uint256) { return _uintStorage[key]; } function getString(bytes32 key) public view returns (string memory) { return _stringStorage[key]; } function getAddress(bytes32 key) public view returns (address) { return _addressStorage[key]; } function getBytes(bytes32 key) public view returns (bytes memory) { return _bytesStorage[key]; } function getBool(bytes32 key) public view returns (bool) { return _boolStorage[key]; } function getInt(bytes32 key) public view returns (int256) { return _intStorage[key]; } // *** Setter Methods *** function _setUint(bytes32 key, uint256 value) internal { _uintStorage[key] = value; } function _setString(bytes32 key, string memory value) internal { _stringStorage[key] = value; } function _setAddress(bytes32 key, address value) internal { _addressStorage[key] = value; } function _setBytes(bytes32 key, bytes memory value) internal { _bytesStorage[key] = value; } function _setBool(bytes32 key, bool value) internal { _boolStorage[key] = value; } function _setInt(bytes32 key, int256 value) internal { _intStorage[key] = value; } // *** Delete Methods *** function _deleteUint(bytes32 key) internal { delete _uintStorage[key]; } function _deleteString(bytes32 key) internal { delete _stringStorage[key]; } function _deleteAddress(bytes32 key) internal { delete _addressStorage[key]; } function _deleteBytes(bytes32 key) internal { delete _bytesStorage[key]; } function _deleteBool(bytes32 key) internal { delete _boolStorage[key]; } function _deleteInt(bytes32 key) internal { delete _intStorage[key]; } } // Dependency file: src/BurnableMintableCappedERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { ERC20 } from 'src/ERC20.sol'; // import { Ownable } from 'src/Ownable.sol'; // import { Burner } from 'src/Burner.sol'; // import { EternalStorage } from 'src/EternalStorage.sol'; contract BurnableMintableCappedERC20 is ERC20, Ownable { uint256 public cap; bytes32 private constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen'); bytes32 private constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen'); event Frozen(address indexed owner); event Unfrozen(address indexed owner); constructor( string memory name, string memory symbol, uint8 decimals, uint256 capacity ) ERC20(name, symbol, decimals) Ownable() { cap = capacity; } function depositAddress(bytes32 salt) public view returns (address) { // This would be easier, cheaper, simpler, and result in globally consistent deposit addresses for any salt (all chains, all tokens). // return address(uint160(uint256(keccak256(abi.encodePacked(bytes32(0x000000000000000000000000000000000000000000000000000000000000dead), salt))))); /* Convert a hash which is bytes32 to an address which is 20-byte long according to https://docs.soliditylang.org/en/v0.8.1/control-structures.html?highlight=create2#salted-contract-creations-create2 */ return address( uint160( uint256( keccak256( abi.encodePacked( bytes1(0xff), owner, salt, keccak256(abi.encodePacked(type(Burner).creationCode, abi.encode(address(this)), salt)) ) ) ) ) ); } function mint(address account, uint256 amount) public onlyOwner { uint256 capacity = cap; require(capacity == 0 || totalSupply + amount <= capacity, 'CAP_EXCEEDED'); _mint(account, amount); } function burn(bytes32 salt) public onlyOwner { address account = depositAddress(salt); _burn(account, balanceOf[account]); } function _beforeTokenTransfer( address, address, uint256 ) internal view override { require(!EternalStorage(owner).getBool(KEY_ALL_TOKENS_FROZEN), 'IS_FROZEN'); require(!EternalStorage(owner).getBool(keccak256(abi.encodePacked(PREFIX_TOKEN_FROZEN, symbol))), 'IS_FROZEN'); } } // Dependency file: src/AdminMultisigBase.sol // pragma solidity >=0.8.0 <0.9.0; // import { EternalStorage } from 'src/EternalStorage.sol'; contract AdminMultisigBase is EternalStorage { // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_ADMIN_EPOCH = keccak256('admin-epoch'); bytes32 internal constant PREFIX_ADMIN = keccak256('admin'); bytes32 internal constant PREFIX_ADMIN_COUNT = keccak256('admin-count'); bytes32 internal constant PREFIX_ADMIN_THRESHOLD = keccak256('admin-threshold'); bytes32 internal constant PREFIX_ADMIN_VOTE_COUNTS = keccak256('admin-vote-counts'); bytes32 internal constant PREFIX_ADMIN_VOTED = keccak256('admin-voted'); bytes32 internal constant PREFIX_IS_ADMIN = keccak256('is-admin'); modifier onlyAdmin() { uint256 adminEpoch = _adminEpoch(); require(_isAdmin(adminEpoch, msg.sender), 'NOT_ADMIN'); bytes32 topic = keccak256(msg.data); // Check that admin has not voted, then record that they have voted. require(!_hasVoted(adminEpoch, topic, msg.sender), 'VOTED'); _setHasVoted(adminEpoch, topic, msg.sender, true); // Determine the new vote count and update it. uint256 adminVoteCount = _getVoteCount(adminEpoch, topic) + uint256(1); _setVoteCount(adminEpoch, topic, adminVoteCount); // Do not proceed with operation execution if insufficient votes. if (adminVoteCount < _getAdminThreshold(adminEpoch)) return; _; // Clear vote count and voted booleans. _setVoteCount(adminEpoch, topic, uint256(0)); uint256 adminCount = _getAdminCount(adminEpoch); for (uint256 i; i < adminCount; i++) { _setHasVoted(adminEpoch, topic, _getAdmin(adminEpoch, i), false); } } /********************\ |* Pure Key Getters *| \********************/ function _getAdminKey(uint256 adminEpoch, uint256 index) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN, adminEpoch, index)); } function _getAdminCountKey(uint256 adminEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_COUNT, adminEpoch)); } function _getAdminThresholdKey(uint256 adminEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_THRESHOLD, adminEpoch)); } function _getAdminVoteCountsKey(uint256 adminEpoch, bytes32 topic) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_VOTE_COUNTS, adminEpoch, topic)); } function _getAdminVotedKey( uint256 adminEpoch, bytes32 topic, address account ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_ADMIN_VOTED, adminEpoch, topic, account)); } function _getIsAdminKey(uint256 adminEpoch, address account) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_IS_ADMIN, adminEpoch, account)); } /***********\ |* Getters *| \***********/ function _adminEpoch() internal view returns (uint256) { return getUint(KEY_ADMIN_EPOCH); } function _getAdmin(uint256 adminEpoch, uint256 index) internal view returns (address) { return getAddress(_getAdminKey(adminEpoch, index)); } function _getAdminCount(uint256 adminEpoch) internal view returns (uint256) { return getUint(_getAdminCountKey(adminEpoch)); } function _getAdminThreshold(uint256 adminEpoch) internal view returns (uint256) { return getUint(_getAdminThresholdKey(adminEpoch)); } function _getVoteCount(uint256 adminEpoch, bytes32 topic) internal view returns (uint256) { return getUint(_getAdminVoteCountsKey(adminEpoch, topic)); } function _hasVoted( uint256 adminEpoch, bytes32 topic, address account ) internal view returns (bool) { return getBool(_getAdminVotedKey(adminEpoch, topic, account)); } function _isAdmin(uint256 adminEpoch, address account) internal view returns (bool) { return getBool(_getIsAdminKey(adminEpoch, account)); } /***********\ |* Setters *| \***********/ function _setAdminEpoch(uint256 adminEpoch) internal { _setUint(KEY_ADMIN_EPOCH, adminEpoch); } function _setAdmin( uint256 adminEpoch, uint256 index, address account ) internal { _setAddress(_getAdminKey(adminEpoch, index), account); } function _setAdminCount(uint256 adminEpoch, uint256 adminCount) internal { _setUint(_getAdminCountKey(adminEpoch), adminCount); } function _setAdmins( uint256 adminEpoch, address[] memory accounts, uint256 threshold ) internal { uint256 adminLength = accounts.length; require(adminLength >= threshold, 'INV_ADMINS'); require(threshold > uint256(0), 'INV_ADMIN_THLD'); _setAdminThreshold(adminEpoch, threshold); _setAdminCount(adminEpoch, adminLength); for (uint256 i; i < adminLength; i++) { address account = accounts[i]; // Check that the account wasn't already set as an admin for this epoch. require(!_isAdmin(adminEpoch, account), 'DUP_ADMIN'); // Set this account as the i-th admin in this epoch (needed to we can clear topic votes in `onlyAdmin`). _setAdmin(adminEpoch, i, account); _setIsAdmin(adminEpoch, account, true); } } function _setAdminThreshold(uint256 adminEpoch, uint256 adminThreshold) internal { _setUint(_getAdminThresholdKey(adminEpoch), adminThreshold); } function _setVoteCount( uint256 adminEpoch, bytes32 topic, uint256 voteCount ) internal { _setUint(_getAdminVoteCountsKey(adminEpoch, topic), voteCount); } function _setHasVoted( uint256 adminEpoch, bytes32 topic, address account, bool voted ) internal { _setBool(_getAdminVotedKey(adminEpoch, topic, account), voted); } function _setIsAdmin( uint256 adminEpoch, address account, bool isAdmin ) internal { _setBool(_getIsAdminKey(adminEpoch, account), isAdmin); } } // Dependency file: src/AxelarGateway.sol // pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGateway } from 'src/interfaces/IAxelarGateway.sol'; // import { BurnableMintableCappedERC20 } from 'src/BurnableMintableCappedERC20.sol'; // import { AdminMultisigBase } from 'src/AdminMultisigBase.sol'; abstract contract AxelarGateway is IAxelarGateway, AdminMultisigBase { /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`. bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc); // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen'); bytes32 internal constant PREFIX_COMMAND_EXECUTED = keccak256('command-executed'); bytes32 internal constant PREFIX_TOKEN_ADDRESS = keccak256('token-address'); bytes32 internal constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen'); bytes32 internal constant SELECTOR_BURN_TOKEN = keccak256('burnToken'); bytes32 internal constant SELECTOR_DEPLOY_TOKEN = keccak256('deployToken'); bytes32 internal constant SELECTOR_MINT_TOKEN = keccak256('mintToken'); bytes32 internal constant SELECTOR_TRANSFER_OPERATORSHIP = keccak256('transferOperatorship'); bytes32 internal constant SELECTOR_TRANSFER_OWNERSHIP = keccak256('transferOwnership'); uint8 internal constant OLD_KEY_RETENTION = 16; modifier onlySelf() { require(msg.sender == address(this), 'NOT_SELF'); _; } /***********\ |* Getters *| \***********/ function allTokensFrozen() public view override returns (bool) { return getBool(KEY_ALL_TOKENS_FROZEN); } function implementation() public view override returns (address) { return getAddress(KEY_IMPLEMENTATION); } function tokenAddresses(string memory symbol) public view override returns (address) { return getAddress(_getTokenAddressKey(symbol)); } function tokenFrozen(string memory symbol) public view override returns (bool) { return getBool(_getFreezeTokenKey(symbol)); } function isCommandExecuted(bytes32 commandId) public view override returns (bool) { return getBool(_getIsCommandExecutedKey(commandId)); } /*******************\ |* Admin Functions *| \*******************/ function freezeToken(string memory symbol) external override onlyAdmin { _setBool(_getFreezeTokenKey(symbol), true); emit TokenFrozen(symbol); } function unfreezeToken(string memory symbol) external override onlyAdmin { _setBool(_getFreezeTokenKey(symbol), false); emit TokenUnfrozen(symbol); } function freezeAllTokens() external override onlyAdmin { _setBool(KEY_ALL_TOKENS_FROZEN, true); emit AllTokensFrozen(); } function unfreezeAllTokens() external override onlyAdmin { _setBool(KEY_ALL_TOKENS_FROZEN, false); emit AllTokensUnfrozen(); } function upgrade(address newImplementation, bytes calldata setupParams) external override onlyAdmin { emit Upgraded(newImplementation); // AUDIT: If `newImplementation.setup` performs `selfdestruct`, it will result in the loss of _this_ implementation (thereby losing the gateway) // if `upgrade` is entered within the context of _this_ implementation itself. (bool success, ) = newImplementation.delegatecall( abi.encodeWithSelector(IAxelarGateway.setup.selector, setupParams) ); require(success, 'SETUP_FAILED'); _setImplementation(newImplementation); } /**********************\ |* Internal Functions *| \**********************/ function _deployToken( string memory name, string memory symbol, uint8 decimals, uint256 cap ) internal { require(tokenAddresses(symbol) == address(0), 'TOKEN_EXIST'); bytes32 salt = keccak256(abi.encodePacked(symbol)); address token = address(new BurnableMintableCappedERC20{ salt: salt }(name, symbol, decimals, cap)); _setTokenAddress(symbol, token); emit TokenDeployed(symbol, token); } function _mintToken( string memory symbol, address account, uint256 amount ) internal { address tokenAddress = tokenAddresses(symbol); require(tokenAddress != address(0), 'TOKEN_NOT_EXIST'); BurnableMintableCappedERC20(tokenAddress).mint(account, amount); } function _burnToken(string memory symbol, bytes32 salt) internal { address tokenAddress = tokenAddresses(symbol); require(tokenAddress != address(0), 'TOKEN_NOT_EXIST'); BurnableMintableCappedERC20(tokenAddress).burn(salt); } /********************\ |* Pure Key Getters *| \********************/ function _getFreezeTokenKey(string memory symbol) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_TOKEN_FROZEN, symbol)); } function _getTokenAddressKey(string memory symbol) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_TOKEN_ADDRESS, symbol)); } function _getIsCommandExecutedKey(bytes32 commandId) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_COMMAND_EXECUTED, commandId)); } /********************\ |* Internal Getters *| \********************/ function _getChainID() internal view returns (uint256 id) { assembly { id := chainid() } } /********************\ |* Internal Setters *| \********************/ function _setTokenAddress(string memory symbol, address tokenAddr) internal { _setAddress(_getTokenAddressKey(symbol), tokenAddr); } function _setCommandExecuted(bytes32 commandId, bool executed) internal { _setBool(_getIsCommandExecutedKey(commandId), executed); } function _setImplementation(address newImplementation) internal { _setAddress(KEY_IMPLEMENTATION, newImplementation); } } // Root file: src/AxelarGatewayMultisig.sol pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGatewayMultisig } from 'src/interfaces/IAxelarGatewayMultisig.sol'; // import { ECDSA } from 'src/ECDSA.sol'; // import { AxelarGateway } from 'src/AxelarGateway.sol'; contract AxelarGatewayMultisig is IAxelarGatewayMultisig, AxelarGateway { // AUDIT: slot names should be prefixed with some standard string // AUDIT: constants should be literal and their derivation should be in comments bytes32 internal constant KEY_OWNER_EPOCH = keccak256('owner-epoch'); bytes32 internal constant PREFIX_OWNER = keccak256('owner'); bytes32 internal constant PREFIX_OWNER_COUNT = keccak256('owner-count'); bytes32 internal constant PREFIX_OWNER_THRESHOLD = keccak256('owner-threshold'); bytes32 internal constant PREFIX_IS_OWNER = keccak256('is-owner'); bytes32 internal constant KEY_OPERATOR_EPOCH = keccak256('operator-epoch'); bytes32 internal constant PREFIX_OPERATOR = keccak256('operator'); bytes32 internal constant PREFIX_OPERATOR_COUNT = keccak256('operator-count'); bytes32 internal constant PREFIX_OPERATOR_THRESHOLD = keccak256('operator-threshold'); bytes32 internal constant PREFIX_IS_OPERATOR = keccak256('is-operator'); function _containsDuplicates(address[] memory accounts) internal pure returns (bool) { uint256 count = accounts.length; for (uint256 i; i < count; ++i) { for (uint256 j = i + 1; j < count; ++j) { if (accounts[i] == accounts[j]) return true; } } return false; } /************************\ |* Owners Functionality *| \************************/ /********************\ |* Pure Key Getters *| \********************/ function _getOwnerKey(uint256 ownerEpoch, uint256 index) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OWNER, ownerEpoch, index)); } function _getOwnerCountKey(uint256 ownerEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OWNER_COUNT, ownerEpoch)); } function _getOwnerThresholdKey(uint256 ownerEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OWNER_THRESHOLD, ownerEpoch)); } function _getIsOwnerKey(uint256 ownerEpoch, address account) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_IS_OWNER, ownerEpoch, account)); } /***********\ |* Getters *| \***********/ function _ownerEpoch() internal view returns (uint256) { return getUint(KEY_OWNER_EPOCH); } function _getOwner(uint256 ownerEpoch, uint256 index) internal view returns (address) { return getAddress(_getOwnerKey(ownerEpoch, index)); } function _getOwnerCount(uint256 ownerEpoch) internal view returns (uint256) { return getUint(_getOwnerCountKey(ownerEpoch)); } function _getOwnerThreshold(uint256 ownerEpoch) internal view returns (uint256) { return getUint(_getOwnerThresholdKey(ownerEpoch)); } function _isOwner(uint256 ownerEpoch, address account) internal view returns (bool) { return getBool(_getIsOwnerKey(ownerEpoch, account)); } /// @dev Returns true if a sufficient quantity of `accounts` are owners in the same `ownerEpoch`, within the last `OLD_KEY_RETENTION + 1` owner epochs. function _areValidRecentOwners(address[] memory accounts) internal view returns (bool) { uint256 ownerEpoch = _ownerEpoch(); uint256 recentEpochs = OLD_KEY_RETENTION + uint256(1); uint256 lowerBoundOwnerEpoch = ownerEpoch > recentEpochs ? ownerEpoch - recentEpochs : uint256(0); while (ownerEpoch > lowerBoundOwnerEpoch) { if (_areValidOwnersInEpoch(ownerEpoch--, accounts)) return true; } return false; } /// @dev Returns true if a sufficient quantity of `accounts` are owners in the `ownerEpoch`. function _areValidOwnersInEpoch(uint256 ownerEpoch, address[] memory accounts) internal view returns (bool) { if (_containsDuplicates(accounts)) return false; uint256 threshold = _getOwnerThreshold(ownerEpoch); uint256 validSignerCount; for (uint256 i; i < accounts.length; i++) { if (_isOwner(ownerEpoch, accounts[i]) && ++validSignerCount >= threshold) return true; } return false; } /// @dev Returns the array of owners within the current `ownerEpoch`. function owners() public view override returns (address[] memory results) { uint256 ownerEpoch = _ownerEpoch(); uint256 ownerCount = _getOwnerCount(ownerEpoch); results = new address[](ownerCount); for (uint256 i; i < ownerCount; i++) { results[i] = _getOwner(ownerEpoch, i); } } /***********\ |* Setters *| \***********/ function _setOwnerEpoch(uint256 ownerEpoch) internal { _setUint(KEY_OWNER_EPOCH, ownerEpoch); } function _setOwner( uint256 ownerEpoch, uint256 index, address account ) internal { require(account != address(0), 'ZERO_ADDR'); _setAddress(_getOwnerKey(ownerEpoch, index), account); } function _setOwnerCount(uint256 ownerEpoch, uint256 ownerCount) internal { _setUint(_getOwnerCountKey(ownerEpoch), ownerCount); } function _setOwners( uint256 ownerEpoch, address[] memory accounts, uint256 threshold ) internal { uint256 accountLength = accounts.length; require(accountLength >= threshold, 'INV_OWNERS'); require(threshold > uint256(0), 'INV_OWNER_THLD'); _setOwnerThreshold(ownerEpoch, threshold); _setOwnerCount(ownerEpoch, accountLength); for (uint256 i; i < accountLength; i++) { address account = accounts[i]; // Check that the account wasn't already set as an owner for this ownerEpoch. require(!_isOwner(ownerEpoch, account), 'DUP_OWNER'); // Set this account as the i-th owner in this ownerEpoch (needed to we can get all the owners for `owners`). _setOwner(ownerEpoch, i, account); _setIsOwner(ownerEpoch, account, true); } } function _setOwnerThreshold(uint256 ownerEpoch, uint256 ownerThreshold) internal { _setUint(_getOwnerThresholdKey(ownerEpoch), ownerThreshold); } function _setIsOwner( uint256 ownerEpoch, address account, bool isOwner ) internal { _setBool(_getIsOwnerKey(ownerEpoch, account), isOwner); } /**************************\ |* Operator Functionality *| \**************************/ /********************\ |* Pure Key Getters *| \********************/ function _getOperatorKey(uint256 operatorEpoch, uint256 index) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OPERATOR, operatorEpoch, index)); } function _getOperatorCountKey(uint256 operatorEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OPERATOR_COUNT, operatorEpoch)); } function _getOperatorThresholdKey(uint256 operatorEpoch) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_OPERATOR_THRESHOLD, operatorEpoch)); } function _getIsOperatorKey(uint256 operatorEpoch, address account) internal pure returns (bytes32) { return keccak256(abi.encodePacked(PREFIX_IS_OPERATOR, operatorEpoch, account)); } /***********\ |* Getters *| \***********/ function _operatorEpoch() internal view returns (uint256) { return getUint(KEY_OPERATOR_EPOCH); } function _getOperator(uint256 operatorEpoch, uint256 index) internal view returns (address) { return getAddress(_getOperatorKey(operatorEpoch, index)); } function _getOperatorCount(uint256 operatorEpoch) internal view returns (uint256) { return getUint(_getOperatorCountKey(operatorEpoch)); } function _getOperatorThreshold(uint256 operatorEpoch) internal view returns (uint256) { return getUint(_getOperatorThresholdKey(operatorEpoch)); } function _isOperator(uint256 operatorEpoch, address account) internal view returns (bool) { return getBool(_getIsOperatorKey(operatorEpoch, account)); } /// @dev Returns true if a sufficient quantity of `accounts` are operator in the same `operatorEpoch`, within the last `OLD_KEY_RETENTION + 1` operator epochs. function _areValidRecentOperators(address[] memory accounts) internal view returns (bool) { uint256 operatorEpoch = _operatorEpoch(); uint256 recentEpochs = OLD_KEY_RETENTION + uint256(1); uint256 lowerBoundOperatorEpoch = operatorEpoch > recentEpochs ? operatorEpoch - recentEpochs : uint256(0); while (operatorEpoch > lowerBoundOperatorEpoch) { if (_areValidOperatorsInEpoch(operatorEpoch--, accounts)) return true; } return false; } /// @dev Returns true if a sufficient quantity of `accounts` are operator in the `operatorEpoch`. function _areValidOperatorsInEpoch(uint256 operatorEpoch, address[] memory accounts) internal view returns (bool) { if (_containsDuplicates(accounts)) return false; uint256 threshold = _getOperatorThreshold(operatorEpoch); uint256 validSignerCount; for (uint256 i; i < accounts.length; i++) { if (_isOperator(operatorEpoch, accounts[i]) && ++validSignerCount >= threshold) return true; } return false; } /// @dev Returns the array of operators within the current `operatorEpoch`. function operators() public view override returns (address[] memory results) { uint256 operatorEpoch = _operatorEpoch(); uint256 operatorCount = _getOperatorCount(operatorEpoch); results = new address[](operatorCount); for (uint256 i; i < operatorCount; i++) { results[i] = _getOperator(operatorEpoch, i); } } /***********\ |* Setters *| \***********/ function _setOperatorEpoch(uint256 operatorEpoch) internal { _setUint(KEY_OPERATOR_EPOCH, operatorEpoch); } function _setOperator( uint256 operatorEpoch, uint256 index, address account ) internal { // AUDIT: Should have `require(account != address(0), 'ZERO_ADDR');` like Singlesig? _setAddress(_getOperatorKey(operatorEpoch, index), account); } function _setOperatorCount(uint256 operatorEpoch, uint256 operatorCount) internal { _setUint(_getOperatorCountKey(operatorEpoch), operatorCount); } function _setOperators( uint256 operatorEpoch, address[] memory accounts, uint256 threshold ) internal { uint256 accountLength = accounts.length; require(accountLength >= threshold, 'INV_OPERATORS'); require(threshold > uint256(0), 'INV_OPERATOR_THLD'); _setOperatorThreshold(operatorEpoch, threshold); _setOperatorCount(operatorEpoch, accountLength); for (uint256 i; i < accountLength; i++) { address account = accounts[i]; // Check that the account wasn't already set as an operator for this operatorEpoch. require(!_isOperator(operatorEpoch, account), 'DUP_OPERATOR'); // Set this account as the i-th operator in this operatorEpoch (needed to we can get all the operators for `operators`). _setOperator(operatorEpoch, i, account); _setIsOperator(operatorEpoch, account, true); } } function _setOperatorThreshold(uint256 operatorEpoch, uint256 operatorThreshold) internal { _setUint(_getOperatorThresholdKey(operatorEpoch), operatorThreshold); } function _setIsOperator( uint256 operatorEpoch, address account, bool isOperator ) internal { _setBool(_getIsOperatorKey(operatorEpoch, account), isOperator); } /**********************\ |* Self Functionality *| \**********************/ function deployToken(bytes calldata params) external onlySelf { (string memory name, string memory symbol, uint8 decimals, uint256 cap) = abi.decode( params, (string, string, uint8, uint256) ); _deployToken(name, symbol, decimals, cap); } function mintToken(bytes calldata params) external onlySelf { (string memory symbol, address account, uint256 amount) = abi.decode(params, (string, address, uint256)); _mintToken(symbol, account, amount); } function burnToken(bytes calldata params) external onlySelf { (string memory symbol, bytes32 salt) = abi.decode(params, (string, bytes32)); _burnToken(symbol, salt); } function transferOwnership(bytes calldata params) external onlySelf { (address[] memory newOwners, uint256 newThreshold) = abi.decode(params, (address[], uint256)); uint256 ownerEpoch = _ownerEpoch(); emit OwnershipTransferred(owners(), _getOwnerThreshold(ownerEpoch), newOwners, newThreshold); _setOwnerEpoch(++ownerEpoch); _setOwners(ownerEpoch, newOwners, newThreshold); } function transferOperatorship(bytes calldata params) external onlySelf { (address[] memory newOperators, uint256 newThreshold) = abi.decode(params, (address[], uint256)); uint256 ownerEpoch = _ownerEpoch(); emit OperatorshipTransferred(operators(), _getOperatorThreshold(ownerEpoch), newOperators, newThreshold); uint256 operatorEpoch = _operatorEpoch(); _setOperatorEpoch(++operatorEpoch); _setOperators(operatorEpoch, newOperators, newThreshold); } /**************************\ |* External Functionality *| \**************************/ function setup(bytes calldata params) external override { // Prevent setup from being called on a non-proxy (the implementation). require(implementation() != address(0), 'NOT_PROXY'); ( address[] memory adminAddresses, uint256 adminThreshold, address[] memory ownerAddresses, uint256 ownerThreshold, address[] memory operatorAddresses, uint256 operatorThreshold ) = abi.decode(params, (address[], uint256, address[], uint256, address[], uint256)); uint256 adminEpoch = _adminEpoch() + uint256(1); _setAdminEpoch(adminEpoch); _setAdmins(adminEpoch, adminAddresses, adminThreshold); uint256 ownerEpoch = _ownerEpoch() + uint256(1); _setOwnerEpoch(ownerEpoch); _setOwners(ownerEpoch, ownerAddresses, ownerThreshold); uint256 operatorEpoch = _operatorEpoch() + uint256(1); _setOperatorEpoch(operatorEpoch); _setOperators(operatorEpoch, operatorAddresses, operatorThreshold); emit OwnershipTransferred(new address[](uint256(0)), uint256(0), ownerAddresses, ownerThreshold); emit OperatorshipTransferred(new address[](uint256(0)), uint256(0), operatorAddresses, operatorThreshold); } function execute(bytes calldata input) external override { (bytes memory data, bytes[] memory signatures) = abi.decode(input, (bytes, bytes[])); _execute(data, signatures); } function _execute(bytes memory data, bytes[] memory signatures) internal { uint256 signatureCount = signatures.length; address[] memory signers = new address[](signatureCount); for (uint256 i; i < signatureCount; i++) { signers[i] = ECDSA.recover(ECDSA.toEthSignedMessageHash(keccak256(data)), signatures[i]); } (uint256 chainId, bytes32[] memory commandIds, string[] memory commands, bytes[] memory params) = abi.decode( data, (uint256, bytes32[], string[], bytes[]) ); require(chainId == _getChainID(), 'INV_CHAIN'); uint256 commandsLength = commandIds.length; require(commandsLength == commands.length && commandsLength == params.length, 'INV_CMDS'); bool areValidCurrentOwners = _areValidOwnersInEpoch(_ownerEpoch(), signers); bool areValidRecentOwners = areValidCurrentOwners || _areValidRecentOwners(signers); bool areValidRecentOperators = _areValidRecentOperators(signers); for (uint256 i; i < commandsLength; i++) { bytes32 commandId = commandIds[i]; if (isCommandExecuted(commandId)) continue; /* Ignore if duplicate commandId received */ bytes4 commandSelector; bytes32 commandHash = keccak256(abi.encodePacked(commands[i])); if (commandHash == SELECTOR_DEPLOY_TOKEN) { if (!areValidRecentOwners) continue; commandSelector = AxelarGatewayMultisig.deployToken.selector; } else if (commandHash == SELECTOR_MINT_TOKEN) { if (!areValidRecentOperators && !areValidRecentOwners) continue; commandSelector = AxelarGatewayMultisig.mintToken.selector; } else if (commandHash == SELECTOR_BURN_TOKEN) { if (!areValidRecentOperators && !areValidRecentOwners) continue; commandSelector = AxelarGatewayMultisig.burnToken.selector; } else if (commandHash == SELECTOR_TRANSFER_OWNERSHIP) { if (!areValidCurrentOwners) continue; commandSelector = AxelarGatewayMultisig.transferOwnership.selector; } else if (commandHash == SELECTOR_TRANSFER_OPERATORSHIP) { if (!areValidCurrentOwners) continue; commandSelector = AxelarGatewayMultisig.transferOperatorship.selector; } else { continue; /* Ignore if unknown command received */ } // Prevent a re-entrancy from executing this command before it can be marked as successful. _setCommandExecuted(commandId, true); (bool success, ) = address(this).call(abi.encodeWithSelector(commandSelector, params[i])); _setCommandExecuted(commandId, success); if (success) { emit Executed(commandId); } } } }
// Dependency file: src/EternalStorage.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; /** * @title EternalStorage * @dev This contract holds all the necessary state variables to carry out the storage of any contract. */ contract EternalStorage { mapping(bytes32 => uint256) private _uintStorage; mapping(bytes32 => string) private _stringStorage; mapping(bytes32 => address) private _addressStorage; mapping(bytes32 => bytes) private _bytesStorage; mapping(bytes32 => bool) private _boolStorage; mapping(bytes32 => int256) private _intStorage; // *** Getter Methods *** function getUint(bytes32 key) public view returns (uint256) { return _uintStorage[key]; } function getString(bytes32 key) public view returns (string memory) { return _stringStorage[key]; } function getAddress(bytes32 key) public view returns (address) { return _addressStorage[key]; } function getBytes(bytes32 key) public view returns (bytes memory) { return _bytesStorage[key]; } function getBool(bytes32 key) public view returns (bool) { return _boolStorage[key]; } function getInt(bytes32 key) public view returns (int256) { return _intStorage[key]; } // *** Setter Methods *** function _setUint(bytes32 key, uint256 value) internal { _uintStorage[key] = value; } function _setString(bytes32 key, string memory value) internal { _stringStorage[key] = value; } function _setAddress(bytes32 key, address value) internal { _addressStorage[key] = value; } function _setBytes(bytes32 key, bytes memory value) internal { _bytesStorage[key] = value; } function _setBool(bytes32 key, bool value) internal { _boolStorage[key] = value; } function _setInt(bytes32 key, int256 value) internal { _intStorage[key] = value; } // *** Delete Methods *** function _deleteUint(bytes32 key) internal { delete _uintStorage[key]; } function _deleteString(bytes32 key) internal { delete _stringStorage[key]; } function _deleteAddress(bytes32 key) internal { delete _addressStorage[key]; } function _deleteBytes(bytes32 key) internal { delete _bytesStorage[key]; } function _deleteBool(bytes32 key) internal { delete _boolStorage[key]; } function _deleteInt(bytes32 key) internal { delete _intStorage[key]; } } // Root file: src/AxelarGatewayProxy.sol pragma solidity >=0.8.0 <0.9.0; // import { EternalStorage } from 'src/EternalStorage.sol'; contract AxelarGatewayProxy is EternalStorage { /// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`. bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc); fallback() external payable { address implementation = getAddress(KEY_IMPLEMENTATION); assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } receive() external payable { revert('NO_ETHER'); } }
// Dependency file: src/interfaces/IERC20.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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 `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @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); } // Dependency file: src/Context.sol // pragma solidity >=0.8.0 <0.9.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 GSN 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 payable) { return payable(msg.sender); } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // Dependency file: src/ERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { IERC20 } from 'src/interfaces/IERC20.sol'; // import { Context } from 'src/Context.sol'; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { mapping(address => uint256) public override balanceOf; mapping(address => mapping(address => uint256)) public override allowance; uint256 public override totalSupply; string public name; string public symbol; uint8 public immutable decimals; /** * @dev Sets the values for {name}, {symbol}, and {decimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { name = name_; symbol = symbol_; decimals = decimals_; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), allowance[sender][_msgSender()] - amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] - subtractedValue); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), 'ZERO_ADDR'); require(recipient != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(sender, recipient, amount); balanceOf[sender] -= amount; balanceOf[recipient] += amount; emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(address(0), account, amount); totalSupply += amount; balanceOf[account] += amount; emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(account, address(0), amount); balanceOf[account] -= amount; totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), 'ZERO_ADDR'); require(spender != address(0), 'ZERO_ADDR'); allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } // Dependency file: src/Ownable.sol // pragma solidity >=0.8.0 <0.9.0; abstract contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } modifier onlyOwner() { require(owner == msg.sender, 'NOT_OWNER'); _; } function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), 'ZERO_ADDR'); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } } // Dependency file: src/Burner.sol // pragma solidity >=0.8.0 <0.9.0; // import { BurnableMintableCappedERC20 } from 'src/BurnableMintableCappedERC20.sol'; contract Burner { constructor(address tokenAddress, bytes32 salt) { BurnableMintableCappedERC20(tokenAddress).burn(salt); selfdestruct(payable(address(0))); } } // Dependency file: src/EternalStorage.sol // pragma solidity >=0.8.0 <0.9.0; /** * @title EternalStorage * @dev This contract holds all the necessary state variables to carry out the storage of any contract. */ contract EternalStorage { mapping(bytes32 => uint256) private _uintStorage; mapping(bytes32 => string) private _stringStorage; mapping(bytes32 => address) private _addressStorage; mapping(bytes32 => bytes) private _bytesStorage; mapping(bytes32 => bool) private _boolStorage; mapping(bytes32 => int256) private _intStorage; // *** Getter Methods *** function getUint(bytes32 key) public view returns (uint256) { return _uintStorage[key]; } function getString(bytes32 key) public view returns (string memory) { return _stringStorage[key]; } function getAddress(bytes32 key) public view returns (address) { return _addressStorage[key]; } function getBytes(bytes32 key) public view returns (bytes memory) { return _bytesStorage[key]; } function getBool(bytes32 key) public view returns (bool) { return _boolStorage[key]; } function getInt(bytes32 key) public view returns (int256) { return _intStorage[key]; } // *** Setter Methods *** function _setUint(bytes32 key, uint256 value) internal { _uintStorage[key] = value; } function _setString(bytes32 key, string memory value) internal { _stringStorage[key] = value; } function _setAddress(bytes32 key, address value) internal { _addressStorage[key] = value; } function _setBytes(bytes32 key, bytes memory value) internal { _bytesStorage[key] = value; } function _setBool(bytes32 key, bool value) internal { _boolStorage[key] = value; } function _setInt(bytes32 key, int256 value) internal { _intStorage[key] = value; } // *** Delete Methods *** function _deleteUint(bytes32 key) internal { delete _uintStorage[key]; } function _deleteString(bytes32 key) internal { delete _stringStorage[key]; } function _deleteAddress(bytes32 key) internal { delete _addressStorage[key]; } function _deleteBytes(bytes32 key) internal { delete _bytesStorage[key]; } function _deleteBool(bytes32 key) internal { delete _boolStorage[key]; } function _deleteInt(bytes32 key) internal { delete _intStorage[key]; } } // Root file: src/BurnableMintableCappedERC20.sol pragma solidity >=0.8.0 <0.9.0; // import { ERC20 } from 'src/ERC20.sol'; // import { Ownable } from 'src/Ownable.sol'; // import { Burner } from 'src/Burner.sol'; // import { EternalStorage } from 'src/EternalStorage.sol'; contract BurnableMintableCappedERC20 is ERC20, Ownable { uint256 public cap; bytes32 private constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen'); bytes32 private constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen'); event Frozen(address indexed owner); event Unfrozen(address indexed owner); constructor( string memory name, string memory symbol, uint8 decimals, uint256 capacity ) ERC20(name, symbol, decimals) Ownable() { cap = capacity; } function depositAddress(bytes32 salt) public view returns (address) { // This would be easier, cheaper, simpler, and result in globally consistent deposit addresses for any salt (all chains, all tokens). // return address(uint160(uint256(keccak256(abi.encodePacked(bytes32(0x000000000000000000000000000000000000000000000000000000000000dead), salt))))); /* Convert a hash which is bytes32 to an address which is 20-byte long according to https://docs.soliditylang.org/en/v0.8.1/control-structures.html?highlight=create2#salted-contract-creations-create2 */ return address( uint160( uint256( keccak256( abi.encodePacked( bytes1(0xff), owner, salt, keccak256(abi.encodePacked(type(Burner).creationCode, abi.encode(address(this)), salt)) ) ) ) ) ); } function mint(address account, uint256 amount) public onlyOwner { uint256 capacity = cap; require(capacity == 0 || totalSupply + amount <= capacity, 'CAP_EXCEEDED'); _mint(account, amount); } function burn(bytes32 salt) public onlyOwner { address account = depositAddress(salt); _burn(account, balanceOf[account]); } function _beforeTokenTransfer( address, address, uint256 ) internal view override { require(!EternalStorage(owner).getBool(KEY_ALL_TOKENS_FROZEN), 'IS_FROZEN'); require(!EternalStorage(owner).getBool(keccak256(abi.encodePacked(PREFIX_TOKEN_FROZEN, symbol))), 'IS_FROZEN'); } }
// Dependency file: src/interfaces/IERC20.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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 `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @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); } // Dependency file: src/Context.sol // pragma solidity >=0.8.0 <0.9.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 GSN 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 payable) { return payable(msg.sender); } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // Dependency file: src/ERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { IERC20 } from 'src/interfaces/IERC20.sol'; // import { Context } from 'src/Context.sol'; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { mapping(address => uint256) public override balanceOf; mapping(address => mapping(address => uint256)) public override allowance; uint256 public override totalSupply; string public name; string public symbol; uint8 public immutable decimals; /** * @dev Sets the values for {name}, {symbol}, and {decimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { name = name_; symbol = symbol_; decimals = decimals_; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), allowance[sender][_msgSender()] - amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] - subtractedValue); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), 'ZERO_ADDR'); require(recipient != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(sender, recipient, amount); balanceOf[sender] -= amount; balanceOf[recipient] += amount; emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(address(0), account, amount); totalSupply += amount; balanceOf[account] += amount; emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(account, address(0), amount); balanceOf[account] -= amount; totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), 'ZERO_ADDR'); require(spender != address(0), 'ZERO_ADDR'); allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } // Dependency file: src/Ownable.sol // pragma solidity >=0.8.0 <0.9.0; abstract contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } modifier onlyOwner() { require(owner == msg.sender, 'NOT_OWNER'); _; } function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), 'ZERO_ADDR'); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } } // Dependency file: src/EternalStorage.sol // pragma solidity >=0.8.0 <0.9.0; /** * @title EternalStorage * @dev This contract holds all the necessary state variables to carry out the storage of any contract. */ contract EternalStorage { mapping(bytes32 => uint256) private _uintStorage; mapping(bytes32 => string) private _stringStorage; mapping(bytes32 => address) private _addressStorage; mapping(bytes32 => bytes) private _bytesStorage; mapping(bytes32 => bool) private _boolStorage; mapping(bytes32 => int256) private _intStorage; // *** Getter Methods *** function getUint(bytes32 key) public view returns (uint256) { return _uintStorage[key]; } function getString(bytes32 key) public view returns (string memory) { return _stringStorage[key]; } function getAddress(bytes32 key) public view returns (address) { return _addressStorage[key]; } function getBytes(bytes32 key) public view returns (bytes memory) { return _bytesStorage[key]; } function getBool(bytes32 key) public view returns (bool) { return _boolStorage[key]; } function getInt(bytes32 key) public view returns (int256) { return _intStorage[key]; } // *** Setter Methods *** function _setUint(bytes32 key, uint256 value) internal { _uintStorage[key] = value; } function _setString(bytes32 key, string memory value) internal { _stringStorage[key] = value; } function _setAddress(bytes32 key, address value) internal { _addressStorage[key] = value; } function _setBytes(bytes32 key, bytes memory value) internal { _bytesStorage[key] = value; } function _setBool(bytes32 key, bool value) internal { _boolStorage[key] = value; } function _setInt(bytes32 key, int256 value) internal { _intStorage[key] = value; } // *** Delete Methods *** function _deleteUint(bytes32 key) internal { delete _uintStorage[key]; } function _deleteString(bytes32 key) internal { delete _stringStorage[key]; } function _deleteAddress(bytes32 key) internal { delete _addressStorage[key]; } function _deleteBytes(bytes32 key) internal { delete _bytesStorage[key]; } function _deleteBool(bytes32 key) internal { delete _boolStorage[key]; } function _deleteInt(bytes32 key) internal { delete _intStorage[key]; } } // Dependency file: src/BurnableMintableCappedERC20.sol // pragma solidity >=0.8.0 <0.9.0; // import { ERC20 } from 'src/ERC20.sol'; // import { Ownable } from 'src/Ownable.sol'; // import { Burner } from 'src/Burner.sol'; // import { EternalStorage } from 'src/EternalStorage.sol'; contract BurnableMintableCappedERC20 is ERC20, Ownable { uint256 public cap; bytes32 private constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen'); bytes32 private constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen'); event Frozen(address indexed owner); event Unfrozen(address indexed owner); constructor( string memory name, string memory symbol, uint8 decimals, uint256 capacity ) ERC20(name, symbol, decimals) Ownable() { cap = capacity; } function depositAddress(bytes32 salt) public view returns (address) { // This would be easier, cheaper, simpler, and result in globally consistent deposit addresses for any salt (all chains, all tokens). // return address(uint160(uint256(keccak256(abi.encodePacked(bytes32(0x000000000000000000000000000000000000000000000000000000000000dead), salt))))); /* Convert a hash which is bytes32 to an address which is 20-byte long according to https://docs.soliditylang.org/en/v0.8.1/control-structures.html?highlight=create2#salted-contract-creations-create2 */ return address( uint160( uint256( keccak256( abi.encodePacked( bytes1(0xff), owner, salt, keccak256(abi.encodePacked(type(Burner).creationCode, abi.encode(address(this)), salt)) ) ) ) ) ); } function mint(address account, uint256 amount) public onlyOwner { uint256 capacity = cap; require(capacity == 0 || totalSupply + amount <= capacity, 'CAP_EXCEEDED'); _mint(account, amount); } function burn(bytes32 salt) public onlyOwner { address account = depositAddress(salt); _burn(account, balanceOf[account]); } function _beforeTokenTransfer( address, address, uint256 ) internal view override { require(!EternalStorage(owner).getBool(KEY_ALL_TOKENS_FROZEN), 'IS_FROZEN'); require(!EternalStorage(owner).getBool(keccak256(abi.encodePacked(PREFIX_TOKEN_FROZEN, symbol))), 'IS_FROZEN'); } } // Root file: src/Burner.sol pragma solidity >=0.8.0 <0.9.0; // import { BurnableMintableCappedERC20 } from 'src/BurnableMintableCappedERC20.sol'; contract Burner { constructor(address tokenAddress, bytes32 salt) { BurnableMintableCappedERC20(tokenAddress).burn(salt); selfdestruct(payable(address(0))); } }
// Root file: src/Context.sol // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.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 GSN 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 payable) { return payable(msg.sender); } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// Root file: src/ECDSA.sol // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address signer) { // Check the signature length require(signature.length == 65, 'INV_LEN'); // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, 'INV_S'); require(v == 27 || v == 28, 'INV_V'); // If the signature is valid (and not malleable), return the signer address require((signer = ecrecover(hash, v, r, s)) != address(0), 'INV_SIG'); } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked('\x19Ethereum Signed Message:\n32', hash)); } }
// Dependency file: src/interfaces/IERC20.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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 `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @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); } // Dependency file: src/Context.sol // pragma solidity >=0.8.0 <0.9.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 GSN 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 payable) { return payable(msg.sender); } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // Root file: src/ERC20.sol pragma solidity >=0.8.0 <0.9.0; // import { IERC20 } from 'src/interfaces/IERC20.sol'; // import { Context } from 'src/Context.sol'; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { mapping(address => uint256) public override balanceOf; mapping(address => mapping(address => uint256)) public override allowance; uint256 public override totalSupply; string public name; string public symbol; uint8 public immutable decimals; /** * @dev Sets the values for {name}, {symbol}, and {decimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { name = name_; symbol = symbol_; decimals = decimals_; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), allowance[sender][_msgSender()] - amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, allowance[_msgSender()][spender] - subtractedValue); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), 'ZERO_ADDR'); require(recipient != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(sender, recipient, amount); balanceOf[sender] -= amount; balanceOf[recipient] += amount; emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(address(0), account, amount); totalSupply += amount; balanceOf[account] += amount; emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ZERO_ADDR'); _beforeTokenTransfer(account, address(0), amount); balanceOf[account] -= amount; totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), 'ZERO_ADDR'); require(spender != address(0), 'ZERO_ADDR'); allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// Root file: src/EternalStorage.sol // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; /** * @title EternalStorage * @dev This contract holds all the necessary state variables to carry out the storage of any contract. */ contract EternalStorage { mapping(bytes32 => uint256) private _uintStorage; mapping(bytes32 => string) private _stringStorage; mapping(bytes32 => address) private _addressStorage; mapping(bytes32 => bytes) private _bytesStorage; mapping(bytes32 => bool) private _boolStorage; mapping(bytes32 => int256) private _intStorage; // *** Getter Methods *** function getUint(bytes32 key) public view returns (uint256) { return _uintStorage[key]; } function getString(bytes32 key) public view returns (string memory) { return _stringStorage[key]; } function getAddress(bytes32 key) public view returns (address) { return _addressStorage[key]; } function getBytes(bytes32 key) public view returns (bytes memory) { return _bytesStorage[key]; } function getBool(bytes32 key) public view returns (bool) { return _boolStorage[key]; } function getInt(bytes32 key) public view returns (int256) { return _intStorage[key]; } // *** Setter Methods *** function _setUint(bytes32 key, uint256 value) internal { _uintStorage[key] = value; } function _setString(bytes32 key, string memory value) internal { _stringStorage[key] = value; } function _setAddress(bytes32 key, address value) internal { _addressStorage[key] = value; } function _setBytes(bytes32 key, bytes memory value) internal { _bytesStorage[key] = value; } function _setBool(bytes32 key, bool value) internal { _boolStorage[key] = value; } function _setInt(bytes32 key, int256 value) internal { _intStorage[key] = value; } // *** Delete Methods *** function _deleteUint(bytes32 key) internal { delete _uintStorage[key]; } function _deleteString(bytes32 key) internal { delete _stringStorage[key]; } function _deleteAddress(bytes32 key) internal { delete _addressStorage[key]; } function _deleteBytes(bytes32 key) internal { delete _bytesStorage[key]; } function _deleteBool(bytes32 key) internal { delete _boolStorage[key]; } function _deleteInt(bytes32 key) internal { delete _intStorage[key]; } }
// Root file: src/interfaces/IAxelarGateway.sol // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; interface IAxelarGateway { /**********\ |* Events *| \**********/ event Executed(bytes32 indexed commandId); event TokenDeployed(string symbol, address tokenAddresses); event TokenFrozen(string indexed symbol); event TokenUnfrozen(string indexed symbol); event AllTokensFrozen(); event AllTokensUnfrozen(); event AccountBlacklisted(address indexed account); event AccountWhitelisted(address indexed account); event Upgraded(address indexed implementation); /***********\ |* Getters *| \***********/ function allTokensFrozen() external view returns (bool); function implementation() external view returns (address); function tokenAddresses(string memory symbol) external view returns (address); function tokenFrozen(string memory symbol) external view returns (bool); function isCommandExecuted(bytes32 commandId) external view returns (bool); /*******************\ |* Admin Functions *| \*******************/ function freezeToken(string memory symbol) external; function unfreezeToken(string memory symbol) external; function freezeAllTokens() external; function unfreezeAllTokens() external; function upgrade(address newImplementation, bytes calldata setupParams) external; /**********************\ |* External Functions *| \**********************/ function setup(bytes calldata params) external; function execute(bytes calldata input) external; }
// Dependency file: src/interfaces/IAxelarGateway.sol // SPDX-License-Identifier: MIT // pragma solidity >=0.8.0 <0.9.0; interface IAxelarGateway { /**********\ |* Events *| \**********/ event Executed(bytes32 indexed commandId); event TokenDeployed(string symbol, address tokenAddresses); event TokenFrozen(string indexed symbol); event TokenUnfrozen(string indexed symbol); event AllTokensFrozen(); event AllTokensUnfrozen(); event AccountBlacklisted(address indexed account); event AccountWhitelisted(address indexed account); event Upgraded(address indexed implementation); /***********\ |* Getters *| \***********/ function allTokensFrozen() external view returns (bool); function implementation() external view returns (address); function tokenAddresses(string memory symbol) external view returns (address); function tokenFrozen(string memory symbol) external view returns (bool); function isCommandExecuted(bytes32 commandId) external view returns (bool); /*******************\ |* Admin Functions *| \*******************/ function freezeToken(string memory symbol) external; function unfreezeToken(string memory symbol) external; function freezeAllTokens() external; function unfreezeAllTokens() external; function upgrade(address newImplementation, bytes calldata setupParams) external; /**********************\ |* External Functions *| \**********************/ function setup(bytes calldata params) external; function execute(bytes calldata input) external; } // Root file: src/interfaces/IAxelarGatewayMultisig.sol pragma solidity >=0.8.0 <0.9.0; // import { IAxelarGateway } from 'src/interfaces/IAxelarGateway.sol'; interface IAxelarGatewayMultisig is IAxelarGateway { event OwnershipTransferred(address[] preOwners, uint256 prevThreshold, address[] newOwners, uint256 newThreshold); event OperatorshipTransferred(address[] preOperators, uint256 prevThreshold, address[] newOperators, uint256 newThreshold); function owners() external view returns (address[] memory); function operators() external view returns (address[] memory); }
// Root file: src/interfaces/IERC20.sol // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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 `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @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); }
// Root file: src/Ownable.sol // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; abstract contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } modifier onlyOwner() { require(owner == msg.sender, 'NOT_OWNER'); _; } function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), 'ZERO_ADDR'); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
[{"inputs":[{"internalType":"bytes","name":"params","type":"bytes"}],"stateMutability":"nonpayable","type":"constructor"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getBool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getBytes","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getInt","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"getUint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"params","type":"bytes"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
608060405234801561001057600080fd5b5060405161541638038061541683398101604081905261002f91610201565b600060405161003d906101ae565b604051809103906000f080158015610059573d6000803e3d6000fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60005260026020527f11141f466c69fd409e1990e063b49cd6d61ed2ecff27a2e402e259ca6b9a01a380546001600160a01b0319166001600160a01b03831617905590506000816001600160a01b0316639ded06df60e01b846040516024016100e491906102ad565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161012291906102e0565b600060405180830381855af49150503d806000811461015d576040519150601f19603f3d011682016040523d82523d6000602084013e610162565b606091505b50509050806101a65760405162461bcd60e51b815260206004820152600c60248201526b14d155155417d1905253115160a21b604482015260640160405180910390fd5b5050506102fc565b614c56806107c083390190565b634e487b7160e01b600052604160045260246000fd5b60005b838110156101ec5781810151838201526020016101d4565b838111156101fb576000848401525b50505050565b60006020828403121561021357600080fd5b81516001600160401b038082111561022a57600080fd5b818401915084601f83011261023e57600080fd5b815181811115610250576102506101bb565b604051601f8201601f19908116603f01168101908382118183101715610278576102786101bb565b8160405282815287602084870101111561029157600080fd5b6102a28360208301602088016101d1565b979650505050505050565b60208152600082518060208401526102cc8160408501602087016101d1565b601f01601f19169190910160400192915050565b600082516102f28184602087016101d1565b9190910192915050565b6104b58061030b6000396000f3fe6080604052600436106100745760003560e01c80639ded06df1161004e5780639ded06df146101ea578063bd02d0f51461020b578063c031a18014610246578063dc97d96214610266576100b0565b806321f8a7211461012a5780637ae1cfca1461017d578063986e791a146101bd576100b0565b366100b05760405162461bcd60e51b81526020600482015260086024820152672727afa2aa2422a960c11b604482015260640160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc600090815260026020527f11141f466c69fd409e1990e063b49cd6d61ed2ecff27a2e402e259ca6b9a01a3546001600160a01b03169036908037600080366000845af43d6000803e808015610125573d6000f35b3d6000fd5b34801561013657600080fd5b50610160610145366004610352565b6000908152600260205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561018957600080fd5b506101ad610198366004610352565b60009081526004602052604090205460ff1690565b6040519015158152602001610174565b3480156101c957600080fd5b506101dd6101d8366004610352565b610293565b60405161017491906103b8565b3480156101f657600080fd5b506102096102053660046103d2565b5050565b005b34801561021757600080fd5b50610238610226366004610352565b60009081526020819052604090205490565b604051908152602001610174565b34801561025257600080fd5b506101dd610261366004610352565b610335565b34801561027257600080fd5b50610238610281366004610352565b60009081526005602052604090205490565b60008181526001602052604090208054606091906102b090610444565b80601f01602080910402602001604051908101604052809291908181526020018280546102dc90610444565b80156103295780601f106102fe57610100808354040283529160200191610329565b820191906000526020600020905b81548152906001019060200180831161030c57829003601f168201915b50505050509050919050565b60008181526003602052604090208054606091906102b090610444565b60006020828403121561036457600080fd5b5035919050565b6000815180845260005b8181101561039157602081850181015186830182015201610375565b818111156103a3576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006103cb602083018461036b565b9392505050565b600080602083850312156103e557600080fd5b823567ffffffffffffffff808211156103fd57600080fd5b818501915085601f83011261041157600080fd5b81358181111561042057600080fd5b86602082850101111561043257600080fd5b60209290920196919550909350505050565b600181811c9082168061045857607f821691505b6020821081141561047957634e487b7160e01b600052602260045260246000fd5b5091905056fea2646970667358221220de895ffe962788e1214df4d87d6cb2898ccd094eda688dc0ec9f1c9eabb1415064736f6c63430008090033608060405234801561001057600080fd5b50614c36806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620001925760003560e01c80639ded06df11620000f0578063d1cf834f11620000a3578063d2bc37f8116200007a578063d2bc37f8146200042a578063dc97d9621462000434578063e3dfa2991462000457578063e673df8a146200046157600080fd5b8063d1cf834f14620003e5578063d26ff21014620003fc578063d289d1cb146200041357600080fd5b80639ded06df1462000301578063aa1e1f0a1462000318578063affe39c1146200036c578063bd02d0f51462000385578063c031a18014620003b7578063c987336c14620003ce57600080fd5b806360c6ff43116200014957806360c6ff431462000248578063646c5d34146200025f5780637ae1cfca14620002765780637b1b769e14620002ad578063935b13f614620002c4578063986e791a14620002db57600080fd5b8063037699c1146200019757806309c5eabe14620001b057806321f8a72114620001c757806334ff698314620002105780633cd6ee6d14620002275780635c60da1b146200023e575b600080fd5b620001ae620001a836600462002dd9565b6200046b565b005b620001ae620001c136600462002dd9565b620004bd565b620001f3620001d836600462002e1e565b6000908152600260205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020015b60405180910390f35b620001ae6200022136600462002f07565b620004de565b620001ae6200023836600462002dd9565b6200066c565b620001f3620006b7565b620001ae6200025936600462002dd9565b6200070e565b620001ae6200027036600462002f07565b6200075c565b6200029c6200028736600462002e1e565b60009081526004602052604090205460ff1690565b604051901515815260200162000207565b6200029c620002be36600462002f07565b620008da565b620001f3620002d536600462002f07565b620008f1565b620002f2620002ec36600462002e1e565b62000902565b60405162000207919062002f9c565b620001ae6200031236600462002dd9565b620009ac565b7f75a31d1ce8e5f9892188befc328d3b9bd3fa5037457e881abc21f388471b8d9660005260046020527fa0b596b8942d6ef5cc77ce0ad89a75f0bfcc7f0132cd8db58e8449dcf04a858a5460ff166200029c565b6200037662000ba6565b60405162000207919062002ff7565b620003a86200039636600462002e1e565b60009081526020819052604090205490565b60405190815260200162000207565b620002f2620003c836600462002e1e565b62000c6d565b620001ae620003df36600462003022565b62000c8c565b620001ae620003f636600462002dd9565b62000eec565b6200029c6200040d36600462002e1e565b62000fa8565b620001ae6200042436600462002dd9565b62000fb9565b620001ae62001083565b620003a86200044536600462002e1e565b60009081526005602052604090205490565b620001ae620011ff565b620003766200137b565b333014620004965760405162461bcd60e51b81526004016200048d906200307c565b60405180910390fd5b600080620004a7838501856200309e565b91509150620004b782826200143c565b50505050565b600080620004ce838501856200310c565b91509150620004b78282620014f6565b6000620004ea62001a10565b9050620004f8813362001a5e565b620005175760405162461bcd60e51b81526004016200048d90620031ef565b600080366040516200052b92919062003212565b604051809103902090506200054282823362001a77565b15620005625760405162461bcd60e51b81526004016200048d9062003222565b62000571828233600162001a92565b6000600162000581848462001aaa565b6200058d919062003257565b90506200059c83838362001abc565b620005a78362001ae4565b811015620005b55750505050565b620005cc620005c48562001af5565b600062001b49565b83604051620005dc919062003272565b604051908190038120907f5327e3af9cd2d5c19a8dff23ef487d7a4d5f6fc985464f1792a33c212057598990600090a26200061a8383600062001abc565b6000620006278462001b69565b905060005b8181101562000664576200064f858562000647888562001b7a565b600062001a92565b806200065b8162003290565b9150506200062c565b505050505050565b3330146200068e5760405162461bcd60e51b81526004016200048d906200307c565b6000808080620006a185870187620032ae565b9350935093509350620006648484848462001b8c565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60005260026020527f11141f466c69fd409e1990e063b49cd6d61ed2ecff27a2e402e259ca6b9a01a3546001600160a01b031690565b333014620007305760405162461bcd60e51b81526004016200048d906200307c565b60008080620007428486018662003339565b9250925092506200075583838362001ca3565b5050505050565b60006200076862001a10565b905062000776813362001a5e565b620007955760405162461bcd60e51b81526004016200048d90620031ef565b60008036604051620007a992919062003212565b60405180910390209050620007c082823362001a77565b15620007e05760405162461bcd60e51b81526004016200048d9062003222565b620007ef828233600162001a92565b60006001620007ff848462001aaa565b6200080b919062003257565b90506200081a83838362001abc565b620008258362001ae4565b811015620008335750505050565b6200084a620008428562001af5565b600162001b49565b836040516200085a919062003272565b604051908190038120907f11260c92ecb021c33706db3a9699410f343d5f2175201a4551fe7634347a0cf190600090a2620008988383600062001abc565b6000620008a58462001b69565b905060005b818110156200066457620008c5858562000647888562001b7a565b80620008d18162003290565b915050620008aa565b6000620008eb620002878362001af5565b92915050565b6000620008eb620001d88362001d5c565b6000818152600160205260409020805460609190620009219062003398565b80601f01602080910402602001604051908101604052809291908181526020018280546200094f9062003398565b8015620009a05780601f106200097457610100808354040283529160200191620009a0565b820191906000526020600020905b8154815290600101906020018083116200098257829003601f168201915b50505050509050919050565b6000620009b8620006b7565b6001600160a01b03161415620009fd5760405162461bcd60e51b81526020600482015260096024820152684e4f545f50524f585960b81b60448201526064016200048d565b6000808080808062000a12878901896200344d565b9550955095509550955095506000600162000a2c62001a10565b62000a38919062003257565b905062000a8c817fd51dc9b187568bb94760866e1d0d066ca470037d2d331afb1a02ec74bfb8990c60009081526020527ff8d0c05cd32e3272241fdc9d5839565f8bcf94139cb4bd09e230e1b57a60ddc255565b62000a9981888862001d96565b6000600162000aa762001ed7565b62000ab3919062003257565b905062000ac08162001f25565b62000acd81878762001f72565b6000600162000adb620020b3565b62000ae7919062003257565b905062000af48162002101565b62000b018186866200214e565b60408051600080825260208201928390527fd167b96814cd24898418cc293e8d47d54afe6dcf0631283f0830e1eae621f6bd9262000b459291908b908b90620034fa565b60405180910390a160408051600080825260208201928390527fe7475da747ead33c03118a4b2cca5e521e930dd8ddc556751fe12d1e8395db2c9262000b9192919089908990620034fa565b60405180910390a15050505050505050505050565b6060600062000bb462001ed7565b9050600062000bc38262002298565b9050806001600160401b0381111562000be05762000be062002e38565b60405190808252806020026020018201604052801562000c0a578160200160208202803683370190505b50925060005b8181101562000c675762000c258382620022a9565b84828151811062000c3a5762000c3a6200353b565b6001600160a01b03909216602092830291909101909101528062000c5e8162003290565b91505062000c10565b50505090565b6000818152600360205260409020805460609190620009219062003398565b600062000c9862001a10565b905062000ca6813362001a5e565b62000cc55760405162461bcd60e51b81526004016200048d90620031ef565b6000803660405162000cd992919062003212565b6040518091039020905062000cf082823362001a77565b1562000d105760405162461bcd60e51b81526004016200048d9062003222565b62000d1f828233600162001a92565b6000600162000d2f848462001aaa565b62000d3b919062003257565b905062000d4a83838362001abc565b62000d558362001ae4565b81101562000d6557505050505050565b6040516001600160a01b038716907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000866001600160a01b0316639ded06df60e01b878760405160240162000dc292919062003551565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905162000e02919062003272565b600060405180830381855af49150503d806000811462000e3f576040519150601f19603f3d011682016040523d82523d6000602084013e62000e44565b606091505b505090508062000e865760405162461bcd60e51b815260206004820152600c60248201526b14d155155417d1905253115160a21b60448201526064016200048d565b62000e9187620022bb565b5062000ea08383600062001abc565b600062000ead8462001b69565b905060005b8181101562000ee25762000ecd858562000647888562001b7a565b8062000ed98162003290565b91505062000eb2565b5050505050505050565b33301462000f0e5760405162461bcd60e51b81526004016200048d906200307c565b60008062000f1f8385018562003580565b91509150600062000f2f62001ed7565b90507fd167b96814cd24898418cc293e8d47d54afe6dcf0631283f0830e1eae621f6bd62000f5c62000ba6565b62000f6783620022e7565b858560405162000f7b9493929190620034fa565b60405180910390a162000f9b62000f928262003290565b91508162001f25565b6200075581848462001f72565b6000620008eb6200028783620022f8565b33301462000fdb5760405162461bcd60e51b81526004016200048d906200307c565b60008062000fec8385018562003580565b91509150600062000ffc62001ed7565b90507fe7475da747ead33c03118a4b2cca5e521e930dd8ddc556751fe12d1e8395db2c620010296200137b565b620010348362002334565b8585604051620010489493929190620034fa565b60405180910390a160006200105c620020b3565b9050620010766200106d8262003290565b91508162002101565b620006648185856200214e565b60006200108f62001a10565b90506200109d813362001a5e565b620010bc5760405162461bcd60e51b81526004016200048d90620031ef565b60008036604051620010d092919062003212565b60405180910390209050620010e782823362001a77565b15620011075760405162461bcd60e51b81526004016200048d9062003222565b62001116828233600162001a92565b6000600162001126848462001aaa565b62001132919062003257565b90506200114183838362001abc565b6200114c8362001ae4565b8110156200115957505050565b620011867f75a31d1ce8e5f9892188befc328d3b9bd3fa5037457e881abc21f388471b8d96600162001b49565b6040517f9f428b56ca209118783db391e8a5a62827b6b218add33411061060d308af6ead90600090a1620011bd8383600062001abc565b6000620011ca8462001b69565b905060005b818110156200075557620011ea858562000647888562001b7a565b80620011f68162003290565b915050620011cf565b60006200120b62001a10565b905062001219813362001a5e565b620012385760405162461bcd60e51b81526004016200048d90620031ef565b600080366040516200124c92919062003212565b604051809103902090506200126382823362001a77565b15620012835760405162461bcd60e51b81526004016200048d9062003222565b62001292828233600162001a92565b60006001620012a2848462001aaa565b620012ae919062003257565b9050620012bd83838362001abc565b620012c88362001ae4565b811015620012d557505050565b620013027f75a31d1ce8e5f9892188befc328d3b9bd3fa5037457e881abc21f388471b8d96600062001b49565b6040517f0e821ff109470f6aa1aebb161aa0e7328b1a8552aa48e09ed2d03184ecece9a690600090a1620013398383600062001abc565b6000620013468462001b69565b905060005b81811015620007555762001366858562000647888562001b7a565b80620013728162003290565b9150506200134b565b6060600062001389620020b3565b90506000620013988262002345565b9050806001600160401b03811115620013b557620013b562002e38565b604051908082528060200260200182016040528015620013df578160200160208202803683370190505b50925060005b8181101562000c6757620013fa838262002356565b8482815181106200140f576200140f6200353b565b6001600160a01b039092166020928302919091019091015280620014338162003290565b915050620013e5565b60006200144983620008f1565b90506001600160a01b038116620014955760405162461bcd60e51b815260206004820152600f60248201526e1513d2d15397d393d517d1561254d5608a1b60448201526064016200048d565b6040516308a1eee160e01b8152600481018390526001600160a01b038216906308a1eee190602401600060405180830381600087803b158015620014d857600080fd5b505af1158015620014ed573d6000803e3d6000fd5b50505050505050565b80516000816001600160401b0381111562001515576200151562002e38565b6040519080825280602002602001820160405280156200153f578160200160208202803683370190505b50905060005b82811015620015c9576200158762001564868051906020012062002368565b8583815181106200157957620015796200353b565b6020026020010151620023a4565b8282815181106200159c576200159c6200353b565b6001600160a01b039092166020928302919091019091015280620015c08162003290565b91505062001545565b5060008060008087806020019051810190620015e691906200372d565b9350935093509350620015f64690565b8414620016325760405162461bcd60e51b815260206004820152600960248201526824a72b2fa1a420a4a760b91b60448201526064016200048d565b8251825181148015620016455750815181145b6200167e5760405162461bcd60e51b8152602060048201526008602482015267494e565f434d445360c01b60448201526064016200048d565b6000620016956200168e62001ed7565b8862002549565b905060008180620016ac5750620016ac88620025f8565b90506000620016bb896200267e565b905060005b8481101562001a01576000888281518110620016e057620016e06200353b565b60200260200101519050620016f58162000fa8565b15620017025750620019ec565b6000808984815181106200171a576200171a6200353b565b602002602001015160405160200162001734919062003272565b6040516020818303038152906040528051906020012090507f5763814b98a3aa86f212797af3273868b5dd6e2a532d764a79b98ca859e7bbad8114156200179457856200178457505050620019ec565b633cd6ee6d60e01b9150620018d7565b7fec78d9c22c08bb9f0ecd5d95571ae83e3f22219c5a9278c3270691d50abfd91b811415620017e95784158015620017ca575085155b15620017d957505050620019ec565b6360c6ff4360e01b9150620018d7565b7fda199c0e76f665e0450020791c7f8eacc75f3cdbace313272c28f93e5390b62c8114156200183e57841580156200181f575085155b156200182e57505050620019ec565b63037699c160e01b9150620018d7565b7f1d4a390f291e2e1f29874769efdef47ddad94d76f77ff516fad206a385e8995f8114156200188657866200187657505050620019ec565b63d1cf834f60e01b9150620018d7565b7fb460dcb6fd5797fc0e7ea0f13406c80d30702ba7f73a42bd91394775dcbca718811415620018ce5786620018be57505050620019ec565b63d289d1cb60e01b9150620018d7565b505050620019ec565b620018e4836001620026f9565b6000306001600160a01b0316838b87815181106200190657620019066200353b565b602002602001015160405160240162001920919062002f9c565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905162001960919062003272565b6000604051808303816000865af19150503d80600081146200199f576040519150601f19603f3d011682016040523d82523d6000602084013e620019a4565b606091505b50509050620019b48482620026f9565b8015620019e75760405184907fa74c8847d513feba22a0f0cb38d53081abf97562cdb293926ba243689e7c41ca90600090a25b505050505b80620019f88162003290565b915050620016c0565b50505050505050505050505050565b7fd51dc9b187568bb94760866e1d0d066ca470037d2d331afb1a02ec74bfb8990c60009081526020527ff8d0c05cd32e3272241fdc9d5839565f8bcf94139cb4bd09e230e1b57a60ddc25490565b600062001a706200028784846200270c565b9392505050565b600062001a8a6200028785858562002763565b949350505050565b620004b762001aa385858562002763565b8262001b49565b600062001a70620003968484620027d7565b62001adf62001acc8484620027d7565b8260009182526020829052604090912055565b505050565b6000620008eb62000396836200281a565b60007f1a7261d3a36c4ce4235d10859911c9444a6963a3591ec5725b96871d9810626b8260405160200162001b2c92919062003825565b604051602081830303815290604052805190602001209050919050565b600091825260046020526040909120805460ff1916911515919091179055565b6000620008eb620003968362002856565b600062001a70620001d8848462002892565b600062001b9984620008f1565b6001600160a01b03161462001bdf5760405162461bcd60e51b815260206004820152600b60248201526a1513d2d15397d1561254d560aa1b60448201526064016200048d565b60008360405160200162001bf4919062003272565b6040516020818303038152906040528051906020012090506000818686868660405162001c219062002d80565b62001c3094939291906200384d565b8190604051809103906000f590508015801562001c51573d6000803e3d6000fd5b50905062001c608582620028d5565b7fbf90b5a1ec9763e8bf4b9245cef0c28db92bab309fc2c5177f17814f38246938858260405162001c939291906200388d565b60405180910390a1505050505050565b600062001cb084620008f1565b90506001600160a01b03811662001cfc5760405162461bcd60e51b815260206004820152600f60248201526e1513d2d15397d393d517d1561254d5608a1b60448201526064016200048d565b6040516340c10f1960e01b81526001600160a01b038481166004830152602482018490528216906340c10f1990604401600060405180830381600087803b15801562001d4757600080fd5b505af115801562000ee2573d6000803e3d6000fd5b60007fc4e632779a6a7838736dd7e5e6a0eadf171dd37dfb6230720e265576dfcf42bb8260405160200162001b2c92919062003825565b50565b81518181101562001dd75760405162461bcd60e51b815260206004820152600a602482015269494e565f41444d494e5360b01b60448201526064016200048d565b6000821162001e1a5760405162461bcd60e51b815260206004820152600e60248201526d12539597d05113525397d512131160921b60448201526064016200048d565b62001e268483620028eb565b62001e328482620028fa565b60005b818110156200075557600084828151811062001e555762001e556200353b565b6020026020010151905062001e6b868262001a5e565b1562001ea65760405162461bcd60e51b8152602060048201526009602482015268222aa82fa0a226a4a760b91b60448201526064016200048d565b62001eb386838362002909565b62001ec18682600162002919565b508062001ece8162003290565b91505062001e35565b7f67cbdff49954077d2dd5637524b03bfe3e6506d456859017a888fad567e1af0360009081526020527f06f01c2f096e745c2a207fbf93babd5e7ee6acfd26f0f34a0e4ac68373c21d0e5490565b7f67cbdff49954077d2dd5637524b03bfe3e6506d456859017a888fad567e1af0360009081526020527f06f01c2f096e745c2a207fbf93babd5e7ee6acfd26f0f34a0e4ac68373c21d0e55565b81518181101562001fb35760405162461bcd60e51b815260206004820152600a602482015269494e565f4f574e45525360b01b60448201526064016200048d565b6000821162001ff65760405162461bcd60e51b815260206004820152600e60248201526d12539597d3d5d3915497d512131160921b60448201526064016200048d565b62002002848362002929565b6200200e848262002938565b60005b81811015620007555760008482815181106200203157620020316200353b565b6020026020010151905062002047868262002947565b15620020825760405162461bcd60e51b8152602060048201526009602482015268222aa82fa7aba722a960b91b60448201526064016200048d565b6200208f86838362002959565b6200209d86826001620029ad565b5080620020aa8162003290565b91505062002011565b7f5369fb791f26307f8e69b07625302ccac6930af09ab22d753b17d2042e9992e760009081526020527f07174c149a4f7627ac1dd39b679bd42f12fc4e8d7e850b055662bbee7188da105490565b7f5369fb791f26307f8e69b07625302ccac6930af09ab22d753b17d2042e9992e760009081526020527f07174c149a4f7627ac1dd39b679bd42f12fc4e8d7e850b055662bbee7188da1055565b815181811015620021925760405162461bcd60e51b815260206004820152600d60248201526c494e565f4f50455241544f525360981b60448201526064016200048d565b60008211620021d85760405162461bcd60e51b815260206004820152601160248201527012539597d3d41154905513d497d5121311607a1b60448201526064016200048d565b620021e48483620029bd565b620021f08482620029cc565b60005b81811015620007555760008482815181106200221357620022136200353b565b60200260200101519050620022298682620029db565b15620022675760405162461bcd60e51b815260206004820152600c60248201526b222aa82fa7a822a920aa27a960a11b60448201526064016200048d565b62002274868383620029ed565b6200228286826001620029fd565b50806200228f8162003290565b915050620021f3565b6000620008eb620003968362002a0d565b600062001a70620001d8848462002a49565b62001d937f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8262002a8c565b6000620008eb620003968362002aba565b604080517f957705a374326b30f4a1069c936d736cc9993ed6c820b4e0e2fd94a8beca0d1d602082015290810182905260009060600162001b2c565b6000620008eb620003968362002af6565b6000620008eb620003968362002b32565b600062001a70620001d8848462002b6e565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c0162001b2c565b60008151604114620023e35760405162461bcd60e51b815260206004820152600760248201526624a72b2fa622a760c91b60448201526064016200048d565b60208201516040830151606084015160001a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156200244f5760405162461bcd60e51b8152602060048201526005602482015264494e565f5360d81b60448201526064016200048d565b8060ff16601b14806200246557508060ff16601c145b6200249b5760405162461bcd60e51b815260206004820152600560248201526424a72b2fab60d91b60448201526064016200048d565b6040805160008082526020820180845289905260ff841692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015620024f0573d6000803e3d6000fd5b505050602060405103519450846001600160a01b03161415620025405760405162461bcd60e51b8152602060048201526007602482015266494e565f53494760c81b60448201526064016200048d565b50505092915050565b6000620025568262002bb1565b156200256557506000620008eb565b60006200257284620022e7565b90506000805b8451811015620025ec57620025aa868683815181106200259c576200259c6200353b565b602002602001015162002947565b8015620025c4575082620025be8362003290565b92508210155b15620025d75760019350505050620008eb565b80620025e38162003290565b91505062002578565b50600095945050505050565b6000806200260562001ed7565b90506000620026176001601062003257565b905060008183116200262b57600062002637565b620026378284620038b9565b90505b8083111562002673576200265c836200265381620038d3565b94508662002549565b156200266d57506001949350505050565b6200263a565b506000949350505050565b6000806200268b620020b3565b905060006200269d6001601062003257565b90506000818311620026b1576000620026bd565b620026bd8284620038b9565b90505b808311156200267357620026e283620026d981620038d3565b94508662002c6b565b15620026f357506001949350505050565b620026c0565b6200270862001aa383620022f8565b5050565b60007f63fa879cb478fddf1de08d49e29115fb768866711bc799ff9ab419a7f16c9afb83836040516020016200274593929190620038ed565b60405160208183030381529060405280519060200120905092915050565b604080517f3ced7a8caf1111e0fbf4b784c5b41aebd0f2389d9f2f64d5c3424aeed3adbd3c6020820152908101849052606080820184905282901b6bffffffffffffffffffffffff191660808201526000906094016040516020818303038152906040528051906020012090509392505050565b604080517fbaea1c6c0f16a0a340b10a9e980806696a68ddbb5e8361fd64630dd21abf515f60208201529081018390526060810182905260009060800162002745565b604080517f79bd914addd90bd67ad800bf0230bb85ffdef5aeba5fd8249f116d3f17f4fb4b602082015290810182905260009060600162001b2c565b604080517f05112ef894367de1270cfae12afcd2285c225830eb8f74e7e938f721bb510cba602082015290810182905260009060600162001b2c565b604080517ff23ec0bb4210edd5cba85afd05127efcd2fc6a781bfed49188da1081670b22d860208201529081018390526060810182905260009060800162002745565b62002708620028e48362001d5c565b8262002a8c565b6200270862001acc836200281a565b6200270862001acc8362002856565b62001adf620028e4848462002892565b62001adf62001aa384846200270c565b6200270862001acc8362002aba565b6200270862001acc8362002a0d565b600062001a7062000287848462002d0e565b6001600160a01b0381166200299d5760405162461bcd60e51b81526020600482015260096024820152682d22a927afa0a2222960b91b60448201526064016200048d565b62001adf620028e4848462002a49565b62001adf62001aa3848462002d0e565b6200270862001acc8362002af6565b6200270862001acc8362002b32565b600062001a7062000287848462002d47565b62001adf620028e4848462002b6e565b62001adf62001aa3848462002d47565b604080517f4ededbb904acd1258c7b9a6b12f5a8a8870a8ec295809cf35dc1f098018d4d20602082015290810182905260009060600162001b2c565b604080517f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c060208201529081018390526060810182905260009060800162002745565b60009182526002602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b604080517f2bd864201f87ce8cc2d875453b39cc3e981d8ee12e4b7d4b7492fd9404bd8229602082015290810182905260009060600162001b2c565b604080517fc0e594a04ce768761133f7238cd95a55441f8ee2d50a03e7d5c44fb26c04d76f602082015290810182905260009060600162001b2c565b604080517f6199febc552b750a8cf661862e77cb2dbed9f3fb6fcc10bd34f8ff9e1992362b602082015290810182905260009060600162001b2c565b604080517f46a52cf33029de9f84853745a87af28464c80bf0346df1b32e205fc73319f62260208201529081018390526060810182905260009060800162002745565b8051600090815b8181101562002c6157600062002bd082600162003257565b90505b8281101562002c4d5784818151811062002bf15762002bf16200353b565b60200260200101516001600160a01b031685838151811062002c175762002c176200353b565b60200260200101516001600160a01b0316141562002c3a57506001949350505050565b62002c458162003290565b905062002bd3565b5062002c598162003290565b905062002bb8565b5060009392505050565b600062002c788262002bb1565b1562002c8757506000620008eb565b600062002c948462002334565b90506000805b8451811015620025ec5762002ccc8686838151811062002cbe5762002cbe6200353b565b6020026020010151620029db565b801562002ce657508262002ce08362003290565b92508210155b1562002cf95760019350505050620008eb565b8062002d058162003290565b91505062002c9a565b60007fffbbd206dad2a77ed223a0ff3d2dcac502bc8c9a91fa18140757561e84f365c083836040516020016200274593929190620038ed565b60007f934404a25b467ba4b73107cf91baadb1313f82988678493324b442216995358683836040516020016200274593929190620038ed565b6112eb806200391683390190565b60008083601f84011262002da157600080fd5b5081356001600160401b0381111562002db957600080fd5b60208301915083602082850101111562002dd257600080fd5b9250929050565b6000806020838503121562002ded57600080fd5b82356001600160401b0381111562002e0457600080fd5b62002e128582860162002d8e565b90969095509350505050565b60006020828403121562002e3157600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171562002e795762002e7962002e38565b604052919050565b60006001600160401b0382111562002e9d5762002e9d62002e38565b50601f01601f191660200190565b600082601f83011262002ebd57600080fd5b813562002ed462002ece8262002e81565b62002e4e565b81815284602083860101111562002eea57600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121562002f1a57600080fd5b81356001600160401b0381111562002f3157600080fd5b62001a8a8482850162002eab565b60005b8381101562002f5c57818101518382015260200162002f42565b83811115620004b75750506000910152565b6000815180845262002f8881602086016020860162002f3f565b601f01601f19169290920160200192915050565b60208152600062001a70602083018462002f6e565b600081518084526020808501945080840160005b8381101562002fec5781516001600160a01b03168752958201959082019060010162002fc5565b509495945050505050565b60208152600062001a70602083018462002fb1565b6001600160a01b038116811462001d9357600080fd5b6000806000604084860312156200303857600080fd5b833562003045816200300c565b925060208401356001600160401b038111156200306157600080fd5b6200306f8682870162002d8e565b9497909650939450505050565b6020808252600890820152672727aa2fa9a2a62360c11b604082015260600190565b60008060408385031215620030b257600080fd5b82356001600160401b03811115620030c957600080fd5b620030d78582860162002eab565b95602094909401359450505050565b60006001600160401b0382111562003102576200310262002e38565b5060051b60200190565b600080604083850312156200312057600080fd5b82356001600160401b03808211156200313857600080fd5b620031468683870162002eab565b93506020915081850135818111156200315e57600080fd5b8501601f810187136200317057600080fd5b80356200318162002ece82620030e6565b81815260059190911b82018401908481019089831115620031a157600080fd5b8584015b83811015620031de57803586811115620031bf5760008081fd5b620031cf8c898389010162002eab565b845250918601918601620031a5565b508096505050505050509250929050565b6020808252600990820152682727aa2fa0a226a4a760b91b604082015260600190565b8183823760009101908152919050565b6020808252600590820152641593d5115160da1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156200326d576200326d62003241565b500190565b600082516200328681846020870162002f3f565b9190910192915050565b6000600019821415620032a757620032a762003241565b5060010190565b60008060008060808587031215620032c557600080fd5b84356001600160401b0380821115620032dd57600080fd5b620032eb8883890162002eab565b955060208701359150808211156200330257600080fd5b50620033118782880162002eab565b935050604085013560ff811681146200332957600080fd5b9396929550929360600135925050565b6000806000606084860312156200334f57600080fd5b83356001600160401b038111156200336657600080fd5b620033748682870162002eab565b935050602084013562003387816200300c565b929592945050506040919091013590565b600181811c90821680620033ad57607f821691505b60208210811415620033cf57634e487b7160e01b600052602260045260246000fd5b50919050565b600082601f830112620033e757600080fd5b81356020620033fa62002ece83620030e6565b82815260059290921b840181019181810190868411156200341a57600080fd5b8286015b848110156200344257803562003434816200300c565b83529183019183016200341e565b509695505050505050565b60008060008060008060c087890312156200346757600080fd5b86356001600160401b03808211156200347f57600080fd5b6200348d8a838b01620033d5565b9750602089013596506040890135915080821115620034ab57600080fd5b620034b98a838b01620033d5565b9550606089013594506080890135915080821115620034d757600080fd5b50620034e689828a01620033d5565b92505060a087013590509295509295509295565b6080815260006200350f608083018762002fb1565b856020840152828103604084015262003529818662002fb1565b91505082606083015295945050505050565b634e487b7160e01b600052603260045260246000fd5b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b600080604083850312156200359457600080fd5b82356001600160401b03811115620035ab57600080fd5b620030d785828601620033d5565b6000620035ca62002ece8462002e81565b9050828152838383011115620035df57600080fd5b62001a7083602083018462002f3f565b600082601f8301126200360157600080fd5b815160206200361462002ece83620030e6565b82815260059290921b840181019181810190868411156200363457600080fd5b8286015b84811015620034425780516001600160401b03811115620036595760008081fd5b8701603f810189136200366c5760008081fd5b6200367f898683015160408401620035b9565b84525091830191830162003638565b600082601f830112620036a057600080fd5b81516020620036b362002ece83620030e6565b82815260059290921b84018101918181019086841115620036d357600080fd5b8286015b84811015620034425780516001600160401b03811115620036f85760008081fd5b8701603f810189136200370b5760008081fd5b6200371e898683015160408401620035b9565b845250918301918301620036d7565b600080600080608085870312156200374457600080fd5b845193506020808601516001600160401b03808211156200376457600080fd5b818801915088601f8301126200377957600080fd5b81516200378a62002ece82620030e6565b81815260059190911b8301840190848101908b831115620037aa57600080fd5b938501935b82851015620037ca57845182529385019390850190620037af565b60408b01519098509450505080831115620037e457600080fd5b620037f289848a01620035ef565b945060608801519250808311156200380957600080fd5b505062003819878288016200368e565b91505092959194509250565b828152600082516200383f81602085016020870162002f3f565b919091016020019392505050565b60808152600062003862608083018762002f6e565b828103602084015262003876818762002f6e565b60ff95909516604084015250506060015292915050565b604081526000620038a2604083018562002f6e565b905060018060a01b03831660208301529392505050565b600082821015620038ce57620038ce62003241565b500390565b600081620038e557620038e562003241565b506000190190565b928352602083019190915260601b6bffffffffffffffffffffffff191660408201526054019056fe60a06040523480156200001157600080fd5b50604051620012eb380380620012eb83398101604081905262000034916200022e565b83838382600390805190602001906200004f929190620000bb565b50815162000065906004906020850190620000bb565b5060ff166080525050600580546001600160a01b031916339081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a360065550620002f6915050565b828054620000c990620002b9565b90600052602060002090601f016020900481019282620000ed576000855562000138565b82601f106200010857805160ff191683800117855562000138565b8280016001018555821562000138579182015b82811115620001385782518255916020019190600101906200011b565b50620001469291506200014a565b5090565b5b808211156200014657600081556001016200014b565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200018957600080fd5b81516001600160401b0380821115620001a657620001a662000161565b604051601f8301601f19908116603f01168101908282118183101715620001d157620001d162000161565b81604052838152602092508683858801011115620001ee57600080fd5b600091505b83821015620002125785820183015181830184015290820190620001f3565b83821115620002245760008385830101525b9695505050505050565b600080600080608085870312156200024557600080fd5b84516001600160401b03808211156200025d57600080fd5b6200026b8883890162000177565b955060208701519150808211156200028257600080fd5b50620002918782880162000177565b935050604085015160ff81168114620002a957600080fd5b6060959095015193969295505050565b600181811c90821680620002ce57607f821691505b60208210811415620002f057634e487b7160e01b600052602260045260246000fd5b50919050565b608051610fd96200031260003960006101950152610fd96000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c806339509351116100a257806395d89b411161007157806395d89b4114610256578063a457c2d71461025e578063a9059cbb14610271578063dd62ed3e14610284578063f2fde38b146102af57600080fd5b806339509351146101fd57806340c10f191461021057806370a08231146102235780638da5cb5b1461024357600080fd5b806323b872dd116100de57806323b872dd1461017d578063313ce5671461019057806331eecaf4146101c9578063355274ea146101f457600080fd5b806306fdde031461011057806308a1eee11461012e578063095ea7b31461014357806318160ddd14610166575b600080fd5b6101186102c2565b6040516101259190610bf8565b60405180910390f35b61014161013c366004610c2b565b610350565b005b610156610151366004610c60565b6103b9565b6040519015158152602001610125565b61016f60025481565b604051908152602001610125565b61015661018b366004610c8a565b6103cf565b6101b77f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610125565b6101dc6101d7366004610c2b565b610421565b6040516001600160a01b039091168152602001610125565b61016f60065481565b61015661020b366004610c60565b610505565b61014161021e366004610c60565b61053c565b61016f610231366004610cc6565b60006020819052908152604090205481565b6005546101dc906001600160a01b031681565b6101186105cd565b61015661026c366004610c60565b6105da565b61015661027f366004610c60565b610611565b61016f610292366004610ce8565b600160209081526000928352604080842090915290825290205481565b6101416102bd366004610cc6565b61061e565b600380546102cf90610d1b565b80601f01602080910402602001604051908101604052809291908181526020018280546102fb90610d1b565b80156103485780601f1061031d57610100808354040283529160200191610348565b820191906000526020600020905b81548152906001019060200180831161032b57829003601f168201915b505050505081565b6005546001600160a01b031633146103835760405162461bcd60e51b815260040161037a90610d56565b60405180910390fd5b600061038e82610421565b6001600160a01b0381166000908152602081905260409020549091506103b59082906106ca565b5050565b60006103c6338484610788565b50600192915050565b60006103dc848484610836565b6001600160a01b038416600090815260016020908152604080832033808552925290912054610417918691610412908690610d8f565b610788565b5060019392505050565b6005546040516000916001600160f81b0319916001600160a01b0390911690849061044e60208201610bbc565b601f1982820381018352601f9091011660408181523060208301520160408051601f198184030181529082905261048a92918890602001610da6565b604051602081830303815290604052805190602001206040516020016104e794939291906001600160f81b031994909416845260609290921b6bffffffffffffffffffffffff191660018401526015830152603582015260550190565b60408051601f19818403018152919052805160209091012092915050565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916103c6918590610412908690610ddb565b6005546001600160a01b031633146105665760405162461bcd60e51b815260040161037a90610d56565b600654801580610583575080826002546105809190610ddb565b11155b6105be5760405162461bcd60e51b815260206004820152600c60248201526b10d05417d15610d15151115160a21b604482015260640161037a565b6105c8838361092e565b505050565b600480546102cf90610d1b565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916103c6918590610412908690610d8f565b60006103c6338484610836565b6005546001600160a01b031633146106485760405162461bcd60e51b815260040161037a90610d56565b6001600160a01b03811661066e5760405162461bcd60e51b815260040161037a90610df3565b6005546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600580546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0382166106f05760405162461bcd60e51b815260040161037a90610df3565b6106fc826000836109e2565b6001600160a01b03821660009081526020819052604081208054839290610724908490610d8f565b92505081905550806002600082825461073d9190610d8f565b90915550506040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6001600160a01b0383166107ae5760405162461bcd60e51b815260040161037a90610df3565b6001600160a01b0382166107d45760405162461bcd60e51b815260040161037a90610df3565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b03831661085c5760405162461bcd60e51b815260040161037a90610df3565b6001600160a01b0382166108825760405162461bcd60e51b815260040161037a90610df3565b61088d8383836109e2565b6001600160a01b038316600090815260208190526040812080548392906108b5908490610d8f565b90915550506001600160a01b038216600090815260208190526040812080548392906108e2908490610ddb565b92505081905550816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161082991815260200190565b6001600160a01b0382166109545760405162461bcd60e51b815260040161037a90610df3565b610960600083836109e2565b80600260008282546109729190610ddb565b90915550506001600160a01b0382166000908152602081905260408120805483929061099f908490610ddb565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200161077c565b600554604051633d70e7e560e11b81527f75a31d1ce8e5f9892188befc328d3b9bd3fa5037457e881abc21f388471b8d9660048201526001600160a01b0390911690637ae1cfca9060240160206040518083038186803b158015610a4557600080fd5b505afa158015610a59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a7d9190610e16565b15610ab65760405162461bcd60e51b815260206004820152600960248201526824a9afa32927ad22a760b91b604482015260640161037a565b6005546040516001600160a01b0390911690637ae1cfca90610aff907f1a7261d3a36c4ce4235d10859911c9444a6963a3591ec5725b96871d9810626b90600490602001610e38565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b8152600401610b3391815260200190565b60206040518083038186803b158015610b4b57600080fd5b505afa158015610b5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b839190610e16565b156105c85760405162461bcd60e51b815260206004820152600960248201526824a9afa32927ad22a760b91b604482015260640161037a565b60c280610ee283390190565b60005b83811015610be3578181015183820152602001610bcb565b83811115610bf2576000848401525b50505050565b6020815260008251806020840152610c17816040850160208701610bc8565b601f01601f19169190910160400192915050565b600060208284031215610c3d57600080fd5b5035919050565b80356001600160a01b0381168114610c5b57600080fd5b919050565b60008060408385031215610c7357600080fd5b610c7c83610c44565b946020939093013593505050565b600080600060608486031215610c9f57600080fd5b610ca884610c44565b9250610cb660208501610c44565b9150604084013590509250925092565b600060208284031215610cd857600080fd5b610ce182610c44565b9392505050565b60008060408385031215610cfb57600080fd5b610d0483610c44565b9150610d1260208401610c44565b90509250929050565b600181811c90821680610d2f57607f821691505b60208210811415610d5057634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600990820152682727aa2fa7aba722a960b91b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082821015610da157610da1610d79565b500390565b60008451610db8818460208901610bc8565b845190830190610dcc818360208901610bc8565b01928352505060200192915050565b60008219821115610dee57610dee610d79565b500190565b6020808252600990820152682d22a927afa0a2222960b91b604082015260600190565b600060208284031215610e2857600080fd5b81518015158114610ce157600080fd5b828152600060206000845481600182811c915080831680610e5a57607f831692505b858310811415610e7857634e487b7160e01b85526022600452602485fd5b808015610e8c5760018114610ea157610ed2565b60ff1985168988015283890187019550610ed2565b60008a81526020902060005b85811015610ec85781548b82018a0152908401908801610ead565b505086848a010195505b5093999850505050505050505056fe6080604052348015600f57600080fd5b506040516100c23803806100c2833981016040819052602c916089565b6040516308a1eee160e01b8152600481018290526001600160a01b038316906308a1eee190602401600060405180830381600087803b158015606d57600080fd5b505af11580156080573d6000803e3d6000fd5b50600092505050ff5b60008060408385031215609b57600080fd5b82516001600160a01b038116811460b157600080fd5b602093909301519294929350505056fea26469706673582212200b9d4704dcf13dfd9787296fefb091302153358bea4f3d74f0ba4e11a9cb95aa64736f6c63430008090033a2646970667358221220650a3d16decebe4547c4596d6c58eb39fc086a5e2785d73373e140c33d3203ab64736f6c634300080900330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000080000000000000000000000003f5876a2b06e54949ab106651ab6694d0289b2b40000000000000000000000009256fd872118ed3a97754b0fb42c15015d17e0cc0000000000000000000000005c8ef9ca7b43c93ac4a146bef77fafbc7d3e69b70000000000000000000000001486157d505c7f7e546ad00e3e2eee25bf665c9b0000000000000000000000002ec991b5c0b742abd9d2ea31fe6c14a85e91c821000000000000000000000000f505462a29e36e26f25ef0175ca1ecba09cc118f000000000000000000000000027c1882b975e2cd771ae068b0389fa38b9dda7300000000000000000000000030932ac1f0477fbd63e4c5be1928f367a58a45a100000000000000000000000000000000000000000000000000000000000000050000000000000000000000007bcb28bcde9938ff530c0489be53a2af9afd2227000000000000000000000000971da4cc7a0d1b1f9f8376098e0797e0e099e00f000000000000000000000000111e9d4529b6f5831018675f91ca98f98bc4d3770000000000000000000000000aa1dea04a534f2e22a690510fe97fcc9d8a8ad500000000000000000000000089dc2de633844e041583b788c3edf1d3728aee86000000000000000000000000000000000000000000000000000000000000000500000000000000000000000082601fa5fabb9a0f2f5e3f852c02afe2f3c234e5000000000000000000000000d7779a66e81cd8e834039794397a832d5d42070a000000000000000000000000bb0ae199105e8bcce1b4a54c804a5c44299675a2000000000000000000000000497a3b0c0addd4db5c44d2c11329465135e9ba200000000000000000000000001a2559f2675d7c67995d6cb0a9c0a79fe21e3624Deployed Bytecode
0x6080604052600436106100745760003560e01c80639ded06df1161004e5780639ded06df146101ea578063bd02d0f51461020b578063c031a18014610246578063dc97d96214610266576100b0565b806321f8a7211461012a5780637ae1cfca1461017d578063986e791a146101bd576100b0565b366100b05760405162461bcd60e51b81526020600482015260086024820152672727afa2aa2422a960c11b604482015260640160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc600090815260026020527f11141f466c69fd409e1990e063b49cd6d61ed2ecff27a2e402e259ca6b9a01a3546001600160a01b03169036908037600080366000845af43d6000803e808015610125573d6000f35b3d6000fd5b34801561013657600080fd5b50610160610145366004610352565b6000908152600260205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561018957600080fd5b506101ad610198366004610352565b60009081526004602052604090205460ff1690565b6040519015158152602001610174565b3480156101c957600080fd5b506101dd6101d8366004610352565b610293565b60405161017491906103b8565b3480156101f657600080fd5b506102096102053660046103d2565b5050565b005b34801561021757600080fd5b50610238610226366004610352565b60009081526020819052604090205490565b604051908152602001610174565b34801561025257600080fd5b506101dd610261366004610352565b610335565b34801561027257600080fd5b50610238610281366004610352565b60009081526005602052604090205490565b60008181526001602052604090208054606091906102b090610444565b80601f01602080910402602001604051908101604052809291908181526020018280546102dc90610444565b80156103295780601f106102fe57610100808354040283529160200191610329565b820191906000526020600020905b81548152906001019060200180831161030c57829003601f168201915b50505050509050919050565b60008181526003602052604090208054606091906102b090610444565b60006020828403121561036457600080fd5b5035919050565b6000815180845260005b8181101561039157602081850181015186830182015201610375565b818111156103a3576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006103cb602083018461036b565b9392505050565b600080602083850312156103e557600080fd5b823567ffffffffffffffff808211156103fd57600080fd5b818501915085601f83011261041157600080fd5b81358181111561042057600080fd5b86602082850101111561043257600080fd5b60209290920196919550909350505050565b600181811c9082168061045857607f821691505b6020821081141561047957634e487b7160e01b600052602260045260246000fd5b5091905056fea2646970667358221220de895ffe962788e1214df4d87d6cb2898ccd094eda688dc0ec9f1c9eabb1415064736f6c63430008090033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000080000000000000000000000003f5876a2b06e54949ab106651ab6694d0289b2b40000000000000000000000009256fd872118ed3a97754b0fb42c15015d17e0cc0000000000000000000000005c8ef9ca7b43c93ac4a146bef77fafbc7d3e69b70000000000000000000000001486157d505c7f7e546ad00e3e2eee25bf665c9b0000000000000000000000002ec991b5c0b742abd9d2ea31fe6c14a85e91c821000000000000000000000000f505462a29e36e26f25ef0175ca1ecba09cc118f000000000000000000000000027c1882b975e2cd771ae068b0389fa38b9dda7300000000000000000000000030932ac1f0477fbd63e4c5be1928f367a58a45a100000000000000000000000000000000000000000000000000000000000000050000000000000000000000007bcb28bcde9938ff530c0489be53a2af9afd2227000000000000000000000000971da4cc7a0d1b1f9f8376098e0797e0e099e00f000000000000000000000000111e9d4529b6f5831018675f91ca98f98bc4d3770000000000000000000000000aa1dea04a534f2e22a690510fe97fcc9d8a8ad500000000000000000000000089dc2de633844e041583b788c3edf1d3728aee86000000000000000000000000000000000000000000000000000000000000000500000000000000000000000082601fa5fabb9a0f2f5e3f852c02afe2f3c234e5000000000000000000000000d7779a66e81cd8e834039794397a832d5d42070a000000000000000000000000bb0ae199105e8bcce1b4a54c804a5c44299675a2000000000000000000000000497a3b0c0addd4db5c44d2c11329465135e9ba200000000000000000000000001a2559f2675d7c67995d6cb0a9c0a79fe21e3624
-----Decoded View---------------
Arg [0] : params (bytes): 0x00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000080000000000000000000000003f5876a2b06e54949ab106651ab6694d0289b2b40000000000000000000000009256fd872118ed3a97754b0fb42c15015d17e0cc0000000000000000000000005c8ef9ca7b43c93ac4a146bef77fafbc7d3e69b70000000000000000000000001486157d505c7f7e546ad00e3e2eee25bf665c9b0000000000000000000000002ec991b5c0b742abd9d2ea31fe6c14a85e91c821000000000000000000000000f505462a29e36e26f25ef0175ca1ecba09cc118f000000000000000000000000027c1882b975e2cd771ae068b0389fa38b9dda7300000000000000000000000030932ac1f0477fbd63e4c5be1928f367a58a45a100000000000000000000000000000000000000000000000000000000000000050000000000000000000000007bcb28bcde9938ff530c0489be53a2af9afd2227000000000000000000000000971da4cc7a0d1b1f9f8376098e0797e0e099e00f000000000000000000000000111e9d4529b6f5831018675f91ca98f98bc4d3770000000000000000000000000aa1dea04a534f2e22a690510fe97fcc9d8a8ad500000000000000000000000089dc2de633844e041583b788c3edf1d3728aee86000000000000000000000000000000000000000000000000000000000000000500000000000000000000000082601fa5fabb9a0f2f5e3f852c02afe2f3c234e5000000000000000000000000d7779a66e81cd8e834039794397a832d5d42070a000000000000000000000000bb0ae199105e8bcce1b4a54c804a5c44299675a2000000000000000000000000497a3b0c0addd4db5c44d2c11329465135e9ba200000000000000000000000001a2559f2675d7c67995d6cb0a9c0a79fe21e3624
-----Encoded View---------------
29 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000360
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [4] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [6] : 00000000000000000000000000000000000000000000000000000000000002a0
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [9] : 0000000000000000000000003f5876a2b06e54949ab106651ab6694d0289b2b4
Arg [10] : 0000000000000000000000009256fd872118ed3a97754b0fb42c15015d17e0cc
Arg [11] : 0000000000000000000000005c8ef9ca7b43c93ac4a146bef77fafbc7d3e69b7
Arg [12] : 0000000000000000000000001486157d505c7f7e546ad00e3e2eee25bf665c9b
Arg [13] : 0000000000000000000000002ec991b5c0b742abd9d2ea31fe6c14a85e91c821
Arg [14] : 000000000000000000000000f505462a29e36e26f25ef0175ca1ecba09cc118f
Arg [15] : 000000000000000000000000027c1882b975e2cd771ae068b0389fa38b9dda73
Arg [16] : 00000000000000000000000030932ac1f0477fbd63e4c5be1928f367a58a45a1
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [18] : 0000000000000000000000007bcb28bcde9938ff530c0489be53a2af9afd2227
Arg [19] : 000000000000000000000000971da4cc7a0d1b1f9f8376098e0797e0e099e00f
Arg [20] : 000000000000000000000000111e9d4529b6f5831018675f91ca98f98bc4d377
Arg [21] : 0000000000000000000000000aa1dea04a534f2e22a690510fe97fcc9d8a8ad5
Arg [22] : 00000000000000000000000089dc2de633844e041583b788c3edf1d3728aee86
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [24] : 00000000000000000000000082601fa5fabb9a0f2f5e3f852c02afe2f3c234e5
Arg [25] : 000000000000000000000000d7779a66e81cd8e834039794397a832d5d42070a
Arg [26] : 000000000000000000000000bb0ae199105e8bcce1b4a54c804a5c44299675a2
Arg [27] : 000000000000000000000000497a3b0c0addd4db5c44d2c11329465135e9ba20
Arg [28] : 0000000000000000000000001a2559f2675d7c67995d6cb0a9c0a79fe21e3624Deployed Bytecode Sourcemap
56363:563:4:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5132:18;;-1:-1:-1;;;5132:18:4;;216:2:15;5132:18:4;;;198:21:15;255:1;235:18;;;228:29;-1:-1:-1;;;273:18:15;;;266:38;321:18;;5132::4;;;;;;;56363:563;4512:66;4624:22;2536:20;;;:15;:20;;;;-1:-1:-1;;;;;2536:20:4;;4732:14;;4624:22;4713:34;4833:1;4830;4814:14;4811:1;4795:14;4788:5;4775:60;4870:16;4867:1;4864;4849:38;4908:6;4927:66;;;;5042:16;5039:1;5032:27;4927:66;4962:16;4959:1;4952:27;2456:107;;;;;;;;;;-1:-1:-1;2456:107:4;;;;;:::i;:::-;2510:7;2536:20;;;:15;:20;;;;;;-1:-1:-1;;;;;2536:20:4;;2456:107;;;;-1:-1:-1;;;;;699:32:15;;;681:51;;669:2;654:18;2456:107:4;;;;;;;;2683:98;;;;;;;;;;-1:-1:-1;2683:98:4;;;;;:::i;:::-;2734:4;2757:17;;;:12;:17;;;;;;;;;2683:98;;;;908:14:15;;901:22;883:41;;871:2;856:18;2683:98:4;743:187:15;2339:111:4;;;;;;;;;;-1:-1:-1;2339:111:4;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;56875:49::-;;;;;;;;;;-1:-1:-1;56875:49:4;;;;;:::i;:::-;;;;;;2232:101;;;;;;;;;;-1:-1:-1;2232:101:4;;;;;:::i;:::-;2283:7;2309:17;;;;;;;;;;;;2232:101;;;;2379:25:15;;;2367:2;2352:18;2232:101:4;2233:177:15;2569:108:4;;;;;;;;;;-1:-1:-1;2569:108:4;;;;;:::i;:::-;;:::i;2787:98::-;;;;;;;;;;-1:-1:-1;2787:98:4;;;;;:::i;:::-;2837:6;2862:16;;;:11;:16;;;;;;;2787:98;2339:111;2424:19;;;;:14;:19;;;;;2417:26;;2392:13;;2424:19;2417:26;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2339:111;;;:::o;2569:108::-;2652:18;;;;:13;:18;;;;;2645:25;;2621:12;;2652:18;2645:25;;;:::i;350:180:15:-;409:6;462:2;450:9;441:7;437:23;433:32;430:52;;;478:1;475;468:12;430:52;-1:-1:-1;501:23:15;;350:180;-1:-1:-1;350:180:15:o;935:472::-;977:3;1015:5;1009:12;1042:6;1037:3;1030:19;1067:1;1077:162;1091:6;1088:1;1085:13;1077:162;;;1153:4;1209:13;;;1205:22;;1199:29;1181:11;;;1177:20;;1170:59;1106:12;1077:162;;;1257:6;1254:1;1251:13;1248:87;;;1323:1;1316:4;1307:6;1302:3;1298:16;1294:27;1287:38;1248:87;-1:-1:-1;1389:2:15;1368:15;-1:-1:-1;;1364:29:15;1355:39;;;;1396:4;1351:50;;935:472;-1:-1:-1;;935:472:15:o;1412:220::-;1561:2;1550:9;1543:21;1524:4;1581:45;1622:2;1611:9;1607:18;1599:6;1581:45;:::i;:::-;1573:53;1412:220;-1:-1:-1;;;1412:220:15:o;1637:591::-;1707:6;1715;1768:2;1756:9;1747:7;1743:23;1739:32;1736:52;;;1784:1;1781;1774:12;1736:52;1824:9;1811:23;1853:18;1894:2;1886:6;1883:14;1880:34;;;1910:1;1907;1900:12;1880:34;1948:6;1937:9;1933:22;1923:32;;1993:7;1986:4;1982:2;1978:13;1974:27;1964:55;;2015:1;2012;2005:12;1964:55;2055:2;2042:16;2081:2;2073:6;2070:14;2067:34;;;2097:1;2094;2087:12;2067:34;2142:7;2137:2;2128:6;2124:2;2120:15;2116:24;2113:37;2110:57;;;2163:1;2160;2153:12;2110:57;2194:2;2186:11;;;;;2216:6;;-1:-1:-1;1637:591:15;;-1:-1:-1;;;;1637:591:15:o;2818:380::-;2897:1;2893:12;;;;2940;;;2961:61;;3015:4;3007:6;3003:17;2993:27;;2961:61;3068:2;3060:6;3057:14;3037:18;3034:38;3031:161;;;3114:10;3109:3;3105:20;3102:1;3095:31;3149:4;3146:1;3139:15;3177:4;3174:1;3167:15;3031:161;;2818:380;;;:::o
Swarm Source
ipfs://650a3d16decebe4547c4596d6c58eb39fc086a5e2785d73373e140c33d3203ab
Loading...LoadingLoading...LoadingABI for the implementation contract at 0x99b5fa03a5ea4315725c43346e55a6a6fbd94098, likely using a custom proxy implementation.
Previously recorded to be on 0xed9938294acf9ee52d097133ca2caaff0c804f16.Learn more about proxy contracts in our Knowledge Base
Loading...LoadingABI for the implementation contract at 0x99b5fa03a5ea4315725c43346e55a6a6fbd94098, likely using a custom proxy implementation.
Previously recorded to be on 0xed9938294acf9ee52d097133ca2caaff0c804f16.Learn more about proxy contracts in our Knowledge Base
Loading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingMultichain Portfolio | 30 Chains
Chain Token Portfolio % Price Amount Value ETH 32.09% $0.998929 32,967,344.0901 $32,932,036.06 ETH 27.81% $95,187 299.8305 $28,539,969.86 ETH 22.23% $3,300.22 6,911.2576 $22,808,654.47 ETH 5.23% $3,907.48 1,373.0327 $5,365,097.88 ETH 4.62% $1.04 4,550,035.2228 $4,736,586.67 ETH 2.92% $0.997975 2,997,327.9459 $2,991,258.36 ETH 1.45% $0.997019 1,491,504.449 $1,487,058.27 ETH 0.71% $0.999103 732,039.1882 $731,382.55 ETH 0.65% $0.015603 42,456,122.11 $662,442.45 ETH 0.45% $0.027146 16,906,725.3861 $458,944.22 ETH 0.42% $3,281.8 130.757 $429,118.96 ETH 0.35% $22.5 15,737.4782 $354,093.26 ETH 0.30% $0.994291 311,668.0836 $309,888.77 ETH 0.19% $0.000018 10,996,185,716.4629 $195,402.22 ETH 0.18% $3,610.57 50.521 $182,409.47 ETH 0.08% $0.000021 3,811,817,120.2823 $81,687.24 ETH 0.07% $3,664.97 20.5908 $75,464.5 ETH 0.04% $0.533201 79,716.4033 $42,504.87 ETH 0.03% $2.51 12,303.3522 $30,881.41 ETH 0.02% $2.98 5,951.7451 $17,736.2 ETH 0.02% $3.2 5,486.4543 $17,556.65 ETH 0.01% $3,687.03 3.3415 $12,320.08 ETH <0.01% $1,564.23 4.0725 $6,370.36 ETH <0.01% $13.76 406.6977 $5,596.16 ETH Ether (ETH)<0.01% $3,300.22 0.9063 $2,991.01 ETH <0.01% $0.004051 338,479.4479 $1,371.17 ETH <0.01% $1.09 868.7939 $946.99 ETH <0.01% $3,567.71 0.1434 $511.49 ETH <0.01% $324.29 0.76 $246.46 ETH <0.01% $3,287.97 0.0451 $148.31 ETH <0.01% $0.420204 54.2856 $22.81 ETH <0.01% <$0.000001 247,339,202.9321 $17.93 ETH <0.01% $1.15 6 $6.9 ETH <0.01% $0.312673 10 $3.13 ETH <0.01% $0.02476 50.0029 $1.24 ETH <0.01% $0.000759 1,000 $0.7592 GLMR 0.12% $6.89 17,479.5694 $120,512.95 GLMR <0.01% $0.232711 6,710.8604 $1,561.69 GLMR <0.01% $0.995223 20 $19.9 BSC <0.01% $1.86 4,984.163 $9,290.75 BASE <0.01% $0.031663 100 $3.17 POL <0.01% $0.000296 500 $0.1477 POL <0.01% $0.474273 0.0018 $0.000854 Loading...Loading[ Download: CSV Export ][ Download: CSV Export ]A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.
Address QR Code
My Address - Private Name Tag or Note
My Name Tag:
Private Name Tags (up to 35 characters) can be used for easy identification of addressesPrivate Note:
A private note (up to 500 characters) can be attached to this address.
Please DO NOT store any passwords or private keys here.Compiler specific version warnings:
The compiled contract might be susceptible to VerbatimInvalidDeduplication (low-severity), FullInlinerNonExpressionSplitArgumentEvaluationOrder (low-severity), MissingSideEffectsOnSelectorAccess (low-severity), AbiReencodingHeadOverflowWithStaticArrayCleanup (medium-severity), DirtyBytesArrayToStorage (low-severity), DataLocationChangeInInternalOverride (very low-severity), NestedCalldataArrayAbiReencodingSizeValidation (very low-severity) Solidity Compiler Bugs.
Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.SignIn
Address Cards
To use this feature, please login to your Etherscan account and return to this page.Before You Copy
Transaction Private Note
This website uses cookies to improve your experience. By continuing to use this website, you agree to its Terms and Privacy Policy.