From 05d142664d08ef5fe5b2e0c79b447dc589daab51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mohamed=20Sohail=20=E5=A4=A9=E6=98=8E?= Date: Mon, 7 Oct 2024 15:12:58 +0300 Subject: [PATCH] feat: handle contract creation (#43) * feat: add contract creation handler * fix: process contract creations * fix: redis keys name --- cmd/bootstrap/main.go | 4 +- cmd/service/router.go | 2 + internal/cache/redis.go | 4 +- internal/handler/contract_creation.go | 28 ++++++++++++ internal/processor/processor.go | 65 +++++++++++++++++++++++++-- pkg/router/router.go | 36 +++++++++++---- 6 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 internal/handler/contract_creation.go diff --git a/cmd/bootstrap/main.go b/cmd/bootstrap/main.go index f664537..974e5f4 100644 --- a/cmd/bootstrap/main.go +++ b/cmd/bootstrap/main.go @@ -230,12 +230,12 @@ func bootstrapCache() error { } for _, address := range ko.MustStrings("bootstrap.watchlist") { - if err := cache.Add(ctx, address); err != nil { + if err := cache.Add(ctx, ethutils.HexToAddress(address).Hex()); err != nil { return err } } for _, address := range ko.MustStrings("bootstrap.blacklist") { - if err := cache.Remove(ctx, address); err != nil { + if err := cache.Remove(ctx, ethutils.HexToAddress(address).Hex()); err != nil { return err } } diff --git a/cmd/service/router.go b/cmd/service/router.go index 80a64d5..b0d7599 100644 --- a/cmd/service/router.go +++ b/cmd/service/router.go @@ -11,6 +11,8 @@ func bootstrapEventRouter(cacheProvider cache.Cache, pubCB router.Callback) *rou handlerContainer := handler.New(cacheProvider) router := router.New(pubCB) + router.RegisterContractCreationHandler(handler.HandleContractCreation()) + router.RegisterLogRoute(w3.H("0x26162814817e23ec5035d6a2edc6c422da2da2119e27cfca6be65cc2dc55ca4c"), handler.HandleFaucetGiveLog()) router.RegisterLogRoute(w3.H("0xa226db3f664042183ee0281230bba26cbf7b5057e50aee7f25a175ff45ce4d7f"), handler.HandleIndexAddLog(handlerContainer)) router.RegisterLogRoute(w3.H("0x24a12366c02e13fe4a9e03d86a8952e85bb74a456c16e4a18b6d8295700b74bb"), handler.HandleIndexRemoveLog(handlerContainer)) diff --git a/internal/cache/redis.go b/internal/cache/redis.go index afdf09d..5424162 100644 --- a/internal/cache/redis.go +++ b/internal/cache/redis.go @@ -40,8 +40,8 @@ func (c *redisCache) Remove(ctx context.Context, key string) error { return c.client.Do(ctx, cmd).Error() } -func (c *redisCache) Exists(ctx context.Context, key ...string) (bool, error) { - cmd := c.client.B().Exists().Key(key...).Build() +func (c *redisCache) Exists(ctx context.Context, keys ...string) (bool, error) { + cmd := c.client.B().Exists().Key(keys...).Build() res, err := c.client.Do(ctx, cmd).AsBool() if err != nil { return false, err diff --git a/internal/handler/contract_creation.go b/internal/handler/contract_creation.go new file mode 100644 index 0000000..0c44695 --- /dev/null +++ b/internal/handler/contract_creation.go @@ -0,0 +1,28 @@ +package handler + +import ( + "context" + + "github.com/grassrootseconomics/eth-tracker/pkg/event" + "github.com/grassrootseconomics/eth-tracker/pkg/router" +) + +const contractCreationEventName = "CONTRACT_CREATION" + +func HandleContractCreation() router.ContractCreationHandlerFunc { + return func(ctx context.Context, ccp router.ContractCreationPayload, c router.Callback) error { + contractCreationEvent := event.Event{ + Block: ccp.Block, + ContractAddress: ccp.ContractAddress, + Success: ccp.Success, + Timestamp: ccp.Timestamp, + TxHash: ccp.TxHash, + TxType: contractCreationEventName, + Payload: map[string]any{ + "from": ccp.From, + }, + } + + return c(ctx, contractCreationEvent) + } +} diff --git a/internal/processor/processor.go b/internal/processor/processor.go index 14376c8..271a108 100644 --- a/internal/processor/processor.go +++ b/internal/processor/processor.go @@ -54,7 +54,7 @@ func (p *Processor) ProcessBlock(ctx context.Context, blockNumber uint64) error } for _, receipt := range receipts { - if receipt.Status > 0 { + if receipt.Status == 1 { for _, log := range receipt.Logs { exists, err := p.cache.Exists(ctx, log.Address.Hex()) if err != nil { @@ -72,13 +72,72 @@ func (p *Processor) ProcessBlock(ctx context.Context, blockNumber uint64) error } } } - } else { + + if receipt.ContractAddress != (common.Address{}) { + tx, err := p.chain.GetTransaction(ctx, receipt.TxHash) + if err != nil && !errors.Is(err, context.Canceled) { + return fmt.Errorf("get transaction error: tx %s: %v", receipt.TxHash.Hex(), err) + } + + from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx) + if err != nil { + return fmt.Errorf("transaction decode error: tx %s: %v", receipt.TxHash.Hex(), err) + } + + exists, err := p.cache.Exists(ctx, from.Hex()) + if err != nil { + return err + } + if exists { + if err := p.router.ProcessContractCreation( + ctx, + router.ContractCreationPayload{ + From: from.Hex(), + Block: blockNumber, + ContractAddress: receipt.ContractAddress.Hex(), + Timestamp: block.Time(), + TxHash: receipt.TxHash.Hex(), + Success: true, + }, + ); err != nil && !errors.Is(err, context.Canceled) { + return fmt.Errorf("route success contract creation error: tx %s: %v", receipt.TxHash.Hex(), err) + } + } + } + } + + if receipt.Status == 0 { tx, err := p.chain.GetTransaction(ctx, receipt.TxHash) if err != nil && !errors.Is(err, context.Canceled) { return fmt.Errorf("get transaction error: tx %s: %v", receipt.TxHash.Hex(), err) } + if tx.To() == nil { + from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx) + if err != nil { + return fmt.Errorf("transaction decode error: tx %s: %v", receipt.TxHash.Hex(), err) + } - if tx.To() != nil { + exists, err := p.cache.Exists(ctx, from.Hex()) + if err != nil { + return err + } + + if exists { + if err := p.router.ProcessContractCreation( + ctx, + router.ContractCreationPayload{ + From: from.Hex(), + Block: blockNumber, + ContractAddress: receipt.ContractAddress.Hex(), + Timestamp: block.Time(), + TxHash: receipt.TxHash.Hex(), + Success: false, + }, + ); err != nil && !errors.Is(err, context.Canceled) { + return fmt.Errorf("route reverted contract creation error: tx %s: %v", receipt.TxHash.Hex(), err) + } + } + } else { exists, err := p.cache.Exists(ctx, tx.To().Hex()) if err != nil { return err diff --git a/pkg/router/router.go b/pkg/router/router.go index 6f5e940..61696bc 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -25,8 +25,18 @@ type ( TxHash string } - LogHandlerFunc func(context.Context, LogPayload, Callback) error - InputDataHandlerFunc func(context.Context, InputDataPayload, Callback) error + ContractCreationPayload struct { + From string + ContractAddress string + Block uint64 + Timestamp uint64 + TxHash string + Success bool + } + + LogHandlerFunc func(context.Context, LogPayload, Callback) error + InputDataHandlerFunc func(context.Context, InputDataPayload, Callback) error + ContractCreationHandlerFunc func(context.Context, ContractCreationPayload, Callback) error LogRouteEntry struct { Signature common.Hash @@ -39,17 +49,19 @@ type ( } Router struct { - callbackFn Callback - logHandlers map[common.Hash]LogRouteEntry - inputDataHandlers map[string]InputDataEntry + callbackFn Callback + logHandlers map[common.Hash]LogRouteEntry + inputDataHandlers map[string]InputDataEntry + contractCreationHandler ContractCreationHandlerFunc } ) func New(callbackFn Callback) *Router { return &Router{ - callbackFn: callbackFn, - logHandlers: make(map[common.Hash]LogRouteEntry), - inputDataHandlers: make(map[string]InputDataEntry), + callbackFn: callbackFn, + logHandlers: make(map[common.Hash]LogRouteEntry), + inputDataHandlers: make(map[string]InputDataEntry), + contractCreationHandler: nil, } } @@ -67,6 +79,10 @@ func (r *Router) RegisterInputDataRoute(signature string, handlerFunc InputDataH } } +func (r *Router) RegisterContractCreationHandler(handlerFunc ContractCreationHandlerFunc) { + r.contractCreationHandler = handlerFunc +} + func (r *Router) ProcessLog(ctx context.Context, payload LogPayload) error { handler, ok := r.logHandlers[payload.Log.Topics[0]] if ok { @@ -88,3 +104,7 @@ func (r *Router) ProcessInputData(ctx context.Context, payload InputDataPayload) return nil } + +func (r *Router) ProcessContractCreation(ctx context.Context, payload ContractCreationPayload) error { + return r.contractCreationHandler(ctx, payload, r.callbackFn) +}