diff --git a/pkg/chain/ethereum/ethutil/subscribe_opts.go b/pkg/chain/ethereum/ethutil/subscribe_opts.go new file mode 100644 index 0000000..d165ba8 --- /dev/null +++ b/pkg/chain/ethereum/ethutil/subscribe_opts.go @@ -0,0 +1,52 @@ +package ethutil + +import "time" + +const ( + // DefaultSubscribeOptsTick is the default duration with which + // past events are pulled from the chain by the subscription monitoring + // mechanism if no other value is provided in SubscribeOpts when creating + // the subscription. + DefaultSubscribeOptsTick = 15 * time.Minute + + // DefaultSubscribeOptsPastBlocks is the default number of past blocks + // pulled from the chain by the subscription monitoring mechanism if no + // other value is provided in SubscribeOpts when creating the subscription. + DefaultSubscribeOptsPastBlocks = 100 + + // SubscriptionBackoffMax is the maximum backoff time between event + // resubscription attempts. + SubscriptionBackoffMax = 2 * time.Minute + + // SubscriptionAlertThreshold is time threshold below which event + // resubscription emits an error to the logs. + // WS connection can be dropped at any moment and event resubscription will + // follow. However, if WS connection for event subscription is getting + // dropped too often, it may indicate something is wrong with Ethereum + // client. This constant defines the minimum lifetime of an event + // subscription required before the subscription failure happens and + // resubscription follows so that the resubscription does not emit an error + // to the logs alerting about potential problems with Ethereum client + // connection. + SubscriptionAlertThreshold = 15 * time.Minute +) + +// SubscribeOpts specifies optional configuration options that can be passed +// when creating Ethereum event subscription. +type SubscribeOpts struct { + + // Tick is the duration with which subscription monitoring mechanism + // pulls events from the chain. This mechanism is an additional process + // next to a regular watchLogs subscription making sure no events are lost + // even in case the regular subscription missed them because of, for + // example, connectivity problems. + Tick time.Duration + + // PastBlocks is the number of past blocks subscription monitoring mechanism + // takes into consideration when pulling past events from the chain. + // This event pull mechanism is an additional process next to a regular + // watchLogs subscription making sure no events are lost even in case the + // regular subscription missed them because of, for example, connectivity + // problems. + PastBlocks uint64 +} diff --git a/tools/generators/ethereum/command.go.tmpl b/tools/generators/ethereum/command.go.tmpl index bd21546..beab0e1 100644 --- a/tools/generators/ethereum/command.go.tmpl +++ b/tools/generators/ethereum/command.go.tmpl @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/keep-network/keep-common/pkg/chain/ethereum/blockcounter" "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" "github.com/keep-network/keep-common/pkg/cmd" @@ -226,6 +227,14 @@ func initialize{{.Class}}(c *cli.Context) (*contract.{{.Class}}, error) { miningWaiter := ethutil.NewMiningWaiter(client, checkInterval, maxGasPrice) + blockCounter, err := blockcounter.CreateBlockCounter(client) + if err != nil { + return nil, fmt.Errorf( + "failed to create Ethereum blockcounter: [%v]", + err, + ) + } + address := common.HexToAddress(config.ContractAddresses["{{.Class}}"]) return contract.New{{.Class}}( @@ -234,6 +243,7 @@ func initialize{{.Class}}(c *cli.Context) (*contract.{{.Class}}, error) { client, ethutil.NewNonceManager(key.Address, client), miningWaiter, + blockCounter, &sync.Mutex{}, ) } diff --git a/tools/generators/ethereum/command_template_content.go b/tools/generators/ethereum/command_template_content.go index 9b6d021..f5e09ea 100644 --- a/tools/generators/ethereum/command_template_content.go +++ b/tools/generators/ethereum/command_template_content.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/keep-network/keep-common/pkg/chain/ethereum/blockcounter" "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" "github.com/keep-network/keep-common/pkg/cmd" @@ -229,6 +230,14 @@ func initialize{{.Class}}(c *cli.Context) (*contract.{{.Class}}, error) { miningWaiter := ethutil.NewMiningWaiter(client, checkInterval, maxGasPrice) + blockCounter, err := blockcounter.CreateBlockCounter(client) + if err != nil { + return nil, fmt.Errorf( + "failed to create Ethereum blockcounter: [%v]", + err, + ) + } + address := common.HexToAddress(config.ContractAddresses["{{.Class}}"]) return contract.New{{.Class}}( @@ -237,6 +246,7 @@ func initialize{{.Class}}(c *cli.Context) (*contract.{{.Class}}, error) { client, ethutil.NewNonceManager(key.Address, client), miningWaiter, + blockCounter, &sync.Mutex{}, ) } diff --git a/tools/generators/ethereum/contract.go.tmpl b/tools/generators/ethereum/contract.go.tmpl index 102bc78..549db2d 100644 --- a/tools/generators/ethereum/contract.go.tmpl +++ b/tools/generators/ethereum/contract.go.tmpl @@ -16,6 +16,7 @@ import ( "github.com/ipfs/go-log" + "github.com/keep-network/keep-common/pkg/chain/ethereum/blockcounter" "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" "github.com/keep-network/keep-common/pkg/subscription" ) @@ -25,21 +26,6 @@ import ( // included or excluded from logging at startup by name. var {{.ShortVar}}Logger = log.Logger("keep-contract-{{.Class}}") -const ( - // Maximum backoff time between event resubscription attempts. - {{.ShortVar}}SubscriptionBackoffMax = 2 * time.Minute - - // Threshold below which event resubscription emits an error to the logs. - // WS connection can be dropped at any moment and event resubscription will - // follow. However, if WS connection for event subscription is getting - // dropped too often, it may indicate something is wrong with Ethereum - // client. This constant defines the minimum lifetime of an event - // subscription required before the subscription failure happens and - // resubscription follows so that the resubscription does not emit an error - // to the logs alerting about potential problems with Ethereum client. - {{.ShortVar}}SubscriptionAlertThreshold = 15 * time.Minute -) - type {{.Class}} struct { contract *abi.{{.AbiClass}} contractAddress common.Address @@ -51,6 +37,7 @@ type {{.Class}} struct { errorResolver *ethutil.ErrorResolver nonceManager *ethutil.NonceManager miningWaiter *ethutil.MiningWaiter + blockCounter *blockcounter.EthereumBlockCounter transactionMutex *sync.Mutex } @@ -61,6 +48,7 @@ func New{{.Class}}( backend bind.ContractBackend, nonceManager *ethutil.NonceManager, miningWaiter *ethutil.MiningWaiter, + blockCounter *blockcounter.EthereumBlockCounter, transactionMutex *sync.Mutex, ) (*{{.Class}}, error) { callerOptions := &bind.CallOpts{ @@ -99,6 +87,7 @@ func New{{.Class}}( errorResolver: ethutil.NewErrorResolver(backend, &contractABI, &contractAddress), nonceManager: nonceManager, miningWaiter: miningWaiter, + blockCounter: blockCounter, transactionMutex: transactionMutex, }, nil } diff --git a/tools/generators/ethereum/contract_events.go.tmpl b/tools/generators/ethereum/contract_events.go.tmpl index c27494c..79d5574 100644 --- a/tools/generators/ethereum/contract_events.go.tmpl +++ b/tools/generators/ethereum/contract_events.go.tmpl @@ -2,66 +2,132 @@ {{- $logger := (print $contract.ShortVar "Logger") -}} {{- range $i, $event := .Events }} -type {{$contract.FullVar}}{{$event.CapsName}}Func func( - {{$event.ParamDeclarations -}} -) - -func ({{$contract.ShortVar}} *{{$contract.Class}}) Past{{$event.CapsName}}Events( - startBlock uint64, - endBlock *uint64, +func ({{$contract.ShortVar}} *{{$contract.Class}}) {{$event.CapsName}}( + opts *ethutil.SubscribeOpts, {{$event.IndexedFilterDeclarations -}} -) ([]*abi.{{$contract.AbiClass}}{{$event.CapsName}}, error){ - iterator, err := {{$contract.ShortVar}}.contract.Filter{{$event.CapsName}}( - &bind.FilterOpts{ - Start: startBlock, - End: endBlock, - }, - {{$event.IndexedFilters}} - ) - if err != nil { - return nil, fmt.Errorf( - "error retrieving past {{$event.CapsName}} events: [%v]", - err, - ) +) *{{$event.SubscriptionCapsName}} { + if opts == nil { + opts = new(ethutil.SubscribeOpts) + } + if opts.Tick == 0 { + opts.Tick = ethutil.DefaultSubscribeOptsTick + } + if opts.PastBlocks == 0 { + opts.PastBlocks = ethutil.DefaultSubscribeOptsPastBlocks } - events := make([]*abi.{{$contract.AbiClass}}{{$event.CapsName}}, 0) - - for iterator.Next() { - event := iterator.Event - events = append(events, event) + return &{{$event.SubscriptionCapsName}}{ + {{$contract.ShortVar}}, + opts, + {{$event.IndexedFilters}} } +} - return events, nil +type {{$event.SubscriptionCapsName}} struct { + contract *{{$contract.Class}} + opts *ethutil.SubscribeOpts + {{$event.IndexedFilterFields -}} } -func ({{$contract.ShortVar}} *{{$contract.Class}}) Watch{{$event.CapsName}}( - success {{$contract.FullVar}}{{$event.CapsName}}Func, - {{$event.IndexedFilterDeclarations -}} -) (subscription.EventSubscription) { - eventOccurred := make(chan *abi.{{$contract.AbiClass}}{{$event.CapsName}}) +type {{$contract.FullVar}}{{$event.CapsName}}Func func( + {{$event.ParamDeclarations -}} +) +func ({{$event.SubscriptionShortVar}} *{{$event.SubscriptionCapsName}}) OnEvent( + handler {{$contract.FullVar}}{{$event.CapsName}}Func, +) subscription.EventSubscription { + eventChan := make(chan *abi.{{$contract.AbiClass}}{{$event.CapsName}}) ctx, cancelCtx := context.WithCancel(context.Background()) - // TODO: Watch* function will soon accept channel as a parameter instead - // of the callback. This loop will be eliminated then. go func() { for { select { case <-ctx.Done(): return - case event := <-eventOccurred: - success( - {{$event.ParamExtractors}} + case event := <- eventChan: + handler( + {{$event.ParamExtractors}} ) } } }() + sub := {{$event.SubscriptionShortVar}}.Pipe(eventChan) + return subscription.NewEventSubscription(func() { + sub.Unsubscribe() + cancelCtx() + }) +} + +func ({{$event.SubscriptionShortVar}} *{{$event.SubscriptionCapsName}}) Pipe( + sink chan *abi.{{$contract.AbiClass}}{{$event.CapsName}}, +) subscription.EventSubscription { + ctx, cancelCtx := context.WithCancel(context.Background()) + go func() { + ticker := time.NewTicker({{$event.SubscriptionShortVar}}.opts.Tick) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + lastBlock, err := {{$event.SubscriptionShortVar}}.contract.blockCounter.CurrentBlock() + if err != nil { + {{$logger}}.Errorf( + "subscription failed to pull events: [%v]", + err, + ) + } + fromBlock := lastBlock-{{$event.SubscriptionShortVar}}.opts.PastBlocks + + {{$logger}}.Infof( + "subscription monitoring fetching past {{$event.CapsName}} events " + + "starting from block [%v]", + fromBlock, + ) + events, err := {{$event.SubscriptionShortVar}}.contract.Past{{$event.CapsName}}Events( + fromBlock, + nil, + {{$event.IndexedFilterExtractors}} + ) + if err != nil { + {{$logger}}.Errorf( + "subscription failed to pull events: [%v]", + err, + ) + continue + } + {{$logger}}.Infof( + "subscription monitoring fetched [%v] past {{$event.CapsName}} events", + len(events), + ) + + for _, event := range events { + sink <- event + } + } + } + }() + + sub := {{$event.SubscriptionShortVar}}.contract.watch{{$event.CapsName}}( + sink, + {{$event.IndexedFilterExtractors}} + ) + + return subscription.NewEventSubscription(func() { + sub.Unsubscribe() + cancelCtx() + }) +} + +func ({{$contract.ShortVar}} *{{$contract.Class}}) watch{{$event.CapsName}}( + sink chan *abi.{{$contract.AbiClass}}{{$event.CapsName}}, + {{$event.IndexedFilterDeclarations -}} +) event.Subscription { subscribeFn := func(ctx context.Context) (event.Subscription, error) { return {{$contract.ShortVar}}.contract.Watch{{$event.CapsName}}( &bind.WatchOpts{Context: ctx}, - eventOccurred, + sink, {{$event.IndexedFilters}} ) } @@ -84,18 +150,42 @@ func ({{$contract.ShortVar}} *{{$contract.Class}}) Watch{{$event.CapsName}}( ) } - sub := ethutil.WithResubscription( - {{$contract.ShortVar}}SubscriptionBackoffMax, + return ethutil.WithResubscription( + ethutil.SubscriptionBackoffMax, subscribeFn, - {{$contract.ShortVar}}SubscriptionAlertThreshold, + ethutil.SubscriptionAlertThreshold, thresholdViolatedFn, subscriptionFailedFn, ) +} - return subscription.NewEventSubscription(func() { - sub.Unsubscribe() - cancelCtx() - }) +func ({{$contract.ShortVar}} *{{$contract.Class}}) Past{{$event.CapsName}}Events( + startBlock uint64, + endBlock *uint64, + {{$event.IndexedFilterDeclarations -}} +) ([]*abi.{{$contract.AbiClass}}{{$event.CapsName}}, error){ + iterator, err := {{$contract.ShortVar}}.contract.Filter{{$event.CapsName}}( + &bind.FilterOpts{ + Start: startBlock, + End: endBlock, + }, + {{$event.IndexedFilters}} + ) + if err != nil { + return nil, fmt.Errorf( + "error retrieving past {{$event.CapsName}} events: [%v]", + err, + ) + } + + events := make([]*abi.{{$contract.AbiClass}}{{$event.CapsName}}, 0) + + for iterator.Next() { + event := iterator.Event + events = append(events, event) + } + + return events, nil } {{- end -}} \ No newline at end of file diff --git a/tools/generators/ethereum/contract_events_template_content.go b/tools/generators/ethereum/contract_events_template_content.go index 1c21c27..ec7c707 100644 --- a/tools/generators/ethereum/contract_events_template_content.go +++ b/tools/generators/ethereum/contract_events_template_content.go @@ -5,66 +5,132 @@ var contractEventsTemplateContent = `{{- $contract := . -}} {{- $logger := (print $contract.ShortVar "Logger") -}} {{- range $i, $event := .Events }} -type {{$contract.FullVar}}{{$event.CapsName}}Func func( - {{$event.ParamDeclarations -}} -) - -func ({{$contract.ShortVar}} *{{$contract.Class}}) Past{{$event.CapsName}}Events( - startBlock uint64, - endBlock *uint64, +func ({{$contract.ShortVar}} *{{$contract.Class}}) {{$event.CapsName}}( + opts *ethutil.SubscribeOpts, {{$event.IndexedFilterDeclarations -}} -) ([]*abi.{{$contract.AbiClass}}{{$event.CapsName}}, error){ - iterator, err := {{$contract.ShortVar}}.contract.Filter{{$event.CapsName}}( - &bind.FilterOpts{ - Start: startBlock, - End: endBlock, - }, - {{$event.IndexedFilters}} - ) - if err != nil { - return nil, fmt.Errorf( - "error retrieving past {{$event.CapsName}} events: [%v]", - err, - ) +) *{{$event.SubscriptionCapsName}} { + if opts == nil { + opts = new(ethutil.SubscribeOpts) + } + if opts.Tick == 0 { + opts.Tick = ethutil.DefaultSubscribeOptsTick + } + if opts.PastBlocks == 0 { + opts.PastBlocks = ethutil.DefaultSubscribeOptsPastBlocks } - events := make([]*abi.{{$contract.AbiClass}}{{$event.CapsName}}, 0) - - for iterator.Next() { - event := iterator.Event - events = append(events, event) + return &{{$event.SubscriptionCapsName}}{ + {{$contract.ShortVar}}, + opts, + {{$event.IndexedFilters}} } +} - return events, nil +type {{$event.SubscriptionCapsName}} struct { + contract *{{$contract.Class}} + opts *ethutil.SubscribeOpts + {{$event.IndexedFilterFields -}} } -func ({{$contract.ShortVar}} *{{$contract.Class}}) Watch{{$event.CapsName}}( - success {{$contract.FullVar}}{{$event.CapsName}}Func, - {{$event.IndexedFilterDeclarations -}} -) (subscription.EventSubscription) { - eventOccurred := make(chan *abi.{{$contract.AbiClass}}{{$event.CapsName}}) +type {{$contract.FullVar}}{{$event.CapsName}}Func func( + {{$event.ParamDeclarations -}} +) +func ({{$event.SubscriptionShortVar}} *{{$event.SubscriptionCapsName}}) OnEvent( + handler {{$contract.FullVar}}{{$event.CapsName}}Func, +) subscription.EventSubscription { + eventChan := make(chan *abi.{{$contract.AbiClass}}{{$event.CapsName}}) ctx, cancelCtx := context.WithCancel(context.Background()) - // TODO: Watch* function will soon accept channel as a parameter instead - // of the callback. This loop will be eliminated then. go func() { for { select { case <-ctx.Done(): return - case event := <-eventOccurred: - success( - {{$event.ParamExtractors}} + case event := <- eventChan: + handler( + {{$event.ParamExtractors}} ) } } }() + sub := {{$event.SubscriptionShortVar}}.Pipe(eventChan) + return subscription.NewEventSubscription(func() { + sub.Unsubscribe() + cancelCtx() + }) +} + +func ({{$event.SubscriptionShortVar}} *{{$event.SubscriptionCapsName}}) Pipe( + sink chan *abi.{{$contract.AbiClass}}{{$event.CapsName}}, +) subscription.EventSubscription { + ctx, cancelCtx := context.WithCancel(context.Background()) + go func() { + ticker := time.NewTicker({{$event.SubscriptionShortVar}}.opts.Tick) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + lastBlock, err := {{$event.SubscriptionShortVar}}.contract.blockCounter.CurrentBlock() + if err != nil { + {{$logger}}.Errorf( + "subscription failed to pull events: [%v]", + err, + ) + } + fromBlock := lastBlock-{{$event.SubscriptionShortVar}}.opts.PastBlocks + + {{$logger}}.Infof( + "subscription monitoring fetching past {{$event.CapsName}} events " + + "starting from block [%v]", + fromBlock, + ) + events, err := {{$event.SubscriptionShortVar}}.contract.Past{{$event.CapsName}}Events( + fromBlock, + nil, + {{$event.IndexedFilterExtractors}} + ) + if err != nil { + {{$logger}}.Errorf( + "subscription failed to pull events: [%v]", + err, + ) + continue + } + {{$logger}}.Infof( + "subscription monitoring fetched [%v] past {{$event.CapsName}} events", + len(events), + ) + + for _, event := range events { + sink <- event + } + } + } + }() + + sub := {{$event.SubscriptionShortVar}}.contract.watch{{$event.CapsName}}( + sink, + {{$event.IndexedFilterExtractors}} + ) + + return subscription.NewEventSubscription(func() { + sub.Unsubscribe() + cancelCtx() + }) +} + +func ({{$contract.ShortVar}} *{{$contract.Class}}) watch{{$event.CapsName}}( + sink chan *abi.{{$contract.AbiClass}}{{$event.CapsName}}, + {{$event.IndexedFilterDeclarations -}} +) event.Subscription { subscribeFn := func(ctx context.Context) (event.Subscription, error) { return {{$contract.ShortVar}}.contract.Watch{{$event.CapsName}}( &bind.WatchOpts{Context: ctx}, - eventOccurred, + sink, {{$event.IndexedFilters}} ) } @@ -87,18 +153,42 @@ func ({{$contract.ShortVar}} *{{$contract.Class}}) Watch{{$event.CapsName}}( ) } - sub := ethutil.WithResubscription( - {{$contract.ShortVar}}SubscriptionBackoffMax, + return ethutil.WithResubscription( + ethutil.SubscriptionBackoffMax, subscribeFn, - {{$contract.ShortVar}}SubscriptionAlertThreshold, + ethutil.SubscriptionAlertThreshold, thresholdViolatedFn, subscriptionFailedFn, ) +} - return subscription.NewEventSubscription(func() { - sub.Unsubscribe() - cancelCtx() - }) +func ({{$contract.ShortVar}} *{{$contract.Class}}) Past{{$event.CapsName}}Events( + startBlock uint64, + endBlock *uint64, + {{$event.IndexedFilterDeclarations -}} +) ([]*abi.{{$contract.AbiClass}}{{$event.CapsName}}, error){ + iterator, err := {{$contract.ShortVar}}.contract.Filter{{$event.CapsName}}( + &bind.FilterOpts{ + Start: startBlock, + End: endBlock, + }, + {{$event.IndexedFilters}} + ) + if err != nil { + return nil, fmt.Errorf( + "error retrieving past {{$event.CapsName}} events: [%v]", + err, + ) + } + + events := make([]*abi.{{$contract.AbiClass}}{{$event.CapsName}}, 0) + + for iterator.Next() { + event := iterator.Event + events = append(events, event) + } + + return events, nil } {{- end -}}` diff --git a/tools/generators/ethereum/contract_parsing.go b/tools/generators/ethereum/contract_parsing.go index 58230d9..ac95519 100644 --- a/tools/generators/ethereum/contract_parsing.go +++ b/tools/generators/ethereum/contract_parsing.go @@ -80,10 +80,15 @@ type returnInfo struct { type eventInfo struct { CapsName string LowerName string + SubscriptionCapsName string + ShortVar string + SubscriptionShortVar string IndexedFilters string ParamExtractors string ParamDeclarations string + IndexedFilterExtractors string IndexedFilterDeclarations string + IndexedFilterFields string } func buildContractInfo( @@ -242,9 +247,22 @@ func buildMethodInfo( func buildEventInfo(eventsByName map[string]abi.Event) []eventInfo { eventInfos := make([]eventInfo, 0, len(eventsByName)) for name, event := range eventsByName { + + capsName := uppercaseFirst(name) + lowerName := lowercaseFirst(name) + subscriptionCapsName := capsName + "Subscription" + + shortVar := strings.ToLower(string(shortVarRegexp.ReplaceAll( + []byte(name), + []byte("$1"), + ))) + subscriptionShortVar := shortVar + "s" + paramDeclarations := "" paramExtractors := "" + indexedFilterExtractors := "" indexedFilterDeclarations := "" + indexedFilterFields := "" indexedFilters := "" for _, param := range event.Inputs { upperParam := uppercaseFirst(param.Name) @@ -253,7 +271,9 @@ func buildEventInfo(eventsByName map[string]abi.Event) []eventInfo { paramDeclarations += fmt.Sprintf("%v %v,\n", upperParam, goType) paramExtractors += fmt.Sprintf("event.%v,\n", upperParam) if param.Indexed { + indexedFilterExtractors += fmt.Sprintf("%v.%vFilter,\n", subscriptionShortVar, param.Name) indexedFilterDeclarations += fmt.Sprintf("%vFilter []%v,\n", param.Name, goType) + indexedFilterFields += fmt.Sprintf("%vFilter []%v\n", param.Name, goType) indexedFilters += fmt.Sprintf("%vFilter,\n", param.Name) } } @@ -262,12 +282,17 @@ func buildEventInfo(eventsByName map[string]abi.Event) []eventInfo { paramExtractors += "event.Raw.BlockNumber,\n" eventInfos = append(eventInfos, eventInfo{ - uppercaseFirst(name), - lowercaseFirst(name), + capsName, + lowerName, + subscriptionCapsName, + shortVar, + subscriptionShortVar, indexedFilters, paramExtractors, paramDeclarations, + indexedFilterExtractors, indexedFilterDeclarations, + indexedFilterFields, }) } diff --git a/tools/generators/ethereum/contract_template_content.go b/tools/generators/ethereum/contract_template_content.go index e2085fb..34f016d 100644 --- a/tools/generators/ethereum/contract_template_content.go +++ b/tools/generators/ethereum/contract_template_content.go @@ -19,6 +19,7 @@ import ( "github.com/ipfs/go-log" + "github.com/keep-network/keep-common/pkg/chain/ethereum/blockcounter" "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" "github.com/keep-network/keep-common/pkg/subscription" ) @@ -28,21 +29,6 @@ import ( // included or excluded from logging at startup by name. var {{.ShortVar}}Logger = log.Logger("keep-contract-{{.Class}}") -const ( - // Maximum backoff time between event resubscription attempts. - {{.ShortVar}}SubscriptionBackoffMax = 2 * time.Minute - - // Threshold below which event resubscription emits an error to the logs. - // WS connection can be dropped at any moment and event resubscription will - // follow. However, if WS connection for event subscription is getting - // dropped too often, it may indicate something is wrong with Ethereum - // client. This constant defines the minimum lifetime of an event - // subscription required before the subscription failure happens and - // resubscription follows so that the resubscription does not emit an error - // to the logs alerting about potential problems with Ethereum client. - {{.ShortVar}}SubscriptionAlertThreshold = 15 * time.Minute -) - type {{.Class}} struct { contract *abi.{{.AbiClass}} contractAddress common.Address @@ -54,6 +40,7 @@ type {{.Class}} struct { errorResolver *ethutil.ErrorResolver nonceManager *ethutil.NonceManager miningWaiter *ethutil.MiningWaiter + blockCounter *blockcounter.EthereumBlockCounter transactionMutex *sync.Mutex } @@ -64,6 +51,7 @@ func New{{.Class}}( backend bind.ContractBackend, nonceManager *ethutil.NonceManager, miningWaiter *ethutil.MiningWaiter, + blockCounter *blockcounter.EthereumBlockCounter, transactionMutex *sync.Mutex, ) (*{{.Class}}, error) { callerOptions := &bind.CallOpts{ @@ -102,6 +90,7 @@ func New{{.Class}}( errorResolver: ethutil.NewErrorResolver(backend, &contractABI, &contractAddress), nonceManager: nonceManager, miningWaiter: miningWaiter, + blockCounter: blockCounter, transactionMutex: transactionMutex, }, nil }