diff --git a/config.toml b/config.toml index 9e1f390..c68e009 100644 --- a/config.toml +++ b/config.toml @@ -16,3 +16,4 @@ registries = [ "0xd1FB944748aca327a1ba036B082993D9dd9Bfa0C", "0x0cc9f4fff962def35bb34a53691180b13e653030", ] +blacklist = ["0x765DE816845861e75A25fCA122bb6898B8B1282a"] diff --git a/internal/cache/bootstrap.go b/internal/cache/bootstrap.go index 69f81ca..70df188 100644 --- a/internal/cache/bootstrap.go +++ b/internal/cache/bootstrap.go @@ -10,11 +10,15 @@ import ( "github.com/grassrootseconomics/w3-celo/module/eth" ) -func bootstrapAllGESmartContracts(ctx context.Context, registries []string, chain *chain.Chain, cache Cache) error { +func bootstrapAllGESmartContracts(ctx context.Context, registries []string, chain *chain.Chain, cache Cache) (WatchableIndex, error) { + var ( + watchableIndex = make(WatchableIndex) + ) + for _, registry := range registries { registryMap, err := chain.Provider.RegistryMap(ctx, w3.A(registry)) if err != nil { - return nil + return nil, nil } for _, v := range registryMap { @@ -24,18 +28,20 @@ func bootstrapAllGESmartContracts(ctx context.Context, registries []string, chai if registryMap[celoutils.TokenIndex] != common.ZeroAddress { tokens, err := chain.GetAllTokensFromTokenIndex(ctx, registryMap[celoutils.TokenIndex]) if err != nil { - return err + return nil, err } for _, token := range tokens { cache.Add(token.Hex()) } + + watchableIndex[registryMap[celoutils.TokenIndex].Hex()] = true } if registryMap[celoutils.PoolIndex] != common.ZeroAddress { pools, err := chain.GetAllTokensFromTokenIndex(ctx, registryMap[celoutils.PoolIndex]) if err != nil { - return err + return nil, err } for _, pool := range pools { @@ -51,21 +57,24 @@ func bootstrapAllGESmartContracts(ctx context.Context, registries []string, chai eth.CallFunc(pool, quoterGetter).Returns(&priceQuoter), ) if err != nil { - return err + return nil, err } cache.Add(priceQuoter.Hex()) poolTokens, err := chain.GetAllTokensFromTokenIndex(ctx, poolTokenRegistry) if err != nil { - return err + return nil, err } for _, token := range poolTokens { cache.Add(token.Hex()) } + watchableIndex[poolTokenRegistry.Hex()] = true } + + watchableIndex[registryMap[celoutils.PoolIndex].Hex()] = true } } - return nil + return watchableIndex, nil } diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 404bb08..cef41e7 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -12,7 +12,10 @@ type ( Cache interface { Purge() error Exists(string) bool - Add(string) bool + Add(string) + Remove(string) + SetWatchableIndex(WatchableIndex) + ISWatchAbleIndex(string) bool Size() int } @@ -21,7 +24,10 @@ type ( Chain *chain.Chain CacheType string Registries []string + Blacklist []string } + + WatchableIndex map[string]bool ) var ( @@ -41,14 +47,21 @@ func New(o CacheOpts) (Cache, error) { cache = NewMapCache() } - if err := bootstrapAllGESmartContracts( + watchableIndex, err := bootstrapAllGESmartContracts( context.Background(), o.Registries, o.Chain, cache, - ); err != nil { + ) + if err != nil { return nil, err } + cache.SetWatchableIndex(watchableIndex) + + for _, address := range o.Blacklist { + cache.Remove(address) + } + o.Logg.Debug("cache bootstrap complete", "cached_addresses", cache.Size()) return cache, nil diff --git a/internal/cache/map.go b/internal/cache/map.go index 5613c13..432439d 100644 --- a/internal/cache/map.go +++ b/internal/cache/map.go @@ -8,8 +8,9 @@ import ( type ( MapCache struct { - mapCache *xsync.Map - logg *slog.Logger + mapCache *xsync.Map + logg *slog.Logger + watchableIndex WatchableIndex } ) @@ -29,11 +30,23 @@ func (c *MapCache) Exists(key string) bool { return ok } -func (c *MapCache) Add(key string) bool { +func (c *MapCache) Add(key string) { c.mapCache.Store(key, nil) - return true +} + +func (c *MapCache) Remove(key string) { + c.mapCache.Delete(key) } func (c *MapCache) Size() int { return c.mapCache.Size() } + +func (c *MapCache) SetWatchableIndex(watchableIndex WatchableIndex) { + c.watchableIndex = watchableIndex +} + +func (c *MapCache) ISWatchAbleIndex(key string) bool { + _, ok := c.watchableIndex[key] + return ok +} diff --git a/internal/handler/index_add.go b/internal/handler/index_add.go new file mode 100644 index 0000000..df8707f --- /dev/null +++ b/internal/handler/index_add.go @@ -0,0 +1,114 @@ +package handler + +import ( + "context" + + "github.com/celo-org/celo-blockchain/common" + "github.com/grassrootseconomics/celo-tracker/internal/cache" + "github.com/grassrootseconomics/celo-tracker/internal/emitter" + "github.com/grassrootseconomics/w3-celo" +) + +type ( + IndexAddHandler struct { + topicHash common.Hash + event *w3.Event + cache cache.Cache + } +) + +var ( + indexAddTopicHash = w3.H("0xa226db3f664042183ee0281230bba26cbf7b5057e50aee7f25a175ff45ce4d7f") + indexAddEvent = w3.MustNewEvent("AddressAdded(address _token)") + indexAddSig = w3.MustNewFunc("add(address)", "bool") + indexRegisterSig = w3.MustNewFunc("register(address)", "bool") +) + +func (h *IndexAddHandler) HandleLog(ctx context.Context, msg LogMessage, emitter emitter.Emitter) error { + if msg.Log.Topics[0] == indexAddTopicHash { + var ( + address common.Address + ) + + if err := indexAddEvent.DecodeArgs(msg.Log, &address); err != nil { + return err + } + + indexAddEvent := Event{ + Block: msg.Log.BlockNumber, + ContractAddress: msg.Log.Address.Hex(), + Success: true, + Timestamp: msg.BlockTime, + TxHash: msg.Log.TxHash.Hex(), + TxType: "INDEX_ADD", + Payload: map[string]any{ + "address": address.Hex(), + }, + } + + if h.cache.ISWatchAbleIndex(address.Hex()) { + h.cache.Add(address.Hex()) + } + + return emitter.Emit(ctx, indexAddEvent) + } + + return nil +} + +func (h *IndexAddHandler) HandleRevert(ctx context.Context, msg RevertMessage, emitter emitter.Emitter) error { + if len(msg.InputData) < 8 { + return nil + } + + switch msg.InputData[:8] { + case "0a3b0a4f": + var ( + address common.Address + ) + + if err := indexAddSig.DecodeArgs(w3.B(msg.InputData), &address); err != nil { + return err + } + + indexAddEvent := Event{ + Block: msg.Block, + ContractAddress: msg.ContractAddress, + Success: false, + Timestamp: msg.Timestamp, + TxHash: msg.TxHash, + TxType: "INDEX_ADD", + Payload: map[string]any{ + "revertReason": msg.RevertReason, + "address": address.Hex(), + }, + } + + return emitter.Emit(ctx, indexAddEvent) + case "4420e486": + var ( + address common.Address + ) + + if err := indexRegisterSig.DecodeArgs(w3.B(msg.InputData), &address); err != nil { + return err + } + + indexAddEvent := Event{ + Block: msg.Block, + ContractAddress: msg.ContractAddress, + Success: false, + Timestamp: msg.Timestamp, + TxHash: msg.TxHash, + TxType: "INDEX_ADD", + Payload: map[string]any{ + "revertReason": msg.RevertReason, + "address": address.Hex(), + }, + } + + return emitter.Emit(ctx, indexAddEvent) + } + + return nil +} diff --git a/internal/handler/index_remove.go b/internal/handler/index_remove.go new file mode 100644 index 0000000..2e5d173 --- /dev/null +++ b/internal/handler/index_remove.go @@ -0,0 +1,90 @@ +package handler + +import ( + "context" + + "github.com/celo-org/celo-blockchain/common" + "github.com/grassrootseconomics/celo-tracker/internal/cache" + "github.com/grassrootseconomics/celo-tracker/internal/emitter" + "github.com/grassrootseconomics/w3-celo" +) + +type ( + IndexRemoveHandler struct { + topicHash common.Hash + event *w3.Event + cache cache.Cache + } +) + +var ( + indexRemoveTopicHash = w3.H("0x24a12366c02e13fe4a9e03d86a8952e85bb74a456c16e4a18b6d8295700b74bb") + indexRemoveEvent = w3.MustNewEvent("AddressRemoved(address _token)") + indexRemoveSig = w3.MustNewFunc("remove(address)", "bool") +) + +func (h *IndexRemoveHandler) HandleLog(ctx context.Context, msg LogMessage, emitter emitter.Emitter) error { + if msg.Log.Topics[0] == indexRemoveTopicHash { + var ( + address common.Address + ) + + if err := indexRemoveEvent.DecodeArgs(msg.Log, &address); err != nil { + return err + } + + indexRemoveEvent := Event{ + Block: msg.Log.BlockNumber, + ContractAddress: msg.Log.Address.Hex(), + Success: true, + Timestamp: msg.BlockTime, + TxHash: msg.Log.TxHash.Hex(), + TxType: "INDEX_REMOVE", + Payload: map[string]any{ + "address": address.Hex(), + }, + } + + if h.cache.ISWatchAbleIndex(address.Hex()) { + h.cache.Remove(address.Hex()) + } + + return emitter.Emit(ctx, indexRemoveEvent) + } + + return nil +} + +func (h *IndexRemoveHandler) HandleRevert(ctx context.Context, msg RevertMessage, emitter emitter.Emitter) error { + if len(msg.InputData) < 8 { + return nil + } + + switch msg.InputData[:8] { + case "29092d0e": + var ( + address common.Address + ) + + if err := indexRemoveSig.DecodeArgs(w3.B(msg.InputData), &address); err != nil { + return err + } + + indexRemoveEvent := Event{ + Block: msg.Block, + ContractAddress: msg.ContractAddress, + Success: false, + Timestamp: msg.Timestamp, + TxHash: msg.TxHash, + TxType: "INDEX_REMOVE", + Payload: map[string]any{ + "revertReason": msg.RevertReason, + "address": address.Hex(), + }, + } + + return emitter.Emit(ctx, indexRemoveEvent) + } + + return nil +} diff --git a/internal/handler/quoter_price.go b/internal/handler/quoter_price.go new file mode 100644 index 0000000..6e07228 --- /dev/null +++ b/internal/handler/quoter_price.go @@ -0,0 +1,89 @@ +package handler + +import ( + "context" + "math/big" + + "github.com/celo-org/celo-blockchain/common" + "github.com/grassrootseconomics/celo-tracker/internal/emitter" + "github.com/grassrootseconomics/w3-celo" +) + +type ( + QuoterPriceHandler struct { + topicHash common.Hash + event *w3.Event + } +) + +var ( + quoterPriceTopicHash = w3.H("0xdb9ce1a76955721ca61ac50cd1b87f9ab8620325c8619a62192c2dc7871d56b1") + quoterPriceEvent = w3.MustNewEvent("PriceIndexUpdated(address _tokenAddress, uint256 _exchangeRate)") + quoterPriceToSig = w3.MustNewFunc("setPriceIndexValue(address, uint256)", "uint256") +) + +func (h *QuoterPriceHandler) HandleLog(ctx context.Context, msg LogMessage, emitter emitter.Emitter) error { + if msg.Log.Topics[0] == quoterPriceTopicHash { + var ( + token common.Address + exchangeRate big.Int + ) + + if err := quoterPriceEvent.DecodeArgs(msg.Log, &token, &exchangeRate); err != nil { + return err + } + + quoterPriceEvent := Event{ + Block: msg.Log.BlockNumber, + ContractAddress: msg.Log.Address.Hex(), + Success: true, + Timestamp: msg.BlockTime, + TxHash: msg.Log.TxHash.Hex(), + TxType: "QUOTER_PRICE_INDEX_UPDATED", + Payload: map[string]any{ + "token": token.Hex(), + "exchangeRate": exchangeRate.String(), + }, + } + + return emitter.Emit(ctx, quoterPriceEvent) + } + + return nil +} + +func (h *QuoterPriceHandler) HandleRevert(ctx context.Context, msg RevertMessage, emitter emitter.Emitter) error { + if len(msg.InputData) < 8 { + return nil + } + + switch msg.InputData[:8] { + case "ebc59dff": + var ( + token common.Address + exchangeRate big.Int + ) + + if err := quoterPriceToSig.DecodeArgs(w3.B(msg.InputData), &token, &exchangeRate); err != nil { + return err + } + + quoterPriceEvent := Event{ + Block: msg.Block, + ContractAddress: msg.ContractAddress, + Success: false, + Timestamp: msg.Timestamp, + TxHash: msg.TxHash, + TxType: "QUOTER_PRICE_INDEX_UPDATED", + Payload: map[string]any{ + "revertReason": msg.RevertReason, + "token": token.Hex(), + "exchangeRate": exchangeRate.String(), + }, + } + + return emitter.Emit(ctx, quoterPriceEvent) + } + + return nil +}