Transaction Hash:
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 | ||
---|---|---|---|---|---|
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
| |||
0x9f374f42...Bfe3e31d8 |
0 Eth
Nonce: 0
|
0 Eth
Nonce: 0
| |||
0xC4aEb207...332b9BC77
Miner
| (Easy2Mine) | 465.340855292386897949 Eth | 465.347733222303663197 Eth | 0.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 2 of 2: GasToken2
// 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; } }