Skip to content

Commit 75466d6

Browse files
committed
add new command devd q account
1 parent 811d55f commit 75466d6

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ devd query balance [account addr] [optional ERC20 addr..] [--erc20] [--evm-rpc h
3939
```
4040
_`--erc20` flag, if provided, will attempt to fetch user balance of contracts on `x/erc20` module and virtual frontier bank contracts. This request additional Rest-API endpoint provided, or use default 1317._
4141

42+
#### Query account info
43+
44+
```bash
45+
devd query account [0xAddress/Bech32] [--rest http://localhost:1317]
46+
# devd q acc 0xAccount
47+
```
48+
4249
#### Query block/tx events
4350

4451
```bash

cmd/query/account.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package query
2+
3+
import (
4+
"encoding/hex"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"math/big"
9+
"net/http"
10+
"os"
11+
12+
sdk "github.com/cosmos/cosmos-sdk/types"
13+
"github.com/ethereum/go-ethereum/crypto"
14+
"github.com/pkg/errors"
15+
16+
"github.com/bcdevtools/devd/v3/cmd/flags"
17+
18+
"github.com/bcdevtools/devd/v3/cmd/utils"
19+
"github.com/spf13/cobra"
20+
)
21+
22+
func GetQueryAccountCommand() *cobra.Command {
23+
cmd := &cobra.Command{
24+
Use: "account [0xAccount/Bech32]",
25+
Aliases: []string{"acc"},
26+
Short: "Get account details based on address",
27+
Args: cobra.MinimumNArgs(1),
28+
Run: func(cmd *cobra.Command, args []string) {
29+
evmAddrs, err := utils.GetEvmAddressFromAnyFormatAddress(args...)
30+
if err != nil {
31+
utils.PrintlnStdErr("ERR:", err)
32+
return
33+
}
34+
35+
restApiEndpoint := flags.MustGetCosmosRest(cmd)
36+
37+
utils.PrintlnStdErr("INF: querying bech32 prefix")
38+
bech32Prefix, statusCode, err := fetchBech32PrefixFromRest(restApiEndpoint)
39+
if err != nil {
40+
if statusCode == 501 {
41+
utils.PrintlnStdErr("ERR: REST API does not support query bech32 prefix info")
42+
} else {
43+
utils.PrintlnStdErr("ERR: failed to fetch bech32 prefix:", err)
44+
}
45+
os.Exit(1)
46+
}
47+
bech32, err := sdk.Bech32ifyAddressBytes(bech32Prefix, evmAddrs[0].Bytes())
48+
if err == nil && bech32 == "" {
49+
err = errors.New("output bech32 address is empty")
50+
}
51+
utils.ExitOnErr(err, "failed to convert address to bech32")
52+
utils.PrintlnStdErr("INF: querying account", bech32)
53+
response, statusCode, err := fetchAccountDetailsFromRest(restApiEndpoint, bech32)
54+
if err != nil {
55+
if statusCode == 501 {
56+
utils.PrintlnStdErr("ERR: REST API does not support this feature")
57+
} else {
58+
utils.PrintlnStdErr("ERR: failed to fetch account details:", err)
59+
}
60+
os.Exit(1)
61+
}
62+
63+
var accountInfoAsMap map[string]interface{}
64+
err = json.Unmarshal([]byte(response), &accountInfoAsMap)
65+
utils.ExitOnErr(err, "failed to unmarshal account details")
66+
67+
if accountRaw, found := accountInfoAsMap["account"]; found {
68+
if accountMap, ok := accountRaw.(map[string]interface{}); ok && accountMap != nil {
69+
if typeRaw, found := accountMap["@type"]; found {
70+
if typeString, ok := typeRaw.(string); ok {
71+
if typeString == "/ethermint.types.v1.EthAccount" {
72+
codeHashOfEmpty := "0x" + hex.EncodeToString(crypto.Keccak256(nil))
73+
74+
if codeHashRaw, found := accountMap["code_hash"]; found {
75+
if codeHashStr, ok := codeHashRaw.(string); ok {
76+
isContract := codeHashStr != codeHashOfEmpty
77+
accountInfoAsMap["_isContract"] = isContract
78+
79+
if !isContract {
80+
if baseAccountRaw, found := accountMap["base_account"]; found {
81+
if baseAccountMap, ok := baseAccountRaw.(map[string]interface{}); ok {
82+
if accountNumberRaw, found := baseAccountMap["account_number"]; found {
83+
nonceStr := fmt.Sprintf("%v", accountNumberRaw)
84+
nonce, ok := new(big.Int).SetString(nonceStr, 10)
85+
if !ok {
86+
utils.PrintlnStdErr("ERR: failed to parse nonce:", nonceStr)
87+
} else {
88+
txSent := nonce
89+
if txSent.Sign() > 0 && txSent.Cmp(big.NewInt(1_000_000)) > 0 {
90+
txSent = new(big.Int).Mod(txSent, big.NewInt(1_000_000_000)) // Dymension RollApps increases nonce at fraud happened
91+
}
92+
accountInfoAsMap["_txSent"] = txSent.String()
93+
}
94+
}
95+
}
96+
}
97+
}
98+
}
99+
}
100+
}
101+
}
102+
}
103+
}
104+
}
105+
106+
bz, err := json.Marshal(accountInfoAsMap)
107+
utils.TryPrintBeautyJson(bz)
108+
},
109+
}
110+
111+
cmd.Flags().String(flags.FlagCosmosRest, "", flags.FlagCosmosRestDesc)
112+
113+
return cmd
114+
}
115+
116+
func fetchAccountDetailsFromRest(rest, bech32Address string) (response string, statusCode int, err error) {
117+
var resp *http.Response
118+
resp, err = http.Get(rest + "/cosmos/auth/v1beta1/accounts/" + bech32Address)
119+
if err != nil {
120+
err = errors.Wrap(err, "failed to fetch account details")
121+
return
122+
}
123+
124+
statusCode = resp.StatusCode
125+
126+
if resp.StatusCode != http.StatusOK {
127+
err = fmt.Errorf("failed to fetch account details! Status code: %d", resp.StatusCode)
128+
return
129+
}
130+
131+
defer func() {
132+
_ = resp.Body.Close()
133+
}()
134+
135+
bz, err := io.ReadAll(resp.Body)
136+
if err != nil {
137+
err = errors.Wrap(err, "failed to read response body of account details")
138+
return
139+
}
140+
141+
response = string(bz)
142+
return
143+
}
144+
145+
func fetchBech32PrefixFromRest(rest string) (bech32Prefix string, statusCode int, err error) {
146+
var resp *http.Response
147+
resp, err = http.Get(rest + "/cosmos/auth/v1beta1/bech32")
148+
if err != nil {
149+
err = errors.Wrap(err, "failed to fetch bech32 prefix info")
150+
return
151+
}
152+
153+
statusCode = resp.StatusCode
154+
155+
if resp.StatusCode != http.StatusOK {
156+
err = fmt.Errorf("failed to fetch bech32 prefix info! Status code: %d", resp.StatusCode)
157+
return
158+
}
159+
160+
defer func() {
161+
_ = resp.Body.Close()
162+
}()
163+
164+
bz, err := io.ReadAll(resp.Body)
165+
if err != nil {
166+
err = errors.Wrap(err, "failed to read response body of bech32 prefix info")
167+
return
168+
}
169+
170+
type bech32PrefixResponse struct {
171+
Bech32Prefix string `json:"bech32_prefix"`
172+
}
173+
174+
var bech32PrefixResp bech32PrefixResponse
175+
err = json.Unmarshal(bz, &bech32PrefixResp)
176+
if err != nil {
177+
err = errors.Wrap(err, "failed to unmarshal bech32 prefix info")
178+
return
179+
}
180+
if bech32PrefixResp.Bech32Prefix == "" {
181+
err = errors.New("bech32 prefix is empty")
182+
return
183+
}
184+
185+
bech32Prefix = bech32PrefixResp.Bech32Prefix
186+
return
187+
}

cmd/query/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ func Commands() *cobra.Command {
1414

1515
cmd.AddCommand(
1616
GetQueryErc20Command(),
17+
GetQueryAccountCommand(),
1718
GetQueryBalanceCommand(),
1819
GetQueryTxsInBlockCommand(),
1920
GetQueryTxEventsCommand(),

0 commit comments

Comments
 (0)