forked from 0xsequence/ethkit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
224 lines (188 loc) · 6.21 KB
/
main.go
File metadata and controls
224 lines (188 loc) · 6.21 KB
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package main
import (
"bytes"
"context"
"fmt"
"log"
"log/slog"
"os"
"sync"
"time"
"github.com/0xsequence/ethkit"
"github.com/0xsequence/ethkit/ethmonitor"
"github.com/0xsequence/ethkit/ethreceipts"
"github.com/0xsequence/ethkit/ethrpc"
"github.com/0xsequence/ethkit/go-ethereum/common"
"github.com/0xsequence/ethkit/go-ethereum/core/types"
"github.com/0xsequence/ethkit/go-ethereum/crypto"
"github.com/0xsequence/ethkit/util"
)
var ETH_NODE_URL = "http://localhost:8545"
var ETH_NODE_WSS_URL = ""
func init() {
testConfig, err := util.ReadTestConfig("../../ethkit-test.json")
if err != nil {
panic(err)
}
if testConfig["POLYGON_MAINNET_URL"] != "" {
ETH_NODE_URL = testConfig["POLYGON_MAINNET_URL"]
ETH_NODE_WSS_URL = testConfig["POLYGON_MAINNET_WSS_URL"]
}
// if testConfig["MAINNET_URL"] != "" {
// ETH_NODE_URL = testConfig["MAINNET_URL"]
// ETH_NODE_WSS_URL = testConfig["MAINNET_WSS_URL"]
// }
}
func main() {
fmt.Println("chain-receipts start")
// Provider
provider, err := ethrpc.NewProvider(ETH_NODE_URL, ethrpc.WithStreaming(ETH_NODE_WSS_URL))
if err != nil {
log.Fatal(err)
}
// Monitor options
monitorOptions := ethmonitor.DefaultOptions
monitorOptions.PollingInterval = time.Duration(1000 * time.Millisecond)
// monitorOptions.DebugLogging = true
monitorOptions.WithLogs = true
monitorOptions.BlockRetentionLimit = 400
monitorOptions.StartBlockNumber = nil // track the head
receiptListenerOptions := ethreceipts.DefaultOptions
receiptListenerOptions.NumBlocksToFinality = 20
receiptListenerOptions.FilterMaxWaitNumBlocks = 5
err = listener(provider, monitorOptions, receiptListenerOptions)
if err != nil {
log.Fatal(err)
}
}
func listener(provider *ethrpc.Provider, monitorOptions ethmonitor.Options, receiptListenerOptions ethreceipts.Options) error {
ctx := context.Background()
monitor, err := ethmonitor.NewMonitor(provider, monitorOptions)
if err != nil {
log.Fatal(err)
}
go func() {
err = monitor.Run(ctx)
if err != nil {
panic(err)
}
}()
defer monitor.Stop()
// monitorSub := monitor.Subscribe()
// defer monitorSub.Unsubscribe()
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
receiptListener, err := ethreceipts.NewReceiptsListener(logger, provider, monitor, receiptListenerOptions)
if err != nil {
log.Fatal(err)
}
go func() {
err := receiptListener.Run(ctx)
if err != nil {
log.Fatal(err)
}
}()
defer receiptListener.Stop()
// Find specific meta transaction -- note: this is not the "transaction hash",
// this is a sub-transaction where the id is emitted as an event.
FilterMetaTransactionID := func(metaTxnID ethkit.Hash) ethreceipts.FilterQuery {
return ethreceipts.FilterLogs(func(logs []*types.Log) bool {
for _, log := range logs {
isTxExecuted := IsTxExecutedEvent(log, metaTxnID)
isTxFailed := IsTxFailedEvent(log, metaTxnID)
if isTxExecuted || isTxFailed {
// found the sequence meta txn
return true
}
}
return false
})
}
_ = FilterMetaTransactionID
// Find any Sequence meta txns
FilterMetaTransactionAny := func() ethreceipts.FilterQuery {
return ethreceipts.FilterLogs(func(logs []*types.Log) bool {
foundNonceEvent := false
for _, log := range logs {
if len(log.Topics) > 0 && log.Topics[0] == NonceChangeEventSig {
foundNonceEvent = true
break
}
}
if !foundNonceEvent {
return false
}
for _, log := range logs {
if len(log.Topics) == 1 && log.Topics[0] == TxFailedEventSig {
// failed sequence txn
return true
} else if len(log.Topics) == 0 && len(log.Data) == 32 {
// possibly a successful sequence txn -- but not for certain
return true
}
}
return false
})
}
_ = FilterMetaTransactionAny
sub := receiptListener.Subscribe(
// FilterMetaTransactionID(common.HexToHash("2d5174e4f5ff20a19c34b63e90818c9ced7854675a679373be92b87f718118d4")).LimitOne(true),
FilterMetaTransactionAny().MaxWait(0), // listen on all sequence txns
)
var wg sync.WaitGroup
wg.Go(func() {
for {
select {
case receipt := <-sub.TransactionReceipt():
fmt.Println("=> sequence txn receipt:", receipt.TransactionHash())
// This block of code will search for the same metaTxnID in 10 seconds,
// so that we can ensure we can find it easily from our cache.
// go func(txn common.Hash, receipt ethreceipts.Receipt) {
// time.Sleep(10 * time.Second)
// var metaTxnID common.Hash
// for _, log := range receipt.Logs() {
// if len(log.Topics) == 0 && len(log.Data) == 32 {
// metaTxnID = common.BytesToHash(log.Data)
// }
// }
// fmt.Println("-> search for metaTxnID", metaTxnID)
// r, _, err := receiptListener.FetchTransactionReceiptWithFilter(context.Background(), FilterMetaTransactionID(metaTxnID).LimitOne(true).SearchCache(true))
// if err != nil {
// panic(err)
// }
// fmt.Println("===> found the meta txn! txnHash:", r.TransactionHash())
// }(receipt.TransactionHash(), receipt)
case <-sub.Done():
return
}
}
})
wg.Wait()
return nil
}
// Transaction events as defined in wallet-contracts IModuleCalls.sol
var (
// NonceChangeEventSig is the signature event emitted as the first event on the batch execution
// 0x1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881
NonceChangeEventSig = MustEncodeSig("NonceChange(uint256,uint256)")
// TxFailedEventSig is the signature event emitted in a failed smart-wallet meta-transaction batch
// 0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7
TxFailedEventSig = MustEncodeSig("TxFailed(bytes32,bytes)")
// TxExecutedEventSig is the signature of the event emitted in a successful transaction
// 0x0639b0b186d373976f8bb98f9f7226ba8070f10cb6c7f9bd5086d3933f169a25
TxExecutedEventSig = MustEncodeSig("TxExecuted(bytes32)")
)
func MustEncodeSig(str string) common.Hash {
return crypto.Keccak256Hash([]byte(str))
}
func IsTxExecutedEvent(log *types.Log, hash common.Hash) bool {
return len(log.Topics) == 0 &&
len(log.Data) == 32 &&
bytes.Equal(log.Data, hash[:])
}
func IsTxFailedEvent(log *types.Log, hash common.Hash) bool {
return len(log.Topics) == 1 &&
log.Topics[0] == TxFailedEventSig &&
bytes.HasPrefix(log.Data, hash[:])
}