1

Taken a smart contract like the following:

contract A {
    address public beneficiary;

    function A(address _beneficiary) public {
         beneficiary = _beneficiary;
    }

    function () public payable {
         beneficiary.transfer(msg.value);
    }
}

How would you write it in inline assembly? No problem for me with the constructor, but got stuck with the fallback function, since I don't know how to translate the transfer()

3

I'd do something like this, using call() to do an external function call, passing the msg.value inside it.

function () payable {
    bytes4 sig = bytes4(keccak256("()")); // function signature

    assembly {
        let x := mload(0x40) // get empty storage location
        mstore ( x, sig ) // 4 bytes - place signature in empty storage

        let ret := call (gas, 
            beneficiary,
            msg.value, 
            x, // input
            0x04, // input size = 4 bytes
            x, // output stored at input location, save space
            0x0 // output size = 0 bytes
        )

        mstore(0x40, add(x,0x20)) // update free memory pointer
    }
}
  • You can also call the fallback also if you pass an empty input. – Ismael May 4 '18 at 5:12
  • Thank you @Elizabeth! I had to change something like beneficiary and msg.value with sload(beneficiary_slot) and callvalue but it is correct. Two more questions: 1. I tried to change input, output and sizes with 0x0 as suggested by @Ismael and it worked, but I don't know if it's like a bad practice. Do I have to always specify input and output in call opcode even if I forward funds like in this case? 2. Updating free memory pointer is a best practice? – altafan May 4 '18 at 11:17
  • 1
    @Ismael, yep the fallback will be called with an empty input or any invalid input, eg. thisIsNotAFunction(). in this case, since we're calling the fallback and don't need input or output I don't think it matters. I just like to keep input and output there for clarity. If you're calling a function that takes parameters, then you would need to pass them in through the input, and get the return value from the output. yeah updating the free memory pointer is a good idea. it's necessary if we're using the return value of the call. – noot May 4 '18 at 17:11
0

Run solc --asm --output-dir=build/binaries A.sol.

Then just take whatever is in the file build/binaries/A.evm.

With pragma solidity ^0.4.18 I got the following piece of code:

    /* "contracts/A.sol":26:254  contract A {
... */
  mstore(0x40, 0x60)
    /* "contracts/A.sol":79:165  function A(address _beneficiary) public {
... */
  jumpi(tag_1, iszero(callvalue))
  0x0
  dup1
  revert
tag_1:
  mload(0x40)
  0x20
  dup1
  bytecodeSize
  dup4
  codecopy
  dup2
  add
  0x40
  mstore
  dup1
  dup1
  mload
  swap1
  0x20
  add
  swap1
  swap2
  swap1
  pop
  pop
    /* "contracts/A.sol":145:157  _beneficiary */
  dup1
    /* "contracts/A.sol":131:142  beneficiary */
  0x0
  dup1
    /* "contracts/A.sol":131:157  beneficiary = _beneficiary */
  0x100
  exp
  dup2
  sload
  dup2
  0xffffffffffffffffffffffffffffffffffffffff
  mul
  not
  and
  swap1
  dup4
  0xffffffffffffffffffffffffffffffffffffffff
  and
  mul
  or
  swap1
  sstore
  pop
    /* "contracts/A.sol":79:165  function A(address _beneficiary) public {
... */
  pop
    /* "contracts/A.sol":26:254  contract A {
... */
  dataSize(sub_0)
  dup1
  dataOffset(sub_0)
  0x0
  codecopy
  0x0
  return
stop

sub_0: assembly {
        /* "contracts/A.sol":26:254  contract A {
... */
      mstore(0x40, 0x60)
      jumpi(tag_1, lt(calldatasize, 0x4))
      calldataload(0x0)
      0x100000000000000000000000000000000000000000000000000000000
      swap1
      div
      0xffffffff
      and
      dup1
      0x38af3eed
      eq
      tag_2
      jumpi
    tag_1:
        /* "contracts/A.sol":212:223  beneficiary */
      0x0
      dup1
      swap1
      sload
      swap1
      0x100
      exp
      swap1
      div
      0xffffffffffffffffffffffffffffffffffffffff
      and
        /* "contracts/A.sol":212:232  beneficiary.transfer */
      0xffffffffffffffffffffffffffffffffffffffff
      and
        /* "contracts/A.sol":212:243  beneficiary.transfer(msg.value) */
      0x8fc
        /* "contracts/A.sol":233:242  msg.value */
      callvalue
        /* "contracts/A.sol":212:243  beneficiary.transfer(msg.value) */
      swap1
      dup2
      iszero
      mul
      swap1
      mload(0x40)
      0x0
      mload(0x40)
      dup1
      dup4
      sub
      dup2
      dup6
      dup9
      dup9
      call
      swap4
      pop
      pop
      pop
      pop
      iszero
      iszero
      tag_5
      jumpi
      0x0
      dup1
      revert
    tag_5:
        /* "contracts/A.sol":26:254  contract A {
... */
      stop
        /* "contracts/A.sol":44:70  address public beneficiary */
    tag_2:
      jumpi(tag_6, iszero(callvalue))
      0x0
      dup1
      revert
    tag_6:
      tag_7
      jump(tag_8)
    tag_7:
      mload(0x40)
      dup1
      dup3
      0xffffffffffffffffffffffffffffffffffffffff
      and
      0xffffffffffffffffffffffffffffffffffffffff
      and
      dup2
      mstore
      0x20
      add
      swap2
      pop
      pop
      mload(0x40)
      dup1
      swap2
      sub
      swap1
      return
    tag_8:
      0x0
      dup1
      swap1
      sload
      swap1
      0x100
      exp
      swap1
      div
      0xffffffffffffffffffffffffffffffffffffffff
      and
      dup2
      jump  // out

    auxdata: 0xa165627a7a72305820dd4c6e28e67e307f6f891abd170c6646cad8d99ce9c533e220d4362c5a3b2ab00029
}

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.