Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(evm-cli): Add the account query to the EVM command. Cover the CLI with tests. #2002

Merged
merged 8 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -108,6 +108,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#2003](https://github.com/NibiruChain/nibiru/pull/2003) - fix(evm): fix FunToken conversions between Cosmos and EVM
- [#2004](https://github.com/NibiruChain/nibiru/pull/2004) - refactor(evm)!: replace `HexAddr` with `EIP55Addr`
- [#2008](https://github.com/NibiruChain/nibiru/pull/2008) - refactor(evm): clean up precompile setups
- [#2002](https://github.com/NibiruChain/nibiru/pull/2002) - feat(evm): Add the account query to the EVM command. Cover the CLI with tests.

#### Dapp modules: perp, spot, oracle, etc

Expand Down
11 changes: 6 additions & 5 deletions app/evmante/evmante_sigverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ func NewEthSigVerificationDecorator(k EVMKeeper) EthSigVerificationDecorator {
}
}

// AnteHandle validates checks that the registered chain id is the same as the one on the message, and
// that the signer address matches the one defined on the message.
// It's not skipped for RecheckTx, because it set `From` address which is critical from other ante handler to work.
// Failure in RecheckTx will prevent tx to be included into block, especially when CheckTx succeed, in which case user
// won't see the error message.
// AnteHandle validates checks that the registered chain id is the same as the
// one on the message, and that the signer address matches the one defined on the
// message. It's not skipped for RecheckTx, because it set `From` address which
// is critical from other ante handler to work. Failure in RecheckTx will prevent
// tx to be included into block, especially when CheckTx succeed, in which case
// user won't see the error message.
func (esvd EthSigVerificationDecorator) AnteHandle(
ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
Expand Down
115 changes: 115 additions & 0 deletions x/evm/cli/cli_setup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package cli_test

import (
"context"
"io"
"testing"

"github.com/stretchr/testify/suite"

rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock"
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdktestutil "github.com/cosmos/cosmos-sdk/testutil"
sdktestutilcli "github.com/cosmos/cosmos-sdk/testutil/cli"
testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil"

svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"

"github.com/NibiruChain/nibiru/v2/x/evm/cli"
"github.com/NibiruChain/nibiru/v2/x/evm/evmmodule"
)

type Suite struct {
suite.Suite

keyring keyring.Keyring
encCfg testutilmod.TestEncodingConfig
baseCtx sdkclient.Context
clientCtx sdkclient.Context

testAcc sdktestutil.TestAccount
}

func (s *Suite) SetupSuite() {
s.encCfg = testutilmod.MakeTestEncodingConfig(evmmodule.AppModuleBasic{})
s.keyring = keyring.NewInMemory(s.encCfg.Codec)
s.baseCtx = sdkclient.Context{}.
WithKeyring(s.keyring).
WithTxConfig(s.encCfg.TxConfig).
WithCodec(s.encCfg.Codec).
WithClient(sdktestutilcli.MockTendermintRPC{Client: rpcclientmock.Client{}}).
WithAccountRetriever(sdkclient.MockAccountRetriever{}).
WithOutput(io.Discard).
WithChainID("test-chain")

s.clientCtx = s.baseCtx

testAccs := sdktestutil.CreateKeyringAccounts(s.T(), s.keyring, 1)
s.testAcc = testAccs[0]
}

func TestSuite(t *testing.T) {
suite.Run(t, new(Suite))
}

// Flags for broadcasting transactions
func commonTxArgs() []string {
return []string{
"--yes=true", // skip confirmation
"--broadcast-mode=sync",
"--fees=1unibi",
"--chain-id=test-chain",
}
}

type TestCase struct {
name string
args []string
extraArgs []string
wantErr string
}

func (tc TestCase) NewCtx(s *Suite) sdkclient.Context {
return s.baseCtx
}

func (tc TestCase) RunTxCmd(s *Suite) {
s.Run(tc.name, func() {
ctx := svrcmd.CreateExecuteContext(context.Background())

cmd := cli.GetTxCmd()
cmd.SetContext(ctx)
args := append(tc.args, commonTxArgs()...)
cmd.SetArgs(append(args, tc.extraArgs...))

s.Require().NoError(sdkclient.SetCmdClientContextHandler(tc.NewCtx(s), cmd))

err := cmd.Execute()
if tc.wantErr != "" {
s.Require().ErrorContains(err, tc.wantErr)
return
}
s.Require().NoError(err)
})
}

func (tc TestCase) RunQueryCmd(s *Suite) {
s.Run(tc.name, func() {
ctx := svrcmd.CreateExecuteContext(context.Background())

cmd := cli.GetQueryCmd()
cmd.SetContext(ctx)
args := tc.args // don't append common tx args
cmd.SetArgs(append(args, tc.extraArgs...))

s.Require().NoError(sdkclient.SetCmdClientContextHandler(tc.NewCtx(s), cmd))

err := cmd.Execute()
if tc.wantErr != "" {
s.Require().ErrorContains(err, tc.wantErr)
return
}
s.Require().NoError(err)
})
}
173 changes: 173 additions & 0 deletions x/evm/cli/cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package cli_test

import (
"fmt"
"math/big"

gethcommon "github.com/ethereum/go-ethereum/common"

"github.com/NibiruChain/nibiru/v2/x/evm"
"github.com/NibiruChain/nibiru/v2/x/evm/evmtest"
)

var (
dummyAccs = evmtest.NewEthPrivAccs(3)
dummyEthAddr = dummyAccs[1].EthAddr.Hex()
dummyFuntoken = evm.NewFunToken(
gethcommon.BigToAddress(big.NewInt(123)),
"ibc/testtoken",
false,
)
)

func (s *Suite) TestCmdConvertCoinToEvm() {
testCases := []TestCase{
{
name: "happy: convert-coin-to-evm",
args: []string{
"convert-coin-to-evm",
dummyEthAddr,
fmt.Sprintf("%d%s", 123, dummyFuntoken.BankDenom),
},
extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)},
wantErr: "",
},
{
name: "sad: coin format",
args: []string{
"convert-coin-to-evm",
dummyAccs[1].EthAddr.Hex(),
fmt.Sprintf("%s %d", dummyFuntoken.BankDenom, 123),
},
extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)},
wantErr: "invalid decimal coin expression",
},
}

