Skip to content

Commit 931092b

Browse files
committed
FetchReceipt
1 parent 9708f4a commit 931092b

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

receipts/fetch.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package receipts
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"math/big"
7+
8+
"github.com/0xsequence/ethkit/ethrpc"
9+
"github.com/0xsequence/ethkit/go-ethereum"
10+
"github.com/0xsequence/ethkit/go-ethereum/common"
11+
"github.com/0xsequence/ethkit/go-ethereum/core/types"
12+
sequence "github.com/0xsequence/go-sequence"
13+
)
14+
15+
// FetchReceipt looks up the transaction that emitted Call* events for the given
16+
// digest and decodes the corresponding receipt information.
17+
func FetchReceipt(ctx context.Context, digest common.Hash, provider *ethrpc.Provider, fromBlock, toBlock *big.Int) (*types.Receipt, *Receipt, error) {
18+
if provider == nil {
19+
return nil, nil, fmt.Errorf("no provider")
20+
}
21+
22+
query := ethereum.FilterQuery{
23+
FromBlock: fromBlock,
24+
ToBlock: toBlock,
25+
Topics: [][]common.Hash{
26+
{sequence.V3CallSucceeded, sequence.V3CallFailed, sequence.V3CallAborted, sequence.V3CallSkipped},
27+
{digest},
28+
},
29+
}
30+
31+
logs, err := provider.FilterLogs(ctx, query)
32+
if err != nil {
33+
return nil, nil, fmt.Errorf("unable to filter logs: %w", err)
34+
}
35+
if len(logs) == 0 {
36+
// Fallback for legacy events where the digest is not indexed.
37+
query.Topics = [][]common.Hash{{sequence.V3CallSucceeded, sequence.V3CallFailed, sequence.V3CallAborted, sequence.V3CallSkipped}}
38+
logs, err = provider.FilterLogs(ctx, query)
39+
if err != nil {
40+
return nil, nil, fmt.Errorf("unable to filter logs without digest topic: %w", err)
41+
}
42+
}
43+
44+
log, callIndex, err := findDigestLog(logs, digest)
45+
if err != nil {
46+
return nil, nil, err
47+
}
48+
49+
receipt, err := provider.TransactionReceipt(ctx, log.TxHash)
50+
if err != nil {
51+
return nil, nil, fmt.Errorf("unable to get transaction receipt %v: %w", log.TxHash, err)
52+
}
53+
54+
decoded, err := TransactionReceiptsForReceipt(ctx, receipt, provider)
55+
if err != nil {
56+
return receipt, nil, fmt.Errorf("unable to decode transaction receipt %v: %w", receipt.TxHash, err)
57+
}
58+
59+
receipts := decoded.Find(digest)
60+
if receipts == nil {
61+
return receipt, nil, fmt.Errorf("decoded receipts do not include digest %v", digest)
62+
}
63+
64+
if !callIndex.IsUint64() {
65+
return receipt, nil, fmt.Errorf("call index %v overflows uint64", callIndex)
66+
}
67+
68+
idx := callIndex.Uint64()
69+
if idx >= uint64(len(receipts.Receipts)) {
70+
return receipt, nil, fmt.Errorf("call index %d out of range for %d receipts", idx, len(receipts.Receipts))
71+
}
72+
73+
return receipt, &receipts.Receipts[idx], nil
74+
}
75+
76+
func findDigestLog(logs []types.Log, digest common.Hash) (*types.Log, *big.Int, error) {
77+
var selected *types.Log
78+
var callIndex *big.Int
79+
80+
for i := range logs {
81+
log := &logs[i]
82+
83+
index, err := decodeCallIndex(log, digest)
84+
if err != nil {
85+
continue
86+
}
87+
88+
if selected == nil || isNewerLog(log, selected) {
89+
selected = log
90+
callIndex = index
91+
}
92+
}
93+
94+
if selected == nil {
95+
return nil, nil, fmt.Errorf("no Call* events found for digest %v", digest)
96+
}
97+
98+
return selected, callIndex, nil
99+
}
100+
101+
func decodeCallIndex(log *types.Log, digest common.Hash) (*big.Int, error) {
102+
if hash, index, err := sequence.V3DecodeCallSucceededEvent(log); err == nil && hash == digest {
103+
return index, nil
104+
}
105+
if hash, index, _, err := sequence.V3DecodeCallFailedEvent(log); err == nil && hash == digest {
106+
return index, nil
107+
}
108+
if hash, index, _, err := sequence.V3DecodeCallAbortedEvent(log); err == nil && hash == digest {
109+
return index, nil
110+
}
111+
if hash, index, err := sequence.V3DecodeCallSkippedEvent(log); err == nil && hash == digest {
112+
return index, nil
113+
}
114+
115+
return nil, fmt.Errorf("log %v does not match digest %v", log.TxHash, digest)
116+
}
117+
118+
func isNewerLog(a, b *types.Log) bool {
119+
if a.BlockNumber != b.BlockNumber {
120+
return a.BlockNumber > b.BlockNumber
121+
}
122+
if a.TxIndex != b.TxIndex {
123+
return a.TxIndex > b.TxIndex
124+
}
125+
return a.Index > b.Index
126+
}

0 commit comments

Comments
 (0)