First, to access variables check the official docs:
For local storage variables or state variables, a single Yul
identifier is not sufficient, since they do not necessarily occupy a
single full storage slot. Therefore, their “address” is composed of a
slot and a byte-offset inside that slot. To retrieve the slot pointed
to by the variable x
, you use x.slot
, and to retrieve the byte-offset
you use x.offset
. Using x
itself will result in an error.
To illustrate the example I will use the latest Solidity version (v0.7.4
).
In order to facilitate the explanation I will change your function. Suppose you have the following contract:
pragma solidity ^0.7.4;
contract D {
function modValue(uint256 a, uint256 b) public pure returns (uint256) {
return a % b;
}
}
Suppose we create another contract in order to perform a delegatecall
. We have the _delegate
variable which is initialized in the constructor. As you can see, this variable is outside the assembly block. Thus, we need to add .slot
to get the variable value inside the assembly block using the sload
opcode.
The first 32 bytes of data
indicate its length, that's why we do add(data,32)
to get the data
value. In addition, to get the data
length you should use the mload
opcode. More info about opcodes here.
pragma solidity ^0.7.4;
contract StackExchange {
address _delegate;
constructor (address delegate) {
_delegate = delegate;
}
function getValue() public returns (uint256) {
bytes memory data = abi.encodeWithSignature("modValue(uint256,uint256)", 52, 3);
assembly {
let pointer := mload(0x40)
if iszero(delegatecall(not(0), sload(_delegate.slot), add(data,32), mload(data), pointer, 0x20)) {
revert(0, 0)
}
let size := returndatasize()
returndatacopy(pointer, 0, size)
return(pointer,size)
}
}
}
Feel free to test the contracts in Remix.