-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathget_nonce.py
executable file
·61 lines (44 loc) · 2.16 KB
/
get_nonce.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#! /usr/bin/env python
# coding: utf8
import argparse
from lib.rpc import Parity
from lib.utils import dump_stack, get_storage_location
ERC20_TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
ERC20_BALANCE_SELECTOR = '0x70a08231'
node = Parity('http://localhost:8545')
def get_nonce(sample_tx):
# get the transaction receipt
result = node.call('eth_getTransactionReceipt', sample_tx)
# find the first ERC20 Transfer() log
transfer = [log for log in result['logs'] if log['topics'][0] == ERC20_TRANSFER_TOPIC][0]
# get the token contract address
token_contract = transfer['address']
print('Token contract:', token_contract)
# `sender_address` will be the 20 byte address, zerop-added to 32 bytes
# e.g. "0x000000000000000000000000a0576b6092d5840b4d4fa7a015ce7942e727a84d"
sender_address = transfer['topics'][1]
assert sender_address.startswith('0x') and len(sender_address) == 2 + 64
# create a set of storage locations that got changed in the token contract by this transaction
result = node.call('trace_replayTransaction', sample_tx, ['stateDiff'])
sloc_changed = set(result['stateDiff'][token_contract]['storage'].keys())
# monitor the stack on a balanceOf(sender_address) call
data = ERC20_BALANCE_SELECTOR + sender_address[2:]
result = node.call('trace_call', {'data': data, 'to': token_contract}, ['vmTrace'], 'latest')
stack_values = dump_stack(result['vmTrace'])
# the storage location of the sender_address balance should be present in both sets
common_values = stack_values & sloc_changed
assert len(common_values) == 1
sender_sloc = common_values.pop()
# brute force the nonce
nonce = 0
while 1:
if get_storage_location(sender_address, nonce) == sender_sloc:
break
nonce += 1
print('Balance map nonce:', nonce)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="Scan a sample ERC20 transfer tx to determine the contract's internal balance map nonce")
parser.add_argument('sample', help='hash of the sample transfer tx')
args = parser.parse_args()
get_nonce(args.sample)