diff --git a/cmd/root.go b/cmd/root.go index e388170..5389d25 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,7 +25,7 @@ func Execute(decompiler *decompiler.Decompiler) { viper.BindPFlag("chainId", rootCmd.PersistentFlags().Lookup("chain-id")) viper.BindPFlag("rpcUrl", rootCmd.PersistentFlags().Lookup("rpc-url")) - tx := NewTransactionCommands() + tx := NewTransactionCommands(decompiler) rootCmd.AddCommand(tx.GetRootCommand()) accountCmd := NewAccountCommands() diff --git a/cmd/tx.go b/cmd/tx.go index efdc77c..34d36dc 100644 --- a/cmd/tx.go +++ b/cmd/tx.go @@ -7,15 +7,17 @@ import ( "log" "github.com/idanya/evm-cli/clients/directory/openchain" + decompiler "github.com/idanya/evm-cli/decompiler" "github.com/idanya/evm-cli/services" "github.com/spf13/cobra" ) type TransactionCommands struct { + decompiler *decompiler.Decompiler } -func NewTransactionCommands() *TransactionCommands { - return &TransactionCommands{} +func NewTransactionCommands(decompiler *decompiler.Decompiler) *TransactionCommands { + return &TransactionCommands{decompiler} } func (tx *TransactionCommands) GetRootCommand() *cobra.Command { @@ -37,7 +39,7 @@ func (tx *TransactionCommands) GetTransactionDataCommand() *cobra.Command { Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - tx, err := NodeClientFromViper().GetTransactionByHash(context.Background(), args[0]) + tx, err := tx.getTransactionService().GetTransactionByHash(context.Background(), args[0]) if err != nil { log.Fatal(err) } @@ -54,8 +56,7 @@ func (tx *TransactionCommands) GetTransactionReceiptCommand() *cobra.Command { Short: "Get transaction receipt by hash", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - transactionService := services.NewTransactionService(NodeClientFromViper(), openchain.NewClient()) - receipt, err := transactionService.GetTransactionReceipt(context.Background(), args[0]) + receipt, err := tx.getTransactionService().GetTransactionReceipt(context.Background(), args[0]) if err != nil { log.Fatal(err) } @@ -65,3 +66,9 @@ func (tx *TransactionCommands) GetTransactionReceiptCommand() *cobra.Command { }, } } + +func (tx *TransactionCommands) getTransactionService() *services.TransactionService { + openchainClient := openchain.NewClient() + contractService := services.NewContractService(NodeClientFromViper(), tx.decompiler, openchainClient) + return services.NewTransactionService(NodeClientFromViper(), openchainClient, contractService) +} diff --git a/entities/enriched_tx_info.go b/entities/enriched_tx_info.go new file mode 100644 index 0000000..f4628d6 --- /dev/null +++ b/entities/enriched_tx_info.go @@ -0,0 +1,46 @@ +package entities + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type EnrichedTxInfo struct { + *types.Transaction + DecodedData *DecodeResult +} + +func (et *EnrichedTxInfo) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + TxType byte + ChainID *big.Int + AccessList types.AccessList + Data []byte + Gas uint64 + GasPrice *big.Int + GasTipCap *big.Int + GasFeeCap *big.Int + Value *big.Int + Nonce uint64 + Hash common.Hash + To *common.Address `json:"to"` + DecodedData *DecodeResult `json:"decodedData,omitempty"` + }{ + TxType: byte(et.Type()), + ChainID: et.Transaction.ChainId(), + AccessList: et.Transaction.AccessList(), + Data: et.Transaction.Data(), + Gas: et.Transaction.Gas(), + GasPrice: et.Transaction.GasPrice(), + GasTipCap: et.Transaction.GasTipCap(), + GasFeeCap: et.Transaction.GasFeeCap(), + Value: et.Transaction.Value(), + Nonce: et.Transaction.Nonce(), + Hash: et.Transaction.Hash(), + To: et.Transaction.To(), + DecodedData: et.DecodedData, + }) +} diff --git a/services/transaction.go b/services/transaction.go index 919b7af..5dba5c8 100644 --- a/services/transaction.go +++ b/services/transaction.go @@ -3,6 +3,7 @@ package services import ( "context" + "github.com/ethereum/go-ethereum/common" "github.com/idanya/evm-cli/clients/directory" "github.com/idanya/evm-cli/clients/nodes" "github.com/idanya/evm-cli/entities" @@ -11,10 +12,13 @@ import ( type TransactionService struct { nodeClient nodes.NodeClient directoryClient directory.DirectoryClient + contractService *ContractService } -func NewTransactionService(nodeClient nodes.NodeClient, directoryClient directory.DirectoryClient) *TransactionService { - return &TransactionService{nodeClient, directoryClient} +func NewTransactionService(nodeClient nodes.NodeClient, + directoryClient directory.DirectoryClient, + contractService *ContractService) *TransactionService { + return &TransactionService{nodeClient, directoryClient, contractService} } func (ts *TransactionService) GetTransactionReceipt(context context.Context, txHash string) (*entities.EnrichedReceipt, error) { @@ -41,3 +45,17 @@ func (ts *TransactionService) GetTransactionReceipt(context context.Context, txH return enrichedReceipt, nil } + +func (ts *TransactionService) GetTransactionByHash(context context.Context, txHash string) (*entities.EnrichedTxInfo, error) { + transaction, err := ts.nodeClient.GetTransactionByHash(context, txHash) + if err != nil { + return nil, err + } + + decoded, err := ts.contractService.DecodeContractCallData(context, common.Bytes2Hex(transaction.Data())) + if err == nil { + return &entities.EnrichedTxInfo{Transaction: transaction, DecodedData: decoded}, nil + } + + return &entities.EnrichedTxInfo{Transaction: transaction}, nil +} diff --git a/services/transaction_test.go b/services/transaction_test.go index 5cd144f..5c5584e 100644 --- a/services/transaction_test.go +++ b/services/transaction_test.go @@ -8,15 +8,19 @@ import ( "github.com/ethereum/go-ethereum/core/types" dirmock "github.com/idanya/evm-cli/clients/directory/mocks" "github.com/idanya/evm-cli/clients/nodes/mocks" + decompiler "github.com/idanya/evm-cli/decompiler" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) func TestTransactionService_GetTransactionReceipt(t *testing.T) { directoryClientMock := dirmock.NewDirectoryClient(t) + decompilerClient := decompiler.NewDecompiler(directoryClientMock) nodeClientMock := mocks.NewNodeClient(t) - transactionService := NewTransactionService(nodeClientMock, directoryClientMock) + contractService := NewContractService(nodeClientMock, decompilerClient, directoryClientMock) + + transactionService := NewTransactionService(nodeClientMock, directoryClientMock, contractService) topicHash := common.HexToHash("0xdb80dd488acf86d17c747445b0eabb5d57c541d3bd7b6b87af987858e5066b2b") txHash := "0xec8ecd56dca115adcc8de346ffe054841f810964a68afc81faf764f8a0ae7c26"