1

Hello im stuck with yul inline assembly when i want to access a double mapping.

mapping(address => mapping(address => uint256)) public approval_;

function approve(address spender, uint256 amount) external returns (bool) {
assembly {
  // doc => https://docs.soliditylang.org/en/v0.8.9/internals/layout_in_storage.html
  // keccak(keccak(spender) => keccak(caller => approve slot))
  // load if user already approve this user
  let msgsender
  mstore(msgsender, caller()) // store msg.sender
  let spender_
  mstore(spender_, spender) // store spender

  let ptr := mload(0x40) // load free memory
  mstore(ptr, keccak256(spender_, 0x20)) // store hash spender
  mstore(add(ptr, 0x20), msgsender) // store msg.sender after spender hash
  mstore(add(ptr, 0x40), approval_.slot) // store approval slot after msg.sender
  let googHash := keccak256(add(ptr, 0x20), 0x40) // hash msg.sender + approval slot
  mstore(add(ptr, 0x20), googHash)
  let approveSlot := keccak256(ptr, 0x40) // hash => hash spender + hash msg.sender + approval slot
  let balanceApproved := sload(approveSlot) // load approve balance

  // store the new value
  sstore(approveSlot, add(balanceApproved, amount))
}
return true;

}

dont understand what im doing wrong, thank's for ut help ;)

1 Answer 1

1

You got almost everything right, only two mistakes.

First, when you're assigning msg.sender and spender to memory. Look what's happening:

  let msgsender                  // msgsender = 0x00
  mstore(msgsender, caller())    // memory(0x00) = caller()
  let spender_                   // spender_ = 0x00
  mstore(spender_, spender)      // memory(0x00) = spender

Your basically using always memory at slot 0x00 for both values, so spender overwrites caller().

The second mistake it's that you're using as slot for approval_[owner][spender] the value:

keccak(keccak(spender) . keccak(owner . approval_.slot))

The correct one is the following (check the example in the docs):

keccak(spender . keccak(owner . approval_.slot))

Putting it all together and simplifying the structure:

    mapping(address => mapping(address => uint256)) public approval_;

    function approve(address spender, uint256 amount) external returns (bool) {
      assembly {
        // compute keccak(owner . approval_.slot)
        mstore(0x00, caller())
        mstore(0x20, approval_.slot)
        let googHash := keccak256(0x00, 0x40) // hash msg.sender + approval slot
        
        // compute keccak(spender . googHash) -> this is the final slot
        // we can reuse the same memory
        mstore(0x00, spender)
        mstore(0x20, googHash)
        let approveSlot := keccak256(0x00, 0x40)
        
        // load approve balance
        let balanceApproved := sload(approveSlot)

        // store the new value
        sstore(approveSlot, add(balanceApproved, amount))
      }
      return true;
    }
0

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.