Transaction Hash:
Block:
16348619 at Jan-06-2023 03:24:47 PM +UTC
Transaction Fee:
0.000931363646165056 ETH
$1.69
Gas Used:
46,601 Gas / 19.985915456 Gwei
Emitted Events:
277 |
BurnableMintableCappedERC20.Approval( owner=[Sender] 0x94a6c29f6256a0fd68b81d4b6c47a9dce4d0dc49, spender=0x68b34658...D8665Fc45, value=115792089237316195423570985008687907853269984665640564039457584007913129639935 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x467719aD...d45a5E5f3 | |||||
0x94a6c29F...Ce4d0dc49 |
1.135496830319296853 Eth
Nonce: 20
|
1.134565466673131797 Eth
Nonce: 21
| 0.000931363646165056 | ||
0xDAFEA492...692c98Bc5
Miner
| (Flashbots: Builder) | 1.212694244315385756 Eth | 1.212705692683189813 Eth | 0.000011448367804057 |
Execution Trace
BurnableMintableCappedERC20.approve( spender=0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45, amount=115792089237316195423570985008687907853269984665640564039457584007913129639935 ) => ( True )
approve[ERC20 (ln:433)]
_approve[ERC20 (ln:434)]
InvalidAccount[ERC20 (ln:583)]
Approval[ERC20 (ln:586)]
// Sources flattened with hardhat v2.9.9 https://hardhat.org // File contracts/interfaces/IAxelarGateway.sol // SPDX-License-Identifier: MIT pragma solidity 0.8.9; interface IAxelarGateway { /**********\ |* Errors *| \**********/ error NotSelf(); error NotProxy(); error InvalidCodeHash(); error SetupFailed(); error InvalidAuthModule(); error InvalidTokenDeployer(); error InvalidAmount(); error InvalidChainId(); error InvalidCommands(); error TokenDoesNotExist(string symbol); error TokenAlreadyExists(string symbol); error TokenDeployFailed(string symbol); error TokenContractDoesNotExist(address token); error BurnFailed(string symbol); error MintFailed(string symbol); error InvalidSetMintLimitsParams(); error ExceedMintLimit(string symbol); /**********\ |* Events *| \**********/ event TokenSent(address indexed sender, string destinationChain, string destinationAddress, string symbol, uint256 amount); event ContractCall( address indexed sender, string destinationChain, string destinationContractAddress, bytes32 indexed payloadHash, bytes payload ); event ContractCallWithToken( address indexed sender, string destinationChain, string destinationContractAddress, bytes32 indexed payloadHash, bytes payload, string symbol, uint256 amount ); event Executed(bytes32 indexed commandId); event TokenDeployed(string symbol, address tokenAddresses); event ContractCallApproved( bytes32 indexed commandId, string sourceChain, string sourceAddress, address indexed contractAddress, bytes32 indexed payloadHash, bytes32 sourceTxHash, uint256 sourceEventIndex ); event ContractCallApprovedWithMint( bytes32 indexed commandId, string sourceChain, string sourceAddress, address indexed contractAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, bytes32 sourceTxHash, uint256 sourceEventIndex ); event TokenMintLimitUpdated(string symbol, uint256 limit); event OperatorshipTransferred(bytes newOperatorsData); event Upgraded(address indexed implementation); /********************\ |* Public Functions *| \********************/ function sendToken( string calldata destinationChain, string calldata destinationAddress, string calldata symbol, uint256 amount ) external; function callContract( string calldata destinationChain, string calldata contractAddress, bytes calldata payload ) external; function callContractWithToken( string calldata destinationChain, string calldata contractAddress, bytes calldata payload, string calldata symbol, uint256 amount ) external; function isContractCallApproved( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, address contractAddress, bytes32 payloadHash ) external view returns (bool); function isContractCallAndMintApproved( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, address contractAddress, bytes32 payloadHash, string calldata symbol, uint256 amount ) external view returns (bool); function validateContractCall( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash ) external returns (bool); function validateContractCallAndMint( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash, string calldata symbol, uint256 amount ) external returns (bool); /***********\ |* Getters *| \***********/ function authModule() external view returns (address); function tokenDeployer() external view returns (address); function tokenMintLimit(string memory symbol) external view returns (uint256); function tokenMintAmount(string memory symbol) external view returns (uint256); 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); function adminEpoch() external view returns (uint256); function adminThreshold(uint256 epoch) external view returns (uint256); function admins(uint256 epoch) external view returns (address[] memory); /*******************\ |* Admin Functions *| \*******************/ function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external; function upgrade( address newImplementation, bytes32 newImplementationCodeHash, bytes calldata setupParams ) external; /**********************\ |* External Functions *| \**********************/ function setup(bytes calldata params) external; function execute(bytes calldata input) external; } // File contracts/interfaces/IERC20Burn.sol interface IERC20Burn { function burn(bytes32 salt) external; } // File contracts/interfaces/IERC20BurnFrom.sol interface IERC20BurnFrom { function burnFrom(address account, uint256 amount) external; } // File contracts/interfaces/IERC20.sol /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { error InvalidAccount(); /** * @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); } // File contracts/interfaces/IERC20Permit.sol interface IERC20Permit { function DOMAIN_SEPARATOR() external view returns (bytes32); function nonces(address account) external view returns (uint256); function permit( address issuer, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; } // File contracts/interfaces/IOwnable.sol interface IOwnable { error NotOwner(); error InvalidOwner(); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function owner() external view returns (address); function transferOwnership(address newOwner) external; } // File contracts/interfaces/IMintableCappedERC20.sol interface IMintableCappedERC20 is IERC20, IERC20Permit, IOwnable { error CapExceeded(); function cap() external view returns (uint256); function mint(address account, uint256 amount) external; } // File contracts/interfaces/IBurnableMintableCappedERC20.sol interface IBurnableMintableCappedERC20 is IERC20Burn, IERC20BurnFrom, IMintableCappedERC20 { function depositAddress(bytes32 salt) external view returns (address); function burn(bytes32 salt) external; function burnFrom(address account, uint256 amount) external; } // File contracts/ERC20.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 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}. */ 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) external virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) external virtual override returns (bool) { _approve(msg.sender, 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 ) external virtual override returns (bool) { uint256 _allowance = allowance[sender][msg.sender]; if (_allowance != type(uint256).max) { _approve(sender, msg.sender, _allowance - amount); } _transfer(sender, recipient, 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) external virtual returns (bool) { _approve(msg.sender, spender, allowance[msg.sender][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) external virtual returns (bool) { _approve(msg.sender, spender, allowance[msg.sender][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 { if (sender == address(0) || recipient == address(0)) revert InvalidAccount(); 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 { if (account == address(0)) revert InvalidAccount(); 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 { if (account == address(0)) revert InvalidAccount(); 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 { if (owner == address(0) || spender == address(0)) revert InvalidAccount(); allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } } // File contracts/ERC20Permit.sol abstract contract ERC20Permit is IERC20, IERC20Permit, ERC20 { error PermitExpired(); error InvalidS(); error InvalidV(); error InvalidSignature(); bytes32 public immutable DOMAIN_SEPARATOR; string private constant EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA = '\x19\x01'; // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)') bytes32 private constant DOMAIN_TYPE_SIGNATURE_HASH = bytes32(0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f); // keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)') bytes32 private constant PERMIT_SIGNATURE_HASH = bytes32(0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9); mapping(address => uint256) public nonces; constructor(string memory name) { DOMAIN_SEPARATOR = keccak256( abi.encode(DOMAIN_TYPE_SIGNATURE_HASH, keccak256(bytes(name)), keccak256(bytes('1')), block.chainid, address(this)) ); } function permit( address issuer, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { if (block.timestamp > deadline) revert PermitExpired(); if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) revert InvalidS(); if (v != 27 && v != 28) revert InvalidV(); bytes32 digest = keccak256( abi.encodePacked( EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA, DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_SIGNATURE_HASH, issuer, spender, value, nonces[issuer]++, deadline)) ) ); address recoveredAddress = ecrecover(digest, v, r, s); if (recoveredAddress != issuer) revert InvalidSignature(); // _approve will revert if issuer is address(0x0) _approve(issuer, spender, value); } } // File contracts/Ownable.sol abstract contract Ownable is IOwnable { address public owner; constructor() { owner = msg.sender; emit OwnershipTransferred(address(0), msg.sender); } modifier onlyOwner() { if (owner != msg.sender) revert NotOwner(); _; } function transferOwnership(address newOwner) external virtual onlyOwner { if (newOwner == address(0)) revert InvalidOwner(); emit OwnershipTransferred(owner, newOwner); owner = newOwner; } } // File contracts/MintableCappedERC20.sol contract MintableCappedERC20 is IMintableCappedERC20, ERC20, ERC20Permit, Ownable { uint256 public immutable cap; constructor( string memory name, string memory symbol, uint8 decimals, uint256 capacity ) ERC20(name, symbol, decimals) ERC20Permit(name) Ownable() { cap = capacity; } function mint(address account, uint256 amount) external onlyOwner { uint256 capacity = cap; _mint(account, amount); if (capacity == 0) return; if (totalSupply > capacity) revert CapExceeded(); } } // File contracts/DepositHandler.sol contract DepositHandler { error IsLocked(); error NotContract(); uint256 internal constant IS_NOT_LOCKED = uint256(1); uint256 internal constant IS_LOCKED = uint256(2); uint256 internal _lockedStatus = IS_NOT_LOCKED; modifier noReenter() { if (_lockedStatus == IS_LOCKED) revert IsLocked(); _lockedStatus = IS_LOCKED; _; _lockedStatus = IS_NOT_LOCKED; } function execute(address callee, bytes calldata data) external noReenter returns (bool success, bytes memory returnData) { if (callee.code.length == 0) revert NotContract(); (success, returnData) = callee.call(data); } // NOTE: The gateway should always destroy the `DepositHandler` in the same runtime context that deploys it. function destroy(address etherDestination) external noReenter { selfdestruct(payable(etherDestination)); } } // File contracts/BurnableMintableCappedERC20.sol contract BurnableMintableCappedERC20 is IBurnableMintableCappedERC20, MintableCappedERC20 { constructor( string memory name, string memory symbol, uint8 decimals, uint256 capacity ) MintableCappedERC20(name, symbol, decimals, capacity) {} function depositAddress(bytes32 salt) public view returns (address) { /* 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(DepositHandler).creationCode))) ) ) ) ); } function burn(bytes32 salt) external onlyOwner { address account = depositAddress(salt); _burn(account, balanceOf[account]); } function burnFrom(address account, uint256 amount) external onlyOwner { uint256 _allowance = allowance[account][msg.sender]; if (_allowance != type(uint256).max) { _approve(account, msg.sender, _allowance - amount); } _burn(account, amount); } }