Skip to content

Commit c9abfaf

Browse files
committed
update
1 parent 120a21d commit c9abfaf

File tree

2 files changed

+83
-38
lines changed

2 files changed

+83
-38
lines changed

receipts/token_transfers.go

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,46 +29,52 @@ func FetchReceiptTokenTransfers(ctx context.Context, provider *ethrpc.Provider,
2929
return receipt, transfers, nil
3030
}
3131

32-
var tokenTransferTopicHashes = []common.Hash{
33-
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), // ERC20 Transfer
34-
common.HexToHash("0xe6497e3ee548a3372136af2fcb0696db31fc6cf20260707645068bd3fe97f3c4"), // Polygon POL LogTransfer (custom)
35-
}
32+
var (
33+
erc20TransferTopic = common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
34+
polLogTransferTopic = common.HexToHash("0xe6497e3ee548a3372136af2fcb0696db31fc6cf20260707645068bd3fe97f3c4")
35+
)
3636

3737
func DecodeTokenTransfersFromLogs(ctx context.Context, logs []*types.Log) (TokenTransfers, error) {
38-
transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
39-
polLogTransferTopic := common.HexToHash("0xe6497e3ee548a3372136af2fcb0696db31fc6cf20260707645068bd3fe97f3c4")
40-
4138
var decoded []*TokenTransfer
4239

4340
for _, log := range logs {
4441
if len(log.Topics) == 0 {
4542
continue
4643
}
47-
if log.Topics[0] != transferTopic && log.Topics[0] != polLogTransferTopic {
48-
continue
49-
}
50-
5144
tokenAddress := log.Address
5245

53-
if log.Topics[0] == transferTopic {
46+
switch log.Topics[0] {
47+
case erc20TransferTopic:
48+
// ERC20 Transfer
5449
filterer, err := tokens.NewIERC20Filterer(log.Address, nil)
5550
if err == nil {
5651
if ev, err := filterer.ParseTransfer(*log); err == nil && ev != nil {
5752
decoded = append(decoded, &TokenTransfer{Token: tokenAddress, From: ev.From, To: ev.To, Value: ev.Value, Raw: *log})
5853
continue
5954
}
6055
}
61-
}
6256

63-
// TODO: need to try all of the various versions of this.. and we may as well support ERC721 and ERC1155 too
64-
// note: "indexed" args, etc.
57+
case polLogTransferTopic:
58+
// Polygon POL LogTransfer (custom)
59+
// https://polygonscan.com/address/0x0000000000000000000000000000000000001010#code
60+
//
61+
// ABI:
62+
// event LogTransfer(address indexed token, address indexed from, address indexed to,
63+
// uint256 amount, uint256 input1, uint256 input2, uint256 output1, uint256 output2)
64+
if len(log.Topics) >= 4 {
65+
from := common.BytesToAddress(log.Topics[2].Bytes())
66+
to := common.BytesToAddress(log.Topics[3].Bytes())
67+
var value *big.Int
68+
if len(log.Data) >= 32 {
69+
value = new(big.Int).SetBytes(log.Data[:32])
70+
} else {
71+
value = new(big.Int)
72+
}
73+
decoded = append(decoded, &TokenTransfer{Token: tokenAddress, From: from, To: to, Value: value, Raw: *log})
74+
}
6575

66-
// TODO: this is wrong, etc.
67-
if len(log.Topics) >= 3 {
68-
from := common.BytesToAddress(log.Topics[1].Bytes())
69-
to := common.BytesToAddress(log.Topics[2].Bytes())
70-
value := new(big.Int).SetBytes(log.Data)
71-
decoded = append(decoded, &TokenTransfer{Token: tokenAddress, From: from, To: to, Value: value, Raw: *log})
76+
default:
77+
continue
7278
}
7379
}
7480

@@ -93,7 +99,7 @@ type TokenBalance struct {
9399

94100
type TokenBalances []*TokenBalance
95101

96-
func (t TokenTransfers) FilterByContractAddress(ctx context.Context, contract common.Address) TokenTransfers {
102+
func (t TokenTransfers) FilterByContractAddress(contract common.Address) TokenTransfers {
97103
var out TokenTransfers
98104
for _, transfer := range t {
99105
if transfer.Raw.Address == contract {
@@ -103,7 +109,7 @@ func (t TokenTransfers) FilterByContractAddress(ctx context.Context, contract co
103109
return out
104110
}
105111

106-
func (t TokenTransfers) FilterByAccountAddress(ctx context.Context, account common.Address) TokenTransfers {
112+
func (t TokenTransfers) FilterByAccountAddress(account common.Address) TokenTransfers {
107113
var out TokenTransfers
108114
for _, transfer := range t {
109115
if transfer.From == account || transfer.To == account {
@@ -113,7 +119,7 @@ func (t TokenTransfers) FilterByAccountAddress(ctx context.Context, account comm
113119
return out
114120
}
115121

116-
func (t TokenTransfers) FilterByFromAddress(ctx context.Context, from common.Address) TokenTransfers {
122+
func (t TokenTransfers) FilterByFromAddress(from common.Address) TokenTransfers {
117123
var out TokenTransfers
118124
for _, transfer := range t {
119125
if transfer.From == from {
@@ -123,7 +129,7 @@ func (t TokenTransfers) FilterByFromAddress(ctx context.Context, from common.Add
123129
return out
124130
}
125131

126-
func (t TokenTransfers) FilterByToAddress(ctx context.Context, to common.Address) TokenTransfers {
132+
func (t TokenTransfers) FilterByToAddress(to common.Address) TokenTransfers {
127133
var out TokenTransfers
128134
for _, transfer := range t {
129135
if transfer.To == to {
@@ -141,7 +147,7 @@ func (t TokenTransfers) Delta() TokenTransfers {
141147
// ComputeBalanceOutputs aggregates net balance changes per token per account from the transfers.
142148
// For each transfer, it subtracts `Value` from `From` and adds `Value` to `To`.
143149
// Accounts with a resulting zero balance change for a given token are omitted.
144-
func (s TokenTransfers) ComputeBalanceOutputs() TokenBalances {
150+
func (s TokenTransfers) ComputeBalanceOutputs(omitZeroBalances ...bool) TokenBalances {
145151
// key: token address + account address
146152
type key struct {
147153
token common.Address
@@ -175,7 +181,7 @@ func (s TokenTransfers) ComputeBalanceOutputs() TokenBalances {
175181
zero := big.NewInt(0)
176182

177183
for k, v := range balances {
178-
if v == nil || v.Cmp(zero) == 0 {
184+
if v == nil || (len(omitZeroBalances) > 0 && omitZeroBalances[0] && v.Cmp(zero) == 0) {
179185
continue
180186
}
181187
out = append(out, &TokenBalance{
@@ -208,6 +214,17 @@ func (s TokenTransfers) ComputeBalanceOutputs() TokenBalances {
208214
return out
209215
}
210216

217+
func (b TokenBalances) OmitZeroBalances() TokenBalances {
218+
var out TokenBalances
219+
zero := big.NewInt(0)
220+
for _, bal := range b {
221+
if bal.Balance != nil && bal.Balance.Cmp(zero) != 0 {
222+
out = append(out, bal)
223+
}
224+
}
225+
return out
226+
}
227+
211228
func (b TokenBalances) FilterByAccount(account common.Address, optToken ...common.Address) TokenBalances {
212229
var out TokenBalances
213230
for _, bal := range b {

receipts/token_transfers_test.go

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestFetchReceiptTokenTransfers(t *testing.T) {
3434
// Get the balance outputs from the transfer logs
3535
balances := transfers.ComputeBalanceOutputs()
3636
require.NotNil(t, balances)
37-
require.Equal(t, len(balances), 2)
37+
require.Equal(t, 2, len(balances))
3838
// spew.Dump(balances)
3939

4040
require.Equal(t, common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831"), balances[0].Token) // USDC
@@ -77,7 +77,7 @@ func TestFetchReceiptTokenTransfers(t *testing.T) {
7777
// Get the balance outputs from the transfer logs
7878
balances := transfers.ComputeBalanceOutputs()
7979
require.NotNil(t, balances)
80-
require.Equal(t, len(balances), 4)
80+
require.Equal(t, 4, len(balances))
8181
// spew.Dump(balances)
8282

8383
require.Equal(t, common.HexToAddress("0x539bdE0d7Dbd336b79148AA742883198BBF60342"), balances[0].Token) // MAGIC
@@ -139,9 +139,9 @@ func TestFetchReceiptTokenTransfers(t *testing.T) {
139139
require.Equal(t, big.NewInt(9979), transfers[2].Value)
140140

141141
// Get the balance outputs from the transfer logs
142-
balances := transfers.ComputeBalanceOutputs()
142+
balances := transfers.ComputeBalanceOutputs().OmitZeroBalances()
143143
require.NotNil(t, balances)
144-
require.Equal(t, len(balances), 3)
144+
require.Equal(t, 3, len(balances))
145145
// spew.Dump(balances)
146146

147147
require.Equal(t, usdc, balances[0].Token)
@@ -176,9 +176,9 @@ func TestFetchReceiptTokenTransfers(t *testing.T) {
176176
// spew.Dump(transfers)
177177

178178
// Get the balance outputs from the transfer logs
179-
balances := transfers.ComputeBalanceOutputs()
179+
balances := transfers.ComputeBalanceOutputs().OmitZeroBalances()
180180
require.NotNil(t, balances)
181-
require.Equal(t, len(balances), 9)
181+
require.Equal(t, 9, len(balances))
182182
// spew.Dump(balances)
183183

184184
usdc := common.HexToAddress("0xaf88d065e77c8cC2239327C5EDb3A432268e5831")
@@ -270,14 +270,13 @@ func TestFetchReceiptTokenTransfers(t *testing.T) {
270270
require.Greater(t, len(transfers), 0)
271271
require.Equal(t, 1, len(receipt.Logs))
272272

273-
// Trails intent
274273
require.Equal(t, 1, len(transfers))
275274
// spew.Dump(transfers)
276275

277276
// Get the balance outputs from the transfer logs
278277
balances := transfers.ComputeBalanceOutputs()
279278
require.NotNil(t, balances)
280-
require.Equal(t, len(balances), 2)
279+
require.Equal(t, 2, len(balances))
281280
// spew.Dump(balances)
282281

283282
require.Equal(t, common.HexToAddress("0x203A662b0BD271A6ed5a60EdFbd04bFce608FD36"), balances[0].Token)
@@ -292,17 +291,46 @@ func TestFetchReceiptTokenTransfers(t *testing.T) {
292291
// Case 6: polygon POL LogTransfer event
293292
// https://polygonscan.com/tx/0x252419983224542bfb07dab75808fa57186a7a269d0d267ae655eb7ef037fdd5
294293
t.Run("Case 6: POL with LogTransfer", func(t *testing.T) {
294+
provider, err := ethrpc.NewProvider("https://nodes.sequence.app/polygon")
295+
require.NoError(t, err)
296+
297+
txnHash := common.HexToHash("0x252419983224542bfb07dab75808fa57186a7a269d0d267ae655eb7ef037fdd5")
298+
receipt, transfers, err := receipts.FetchReceiptTokenTransfers(context.Background(), provider, txnHash)
299+
require.NoError(t, err)
300+
require.NotNil(t, receipt)
301+
require.Greater(t, len(transfers), 0)
302+
require.Equal(t, 25, len(receipt.Logs)) // actually a ton of stuff in here
303+
304+
// Trails intent
305+
transfers = transfers.FilterByContractAddress(common.HexToAddress("0x0000000000000000000000000000000000001010")) // POL token
306+
require.Equal(t, 2, len(transfers))
307+
// spew.Dump(transfers)
308+
309+
// Get the balance outputs from the transfer logs
310+
balances := transfers.ComputeBalanceOutputs(true) // omit zero balances
311+
require.NotNil(t, balances)
312+
require.Equal(t, 2, len(balances))
313+
// spew.Dump(balances)
314+
315+
polPsuedoToken := common.HexToAddress("0x0000000000000000000000000000000000001010")
316+
317+
require.Equal(t, polPsuedoToken, balances[0].Token)
318+
require.Equal(t, common.HexToAddress("0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270"), balances[0].Account)
319+
require.Equal(t, makeBigInt(t, "-3965683724100320759"), balances[0].Balance)
320+
321+
require.Equal(t, polPsuedoToken, balances[1].Token)
322+
require.Equal(t, common.HexToAddress("0x1D17C0F90A0b3dFb5124C2FF56B33a0D2E202e1d"), balances[1].Account)
323+
require.Equal(t, makeBigInt(t, "3965683724100320759"), balances[1].Balance)
295324
})
296325

297326
// Case 7: bunch of logs for the same erc20 token, and we need to sum it up, ie. a big uniswap call
298327
// https://etherscan.io/tx/0xb11ff491495e145b07a1d3cc304f7d04b235b80af51b50da9a54095a6882fca4
299328
t.Run("Case 7: Random txn with swap and many tokens", func(t *testing.T) {
329+
// NOTE, skippig writing this for now as its pretty similar
330+
// to case 4
300331
})
301332
}
302333

303-
// TODO: lets test the TokenTransfers directly with mock
304-
// data we write by hand to ensure aggregation works properly, etc.
305-
306334
func makeBigInt(t *testing.T, s string) *big.Int {
307335
bi, ok := new(big.Int).SetString(s, 10)
308336
require.True(t, ok)

0 commit comments

Comments
 (0)