4

I am going through a sample gas token code on Ethereum. The token mints some dummy smart contracts using CREATE2 and then calculates the address. Later on it destroys them to get the gas refund.

Minting part looks like this

 assembly {
        mstore(0, 0x746d4946c0e9F43F4Dee607b0eF1fA1c3318585733ff6000526015600bf30000)

        for {let i := div(value, 32)} i {i := sub(i, 1)} {
            pop(create2(0, 0, 30, add(offset, 0))) pop(create2(0, 0, 30, add(offset, 1)))
            pop(create2(0, 0, 30, add(offset, 2))) pop(create2(0, 0, 30, add(offset, 3)))
            pop(create2(0, 0, 30, add(offset, 4))) pop(create2(0, 0, 30, add(offset, 5)))
            pop(create2(0, 0, 30, add(offset, 6))) pop(create2(0, 0, 30, add(offset, 7)))
            pop(create2(0, 0, 30, add(offset, 8))) pop(create2(0, 0, 30, add(offset, 9)))
            pop(create2(0, 0, 30, add(offset, 10))) pop(create2(0, 0, 30, add(offset, 11)))
            pop(create2(0, 0, 30, add(offset, 12))) pop(create2(0, 0, 30, add(offset, 13)))
            pop(create2(0, 0, 30, add(offset, 14))) pop(create2(0, 0, 30, add(offset, 15)))
            pop(create2(0, 0, 30, add(offset, 16))) pop(create2(0, 0, 30, add(offset, 17)))
            pop(create2(0, 0, 30, add(offset, 18))) pop(create2(0, 0, 30, add(offset, 19)))
            pop(create2(0, 0, 30, add(offset, 20))) pop(create2(0, 0, 30, add(offset, 21)))
            pop(create2(0, 0, 30, add(offset, 22))) pop(create2(0, 0, 30, add(offset, 23)))
            pop(create2(0, 0, 30, add(offset, 24))) pop(create2(0, 0, 30, add(offset, 25)))
            pop(create2(0, 0, 30, add(offset, 26))) pop(create2(0, 0, 30, add(offset, 27)))
            pop(create2(0, 0, 30, add(offset, 28))) pop(create2(0, 0, 30, add(offset, 29)))
            pop(create2(0, 0, 30, add(offset, 30))) pop(create2(0, 0, 30, add(offset, 31)))
            offset := add(offset, 32)
        }

        for {let i := and(value, 0x1F)} i {i := sub(i, 1)} {
            pop(create2(0, 0, 30, offset))
            offset := add(offset, 1)
        }
    } 

The code clearly creates n number of smart contracts, but what's the significance of using mstore at the beginning. Also, I don't understand significance of the 30 in the create commands and the use of pop. Why are they needed?

Here comes the burning part

Contract tries to get the address of next burnable address based on already burnt tokens and then uses it as salt to generate the address of the token

function computeAddress2(uint256 salt) public view returns (address) {
    bytes32 _data = keccak256(
        abi.encodePacked(bytes1(0xff), address(this), salt, bytes32(0x3c1644c68e5d6cb380c36d1bf847fdbc0c7ac28030025a2fc5e63cce23c16348))
    );
    return address(uint256(_data));
}

function _destroyChildren(uint256 value) internal {
    uint256 _totalBurned = totalBurned;
    for (uint256 i = 0; i < value; i++) {
        computeAddress2(_totalBurned + i).call("");
    }
    totalBurned = _totalBurned + value;
}

How does just calling the contract is destroying it ? Where is the code of deployed dummy contract ? I need help in understanding the flow.

5
  • 1
    0x746d49... is the bytecode of the contract to deploy and if that one contains a fallback function that self-destroys, it would explain the .call(""). Sep 28, 2020 at 6:57
  • @Markus-soliditydeveloper.com Can you help me with converting bytecode of a contract to this format ? Sep 28, 2020 at 7:05
  • The bytecode of contract starts with 6d4. What is 74 in beginning ? What is "6000526015600bf30000" at the end ? In between part is bytecode of contract Sep 28, 2020 at 7:07
  • It's a good question how they got the 0x74... bytecode. All my contracts seem to start with 0x608.... In theory all you need is to add the 0x. Sep 28, 2020 at 21:34
  • I think when London fork come all gas tokens will not work anymore
    – Majd TL
    Mar 24, 2021 at 20:02

1 Answer 1

1

Here is a quote from another gas token's documentation explaining what mstore is doing:

// EVM assembler of runtime portion of child contract:
//     ;; Pseudocode: if (msg.sender != 0x000000000000cb2d80a37898be43579c7b616844) { throw; }
//     ;;             suicide(msg.sender)
//     PUSH14 0xcb2d80a37898be43579c7b616856 ;; hardcoded address of this contract
//     CALLER
//     XOR
//     JUMP
//     JUMPDEST
//     CALLER
//     SELFDESTRUCT
// Or in binary: 6dcb2d80a37898be43579c7b6168563318565b33ff
// Since the binary is so short (21 bytes), we can get away
// with a very simple initcode:
//     PUSH21 0x6dcb2d80a37898be43579c7b6168573318565b33ff
//     PUSH1 0
//     MSTORE ;; at this point, memory locations mem[10] through
//            ;; mem[30] contain the runtime portion of the child
//            ;; contract. all that's left to do is to RETURN this
//            ;; chunk of memory.
//     PUSH1 21 ;; length
//     PUSH1 11 ;; offset
//     RETURN
// Or in binary: 746dcb2d80a37898be43579c7b6168563318565b33ff6000526015600bf30000
// Almost done! All we have to do is put this short (30 bytes) blob into
// memory and call CREATE with the appropriate offsets.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.