Skip to content

Commit b5362ae

Browse files
authored
[EVM] show bank send in GetBlock response (#1187)
* [EVM] show bank send in GetBlock response * add utilities
1 parent b3a5a7e commit b5362ae

File tree

7 files changed

+186
-54
lines changed

7 files changed

+186
-54
lines changed

evmrpc/block.go

+27-20
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/cosmos/cosmos-sdk/client"
99
sdk "github.com/cosmos/cosmos-sdk/types"
10+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
1011
"github.com/ethereum/go-ethereum/common"
1112
"github.com/ethereum/go-ethereum/common/hexutil"
1213
ethtypes "github.com/ethereum/go-ethereum/core/types"
@@ -59,7 +60,7 @@ func (a *BlockAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fu
5960
if err != nil {
6061
return nil, err
6162
}
62-
return encodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, a.keeper, a.txConfig.TxDecoder(), fullTx)
63+
return EncodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, a.keeper, a.txConfig.TxDecoder(), fullTx)
6364
}
6465

6566
func (a *BlockAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
@@ -75,10 +76,10 @@ func (a *BlockAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber,
7576
if err != nil {
7677
return nil, err
7778
}
78-
return encodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, a.keeper, a.txConfig.TxDecoder(), fullTx)
79+
return EncodeTmBlock(a.ctxProvider(LatestCtxHeight), block, blockRes, a.keeper, a.txConfig.TxDecoder(), fullTx)
7980
}
8081

