github.com/0xsequence/ethkit@v1.25.0/ethreceipts/filterer.go (about) 1 package ethreceipts 2 3 import ( 4 "context" 5 6 "github.com/0xsequence/ethkit" 7 "github.com/0xsequence/ethkit/go-ethereum/core/types" 8 ) 9 10 // Filter the transaction payload for specific txn "hash" 11 func FilterTxnHash(txnHash ethkit.Hash) FilterQuery { 12 return &filter{ 13 cond: FilterCond{ 14 TxnHash: ethkit.ToPtr(txnHash), 15 }, 16 17 // default options for TxnHash filter. Note, other filter conds 18 // have a different set of defaults. 19 options: FilterOptions{ 20 Finalize: true, 21 LimitOne: true, 22 SearchCache: true, 23 SearchOnChain: true, 24 25 // wait up to NumBlocksToFinality*2 number of blocks between 26 // filter matches before unsubcribing if no matches occured 27 MaxWait: ethkit.ToPtr(-1), 28 }, 29 30 exhausted: make(chan struct{}), 31 } 32 } 33 34 // Filter the transaction payload for "from" address. 35 func FilterFrom(from ethkit.Address) FilterQuery { 36 return &filter{ 37 cond: FilterCond{ 38 From: ethkit.ToPtr(from), 39 }, 40 41 // no default options for From filter 42 options: FilterOptions{}, 43 exhausted: make(chan struct{}), 44 } 45 } 46 47 // Filter the transaction payload for "to" address. 48 func FilterTo(to ethkit.Address) FilterQuery { 49 return &filter{ 50 cond: FilterCond{ 51 To: ethkit.ToPtr(to), 52 }, 53 54 // no default options for To filter 55 options: FilterOptions{}, 56 exhausted: make(chan struct{}), 57 } 58 } 59 60 // Filter the logs of a transaction and search for an event log 61 // from a specific contract address. 62 func FilterLogContract(contractAddress ethkit.Address) FilterQuery { 63 return FilterLogs(func(logs []*types.Log) bool { 64 for _, log := range logs { 65 if log.Address == contractAddress { 66 return true 67 } 68 } 69 return false 70 }) 71 } 72 73 // Filter the log topics for a transaction 74 func FilterLogTopic(eventTopicHash ethkit.Hash) FilterQuery { 75 return &filter{ 76 cond: FilterCond{ 77 LogTopic: ethkit.ToPtr(eventTopicHash), 78 }, 79 80 // no default options for EventSig filter 81 options: FilterOptions{}, 82 exhausted: make(chan struct{}), 83 } 84 } 85 86 // Filter logs of a transaction 87 func FilterLogs(logFn func([]*types.Log) bool) FilterQuery { 88 return &filter{ 89 cond: FilterCond{ 90 Logs: logFn, 91 }, 92 93 // no default options for Log filter 94 options: FilterOptions{}, 95 exhausted: make(chan struct{}), 96 } 97 } 98 99 type Filterer interface { 100 FilterQuery 101 102 FilterID() uint64 103 Options() FilterOptions 104 Cond() FilterCond 105 106 Match(ctx context.Context, receipt Receipt) (bool, error) 107 StartBlockNum() uint64 108 LastMatchBlockNum() uint64 109 Exhausted() <-chan struct{} 110 } 111 112 type FilterQuery interface { 113 ID(uint64) FilterQuery 114 Finalize(bool) FilterQuery 115 LimitOne(bool) FilterQuery 116 SearchCache(bool) FilterQuery 117 SearchOnChain(bool) FilterQuery 118 MaxWait(int) FilterQuery 119 } 120 121 type FilterOptions struct { 122 // .. 123 ID uint64 124 125 // .. 126 Finalize bool 127 128 // . 129 LimitOne bool 130 131 // .. 132 SearchCache bool 133 134 // SearchOnChain will search for txn hash on-chain. This is only useful 135 // when used in combination with TxnHash filter cond. 136 SearchOnChain bool 137 138 // MaxWait filter option waits some number of blocks without a filter match after 139 // which point will auto-unsubscribe the filter. This is useful to help automatically 140 // remove filters which likely won't come up. 141 // 142 // nil : use the ReceiptsListener option FilterMaxWaitNumBlocks value as the default 143 // -1 : set value to ReceiptsListener option NumFinality * 3 144 // 0 : option is disabled, and has no limit on wait. filters need to be manually unsubscribed 145 // N : a specified number of blocks without a match before unsusbcribe 146 MaxWait *int 147 } 148 149 type FilterCond struct { 150 TxnHash *ethkit.Hash 151 From *ethkit.Address 152 To *ethkit.Address 153 LogTopic *ethkit.Hash // event signature topic hash 154 Logs func([]*types.Log) bool 155 } 156 157 type filter struct { 158 options FilterOptions 159 cond FilterCond 160 161 // startBlockNum is the first block number observed once filter is active 162 startBlockNum uint64 163 164 // lastMatchBlockNum is the block number where a last match occured 165 lastMatchBlockNum uint64 166 167 // exhausted signals if the filter hit MaxWait 168 exhausted chan struct{} 169 } 170 171 var ( 172 _ Filterer = &filter{} 173 _ FilterQuery = &filter{} 174 ) 175 176 func (f *filter) ID(id uint64) FilterQuery { 177 f.options.ID = id 178 return f 179 } 180 181 func (f *filter) Finalize(finalize bool) FilterQuery { 182 f.options.Finalize = finalize 183 return f 184 } 185 186 func (f *filter) LimitOne(limitOne bool) FilterQuery { 187 f.options.LimitOne = limitOne 188 return f 189 } 190 191 func (f *filter) SearchCache(searchCache bool) FilterQuery { 192 f.options.SearchCache = searchCache 193 return f 194 } 195 196 func (f *filter) SearchOnChain(searchOnChain bool) FilterQuery { 197 f.options.SearchOnChain = searchOnChain 198 return f 199 } 200 201 func (f *filter) MaxWait(maxWait int) FilterQuery { 202 f.options.MaxWait = &maxWait 203 return f 204 } 205 206 func (f *filter) FilterID() uint64 { 207 return f.options.ID 208 } 209 210 func (f *filter) Options() FilterOptions { 211 return f.options 212 } 213 214 func (f *filter) Cond() FilterCond { 215 return f.cond 216 } 217 218 func (f *filter) Match(ctx context.Context, receipt Receipt) (bool, error) { 219 c := f.cond 220 221 if c.TxnHash != nil { 222 ok := receipt.TransactionHash() == *c.TxnHash 223 return ok, nil 224 } 225 226 if c.From != nil { 227 ok := receipt.From() == *c.From 228 return ok, nil 229 } 230 231 if c.To != nil { 232 ok := receipt.To() == *c.To 233 return ok, nil 234 } 235 236 if c.LogTopic != nil && len(receipt.Logs()) > 0 { 237 for _, log := range receipt.Logs() { 238 if len(log.Topics) == 0 { 239 continue 240 } 241 if *c.LogTopic == log.Topics[0] { 242 return true, nil 243 } 244 } 245 return false, nil 246 } 247 248 if c.Logs != nil && len(receipt.Logs()) > 0 { 249 ok := c.Logs(receipt.Logs()) 250 return ok, nil 251 } else if c.Logs != nil { 252 // no log matches, but a log filter is present 253 return false, nil 254 } 255 256 return false, ErrFilterCond 257 } 258 259 func (f *filter) StartBlockNum() uint64 { 260 return f.startBlockNum 261 } 262 263 func (f *filter) LastMatchBlockNum() uint64 { 264 return f.lastMatchBlockNum 265 } 266 267 func (f *filter) Exhausted() <-chan struct{} { 268 return f.exhausted 269 }