Skip to content

Commit 811d55f

Browse files
committed
add new command devd q eth_getAccount
1 parent 2bccf59 commit 811d55f

File tree

6 files changed

+149
-2
lines changed

6 files changed

+149
-2
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ devd query debug_traceTransaction [0xHash] [--tracer callTracer] [--evm-rpc http
7979

8080
devd query eth_call 0xContractAddr 0xCallData [--evm-rpc http://localhost:8545] [--from 0xFromAddr/Bech32] [--height 5m/0xHex/latest] [--gas 500k/0xHex] [--gas-prices 20e9/0xHex] [--value 1e18/0xHex]
8181

82+
devd query eth_getAccount [0xAddress/Bech32] [--evm-rpc http://localhost:8545]
83+
# devd q evm-account 0xAddress
84+
8285
devd query eth_chainId [--evm-rpc http://localhost:8545]
8386
```
8487

cmd/query/balance.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const (
2626

2727
func GetQueryBalanceCommand() *cobra.Command {
2828
cmd := &cobra.Command{
29-
Use: "balance [account] [?optional ERC-20 addrs..]",
29+
Use: "balance [0xAccount/Bech32] [?optional ERC-20 addrs..]",
3030
Aliases: []string{"b"},
3131
Short: "Get native balance of account. Optionally query ERC-20 token balances.",
3232
Long: fmt.Sprintf(`Get native balance of account. Optionally query ERC-20 token balances of if 2nd arg is provided or flag --%s is used.

cmd/query/erc20.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515

1616
func GetQueryErc20Command() *cobra.Command {
1717
cmd := &cobra.Command{
18-
Use: "erc20 [contract address] [?account address]",
18+
Use: "erc20 [contract address] [?0xAccount/Bech32]",
1919
Short: "Get ERC-20 token information. Optionally query the balance of an account.",
2020
Long: `Get ERC-20 token information. If account address is provided, it will query the balance of the account.
2121
Support bech32 address format`,

cmd/query/eth_getAccount.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package query
2+
3+
import (
4+
"context"
5+
"encoding/hex"
6+
"encoding/json"
7+
"fmt"
8+
"math/big"
9+
"strings"
10+
11+
"github.com/bcdevtools/devd/v3/cmd/types"
12+
"github.com/ethereum/go-ethereum/crypto"
13+
14+
"github.com/bcdevtools/devd/v3/cmd/flags"
15+
"github.com/bcdevtools/devd/v3/cmd/utils"
16+
"github.com/spf13/cobra"
17+
)
18+
19+
func GetQueryEvmRpcEthGetAccountCommand() *cobra.Command {
20+
cmd := &cobra.Command{
21+
Use: "eth_getAccount [0xAddress/Bech32]",
22+
Aliases: []string{"evm-account"},
23+
Short: "Query account using `eth_getAccount` via EVM RPC",
24+
Args: cobra.ExactArgs(1),
25+
Run: func(cmd *cobra.Command, args []string) {
26+
ethClient8545, evmRpc := flags.MustGetEthClient(cmd)
27+
28+
evmAddrs, err := utils.GetEvmAddressFromAnyFormatAddress(args...)
29+
utils.ExitOnErr(err, "failed to get evm address from input")
30+
evmAddr := evmAddrs[0]
31+
32+
contextHeight, err := flags.ReadFlagBlockNumberOrNil(cmd, flags.FlagHeight)
33+
utils.ExitOnErr(err, "failed to parse block number")
34+
35+
var params []types.JsonRpcQueryParam
36+
37+
paramsAddr, err := types.NewJsonRpcStringQueryParam(evmAddr.String())
38+
utils.ExitOnErr(err, "failed to create json rpc query param")
39+
params = append(params, paramsAddr)
40+
41+
paramsContext, err := types.NewJsonRpcStringQueryParam(func() string {
42+
if contextHeight == nil {
43+
return "latest"
44+
}
45+
return contextHeight.Text(16)
46+
}())
47+
utils.ExitOnErr(err, "failed to create json rpc query param")
48+
params = append(params, paramsContext)
49+
50+
bz, err := types.DoEvmRpcQuery(
51+
evmRpc,
52+
types.NewJsonRpcQueryBuilder(
53+
"eth_getAccount",
54+
params...,
55+
),
56+
0,
57+
)
58+
59+
utils.ExitOnErr(err, "failed to query account")
60+
61+
codeHashOfEmpty := "0x" + hex.EncodeToString(crypto.Keccak256(nil))
62+
63+
accountInfoAsMap, err := getResultObjectFromEvmRpcResponse(bz)
64+
if err != nil && strings.Contains(err.Error(), "method eth_getAccount does not exist") {
65+
// fallback
66+
err = nil
67+
68+
code, err := ethClient8545.CodeAt(context.Background(), evmAddr, contextHeight)
69+
utils.ExitOnErr(err, "failed to get code")
70+
71+
balance, err := ethClient8545.BalanceAt(context.Background(), evmAddr, contextHeight)
72+
utils.ExitOnErr(err, "failed to get balance")
73+
74+
nonce, err := ethClient8545.NonceAt(context.Background(), evmAddr, contextHeight)
75+
utils.ExitOnErr(err, "failed to get nonce")
76+
77+
accountInfoAsMap = map[string]interface{}{
78+
"codeHash": func() string {
79+
if len(code) == 0 {
80+
return codeHashOfEmpty
81+
}
82+
83+
return "0x" + hex.EncodeToString(crypto.Keccak256(code))
84+
}(),
85+
"balance": balance.String(),
86+
"nonce": nonce,
87+
}
88+
}
89+
utils.ExitOnErr(err, "failed to get result object from response")
90+
91+
if codeHashRaw, found := accountInfoAsMap["codeHash"]; found {
92+
// normalize
93+
if codeHashStr, ok := codeHashRaw.(string); ok && codeHashStr == "0x" {
94+
accountInfoAsMap["codeHash"] = codeHashOfEmpty
95+
}
96+
}
97+
if codeHashRaw, found := accountInfoAsMap["codeHash"]; found {
98+
if codeHashStr, ok := codeHashRaw.(string); ok {
99+
isContract := codeHashStr != codeHashOfEmpty
100+
accountInfoAsMap["_isContract"] = isContract
101+
102+
if !isContract {
103+
if nonceRaw, found := accountInfoAsMap["nonce"]; found {
104+
nonceStr := fmt.Sprintf("%v", nonceRaw)
105+
nonce, ok := new(big.Int).SetString(nonceStr, 10)
106+
if !ok {
107+
utils.PrintlnStdErr("ERR: failed to parse nonce:", nonceStr)
108+
} else {
109+
txSent := nonce
110+
if txSent.Sign() > 0 && txSent.Cmp(big.NewInt(1_000_000)) > 0 {
111+
txSent = new(big.Int).Mod(txSent, big.NewInt(1_000_000_000)) // Dymension RollApps increases nonce at fraud happened
112+
}
113+
accountInfoAsMap["_txSent"] = txSent.String()
114+
}
115+
}
116+
}
117+
}
118+
}
119+
120+
bz, err = json.Marshal(accountInfoAsMap)
121+
utils.ExitOnErr(err, "failed to marshal account info")
122+
123+
utils.TryPrintBeautyJson(bz)
124+
},
125+
}
126+
127+
cmd.Flags().String(flags.FlagEvmRpc, "", flags.FlagEvmRpcDesc)
128+
cmd.Flags().String(flags.FlagHeight, "", "query account info at specific height")
129+
130+
return cmd
131+
}

cmd/query/eth_getBlockByNumber.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,18 @@ func getResultObjectFromEvmRpcResponse(bz []byte) (map[string]interface{}, error
106106
if err != nil {
107107
return nil, err
108108
}
109+
errObj, found := _map["error"]
110+
if found && errObj != nil {
111+
errMap, ok := errObj.(map[string]interface{})
112+
if ok {
113+
code, foundCode := errMap["code"]
114+
msg, foundMsg := errMap["message"]
115+
if foundCode && foundMsg {
116+
return nil, fmt.Errorf("RPC response error: code=%v, message=%v", code, msg)
117+
}
118+
}
119+
return nil, fmt.Errorf("RPC response error: %v", errObj)
120+
}
109121
result, found := _map["result"]
110122
if !found {
111123
return nil, nil

cmd/query/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func Commands() *cobra.Command {
2222
GetQueryEvmRpcEthGetBlockByNumberCommand(),
2323
GetQueryEvmRpcEthChainIdCommand(),
2424
GetQueryEvmRpcEthCallCommand(),
25+
GetQueryEvmRpcEthGetAccountCommand(),
2526
GetQueryEvmRpcDebugTraceTransactionCommand(),
2627
// fake command for deprecated alias
2728
GetDeprecatedAliasBlockAsCommand(),

0 commit comments

Comments
 (0)