81-
func encodeTmBlock(
82+
func EncodeTmBlock(
8283
ctx sdk.Context,
8384
block *coretypes.ResultBlock,
8485
blockRes *coretypes.ResultBlockResults,
@@ -102,24 +103,30 @@ func encodeTmBlock(
102103
if err != nil {
103104
return nil, errors.New("failed to decode transaction")
104105
}
105-
if len(decoded.GetMsgs()) != 1 {
106-
// EVM message must have exactly one message
107-
continue
108-
}
109-
evmTx, ok := decoded.GetMsgs()[0].(*types.MsgEVMTransaction)
110-
if !ok || evmTx.IsAssociateTx() {
111-
continue
112-
}
113-
ethtx, _ := evmTx.AsTransaction()
114-
hash := ethtx.Hash()
115-
if !fullTx {
116-
transactions = append(transactions, hash)
117-
} else {
118-
receipt, err := k.GetReceipt(ctx, hash)
119-
if err != nil {
120-
continue
106+
for _, msg := range decoded.GetMsgs() {
107+
switch m := msg.(type) {
108+
case *types.MsgEVMTransaction:
109+
if m.IsAssociateTx() {
110+
continue
111+
}
112+
ethtx, _ := m.AsTransaction()
113+
hash := ethtx.Hash()
114+
if !fullTx {
115+
transactions = append(transactions, hash)
116+
} else {
117+
receipt, err := k.GetReceipt(ctx, hash)
118+
if err != nil {
119+
continue
120+
}
121+
transactions = append(transactions, hydrateTransaction(ethtx, number, blockhash, receipt))
122+
}
123+
case *banktypes.MsgSend:
124+
// bank send does not have an EVM tx hash, so we only consider fullTx case here
125+
if !fullTx {
126+
continue
127+
}
128+
transactions = append(transactions, hydrateBankSendTransaction(ctx, m, k))
121129
}
122-
transactions = append(transactions, hydrateTransaction(ethtx, number, blockhash, receipt))
123130
}
124131
}
125132
result := map[string]interface{}{

evmrpc/block_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
package evmrpc_test
22

33
import (
4+
"math/big"
45
"testing"
56

7+
sdk "github.com/cosmos/cosmos-sdk/types"
8+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/ethereum/go-ethereum/common/hexutil"
11+
"github.com/sei-protocol/sei-chain/evmrpc"
12+
testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper"
613
"github.com/stretchr/testify/require"
14+
abci "github.com/tendermint/tendermint/abci/types"
15+
"github.com/tendermint/tendermint/rpc/coretypes"
16+
tmtypes "github.com/tendermint/tendermint/types"
717
)
818

919
func TestGetBlockByHash(t *testing.T) {
@@ -82,3 +92,47 @@ func verifyBlockResult(t *testing.T, resObj map[string]interface{}) {
8292
require.Equal(t, []interface{}{}, resObj["uncles"])
8393
require.Equal(t, "0x0", resObj["baseFeePerGas"])
8494
}
95+
96+
func TestEncodeBankMsg(t *testing.T) {
97+
k, ctx := testkeeper.MockEVMKeeper()
98+
fromSeiAddr, _ := testkeeper.MockAddressPair()
99+
toSeiAddr, _ := testkeeper.MockAddressPair()
100+
b := TxConfig.NewTxBuilder()
101+
b.SetMsgs(banktypes.NewMsgSend(fromSeiAddr, toSeiAddr, sdk.NewCoins(sdk.NewCoin("usei", sdk.NewInt(10)))))
102+
tx := b.GetTx()
103+
resBlock := coretypes.ResultBlock{
104+
BlockID: MockBlockID,
105+
Block: &tmtypes.Block{
106+
Header: mockBlockHeader(MockHeight),
107+
Data: tmtypes.Data{
108+
Txs: []tmtypes.Tx{func() []byte {
109+
bz, _ := Encoder(tx)
110+
return bz
111+
}()},
112+
},
113+
LastCommit: &tmtypes.Commit{
114+
Height: MockHeight - 1,
115+
},
116+
},
117+
}
118+
resBlockRes := coretypes.ResultBlockResults{
119+
TxsResults: []*abci.ExecTxResult{
120+
{
121+
Data: func() []byte {
122+
bz, _ := Encoder(tx)
123+
return bz
124+
}(),
125+
},
126+
},
127+
}
128+
res, err := evmrpc.EncodeTmBlock(ctx, &resBlock, &resBlockRes, k, Decoder, true)
129+
require.Nil(t, err)
130+
txs := res["transactions"].([]interface{})
131+
require.Equal(t, 1, len(txs))
132+
to := common.BytesToAddress(toSeiAddr)
133+
require.Equal(t, evmrpc.RPCTransaction{
134+
From: common.BytesToAddress(fromSeiAddr),
135+
To: &to,
136+
Value: (*hexutil.Big)(big.NewInt(10000000000000)),
137+
}, txs[0])
138+
}

evmrpc/setup_test.go

+26-20
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ var SConfig = evmrpc.SimulateConfig{GasCap: 10000000}
5858
var filterTimeoutDuration = 500 * time.Millisecond
5959
var TotalTxCount int = 11
6060

61+
var MockBlockID = tmtypes.BlockID{
62+
Hash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000001")),
63+
}
64+
6165
type MockClient struct {
6266
mock.Client
6367
}
@@ -70,29 +74,31 @@ func mustHexToBytes(h string) []byte {
7074
return bz
7175
}
7276

77+
func mockBlockHeader(height int64) tmtypes.Header {
78+
return tmtypes.Header{
79+
ChainID: "test",
80+
Height: height,
81+
Time: time.Unix(1696941649, 0),
82+
DataHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000002")),
83+
AppHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000003")),
84+
LastResultsHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000004")),
85+
ProposerAddress: tmtypes.Address(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000005")),
86+
LastBlockID: tmtypes.BlockID{
87+
Hash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000006")),
88+
},
89+
LastCommitHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000007")),
90+
ValidatorsHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000009")),
91+
NextValidatorsHash: bytes.HexBytes(mustHexToBytes("000000000000000000000000000000000000000000000000000000000000000A")),
92+
ConsensusHash: bytes.HexBytes(mustHexToBytes("000000000000000000000000000000000000000000000000000000000000000B")),
93+
EvidenceHash: bytes.HexBytes(mustHexToBytes("000000000000000000000000000000000000000000000000000000000000000E")),
94+
}
95+
}
96+
7397
func (c *MockClient) mockBlock(height int64) *coretypes.ResultBlock {
7498
return &coretypes.ResultBlock{
75-
BlockID: tmtypes.BlockID{
76-
Hash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000001")),
77-
},
99+
BlockID: MockBlockID,
78100
Block: &tmtypes.Block{
79-
Header: tmtypes.Header{
80-
ChainID: "test",
81-
Height: height,
82-
Time: time.Unix(1696941649, 0),
83-
DataHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000002")),
84-
AppHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000003")),
85-
LastResultsHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000004")),
86-
ProposerAddress: tmtypes.Address(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000005")),
87-
LastBlockID: tmtypes.BlockID{
88-
Hash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000006")),
89-
},
90-
LastCommitHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000007")),
91-
ValidatorsHash: bytes.HexBytes(mustHexToBytes("0000000000000000000000000000000000000000000000000000000000000009")),
92-
NextValidatorsHash: bytes.HexBytes(mustHexToBytes("000000000000000000000000000000000000000000000000000000000000000A")),
93-
ConsensusHash: bytes.HexBytes(mustHexToBytes("000000000000000000000000000000000000000000000000000000000000000B")),
94-
EvidenceHash: bytes.HexBytes(mustHexToBytes("000000000000000000000000000000000000000000000000000000000000000E")),
95-
},
101+
Header: mockBlockHeader(height),
96102
Data: tmtypes.Data{
97103
Txs: []tmtypes.Tx{func() []byte {
98104
bz, _ := Encoder(Tx)

evmrpc/tx.go

+8
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ func (t *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.
7575
return encodeReceipt(receipt, block.BlockID.Hash, blockRes.TxsResults[receipt.TransactionIndex])
7676
}
7777

78+
func (t *TransactionAPI) GetVMError(hash common.Hash) (string, error) {
79+
receipt, err := t.keeper.GetReceipt(t.ctxProvider(LatestCtxHeight), hash)
80+
if err != nil {
81+
return "", err
82+
}
83+
return receipt.VmError, nil
84+
}
85+
7886
func (t *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction {
7987
blockNumber, err := getBlockNumber(ctx, t.tmClient, blockNr)
8088
if err != nil {

evmrpc/tx_test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,12 @@ func TestSign(t *testing.T) {
224224

225225
func TestGetPendingNonces(t *testing.T) {
226226
resObj := sendRequestGood(t, "getPendingNonces", "0x1111111111111111111111111111111111111113")
227-
fmt.Println(resObj)
228227
require.Equal(t, "", resObj["result"].(string))
229228
}
229+
230+
func TestGetVMError(t *testing.T) {
231+
resObj := sendRequestGood(t, "getVMError", "0x78b0bd7fe9ccc8ae8a61eae9315586cf2a406dacf129313e6c5769db7cd14372")
232+
require.Equal(t, "", resObj["result"].(string))
233+
resObj = sendRequestGood(t, "getVMError", "0x78b0bd7fe9ccc8ae8a61eae9315586cf2a406dacf129313e6c5769db7cd14374")
234+
require.Equal(t, "not found", resObj["error"].(map[string]interface{})["message"])
235+
}

evmrpc/utils.go

+30
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ import (
1010
"github.com/cosmos/cosmos-sdk/client/config"
1111
"github.com/cosmos/cosmos-sdk/codec/legacy"
1212
"github.com/cosmos/cosmos-sdk/crypto/keyring"
13+
sdk "github.com/cosmos/cosmos-sdk/types"
14+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
1315
"github.com/ethereum/go-ethereum/common"
1416
"github.com/ethereum/go-ethereum/common/hexutil"
1517
ethtypes "github.com/ethereum/go-ethereum/core/types"
1618
"github.com/ethereum/go-ethereum/crypto"
1719
"github.com/ethereum/go-ethereum/rpc"
1820
"github.com/sei-protocol/sei-chain/x/evm/ante"
21+
"github.com/sei-protocol/sei-chain/x/evm/keeper"
22+
"github.com/sei-protocol/sei-chain/x/evm/state"
1923
"github.com/sei-protocol/sei-chain/x/evm/types"
2024
rpcclient "github.com/tendermint/tendermint/rpc/client"
2125
)
@@ -92,6 +96,32 @@ func hydrateTransaction(
9296
}
9397
}
9498

99+
func hydrateBankSendTransaction(ctx sdk.Context, msg *banktypes.MsgSend, k *keeper.Keeper) RPCTransaction {
100+
useiAmount := msg.Amount.AmountOf(k.GetBaseDenom(ctx))
101+
if useiAmount.IsZero() {
102+
return RPCTransaction{}
103+
}
104+
value := new(big.Int).Mul(useiAmount.BigInt(), state.UseiToSweiMultiplier)
105+
fromSeiAddr := sdk.MustAccAddressFromBech32(msg.FromAddress)
106+
toSeiAddr := sdk.MustAccAddressFromBech32(msg.ToAddress)
107+
var from, to common.Address
108+
if fromAddr, ok := k.GetEVMAddress(ctx, fromSeiAddr); ok {
109+
from = fromAddr
110+
} else {
111+
from = common.BytesToAddress(fromSeiAddr)
112+
}
113+
if toAddr, ok := k.GetEVMAddress(ctx, toSeiAddr); ok {
114+
to = toAddr
115+
} else {
116+
to = common.BytesToAddress(toSeiAddr)
117+
}
118+
return RPCTransaction{
119+
From: from,
120+
To: &to,
121+
Value: (*hexutil.Big)(value),
122+
}
123+
}
124+
95125
func GetBlockNumberByNrOrHash(ctx context.Context, tmClient rpcclient.Client, blockNrOrHash rpc.BlockNumberOrHash) (*int64, error) {
96126
if blockNrOrHash.BlockHash != nil {
97127
res, err := tmClient.BlockByHash(ctx, blockNrOrHash.BlockHash[:])

x/evm/client/cli/tx.go

+34-13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
ethabi "github.com/ethereum/go-ethereum/accounts/abi"
1515
"github.com/ethereum/go-ethereum/common"
16+
"github.com/ethereum/go-ethereum/common/hexutil"
1617
"github.com/ethereum/go-ethereum/crypto"
1718
"github.com/ethereum/go-ethereum/rlp"
1819
"github.com/spf13/cobra"
@@ -137,10 +138,10 @@ func CmdAssociateAddress() *cobra.Command {
137138

138139
func CmdSend() *cobra.Command {
139140
cmd := &cobra.Command{
140-
Use: "send [to EVM address] [amount in usei] [nonce] --from=<sender> --gas-fee-cap=<cap> --gas-limit=<limit> --evm-chain-id=<chain-id> --evm-rpc=<url>",
141+
Use: "send [to EVM address] [amount in usei] --from=<sender> --gas-fee-cap=<cap> --gas-limit=<limit> --evm-chain-id=<chain-id> --evm-rpc=<url>",
141142
Short: "send usei to EVM address",
142143
Long: "",
143-
Args: cobra.ExactArgs(3),
144+
Args: cobra.ExactArgs(2),
144145
RunE: func(cmd *cobra.Command, args []string) (err error) {
145146
clientCtx, err := client.GetClientTxContext(cmd)
146147
if err != nil {
@@ -163,12 +164,36 @@ func CmdSend() *cobra.Command {
163164
privHex := hex.EncodeToString(priv.Bytes())
164165
key, _ := crypto.HexToECDSA(privHex)
165166

166-
to := common.HexToAddress(args[0])
167-
val, err := strconv.ParseUint(args[1], 10, 64)
167+
rpc, err := cmd.Flags().GetString(FlagRPC)
168+
if err != nil {
169+
return err
170+
}
171+
nonceQuery := fmt.Sprintf("{\"jsonrpc\": \"2.0\",\"method\": \"eth_getTransactionCount\",\"params\":[\"%s\",\"latest\"],\"id\":\"send-cli\"}", crypto.PubkeyToAddress(key.PublicKey).Hex())
172+
req, err := http.NewRequest(http.MethodGet, rpc, strings.NewReader(nonceQuery))
173+
if err != nil {
174+
return err
175+
}
176+
req.Header.Set("Content-Type", "application/json")
177+
res, err := http.DefaultClient.Do(req)
178+
if err != nil {
179+
return err
180+
}
181+
defer res.Body.Close()
182+
resBody, err := io.ReadAll(res.Body)
168183
if err != nil {
169184
return err
170185
}
171-
nonce, err := strconv.ParseUint(args[2], 10, 64)
186+
resObj := map[string]interface{}{}
187+
if err := json.Unmarshal(resBody, &resObj); err != nil {
188+
return err
189+
}
190+
nonce := new(hexutil.Uint64)
191+
if err := nonce.UnmarshalText([]byte(resObj["result"].(string))); err != nil {
192+
return err
193+
}
194+
195+
to := common.HexToAddress(args[0])
196+
val, err := strconv.ParseUint(args[1], 10, 64)
172197
if err != nil {
173198
return err
174199
}
@@ -185,7 +210,7 @@ func CmdSend() *cobra.Command {
185210
return err
186211
}
187212
txData := ethtypes.DynamicFeeTx{
188-
Nonce: nonce,
213+
Nonce: uint64(*nonce),
189214
GasFeeCap: new(big.Int).SetUint64(gasFeeCap),
190215
Gas: gasLimit,
191216
To: &to,
@@ -207,21 +232,17 @@ func CmdSend() *cobra.Command {
207232
payload := "0x" + hex.EncodeToString(bz)
208233

209234
body := fmt.Sprintf("{\"jsonrpc\": \"2.0\",\"method\": \"eth_sendRawTransaction\",\"params\":[\"%s\"],\"id\":\"send\"}", payload)
210-
rpc, err := cmd.Flags().GetString(FlagRPC)
211-
if err != nil {
212-
return err
213-
}
214-
req, err := http.NewRequest(http.MethodGet, rpc, strings.NewReader(body))
235+
req, err = http.NewRequest(http.MethodGet, rpc, strings.NewReader(body))
215236
if err != nil {
216237
return err
217238
}
218239
req.Header.Set("Content-Type", "application/json")
219-
res, err := http.DefaultClient.Do(req)
240+
res, err = http.DefaultClient.Do(req)
220241
if err != nil {
221242
return err
222243
}
223244
defer res.Body.Close()
224-
resBody, err := io.ReadAll(res.Body)
245+
resBody, err = io.ReadAll(res.Body)
225246
if err != nil {
226247
return err
227248
}

0 commit comments

Comments
 (0)