diff --git a/cmd/index.go b/cmd/index.go index d82403b..febb84c 100644 --- a/cmd/index.go +++ b/cmd/index.go @@ -100,7 +100,6 @@ func setupIndexer() *Indexer { // Setup chain specific stuff core.SetupAddressRegex(indexer.cfg.Lens.AccountPrefix + "(valoper)?1[a-z0-9]{38}") core.SetupAddressPrefix(indexer.cfg.Lens.AccountPrefix) - core.ChainSpecificMessageTypeHandlerBootstrap(indexer.cfg.Lens.ChainID) core.ChainSpecificBeginBlockerEventTypeHandlerBootstrap(indexer.cfg.Lens.ChainID) core.ChainSpecificEndBlockerEventTypeHandlerBootstrap(indexer.cfg.Lens.ChainID) core.ChainSpecificEpochIdentifierEventTypeHandlersBootstrap(indexer.cfg.Lens.ChainID) @@ -121,6 +120,8 @@ func setupIndexer() *Indexer { tasks.DoChainSpecificUpsertDenoms(indexer.db, indexer.cfg.Lens.ChainID, indexer.cfg.Base.RequestRetryAttempts, indexer.cfg.Base.RequestRetryMaxWait) indexer.cl = config.GetLensClient(indexer.cfg.Lens) + core.ChainSpecificMessageTypeHandlerBootstrap(indexer.cfg.Lens.ChainID, indexer.cl) + // Depending on the app configuration, wait for the chain to catch up chainCatchingUp, err := rpc.IsCatchingUp(indexer.cl) for indexer.cfg.Base.WaitForChain && chainCatchingUp && err == nil { diff --git a/core/tx.go b/core/tx.go index a00f950..ccee8d2 100644 --- a/core/tx.go +++ b/core/tx.go @@ -21,6 +21,7 @@ import ( "github.com/DefiantLabs/cosmos-tax-cli/cosmos/modules/staking" txtypes "github.com/DefiantLabs/cosmos-tax-cli/cosmos/modules/tx" "github.com/DefiantLabs/cosmos-tax-cli/cosmos/modules/vesting" + "github.com/DefiantLabs/cosmos-tax-cli/cosmwasm" "github.com/DefiantLabs/cosmos-tax-cli/cosmwasm/modules/wasm" dbTypes "github.com/DefiantLabs/cosmos-tax-cli/db" "github.com/DefiantLabs/cosmos-tax-cli/osmosis" @@ -159,7 +160,6 @@ var messageTypeIgnorer = map[string]interface{}{ /////// Possible Taxable Events, future work /////// //////////////////////////////////////////////////// // CosmWasm - wasm.MsgExecuteContract: nil, wasm.MsgInstantiateContract: nil, wasm.MsgInstantiateContract2: nil, wasm.MsgStoreCode: nil, @@ -179,11 +179,13 @@ var messageTypeIgnorer = map[string]interface{}{ // Merge the chain specific message type handlers into the core message type handler map. // Chain specific handlers will be registered BEFORE any generic handlers. -func ChainSpecificMessageTypeHandlerBootstrap(chainID string) { +func ChainSpecificMessageTypeHandlerBootstrap(chainID string, lensClient *client.ChainClient) { + var customContractAddressHandlers []wasm.ContractExecutionMessageHandler var chainSpecificMessageTpeHandler map[string][]func() txtypes.CosmosMessage if chainID == osmosis.ChainID { chainSpecificMessageTpeHandler = osmosis.MessageTypeHandler } + for key, value := range chainSpecificMessageTpeHandler { if list, ok := messageTypeHandler[key]; ok { messageTypeHandler[key] = append(value, list...) @@ -191,6 +193,19 @@ func ChainSpecificMessageTypeHandlerBootstrap(chainID string) { messageTypeHandler[key] = value } } + + cosmWasmHandlers, err := cosmwasm.GetCosmWasmMessageTypeHandlers(customContractAddressHandlers, lensClient) + if err != nil { + config.Log.Fatal("Error getting CosmWasm message type handlers.", err) + } + + for key, value := range cosmWasmHandlers { + if list, ok := messageTypeHandler[key]; ok { + messageTypeHandler[key] = append(value, list...) + } else { + messageTypeHandler[key] = value + } + } } // ParseCosmosMessageJSON - Parse a SINGLE Cosmos Message into the appropriate type. diff --git a/cosmwasm/handlers.go b/cosmwasm/handlers.go new file mode 100644 index 0000000..f180bbf --- /dev/null +++ b/cosmwasm/handlers.go @@ -0,0 +1,52 @@ +package cosmwasm + +import ( + txTypes "github.com/DefiantLabs/cosmos-tax-cli/cosmos/modules/tx" + "github.com/DefiantLabs/cosmos-tax-cli/cosmwasm/modules/wasm" + "github.com/DefiantLabs/cosmos-tax-cli/rpc" + "github.com/DefiantLabs/lens/client" +) + +var defaultMessageTypeHandler = map[string][]func() txTypes.CosmosMessage{} + +var contractAddressRegistry = map[string]wasm.ContractExecutionMessageHandler{} + +func GetCosmWasmMessageTypeHandlers(customContractAddressHandlers []wasm.ContractExecutionMessageHandler, lensClient *client.ChainClient) (map[string][]func() txTypes.CosmosMessage, error) { + msgExecuteContractHandlers, err := configureMsgExecuteContractHandler(customContractAddressHandlers, lensClient) + if err != nil { + return nil, err + } + + defaultMessageTypeHandler[wasm.MsgExecuteContract] = msgExecuteContractHandlers + + return defaultMessageTypeHandler, nil +} + +// Configures a handler wrapper that will allow using registry values to find custom message handlers +func configureMsgExecuteContractHandler(customContractAddressHandlers []wasm.ContractExecutionMessageHandler, lensClient *client.ChainClient) ([]func() txTypes.CosmosMessage, error) { + for _, handler := range customContractAddressHandlers { + if castHandler, ok := handler.(wasm.ContractExecutionMessageHandlerByContractAddress); ok { + contractAddressRegistry[castHandler.ContractAddress()] = handler + } else if castHandler, ok := handler.(wasm.ContractExecutionMessageHandlerByCodeID); ok { + // Query for code ID contract addresses using wasm querying + resp, err := rpc.GetContractsByCodeIDAtHeight(lensClient, castHandler.CodeID(), 0) + if err != nil { + return nil, err + } + + for _, contractAddress := range resp.Contracts { + contractAddressRegistry[contractAddress] = handler + } + } + } + + configuredExecuteContractWrapper := wasm.WrapperMsgExecuteContract{ + ContractAddressRegistry: contractAddressRegistry, + } + + return []func() txTypes.CosmosMessage{ + func() txTypes.CosmosMessage { + return configuredExecuteContractWrapper + }, + }, nil +} diff --git a/cosmwasm/modules/wasm/types.go b/cosmwasm/modules/wasm/types.go index 64d5b1f..bdc225d 100644 --- a/cosmwasm/modules/wasm/types.go +++ b/cosmwasm/modules/wasm/types.go @@ -1,5 +1,14 @@ package wasm +import ( + "fmt" + + wasmTypes "github.com/CosmWasm/wasmd/x/wasm/types" + parsingTypes "github.com/DefiantLabs/cosmos-tax-cli/cosmos/modules" + txTypes "github.com/DefiantLabs/cosmos-tax-cli/cosmos/modules/tx" + sdk "github.com/cosmos/cosmos-sdk/types" +) + const ( MsgExecuteContract = "/cosmwasm.wasm.v1.MsgExecuteContract" MsgInstantiateContract = "/cosmwasm.wasm.v1.MsgInstantiateContract" @@ -18,3 +27,60 @@ const ( MsgAddCodeUploadParamsAddresses = "/cosmwasm.wasm.v1.MsgAddCodeUploadParamsAddresses" MsgStoreAndMigrateContract = "/cosmwasm.wasm.v1.MsgStoreAndMigrateContract" ) + +type ContractExecutionMessageHandler interface { + txTypes.CosmosMessage + ContractFriendlyName() string + TopLevelFieldIdentifiers() []string + TopLevelIdentifierType() any + CosmosMessageType() txTypes.CosmosMessage +} + +type ContractExecutionMessageHandlerByCodeID interface { + ContractExecutionMessageHandler + CodeID() uint64 +} + +type ContractExecutionMessageHandlerByContractAddress interface { + ContractExecutionMessageHandler + ContractAddress() string +} + +type WrapperMsgExecuteContract struct { + txTypes.Message + CosmosMsgExecuteContract *wasmTypes.MsgExecuteContract + ContractAddressRegistry map[string]ContractExecutionMessageHandler + CurrentHandler ContractExecutionMessageHandler + ContractAddress string +} + +func (w WrapperMsgExecuteContract) HandleMsg(typeURL string, msg sdk.Msg, log *txTypes.LogMessage) error { + w.Type = typeURL + w.CosmosMsgExecuteContract = msg.(*wasmTypes.MsgExecuteContract) + + if handler, ok := w.ContractAddressRegistry[w.CosmosMsgExecuteContract.Contract]; ok { + w.CurrentHandler = handler + return w.CurrentHandler.HandleMsg(typeURL, msg, log) + } + + return nil +} + +func (w WrapperMsgExecuteContract) ParseRelevantData() []parsingTypes.MessageRelevantInformation { + if w.CurrentHandler != nil { + return w.CurrentHandler.ParseRelevantData() + } + + return nil +} + +func (w WrapperMsgExecuteContract) GetType() string { + return MsgExecuteContract +} + +func (w WrapperMsgExecuteContract) String() string { + if w.CurrentHandler != nil { + return w.CurrentHandler.String() + } + return fmt.Sprintf("MsgExecuteContract: No handler found for contract address %s", w.ContractAddress) +} diff --git a/go.mod b/go.mod index 530c397..ab7df0c 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ toolchain go1.21.11 require ( github.com/BurntSushi/toml v1.3.2 - // Our lens library is pinned to this specific SHA and branch: 7732945f81598e4d6aff47e0d4ca81b0ec26d8b3, https://github.com/DefiantLabs/lens/tree/v0.0.21-dl - github.com/DefiantLabs/lens v0.3.1-0.20240626071018-7732945f8159 + // Our lens library is pinned to this specific SHA and branch: 2ff2d8138af66fb3f3e3429b9837db1e720ed39f, https://github.com/DefiantLabs/lens/tree/v0.0.22-dl + github.com/DefiantLabs/lens v0.3.1-0.20240627235205-2ff2d8138af6 github.com/cosmos/cosmos-sdk v0.47.8 github.com/gin-gonic/gin v1.9.1 github.com/go-co-op/gocron v1.13.0 @@ -26,6 +26,7 @@ require ( require ( cosmossdk.io/math v1.3.0 + github.com/CosmWasm/wasmd v0.45.1-0.20231128163306-4b9b61faeaa3 github.com/cometbft/cometbft v0.38.0 github.com/cosmos/ibc-go/v7 v7.4.1 github.com/go-git/go-git/v5 v5.11.0 @@ -53,7 +54,6 @@ require ( dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/keyring v1.2.1 // indirect - github.com/CosmWasm/wasmd v0.45.1-0.20231128163306-4b9b61faeaa3 // indirect github.com/CosmWasm/wasmvm v1.5.2 // indirect github.com/DataDog/zstd v1.5.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect diff --git a/go.sum b/go.sum index e102c34..00acb3e 100644 --- a/go.sum +++ b/go.sum @@ -558,8 +558,8 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.0 h1:+K/VEwIAaPcHiMtQvpLD4lqW7f0Gk3xdYZmI1hD+CXo= github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/DefiantLabs/lens v0.3.1-0.20240626071018-7732945f8159 h1:GB/lSc4/UT7M64scdyh0lrwyRRi3UyCto9RvwJekVKI= -github.com/DefiantLabs/lens v0.3.1-0.20240626071018-7732945f8159/go.mod h1:nTPf+NQkhPH6NcBY5p0lpdb14WPiZOeMZDDkq1IWl30= +github.com/DefiantLabs/lens v0.3.1-0.20240627235205-2ff2d8138af6 h1:n1wKVmFX2hUfL3B+PDbPrEbYAD47hNdNUQOnyPn32TE= +github.com/DefiantLabs/lens v0.3.1-0.20240627235205-2ff2d8138af6/go.mod h1:nTPf+NQkhPH6NcBY5p0lpdb14WPiZOeMZDDkq1IWl30= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= diff --git a/rpc/requests.go b/rpc/requests.go index 799d607..9720f42 100644 --- a/rpc/requests.go +++ b/rpc/requests.go @@ -8,6 +8,7 @@ import ( osmosisProtorev "github.com/osmosis-labs/osmosis/v25/x/protorev/types" osmosisEpochs "github.com/osmosis-labs/osmosis/x/epochs/types" + wasmTypes "github.com/CosmWasm/wasmd/x/wasm/types" "github.com/DefiantLabs/cosmos-tax-cli/config" lensClient "github.com/DefiantLabs/lens/client" lensQuery "github.com/DefiantLabs/lens/client/query" @@ -193,3 +194,14 @@ func GetProtorevDeveloperAccount(cl *lensClient.ChainClient) (*osmosisProtorev.Q resp, err := query.ProtorevDeveloperAccount() return resp, err } + +func GetContractsByCodeIDAtHeight(cl *lensClient.ChainClient, codeID uint64, height int64) (*wasmTypes.QueryContractsByCodeResponse, error) { + pg := query.PageRequest{Limit: 100} + options := lensQuery.QueryOptions{Height: height, Pagination: &pg} + query := lensQuery.Query{Client: cl, Options: &options} + resp, err := query.ContractsByCodeIDAtHeight(codeID, height) + if err != nil { + return nil, err + } + return resp, nil +}