以太坊实战【地址监控三】
在以太坊实战【地址监控二】中已经实现了对以太坊交易地址的监控,但是以太坊上还有很多TOKENS,能监控到吗?
token交易测试
布置一个代币合约(可以参考以太坊研究系列贴子),帐号转一次代币VF,用已经实现的方法,调用eth_getBlockByNumber取到的交易数据如下:
"transactions":[{"blockHash":"0x51c9e0e10bfdc01110ff239e9c7bddca68b102741f7b9a3d87dd624a83f9d826","blockNumber":"0x2e10","from":"0x54b865714068f5f03574ace39a1f3279c4e83e2c","gas":"0x21905","gasPrice":"0x430e23400","hash":"0x781942bb5acfed62689ba068e975989461d5b2ef657dd1eff7057bc56de464ce","input":"0xa9059cbb000000000000000000000000823e1c4acbfc6527a6210e9bb9b4d5a45dc9c9a40000000000000000000000000000000000000000000000000000000000000058","nonce":"0x33","to":"0xf66448735c78389acd4834cf7b94d1b7c0d2a91a","transactionIndex":"0x0","value":"0x0","v":"0x42","r":"0xabf55940fbaede52a2706c3c7405e21af42577d8da35446dcf7bbf362ad151fd","s":"0x65e27dca91550fdd2ad8d001c67ebdda228a222d97ccf8ba357d62914192e5a7"}]
我是从地址 0x54b865714068f5F03574ACe39a1F3279C4E83E2c 转代币VF,转到地址 0x823e1C4acBfC6527a6210E9BB9B4d5a45DC9C9A4,从交易数据from字段可以看到发送地址是一样的,但是to地址 0xf66448735c78389acd4834cf7b94d1b7c0d2a91a 并不是我转代币的目标地址。
什么原因呢?以太坊上代币只是一个智能合约,对代币的所有操作都是对智能合约的调用,钱包转代币时实际的操作是调用智能合约的transfer函数,在mist的交易记录里也可以看到,0xf66448735c78389acd4834cf7b94d1b7c0d2a91a 这个地址就是VF智能合约的地址。
那再看看从交易数据有没有办法解析出是哪种代币,同时交易的代币是多少呢?理论上来说,to地址应该是代币合约地址,input中的数据应该包含了调用等数据,解析input这种二进制数据合适吗?
token监控
大概找到了几种方法查询token数量,可以进行监控可能:
- 解析以太坊数据包,并解析合约调用数据
在上面交易记录里,input数据如下:
"input":"0xa9059cbb000000000000000000000000823e1c4acbfc6527a6210e9bb9b4d5a45dc9c9a40000000000000000000000000000000000000000000000000000000000000058
解析后格式:
Function: transfer(address _to, uint256 _value)
MethodID: 0xa9059cbb
[0]: 000000000000000000000000823e1c4acbfc6527a6210e9bb9b4d5a45dc9c9a4
[1]: 0000000000000000000000000000000000000000000000000000000000000058
- 通过合约方法调用web3js
既然token是智能合约,而且这些token合约基本是erc20标准的合约,那我们查询到合约地址、abi,就可以直接调用合约的balanceOf()取得指定地址的代币数量,另外合约一般都有Transfer事件,也可以监控交易事件。
- etherscan接口调用
内部实现应该也是调用合约接口,只是提供web式调用
结论
从研究结果来看,可以解析以太坊的区块数据来监控token的变化,只要to地址是合约地址(可以取到帐户的codeHash),再判断是否需要监控的TOKEN合约地址,解析input数据,是transfer或transferFrom时可以得到转帐数量。
#参考
https://github.com/bokkypoobah/TokenTrader/blob/master/scripts/getGNTBalances
ETH和ERC20查询
https://ethereum-balance.com
这个查询不错,我的BEC和CryptoKitties也给查出来了
https://ethplorer.io
以太坊交易数据
https://medium.com/@codetractio/inside-an-ethereum-transaction-fa94ffca912f
https://github.com/kentaro/tiperc20/blob/master/token.go
看到有人在找这个method和交易二进制对照表,可能以后也有用,方法见 https://www.bokconsulting.com.au/blog/a-quick-look-at-paritys-signature-registry-contract/
totalSignatures: 189
0: 0xf2c298be => register(string)
1: 0x861731d5 => ()
2: 0x095ea7b3 => approve(address,uint256)
3: 0x23b872dd => transferFrom(address,address,uint256)
4: 0xa9059cbb => transfer(address,uint256)
5: 0x13af4035 => setOwner(address)
6: 0x79ce9fac => transfer(bytes32,address)
7: 0xeadf9760 => setUint(bytes32,string,uint256)
8: 0x47448e8a => set(bytes32,string,bytes32)
9: 0xf6d339e4 => setAddress(bytes32,string,address)
10: 0x69fe0e2d => setFee(uint256)
11: 0x4f39ca59 => drop(bytes32)
12: 0xf25eb5c1 => removeReverse()
13: 0xac4e73f9 => proposeReverse(string,address)
14: 0x432ced04 => reserve(bytes32)
15: 0x3f3935d1 => confirmReverse(string)
16: 0x9890220b => drain()
17: 0x29cbdc86 => buyin(address,uint256)
18: 0x5af36e3e => refund(uint256,uint256)
19: 0xa02b161e => unregister(uint256)
20: 0x66b42dcb => register(address,string,uint256,string)
21: 0xdd93890b => setMeta(uint256,bytes32,bytes32)
22: 0x7b1a547c => registerAs(address,string,uint256,string,address)
23: 0x2196ae0d => hint(bytes32,string,bytes20)
24: 0x7c8c6643 => unhint(bytes32)
25: 0x02f2008d => hintURL(bytes32,string)
26: 0x1a0919dc => unregister(bytes32)
27: 0xc52bd836 => setDappOwner(bytes32,address)
28: 0x91cd242d => setMeta(bytes32,bytes32,bytes32)
29: 0xe1fa8e84 => register(bytes32)
30: 0x4a18695f => triggerPayOut(uint256)
31: 0xd0517bd3 => markSigned()
32: 0xb8b4f1a0 => signContract()
33: 0xb20d30a9 => setDailyLimit(uint256)
34: 0xb75c7dc6 => revoke(bytes32)
35: 0x5c52c2f5 => resetSpentToday()
36: 0x173825d9 => removeOwner(address)
37: 0xcbf0b0c0 => kill(address)
38: 0x2f54bf6e => isOwner(address)
39: 0xb61d27f6 => execute(address,uint256,bytes)
40: 0x797af627 => confirm(bytes32)
41: 0xba51a6df => changeRequirement(uint256)
42: 0xf00d4b5d => changeOwner(address,address)
43: 0x7065cb48 => addOwner(address)
44: 0xca5eb5e1 => setDelegate(address)
45: 0x74a8f103 => revoke(address)
46: 0x338cdca1 => request()
47: 0x3da5c3ce => puzzle(address,bytes32)
48: 0x14253887 => certify(address)
49: 0x26316e58 => setMigrationMaster(address)
50: 0x590e1ae3 => refund()
51: 0x75e2ff65 => setMigrationAgent(address)
52: 0x454b0608 => migrate(uint256)
53: 0xefc81a8c => create()
54: 0x4bb278f3 => finalize()
55: 0xcae9ca51 => approveAndCall(address,uint256,bytes)
56: 0xeb1ff845 => changeId(uint256,uint256,uint256)
57: 0x75090ebf => changeDomain(uint256,uint256,uint256,address)
58: 0x2ebec916 => withdrawalProfit()
59: 0xf2fde38b => transferOwnership(address)
60: 0x523aee69 => changeTokenContract(address)
61: 0x20145328 => changeMelonportAddress(address)
62: 0xdda44b10 => buyRecipient(address,uint8,bytes32,bytes32)
63: 0xd89397b1 => btcsBuyRecipient(address)
64: 0xe5fe4f31 => buy(uint8,bytes32,bytes32)
65: 0xcb3e64fd => unhalt()
66: 0x5ed7ca5b => halt()
67: 0x5de01497 => ownerWithdrawERC20Token(address,uint256)
68: 0x3d6a32bd => createTradeContract(address,uint256,uint256,uint256,bool,bool)
69: 0xc60ccb0e => takerBuyAsset()
70: 0xeff883bd => takerSellAsset(uint256)
71: 0x2170ebf7 => makerWithdrawEther(uint256)
72: 0xcd53a3b7 => makerWithdrawAsset(uint256)
73: 0xc34764cf => makerWithdrawERC20Token(address,uint256)
74: 0x919f8cfc => makerDepositEther()
75: 0x52954e5a => makerTransferAsset(address,uint256)
76: 0xa7abc124 => activate(bool,bool)
77: 0xbe86d5a7 => makerTransferEther(address,uint256)
78: 0xc5e412e5 => createSaleContract(address,uint256,uint256,bool)
79: 0xce5e84a3 => activate(bool)
80: 0x17be89f0 => createTradeContract(address,uint256,uint256,bool)
81: 0x8d92fdf3 => withdrawAsset(uint256)
82: 0x9e281a98 => withdrawToken(address,uint256)
83: 0x2e1a7d4d => withdraw(uint256)
84: 0xa6f2ae3a => buy()
85: 0xbca7a9e2 => lockToken()
86: 0x49606455 => take(bytes32,uint128)
87: 0x093f5198 => make(address,address,uint128,uint128)
88: 0xf09ea2a6 => offer(uint256,address,uint256,address)
89: 0xd6febde8 => buy(uint256,uint256)
90: 0x779997c3 => bump(bytes32)
91: 0x40e58ee5 => cancel(uint256)
92: 0xb4f9b6c8 => kill(bytes32)
93: 0xb4427263 => createTokens()
94: 0xa91ee0dc => setRegistry(address)
95: 0xb660f4c0 => setTargetDragoDAO(address,address)
96: 0xf08d29d5 => createDrago(string,string)
97: 0x74fc5df7 => changeDragoDAO(address)
98: 0x1c31f710 => setBeneficiary(address)
99: 0x0bc1f0d5 => changeGabcoinDAO(address)
100: 0xe96e7a7e => createGabcoin(string,string)
101: 0x05db8f74 => setTargetGabcoinDAO(address,address)
102: 0xbb60160e => setExchange(address,address)
103: 0xf3fef3a3 => withdraw(address,uint256)
104: 0x92a0006c => trade(address,uint256,address,uint256,uint256,address,uint256)
105: 0x808187e5 => finalize(address,uint24)
106: 0x5ae11d5d => order(address,uint256,address,uint256,uint256)
107: 0x279a7094 => orderCFD(address,bool,uint32,uint128)
108: 0xd19d5138 => setCfdMaxLeverage(address,uint24)
109: 0xa7154d22 => cancel(address,uint32)
110: 0x47e7ef24 => deposit(address,uint256)
111: 0x7879de90 => cancelOrder(address,uint256,address,uint256,uint256)
112: 0x0e5c8d41 => addCredits(address,uint256,address,uint256,uint24)
113: 0xc81d1d5b => getPunk(uint256)
114: 0xbed2af80 => getPunk()
115: 0x3ccfd60b => withdraw()
116: 0xf6eeff1e => punkNoLongerForSale(uint256)
117: 0x8b72a2ec => transferPunk(address,uint256)
118: 0x08573a0b => reservePunksForOwner(uint256)
119: 0x62a5af3b => freeze()
120: 0xe5225381 => collect()
121: 0xd1058e59 => claimAll()
122: 0x379607f5 => claim(uint256)
123: 0xc4d66de8 => initialize(address)
124: 0x7a9e5e4b => setAuthority(address)
125: 0xe0cb3aa0 => buyWithLimit(uint256,uint256)
126: 0x40cc8854 => bite(bytes32)
127: 0x080b6132 => chop(uint128)
128: 0xbdf6e771 => cuff(uint128)
129: 0xf5893bd0 => crop(uint128)
130: 0x56371435 => cork(uint128)
131: 0xcbd5accc => cage(uint128)
132: 0x295d3a54 => join(uint128)
133: 0x67c48dd6 => free(bytes32,uint128)
134: 0x9f678cca => drip()
135: 0x69855358 => exit(uint128)
136: 0xa22664ad => draw(bytes32,uint128)
137: 0xc92aecc4 => chi()
138: 0x51f91066 => tag()
139: 0xb84d2106 => shut(bytes32)
140: 0xfcfff16f => open()
141: 0x4b11982e => setCooldown(uint64)
142: 0x07c241aa => lock(bytes32,uint128)
143: 0xbaa8529c => give(bytes32,address)
144: 0x6299c83e => wipe(bytes32,uint128)
145: 0xde3e7a9c => boom(uint128)
146: 0x90089adf => bust(uint128)
147: 0x65d1df24 => s2s()
148: 0x420f2ed9 => jump(uint128)
149: 0xc8d10f40 => warp(uint64)
150: 0xca2201c5 => coax(uint128)
151: 0x495d32cb => par()
152: 0x0302c688 => prod()
153: 0x1406b921 => vent()
154: 0x961be391 => cash()
155: 0x69245009 => cage()
156: 0x44df8e70 => burn()
157: 0x90bc1693 => burn(uint128)
158: 0x7261e469 => burn(address,uint128)
159: 0xbe29184f => mint(address,uint128)
160: 0x8402181f => pull(address,uint128)
161: 0x69d3e20e => mint(uint128)
162: 0x12e5ff77 => exit(address,uint128)
163: 0x89afcb44 => burn(address)
164: 0x6b2113cd => pull(address,address,uint128)
165: 0xceae3abd => join(address,uint128)
166: 0x52d11238 => pull(address)
167: 0x66b3bcb0 => pull(address,address)
168: 0x3c71a7c7 => push(address,address,uint128)
169: 0x89b09de7 => push(address)
170: 0x03438dd0 => swap(address)
171: 0x3452f51d => push(address,uint128)
172: 0xb716ba67 => push(address,address)
173: 0xa57d9ad3 => lend(address,uint128)
174: 0x379f5217 => mend(address,uint128)
175: 0xe0cd1d69 => heal(address)
176: 0x065a50ed => pool(address,uint128)
177: 0x114e11a0 => take(address,uint128)
178: 0xbe9a6555 => start()
179: 0x5ac801fe => setName(bytes32)
180: 0x07da68f5 => stop()
181: 0x67aff484 => setUserRole(address,uint8,bool)
182: 0xd381ba7c => setRootUser(address,bool)
183: 0xc6b0263e => setPublicCapability(address,bytes4,bool)
184: 0x7d40583d => setRoleCapability(uint8,address,bytes4,bool)
185: 0xf0217ce5 => permit(bytes32,bytes32,bytes32)
186: 0xcbeea68c => permit(address,address,bytes32)
187: 0x79d88d87 => forbid(bytes32,bytes32,bytes32)
188: 0x2bc3217d => forbid(address,address,bytes32)
感谢您阅读 @chaimyu 的帖子,期待您能留言交流!
Congratulations @chaimyu! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Click on any badge to view your own Board of Honor on SteemitBoard.
To support your work, I also upvoted your post!
For more information about SteemitBoard, click here
If you no longer want to receive notifications, reply to this comment with the word
STOP
很有用的文章,给你点赞。
有个问题请教一下,懂编程的人应该已经知道可以用你的方法解决问题了,但是我不太懂编程,不会用python,有没有简单点的方法可以实现以太坊地址上的以太币或代币数量发生变化时就收到邮件通知?
钱包直接加入观察地址就能通知,但不是邮件。为什么要邮件通知呢?
是手机钱包吗?哪个钱包可以通知?不一定需要邮件
我看imToken有提示
钱包在后台能不能通知还真没注意,哪天试试,imToken之类的不行吗?交易所充值是有通知的。