0

The state override set allows you to deploy contracts and/or change the state of any contracts on demand while using eth_call. An example is given here - https://www.libevm.com/2022/01/12/advance-geth-pt-2-stateoverrides/

In the above example, they use const slot = 3; for the DAI contract allowance mapping. How can I find the slot number for allowance mapping for other ERC20s?

note: I already tried doing this for USDT, by trial and error I got a valid output for slot 5 (allowance mapping in USDT). so it's not the same for all tokens

2 Answers 2

1

There are multiple ways. I'm listing here all that I know.

  1. Examining the source code (solidity).

Solidity assigns storage slots in the order they are written starting from 0. If a contract inherits from others, the variables of the parents are listed first. Taking USDT as an example, we need to look at the contract and all the parents:

contract TetherToken is Pausable, StandardToken, BlackList {
    string public name;
    string public symbol;
    uint public decimals;
    address public upgradedAddress;
    bool public deprecated;
...

contract Pausable is Ownable {
  bool public paused = false;
...

contract Ownable {
    address public owner;
...

contract StandardToken is BasicToken, ERC20 {
    mapping (address => mapping (address => uint)) public allowed;
...

contract BasicToken is Ownable, ERC20Basic {
    mapping(address => uint) public balances;
    uint public basisPointsRate = 0;
    uint public maximumFee = 0;
...

contract ERC20Basic {
    uint public _totalSupply;

So the variables will be:

slot 0 -> owner and paused (since they can fit in one slot)
slot 1 -> _totalSupply
slot 2 -> balances
slot 3 -> basisPointsRate
slot 4 -> maximumFee
slot 5 -> allowed
  1. Decompiling the code

This is fastest method, so I always start from this, but it works only for common contracts. Basically a decompiler sometimes is smart and can recognize common functions or libraries, so it shows what the variables represent.

https://library.dedaub.com/decompile is one good tool for this. You can see for yourself the decompiled USDT here. In particular:

mapping (uint256 => mapping (uint256 => uint256)) _allowed; // STORAGE[0x5]
  1. Simulating approve and looking at storage diff

This method is good if you don't have the source code, or if the decompiler doesn't work. It can also be used programmatically (let's say you need the storage slot for hundreds of tokens...).

Basically what you need to do is call the approve function in a simulated environment and examine the storage diff (any tool that lets you debug a transaction trace should be able to). Usually only one slot should change: keccak256(spender . keccak256(owner . slot)) (docs), so we can infer the slot from this (we can't invert the keccak, but generally the slot will be small so we need only to cycle the slot up to ~100 until we find a match).

0

you need to calculate the slot from the given addresses (owner and spender) AND the base slot number of the mapping.

the easiest way is to use the cast index command: https://book.getfoundry.sh/reference/cast/cast-index

since allowances are mapping to a mapping, to understand how to apply this recursively I recommend reading this: https://medium.com/@flores.eugenio03/exploring-the-storage-layout-in-solidity-and-how-to-access-state-variables-bf2cbc6f8018#949c

for example, looking at a random address on block 18940000 I see an approval for WETH with owner = 0x941a6d105802cccaa06de58a13a6f49ebdcd481c and spender = 0xdef1c0ded9bec7f1a1670819833240f027b25eff

I use cast call command to show there is an allowance:

$ cast call 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 "allowance(address,address)" 0x941a6d105802cccaa06de58a13a6f49ebdcd481c 0xdef1c0ded9bec7f1a1670819833240f027b25eff --rpc-url https://rpc.ankr.com/eth --block 18940000

0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

the allowance base slot for WETH is 4 (you can verify this by compiling the contract with storage information) and the first key is the owner, so to calculate the slot for allowances[owner]:

cast index address 0x941a6d105802cccaa06de58a13a6f49ebdcd481c 4

0x27eca9f618109fb965e9e145b76a8b136be37d6799c43ba5bedae1a4b748e672

and to calculate allowances[owner][spender] simply apply this result as the base slot and the spender as key:

$ cast index address 0xdef1c0ded9bec7f1a1670819833240f027b25eff 0x27eca9f618109fb965e9e145b76a8b136be37d6799c43ba5bedae1a4b748e672

0x9840d0e64934992a699109835e93cb256ddc70208c41b23f714fe551d7f52155

this is the storage slot for that particular allowance. we can verify it with cast storage command:

$ cast storage 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 0x9840d0e64934992a699109835e93cb256ddc70208c41b23f714fe551d7f52155 --rpc-url https://rpc.ankr.com/eth --block 18940000

0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

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.