Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

feat: add tx cli to build evm transaction #712

Merged
merged 1 commit into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (rpc, evm) [tharsis#673](https://github.com/tharsis/ethermint/pull/673) Use tendermint events to store fee market basefee.
* (rpc) [tharsis#624](https://github.com/tharsis/ethermint/pull/624) Implement new JSON-RPC endpoints from latest geth version
* (evm) [tharsis#662](https://github.com/tharsis/ethermint/pull/662) Disable basefee for non london blocks
* (cmd) [tharsis#712](https://github.com/tharsis/ethermint/pull/712) add tx cli to build evm transaction

### Bug Fixes

Expand Down
33 changes: 3 additions & 30 deletions rpc/ethereum/namespaces/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ import (

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/ethereum/go-ethereum/accounts/keystore"
Expand Down Expand Up @@ -452,46 +450,21 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
return common.Hash{}, err
}

builder, ok := e.clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
if !ok {
e.logger.Error("clientCtx.TxConfig.NewTxBuilder returns unsupported builder")
}

option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{})
if err != nil {
e.logger.Error("codectypes.NewAnyWithValue failed to pack an obvious value", "error", err.Error())
}

builder.SetExtensionOptions(option)
err = builder.SetMsgs(tx.GetMsgs()...)
if err != nil {
e.logger.Error("builder.SetMsgs failed", "error", err.Error())
}

// Query params to use the EVM denomination
res, err := e.queryClient.QueryClient.Params(e.ctx, &evmtypes.QueryParamsRequest{})
if err != nil {
e.logger.Error("failed to query evm params", "error", err.Error())
return common.Hash{}, err
}

txData, err := evmtypes.UnpackTxData(ethereumTx.Data)
cosmosTx, err := ethereumTx.BuildTx(e.clientCtx.TxConfig.NewTxBuilder(), res.Params.EvmDenom)
if err != nil {
e.logger.Error("failed to unpack tx data", "error", err.Error())
e.logger.Error("failed to build cosmos tx", "error", err.Error())
return common.Hash{}, err
}

fees := sdk.Coins{
{
Denom: res.Params.EvmDenom,
Amount: sdk.NewIntFromBigInt(txData.Fee()),
},
}
builder.SetFeeAmount(fees)
builder.SetGasLimit(ethereumTx.GetGas())

// Encode transaction by default Tx encoder
txBytes, err := e.clientCtx.TxConfig.TxEncoder()(builder.GetTx())
txBytes, err := e.clientCtx.TxConfig.TxEncoder()(cosmosTx)
if err != nil {
e.logger.Error("failed to encode eth tx using default encoder", "error", err.Error())
return common.Hash{}, err
Expand Down
111 changes: 111 additions & 0 deletions x/evm/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package cli

import (
"bufio"
"fmt"
"os"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/spf13/cobra"

rpctypes "github.com/tharsis/ethermint/rpc/ethereum/types"
"github.com/tharsis/ethermint/x/evm/types"
)

// GetTxCmd returns the transaction commands for this module
func GetTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: types.ModuleName,
Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName),
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmd.AddCommand(NewRawTxCmd())
return cmd
}

// NewRawTxCmd command build cosmos transaction from raw ethereum transaction
func NewRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "raw [tx-hex]",
Short: "Build cosmos transaction from raw ethereum transaction",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
data, err := hexutil.Decode(args[0])
if err != nil {
return errors.Wrap(err, "failed to decode ethereum tx hex bytes")
}

msg := &types.MsgEthereumTx{}
if err := msg.UnmarshalBinary(data); err != nil {
return err
}

if err := msg.ValidateBasic(); err != nil {
return err
}

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

rsp, err := rpctypes.NewQueryClient(clientCtx).Params(cmd.Context(), &types.QueryParamsRequest{})
if err != nil {
return err
}

tx, err := msg.BuildTx(clientCtx.TxConfig.NewTxBuilder(), rsp.Params.EvmDenom)
if err != nil {
return err
}

if clientCtx.GenerateOnly {
json, err := clientCtx.TxConfig.TxJSONEncoder()(tx)
if err != nil {
return err
}

return clientCtx.PrintString(fmt.Sprintf("%s\n", json))
}

if !clientCtx.SkipConfirm {
out, err := clientCtx.TxConfig.TxJSONEncoder()(tx)
if err != nil {
return err
}

_, _ = fmt.Fprintf(os.Stderr, "%s\n\n", out)

buf := bufio.NewReader(os.Stdin)
ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf, os.Stderr)

if err != nil || !ok {
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "canceled transaction")
return err
}
}

txBytes, err := clientCtx.TxConfig.TxEncoder()(tx)
if err != nil {
return err
}

// broadcast to a Tendermint node
res, err := clientCtx.BroadcastTx(txBytes)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddTxFlagsToCmd(cmd)
return cmd
}
2 changes: 1 addition & 1 deletion x/evm/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c client.Context, serveMux *ru

// GetTxCmd returns the root tx command for the evm module.
func (AppModuleBasic) GetTxCmd() *cobra.Command {
return nil
return cli.GetTxCmd()
}

// GetQueryCmd returns no root query command for the evm module.
Expand Down
47 changes: 47 additions & 0 deletions x/evm/types/msg.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package types

import (
"errors"
"fmt"
"math/big"

"github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"

"github.com/tharsis/ethermint/types"

Expand Down Expand Up @@ -282,3 +286,46 @@ func (msg *MsgEthereumTx) GetSender(chainID *big.Int) (common.Address, error) {
func (msg MsgEthereumTx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
return unpacker.UnpackAny(msg.Data, new(TxData))
}

// UnmarshalBinary decodes the canonical encoding of transactions.
func (msg *MsgEthereumTx) UnmarshalBinary(b []byte) error {
tx := &ethtypes.Transaction{}
if err := tx.UnmarshalBinary(b); err != nil {
return err
}
return msg.FromEthereumTx(tx)
}

// BuildTx builds the canonical cosmos tx from ethereum msg
func (msg *MsgEthereumTx) BuildTx(b client.TxBuilder, evmDenom string) (signing.Tx, error) {
builder, ok := b.(authtx.ExtensionOptionsTxBuilder)
if !ok {
return nil, errors.New("unsupported builder")
}

option, err := codectypes.NewAnyWithValue(&ExtensionOptionsEthereumTx{})
if err != nil {
return nil, err
}

txData, err := UnpackTxData(msg.Data)
if err != nil {
return nil, err
}
fees := sdk.Coins{
{
Denom: evmDenom,
Amount: sdk.NewIntFromBigInt(txData.Fee()),
},
}

builder.SetExtensionOptions(option)
err = builder.SetMsgs(msg)
if err != nil {
return nil, err
}
builder.SetFeeAmount(fees)
builder.SetGasLimit(msg.GetGas())
tx := builder.GetTx()
return tx, nil
}
Loading