1

I tried asking chat-gpt but it kept shitting the be for the explaination. I have some code which looks like so:

function getAuthorityScope(bytes memory authorityScope) public pure returns (address, uint256, uint256) {
    address authorityAddress;
    uint256 tokenId;

    // Extracting address and tokenId from the authorityScope bytes variable
    assembly {
        authorityAddress := mload(add(authorityScope, 20)) // Load the first 20 bytes (address size)
        tokenId := mload(add(authorityScope, 52))          // Load the next 32 bytes (uint256 size)
    }

    return (authorityAddress, tokenId, releaseTime);
}

mload is loading from the authority scope however im trying to understand the offset.

1. Address is 20 bytes long
2. Uint256 is 32 bytes long

I'm trying to understand how 20, and then 52 works. My initial assumption is that some how the compiler is tracking where to read from the memory buffer.

e.g 0-20 is the address, and 20-52 is the uint256. However its unclear to me how the pointer to the location in memory is tracked.

Does add shift the memory pointer 20 bytes and put that into a variable.
Leaving the memory pointer at the last place read (20 bytes past the starting point).

Then the second read grabs out 32 bytes (starting from the last place read)?

1
  • what are you passing as a value for authorityScope? Commented Jun 8 at 5:30

1 Answer 1

2

authorityScope is a byte array, so its layout in memory is

+--------------+------------------+
|    length    |       data       |
+-- 32 bytes --+-- length bytes --+

The code add(authorityScope, 20) is calculating an offset that starts in the middle of the length (at offset 20).

Now mload() always read 32 bytes, so mload(add(authorityScope, 20)) read the last 12 bytes from length and 20 bytes from data.

Let's suppose we have that authorityScope is equal to the hex string 0x0102030405060708a1a2a3a4a5a6a7a81112131415161718b1b2b3b4b5b6b7b80102030405060708a1a2a3a4a5a6a7a81112131415161718b1b2b3b4b5b6b7b8. Then length is 64 = 0x40.

The layout in memory will be 0x00000000000000000000000000000000000000000000000000000000000000400102030405060708a1a2a3a4a5a6a7a81112131415161718b1b2b3b4b5b6b7b80102030405060708a1a2a3a4a5a6a7a81112131415161718b1b2b3b4b5b6b7b8

  • Then mload(add(authorityScope, 20)) returns 32 bytes. 0x0000000000000000000000400102030405060708a1a2a3a4a5a6a7a81112131415161718b1b2b3b4. When it is assigned to authorityAddress it gets truncated to the last 20 bytes, so authorityAddress = 0x0102030405060708a1a2a3a4a5a6a7a81112131415161718b1b2b3b4.

  • Similarly tokenId := mload(add(authorityScope, 52)), add(authorityScope, 52) skips the 32 bytes from the length, then the 20 bytes from the address. mload(add(authorityScope, 52)) will return the next 32 bytes after the address: 0xb5b6b7b80102030405060708a1a2a3a4a5a6a7a81112131415161718b1b2b3b4.

2
  • Okay my confusion is how does it know which slot to read from when I pass does it just take the offset and divide by 32? What if i had a variable that cross the bounds in a packed load. e.g I store a varible thats 24 bytes then one thats 16 bytes, How do I read from 24-40, would I have to do 2 seperate loads and peice the data together using bitwise operators?
    – RitzyDevUk
    Commented Jun 9 at 22:30
  • @RitzyDevUk Memory doesn't have slot it is like an array. Suppose that memory is a c-like array mem. Then reading from pointer p is like reading the slice mem[p..p+31].
    – Ismael
    Commented Jun 10 at 0:43

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.