ETH Price: $2,386.65 (-1.68%)

Transaction Decoder

Block:
15438928 at Aug-30-2022 06:55:08 AM +UTC
Transaction Fee:
0.000595841478575742 ETH $1.42
Gas Used:
69,657 Gas / 8.553935406 Gwei

Emitted Events:

267 IceToken.Transfer( from=[Receiver] nICE, to=[Sender] 0x4366f380292abcf2a62457dbf8bd19924c3731fe, value=2562707167038262908220 )
268 nICE.Transfer( _from=[Sender] 0x4366f380292abcf2a62457dbf8bd19924c3731fe, _to=0x0000000000000000000000000000000000000000, _value=2299797828389150417155 )

Account State Difference:

  Address   Before After State Difference Code
0x4366F380...24c3731fE
0.747026155944818974 Eth
Nonce: 480
0.746430314466243232 Eth
Nonce: 481
0.000595841478575742
(Miner: 0xb7e...707)
9.3253852266196093 Eth9.3254897121196093 Eth0.0001044855
0xf16e81dc...855B87DF9
0xfF3Ac80c...d54C7342f

Execution Trace

nICE.burn( to=0x4366F380292ABcf2a62457DbF8Bd19924c3731fE, shares=2299797828389150417155 ) => ( True )
  • IceToken.balanceOf( account=0xfF3Ac80c1caA08Cbd43a7e90d20c398d54C7342f ) => ( 7229155901232289261636933 )
  • IceToken.transfer( recipient=0x4366F380292ABcf2a62457DbF8Bd19924c3731fE, amount=2562707167038262908220 ) => ( True )
    File 1 of 2: nICE
    // SPDX-License-Identifier: MIXED
    
    // File @boringcrypto/boring-solidity/contracts/libraries/[email protected]
    // License-Identifier: MIT
    pragma solidity 0.6.12;
    pragma experimental ABIEncoderV2;
    
    /// @notice A library for performing overflow-/underflow-safe math,
    /// updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math).
    library BoringMath {
        function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
            require((c = a + b) >= b, "BoringMath: Add Overflow");
        }
    
        function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
            require((c = a - b) <= a, "BoringMath: Underflow");
        }
    
        function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
            require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
        }
    
        function to128(uint256 a) internal pure returns (uint128 c) {
            require(a <= uint128(-1), "BoringMath: uint128 Overflow");
            c = uint128(a);
        }
    
        function to64(uint256 a) internal pure returns (uint64 c) {
            require(a <= uint64(-1), "BoringMath: uint64 Overflow");
            c = uint64(a);
        }
    
        function to32(uint256 a) internal pure returns (uint32 c) {
            require(a <= uint32(-1), "BoringMath: uint32 Overflow");
            c = uint32(a);
        }
    }
    
    /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint128.
    library BoringMath128 {
        function add(uint128 a, uint128 b) internal pure returns (uint128 c) {
            require((c = a + b) >= b, "BoringMath: Add Overflow");
        }
    
        function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {
            require((c = a - b) <= a, "BoringMath: Underflow");
        }
    }
    
    /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint64.
    library BoringMath64 {
        function add(uint64 a, uint64 b) internal pure returns (uint64 c) {
            require((c = a + b) >= b, "BoringMath: Add Overflow");
        }
    
        function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {
            require((c = a - b) <= a, "BoringMath: Underflow");
        }
    }
    
    /// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint32.
    library BoringMath32 {
        function add(uint32 a, uint32 b) internal pure returns (uint32 c) {
            require((c = a + b) >= b, "BoringMath: Add Overflow");
        }
    
        function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {
            require((c = a - b) <= a, "BoringMath: Underflow");
        }
    }
    
    // File @boringcrypto/boring-solidity/contracts/interfaces/[email protected]
    // License-Identifier: MIT
    
    interface IERC20 {
        function totalSupply() external view returns (uint256);
    
        function balanceOf(address account) external view returns (uint256);
    
        function allowance(address owner, address spender) external view returns (uint256);
    
        function approve(address spender, uint256 amount) external returns (bool);
    
        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(address indexed owner, address indexed spender, uint256 value);
    
        /// @notice EIP 2612
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
    }
    
    // File @boringcrypto/boring-solidity/contracts/libraries/[email protected]
    // License-Identifier: MIT
    
    // solhint-disable avoid-low-level-calls
    
    library BoringERC20 {
        bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()
        bytes4 private constant SIG_NAME = 0x06fdde03; // name()
        bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()
        bytes4 private constant SIG_TRANSFER = 0xa9059cbb; // transfer(address,uint256)
        bytes4 private constant SIG_TRANSFER_FROM = 0x23b872dd; // transferFrom(address,address,uint256)
    
        function returnDataToString(bytes memory data) internal pure returns (string memory) {
            if (data.length >= 64) {
                return abi.decode(data, (string));
            } else if (data.length == 32) {
                uint8 i = 0;
                while(i < 32 && data[i] != 0) {
                    i++;
                }
                bytes memory bytesArray = new bytes(i);
                for (i = 0; i < 32 && data[i] != 0; i++) {
                    bytesArray[i] = data[i];
                }
                return string(bytesArray);
            } else {
                return "???";
            }
        }
    
        /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
        /// @param token The address of the ERC-20 token contract.
        /// @return (string) Token symbol.
        function safeSymbol(IERC20 token) internal view returns (string memory) {
            (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));
            return success ? returnDataToString(data) : "???";
        }
    
        /// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
        /// @param token The address of the ERC-20 token contract.
        /// @return (string) Token name.
        function safeName(IERC20 token) internal view returns (string memory) {
            (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));
            return success ? returnDataToString(data) : "???";
        }
    
        /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
        /// @param token The address of the ERC-20 token contract.
        /// @return (uint8) Token decimals.
        function safeDecimals(IERC20 token) internal view returns (uint8) {
            (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));
            return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
        }
    
        /// @notice Provides a safe ERC20.transfer version for different ERC-20 implementations.
        /// Reverts on a failed transfer.
        /// @param token The address of the ERC-20 token.
        /// @param to Transfer tokens to.
        /// @param amount The token amount.
        function safeTransfer(
            IERC20 token,
            address to,
            uint256 amount
        ) internal {
            (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER, to, amount));
            require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed");
        }
    
        /// @notice Provides a safe ERC20.transferFrom version for different ERC-20 implementations.
        /// Reverts on a failed transfer.
        /// @param token The address of the ERC-20 token.
        /// @param from Transfer tokens from.
        /// @param to Transfer tokens to.
        /// @param amount The token amount.
        function safeTransferFrom(
            IERC20 token,
            address from,
            address to,
            uint256 amount
        ) internal {
            (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER_FROM, from, to, amount));
            require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: TransferFrom failed");
        }
    }
    
    // File @boringcrypto/boring-solidity/contracts/[email protected]
    // License-Identifier: MIT
    // Based on code and smartness by Ross Campbell and Keno
    // Uses immutable to store the domain separator to reduce gas usage
    // If the chain id changes due to a fork, the forked chain will calculate on the fly.
    
    // solhint-disable no-inline-assembly
    
    contract Domain {
        bytes32 private constant DOMAIN_SEPARATOR_SIGNATURE_HASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
        // See https://eips.ethereum.org/EIPS/eip-191
        string private constant EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA = "\x19\x01";
    
        // solhint-disable var-name-mixedcase
        bytes32 private immutable _DOMAIN_SEPARATOR;
        uint256 private immutable DOMAIN_SEPARATOR_CHAIN_ID;    
    
        /// @dev Calculate the DOMAIN_SEPARATOR
        function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) {
            return keccak256(
                abi.encode(
                    DOMAIN_SEPARATOR_SIGNATURE_HASH,
                    chainId,
                    address(this)
                )
            );
        }
    
        constructor() public {
            uint256 chainId; assembly {chainId := chainid()}
            _DOMAIN_SEPARATOR = _calculateDomainSeparator(DOMAIN_SEPARATOR_CHAIN_ID = chainId);
        }
    
        /// @dev Return the DOMAIN_SEPARATOR
        // It's named internal to allow making it public from the contract that uses it by creating a simple view function
        // with the desired public name, such as DOMAIN_SEPARATOR or domainSeparator.
        // solhint-disable-next-line func-name-mixedcase
        function _domainSeparator() internal view returns (bytes32) {
            uint256 chainId; assembly {chainId := chainid()}
            return chainId == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(chainId);
        }
    
        function _getDigest(bytes32 dataHash) internal view returns (bytes32 digest) {
            digest =
                keccak256(
                    abi.encodePacked(
                        EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA,
                        _domainSeparator(),
                        dataHash
                    )
                );
        }
    }
    
    // File contracts/nICE.sol
    //License-Identifier: MIT
    
    // Staking in sSpell inspired by Chef Nomi's SushiBar - MIT license (originally WTFPL)
    // modified by BoringCrypto for DictatorDAO
    
    contract nICE is IERC20, Domain {
        using BoringMath for uint256;
        using BoringMath128 for uint128;
        using BoringERC20 for IERC20;
    
        string public constant symbol = "nICE";
        string public constant name = "Staked Ice Tokens";
        uint8 public constant decimals = 18;
        uint256 public override totalSupply;
        uint256 private constant LOCK_TIME = 24 hours;
    
        IERC20 public immutable token;
    
        constructor(IERC20 _token) public {
            token = _token;
        }
    
        struct User {
            uint128 balance;
            uint128 lockedUntil;
        }
    
        /// @notice owner > balance mapping.
        mapping(address => User) public users;
        /// @notice owner > spender > allowance mapping.
        mapping(address => mapping(address => uint256)) public override allowance;
        /// @notice owner > nonce mapping. Used in `permit`.
        mapping(address => uint256) public nonces;
    
        event Transfer(address indexed _from, address indexed _to, uint256 _value);
        event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    
        function balanceOf(address user) public view override returns (uint256 balance) {
            return users[user].balance;
        }
    
        function _transfer(
            address from,
            address to,
            uint256 shares
        ) internal {
            User memory fromUser = users[from];
            require(block.timestamp >= fromUser.lockedUntil, "Locked");
            if (shares != 0) {
                require(fromUser.balance >= shares, "Low balance");
                if (from != to) {
                    require(to != address(0), "Zero address"); // Moved down so other failed calls safe some gas
                    User memory toUser = users[to];
                    users[from].balance = fromUser.balance - shares.to128(); // Underflow is checked
                    users[to].balance = toUser.balance + shares.to128(); // Can't overflow because totalSupply would be greater than 2^128-1;
                }
            }
            emit Transfer(from, to, shares);
        }
    
        function _useAllowance(address from, uint256 shares) internal {
            if (msg.sender == from) {
                return;
            }
            uint256 spenderAllowance = allowance[from][msg.sender];
            // If allowance is infinite, don't decrease it to save on gas (breaks with EIP-20).
            if (spenderAllowance != type(uint256).max) {
                require(spenderAllowance >= shares, "Low allowance");
                allowance[from][msg.sender] = spenderAllowance - shares; // Underflow is checked
            }
        }
    
        /// @notice Transfers `shares` tokens from `msg.sender` to `to`.
        /// @param to The address to move the tokens.
        /// @param shares of the tokens to move.
        /// @return (bool) Returns True if succeeded.
        function transfer(address to, uint256 shares) public returns (bool) {
            _transfer(msg.sender, to, shares);
            return true;
        }
    
        /// @notice Transfers `shares` tokens from `from` to `to`. Caller needs approval for `from`.
        /// @param from Address to draw tokens from.
        /// @param to The address to move the tokens.
        /// @param shares The token shares to move.
        /// @return (bool) Returns True if succeeded.
        function transferFrom(
            address from,
            address to,
            uint256 shares
        ) public returns (bool) {
            _useAllowance(from, shares);
            _transfer(from, to, shares);
            return true;
        }
    
        /// @notice Approves `amount` from sender to be spend by `spender`.
        /// @param spender Address of the party that can draw from msg.sender's account.
        /// @param amount The maximum collective amount that `spender` can draw.
        /// @return (bool) Returns True if approved.
        function approve(address spender, uint256 amount) public override returns (bool) {
            allowance[msg.sender][spender] = amount;
            emit Approval(msg.sender, spender, amount);
            return true;
        }
    
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32) {
            return _domainSeparator();
        }
    
        // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        bytes32 private constant PERMIT_SIGNATURE_HASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
    
        /// @notice Approves `value` from `owner_` to be spend by `spender`.
        /// @param owner_ Address of the owner.
        /// @param spender The address of the spender that gets approved to draw from `owner_`.
        /// @param value The maximum collective amount that `spender` can draw.
        /// @param deadline This permit must be redeemed before this deadline (UTC timestamp in seconds).
        function permit(
            address owner_,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external override {
            require(owner_ != address(0), "Zero owner");
            require(block.timestamp < deadline, "Expired");
            require(
                ecrecover(_getDigest(keccak256(abi.encode(PERMIT_SIGNATURE_HASH, owner_, spender, value, nonces[owner_]++, deadline))), v, r, s) ==
                    owner_,
                "Invalid Sig"
            );
            allowance[owner_][spender] = value;
            emit Approval(owner_, spender, value);
        }
    
        /// math is ok, because amount, totalSupply and shares is always 0 <= amount <= 69.000.000 * 10^18
        /// theoretically you can grow the amount/share ratio, but it's not practical and useless
        function mint(uint256 amount) public returns (bool) {
            require(msg.sender != address(0), "Zero address");
            User memory user = users[msg.sender];
    
            uint256 totalTokens = token.balanceOf(address(this));
            uint256 shares = totalSupply == 0 ? amount : (amount * totalSupply) / totalTokens;
            user.balance += shares.to128();
            user.lockedUntil = (block.timestamp + LOCK_TIME).to128();
            users[msg.sender] = user;
            totalSupply += shares;
    
            token.safeTransferFrom(msg.sender, address(this), amount);
    
            emit Transfer(address(0), msg.sender, shares);
            return true;
        }
    
        function _burn(
            address from,
            address to,
            uint256 shares
        ) internal {
            require(to != address(0), "Zero address");
            User memory user = users[from];
            require(block.timestamp >= user.lockedUntil, "Locked");
            uint256 amount = (shares * token.balanceOf(address(this))) / totalSupply;
            users[from].balance = user.balance.sub(shares.to128()); // Must check underflow
            totalSupply -= shares;
    
            token.safeTransfer(to, amount);
    
            emit Transfer(from, address(0), shares);
        }
    
        function burn(address to, uint256 shares) public returns (bool) {
            _burn(msg.sender, to, shares);
            return true;
        }
    
        function burnFrom(
            address from,
            address to,
            uint256 shares
        ) public returns (bool) {
            _useAllowance(from, shares);
            _burn(from, to, shares);
            return true;
        }
    }

    File 2 of 2: IceToken
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    /*
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
    
        function _msgData() internal view virtual returns (bytes calldata) {
            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
            return msg.data;
        }
    }
    
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor () {
            address msgSender = _msgSender();
            _owner = msgSender;
            emit OwnershipTransferred(address(0), msgSender);
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    /**
     * @title Counters
     * @author Matt Condon (@shrugs)
     * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
     *
     * Include with `using Counters for Counters.Counter;`
     */
    library Counters {
        struct Counter {
            // This variable should never be directly accessed by users of the library: interactions must be restricted to
            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
            // this feature: see https://github.com/ethereum/solidity/issues/4637
            uint256 _value; // default: 0
        }
    
        function current(Counter storage counter) internal view returns (uint256) {
            return counter._value;
        }
    
        function increment(Counter storage counter) internal {
            unchecked {
                counter._value += 1;
            }
        }
    
        function decrement(Counter storage counter) internal {
            uint256 value = counter._value;
            require(value > 0, "Counter: decrement overflow");
            unchecked {
                counter._value = value - 1;
            }
        }
    }
    
    /**
     * @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) {
            // Check the signature length
            if (signature.length != 65) {
                revert("ECDSA: invalid signature length");
            }
    
            // 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)))
            }
    
            return recover(hash, v, r, s);
        }
    
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
            // 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, "ECDSA: invalid signature 's' value");
            require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
    
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            require(signer != address(0), "ECDSA: invalid signature");
    
            return signer;
        }
    
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
        }
    
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        }
    }
    
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
     *
     * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
     * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
     * they need in their contracts using a combination of `abi.encode` and `keccak256`.
     *
     * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
     * ({_hashTypedDataV4}).
     *
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
     * the chain id to protect against replay attacks on an eventual fork of the chain.
     *
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
     *
     * _Available since v3.4._
     */
    abstract contract EIP712 {
        /* solhint-disable var-name-mixedcase */
        // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
        // invalidate the cached domain separator if the chain id changes.
        bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
        uint256 private immutable _CACHED_CHAIN_ID;
    
        bytes32 private immutable _HASHED_NAME;
        bytes32 private immutable _HASHED_VERSION;
        bytes32 private immutable _TYPE_HASH;
        /* solhint-enable var-name-mixedcase */
    
        /**
         * @dev Initializes the domain separator and parameter caches.
         *
         * The meaning of `name` and `version` is specified in
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
         *
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
         * - `version`: the current major version of the signing domain.
         *
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
         * contract upgrade].
         */
        constructor(string memory name, string memory version) {
            bytes32 hashedName = keccak256(bytes(name));
            bytes32 hashedVersion = keccak256(bytes(version));
            bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
            _HASHED_NAME = hashedName;
            _HASHED_VERSION = hashedVersion;
            _CACHED_CHAIN_ID = block.chainid;
            _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
            _TYPE_HASH = typeHash;
        }
    
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            if (block.chainid == _CACHED_CHAIN_ID) {
                return _CACHED_DOMAIN_SEPARATOR;
            } else {
                return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
            }
        }
    
        function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
            return keccak256(
                abi.encode(
                    typeHash,
                    name,
                    version,
                    block.chainid,
                    address(this)
                )
            );
        }
    
        /**
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
         * function returns the hash of the fully encoded EIP712 message for this domain.
         *
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
         *
         * ```solidity
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
         *     keccak256("Mail(address to,string contents)"),
         *     mailTo,
         *     keccak256(bytes(mailContents))
         * )));
         * address signer = ECDSA.recover(digest, signature);
         * ```
         */
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
            return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
        }
    }
    
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         */
        function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
    
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
    
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    
    /**
     * @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);
    }
    
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
    
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
    
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    
    /**
     * @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, IERC20Metadata {
        mapping (address => uint256) internal _balances;
    
        mapping (address => mapping (address => uint256)) private _allowances;
    
        uint256 internal _totalSupply;
    
        string private _name;
        string private _symbol;
    
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * The defaut value of {decimals} is 18. To select a different value for
         * {decimals} you should overload it.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor (string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
    
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
    
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
    
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless this function is
         * overloaded;
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
    
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
    
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
    
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `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-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
    
        /**
         * @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);
    
            uint256 currentAllowance = _allowances[sender][_msgSender()];
            require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
            _approve(sender, _msgSender(), currentAllowance - 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, _allowances[_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) {
            uint256 currentAllowance = _allowances[_msgSender()][spender];
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            _approve(_msgSender(), spender, currentAllowance - 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), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
    
            uint256 senderBalance = _balances[sender];
            require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
            _balances[sender] = senderBalance - amount;
            _balances[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), "ERC20: mint to the zero address");
    
            _totalSupply += amount;
            _balances[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), "ERC20: burn from the zero address");
    
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            _balances[account] = accountBalance - 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), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
    
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
    }
    
    /**
     * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     *
     * _Available since v3.4._
     */
    abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
        using Counters for Counters.Counter;
    
        mapping (address => Counters.Counter) private _nonces;
    
        // solhint-disable-next-line var-name-mixedcase
        bytes32 private immutable _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    
        /**
         * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
         *
         * It's a good idea to use the same `name` that is defined as the ERC20 token name.
         */
        constructor(string memory name) EIP712(name, "1") {
        }
    
        /**
         * @dev See {IERC20Permit-permit}.
         */
        function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override {
            // solhint-disable-next-line not-rely-on-time
            require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
    
            bytes32 structHash = keccak256(
                abi.encode(
                    _PERMIT_TYPEHASH,
                    owner,
                    spender,
                    value,
                    _useNonce(owner),
                    deadline
                )
            );
    
            bytes32 hash = _hashTypedDataV4(structHash);
    
            address signer = ECDSA.recover(hash, v, r, s);
            require(signer == owner, "ERC20Permit: invalid signature");
    
            _approve(owner, spender, value);
        }
    
        /**
         * @dev See {IERC20Permit-nonces}.
         */
        function nonces(address owner) public view virtual override returns (uint256) {
            return _nonces[owner].current();
        }
    
        /**
         * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view override returns (bytes32) {
            return _domainSeparatorV4();
        }
    
        /**
         * @dev "Consume a nonce": return the current value and increment.
         */
        function _useNonce(address owner) internal virtual returns (uint256 current) {
            Counters.Counter storage nonce = _nonces[owner];
            current = nonce.current();
            nonce.increment();
        }
    }
    
    /**
     * ICE is a token which enables the work and token economics of Popsicle finance -
     * a cross-chain yield enhancement platform focusing on
     * Automated Market-Making (AMM) Liquidity Providers (LP)
     */
    contract IceToken is ERC20Permit, Ownable {
        
        constructor()  ERC20("IceToken", "ICE") ERC20Permit("IceToken") 
        {
            
        }
        
        
        // Maximum total supply of the token (69M)
        uint256 private _maxTotalSupply = 69000000000000000000000000;
    
        
        // Returns maximum total supply of the token
        function getMaxTotalSupply() external view returns (uint256) {
            return _maxTotalSupply;
        }
        
        /** @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.
         * - can be called only by the owner of contract
         */
        function mint(address account, uint256 amount) external onlyOwner {
            _mint(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) external onlyOwner {
            _burn(account, 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 override {
            require(account != address(0), "ERC20: mint to the zero address");
            require(_totalSupply + amount <= _maxTotalSupply, "ERC20: minting more then MaxTotalSupply");
            
            _totalSupply += amount;
            _balances[account] += amount;
            emit Transfer(address(0), account, amount);
        }
    }