2

I'm trying to write a super simple contract that's gas efficient utilizing inline assembly in solidity. All this function needs to do is create a child smart contract with one function, self destruct. The function also should check msg.sender to make sure it's the contract owner. This GasToken project does this in a form where they've tokenized the effective stored gas. They took it to another level where they also used a vanity address to make this even more gas efficient. I've been trying to generate a vanity address so that I can use their code exactly, but it's taking an extremely long time so I resorted to modifying the code so that I can do it with a normal address and not a vanity address with 10 leading zeros. So for this question's purpose, let's not do a vanity address.

contract pseudocode:

if (msg.sender != 0x1234....5678) { throw; }
suicide(msg.sender)

How can I write this in inline assembly in solidity? Here is the source for gasToken.io where they used the vanity address. I'd like to NOT use the vanity address to simplify my code for the time being since this kind of vanity address is hard to generate without mining hardware. I've been referencing solidity opcodes but have been unable to successfully get this up and running.

// 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)
    }
}
0

I'm no EVM assembly wizard, nor do I have the environment set up to test this and debug it for you -- but I'll try to give some advice / ideas for how I would go about doing this. The comments in the Assembly are great at walking us through what is going on, so let's walk through them..

The steps I see in the comments:

  • Build the Child runtime asm
  • Turn child asm into bytes
  • Shove child asm bytes into initcode for the child contract
  • Turn child initcode into binary
  • put the initcode binary into memory and call create on it

So let's walk through the steps and see what we need to change

Build the Child asm

In the first part (defining the child asm) I see a few things we need to change (my added comments in <>):

 // EVM assembler of runtime portion of child contract:
        //     ;; Pseudocode: if (msg.sender != 0x1234....5678) { throw; }
        //     ;;             suicide(msg.sender)
        <It was PUSH15 before because their vanity address only had 15 bytes of non0 data>
        <A normal ETH address has 20 bytes (40 hex characters)>
        //     <OLD>: PUSH15 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract 
        //     <NEW>: PUSH20 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract
        //     <... The rest of this seems like it should still work>

Turn child asm into bytes

You can use this site to get the bytecode for each opcode. Since we only changed the first opcode from PUSH15 (6e) to PUSH20 (73), and the bytes after that, we should be able to simply swap those out.

OLD: 6eb3f879cb30fe243b4dfee438691c043318585733ff

NEW: 73<1234....5678>3318585733ff

Note that instead of 22 bytes, your new child runtime asm should be 27 bytes (5 more for your longer address)

Shove child asm bytes into initcode for the child contract

        //     PUSH27 0x73<1234....5678>3318585733ff
        //     PUSH1 0
        //     MSTORE ;; at this point, memory locations mem[10] through
        //            ;; mem[36] <added 5 bytes to the end here since your runtime is longer> contain the runtime portion of the child
        //            ;; contract. all that's left to do is to RETURN this
        //            ;; chunk of memory.
        //     PUSH1 27 ;; length <since your length is 27 not 22 now>
        //     PUSH1 10 ;; offset
        //     RETURN

Turn child initcode into binary

Similar to step 2:

OLD: 756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3

7a) and change PUSH1 22 to PUSH1 27 (6016 to 601b) NEW: 7a<73<1234....5678>3318585733ff>600052601b600af3

Now you should be able to plop this into the mstore in the original code.

Finally

Again, I'm not positive this works and have not tested it. But hopefully it helped you get a sense of how to go about solving this kind of issue!

  • So MSTORE says "writes a (u)int256 to memory". Won't I hit the size limit for that one single MSTORE call after increasing the size of the address field from push22 to push27? My first attempt at this yielded failure and that was my best guess. I'm going to give this another try based on your feedback and we'll see if I run into that same problem or if it just works! If I get some sort of error that confirms a conflict with that size issue i'll update the post with it. It's also possible i'm misunderstanding how MSTORE works... – LampShade Nov 22 '19 at 17:38
  • Another suspect here, Any ideas how this part works: "PUSH1 10 ;; offset"? I can't figure out how they derived the 10. The docs say MSTORE expects two stack inputs: "offset" and "value". And in their code they call them "length" and "offset" and it's a bit confusing. Length seems obvious, I agree. I wonder if changing the length affects the offset as well. – LampShade Nov 22 '19 at 17:43
  • Offset usually means where to start looking for bytes -- so I don't think the additional length of the runtime asm should affect it. I'm not sure how exactly they derived the 10 bytes, though. – Mark Toda Nov 22 '19 at 23:59
  • > Won't I hit the size limit for that one single MSTORE call after increasing the size of the address field from push22 to push27? I don't think so, the size of the child runtime all told is still less than 256 bits – Mark Toda Nov 23 '19 at 0:02
  • Okay, I tried it and got a compilation error. This is the error I got originally when I tried something similar: "TypeError: Number literal too large (> 256 bits) " and it points to the mstore line with that large garble of data. So that is over the limit for one single mstore call. Maybe I need to break this up into multipe mstore calls? I'm not sure how to do that... Do note: I do not get this compiler error with solidity 0.4.11. It does not work though (0x0 Transaction mined but execution failed). When I compile with solidity 0.5.7, it gives this error and will not compile. – LampShade Nov 25 '19 at 0:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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