for _, tc := range testCases {
tc.RunTxCmd(s)
}
}

func (s *Suite) TestCmdCreateFunToken() {
testCases := []TestCase{
{
name: "happy: create-funtoken (erc20)",
args: []string{
"create-funtoken",
fmt.Sprintf("--erc20=%s", dummyEthAddr),
},
extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)},
wantErr: "",
},
{
name: "happy: create-funtoken (bank coin)",
args: []string{
"create-funtoken",
fmt.Sprintf("--bank-denom=%s", dummyFuntoken.BankDenom),
},
extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)},
wantErr: "",
},
{
name: "sad: too many args",
args: []string{
"create-funtoken",
fmt.Sprintf("--erc20=%s", dummyEthAddr),
fmt.Sprintf("--bank-denom=%s", dummyFuntoken.BankDenom),
},
extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)},
wantErr: "exactly one of the flags --bank-denom or --erc20 must be specified",
},
}

for _, tc := range testCases {
tc.RunTxCmd(s)
}
}

func (s *Suite) TestCmdQueryAccount() {
testCases := []TestCase{
{
name: "happy: query account (bech32)",
args: []string{
"account",
dummyAccs[0].NibiruAddr.String(),
},
wantErr: "",
},
{
name: "happy: query account (eth hex)",
args: []string{
"account",
dummyAccs[0].EthAddr.Hex(),
},
wantErr: "",
},
{
name: "happy: query account (eth hex) --offline",
args: []string{
"account",
dummyAccs[0].EthAddr.Hex(),
"--offline",
},
wantErr: "",
},
{
name: "happy: query account (bech32) --offline",
args: []string{
"account",
dummyAccs[0].NibiruAddr.String(),
"--offline",
},
wantErr: "",
},
{
name: "sad: too many args",
args: []string{
"funtoken",
"arg1",
"arg2",
},
wantErr: "accepts 1 arg",
},
}

for _, tc := range testCases {
tc.RunQueryCmd(s)
}
}

func (s *Suite) TestCmdQueryFunToken() {
testCases := []TestCase{
{
name: "happy: query funtoken (bank coin denom)",
args: []string{
"funtoken",
dummyFuntoken.BankDenom,
},
wantErr: "",
},
{
name: "happy: query funtoken (erc20 addr)",
args: []string{
"funtoken",
dummyFuntoken.Erc20Addr.String(),
},
wantErr: "",
},
{
name: "sad: too many args",
args: []string{
"funtoken",
"arg1",
"arg2",
},
wantErr: "accepts 1 arg",
},
}

for _, tc := range testCases {
tc.RunQueryCmd(s)
}
}
Loading
Loading