mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-27 00:35:18 +00:00
ff709d73e1
* add proto for allowed sdk denoms -> evm conversion * add validation for AllowedNativeCoinERC20Token * add validation for AllowedNativeCoinERC20Tokens * add AllowedNativeDenoms into params & genesis * add evmutil Params.Validate() test * fix eip712 ante test * update changelog * update internal testnet genesis.json * update state & param specs updates to the sections describing functionality will be updated once that functionality actually exists... :) * update field decimal -> decimals field now matches erc20 spec * add validation decimals will cast to uint8 * add v2 store migration for evmutil * create & register evmutil migrations * adds migrator to evmutil's keeper * sets up Migrate1To2 migration * registers migration in module * updates GetParams to properly handle historic block queries * add unit test for GetParams with historic store
788 lines
26 KiB
Go
788 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/simapp/helpers"
|
|
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"
|
|
|
|
"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"
|
|
abci "github.com/tendermint/tendermint/abci/types"
|
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
|
|
"github.com/tendermint/tendermint/version"
|
|
|
|
"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 = "kavatest_1-1"
|
|
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(ðerminttypes.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,
|
|
)
|
|
|
|
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 := ðerminttypes.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()
|
|
|
|
// 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 convertion 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(ðerminttypes.ExtensionOptionsWeb3Tx{
|
|
FeePayer: suite.testAddr.String(),
|
|
TypedDataChainID: 1,
|
|
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(helpers.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(helpers.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(helpers.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(helpers.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(helpers.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))
|
|
}
|