What does this code mean?
sender := shr(96, calldataload(sub(calldatasize(), 20)))
Btw sender is an address variable.
Ethereum Stack Exchange is a question and answer site for users of Ethereum, the decentralized application platform and smart contract enabled blockchain. It only takes a minute to sign up.Sign up to join this community
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
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:
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:
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.
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:
Basically what it contains is all this data combined into one:
Here's the breakdown:
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.
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)
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.