ETH Price: $2,546.65 (+0.48%)

Transaction Decoder

Block:
12418157 at May-12-2021 07:06:27 AM +UTC
Transaction Fee:
0.006877929916765248 ETH $17.52
Gas Used:
34,752 Gas / 197.914649999 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x00000000...438691c04
0x21F179DF...6880Cf49e
184.508210396423796677 Eth
Nonce: 67667
184.501332466507031429 Eth
Nonce: 67668
0.006877929916765248
0x5E705716...E9C974aBF
0 Eth
Nonce: 0
0 Eth
Nonce: 0
From: 0 To: 0
0x9f374f42...Bfe3e31d8
0 Eth
Nonce: 0
0 Eth
Nonce: 0
From: 0 To: 0
(Easy2Mine)
465.340855292386897949 Eth465.347733222303663197 Eth0.006877929916765248

Execution Trace

MEV Bot: 0x000...f56.0202f486( )
  • 0xf4863028b093fdac9cf7fd67c0df6866ac3c7a60.ced27fd1( )
    • UniswapV2Pair.CALL( )
    • GasToken2.free( value=2 ) => ( success=True )
      • 0x5e7057161c9b991274e0222d8318cb1e9c974abf.CALL( )
        • GasToken2.SELFDESTRUCT( )
        • 0x9f374f424e0b4cb0dae8899c3ad8b4bbfe3e31d8.CALL( )
          • GasToken2.SELFDESTRUCT( )
            File 1 of 2: UniswapV2Pair
            // File: contracts/interfaces/IUniswapV2Pair.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Pair {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                function MINIMUM_LIQUIDITY() external pure returns (uint);
                function factory() external view returns (address);
                function token0() external view returns (address);
                function token1() external view returns (address);
                function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                function price0CumulativeLast() external view returns (uint);
                function price1CumulativeLast() external view returns (uint);
                function kLast() external view returns (uint);
            
                function mint(address to) external returns (uint liquidity);
                function burn(address to) external returns (uint amount0, uint amount1);
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                function skim(address to) external;
                function sync() external;
            
                function initialize(address, address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2ERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2ERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            }
            
            // File: contracts/libraries/SafeMath.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
            
            library SafeMath {
                function add(uint x, uint y) internal pure returns (uint z) {
                    require((z = x + y) >= x, 'ds-math-add-overflow');
                }
            
                function sub(uint x, uint y) internal pure returns (uint z) {
                    require((z = x - y) <= x, 'ds-math-sub-underflow');
                }
            
                function mul(uint x, uint y) internal pure returns (uint z) {
                    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
                }
            }
            
            // File: contracts/UniswapV2ERC20.sol
            
            pragma solidity =0.5.16;
            
            
            
            contract UniswapV2ERC20 is IUniswapV2ERC20 {
                using SafeMath for uint;
            
                string public constant name = 'Uniswap V2';
                string public constant symbol = 'UNI-V2';
                uint8 public constant decimals = 18;
                uint  public totalSupply;
                mapping(address => uint) public balanceOf;
                mapping(address => mapping(address => uint)) public allowance;
            
                bytes32 public DOMAIN_SEPARATOR;
                // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                mapping(address => uint) public nonces;
            
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                constructor() public {
                    uint chainId;
                    assembly {
                        chainId := chainid
                    }
                    DOMAIN_SEPARATOR = keccak256(
                        abi.encode(
                            keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                            keccak256(bytes(name)),
                            keccak256(bytes('1')),
                            chainId,
                            address(this)
                        )
                    );
                }
            
                function _mint(address to, uint value) internal {
                    totalSupply = totalSupply.add(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(address(0), to, value);
                }
            
                function _burn(address from, uint value) internal {
                    balanceOf[from] = balanceOf[from].sub(value);
                    totalSupply = totalSupply.sub(value);
                    emit Transfer(from, address(0), value);
                }
            
                function _approve(address owner, address spender, uint value) private {
                    allowance[owner][spender] = value;
                    emit Approval(owner, spender, value);
                }
            
                function _transfer(address from, address to, uint value) private {
                    balanceOf[from] = balanceOf[from].sub(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(from, to, value);
                }
            
                function approve(address spender, uint value) external returns (bool) {
                    _approve(msg.sender, spender, value);
                    return true;
                }
            
                function transfer(address to, uint value) external returns (bool) {
                    _transfer(msg.sender, to, value);
                    return true;
                }
            
                function transferFrom(address from, address to, uint value) external returns (bool) {
                    if (allowance[from][msg.sender] != uint(-1)) {
                        allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
                    }
                    _transfer(from, to, value);
                    return true;
                }
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
                    require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
                    bytes32 digest = keccak256(
                        abi.encodePacked(
                            '\x19\x01',
                            DOMAIN_SEPARATOR,
                            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                        )
                    );
                    address recoveredAddress = ecrecover(digest, v, r, s);
                    require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
                    _approve(owner, spender, value);
                }
            }
            
            // File: contracts/libraries/Math.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing various math operations
            
            library Math {
                function min(uint x, uint y) internal pure returns (uint z) {
                    z = x < y ? x : y;
                }
            
                // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
                function sqrt(uint y) internal pure returns (uint z) {
                    if (y > 3) {
                        z = y;
                        uint x = y / 2 + 1;
                        while (x < z) {
                            z = x;
                            x = (y / x + x) / 2;
                        }
                    } else if (y != 0) {
                        z = 1;
                    }
                }
            }
            
            // File: contracts/libraries/UQ112x112.sol
            
            pragma solidity =0.5.16;
            
            // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
            
            // range: [0, 2**112 - 1]
            // resolution: 1 / 2**112
            
            library UQ112x112 {
                uint224 constant Q112 = 2**112;
            
                // encode a uint112 as a UQ112x112
                function encode(uint112 y) internal pure returns (uint224 z) {
                    z = uint224(y) * Q112; // never overflows
                }
            
                // divide a UQ112x112 by a uint112, returning a UQ112x112
                function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
                    z = x / uint224(y);
                }
            }
            
            // File: contracts/interfaces/IERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function decimals() external view returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            }
            
            // File: contracts/interfaces/IUniswapV2Factory.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Factory {
                event PairCreated(address indexed token0, address indexed token1, address pair, uint);
            
                function feeTo() external view returns (address);
                function feeToSetter() external view returns (address);
            
                function getPair(address tokenA, address tokenB) external view returns (address pair);
                function allPairs(uint) external view returns (address pair);
                function allPairsLength() external view returns (uint);
            
                function createPair(address tokenA, address tokenB) external returns (address pair);
            
                function setFeeTo(address) external;
                function setFeeToSetter(address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2Callee.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Callee {
                function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
            }
            
            // File: contracts/UniswapV2Pair.sol
            
            pragma solidity =0.5.16;
            
            
            
            
            
            
            
            
            contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
                using SafeMath  for uint;
                using UQ112x112 for uint224;
            
                uint public constant MINIMUM_LIQUIDITY = 10**3;
                bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
            
                address public factory;
                address public token0;
                address public token1;
            
                uint112 private reserve0;           // uses single storage slot, accessible via getReserves
                uint112 private reserve1;           // uses single storage slot, accessible via getReserves
                uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
            
                uint public price0CumulativeLast;
                uint public price1CumulativeLast;
                uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
            
                uint private unlocked = 1;
                modifier lock() {
                    require(unlocked == 1, 'UniswapV2: LOCKED');
                    unlocked = 0;
                    _;
                    unlocked = 1;
                }
            
                function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
                    _reserve0 = reserve0;
                    _reserve1 = reserve1;
                    _blockTimestampLast = blockTimestampLast;
                }
            
                function _safeTransfer(address token, address to, uint value) private {
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
                }
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                constructor() public {
                    factory = msg.sender;
                }
            
                // called once by the factory at time of deployment
                function initialize(address _token0, address _token1) external {
                    require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
                    token0 = _token0;
                    token1 = _token1;
                }
            
                // update reserves and, on the first call per block, price accumulators
                function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
                    require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
                    uint32 blockTimestamp = uint32(block.timestamp % 2**32);
                    uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
                    if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                        // * never overflows, and + overflow is desired
                        price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                        price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
                    }
                    reserve0 = uint112(balance0);
                    reserve1 = uint112(balance1);
                    blockTimestampLast = blockTimestamp;
                    emit Sync(reserve0, reserve1);
                }
            
                // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
                function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
                    address feeTo = IUniswapV2Factory(factory).feeTo();
                    feeOn = feeTo != address(0);
                    uint _kLast = kLast; // gas savings
                    if (feeOn) {
                        if (_kLast != 0) {
                            uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                            uint rootKLast = Math.sqrt(_kLast);
                            if (rootK > rootKLast) {
                                uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                                uint denominator = rootK.mul(5).add(rootKLast);
                                uint liquidity = numerator / denominator;
                                if (liquidity > 0) _mint(feeTo, liquidity);
                            }
                        }
                    } else if (_kLast != 0) {
                        kLast = 0;
                    }
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function mint(address to) external lock returns (uint liquidity) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    uint balance0 = IERC20(token0).balanceOf(address(this));
                    uint balance1 = IERC20(token1).balanceOf(address(this));
                    uint amount0 = balance0.sub(_reserve0);
                    uint amount1 = balance1.sub(_reserve1);
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    if (_totalSupply == 0) {
                        liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                       _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
                    } else {
                        liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
                    }
                    require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
                    _mint(to, liquidity);
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Mint(msg.sender, amount0, amount1);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function burn(address to) external lock returns (uint amount0, uint amount1) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    address _token0 = token0;                                // gas savings
                    address _token1 = token1;                                // gas savings
                    uint balance0 = IERC20(_token0).balanceOf(address(this));
                    uint balance1 = IERC20(_token1).balanceOf(address(this));
                    uint liquidity = balanceOf[address(this)];
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
                    amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
                    require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
                    _burn(address(this), liquidity);
                    _safeTransfer(_token0, to, amount0);
                    _safeTransfer(_token1, to, amount1);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Burn(msg.sender, amount0, amount1, to);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
                    require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
            
                    uint balance0;
                    uint balance1;
                    { // scope for _token{0,1}, avoids stack too deep errors
                    address _token0 = token0;
                    address _token1 = token1;
                    require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
                    if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
                    if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
                    if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
                    }
                    uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
                    uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
                    require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
                    { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
                    uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
                    uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
                    require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
                    }
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
                }
            
                // force balances to match reserves
                function skim(address to) external lock {
                    address _token0 = token0; // gas savings
                    address _token1 = token1; // gas savings
                    _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
                    _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
                }
            
                // force reserves to match balances
                function sync() external lock {
                    _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
                }
            }

            File 2 of 2: GasToken2
            pragma solidity ^0.4.10;
            
            contract GasToken2 {
                //////////////////////////////////////////////////////////////////////////
                // RLP.sol
                // Due to some unexplained bug, we get a slightly different bytecode if 
                // we use an import, and are then unable to verify the code in Etherscan
                //////////////////////////////////////////////////////////////////////////
                
                uint256 constant ADDRESS_BYTES = 20;
                uint256 constant MAX_SINGLE_BYTE = 128;
                uint256 constant MAX_NONCE = 256**9 - 1;
            
                // count number of bytes required to represent an unsigned integer
                function count_bytes(uint256 n) constant internal returns (uint256 c) {
                    uint i = 0;
                    uint mask = 1;
                    while (n >= mask) {
                        i += 1;
                        mask *= 256;
                    }
            
                    return i;
                }
            
                function mk_contract_address(address a, uint256 n) constant internal returns (address rlp) {
                    /*
                     * make sure the RLP encoding fits in one word:
                     * total_length      1 byte
                     * address_length    1 byte
                     * address          20 bytes
                     * nonce_length      1 byte (or 0)
                     * nonce           1-9 bytes
                     *                ==========
                     *                24-32 bytes
                     */
                    require(n <= MAX_NONCE);
            
                    // number of bytes required to write down the nonce
                    uint256 nonce_bytes;
                    // length in bytes of the RLP encoding of the nonce
                    uint256 nonce_rlp_len;
            
                    if (0 < n && n < MAX_SINGLE_BYTE) {
                        // nonce fits in a single byte
                        // RLP(nonce) = nonce
                        nonce_bytes = 1;
                        nonce_rlp_len = 1;
                    } else {
                        // RLP(nonce) = [num_bytes_in_nonce nonce]
                        nonce_bytes = count_bytes(n);
                        nonce_rlp_len = nonce_bytes + 1;
                    }
            
                    // [address_length(1) address(20) nonce_length(0 or 1) nonce(1-9)]
                    uint256 tot_bytes = 1 + ADDRESS_BYTES + nonce_rlp_len;
            
                    // concatenate all parts of the RLP encoding in the leading bytes of
                    // one 32-byte word
                    uint256 word = ((192 + tot_bytes) * 256**31) +
                                   ((128 + ADDRESS_BYTES) * 256**30) +
                                   (uint256(a) * 256**10);
            
                    if (0 < n && n < MAX_SINGLE_BYTE) {
                        word += n * 256**9;
                    } else {
                        word += (128 + nonce_bytes) * 256**9;
                        word += n * 256**(9 - nonce_bytes);
                    }
            
                    uint256 hash;
            
                    assembly {
                        let mem_start := mload(0x40)        // get a pointer to free memory
                        mstore(0x40, add(mem_start, 0x20))  // update the pointer
            
                        mstore(mem_start, word)             // store the rlp encoding
                        hash := sha3(mem_start,
                                     add(tot_bytes, 1))     // hash the rlp encoding
                    }
            
                    // interpret hash as address (20 least significant bytes)
                    return address(hash);
                }
                
                //////////////////////////////////////////////////////////////////////////
                // Generic ERC20
                //////////////////////////////////////////////////////////////////////////
            
                // owner -> amount
                mapping(address => uint256) s_balances;
                // owner -> spender -> max amount
                mapping(address => mapping(address => uint256)) s_allowances;
            
                event Transfer(address indexed from, address indexed to, uint256 value);
            
                event Approval(address indexed owner, address indexed spender, uint256 value);
            
                // Spec: Get the account balance of another account with address `owner`
                function balanceOf(address owner) public constant returns (uint256 balance) {
                    return s_balances[owner];
                }
            
                function internalTransfer(address from, address to, uint256 value) internal returns (bool success) {
                    if (value <= s_balances[from]) {
                        s_balances[from] -= value;
                        s_balances[to] += value;
                        Transfer(from, to, value);
                        return true;
                    } else {
                        return false;
                    }
                }
            
                // Spec: Send `value` amount of tokens to address `to`
                function transfer(address to, uint256 value) public returns (bool success) {
                    address from = msg.sender;
                    return internalTransfer(from, to, value);
                }
            
                // Spec: Send `value` amount of tokens from address `from` to address `to`
                function transferFrom(address from, address to, uint256 value) public returns (bool success) {
                    address spender = msg.sender;
                    if(value <= s_allowances[from][spender] && internalTransfer(from, to, value)) {
                        s_allowances[from][spender] -= value;
                        return true;
                    } else {
                        return false;
                    }
                }
            
                // Spec: Allow `spender` to withdraw from your account, multiple times, up
                // to the `value` amount. If this function is called again it overwrites the
                // current allowance with `value`.
                function approve(address spender, uint256 value) public returns (bool success) {
                    address owner = msg.sender;
                    if (value != 0 && s_allowances[owner][spender] != 0) {
                        return false;
                    }
                    s_allowances[owner][spender] = value;
                    Approval(owner, spender, value);
                    return true;
                }
            
                // Spec: Returns the `amount` which `spender` is still allowed to withdraw
                // from `owner`.
                // What if the allowance is higher than the balance of the `owner`?
                // Callers should be careful to use min(allowance, balanceOf) to make sure
                // that the allowance is actually present in the account!
                function allowance(address owner, address spender) public constant returns (uint256 remaining) {
                    return s_allowances[owner][spender];
                }
            
                //////////////////////////////////////////////////////////////////////////
                // GasToken specifics
                //////////////////////////////////////////////////////////////////////////
            
                uint8 constant public decimals = 2;
                string constant public name = "Gastoken.io";
                string constant public symbol = "GST2";
            
                // We build a queue of nonces at which child contracts are stored. s_head is
                // the nonce at the head of the queue, s_tail is the nonce behind the tail
                // of the queue. The queue grows at the head and shrinks from the tail.
                // Note that when and only when a contract CREATEs another contract, the
                // creating contract's nonce is incremented.
                // The first child contract is created with nonce == 1, the second child
                // contract is created with nonce == 2, and so on...
                // For example, if there are child contracts at nonces [2,3,4],
                // then s_head == 4 and s_tail == 1. If there are no child contracts,
                // s_head == s_tail.
                uint256 s_head;
                uint256 s_tail;
            
                // totalSupply gives  the number of tokens currently in existence
                // Each token corresponds to one child contract that can be SELFDESTRUCTed
                // for a gas refund.
                function totalSupply() public constant returns (uint256 supply) {
                    return s_head - s_tail;
                }
            
                // Creates a child contract that can only be destroyed by this contract.
                function makeChild() internal returns (address addr) {
                    assembly {
                        // EVM assembler of runtime portion of child contract:
                        //     ;; Pseudocode: if (msg.sender != 0x0000000000b3f879cb30fe243b4dfee438691c04) { throw; }
                        //     ;;             suicide(msg.sender)
                        //     PUSH15 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract
                        //     CALLER
                        //     XOR
                        //     PC
                        //     JUMPI
                        //     CALLER
                        //     SELFDESTRUCT
                        // Or in binary: 6eb3f879cb30fe243b4dfee438691c043318585733ff
                        // Since the binary is so short (22 bytes), we can get away
                        // with a very simple initcode:
                        //     PUSH22 0x6eb3f879cb30fe243b4dfee438691c043318585733ff
                        //     PUSH1 0
                        //     MSTORE ;; at this point, memory locations mem[10] through
                        //            ;; mem[31] contain the runtime portion of the child
                        //            ;; contract. all that's left to do is to RETURN this
                        //            ;; chunk of memory.
                        //     PUSH1 22 ;; length
                        //     PUSH1 10 ;; offset
                        //     RETURN
                        // Or in binary: 756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3
                        // Almost done! All we have to do is put this short (31 bytes) blob into
                        // memory and call CREATE with the appropriate offsets.
                        let solidity_free_mem_ptr := mload(0x40)
                        mstore(solidity_free_mem_ptr, 0x00756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3)
                        addr := create(0, add(solidity_free_mem_ptr, 1), 31)
                    }
                }
            
                // Mints `value` new sub-tokens (e.g. cents, pennies, ...) by creating `value`
                // new child contracts. The minted tokens are owned by the caller of this
                // function.
                function mint(uint256 value) public {
                    for (uint256 i = 0; i < value; i++) {
                        makeChild();
                    }
                    s_head += value;
                    s_balances[msg.sender] += value;
                }
            
                // Destroys `value` child contracts and updates s_tail.
                //
                // This function is affected by an issue in solc: https://github.com/ethereum/solidity/issues/2999
                // The `mk_contract_address(this, i).call();` doesn't forward all available gas, but only GAS - 25710.
                // As a result, when this line is executed with e.g. 30000 gas, the callee will have less than 5000 gas
                // available and its SELFDESTRUCT operation will fail leading to no gas refund occurring.
                // The remaining ~29000 gas left after the call is enough to update s_tail and the caller's balance.
                // Hence tokens will have been destroyed without a commensurate gas refund.
                // Fortunately, there is a simple workaround:
                // Whenever you call free, freeUpTo, freeFrom, or freeUpToFrom, ensure that you pass at least
                // 25710 + `value` * (1148 + 5722 + 150) gas. (It won't all be used)
                function destroyChildren(uint256 value) internal {
                    uint256 tail = s_tail;
                    // tail points to slot behind the last contract in the queue
                    for (uint256 i = tail + 1; i <= tail + value; i++) {
                        mk_contract_address(this, i).call();
                    }
            
                    s_tail = tail + value;
                }
            
                // Frees `value` sub-tokens (e.g. cents, pennies, ...) belonging to the
                // caller of this function by destroying `value` child contracts, which
                // will trigger a partial gas refund.
                // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                // when calling this function. For details, see the comment above `destroyChilden`.
                function free(uint256 value) public returns (bool success) {
                    uint256 from_balance = s_balances[msg.sender];
                    if (value > from_balance) {
                        return false;
                    }
            
                    destroyChildren(value);
            
                    s_balances[msg.sender] = from_balance - value;
            
                    return true;
                }
            
                // Frees up to `value` sub-tokens. Returns how many tokens were freed.
                // Otherwise, identical to free.
                // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                // when calling this function. For details, see the comment above `destroyChilden`.
                function freeUpTo(uint256 value) public returns (uint256 freed) {
                    uint256 from_balance = s_balances[msg.sender];
                    if (value > from_balance) {
                        value = from_balance;
                    }
            
                    destroyChildren(value);
            
                    s_balances[msg.sender] = from_balance - value;
            
                    return value;
                }
            
                // Frees `value` sub-tokens owned by address `from`. Requires that `msg.sender`
                // has been approved by `from`.
                // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                // when calling this function. For details, see the comment above `destroyChilden`.
                function freeFrom(address from, uint256 value) public returns (bool success) {
                    address spender = msg.sender;
                    uint256 from_balance = s_balances[from];
                    if (value > from_balance) {
                        return false;
                    }
            
                    mapping(address => uint256) from_allowances = s_allowances[from];
                    uint256 spender_allowance = from_allowances[spender];
                    if (value > spender_allowance) {
                        return false;
                    }
            
                    destroyChildren(value);
            
                    s_balances[from] = from_balance - value;
                    from_allowances[spender] = spender_allowance - value;
            
                    return true;
                }
            
                // Frees up to `value` sub-tokens owned by address `from`. Returns how many tokens were freed.
                // Otherwise, identical to `freeFrom`.
                // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                // when calling this function. For details, see the comment above `destroyChilden`.
                function freeFromUpTo(address from, uint256 value) public returns (uint256 freed) {
                    address spender = msg.sender;
                    uint256 from_balance = s_balances[from];
                    if (value > from_balance) {
                        value = from_balance;
                    }
            
                    mapping(address => uint256) from_allowances = s_allowances[from];
                    uint256 spender_allowance = from_allowances[spender];
                    if (value > spender_allowance) {
                        value = spender_allowance;
                    }
            
                    destroyChildren(value);
            
                    s_balances[from] = from_balance - value;
                    from_allowances[spender] = spender_allowance - value;
            
                    return value;
                }
            }