2

What does this code mean?

sender := shr(96, calldataload(sub(calldatasize(), 20)))

Btw sender is an address variable.

2 Answers 2

7

shr(a, b) shifts b right by a bits, so in this case it shifts the result of calldataload right by 96 bits. This code essentially takes the last 20 bytes from the calldata (a.k.a. msg.data) and removes the first 12 bytes (96 bits) from it to get a 20 byte address.

You can find all Yul (inline assembly) functions and what they do here: https://docs.soliditylang.org/en/v0.8.9/yul.html#evm-dialect

0
3

The shr function is used to extract variables from the calldata. The calldata is the msg.data you're passing to the contract with the function.

Imagine your calldata is this:

0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2eaabb2ee89221455302d9ffb5325689ff8f0c37b000000000000000009369ed80b93d665

When you do shr(96, calldataload(0x00)) what you're doing is adding 12 bytes to the left of the calldata (pushing the calldata to the right).

The 96 from shr(96) is in bits, in bytes that's 96 / 8 = 12 bytes. Meaning you're adding 24 zeroes in front of the calldata (1 byte is 2 letters). So now calldata would look like this:

0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2eaabb2ee89221455302d9ffb5325689ff8f0c37b000000000000000009369ed80b93d665

Why is that important? Because you're storing the data into a variable called sender := shr(96, ...).

Variables in assembly are all 32 bytes (64 characters).

Therefore the variable cuts off everything after 32 bytes (or 64 characters remember that 1 bytes is 2 letters). So the variable sender becomes:

sender := 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

Storing successfully the first address into sender.

So that's how you extract the first parameter of the calldata.

That's it!

I figured this all by playing around with assembly in remix and seeing how the zeroes are being added.




Here's more info. Imagine your calldata being this:

0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2eaabb2ee89221455302d9ffb5325689ff8f0c37b000000000000000009369ed80b93d665

Basically what it contains is all this data combined into one:

0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

eaabb2ee89221455302d9ffb5325689ff8f0c37b

000000000000000009369ed80b93d665

Here's the breakdown:

  1. First parameter is an address the length is 42 characters, 2 characters are 1 byte. Addresses in solidity are 40 characters long (0x14 hex). This will be important later on.

  2. Second address (notice how the 0x at the start is removed because it's only purpose is to tell the compiler that the data you're sending is hexadecimal)

  3. Third parameter is the number 663892645828089500 expressed in hexadecimal padded with zeroes so it has a length of 16 bytes which means it's a uint128.

All variables in assembly are 32 bytes (0x20 in hex) or 64 characters. Therefore addresses are represented with 24 zeroes in front of them, because addresses are 40 characters long, they need those extra 24 to have a length of 64 characters and become 32 bytes, which then becomes a valid address variable in assembly.

Numbers in assembly are expressed in hexadecimal because it allows you to store more data in less space. A uint256 is 32 bytes long or 64 characters. A uint128 is 16 bytes or 32 characters long. Once you convert the number to hex, you add zeroes to the left to make it a valid variable.

Hope that was helpful.

Your Answer

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

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