7

I am trying to derive changes of account storage by mapping accounts (here, the accounts represent the key address in the ERC20 balances mapping mapping(address => uint256)) to their corresponding storage keys from a parity transaction trace.

An example trace (fetched through web3.py) of transaction https://etherscan.io/tx/0x210cfe3d3b62f415ed0327a3c6177086df4937b6280031255360b6d137308554 is:

{
    'output': '0x',
    'stateDiff': {
        '0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5': {
            'balance': {
                '*': {
                    'from': '0x122291885d6222bc5ec',
                    'to': '0x12229193ca3f58565ec'
                }
            },
            'code': '=',
            'nonce': '=',
            'storage': {}
        },
        '0xaddba95f769b5d42c02e144102817eab9d00efd3': {
            'balance': '=',
            'code': '=',
            'nonce': '=',
            'storage': {
                '0x1ded6755a6d7d843883da8cd8948931cf9f8b1e8f8983ad77e1685ece0b92fc2': {
                    '*': {
                        'from': '0x0000000000000000000000000000000000000000000000000000000000000000',
                        'to': '0x000000000000000000000000000000000000000000000000000bfb8d0ebc5000'
                    }
                },
                '0x5020d77d5345022a5466af6f78731b10f92d31e17961c23ea6ce5c185dc75d49': {
                    '*': {
                        'from': '0x0000000000000000000000000000000000000000000000000000000000000000',
                        'to': '0x0000000000000000000000000000000000000000000000000000e1412a5f1c00'
                    }
                }
            }
        },
        '0xf2eeb980b60b9ed146636c65ecc5e8f27a8aed40': {
            'balance': {
                '*': {
                    'from': '0x2c4c7111f4801a8',
                    'to': '0x2c410434bee61a8'
                }
            },
            'code': '=',
            'nonce': {
                '*': {
                    'from': '0x143',
                    'to': '0x144'
                }
            },
            'storage': {}
        }
    },
    'trace': [{
        'action': {
            'callType': 'call',
            'from': '0xf2eeb980b60b9ed146636c65ecc5e8f27a8aed40',
            'gas': '0x9008',
            'input': '0xa9059cbb000000000000000000000000e034e561ce112c5f261f15d447e8f2436d9625040000000000000000000000000000000000000000000000000000e130de0be000',
            'to': '0xaddba95f769b5d42c02e144102817eab9d00efd3',
            'value': '0x0'
        },
        'result': {
            'gasUsed': '0x3924',
            'output': '0x'
        },
        'subtraces': 0,
        'traceAddress': [],
        'type': 'call'
    }],
    'vmTrace': None
}

According to https://solidity.readthedocs.io/en/v0.4.24/miscellaneous.html#layout-of-state-variables-in-storage, "value corresponding to a mapping key k is located at keccak256(k . p) where . is concatenation". I am aware of the necessary left pad, according to "keccack(LeftPad32(key, 0), LeftPad32(map position, 0))" https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat, although the hashing functions seem to handle this internaly already.

I tried the Web3.sha3() for this keccak256 storage key, and other issues on StackOverflow mention that Solidity employs a slightly different hashing algorithm. Nevertheless, also by using the proper Web3.soliditySha3() I do not arrive at a hash that would present me the key found in the the trace (here, 0x1ded6755a6d7d843883da8cd8948931cf9f8b1e8f8983ad77e1685ece0b92fc2 or 0x5020d77d5345022a5466af6f78731b10f92d31e17961c23ea6ce5c185dc75d49) to the account 0xf2eeb980b60b9ed146636c65ecc5e8f27a8aed40.

What is the right way (preferably with web3.py) to hash the account and position in order to arrive at the mapping storage key?

Daniel
  • 73
  • 4
  • Daniel how are you determining the value for p. This is actually related to the position of the mapping variable and you need it. – Jaime May 30 '18 at 08:33
  • 1
    For the position, I tried enumerating the position according to the "slot" logic from https://programtheblockchain.com/posts/2018/03/09/understanding-ethereum-smart-contract-storage/. Unfortunately without success. – Daniel May 30 '18 at 20:29

1 Answers1

7

As mentioned by @jaime in the comments you need the position of the mapping variable to find the relation between address and storage key.

You can find a code I have written to detect the position for a basic erc20 contract below (using web3.py):

    import json
    from web3.auto.infura import w3
    from eth_utils import remove_0x_prefix, to_int, to_checksum_address, to_hex
# erc20 contract address
CONTRACT_ADDRESS = '0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7'
# address with non zero token balance
HOLDER_ADDRESS = '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb' 

def getStorageAtIndex(i):
    pos = str(i).rjust(64, '0')
    key = remove_0x_prefix(HOLDER_ADDRESS).rjust(64, '0').lower()
    storage_key = to_hex(w3.sha3(hexstr=key + pos))
    return to_int(w3.eth.getStorageAt(CONTRACT_ADDRESS, storage_key))

for i in range(0, 20):
    if getStorageAtIndex(i) != 0:
        print("position is {}".format(i))

Result:
>>> position is 5

Once you have the correct value of position you can fetch the values stored in contract easily.

    pos = str(5).rjust(64, '0')
    key = remove_0x_prefix(HOLDER_ADDRESS).rjust(64, '0').lower()
    storage_key = to_hex(w3.sha3(hexstr=key + pos))
    w3.eth.getStorageAt(CONTRACT_ADDRESS, storage_key)
Jeremy Then
  • 4,599
  • 3
  • 5
  • 28