0g-chain/app/ante/eip712_test.go
Nick DeLuca ab3cf7c994
feat!(precompile): Add registry and genesis tests (#1999)
* feat!(precompile): Add registry and genesis tests

Based on evgeniy-scherbina's work, this adds a new precompile module
which defines a contract moudule with an example noop contract that
will be will be used for implementing test functions.  In addition,
it defines a registry module that instantiates stateful precompile
contracts and associates them with an address in a global registry
defined in kava-labs/go-ethereum. See precompile/README.md for more
information.

The kava-labs/go-ethereum and kava-labs/etheremint replace statements
are updated to support these changes as well as an update to kvtool
which includes genesis state for the registry.NoopContractAddress and
initializes the contract's EthAccount with a non-zero sequence and
codehash set to keccak256(0x01), and sets the contract code to 0x01.
See tests/e2e/e2e_precompile_genesis_test.go for an overview of the
expected genesis state for an enabled precompile.

Co-authored-by: evgeniy-scherbina <evgeniy.shcherbina.es@gmail.com>

* chore: Precompile readme improvements

This fixes a typo (import -> important) and uses package terminology
instead of unclear module terminology.  This aligns best with golang
terminology were modules and packages are distinctly different and
modules are defined using go.mod.

* chore: Improve noop contract godoc

Add a more meaningful godoc where the noop contract is constructed.

* chore(e2e): Improve comments around query checks

Improve the clarity of comments around where the error is checked for
accounts and why it is not checked directly.

In addition, improve comment on why both grpc and rpc code is fetched
and where they are used.

---------

Co-authored-by: evgeniy-scherbina <evgeniy.shcherbina.es@gmail.com>
2024-08-09 09:55:31 -07:00

795 lines
26 KiB
Go

package ante_test
import (
"math/big"
"testing"
"time"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/crypto/tmhash"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
tmversion "github.com/cometbft/cometbft/proto/tendermint/version"
"github.com/cometbft/cometbft/version"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
"github.com/evmos/ethermint/ethereum/eip712"
"github.com/evmos/ethermint/tests"
etherminttypes "github.com/evmos/ethermint/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
"github.com/stretchr/testify/suite"
"github.com/kava-labs/kava/app"
cdptypes "github.com/kava-labs/kava/x/cdp/types"
evmutilkeeper "github.com/kava-labs/kava/x/evmutil/keeper"
evmutiltestutil "github.com/kava-labs/kava/x/evmutil/testutil"
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
hardtypes "github.com/kava-labs/kava/x/hard/types"
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
)
const (
ChainID = app.TestChainId
USDCCoinDenom = "erc20/usdc"
USDCCDPType = "erc20-usdc"
)
type EIP712TestSuite struct {
suite.Suite
tApp app.TestApp
ctx sdk.Context
evmutilKeeper evmutilkeeper.Keeper
clientCtx client.Context
ethSigner ethtypes.Signer
testAddr sdk.AccAddress
testAddr2 sdk.AccAddress
testPrivKey cryptotypes.PrivKey
testPrivKey2 cryptotypes.PrivKey
testEVMAddr evmutiltypes.InternalEVMAddress
testEVMAddr2 evmutiltypes.InternalEVMAddress
usdcEVMAddr evmutiltypes.InternalEVMAddress
}
func (suite *EIP712TestSuite) getEVMAmount(amount int64) sdkmath.Int {
incr := sdkmath.RelativePow(sdkmath.NewUint(10), sdkmath.NewUint(18), sdkmath.OneUint())
return sdkmath.NewInt(amount).Mul(sdkmath.NewIntFromUint64(incr.Uint64()))
}
func (suite *EIP712TestSuite) createTestEIP712CosmosTxBuilder(
from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msgs []sdk.Msg,
) client.TxBuilder {
var err error
nonce, err := suite.tApp.GetAccountKeeper().GetSequence(suite.ctx, from)
suite.Require().NoError(err)
pc, err := etherminttypes.ParseChainID(chainId)
suite.Require().NoError(err)
ethChainId := pc.Uint64()
// GenerateTypedData TypedData
fee := legacytx.NewStdFee(gas, gasAmount)
accNumber := suite.tApp.GetAccountKeeper().GetAccount(suite.ctx, from).GetAccountNumber()
data := eip712.ConstructUntypedEIP712Data(chainId, accNumber, nonce, 0, fee, msgs, "", nil)
typedData, err := eip712.WrapTxToTypedData(ethChainId, msgs, data, &eip712.FeeDelegationOptions{
FeePayer: from,
}, suite.tApp.GetEvmKeeper().GetParams(suite.ctx))
suite.Require().NoError(err)
sigHash, err := eip712.ComputeTypedDataHash(typedData)
suite.Require().NoError(err)
// Sign typedData
keyringSigner := tests.NewSigner(priv)
signature, pubKey, err := keyringSigner.SignByAddress(from, sigHash)
suite.Require().NoError(err)
signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
// Add ExtensionOptionsWeb3Tx extension
var option *codectypes.Any
option, err = codectypes.NewAnyWithValue(&etherminttypes.ExtensionOptionsWeb3Tx{
FeePayer: from.String(),
TypedDataChainID: ethChainId,
FeePayerSig: signature,
})
suite.Require().NoError(err)
suite.clientCtx.TxConfig.SignModeHandler()
txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder)
suite.Require().True(ok)
builder.SetExtensionOptions(option)
builder.SetFeeAmount(gasAmount)
builder.SetGasLimit(gas)
sigsV2 := signing.SignatureV2{
PubKey: pubKey,
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
},
Sequence: nonce,
}
err = builder.SetSignatures(sigsV2)
suite.Require().NoError(err)
err = builder.SetMsgs(msgs...)
suite.Require().NoError(err)
return builder
}
func (suite *EIP712TestSuite) SetupTest() {
tApp := app.NewTestApp()
suite.tApp = tApp
cdc := tApp.AppCodec()
suite.evmutilKeeper = tApp.GetEvmutilKeeper()
addr, privkey := tests.NewAddrKey()
suite.testAddr = sdk.AccAddress(addr.Bytes())
suite.testPrivKey = privkey
suite.testEVMAddr = evmutiltestutil.MustNewInternalEVMAddressFromString(addr.String())
addr2, privKey2 := tests.NewAddrKey()
suite.testPrivKey2 = privKey2
suite.testAddr2 = sdk.AccAddress(addr2.Bytes())
suite.testEVMAddr2 = evmutiltestutil.MustNewInternalEVMAddressFromString(addr2.String())
encodingConfig := app.MakeEncodingConfig()
suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
suite.ethSigner = ethtypes.LatestSignerForChainID(tApp.GetEvmKeeper().ChainID())
// Genesis states
evmGs := evmtypes.NewGenesisState(
evmtypes.NewParams(
"akava", // evmDenom
false, // allowedUnprotectedTxs
true, // enableCreate
true, // enableCall
evmtypes.DefaultChainConfig(), // ChainConfig
nil, // extraEIPs
nil, // eip712AllowedMsgs
nil, // enabledPrecompiles
),
nil,
)
feemarketGenesis := feemarkettypes.DefaultGenesisState()
feemarketGenesis.Params.EnableHeight = 1
feemarketGenesis.Params.NoBaseFee = false
cdpGenState := cdptypes.DefaultGenesisState()
cdpGenState.Params.GlobalDebtLimit = sdk.NewInt64Coin("usdx", 53000000000000)
cdpGenState.Params.CollateralParams = cdptypes.CollateralParams{
{
Denom: USDCCoinDenom,
Type: USDCCDPType,
LiquidationRatio: sdk.MustNewDecFromStr("1.01"),
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
StabilityFee: sdk.OneDec(),
AuctionSize: sdkmath.NewIntFromUint64(10000000000),
LiquidationPenalty: sdk.MustNewDecFromStr("0.05"),
CheckCollateralizationIndexCount: sdkmath.NewInt(10),
KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"),
SpotMarketID: "usdc:usd",
LiquidationMarketID: "usdc:usd:30",
ConversionFactor: sdkmath.NewInt(18),
},
}
hardGenState := hardtypes.DefaultGenesisState()
hardGenState.Params.MoneyMarkets = []hardtypes.MoneyMarket{
{
Denom: "usdx",
BorrowLimit: hardtypes.BorrowLimit{
HasMaxLimit: true,
MaximumLimit: sdk.MustNewDecFromStr("100000000000"),
LoanToValue: sdk.MustNewDecFromStr("1"),
},
SpotMarketID: "usdx:usd",
ConversionFactor: sdkmath.NewInt(1_000_000),
InterestRateModel: hardtypes.InterestRateModel{
BaseRateAPY: sdk.MustNewDecFromStr("0.05"),
BaseMultiplier: sdk.MustNewDecFromStr("2"),
Kink: sdk.MustNewDecFromStr("0.8"),
JumpMultiplier: sdk.MustNewDecFromStr("10"),
},
ReserveFactor: sdk.MustNewDecFromStr("0.05"),
KeeperRewardPercentage: sdk.ZeroDec(),
},
}
pricefeedGenState := pricefeedtypes.DefaultGenesisState()
pricefeedGenState.Params.Markets = []pricefeedtypes.Market{
{
MarketID: "usdx:usd",
BaseAsset: "usdx",
QuoteAsset: "usd",
Oracles: []sdk.AccAddress{},
Active: true,
},
{
MarketID: "usdc:usd",
BaseAsset: "usdc",
QuoteAsset: "usd",
Oracles: []sdk.AccAddress{},
Active: true,
},
{
MarketID: "usdc:usd:30",
BaseAsset: "usdc",
QuoteAsset: "usd",
Oracles: []sdk.AccAddress{},
Active: true,
},
}
pricefeedGenState.PostedPrices = []pricefeedtypes.PostedPrice{
{
MarketID: "usdx:usd",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("1.00"),
Expiry: time.Now().Add(1 * time.Hour),
},
{
MarketID: "usdc:usd",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("1.00"),
Expiry: time.Now().Add(1 * time.Hour),
},
{
MarketID: "usdc:usd:30",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("1.00"),
Expiry: time.Now().Add(1 * time.Hour),
},
}
genState := app.GenesisState{
evmtypes.ModuleName: cdc.MustMarshalJSON(evmGs),
feemarkettypes.ModuleName: cdc.MustMarshalJSON(feemarketGenesis),
cdptypes.ModuleName: cdc.MustMarshalJSON(&cdpGenState),
hardtypes.ModuleName: cdc.MustMarshalJSON(&hardGenState),
pricefeedtypes.ModuleName: cdc.MustMarshalJSON(&pricefeedGenState),
}
// funds our test accounts with some ukava
coinsGenState := app.NewFundedGenStateWithSameCoins(
tApp.AppCodec(),
sdk.NewCoins(sdk.NewInt64Coin("ukava", 1e9)),
[]sdk.AccAddress{suite.testAddr, suite.testAddr2},
)
tApp.InitializeFromGenesisStatesWithTimeAndChainID(
time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC),
ChainID,
genState,
coinsGenState,
)
// consensus key
consPriv, err := ethsecp256k1.GenerateKey()
suite.Require().NoError(err)
consAddress := sdk.ConsAddress(consPriv.PubKey().Address())
ctx := tApp.NewContext(false, tmproto.Header{
Height: tApp.LastBlockHeight() + 1,
ChainID: ChainID,
Time: time.Now().UTC(),
ProposerAddress: consAddress.Bytes(),
Version: tmversion.Consensus{
Block: version.BlockProtocol,
},
LastBlockId: tmproto.BlockID{
Hash: tmhash.Sum([]byte("block_id")),
PartSetHeader: tmproto.PartSetHeader{
Total: 11,
Hash: tmhash.Sum([]byte("partset_header")),
},
},
AppHash: tmhash.Sum([]byte("app")),
DataHash: tmhash.Sum([]byte("data")),
EvidenceHash: tmhash.Sum([]byte("evidence")),
ValidatorsHash: tmhash.Sum([]byte("validators")),
NextValidatorsHash: tmhash.Sum([]byte("next_validators")),
ConsensusHash: tmhash.Sum([]byte("consensus")),
LastResultsHash: tmhash.Sum([]byte("last_result")),
})
suite.ctx = ctx
// We need to set the validator as calling the EVM looks up the validator address
// https://github.com/evmos/ethermint/blob/f21592ebfe74da7590eb42ed926dae970b2a9a3f/x/evm/keeper/state_transition.go#L487
// evmkeeper.EVMConfig() will return error "failed to load evm config" if not set
valAcc := &etherminttypes.EthAccount{
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(consAddress.Bytes()), nil, 0, 0),
CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(),
}
tApp.GetAccountKeeper().SetAccount(ctx, valAcc)
_, testAddresses := app.GeneratePrivKeyAddressPairs(1)
valAddr := sdk.ValAddress(testAddresses[0].Bytes())
validator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{})
suite.Require().NoError(err)
err = tApp.GetStakingKeeper().SetValidatorByConsAddr(ctx, validator)
suite.Require().NoError(err)
tApp.GetStakingKeeper().SetValidator(ctx, validator)
// Deploy an ERC20 contract for USDC
contractAddr := suite.deployUSDCERC20(tApp, ctx)
pair := evmutiltypes.NewConversionPair(
contractAddr,
USDCCoinDenom,
)
suite.usdcEVMAddr = pair.GetAddress()
// update consensus params
cParams := tApp.GetConsensusParams(suite.ctx)
cParams.Block.MaxGas = sims.DefaultGenTxGas * 20
tApp.StoreConsensusParams(suite.ctx, cParams)
// Add a contract to evmutil conversion pair
evmutilParams := suite.evmutilKeeper.GetParams(suite.ctx)
evmutilParams.EnabledConversionPairs =
evmutiltypes.NewConversionPairs(
evmutiltypes.NewConversionPair(
// First contract evmutil module deploys
evmutiltestutil.MustNewInternalEVMAddressFromString("0x15932E26f5BD4923d46a2b205191C4b5d5f43FE3"),
"erc20/usdc",
),
)
suite.evmutilKeeper.SetParams(suite.ctx, evmutilParams)
// allow msgs through evm eip712
evmKeeper := suite.tApp.GetEvmKeeper()
params := evmKeeper.GetParams(suite.ctx)
params.EIP712AllowedMsgs = []evmtypes.EIP712AllowedMsg{
{
MsgTypeUrl: "/kava.evmutil.v1beta1.MsgConvertERC20ToCoin",
MsgValueTypeName: "MsgValueEVMConvertERC20ToCoin",
ValueTypes: []evmtypes.EIP712MsgAttrType{
{Name: "initiator", Type: "string"},
{Name: "receiver", Type: "string"},
{Name: "kava_erc20_address", Type: "string"},
{Name: "amount", Type: "string"},
},
},
{
MsgTypeUrl: "/kava.cdp.v1beta1.MsgCreateCDP",
MsgValueTypeName: "MsgValueCDPCreate",
ValueTypes: []evmtypes.EIP712MsgAttrType{
{Name: "sender", Type: "string"},
{Name: "collateral", Type: "Coin"},
{Name: "principal", Type: "Coin"},
{Name: "collateral_type", Type: "string"},
},
},
{
MsgTypeUrl: "/kava.cdp.v1beta1.MsgDeposit",
MsgValueTypeName: "MsgValueCDPDeposit",
ValueTypes: []evmtypes.EIP712MsgAttrType{
{Name: "depositor", Type: "string"},
{Name: "owner", Type: "string"},
{Name: "collateral", Type: "Coin"},
{Name: "collateral_type", Type: "string"},
},
},
{
MsgTypeUrl: "/kava.hard.v1beta1.MsgDeposit",
MsgValueTypeName: "MsgValueHardDeposit",
ValueTypes: []evmtypes.EIP712MsgAttrType{
{Name: "depositor", Type: "string"},
{Name: "amount", Type: "Coin[]"},
},
},
{
MsgTypeUrl: "/kava.evmutil.v1beta1.MsgConvertCoinToERC20",
MsgValueTypeName: "MsgValueEVMConvertCoinToERC20",
ValueTypes: []evmtypes.EIP712MsgAttrType{
{Name: "initiator", Type: "string"},
{Name: "receiver", Type: "string"},
{Name: "amount", Type: "Coin"},
},
},
{
MsgTypeUrl: "/kava.cdp.v1beta1.MsgRepayDebt",
MsgValueTypeName: "MsgValueCDPRepayDebt",
ValueTypes: []evmtypes.EIP712MsgAttrType{
{Name: "sender", Type: "string"},
{Name: "collateral_type", Type: "string"},
{Name: "payment", Type: "Coin"},
},
},
{
MsgTypeUrl: "/kava.hard.v1beta1.MsgWithdraw",
MsgValueTypeName: "MsgValueHardWithdraw",
ValueTypes: []evmtypes.EIP712MsgAttrType{
{Name: "depositor", Type: "string"},
{Name: "amount", Type: "Coin[]"},
},
},
}
evmKeeper.SetParams(suite.ctx, params)
// give test address 50k erc20 usdc to begin with
initBal := suite.getEVMAmount(50_000)
err = suite.evmutilKeeper.MintERC20(
ctx,
pair.GetAddress(), // contractAddr
suite.testEVMAddr, //receiver
initBal.BigInt(),
)
suite.Require().NoError(err)
err = suite.evmutilKeeper.MintERC20(
ctx,
pair.GetAddress(), // contractAddr
suite.testEVMAddr2, //receiver
initBal.BigInt(),
)
suite.Require().NoError(err)
// We need to commit so that the ethermint feemarket beginblock runs to set the minfee
// feeMarketKeeper.GetBaseFee() will return nil otherwise
suite.Commit()
// set base fee
suite.tApp.GetFeeMarketKeeper().SetBaseFee(suite.ctx, big.NewInt(100))
}
func (suite *EIP712TestSuite) Commit() {
_ = suite.tApp.Commit()
header := suite.ctx.BlockHeader()
header.Height += 1
suite.tApp.BeginBlock(abci.RequestBeginBlock{
Header: header,
})
// update ctx
suite.ctx = suite.tApp.NewContext(false, header)
}
func (suite *EIP712TestSuite) deployUSDCERC20(app app.TestApp, ctx sdk.Context) evmutiltypes.InternalEVMAddress {
// make sure module account is created
suite.tApp.FundModuleAccount(
suite.ctx,
evmutiltypes.ModuleName,
sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(0))),
)
contractAddr, err := suite.evmutilKeeper.DeployTestMintableERC20Contract(suite.ctx, "USDC", "USDC", uint8(18))
suite.Require().NoError(err)
suite.Require().Greater(len(contractAddr.Address), 0)
return contractAddr
}
func (suite *EIP712TestSuite) TestEIP712Tx() {
encodingConfig := app.MakeEncodingConfig()
testcases := []struct {
name string
usdcDepositAmt int64
usdxToMintAmt int64
updateTx func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder
updateMsgs func(msgs []sdk.Msg) []sdk.Msg
expectedCode uint32
failCheckTx bool
errMsg string
}{
{
name: "processes deposit eip712 messages successfully",
usdcDepositAmt: 100,
usdxToMintAmt: 99,
},
{
name: "fails when conversion more erc20 usdc than balance",
usdcDepositAmt: 51_000,
usdxToMintAmt: 100,
errMsg: "transfer amount exceeds balance",
},
{
name: "fails when minting more usdx than allowed",
usdcDepositAmt: 100,
usdxToMintAmt: 100,
errMsg: "proposed collateral ratio is below liquidation ratio",
},
{
name: "fails when trying to convert usdc for another address",
usdcDepositAmt: 100,
usdxToMintAmt: 90,
errMsg: "unauthorized",
failCheckTx: true,
updateMsgs: func(msgs []sdk.Msg) []sdk.Msg {
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
suite.testEVMAddr2,
suite.testAddr,
suite.usdcEVMAddr,
suite.getEVMAmount(100),
)
msgs[0] = &convertMsg
return msgs
},
},
{
name: "fails when trying to convert erc20 for non-whitelisted contract",
usdcDepositAmt: 100,
usdxToMintAmt: 90,
errMsg: "ERC20 token not enabled to convert to sdk.Coin",
updateMsgs: func(msgs []sdk.Msg) []sdk.Msg {
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
suite.testEVMAddr,
suite.testAddr,
suite.testEVMAddr2,
suite.getEVMAmount(100),
)
msgs[0] = &convertMsg
return msgs
},
},
{
name: "fails when signer tries to send messages with invalid signature",
usdcDepositAmt: 100,
usdxToMintAmt: 90,
failCheckTx: true,
errMsg: "tx intended signer does not match the given signer",
updateTx: func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder {
var option *codectypes.Any
option, _ = codectypes.NewAnyWithValue(&etherminttypes.ExtensionOptionsWeb3Tx{
FeePayer: suite.testAddr.String(),
TypedDataChainID: 2221,
FeePayerSig: []byte("sig"),
})
builder, _ := txBuilder.(authtx.ExtensionOptionsTxBuilder)
builder.SetExtensionOptions(option)
return txBuilder
},
},
{
name: "fails when insufficient gas fees are provided",
usdcDepositAmt: 100,
usdxToMintAmt: 90,
errMsg: "insufficient funds",
updateTx: func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder {
bk := suite.tApp.GetBankKeeper()
gasCoins := bk.GetBalance(suite.ctx, suite.testAddr, "ukava")
suite.tApp.GetBankKeeper().SendCoins(suite.ctx, suite.testAddr, suite.testAddr2, sdk.NewCoins(gasCoins))
return txBuilder
},
},
{
name: "fails when invalid chain id is provided",
usdcDepositAmt: 100,
usdxToMintAmt: 90,
failCheckTx: true,
errMsg: "invalid chain-id",
updateTx: func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder {
gasAmt := sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20)))
return suite.createTestEIP712CosmosTxBuilder(
suite.testAddr, suite.testPrivKey, "kavatest_12-1", uint64(sims.DefaultGenTxGas*10), gasAmt, msgs,
)
},
},
{
name: "fails when invalid fee payer is provided",
usdcDepositAmt: 100,
usdxToMintAmt: 90,
failCheckTx: true,
errMsg: "invalid pubkey",
updateTx: func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder {
gasAmt := sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20)))
return suite.createTestEIP712CosmosTxBuilder(
suite.testAddr2, suite.testPrivKey2, ChainID, uint64(sims.DefaultGenTxGas*10), gasAmt, msgs,
)
},
},
}
for _, tc := range testcases {
suite.Run(tc.name, func() {
suite.SetupTest()
// create messages to convert, mint, and deposit to lend
usdcAmt := suite.getEVMAmount(tc.usdcDepositAmt)
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
suite.testEVMAddr,
suite.testAddr,
suite.usdcEVMAddr,
usdcAmt,
)
usdxAmt := sdkmath.NewInt(1_000_000).Mul(sdkmath.NewInt(tc.usdxToMintAmt))
mintMsg := cdptypes.NewMsgCreateCDP(
suite.testAddr,
sdk.NewCoin(USDCCoinDenom, usdcAmt),
sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt),
USDCCDPType,
)
lendMsg := hardtypes.NewMsgDeposit(
suite.testAddr,
sdk.NewCoins(sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt)),
)
msgs := []sdk.Msg{
&convertMsg,
&mintMsg,
&lendMsg,
}
if tc.updateMsgs != nil {
msgs = tc.updateMsgs(msgs)
}
gasAmt := sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20)))
txBuilder := suite.createTestEIP712CosmosTxBuilder(
suite.testAddr, suite.testPrivKey, ChainID, uint64(sims.DefaultGenTxGas*10), gasAmt, msgs,
)
if tc.updateTx != nil {
txBuilder = tc.updateTx(txBuilder, msgs)
}
txBytes, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
suite.Require().NoError(err)
resCheckTx := suite.tApp.CheckTx(
abci.RequestCheckTx{
Tx: txBytes,
Type: abci.CheckTxType_New,
},
)
if !tc.failCheckTx {
suite.Require().Equal(resCheckTx.Code, uint32(0), resCheckTx.Log)
} else {
suite.Require().NotEqual(resCheckTx.Code, uint32(0), resCheckTx.Log)
suite.Require().Contains(resCheckTx.Log, tc.errMsg)
}
resDeliverTx := suite.tApp.DeliverTx(
abci.RequestDeliverTx{
Tx: txBytes,
},
)
if tc.errMsg == "" {
suite.Require().Equal(resDeliverTx.Code, uint32(0), resDeliverTx.Log)
// validate user cosmos erc20/usd balance
bk := suite.tApp.GetBankKeeper()
amt := bk.GetBalance(suite.ctx, suite.testAddr, USDCCoinDenom)
suite.Require().Equal(sdk.ZeroInt(), amt.Amount)
// validate cdp
cdp, found := suite.tApp.GetCDPKeeper().GetCdpByOwnerAndCollateralType(suite.ctx, suite.testAddr, USDCCDPType)
suite.Require().True(found)
suite.Require().Equal(suite.testAddr, cdp.Owner)
suite.Require().Equal(sdk.NewCoin(USDCCoinDenom, suite.getEVMAmount(100)), cdp.Collateral)
suite.Require().Equal(sdk.NewCoin("usdx", sdkmath.NewInt(99_000_000)), cdp.Principal)
// validate hard
hardDeposit, found := suite.tApp.GetHardKeeper().GetDeposit(suite.ctx, suite.testAddr)
suite.Require().True(found)
suite.Require().Equal(suite.testAddr, hardDeposit.Depositor)
suite.Require().Equal(sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(99_000_000))), hardDeposit.Amount)
} else {
suite.Require().NotEqual(resDeliverTx.Code, uint32(0), resCheckTx.Log)
suite.Require().Contains(resDeliverTx.Log, tc.errMsg)
}
})
}
}
func (suite *EIP712TestSuite) TestEIP712Tx_DepositAndWithdraw() {
encodingConfig := app.MakeEncodingConfig()
// create deposit msgs
usdcAmt := suite.getEVMAmount(100)
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
suite.testEVMAddr,
suite.testAddr,
suite.usdcEVMAddr,
usdcAmt,
)
usdxAmt := sdkmath.NewInt(1_000_000).Mul(sdkmath.NewInt(99))
mintMsg := cdptypes.NewMsgCreateCDP(
suite.testAddr,
sdk.NewCoin(USDCCoinDenom, usdcAmt),
sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt),
USDCCDPType,
)
lendMsg := hardtypes.NewMsgDeposit(
suite.testAddr,
sdk.NewCoins(sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt)),
)
depositMsgs := []sdk.Msg{
&convertMsg,
&mintMsg,
&lendMsg,
}
// deliver deposit msg
gasAmt := sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20)))
txBuilder := suite.createTestEIP712CosmosTxBuilder(
suite.testAddr, suite.testPrivKey, ChainID, uint64(sims.DefaultGenTxGas*10), gasAmt, depositMsgs,
)
txBytes, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
suite.Require().NoError(err)
resDeliverTx := suite.tApp.DeliverTx(
abci.RequestDeliverTx{
Tx: txBytes,
},
)
suite.Require().Equal(resDeliverTx.Code, uint32(0), resDeliverTx.Log)
// validate hard
hardDeposit, found := suite.tApp.GetHardKeeper().GetDeposit(suite.ctx, suite.testAddr)
suite.Require().True(found)
suite.Require().Equal(suite.testAddr, hardDeposit.Depositor)
suite.Require().Equal(sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(99_000_000))), hardDeposit.Amount)
// validate erc20 balance
coinBal, err := suite.evmutilKeeper.QueryERC20BalanceOf(suite.ctx, suite.usdcEVMAddr, suite.testEVMAddr)
suite.Require().NoError(err)
suite.Require().Equal(suite.getEVMAmount(49_900).BigInt(), coinBal)
// withdraw msgs
withdrawConvertMsg := evmutiltypes.NewMsgConvertCoinToERC20(
suite.testAddr.String(),
suite.testEVMAddr.String(),
sdk.NewCoin(USDCCoinDenom, usdcAmt),
)
cdpWithdrawMsg := cdptypes.NewMsgRepayDebt(
suite.testAddr,
USDCCDPType,
sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt),
)
hardWithdrawMsg := hardtypes.NewMsgWithdraw(
suite.testAddr,
sdk.NewCoins(sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt)),
)
withdrawMsgs := []sdk.Msg{
&hardWithdrawMsg,
&cdpWithdrawMsg,
&withdrawConvertMsg,
}
// deliver withdraw msg
txBuilder = suite.createTestEIP712CosmosTxBuilder(
suite.testAddr, suite.testPrivKey, ChainID, uint64(sims.DefaultGenTxGas*10), gasAmt, withdrawMsgs,
)
txBytes, err = encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
suite.Require().NoError(err)
resDeliverTx = suite.tApp.DeliverTx(
abci.RequestDeliverTx{
Tx: txBytes,
},
)
suite.Require().Equal(resDeliverTx.Code, uint32(0), resDeliverTx.Log)
// validate hard & cdp should be repayed
_, found = suite.tApp.GetHardKeeper().GetDeposit(suite.ctx, suite.testAddr)
suite.Require().False(found)
_, found = suite.tApp.GetCDPKeeper().GetCdpByOwnerAndCollateralType(suite.ctx, suite.testAddr, USDCCDPType)
suite.Require().False(found)
// validate user cosmos erc20/usd balance
bk := suite.tApp.GetBankKeeper()
amt := bk.GetBalance(suite.ctx, suite.testAddr, USDCCoinDenom)
suite.Require().Equal(sdk.ZeroInt(), amt.Amount)
// validate erc20 balance
coinBal, err = suite.evmutilKeeper.QueryERC20BalanceOf(suite.ctx, suite.usdcEVMAddr, suite.testEVMAddr)
suite.Require().NoError(err)
suite.Require().Equal(suite.getEVMAmount(50_000).BigInt(), coinBal)
}
func TestEIP712Suite(t *testing.T) {
suite.Run(t, new(EIP712TestSuite))
}