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  }