From 2b123bf0070eacf65c63377b594e6ed832bf76a1 Mon Sep 17 00:00:00 2001 From: Draco Li Date: Tue, 23 Aug 2022 13:04:40 -0400 Subject: [PATCH] Add EIP712 ante (#1267) * add eip712 ante * minor cleanup * eip712 integration test with bridge conversion * fix issues * update bridge module * merge bridge module convert logic * update eip712 tests & update deps * remove v17 migrations * remove v17 migrations * fix genesis test * fix erc20 to coin tx * remove eth check * clean up imports * remove * fix evmutil cli * remove bridge comments * address feedback * rename mint method * add transfer checks for locking & unlocking funds * fix gas * increase gas even more * fix amount check Co-authored-by: Ruaridh Co-authored-by: Ruaridh --- app/ante/ante.go | 42 +- app/ante/eip712_test.go | 777 ++++++ app/app.go | 8 +- app/test_common.go | 4 + docs/core/proto-docs.md | 201 ++ go.mod | 6 +- go.sum | 44 +- migrate/cmd.go | 46 +- migrate/v0_17/app.toml | 268 -- migrate/v0_17/cosmos.go | 35 - migrate/v0_17/cosmos_auth.go | 67 - migrate/v0_17/kava.go | 133 - migrate/v0_17/kava_committee.go | 148 -- migrate/v0_17/migrate.go | 52 - migrate/v0_17/migrate.md | 108 - migrate/v0_17/migrate_test.go | 200 -- migrate/v0_17/rollback.md | 45 - migrate/v0_17/testdata/genesis-v16.json | 2327 ---------------- migrate/v0_17/testdata/genesis-v17.json | 2366 ----------------- .../evmutil/v1beta1/conversion_pair.proto | 20 + proto/kava/evmutil/v1beta1/genesis.proto | 17 +- proto/kava/evmutil/v1beta1/query.proto | 26 + proto/kava/evmutil/v1beta1/tx.proto | 52 + x/evmutil/client/cli/address.go | 73 + x/evmutil/client/cli/evm.go | 129 + x/evmutil/client/cli/query.go | 63 + x/evmutil/client/cli/tx.go | 122 + x/evmutil/client/cli/tx_util.go | 86 + x/evmutil/genesis.go | 4 +- x/evmutil/genesis_test.go | 43 +- x/evmutil/keeper/conversion.go | 223 ++ x/evmutil/keeper/conversion_test.go | 345 +++ x/evmutil/keeper/erc20.go | 144 + x/evmutil/keeper/erc20_test.go | 86 + x/evmutil/keeper/evm.go | 152 ++ x/evmutil/keeper/grpc_query.go | 35 + x/evmutil/keeper/grpc_query_test.go | 37 + x/evmutil/keeper/invariants.go | 45 +- x/evmutil/keeper/invariants_test.go | 24 +- x/evmutil/keeper/keeper.go | 34 +- x/evmutil/keeper/msg_server.go | 104 + x/evmutil/keeper/msg_server_test.go | 266 ++ x/evmutil/keeper/params.go | 51 + x/evmutil/keeper/params_test.go | 36 + x/evmutil/module.go | 27 +- x/evmutil/testutil/suite.go | 358 ++- x/evmutil/types/address.go | 33 + x/evmutil/types/bytes.go | 25 + x/evmutil/types/bytes_test.go | 32 + x/evmutil/types/codec.go | 36 + x/evmutil/types/contract.go | 48 + x/evmutil/types/conversion_pair.go | 89 + x/evmutil/types/conversion_pair.pb.go | 428 +++ x/evmutil/types/conversion_pairs_test.go | 233 ++ x/evmutil/types/errors.go | 14 + .../ethermint_json/ERC20MintableBurnable.json | 4 + x/evmutil/types/events.go | 18 + x/evmutil/types/expected_keepers.go | 13 + x/evmutil/types/genesis.go | 9 +- x/evmutil/types/genesis.pb.go | 477 +++- x/evmutil/types/genesis_test.go | 39 +- x/evmutil/types/keys.go | 14 +- x/evmutil/types/msg.go | 146 + x/evmutil/types/msg_test.go | 199 ++ x/evmutil/types/params.go | 48 + x/evmutil/types/params_test.go | 75 + x/evmutil/types/query.pb.go | 638 +++++ x/evmutil/types/query.pb.gw.go | 148 ++ x/evmutil/types/tx.pb.go | 1378 ++++++++++ 69 files changed, 7738 insertions(+), 5885 deletions(-) create mode 100644 app/ante/eip712_test.go delete mode 100644 migrate/v0_17/app.toml delete mode 100644 migrate/v0_17/cosmos.go delete mode 100644 migrate/v0_17/cosmos_auth.go delete mode 100644 migrate/v0_17/kava.go delete mode 100644 migrate/v0_17/kava_committee.go delete mode 100644 migrate/v0_17/migrate.go delete mode 100644 migrate/v0_17/migrate.md delete mode 100644 migrate/v0_17/migrate_test.go delete mode 100644 migrate/v0_17/rollback.md delete mode 100644 migrate/v0_17/testdata/genesis-v16.json delete mode 100644 migrate/v0_17/testdata/genesis-v17.json create mode 100644 proto/kava/evmutil/v1beta1/conversion_pair.proto create mode 100644 proto/kava/evmutil/v1beta1/query.proto create mode 100644 proto/kava/evmutil/v1beta1/tx.proto create mode 100644 x/evmutil/client/cli/address.go create mode 100644 x/evmutil/client/cli/evm.go create mode 100644 x/evmutil/client/cli/query.go create mode 100644 x/evmutil/client/cli/tx.go create mode 100644 x/evmutil/client/cli/tx_util.go create mode 100644 x/evmutil/keeper/conversion.go create mode 100644 x/evmutil/keeper/conversion_test.go create mode 100644 x/evmutil/keeper/erc20.go create mode 100644 x/evmutil/keeper/erc20_test.go create mode 100644 x/evmutil/keeper/evm.go create mode 100644 x/evmutil/keeper/grpc_query.go create mode 100644 x/evmutil/keeper/grpc_query_test.go create mode 100644 x/evmutil/keeper/msg_server.go create mode 100644 x/evmutil/keeper/msg_server_test.go create mode 100644 x/evmutil/keeper/params.go create mode 100644 x/evmutil/keeper/params_test.go create mode 100644 x/evmutil/types/address.go create mode 100644 x/evmutil/types/bytes.go create mode 100644 x/evmutil/types/bytes_test.go create mode 100644 x/evmutil/types/codec.go create mode 100644 x/evmutil/types/contract.go create mode 100644 x/evmutil/types/conversion_pair.go create mode 100644 x/evmutil/types/conversion_pair.pb.go create mode 100644 x/evmutil/types/conversion_pairs_test.go create mode 100644 x/evmutil/types/errors.go create mode 100644 x/evmutil/types/ethermint_json/ERC20MintableBurnable.json create mode 100644 x/evmutil/types/events.go create mode 100644 x/evmutil/types/msg.go create mode 100644 x/evmutil/types/msg_test.go create mode 100644 x/evmutil/types/params.go create mode 100644 x/evmutil/types/params_test.go create mode 100644 x/evmutil/types/query.pb.go create mode 100644 x/evmutil/types/query.pb.gw.go create mode 100644 x/evmutil/types/tx.pb.go diff --git a/app/ante/ante.go b/app/ante/ante.go index cd435cab..5599f6f1 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -47,6 +47,12 @@ func (options HandlerOptions) Validate() error { return nil } +// cosmosHandlerOptions extends HandlerOptions to provide some Cosmos specific configurations +type cosmosHandlerOptions struct { + HandlerOptions + isEIP712 bool +} + // NewAnteHandler returns an 'AnteHandler' that will run actions before a tx is sent to a module's handler. func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { if err := options.Validate(); err != nil { @@ -63,11 +69,24 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { txWithExtensions, ok := tx.(authante.HasExtensionOptionsTx) if ok { opts := txWithExtensions.GetExtensionOptions() - if len(opts) > 0 { + if len(opts) > 1 { + return ctx, sdkerrors.Wrap( + sdkerrors.ErrInvalidRequest, + "rejecting tx with more than 1 extension option", + ) + } + + if len(opts) == 1 { switch typeURL := opts[0].GetTypeUrl(); typeURL { case "/ethermint.evm.v1.ExtensionOptionsEthereumTx": // handle as *evmtypes.MsgEthereumTx anteHandler = newEthAnteHandler(options) + case "/ethermint.types.v1.ExtensionOptionsWeb3Tx": + // handle as normal Cosmos SDK tx, except signature is checked for EIP712 representation + anteHandler = newCosmosAnteHandler(cosmosHandlerOptions{ + HandlerOptions: options, + isEIP712: true, + }) default: return ctx, sdkerrors.Wrapf( sdkerrors.ErrUnknownExtensionOptions, @@ -82,7 +101,10 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { // handle as totally normal Cosmos SDK tx switch tx.(type) { case sdk.Tx: - anteHandler = newCosmosAnteHandler(options) + anteHandler = newCosmosAnteHandler(cosmosHandlerOptions{ + HandlerOptions: options, + isEIP712: false, + }) default: return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) } @@ -91,17 +113,27 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { }, nil } -func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { +func newCosmosAnteHandler(options cosmosHandlerOptions) sdk.AnteHandler { decorators := []sdk.AnteDecorator{} decorators = append(decorators, evmante.RejectMessagesDecorator{}, // reject MsgEthereumTxs authante.NewSetUpContextDecorator(), // second decorator. SetUpContext must be called before other decorators - authante.NewRejectExtensionOptionsDecorator(), ) + + if !options.isEIP712 { + decorators = append(decorators, authante.NewRejectExtensionOptionsDecorator()) + } + if len(options.AddressFetchers) > 0 { decorators = append(decorators, NewAuthenticatedMempoolDecorator(options.AddressFetchers...)) } + + var sigVerification sdk.AnteDecorator = authante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler) + if options.isEIP712 { + sigVerification = evmante.NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler, options.EvmKeeper) + } + decorators = append(decorators, NewEvmMinGasFilter(options.EvmKeeper), // filter out evm denom from min-gas-prices authante.NewMempoolFeeDecorator(), @@ -118,7 +150,7 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { authante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators authante.NewValidateSigCountDecorator(options.AccountKeeper), authante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), - authante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), + sigVerification, authante.NewIncrementSequenceDecorator(options.AccountKeeper), // innermost AnteDecorator ibcante.NewAnteDecorator(options.IBCKeeper), ) diff --git a/app/ante/eip712_test.go b/app/ante/eip712_test.go new file mode 100644 index 00000000..4c1b4e10 --- /dev/null +++ b/app/ante/eip712_test.go @@ -0,0 +1,777 @@ +package ante_test + +import ( + "math/big" + "testing" + "time" + + "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/legacy/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/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/tharsis/ethermint/crypto/ethsecp256k1" + "github.com/tharsis/ethermint/ethereum/eip712" + "github.com/tharsis/ethermint/tests" + etherminttypes "github.com/tharsis/ethermint/types" + evmtypes "github.com/tharsis/ethermint/x/evm/types" + feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types" + + "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) sdk.Int { + incr := sdk.RelativePow(sdk.NewUint(10), sdk.NewUint(18), sdk.OneUint()) + return sdk.NewInt(amount).Mul(sdk.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, "") + 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", true, true, evmtypes.DefaultChainConfig()), + 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: sdk.NewIntFromUint64(10000000000), + LiquidationPenalty: sdk.MustNewDecFromStr("0.05"), + CheckCollateralizationIndexCount: sdk.NewInt(10), + KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"), + SpotMarketID: "usdc:usd", + LiquidationMarketID: "usdc:usd:30", + ConversionFactor: sdk.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: sdk.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/tharsis/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 + suite.evmutilKeeper.SetParams(suite.ctx, evmutiltypes.NewParams( + evmutiltypes.NewConversionPairs( + evmutiltypes.NewConversionPair( + // First contract evmutil module deploys + evmutiltestutil.MustNewInternalEVMAddressFromString("0x15932E26f5BD4923d46a2b205191C4b5d5f43FE3"), + "erc20/usdc", + ), + ), + )) + + // 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", sdk.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", sdk.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", sdk.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 := sdk.NewInt(1_000_000).Mul(sdk.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", sdk.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", sdk.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", sdk.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 := sdk.NewInt(1_000_000).Mul(sdk.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", sdk.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", sdk.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)) +} diff --git a/app/app.go b/app/app.go index 48d22168..212687cc 100644 --- a/app/app.go +++ b/app/app.go @@ -210,7 +210,7 @@ var ( govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, // used for secure addition and subtraction of balance using module account - evmutiltypes.ModuleName: nil, + evmutiltypes.ModuleName: {authtypes.Minter, authtypes.Burner}, kavadisttypes.KavaDistMacc: {authtypes.Minter}, auctiontypes.ModuleName: nil, issuancetypes.ModuleAccountName: {authtypes.Minter, authtypes.Burner}, @@ -392,6 +392,7 @@ func NewApp( feemarketSubspace := app.paramsKeeper.Subspace(feemarkettypes.ModuleName) evmSubspace := app.paramsKeeper.Subspace(evmtypes.ModuleName) bridgeSubspace := app.paramsKeeper.Subspace(bridgetypes.ModuleName) + evmutilSubspace := app.paramsKeeper.Subspace(evmutiltypes.ModuleName) earnSubspace := app.paramsKeeper.Subspace(earntypes.ModuleName) bApp.SetParamStore( @@ -491,6 +492,9 @@ func NewApp( app.evmutilKeeper = evmutilkeeper.NewKeeper( app.appCodec, keys[evmutiltypes.StoreKey], + evmutilSubspace, + app.bankKeeper, + app.accountKeeper, ) evmBankKeeper := evmutilkeeper.NewEvmBankKeeper(app.evmutilKeeper, app.bankKeeper, app.accountKeeper) @@ -500,6 +504,8 @@ func NewApp( options.EVMTrace, ) + app.evmutilKeeper.SetEvmKeeper(app.evmKeeper) + app.kavadistKeeper = kavadistkeeper.NewKeeper( appCodec, keys[kavadisttypes.StoreKey], diff --git a/app/test_common.go b/app/test_common.go index 33f95f8e..c301b8cf 100644 --- a/app/test_common.go +++ b/app/test_common.go @@ -22,12 +22,14 @@ import ( paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + bridgekeeper "github.com/kava-labs/kava-bridge/x/bridge/keeper" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmdb "github.com/tendermint/tm-db" evmkeeper "github.com/tharsis/ethermint/x/evm/keeper" + feemarketkeeper "github.com/tharsis/ethermint/x/feemarket/keeper" auctionkeeper "github.com/kava-labs/kava/x/auction/keeper" bep3keeper "github.com/kava-labs/kava/x/bep3/keeper" @@ -107,6 +109,8 @@ func (tApp TestApp) GetIncentiveKeeper() incentivekeeper.Keeper { return tApp.in func (tApp TestApp) GetEvmutilKeeper() evmutilkeeper.Keeper { return tApp.evmutilKeeper } func (tApp TestApp) GetEvmKeeper() *evmkeeper.Keeper { return tApp.evmKeeper } func (tApp TestApp) GetSavingsKeeper() savingskeeper.Keeper { return tApp.savingsKeeper } +func (tApp TestApp) GetBridgeKeeper() bridgekeeper.Keeper { return tApp.bridgeKeeper } +func (tApp TestApp) GetFeeMarketKeeper() feemarketkeeper.Keeper { return tApp.feeMarketKeeper } func (tApp TestApp) GetEarnKeeper() earnkeeper.Keeper { return tApp.earnKeeper } // LegacyAmino returns the app's amino codec. diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index a6391430..f4e5b06e 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -214,9 +214,27 @@ - [Msg](#kava.earn.v1beta1.Msg) +- [kava/evmutil/v1beta1/conversion_pair.proto](#kava/evmutil/v1beta1/conversion_pair.proto) + - [ConversionPair](#kava.evmutil.v1beta1.ConversionPair) + - [kava/evmutil/v1beta1/genesis.proto](#kava/evmutil/v1beta1/genesis.proto) - [Account](#kava.evmutil.v1beta1.Account) - [GenesisState](#kava.evmutil.v1beta1.GenesisState) + - [Params](#kava.evmutil.v1beta1.Params) + +- [kava/evmutil/v1beta1/query.proto](#kava/evmutil/v1beta1/query.proto) + - [QueryParamsRequest](#kava.evmutil.v1beta1.QueryParamsRequest) + - [QueryParamsResponse](#kava.evmutil.v1beta1.QueryParamsResponse) + + - [Query](#kava.evmutil.v1beta1.Query) + +- [kava/evmutil/v1beta1/tx.proto](#kava/evmutil/v1beta1/tx.proto) + - [MsgConvertCoinToERC20](#kava.evmutil.v1beta1.MsgConvertCoinToERC20) + - [MsgConvertCoinToERC20Response](#kava.evmutil.v1beta1.MsgConvertCoinToERC20Response) + - [MsgConvertERC20ToCoin](#kava.evmutil.v1beta1.MsgConvertERC20ToCoin) + - [MsgConvertERC20ToCoinResponse](#kava.evmutil.v1beta1.MsgConvertERC20ToCoinResponse) + + - [Msg](#kava.evmutil.v1beta1.Msg) - [kava/hard/v1beta1/hard.proto](#kava/hard/v1beta1/hard.proto) - [Borrow](#kava.hard.v1beta1.Borrow) @@ -3163,6 +3181,39 @@ Msg defines the earn Msg service. + +

Top

+ +## kava/evmutil/v1beta1/conversion_pair.proto + + + + + +### ConversionPair +ConversionPair defines a Kava ERC20 address and corresponding denom that is +allowed to be converted between ERC20 and sdk.Coin + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `kava_erc20_address` | [bytes](#bytes) | | ERC20 address of the token on the Kava EVM | +| `denom` | [string](#string) | | Denom of the corresponding sdk.Coin | + + + + + + + + + + + + + + +

Top

@@ -3195,6 +3246,22 @@ GenesisState defines the evmutil module's genesis state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `accounts` | [Account](#kava.evmutil.v1beta1.Account) | repeated | | +| `params` | [Params](#kava.evmutil.v1beta1.Params) | | params defines all the parameters of the module. | + + + + + + + + +### Params +Params defines the evmutil module params + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `enabled_conversion_pairs` | [ConversionPair](#kava.evmutil.v1beta1.ConversionPair) | repeated | enabled_conversion_pairs defines the list of conversion pairs allowed to be converted between Kava ERC20 and sdk.Coin | @@ -3210,6 +3277,140 @@ GenesisState defines the evmutil module's genesis state. + +

Top

+ +## kava/evmutil/v1beta1/query.proto + + + + + +### QueryParamsRequest +QueryParamsRequest defines the request type for querying x/evmutil parameters. + + + + + + + + +### QueryParamsResponse +QueryParamsResponse defines the response type for querying x/evmutil parameters. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#kava.evmutil.v1beta1.Params) | | | + + + + + + + + + + + + + + +### Query +Query defines the gRPC querier service for evmutil module + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Params` | [QueryParamsRequest](#kava.evmutil.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#kava.evmutil.v1beta1.QueryParamsResponse) | Params queries all parameters of the evmutil module. | GET|/kava/evmutil/v1beta1/params| + + + + + + +

Top

+ +## kava/evmutil/v1beta1/tx.proto + + + + + +### MsgConvertCoinToERC20 +MsgConvertCoinToERC20 defines a conversion from sdk.Coin to Kava ERC20. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `initiator` | [string](#string) | | Kava bech32 address initiating the conversion. | +| `receiver` | [string](#string) | | EVM 0x hex address that will receive the converted Kava ERC20 tokens. | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | Amount is the sdk.Coin amount to convert. | + + + + + + + + +### MsgConvertCoinToERC20Response +MsgConvertCoinToERC20Response defines the response value from Msg/ConvertCoinToERC20. + + + + + + + + +### MsgConvertERC20ToCoin +MsgConvertERC20ToCoin defines a conversion from Kava ERC20 to sdk.Coin. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `initiator` | [string](#string) | | EVM 0x hex address initiating the conversion. | +| `receiver` | [string](#string) | | Kava bech32 address that will receive the converted sdk.Coin. | +| `kava_erc20_address` | [string](#string) | | EVM 0x hex address of the ERC20 contract. | +| `amount` | [string](#string) | | ERC20 token amount to convert. | + + + + + + + + +### MsgConvertERC20ToCoinResponse +MsgConvertERC20ToCoinResponse defines the response value from +Msg/MsgConvertERC20ToCoin. + + + + + + + + + + + + + + +### Msg +Msg defines the evmutil Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `ConvertCoinToERC20` | [MsgConvertCoinToERC20](#kava.evmutil.v1beta1.MsgConvertCoinToERC20) | [MsgConvertCoinToERC20Response](#kava.evmutil.v1beta1.MsgConvertCoinToERC20Response) | ConvertCoinToERC20 defines a method for converting sdk.Coin to Kava ERC20. | | +| `ConvertERC20ToCoin` | [MsgConvertERC20ToCoin](#kava.evmutil.v1beta1.MsgConvertERC20ToCoin) | [MsgConvertERC20ToCoinResponse](#kava.evmutil.v1beta1.MsgConvertERC20ToCoinResponse) | ConvertERC20ToCoin defines a method for converting Kava ERC20 to sdk.Coin. | | + + + + +

Top

diff --git a/go.mod b/go.mod index 523af467..496b53b7 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.2 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 - github.com/kava-labs/kava-bridge v0.2.0 + github.com/kava-labs/kava-bridge v0.2.1-0.20220707160130-6199d30c5fe6 github.com/spf13/cast v1.4.1 github.com/spf13/cobra v1.4.0 github.com/stretchr/testify v1.7.1 @@ -158,7 +158,7 @@ replace ( // Use the cosmos keyring code github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 // Use cosmos-sdk fork with backported fix for unsafe-reset-all - github.com/cosmos/cosmos-sdk => github.com/kava-labs/cosmos-sdk v0.45.4-kava.1 + github.com/cosmos/cosmos-sdk => github.com/kava-labs/cosmos-sdk v0.45.4-kava.3 // See https://github.com/cosmos/cosmos-sdk/pull/10401, https://github.com/cosmos/cosmos-sdk/commit/0592ba6158cd0bf49d894be1cef4faeec59e8320 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 // Use the cosmos modified protobufs @@ -166,7 +166,7 @@ replace ( // Use rocksdb 7.1.2 github.com/tendermint/tm-db => github.com/kava-labs/tm-db v0.6.7-kava.1 // Use ethermint fork that respects min-gas-price with NoBaseFee true and london enabled - github.com/tharsis/ethermint => github.com/Kava-Labs/ethermint v0.14.0-kava-v17.3 + github.com/tharsis/ethermint => github.com/Kava-Labs/ethermint v0.14.0-kava-v17.5 // Make sure that we use grpc compatible with cosmos google.golang.org/grpc => google.golang.org/grpc v1.33.2 ) diff --git a/go.sum b/go.sum index e19513b0..f91b58f0 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= +bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= @@ -100,14 +101,9 @@ github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/Kava-Labs/ethermint v0.14.0-kava-v17.3 h1:4+Lg+qmJAL8ey9ns85WqxQkYCCKvN8RyuoxQ7CHJRPU= -github.com/Kava-Labs/ethermint v0.14.0-kava-v17.3/go.mod h1:k6rs7PRm04GhQZhCmwYLJGQ5czvgbZqcrqpR630+Rik= +github.com/Kava-Labs/ethermint v0.14.0-kava-v17.5 h1:oQH7YQ5MBExEbFQZ95UMdXimYLYHAc9su2yIsJ9+ISs= +github.com/Kava-Labs/ethermint v0.14.0-kava-v17.5/go.mod h1:/x9PysLnuLxE1/33pHf8W+TQJFJh7XE49g05uJoQ7kw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= @@ -135,6 +131,7 @@ github.com/adlio/schema v1.3.0 h1:eSVYLxYWbm/6ReZBCkLw4Fz7uqC+ZNoPvA39bOwi52A= github.com/adlio/schema v1.3.0/go.mod h1:51QzxkpeFs6lRY11kPye26IaFPOV+HqEj01t5aXXKfs= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -147,8 +144,6 @@ github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2uc github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= -github.com/aokoli/goutils v1.1.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -186,6 +181,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/binance-chain/tss-lib v1.3.3/go.mod h1:xfM6gCPA61WIV5q5tK9Acdv46n1QJLhXnZ4eD17hJpI= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= @@ -195,6 +191,7 @@ github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnC github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= @@ -313,6 +310,7 @@ github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6Uh github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/decred/dcrd/dcrec/edwards/v2 v2.0.0/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= @@ -363,9 +361,7 @@ github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= github.com/ethereum/go-ethereum v1.10.4/go.mod h1:nEE0TP5MtxGzOMd7egIrbPJMQBnhVU3ELNxhBglIzhg= github.com/ethereum/go-ethereum v1.10.16 h1:3oPrumn0bCW/idjcxMn5YYVCdK7VzJYIvwGZUGLEaoc= @@ -487,7 +483,6 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71 github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -682,8 +677,6 @@ github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25 github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= @@ -694,8 +687,6 @@ github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3 github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= @@ -775,10 +766,10 @@ github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kava-labs/cosmos-sdk v0.45.4-kava.1 h1:nGL6b6eracc8+FGWbJ/NexFGorI8j6DBMwZ6KvEqteA= -github.com/kava-labs/cosmos-sdk v0.45.4-kava.1/go.mod h1:WOqtDxN3eCCmnYLVla10xG7lEXkFjpTaqm2a2WasgCc= -github.com/kava-labs/kava-bridge v0.2.0 h1:5t9AsPUy9Riwi+gRLgozhv0dut/xQcvRF+yIB+wLuo8= -github.com/kava-labs/kava-bridge v0.2.0/go.mod h1:APqFV9V/rmkm3MnJ27R/CGOjteCX+3mVuRTYE70v7Yw= +github.com/kava-labs/cosmos-sdk v0.45.4-kava.3 h1:U4esIl4rzu9sApLFYGwbccPQTQdRg6C9exUVrokqSHY= +github.com/kava-labs/cosmos-sdk v0.45.4-kava.3/go.mod h1:WOqtDxN3eCCmnYLVla10xG7lEXkFjpTaqm2a2WasgCc= +github.com/kava-labs/kava-bridge v0.2.1-0.20220707160130-6199d30c5fe6 h1:uhdPatSr1VLKvVWTaphkielTlEWF/MvTwmp3uCHgjPo= +github.com/kava-labs/kava-bridge v0.2.1-0.20220707160130-6199d30c5fe6/go.mod h1:PNkTAuIw+PhMjlNr2EKPo2AUMcGNwcUIcJHaMhf+qAM= github.com/kava-labs/tm-db v0.6.7-kava.1 h1:7cVYlvWx1yP+gGdaAWcfm6NwMLzf4z6DxXguWn3+O3w= github.com/kava-labs/tm-db v0.6.7-kava.1/go.mod h1:HVZfZzWXuqWseXQVplxsWXK6kLHLkk3kQB6c+nuSZvk= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= @@ -918,7 +909,6 @@ github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -995,7 +985,6 @@ github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -1010,7 +999,6 @@ github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGg github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -1063,8 +1051,6 @@ github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= -github.com/mwitkow/go-proto-validators v0.3.2/go.mod h1:ej0Qp0qMgHN/KtDyUt+Q1/tA7a5VarXUOUxD+oeD30w= github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= @@ -1145,8 +1131,10 @@ github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ= github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/otiai10/primes v0.0.0-20180210170552-f6d2a1ba97c4/go.mod h1:UmSP7QeU3XmAdGu5+dnrTJqjBc+IscpVZkQzk473cjM= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= @@ -1236,9 +1224,6 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/pseudomuto/protoc-gen-doc v1.5.1/go.mod h1:XpMKYg6zkcpgfpCfQ8GcWBDRtRxOmMR5w7pz4Xo+dYM= -github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= -github.com/pseudomuto/protokit v0.2.1/go.mod h1:gt7N5Rz2flBzYafvaxyIxMZC0TTF5jDZfRnw25hAAyo= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/raulk/clock v1.1.0/go.mod h1:3MpVxdZ/ODBQDxbN+kzshf5OSZwPjtMDx6BBXBmOeY0= @@ -1375,7 +1360,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1973,7 +1957,6 @@ google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6 google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E= google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= @@ -1990,7 +1973,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/migrate/cmd.go b/migrate/cmd.go index ea9c5e75..769a7d21 100644 --- a/migrate/cmd.go +++ b/migrate/cmd.go @@ -4,52 +4,48 @@ import ( "encoding/json" "fmt" - "github.com/cosmos/cosmos-sdk/client" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/spf13/cobra" - tmjson "github.com/tendermint/tendermint/libs/json" tmtypes "github.com/tendermint/tendermint/types" "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app/params" - "github.com/kava-labs/kava/migrate/v0_17" ) // MigrateGenesisCmd returns a command to execute genesis state migration. func MigrateGenesisCmd() *cobra.Command { cmd := &cobra.Command{ Use: "migrate [genesis-file]", - Short: "Migrate genesis from v0.16 to v0.17", - Long: "Migrate the source genesis into v0.17 and print to STDOUT.", + Short: "Migrate genesis from v0.17 to v0.18", + Long: "Migrate the source genesis into v0.18 and print to STDOUT.", Example: fmt.Sprintf(`%s migrate /path/to/genesis.json`, version.AppName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.GetClientContextFromCmd(cmd) - importGenesis := args[0] + // clientCtx := client.GetClientContextFromCmd(cmd) + // importGenesis := args[0] - oldGenDoc, err := tmtypes.GenesisDocFromFile(importGenesis) - if err != nil { - return fmt.Errorf("failed to read genesis document from file %s: %w", importGenesis, err) - } + // oldGenDoc, err := tmtypes.GenesisDocFromFile(importGenesis) + // if err != nil { + // return fmt.Errorf("failed to read genesis document from file %s: %w", importGenesis, err) + // } - newGenDoc, err := v0_17.Migrate(oldGenDoc, clientCtx) - if err != nil { - return fmt.Errorf("failed to run migration: %w", err) - } + // newGenDoc, err := v0_17.Migrate(oldGenDoc, clientCtx) + // if err != nil { + // return fmt.Errorf("failed to run migration: %w", err) + // } - bz, err := tmjson.Marshal(newGenDoc) - if err != nil { - return fmt.Errorf("failed to marshal genesis doc: %w", err) - } + // bz, err := tmjson.Marshal(newGenDoc) + // if err != nil { + // return fmt.Errorf("failed to marshal genesis doc: %w", err) + // } - sortedBz, err := sdk.SortJSON(bz) - if err != nil { - return fmt.Errorf("failed to sort JSON genesis doc: %w", err) - } + // sortedBz, err := sdk.SortJSON(bz) + // if err != nil { + // return fmt.Errorf("failed to sort JSON genesis doc: %w", err) + // } - fmt.Println(string(sortedBz)) + // fmt.Println(string(sortedBz)) return nil }, } diff --git a/migrate/v0_17/app.toml b/migrate/v0_17/app.toml deleted file mode 100644 index 754c6832..00000000 --- a/migrate/v0_17/app.toml +++ /dev/null @@ -1,268 +0,0 @@ -# This is a TOML config file. -# For more information, see https://github.com/toml-lang/toml - -############################################################################### -### Base Configuration ### -############################################################################### - -# The minimum gas prices a validator is willing to accept for processing a -# transaction. A transaction's fees must meet the minimum of any denomination -# specified in this config (e.g. 0.25token1;0.0001token2). -minimum-gas-prices = "0.001ukava;1000000000akava" - -# default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals -# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) -# everything: all saved states will be deleted, storing only the current and previous state; pruning at 10 block intervals -# custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval' -pruning = "default" - -# These are applied if and only if the pruning strategy is custom. -pruning-keep-recent = "0" -pruning-keep-every = "0" -pruning-interval = "0" - -# HaltHeight contains a non-zero block height at which a node will gracefully -# halt and shutdown that can be used to assist upgrades and testing. -# -# Note: Commitment of state will be attempted on the corresponding block. -halt-height = 0 - -# HaltTime contains a non-zero minimum block time (in Unix seconds) at which -# a node will gracefully halt and shutdown that can be used to assist upgrades -# and testing. -# -# Note: Commitment of state will be attempted on the corresponding block. -halt-time = 0 - -# MinRetainBlocks defines the minimum block height offset from the current -# block being committed, such that all blocks past this offset are pruned -# from Tendermint. It is used as part of the process of determining the -# ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates -# that no blocks should be pruned. -# -# This configuration value is only responsible for pruning Tendermint blocks. -# It has no bearing on application state pruning which is determined by the -# "pruning-*" configurations. -# -# Note: Tendermint block pruning is dependant on this parameter in conunction -# with the unbonding (safety threshold) period, state pruning and state sync -# snapshot parameters to determine the correct minimum value of -# ResponseCommit.RetainHeight. -min-retain-blocks = 0 - -# InterBlockCache enables inter-block caching. -inter-block-cache = true - -# IndexEvents defines the set of events in the form {eventType}.{attributeKey}, -# which informs Tendermint what to index. If empty, all events will be indexed. -# -# Example: -# ["message.sender", "message.recipient"] -index-events = [] - -# IavlCacheSize set the size of the iavl tree cache. -# Default cache size is 50mb. -iavl-cache-size = 781250 - -############################################################################### -### Telemetry Configuration ### -############################################################################### - -[telemetry] - -# Prefixed with keys to separate services. -service-name = "" - -# Enabled enables the application telemetry functionality. When enabled, -# an in-memory sink is also enabled by default. Operators may also enabled -# other sinks such as Prometheus. -enabled = false - -# Enable prefixing gauge values with hostname. -enable-hostname = false - -# Enable adding hostname to labels. -enable-hostname-label = false - -# Enable adding service to labels. -enable-service-label = false - -# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink. -prometheus-retention-time = 0 - -# GlobalLabels defines a global set of name/value label tuples applied to all -# metrics emitted using the wrapper functions defined in telemetry package. -# -# Example: -# [["chain_id", "cosmoshub-1"]] -global-labels = [ -] - -############################################################################### -### API Configuration ### -############################################################################### - -[api] - -# Enable defines if the API server should be enabled. -enable = false - -# Swagger defines if swagger documentation should automatically be registered. -swagger = false - -# Address defines the API server to listen on. -address = "tcp://0.0.0.0:1317" - -# MaxOpenConnections defines the number of maximum open connections. -max-open-connections = 1000 - -# RPCReadTimeout defines the Tendermint RPC read timeout (in seconds). -rpc-read-timeout = 10 - -# RPCWriteTimeout defines the Tendermint RPC write timeout (in seconds). -rpc-write-timeout = 0 - -# RPCMaxBodyBytes defines the Tendermint maximum response body (in bytes). -rpc-max-body-bytes = 1000000 - -# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk). -enabled-unsafe-cors = false - -############################################################################### -### Rosetta Configuration ### -############################################################################### - -[rosetta] - -# Enable defines if the Rosetta API server should be enabled. -enable = false - -# Address defines the Rosetta API server to listen on. -address = ":8080" - -# Network defines the name of the blockchain that will be returned by Rosetta. -blockchain = "app" - -# Network defines the name of the network that will be returned by Rosetta. -network = "network" - -# Retries defines the number of retries when connecting to the node before failing. -retries = 3 - -# Offline defines if Rosetta server should run in offline mode. -offline = false - -############################################################################### -### gRPC Configuration ### -############################################################################### - -[grpc] - -# Enable defines if the gRPC server should be enabled. -enable = true - -# Address defines the gRPC server address to bind to. -address = "0.0.0.0:9090" - -############################################################################### -### gRPC Web Configuration ### -############################################################################### - -[grpc-web] - -# GRPCWebEnable defines if the gRPC-web should be enabled. -# NOTE: gRPC must also be enabled, otherwise, this configuration is a no-op. -enable = true - -# Address defines the gRPC-web server address to bind to. -address = "0.0.0.0:9091" - -# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk). -enable-unsafe-cors = false - -############################################################################### -### State Sync Configuration ### -############################################################################### - -# State sync snapshots allow other nodes to rapidly join the network without replaying historical -# blocks, instead downloading and applying a snapshot of the application state at a given height. -[state-sync] - -# snapshot-interval specifies the block interval at which local state sync snapshots are -# taken (0 to disable). Must be a multiple of pruning-keep-every. -snapshot-interval = 0 - -# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all). -snapshot-keep-recent = 2 - -############################################################################### -### EVM Configuration ### -############################################################################### - -[evm] - -# Tracer defines the 'vm.Tracer' type that the EVM will use when the node is run in -# debug mode. To enable tracing use the '--evm.tracer' flag when starting your node. -# Valid types are: json|struct|access_list|markdown -tracer = "" - -# MaxTxGasWanted defines the gas wanted for each eth tx returned in ante handler in check tx mode. -max-tx-gas-wanted = 500000 - -############################################################################### -### JSON RPC Configuration ### -############################################################################### - -[json-rpc] - -# Enable defines if the gRPC server should be enabled. -enable = true - -# Address defines the EVM RPC HTTP server address to bind to. -address = "0.0.0.0:8545" - -# Address defines the EVM WebSocket server address to bind to. -ws-address = "0.0.0.0:8546" - -# API defines a list of JSON-RPC namespaces that should be enabled -# Example: "eth,txpool,personal,net,debug,web3" -api = "eth,net,web3" - -# GasCap sets a cap on gas that can be used in eth_call/estimateGas (0=infinite). Default: 25,000,000. -gas-cap = 25000000 - -# EVMTimeout is the global timeout for eth_call. Default: 5s. -evm-timeout = "5s" - -# TxFeeCap is the global tx-fee cap for send transaction. Default: 1eth. -txfee-cap = 1 - -# FilterCap sets the global cap for total number of filters that can be created -filter-cap = 200 - -# FeeHistoryCap sets the global cap for total number of blocks that can be fetched -feehistory-cap = 100 - -# LogsCap defines the max number of results can be returned from single 'eth_getLogs' query. -logs-cap = 10000 - -# BlockRangeCap defines the max block range allowed for 'eth_getLogs' query. -block-range-cap = 10000 - -# HTTPTimeout is the read/write timeout of http json-rpc server. -http-timeout = "30s" - -# HTTPIdleTimeout is the idle timeout of http json-rpc server. -http-idle-timeout = "2m0s" - -############################################################################### -### TLS Configuration ### -############################################################################### - -[tls] - -# Certificate path defines the cert.pem file path for the TLS configuration. -certificate-path = "" - -# Key path defines the key.pem file path for the TLS configuration. -key-path = "" diff --git a/migrate/v0_17/cosmos.go b/migrate/v0_17/cosmos.go deleted file mode 100644 index 2f41fc1b..00000000 --- a/migrate/v0_17/cosmos.go +++ /dev/null @@ -1,35 +0,0 @@ -package v0_17 - -import ( - "time" - - "github.com/cosmos/cosmos-sdk/client" - - v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" - v040authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" -) - -func MigrateCosmosAppState(appState genutiltypes.AppMap, clientCtx client.Context, genesisTime time.Time) genutiltypes.AppMap { - appState = migrateV040(appState, clientCtx, genesisTime) - return appState -} - -// reset periodic vesting data for accounts -func migrateV040(appState genutiltypes.AppMap, clientCtx client.Context, genesisTime time.Time) genutiltypes.AppMap { - setConfigIfUnsealed() - - v040Codec := clientCtx.Codec - // reset periodic vesting data for accounts - if appState[v040auth.ModuleName] != nil { - // unmarshal relative source genesis application state - var authGenState v040authtypes.GenesisState - v040Codec.MustUnmarshalJSON(appState[v040auth.ModuleName], &authGenState) - - // reset periodic vesting data for accounts - appState[v040auth.ModuleName] = v040Codec.MustMarshalJSON(MigrateAuthV040(authGenState, genesisTime, clientCtx)) - } - - return appState -} diff --git a/migrate/v0_17/cosmos_auth.go b/migrate/v0_17/cosmos_auth.go deleted file mode 100644 index 4ca6ae8d..00000000 --- a/migrate/v0_17/cosmos_auth.go +++ /dev/null @@ -1,67 +0,0 @@ -package v0_17 - -import ( - "time" - - "github.com/cosmos/cosmos-sdk/client" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - v040auth "github.com/cosmos/cosmos-sdk/x/auth/types" - v040vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" - "github.com/kava-labs/kava/migrate/utils" -) - -// MigrateAuthV040 resets all periodic vesting accounts for a given -// v40 cosmos auth module genesis state, returning a copy of the original state where all -// periodic vesting accounts have been zeroed out. -func MigrateAuthV040(authGenState v040auth.GenesisState, genesisTime time.Time, ctx client.Context) *v040auth.GenesisState { - anyAccounts := make([]*codectypes.Any, len(authGenState.Accounts)) - for i, anyAcc := range authGenState.Accounts { - // Only need to make modifications to vesting accounts - if anyAcc.TypeUrl != "/cosmos.vesting.v1beta1.PeriodicVestingAccount" { - anyAccounts[i] = anyAcc - continue - } - var acc v040auth.GenesisAccount - if err := ctx.InterfaceRegistry.UnpackAny(anyAcc, &acc); err != nil { - panic(err) - } - if vacc, ok := acc.(*v040vesting.PeriodicVestingAccount); ok { - vestingPeriods := make([]v040vesting.Period, len(vacc.VestingPeriods)) - for j, period := range vacc.VestingPeriods { - vestingPeriods[j] = v040vesting.Period{ - Length: period.Length, - Amount: period.Amount, - } - } - vacc := v040vesting.PeriodicVestingAccount{ - BaseVestingAccount: vacc.BaseVestingAccount, - StartTime: vacc.StartTime, - VestingPeriods: vestingPeriods, - } - - utils.ResetPeriodicVestingAccount(&vacc, genesisTime) - - // If periodic vesting account has zero periods, convert back - // to a base account - if genesisTime.Unix() >= vacc.EndTime { - any, err := codectypes.NewAnyWithValue(vacc.BaseVestingAccount.BaseAccount) - if err != nil { - panic(err) - } - anyAccounts[i] = any - continue - } - // Convert back to any - any, err := codectypes.NewAnyWithValue(&vacc) - if err != nil { - panic(err) - } - anyAccounts[i] = any - } - } - - return &v040auth.GenesisState{ - Params: authGenState.Params, - Accounts: anyAccounts, - } -} diff --git a/migrate/v0_17/kava.go b/migrate/v0_17/kava.go deleted file mode 100644 index 932ec42e..00000000 --- a/migrate/v0_17/kava.go +++ /dev/null @@ -1,133 +0,0 @@ -package v0_17 - -import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/codec/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - authz "github.com/cosmos/cosmos-sdk/x/authz" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - evmtypes "github.com/tharsis/ethermint/x/evm/types" - feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types" - - evmutiltypes "github.com/kava-labs/kava/x/evmutil/types" - - bridgetypes "github.com/kava-labs/kava-bridge/x/bridge/types" - v016auction "github.com/kava-labs/kava/x/auction/legacy/v0_16" - v017auction "github.com/kava-labs/kava/x/auction/legacy/v0_17" - auctiontypes "github.com/kava-labs/kava/x/auction/types" - v017bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_17" - bep3types "github.com/kava-labs/kava/x/bep3/types" - cdptypes "github.com/kava-labs/kava/x/cdp/types" - committeetypes "github.com/kava-labs/kava/x/committee/types" - incentivetypes "github.com/kava-labs/kava/x/incentive/types" - savingstypes "github.com/kava-labs/kava/x/savings/types" -) - -func migrateAppState(appState genutiltypes.AppMap, clientCtx client.Context) { - interfaceRegistry := types.NewInterfaceRegistry() - v016auction.RegisterInterfaces(interfaceRegistry) - v16Codec := codec.NewProtoCodec(interfaceRegistry) - - codec := clientCtx.Codec - - // x/emvutil - evmUtilGenState := evmutiltypes.NewGenesisState([]evmutiltypes.Account{}) - appState[evmutiltypes.ModuleName] = codec.MustMarshalJSON(evmUtilGenState) - - // x/evm - evmChainConfig := evmtypes.DefaultChainConfig() - evmChainConfig.LondonBlock = nil - evmChainConfig.ArrowGlacierBlock = nil - evmChainConfig.MergeForkBlock = nil - - evmGenState := &evmtypes.GenesisState{ - Accounts: []evmtypes.GenesisAccount{}, - Params: evmtypes.Params{ - EvmDenom: "akava", - EnableCreate: true, - EnableCall: true, - ChainConfig: evmChainConfig, - ExtraEIPs: nil, - }, - } - appState[evmtypes.ModuleName] = codec.MustMarshalJSON(evmGenState) - - // x/bridge - bridgeGenState := bridgetypes.NewGenesisState( - bridgetypes.NewParams( - false, // Bridge disabled - bridgetypes.EnabledERC20Tokens{}, // No bridge ERC20 tokens - nil, // No relayer - bridgetypes.ConversionPairs{}, // No conversion pairs - ), - bridgetypes.ERC20BridgePairs{}, // Empty state as there has been no ERC20 contracts deployed - sdk.OneInt(), // NextWithdrawSequence starts at 1 - ) - appState[bridgetypes.ModuleName] = codec.MustMarshalJSON(&bridgeGenState) - - // x/feemarket - feemarketState := feemarkettypes.DefaultGenesisState() - // disable fee market and use minimum-gas-price instead of dynamic base fee - feemarketState.Params.NoBaseFee = true - appState[feemarkettypes.ModuleName] = codec.MustMarshalJSON(feemarketState) - - // x/authz - authzState := authz.DefaultGenesisState() - appState[authz.ModuleName] = codec.MustMarshalJSON(authzState) - - // x/cdp - if appState[cdptypes.ModuleName] != nil { - var genState cdptypes.GenesisState - codec.MustUnmarshalJSON(appState[cdptypes.ModuleName], &genState) - - genState.Params.GlobalDebtLimit = sdk.NewCoin("usdx", sdk.NewInt(393000000000000)) - encodedState := codec.MustMarshalJSON(&genState) - - appState[cdptypes.ModuleName] = encodedState - } - - // x/auction - if appState[auctiontypes.ModuleName] != nil { - var v16GenState v016auction.GenesisState - v16Codec.MustUnmarshalJSON(appState[auctiontypes.ModuleName], &v16GenState) - - migratedState := v017auction.Migrate(v16GenState) - encodedState := codec.MustMarshalJSON(migratedState) - - appState[auctiontypes.ModuleName] = encodedState - } - - // x/incentive - if appState[incentivetypes.ModuleName] != nil { - var incentiveState incentivetypes.GenesisState - codec.MustUnmarshalJSON(appState[incentivetypes.ModuleName], &incentiveState) - - appState[incentivetypes.ModuleName] = codec.MustMarshalJSON(&incentiveState) - } - - // x/savings - savingsState := savingstypes.DefaultGenesisState() - appState[savingstypes.ModuleName] = codec.MustMarshalJSON(&savingsState) - - // x/bep3 - if appState[bep3types.ModuleName] != nil { - var v16GenState bep3types.GenesisState - codec.MustUnmarshalJSON(appState[bep3types.ModuleName], &v16GenState) - - migratedState := v017bep3.Migrate(v16GenState) - - appState[bep3types.ModuleName] = codec.MustMarshalJSON(migratedState) - } - - // x/committee - if appState[committeetypes.ModuleName] != nil { - var genState committeetypes.GenesisState - codec.MustUnmarshalJSON(appState[committeetypes.ModuleName], &genState) - - migratedState := migrateCommitteePermissions(genState) - - appState[committeetypes.ModuleName] = codec.MustMarshalJSON(&migratedState) - } -} diff --git a/migrate/v0_17/kava_committee.go b/migrate/v0_17/kava_committee.go deleted file mode 100644 index 525cd093..00000000 --- a/migrate/v0_17/kava_committee.go +++ /dev/null @@ -1,148 +0,0 @@ -package v0_17 - -import ( - auctiontypes "github.com/kava-labs/kava/x/auction/types" - cdptypes "github.com/kava-labs/kava/x/cdp/types" - committeetypes "github.com/kava-labs/kava/x/committee/types" - hardtypes "github.com/kava-labs/kava/x/hard/types" -) - -func migrateCommitteePermissions(genState committeetypes.GenesisState) committeetypes.GenesisState { - var newCommittees committeetypes.Committees - for _, committee := range genState.GetCommittees() { - switch committee.GetDescription() { - case "Hard Governance Committee": - committee = fixHardPermissions(committee) - case "Kava Stability Committee": - committee = fixStabilityPermissions(committee) - } - newCommittees = append(newCommittees, committee) - } - - packedCommittees, err := committeetypes.PackCommittees(newCommittees) - if err != nil { - panic(err) - } - genState.Committees = packedCommittees - return genState -} - -func fixStabilityPermissions(committee committeetypes.Committee) committeetypes.Committee { - permissions := committee.GetPermissions() - - // get first params change permission in committee - var perm *committeetypes.ParamsChangePermission - var permIndex int - var found bool - for permIndex = range permissions { - p, ok := permissions[permIndex].(*committeetypes.ParamsChangePermission) - if ok { - perm = p - found = true - break - } - } - if !found { - panic("ParamsChangePermission not found") - } - - appendSubparamRequirement( - perm.AllowedParamsChanges, - hardtypes.ModuleName, string(hardtypes.KeyMoneyMarkets), - committeetypes.SubparamRequirement{ - Key: "denom", - Val: "ibc/799FDD409719A1122586A629AE8FCA17380351A51C1F47A80A1B8E7F2A491098", - AllowedSubparamAttrChanges: []string{ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id", - }, - }, - ) - - appendSubparamRequirement( - perm.AllowedParamsChanges, - cdptypes.ModuleName, string(cdptypes.KeyCollateralParams), - committeetypes.SubparamRequirement{ - Key: "type", - Val: "ust-a", - AllowedSubparamAttrChanges: []string{ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee", - }, - }, - ) - - perm.AllowedParamsChanges.Delete(auctiontypes.ModuleName, "BidDuration") - - perm.AllowedParamsChanges.Set(committeetypes.AllowedParamsChange{ - Subspace: auctiontypes.ModuleName, - Key: string(auctiontypes.KeyForwardBidDuration), - }) - perm.AllowedParamsChanges.Set(committeetypes.AllowedParamsChange{ - Subspace: auctiontypes.ModuleName, - Key: string(auctiontypes.KeyReverseBidDuration), - }) - - // update committee - permissions[permIndex] = perm - committee.SetPermissions(permissions) - - return committee -} - -func fixHardPermissions(committee committeetypes.Committee) committeetypes.Committee { - permissions := committee.GetPermissions() - - // get first params change permission in committee - var perm *committeetypes.ParamsChangePermission - var permIndex int - var found bool - for permIndex = range permissions { - p, ok := permissions[permIndex].(*committeetypes.ParamsChangePermission) - if ok { - perm = p - found = true - break - } - } - if !found { - panic("ParamsChangePermission not found") - } - - appendSubparamRequirement( - perm.AllowedParamsChanges, - hardtypes.ModuleName, string(hardtypes.KeyMoneyMarkets), - committeetypes.SubparamRequirement{ - Key: "denom", - Val: "ibc/799FDD409719A1122586A629AE8FCA17380351A51C1F47A80A1B8E7F2A491098", - AllowedSubparamAttrChanges: []string{ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id", - }, - }, - ) - - // update committee - permissions[permIndex] = perm - committee.SetPermissions(permissions) - - return committee -} - -func appendSubparamRequirement(allowed committeetypes.AllowedParamsChanges, subspace, key string, requirement committeetypes.SubparamRequirement) { - apc, found := allowed.Get(subspace, key) - if !found { - panic("AllowedParamsChange not found") - } - apc.MultiSubparamsRequirements = append(apc.MultiSubparamsRequirements, requirement) - allowed.Set(apc) -} diff --git a/migrate/v0_17/migrate.go b/migrate/v0_17/migrate.go deleted file mode 100644 index 384ac798..00000000 --- a/migrate/v0_17/migrate.go +++ /dev/null @@ -1,52 +0,0 @@ -package v0_17 - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/cosmos/cosmos-sdk/client" - sdk "github.com/cosmos/cosmos-sdk/types" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/kava-labs/kava/app" - tmtypes "github.com/tendermint/tendermint/types" -) - -var ( - // TODO: needs verification before release - GenesisTime = time.Date(2022, 5, 25, 17, 0, 0, 0, time.UTC) - ChainID = "kava_2222-10" -) - -func setConfigIfUnsealed() { - config := sdk.GetConfig() - if config.GetBech32AccountAddrPrefix() == "kava" { - return - } - app.SetSDKConfig() -} - -// Migrate converts v16 genesis doc to v17 genesis doc -func Migrate(genDoc *tmtypes.GenesisDoc, ctx client.Context) (*tmtypes.GenesisDoc, error) { - setConfigIfUnsealed() - - var appState genutiltypes.AppMap - var err error - if err := json.Unmarshal(genDoc.AppState, &appState); err != nil { - return nil, fmt.Errorf("failed to unmarshal app state from genesis doc: %w", err) - } - - MigrateCosmosAppState(appState, ctx, GenesisTime) - migrateAppState(appState, ctx) - - genDoc.AppState, err = json.Marshal(appState) - if err != nil { - return nil, err - } - - genDoc.GenesisTime = GenesisTime - genDoc.ChainID = ChainID - genDoc.InitialHeight = 1 - - return genDoc, nil -} diff --git a/migrate/v0_17/migrate.md b/migrate/v0_17/migrate.md deleted file mode 100644 index ab2f3d0d..00000000 --- a/migrate/v0_17/migrate.md +++ /dev/null @@ -1,108 +0,0 @@ -# kava-10 Upgrade Instructions - -## Software Version and Key Dates - -- We will be upgrading from chain-id "kava-9" to chain-id "kava_2222-10". -- The version of Kava for kava-10 is v0.17.3 -- The kava-9 chain will be shutdown with a `SoftwareUpgradeProposal` that activates at block height **1610471**, which is approximately 15:00 UTC on May, 25 2022. -- kava-10 genesis time is set to May 25, 2022 at 17:00 UTC -- The version of cosmos-sdk for kava-10 is v0.45.3 -- The version of tendermint for kava-10 v0.34.19 -- The minimum version of golang for kava-10 is **1.17+**. - -### Risks - -As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and being slashed. The most important piece of this procedure is verifying your software version and genesis file hash before starting your validator and signing. - -The riskiest thing a validator can do is discover that they made a mistake and repeat the upgrade procedure again during the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start before correcting it. If the network is halted and you have started with a different genesis file than the expected one, seek advice from a Kava developer before resetting your validator. - -### Recovery - -Prior to exporting kava-9 state, validators are encouraged to take a full data snapshot at the export height before proceeding. Snap-shotting depends heavily on infrastructure, but generally this can be done by backing up the .kava directory. - -It is critically important to back-up the .kava/data/priv_validator_state.json file after stopping your kava process. This file is updated every block as your validator participates in consensus rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails and the previous chain needs to be restarted. - -In the event that the upgrade does not succeed, validators and operators must downgrade back to v0.16.x of the Kava software and restore to their latest snapshot before restarting their nodes. - -## Upgrade Procedure - -### Before the upgrade - -Kava Labs has submitted a `SoftwareUpgradeProposal` that specifies block height **1610471** as the final block height for kava-9. This height corresponds to approximately 15:00 UTC on May 25th. Once the proposal passes, the chain will shutdown automatically at the specified height and does not require manual intervention by validators. - -### On the day of the upgrade - -**The kava chain is expected to halt at block height **1610471**, at approximately 15:00 UTC, and restart with new software at 17:00 UTC May 25th. Do not stop your node and begin the upgrade before 15:00 UTC on May 25th, or you may go offline and be unable to recover until after the upgrade!** - -**Make sure the kava process is stopped before proceeding and that you have backed up your validator**. Failure to backup your validator could make it impossible to restart your node if the upgrade fails. - -1. Export State (this **MUST** be done using **v0.16.x**) - -```sh -# verify version before export: -kava version --long -# name: kava -# server_name: kava -# version: 0.16.0 (any 0.16 version is fine) -# commit: 184ef2ad4127517828a4a04cc2c51594b66ac012 -# build_tags: netgo,ledger -# go: go version go1.17.1 linux/amd64 - -# export genesis using v0.16.x -kava export --for-zero-height --height 1610471 > export-genesis.json -``` - -**Note:** This can take a while! - -2. Update to kava-10 - -```sh - # in the `kava` folder - git pull - git checkout v0.17.3 - make install - - # verify versions - kava version --long - # name: kava - # server_name: kava - # version: v0.17.3 - # commit: [TBD] - # build_tags: netgo,ledger - # go: go version go1.17.1 linux/amd64 - - - # Migrate genesis state - kava migrate export-genesis.json > genesis.json - - # Verify output of genesis migration - kava validate-genesis genesis.json # should say it's valid - kava assert-invariants genesis.json # should say invariants pass - jq -S -c -M '' genesis.json | shasum -a 256 - # 3bc9829faf3beae2892ff1dfb7158f41a3f0cff303b4798777a559250a4dc815 - - # Restart node with migrated genesis state - cp genesis.json ~/.kava/config/genesis.json - kava tendermint unsafe-reset-all - - # Update app.toml - see section below - - # Restart node - - # ! Be sure to remove --halt-time flag if it is set in systemd/docker - kava start -``` - -kava v0.17 requires changes to app.toml: - -- There are 3 new sections - `evm`, `json-rpc`, `tls`. see the [default app.toml](app.toml) -- There is one addition to Base Configuration: `iavl-cache-size`. -- It is recommended to add a min gas price for evm txs in `akava` (where 1ukava = 10^12akava). eg `minimum-gas-prices = "0.001ukava;1000000000akava"` - -kava v0.17 requires changes to config.toml: - -- Depending on your setup, you will need to update the `seeds` and/or `persistent_peers` values in config.toml. These can be found [here](https://docs.google.com/spreadsheets/d/1s3LXLPFJazzdmwKdRv909pe_-ZV_Xbt2uCCNrhoOyIE). - - -### Coordination - -If the kava-10 chain does not launch by May 25, 2022 at 21:00 UTC, the launch should be considered a failure and validators should refer to the [rollback](./rollback.md) instructions to restart the previous kava-9 chain. In the event of launch failure, coordination will occur in the [Kava discord](https://discord.com/invite/kQzh3Uv). diff --git a/migrate/v0_17/migrate_test.go b/migrate/v0_17/migrate_test.go deleted file mode 100644 index 1dde6eeb..00000000 --- a/migrate/v0_17/migrate_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package v0_17 - -import ( - "io/ioutil" - "path/filepath" - "testing" - - bridgetypes "github.com/kava-labs/kava-bridge/x/bridge/types" - "github.com/kava-labs/kava/app" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/client" - sdk "github.com/cosmos/cosmos-sdk/types" - authz "github.com/cosmos/cosmos-sdk/x/authz" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - tmjson "github.com/tendermint/tendermint/libs/json" - tmtypes "github.com/tendermint/tendermint/types" - evmtypes "github.com/tharsis/ethermint/x/evm/types" - feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types" - - auctiontypes "github.com/kava-labs/kava/x/auction/types" - cdptypes "github.com/kava-labs/kava/x/cdp/types" - evmutiltypes "github.com/kava-labs/kava/x/evmutil/types" - incentivetypes "github.com/kava-labs/kava/x/incentive/types" - savingstypes "github.com/kava-labs/kava/x/savings/types" -) - -func TestMigrateGenesisDoc(t *testing.T) { - expected := getTestDataJSON("genesis-v17.json") - genDoc, err := tmtypes.GenesisDocFromFile(filepath.Join("testdata", "genesis-v16.json")) - assert.NoError(t, err) - - actualGenDoc, err := Migrate(genDoc, newClientContext()) - assert.NoError(t, err) - - actualJson, err := tmjson.Marshal(actualGenDoc) - assert.NoError(t, err) - - assert.JSONEq(t, expected, string(actualJson)) -} - -func TestMigrateEvmUtil(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate evmutiltypes.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[evmutiltypes.ModuleName], &genstate) - assert.NoError(t, err) - assert.Len(t, genstate.Accounts, 0) -} - -func TestMigrateEvm(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate evmtypes.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[evmtypes.ModuleName], &genstate) - - expectedChainConfig := evmtypes.DefaultChainConfig() - expectedChainConfig.LondonBlock = nil - expectedChainConfig.ArrowGlacierBlock = nil - expectedChainConfig.MergeForkBlock = nil - - assert.NoError(t, err) - assert.Len(t, genstate.Accounts, 0) - assert.Equal(t, evmtypes.Params{ - EvmDenom: "akava", - EnableCreate: true, - EnableCall: true, - ChainConfig: expectedChainConfig, - ExtraEIPs: []int64{}, - }, genstate.Params) -} - -func TestMigrateCDP(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate cdptypes.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[cdptypes.ModuleName], &genstate) - assert.NoError(t, err) - assert.Equal(t, genstate.Params.GlobalDebtLimit, sdk.NewCoin("usdx", sdk.NewInt(393000000000000))) -} - -func TestMigrateAuction(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate auctiontypes.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[auctiontypes.ModuleName], &genstate) - assert.NoError(t, err) - assert.Len(t, genstate.Auctions, 3) -} - -func TestMigrateFeeMarket(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate feemarkettypes.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[feemarkettypes.ModuleName], &genstate) - assert.NoError(t, err) - - expectedState := feemarkettypes.DefaultGenesisState() - expectedState.Params.NoBaseFee = true - assert.Equal(t, expectedState, &genstate) -} - -func TestMigrateAuthz(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate authz.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[authz.ModuleName], &genstate) - assert.NoError(t, err) - assert.Equal(t, genstate, authz.GenesisState{ - Authorization: []authz.GrantAuthorization{}, - }) -} - -func TestMigrateBridge(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate bridgetypes.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[bridgetypes.ModuleName], &genstate) - assert.NoError(t, err) - - assert.Len(t, genstate.ERC20BridgePairs, 0) - assert.Equal(t, genstate.NextWithdrawSequence, sdk.OneInt()) - assert.Equal(t, genstate.Params, bridgetypes.Params{ - BridgeEnabled: false, - EnabledERC20Tokens: bridgetypes.EnabledERC20Tokens{}, - Relayer: sdk.AccAddress{}, - EnabledConversionPairs: bridgetypes.ConversionPairs{}, - }) -} - -func TestMigrateIncentive(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate incentivetypes.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[incentivetypes.ModuleName], &genstate) - assert.NoError(t, err) - assert.Len(t, genstate.SavingsClaims, 0) - assert.Len(t, genstate.SavingsRewardState.AccumulationTimes, 0) - assert.Len(t, genstate.SavingsRewardState.MultiRewardIndexes, 0) - assert.Len(t, genstate.Params.SavingsRewardPeriods, 0) -} - -func TestMigrateSavings(t *testing.T) { - appMap, ctx := migrateToV17AndGetAppMap(t) - var genstate savingstypes.GenesisState - err := ctx.Codec.UnmarshalJSON(appMap[savingstypes.ModuleName], &genstate) - assert.NoError(t, err) - assert.Len(t, genstate.Deposits, 0) - assert.Equal(t, genstate.Params, savingstypes.Params{ - SupportedDenoms: []string{}, - }) -} - -func TestMigrateFull(t *testing.T) { - t.Skip() - - // File: https://s3.us-west-2.amazonaws.com/levi.testing.kava.io/kava-9-4-19-export-genesis.json - // Height: 1145621 - genDoc, err := tmtypes.GenesisDocFromFile(filepath.Join("testdata", "kava-9-4-19-export-genesis.json")) - assert.NoError(t, err) - ctx := newClientContext() - newGenDoc, err := Migrate(genDoc, ctx) - assert.NoError(t, err) - - var appMap genutiltypes.AppMap - err = tmjson.Unmarshal(newGenDoc.AppState, &appMap) - assert.NoError(t, err) - config := app.MakeEncodingConfig() - err = app.ModuleBasics.ValidateGenesis(ctx.Codec, config.TxConfig, appMap) - assert.NoError(t, err) - tApp := app.NewTestApp() - require.NotPanics(t, func() { - tApp.InitializeFromGenesisStatesWithTimeAndChainID(newGenDoc.GenesisTime, newGenDoc.ChainID, app.GenesisState(appMap)) - }) -} - -func migrateToV17AndGetAppMap(t *testing.T) (genutiltypes.AppMap, client.Context) { - genDoc, err := tmtypes.GenesisDocFromFile(filepath.Join("testdata", "genesis-v16.json")) - assert.NoError(t, err) - - ctx := newClientContext() - actualGenDoc, err := Migrate(genDoc, ctx) - assert.NoError(t, err) - - var appMap genutiltypes.AppMap - err = tmjson.Unmarshal(actualGenDoc.AppState, &appMap) - assert.NoError(t, err) - - return appMap, ctx -} - -func getTestDataJSON(filename string) string { - file := filepath.Join("testdata", filename) - data, err := ioutil.ReadFile(file) - if err != nil { - panic(err) - } - return string(data) -} - -func newClientContext() client.Context { - config := app.MakeEncodingConfig() - return client.Context{}. - WithCodec(config.Marshaler). - WithLegacyAmino(config.Amino). - WithInterfaceRegistry(config.InterfaceRegistry) -} diff --git a/migrate/v0_17/rollback.md b/migrate/v0_17/rollback.md deleted file mode 100644 index e50c568a..00000000 --- a/migrate/v0_17/rollback.md +++ /dev/null @@ -1,45 +0,0 @@ -# Kava-10 Rollback Instructions - -In the event that the Kava-10 relaunch is unsuccessful, we will restart the Kava-9 chain using the last known state. - -In order to restore the previous chain, the following data must be recovered by validators: - -- The database that contains the state of the previous chain (in ~/.kava/data by default) -- The priv_validator_state.json file of the validator (in ~/.kava/data by default) - -If you don't have the database data, the Kava developer team or another validator will share a copy of the database via Amazon s3 or a similar service. You will be able to download a copy of the data and verify it before starting your node. -If you don't have the backup priv_validator_state.json file, you will not have double sign protection on the first block. If this is the case, it's best to consult in the validator discord before starting your node. - -## Restoring state procedure - -1. Stop your node - -```sh -kava stop -``` - -2. Copy the contents of your backup data directory back to the $KAVA_HOME/data directory. By default this is ~/.kava/data. - -```sh -# Assumes backup is stored in "backup" directory -rm -rf ~/.kava/data -mv backup/.kava/data ~/.kava/data -``` - -3. Install the previous version of kava - -```sh -# from kava directory -git checkout v0.16.1 -make install -## verify version -kava version --long -``` - -4. Start kava process - -```sh -### be sure to remove --halt-time flag if it is set -sudo systemctl daemon-reload -sudo systemctl start kava -``` \ No newline at end of file diff --git a/migrate/v0_17/testdata/genesis-v16.json b/migrate/v0_17/testdata/genesis-v16.json deleted file mode 100644 index a60d6a49..00000000 --- a/migrate/v0_17/testdata/genesis-v16.json +++ /dev/null @@ -1,2327 +0,0 @@ -{ - "genesis_time": "2022-01-19T16:00:00Z", - "chain_id": "kava-9", - "initial_height": "1", - "consensus_params": { - "block": { - "max_bytes": "200000", - "max_gas": "20000000", - "time_iota_ms": "1000" - }, - "evidence": { - "max_age_num_blocks": "100000", - "max_age_duration": "172800000000000", - "max_bytes": "50000" - }, - "validator": { "pub_key_types": ["ed25519"] }, - "version": { - "app_version": "1" - } - }, - "app_hash": "", - "app_state": { - "incentive": { - "params": { - "usdx_minting_reward_periods": [ - { - "active": true, - "collateral_type": "bnb-a", - "start": "2021-07-20T14:00:00Z", - "end": "2024-10-16T14:00:00Z", - "rewards_per_second": { "denom": "ukava", "amount": "122354" } - }, - { - "active": true, - "collateral_type": "hard-a", - "start": "2021-07-20T14:00:00Z", - "end": "2024-10-16T14:00:00Z", - "rewards_per_second": { "denom": "ukava", "amount": "23809" } - } - ], - "hard_supply_reward_periods": [ - { - "active": true, - "collateral_type": "bnb", - "start": "2021-07-20T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [{ "denom": "hard", "amount": "123455" }] - }, - { - "active": true, - "collateral_type": "hard", - "start": "2021-07-20T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [{ "denom": "hard", "amount": "123455" }] - } - ], - "hard_borrow_reward_periods": [ - { - "active": true, - "collateral_type": "bnb", - "start": "2020-01-01T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [{ "denom": "hard", "amount": "12345" }] - }, - { - "active": true, - "collateral_type": "btcb", - "start": "2020-01-01T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [{ "denom": "hard", "amount": "12345" }] - } - ], - "delegator_reward_periods": [ - { - "active": true, - "collateral_type": "ukava", - "start": "2020-01-01T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [ - { "denom": "hard", "amount": "316880" }, - { "denom": "swp", "amount": "316880" } - ] - } - ], - "swap_reward_periods": [ - { - "active": true, - "collateral_type": "ukava:usdx", - "start": "2021-07-14T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [ - { "denom": "swp", "amount": "316880" }, - { "denom": "ukava", "amount": "31688" } - ] - }, - { - "active": true, - "collateral_type": "swp:usdx", - "start": "2021-07-14T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [ - { "denom": "swp", "amount": "616880" }, - { "denom": "ukava", "amount": "31688" } - ] - } - ], - "claim_multipliers": [ - { - "denom": "hard", - "multipliers": [ - { - "name": "small", - "months_lockup": "1", - "factor": "0.200000000000000000" - }, - { - "name": "large", - "months_lockup": "12", - "factor": "1.000000000000000000" - } - ] - }, - { - "denom": "swp", - "multipliers": [ - { - "name": "small", - "months_lockup": "1", - "factor": "0.100000000000000000" - }, - { - "name": "large", - "months_lockup": "12", - "factor": "1.000000000000000000" - } - ] - } - ], - "claim_end": "2025-01-01T00:00:00Z" - }, - "usdx_reward_state": { - "accumulation_times": [ - { - "collateral_type": "bnb-a", - "previous_accumulation_time": "2021-06-10T16:43:11.679705Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "bnb-a", - "reward_indexes": [ - { - "collateral_type": "ukava", - "reward_factor": "0.043949244534927716" - } - ] - } - ] - }, - "hard_supply_reward_state": { - "accumulation_times": [ - { - "collateral_type": "bnb", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z" - }, - { - "collateral_type": "hard", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "bnb", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "32.458112657412585027" - } - ] - }, - { - "collateral_type": "hard", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "35.000000000000000000" - } - ] - } - ] - }, - "hard_borrow_reward_state": { - "accumulation_times": [ - { - "collateral_type": "btcb", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "btcb", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "88.582200355378048199" - } - ] - } - ] - }, - "delegator_reward_state": { - "accumulation_times": [ - { - "collateral_type": "ukava", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "ukava", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "0.078380843036861815" - }, - { - "collateral_type": "swp", - "reward_factor": "0.013629025935176301" - } - ] - } - ] - }, - "swap_reward_state": { - "accumulation_times": [ - { - "collateral_type": "btcb-a", - "previous_accumulation_time": "2021-06-10T16:43:11.679705Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "btcb:usdx", - "reward_indexes": [ - { - "collateral_type": "swp", - "reward_factor": "6.145396761233172901" - } - ] - } - ] - }, - "usdx_minting_claims": [ - { - "base_claim": { - "owner": "kava1qptt5vu26cmxpmv0hf2tnnmf293x266pjcsjar", - "reward": { "denom": "ukava", "amount": "550" } - }, - "reward_indexes": [ - { - "collateral_type": "bnb-a", - "reward_factor": "0.043949244534927716" - }, - { - "collateral_type": "btcb-a", - "reward_factor": "0.046551281526135881" - } - ] - } - ], - "hard_liquidity_provider_claims": [ - { - "base_claim": { - "owner": "kava1qqqvdyv8w0xdu7gjdtt598q78gtgqyukct4yz2", - "reward": [{ "denom": "hard", "amount": "1747514" }] - }, - "supply_reward_indexes": [ - { - "collateral_type": "bnb", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "22.000000000000000000" - } - ] - }, - { - "collateral_type": "hard", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "30.000000000000000000" - } - ] - } - ], - "borrow_reward_indexes": [ - { - "collateral_type": "btc", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "88.582200355378048199" - } - ] - } - ] - }, - { - "base_claim": { - "owner": "kava1w2uj6rpejrma47vjx4rcghh3fndhmrvs6pmxph", - "reward": [ - { - "amount": "1747514", - "denom": "hard" - } - ] - }, - "borrow_reward_indexes": [], - "supply_reward_indexes": [] - } - ], - "delegator_claims": [ - { - "base_claim": { - "owner": "kava1qqqvdyv8w0xdu7gjdtt598q78gtgqyukct4yz2", - "reward": [] - }, - "reward_indexes": [ - { - "collateral_type": "ukava", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "0.000000000000000000" - }, - { - "collateral_type": "swp", - "reward_factor": "0.000000100000000000" - } - ] - } - ] - } - ], - "swap_claims": [ - { - "base_claim": { - "owner": "kava1qzease8mre5adak7wcc2twh6ryh9evnxpr6caj", - "reward": [{ "denom": "swp", "amount": "1960368" }] - }, - "reward_indexes": [ - { - "collateral_type": "busd:usdx", - "reward_indexes": [ - { - "collateral_type": "swp", - "reward_factor": "0.018083281889996318" - } - ] - } - ] - } - ] - }, - "hard": { - "params": { - "money_markets": [ - { - "denom": "usdx", - "borrow_limit": { - "has_max_limit": false, - "maximum_limit": "20000000.000000000000000000", - "loan_to_value": "0.800000000000000000" - }, - "spot_market_id": "usdx:usd", - "conversion_factor": "1000000", - "interest_rate_model": { - "base_rate_apy": "0.050000000000000000", - "base_multiplier": "0.100000000000000000", - "kink": "0.800000000000000000", - "jump_multiplier": "0.500000000000000000" - }, - "reserve_factor": "0.000000000000000000", - "keeper_reward_percentage": "0.050000000000000000" - }, - { - "denom": "ukava", - "borrow_limit": { - "has_max_limit": false, - "maximum_limit": "100000000000.000000000000000000", - "loan_to_value": "0.800000000000000000" - }, - "spot_market_id": "kava:usd", - "conversion_factor": "1000000", - "interest_rate_model": { - "base_rate_apy": "0.050000000000000000", - "base_multiplier": "2.000000000000000000", - "kink": "0.850000000000000000", - "jump_multiplier": "10.000000000000000000" - }, - "reserve_factor": "0.100000000000000000", - "keeper_reward_percentage": "0.010000000000000000" - }, - { - "denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "borrow_limit": { - "has_max_limit": true, - "maximum_limit": "25000000000.000000000000000000", - "loan_to_value": "0.500000000000000000" - }, - "spot_market_id": "atom:usd:30", - "conversion_factor": "1000000", - "interest_rate_model": { - "base_rate_apy": "0.000000000000000000", - "base_multiplier": "0.050000000000000000", - "kink": "0.800000000000000000", - "jump_multiplier": "5.000000000000000000" - }, - "reserve_factor": "0.025000000000000000", - "keeper_reward_percentage": "0.020000000000000000" - } - ], - "minimum_borrow_usd_value": "10.000000000000000000" - }, - "previous_accumulation_times": [ - { - "collateral_type": "btcb", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z", - "supply_interest_factor": "1.000205165357775358", - "borrow_interest_factor": "1.002233592784895182" - } - ], - "deposits": [ - { - "depositor": "kava1qq9ustlc0nv4aew275w93g4qy5zahqgxf5mwv9", - "amount": [ - { "denom": "bnb", "amount": "162103943" }, - { "denom": "btcb", "amount": "19428483" } - ], - "index": [{ "denom": "bnb", "value": "1.001740185031830285" }] - } - ], - "borrows": [ - { - "borrower": "kava1qq9ustlc0nv4aew275w93g4qy5zahqgxf5mwv9", - "amount": [ - { "denom": "usdx", "amount": "146724966" }, - { "denom": "xrpb", "amount": "541061835659" } - ], - "index": [ - { "denom": "usdx", "value": "1.000156840239586720" }, - { "denom": "xrpb", "value": "1.002063063678030789" } - ] - } - ], - "total_supplied": [{ "denom": "bnb", "amount": "1246173151758" }], - "total_borrowed": [{ "denom": "busd", "amount": "704609324351367" }], - "total_reserves": [{ "denom": "xrpb", "amount": "711656301126744" }] - }, - "pricefeed": { - "params": { - "markets": [ - { - "active": true, - "base_asset": "bnb", - "market_id": "bnb:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "bnb", - "market_id": "bnb:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "atom", - "market_id": "atom:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "atom", - "market_id": "atom:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "akt", - "market_id": "akt:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "akt", - "market_id": "akt:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "luna", - "market_id": "luna:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "luna", - "market_id": "luna:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "osmo", - "market_id": "osmo:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "osmo", - "market_id": "osmo:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "ust", - "market_id": "ust:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "ust", - "market_id": "ust:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - } - ] - }, - "posted_prices": [ - { - "expiry": "2022-07-20T00:00:00Z", - "market_id": "bnb:usd", - "oracle_address": "kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em", - "price": "215.962650000000001782" - }, - { - "expiry": "2022-07-20T00:00:00Z", - "market_id": "bnb:usd:30", - "oracle_address": "kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em", - "price": "217.962650000000001782" - } - ] - }, - "issuance": { - "params": { - "assets": [ - { - "blockable": true, - "blocked_addresses": [], - "denom": "hbtc", - "owner": "kava1dmm9zpdnm6mfhywzt9sstm4p33y0cnsd0m673z", - "paused": false, - "rate_limit": { - "active": false, - "limit": "0", - "time_period": "0s" - } - } - ] - }, - "supplies": [ - { - "current_supply": { "denom": "ukava", "amount": "100" }, - "time_elapsed": "3600s" - }, - { - "current_supply": { "denom": "bnb", "amount": "300" }, - "time_elapsed": "300s" - } - ] - }, - "cdp": { - "params": { - "collateral_params": [ - { - "denom": "bnb", - "type": "bnb-a", - "liquidation_ratio": "1.500000000000000000", - "debt_limit": { "denom": "usdx", "amount": "20000000000000" }, - "stability_fee": "1.000000000782997700", - "auction_size": "50000000000", - "liquidation_penalty": "0.050000000000000000", - "spot_market_id": "bnb:usd", - "liquidation_market_id": "bnb:usd:30", - "keeper_reward_percentage": "0.010000000000000000", - "check_collateralization_index_count": "10", - "conversion_factor": "8" - }, - { - "denom": "hbtc", - "type": "hbtc-a", - "liquidation_ratio": "1.500000000000000000", - "debt_limit": { "denom": "usdx", "amount": "10000000000000" }, - "stability_fee": "1.000000001547125958", - "auction_size": "1000000000000", - "liquidation_penalty": "0.075000000000000000", - "spot_market_id": "btc:usd", - "liquidation_market_id": "btc:usd:30", - "keeper_reward_percentage": "0.010000000000000000", - "check_collateralization_index_count": "10", - "conversion_factor": "8" - } - ], - "debt_param": { - "denom": "usdx", - "reference_asset": "usd", - "conversion_factor": "6", - "debt_floor": "10000000" - }, - "global_debt_limit": { "denom": "usdx", "amount": "43000000000000" }, - "surplus_auction_threshold": "500000000000", - "surplus_auction_lot": "10000000000", - "debt_auction_threshold": "100000000000", - "debt_auction_lot": "10000000000", - "circuit_breaker": false - }, - "cdps": [ - { - "id": "19", - "owner": "kava1u4p6a2yse66ewfndw4j7ppsa777vdcex3m4n9s", - "type": "bnb-a", - "collateral": { "denom": "bnb", "amount": "40911876" }, - "principal": { "denom": "usdx", "amount": "10016831" }, - "accumulated_fees": { "denom": "usdx", "amount": "36088" }, - "fees_updated": "2021-11-05T21:13:12.856088470Z", - "interest_factor": "1.002881426571765716" - }, - { - "id": "22", - "owner": "kava1e8xjfylam4nvugcmkxwvuxh22uvvad5vknu4yh", - "type": "bnb-a", - "collateral": { "denom": "bnb", "amount": "88268546" }, - "principal": { "denom": "usdx", "amount": "10000000" }, - "accumulated_fees": { "denom": "usdx", "amount": "156662" }, - "fees_updated": "2021-11-05T21:13:12.856088470Z", - "interest_factor": "1.002881426571765716" - } - ], - "deposits": [ - { - "cdp_id": "19", - "depositor": "kava1u4p6a2yse66ewfndw4j7ppsa777vdcex3m4n9s", - "amount": { "denom": "bnb", "amount": "40911876" } - }, - { - "cdp_id": "22", - "depositor": "kava1e8xjfylam4nvugcmkxwvuxh22uvvad5vknu4yh", - "amount": { "denom": "bnb", "amount": "88268546" } - } - ], - "starting_cdp_id": "1", - "debt_denom": "debt", - "gov_denom": "ukava", - "previous_accumulation_times": [ - { - "collateral_type": "bnb-a", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z", - "interest_factor": "1.002881426571765716" - } - ], - "total_principals": [ - { "collateral_type": "bnb-a", "total_principal": "9285009581820" } - ] - }, - "auction": { - "next_auction_id": "12", - "params": { - "max_auction_duration": "172800s", - "bid_duration": "600s", - "increment_surplus": "0.010000000000000000", - "increment_debt": "0.010000000000000000", - "increment_collateral": "0.010000000000000000" - }, - "auctions": [ - { - "@type": "/kava.auction.v1beta1.CollateralAuction", - "base_auction": { - "id": "3795", - "initiator": "hard", - "lot": { "denom": "bnb", "amount": "1" }, - "bidder": "", - "bid": { "denom": "bnb", "amount": "0" }, - "has_received_bids": false, - "end_time": "9000-01-01T00:00:00Z", - "max_end_time": "9000-01-01T00:00:00Z" - }, - "corresponding_debt": { "denom": "debt", "amount": "0" }, - "max_bid": { "denom": "bnb", "amount": "1" }, - "lot_returns": { - "addresses": ["kava1eevfnzkf2mt6feyttyzh6ektclauq7zlayefwf"], - "weights": ["100"] - } - }, - { - "@type": "/kava.auction.v1beta1.SurplusAuction", - "base_auction": { - "id": "3796", - "initiator": "hard", - "lot": { "denom": "bnb", "amount": "1" }, - "bidder": "", - "bid": { "denom": "bnb", "amount": "0" }, - "has_received_bids": false, - "end_time": "9000-01-01T00:00:00Z", - "max_end_time": "9000-01-01T00:00:00Z" - } - }, - { - "@type": "/kava.auction.v1beta1.DebtAuction", - "base_auction": { - "id": "3895", - "initiator": "hard", - "lot": { "denom": "bnb", "amount": "1" }, - "bidder": "", - "bid": { "denom": "bnb", "amount": "0" }, - "has_received_bids": false, - "end_time": "9000-01-01T00:00:00Z", - "max_end_time": "9000-01-01T00:00:00Z" - }, - "corresponding_debt": { "denom": "debt", "amount": "0" } - } - ] - }, - "auth": { - "params": { - "max_memo_characters": "256", - "tx_sig_limit": "7", - "tx_size_cost_per_byte": "10", - "sig_verify_cost_ed25519": "590", - "sig_verify_cost_secp256k1": "1000" - }, - "accounts": [ - { - "@type": "/cosmos.auth.v1beta1.ModuleAccount", - "base_account": { - "address": "kava1cj7njkw2g9fqx4e768zc75dp9sks8u9znxrf0w", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - "name": "kavadist", - "permissions": ["minter"] - }, - { - "@type": "/cosmos.auth.v1beta1.ModuleAccount", - "base_account": { - "address": "kava1a42xtwfzphuuu9mdp0es6633wu5mm8fhm789v3", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - "name": "hard", - "permissions": ["minter", "burner"] - }, - { - "@type": "/cosmos.auth.v1beta1.ModuleAccount", - "base_account": { - "address": "kava1mfru9azs5nua2wxcd4sq64g5nt7nn4n8s2w8cu", - "pub_key": null, - "account_number": "32", - "sequence": "0" - }, - "name": "swap", - "permissions": [] - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1ypjp0m04pyp73hwgtc0dgkx0e9rrydecm054da", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1agcvt07tcw0tglu0hmwdecsnuxp2yd45f3avgm", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1z3ytjpr6ancl8gw80z6f47z9smug7986x29vtj", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.vesting.v1beta1.PeriodicVestingAccount", - "base_vesting_account": { - "base_account": { - "address": "kava1jun8w7meyvgxl68fsjrsdhnv87zursv7dwlgee", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - "original_vesting": [ - { - "denom": "swp", - "amount": "14791312" - } - ], - "delegated_free": [], - "delegated_vesting": [], - "end_time": "1665767828" - }, - "start_time": "1642608000", - "vesting_periods": [ - { - "length": "23159828", - "amount": [ - { - "denom": "swp", - "amount": "14791312" - } - ] - } - ] - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1h932rkxxhwf0gz3gq23qsnedgax5vwuxw4ufhy", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1kla4wl0ccv7u85cemvs3y987hqk0afcv7vue84", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava14q5sawxdxtpap5x5sgzj7v4sp3ucncjlpuk3hs", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1tfvn5t8qwngqd2q427za2mel48pcus3z9u73fl", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1j9je7f6s0v6k7dmgv6u5k5ru202f5ffsc7af04", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1ektgdyy0z23qqnd67ns3qvfzgfgjd5xe82lf5c", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1g33w0mh4mjllhaj3y4dcwkwquxgwrma9ga5t94", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1ynf22ap74j6znl503a56y23x5stfr0aw5kntp8", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava173w2zz287s36ewnnkf4mjansnthnnsz7rtrxqc", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1fwfwmt6vupf3m9uvpdsuuc4dga8p5dtl4npcqz", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1sw54s6gq76acm35ls6m5c0kr93dstgrh6eftld", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1t4dvu32e309pzhmdn3aqcjlj79h9876plynrfm", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1wuzhkn2f8nqe2aprnwt3jkjvvr9m7dlkpumtz2", - "pub_key": null, - "account_number": "0", - "sequence": "0" - } - ] - }, - "bank": { - "params": { "send_enabled": [], "default_send_enabled": true }, - "balances": [ - { - "address": "kava1cj7njkw2g9fqx4e768zc75dp9sks8u9znxrf0w", - "coins": [ - { "denom": "hard", "amount": "1000000000000" }, - { "denom": "swp", "amount": "1000000000000" }, - { "denom": "ukava", "amount": "1000000000000" } - ] - }, - { - "address": "kava1a42xtwfzphuuu9mdp0es6633wu5mm8fhm789v3", - "coins": [{ "denom": "usdx", "amount": "1000000000" }] - }, - { - "address": "kava1mfru9azs5nua2wxcd4sq64g5nt7nn4n8s2w8cu", - "coins": [ - { "denom": "ukava", "amount": "1000000000000" }, - { "denom": "usdx", "amount": "4100000000000" } - ] - }, - { - "address": "kava1ypjp0m04pyp73hwgtc0dgkx0e9rrydecm054da", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1agcvt07tcw0tglu0hmwdecsnuxp2yd45f3avgm", - "coins": [ - { "denom": "bnb", "amount": "100000000000000" }, - { "denom": "ukava", "amount": "1000000000000" } - ] - }, - { - "address": "kava1z3ytjpr6ancl8gw80z6f47z9smug7986x29vtj", - "coins": [ - { "denom": "ukava", "amount": "565077579" }, - { "denom": "usdx", "amount": "1363200" } - ] - }, - { - "address": "kava1jun8w7meyvgxl68fsjrsdhnv87zursv7dwlgee", - "coins": [ - { "denom": "swp", "amount": "15915874" }, - { "denom": "ukava", "amount": "1000000" } - ] - }, - { - "address": "kava1h932rkxxhwf0gz3gq23qsnedgax5vwuxw4ufhy", - "coins": [{ "denom": "ukava", "amount": "2499984990625" }] - }, - { - "address": "kava1kla4wl0ccv7u85cemvs3y987hqk0afcv7vue84", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava14q5sawxdxtpap5x5sgzj7v4sp3ucncjlpuk3hs", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1tfvn5t8qwngqd2q427za2mel48pcus3z9u73fl", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1j9je7f6s0v6k7dmgv6u5k5ru202f5ffsc7af04", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1ektgdyy0z23qqnd67ns3qvfzgfgjd5xe82lf5c", - "coins": [ - { "denom": "bnb", "amount": "10000000000000000" }, - { "denom": "btcb", "amount": "10000000000000000" }, - { "denom": "busd", "amount": "10000000000000000" }, - { "denom": "hard", "amount": "1000000000000000000" }, - { "denom": "hbtc", "amount": "10000000000000000" }, - { "denom": "swp", "amount": "1000000000000000000" }, - { "denom": "ukava", "amount": "10000000000000000" }, - { "denom": "usdx", "amount": "100000000000000000" }, - { "denom": "xrpb", "amount": "10000000000000000" } - ] - }, - { - "address": "kava1g33w0mh4mjllhaj3y4dcwkwquxgwrma9ga5t94", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1ynf22ap74j6znl503a56y23x5stfr0aw5kntp8", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava173w2zz287s36ewnnkf4mjansnthnnsz7rtrxqc", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1fwfwmt6vupf3m9uvpdsuuc4dga8p5dtl4npcqz", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1sw54s6gq76acm35ls6m5c0kr93dstgrh6eftld", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1t4dvu32e309pzhmdn3aqcjlj79h9876plynrfm", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1wuzhkn2f8nqe2aprnwt3jkjvvr9m7dlkpumtz2", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - } - ], - "supply": [], - "denom_metadata": [] - }, - "bep3": { - "params": { - "asset_params": [ - { - "denom": "btcb", - "coin_id": "0", - "supply_limit": { - "limit": "100000000000", - "time_limited": false, - "time_period": "0s", - "time_based_limit": "0" - }, - "active": true, - "deputy_address": "kava1kla4wl0ccv7u85cemvs3y987hqk0afcv7vue84", - "fixed_fee": "2", - "min_swap_amount": "3", - "max_swap_amount": "2000000000", - "min_block_lock": "24686", - "max_block_lock": "86400" - }, - { - "denom": "xrpb", - "coin_id": "144", - "supply_limit": { - "limit": "2000000000000000", - "time_limited": false, - "time_period": "0s", - "time_based_limit": "0" - }, - "active": true, - "deputy_address": "kava14q5sawxdxtpap5x5sgzj7v4sp3ucncjlpuk3hs", - "fixed_fee": "100000", - "min_swap_amount": "100001", - "max_swap_amount": "250000000000000", - "min_block_lock": "24686", - "max_block_lock": "86400" - }, - { - "denom": "bnb", - "coin_id": "714", - "supply_limit": { - "limit": "100000000000000", - "time_limited": false, - "time_period": "0s", - "time_based_limit": "0" - }, - "active": true, - "deputy_address": "kava1agcvt07tcw0tglu0hmwdecsnuxp2yd45f3avgm", - "fixed_fee": "1000", - "min_swap_amount": "1001", - "max_swap_amount": "500000000000", - "min_block_lock": "24686", - "max_block_lock": "86400" - }, - { - "denom": "busd", - "coin_id": "727", - "supply_limit": { - "limit": "2000000000000000", - "time_limited": false, - "time_period": "0s", - "time_based_limit": "0" - }, - "active": true, - "deputy_address": "kava1j9je7f6s0v6k7dmgv6u5k5ru202f5ffsc7af04", - "fixed_fee": "20000", - "min_swap_amount": "20001", - "max_swap_amount": "100000000000000", - "min_block_lock": "24686", - "max_block_lock": "86400" - } - ] - }, - "atomic_swaps": [ - { - "amount": [ - { - "amount": "1999955998", - "denom": "btcb" - } - ], - "closed_block": "838115", - "cross_chain": true, - "direction": "SWAP_DIRECTION_INCOMING", - "expire_height": "838627", - "random_number_hash": "6F1CF8F2E13A0C0F0A359F54E47E4E265D766B8E006D2F00BDF994ABDEF1E9E4", - "recipient": "kava1fl2hs6y9vz986g5v52pdan9ga923n9mn5cxxkw", - "recipient_other_chain": "bnb1xz3xqf4p2ygrw9lhp5g5df4ep4nd20vsywnmpr", - "sender": "kava14qsmvzprqvhwmgql9fr0u3zv9n2qla8zhnm5pc", - "sender_other_chain": "bnb19k9wuv2j7c7ck8tmc7kav0r0cnt3esmkrpf25x", - "status": "SWAP_STATUS_COMPLETED", - "timestamp": "1636034914" - }, - { - "amount": [ - { - "amount": "19000000000", - "denom": "bnb" - } - ], - "closed_block": "1712118", - "cross_chain": true, - "direction": "SWAP_DIRECTION_OUTGOING", - "expire_height": "1736797", - "random_number_hash": "280EB832A37F2265CC82F3957CE603AAD57BAD7038B876A1F28953AFA29FA1C3", - "recipient": "kava1r4v2zdhdalfj2ydazallqvrus9fkphmglhn6u6", - "recipient_other_chain": "bnb18nsgj50zvc4uq93w4j0ltz5gaxhwv7aq4qnq0p", - "sender": "kava1zw6gg4ztvly7zf25pa33mclav3spvj3ympxxna", - "sender_other_chain": "bnb1jh7uv2rm6339yue8k4mj9406k3509kr4wt5nxn", - "status": "SWAP_STATUS_COMPLETED", - "timestamp": "1641976566" - }, - { - "amount": [ - { - "amount": "999595462080", - "denom": "busd" - } - ], - "closed_block": "787122", - "cross_chain": true, - "direction": "SWAP_DIRECTION_INCOMING", - "expire_height": "811799", - "random_number_hash": "BFB7CC82DA0E0C8556AC37843F5AB136B9A7A066054368F5948944282B414D83", - "recipient": "kava1eufgf0w9d7hf5mgtek4zr2upkxag9stmzx6unl", - "recipient_other_chain": "bnb10zq89008gmedc6rrwzdfukjk94swynd7dl97w8", - "sender": "kava1hh4x3a4suu5zyaeauvmv7ypf7w9llwlfufjmuu", - "sender_other_chain": "bnb1vl3wn4x8kqajg2j9wxa5y5amgzdxchutkxr6at", - "status": "SWAP_STATUS_EXPIRED", - "timestamp": "1635694492" - }, - { - "amount": [ - { - "amount": "999595462080", - "denom": "busd" - } - ], - "closed_block": "787122", - "cross_chain": true, - "direction": "SWAP_DIRECTION_OUTGOING", - "expire_height": "811799", - "random_number_hash": "BFB7CC82DA0E0C8556AC37843F5AB136B9A7A066054368F5948944282B414D83", - "recipient": "kava1hh4x3a4suu5zyaeauvmv7ypf7w9llwlfufjmuu", - "recipient_other_chain": "bnb1vl3wn4x8kqajg2j9wxa5y5amgzdxchutkxr6at", - "sender": "kava1eufgf0w9d7hf5mgtek4zr2upkxag9stmzx6unl", - "sender_other_chain": "bnb10zq89008gmedc6rrwzdfukjk94swynd7dl97w8", - "status": "SWAP_STATUS_EXPIRED", - "timestamp": "1635694492" - }, - { - "amount": [ - { - "amount": "1000000", - "denom": "btcb" - } - ], - "closed_block": "0", - "cross_chain": true, - "direction": "SWAP_DIRECTION_OUTGOING", - "expire_height": "1730589", - "random_number_hash": "A74EA1AB58D312FDF1E872D18583CACCF294E639DDA4F303939E9ADCEC081D93", - "recipient": "kava14qsmvzprqvhwmgql9fr0u3zv9n2qla8zhnm5pc", - "recipient_other_chain": "bnb1lhk5ndlgf5wz55t8k35cqj6h9l3m4l5ek2w7q6", - "sender": "kava1d2u28azje7rhqyjtxc2ex8q0cxxpw7dfm7ltq5", - "sender_other_chain": "bnb1xz3xqf4p2ygrw9lhp5g5df4ep4nd20vsywnmpr", - "status": "SWAP_STATUS_OPEN", - "timestamp": "1641934114" - }, - { - "amount": [ - { - "amount": "1000000", - "denom": "btcb" - } - ], - "closed_block": "0", - "cross_chain": true, - "direction": "SWAP_DIRECTION_INCOMING", - "expire_height": "1740000", - "random_number_hash": "39E9ADCEC081D93A74EA1A83CACCF294E639DDA4F3039B58D312FDF1E872D185", - "recipient": "kava1d2u28azje7rhqyjtxc2ex8q0cxxpw7dfm7ltq5", - "recipient_other_chain": "bnb1xz3xqf4p2ygrw9lhp5g5df4ep4nd20vsywnmpr", - "sender": "kava14qsmvzprqvhwmgql9fr0u3zv9n2qla8zhnm5pc", - "sender_other_chain": "bnb1lhk5ndlgf5wz55t8k35cqj6h9l3m4l5ek2w7q6", - "status": "SWAP_STATUS_OPEN", - "timestamp": "1641934114" - } - ], - "supplies": [ - { - "incoming_supply": { "denom": "bnb", "amount": "0" }, - "outgoing_supply": { "denom": "bnb", "amount": "0" }, - "current_supply": { "denom": "bnb", "amount": "30467559434006" }, - "time_limited_current_supply": { "denom": "bnb", "amount": "0" }, - "time_elapsed": "0s" - } - ], - "previous_block_time": "1970-01-01T00:00:00Z" - }, - "committee": { - "next_proposal_id": "1", - "committees": [ - { - "@type": "/kava.committee.v1beta1.MemberCommittee", - "base_committee": { - "id": "1", - "description": "Kava Stability Committee", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { - "@type": "/kava.committee.v1beta1.ParamsChangePermission", - "allowed_params_changes": [ - { - "subspace": "auction", - "key": "BidDuration", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "auction", - "key": "IncrementSurplus", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "auction", - "key": "IncrementDebt", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "auction", - "key": "IncrementCollateral", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "GlobalDebtLimit", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "SurplusThreshold", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "SurplusLot", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "DebtThreshold", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "DebtLot", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "DistributionFrequency", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "Active", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "kavadist", - "key": "Active", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "hard", - "key": "MinimumBorrowUSDValue", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "CollateralParams", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "type", - "val": "bnb-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "busd-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "busd-b", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "btcb-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "xrpb-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "ukava-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "hard-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "hbtc-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "swp-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - } - ] - }, - { - "subspace": "cdp", - "key": "DebtParam", - "single_subparam_allowed_attrs": ["debt_floor"], - "multi_subparams_requirements": [] - }, - { - "subspace": "bep3", - "key": "AssetParams", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "denom", - "val": "bnb", - "allowed_subparam_attr_changes": [ - "active", - "limit", - "max_swap_amount", - "min_block_lock" - ] - }, - { - "key": "denom", - "val": "busd", - "allowed_subparam_attr_changes": [ - "active", - "coin_id", - "limit", - "max_swap_amount", - "min_block_lock" - ] - }, - { - "key": "denom", - "val": "btcb", - "allowed_subparam_attr_changes": [ - "active", - "limit", - "max_swap_amount", - "min_block_lock" - ] - }, - { - "key": "denom", - "val": "xrpb", - "allowed_subparam_attr_changes": [ - "active", - "limit", - "max_swap_amount", - "min_block_lock" - ] - } - ] - }, - { - "subspace": "pricefeed", - "key": "Markets", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "market_id", - "val": "bnb:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "bnb:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "btc:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "btc:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "xrp:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "xrp:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "busd:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "busd:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "kava:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "kava:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "hard:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "hard:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "usdx:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "usdx:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "usdx:usd:720", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "swp:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "swp:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "atom:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "atom:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "akt:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "akt:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "luna:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "luna:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "osmo:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "osmo:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "ust:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "ust:usd:30", - "allowed_subparam_attr_changes": ["active"] - } - ] - }, - { - "subspace": "hard", - "key": "MoneyMarkets", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "denom", - "val": "bnb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "busd", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "btcb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "xrpb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "usdx", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "ukava", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "hard", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "swp", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - } - ] - } - ] - }, - { "@type": "/kava.committee.v1beta1.TextPermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "600s", - "tally_option": "TALLY_OPTION_FIRST_PAST_THE_POST" - } - }, - { - "@type": "/kava.committee.v1beta1.MemberCommittee", - "base_committee": { - "id": "2", - "description": "Kava Safety Committee", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { "@type": "/kava.committee.v1beta1.SoftwareUpgradePermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "604800s", - "tally_option": "TALLY_OPTION_FIRST_PAST_THE_POST" - } - }, - { - "@type": "/kava.committee.v1beta1.TokenCommittee", - "base_committee": { - "id": "3", - "description": "Hard Governance Committee", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { - "@type": "/kava.committee.v1beta1.ParamsChangePermission", - "allowed_params_changes": [ - { - "subspace": "hard", - "key": "MinimumBorrowUSDValue", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "HardSupplyRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "HardBorrowRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "DelegatorRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "hard", - "key": "MoneyMarkets", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "denom", - "val": "bnb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "busd", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "btcb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "xrpb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "usdx", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "ukava", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "hard", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "swp", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - } - ] - } - ] - }, - { "@type": "/kava.committee.v1beta1.TextPermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "600s", - "tally_option": "TALLY_OPTION_DEADLINE" - }, - "quorum": "0.330000000000000000", - "tally_denom": "hard" - }, - { - "@type": "/kava.committee.v1beta1.TokenCommittee", - "base_committee": { - "id": "4", - "description": "Swp Governance Committee", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { - "@type": "/kava.committee.v1beta1.ParamsChangePermission", - "allowed_params_changes": [ - { - "subspace": "swap", - "key": "AllowedPools", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "swap", - "key": "SwapFee", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "DelegatorRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "SwapRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - } - ] - }, - { "@type": "/kava.committee.v1beta1.TextPermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "600s", - "tally_option": "TALLY_OPTION_DEADLINE" - }, - "quorum": "0.330000000000000000", - "tally_denom": "swp" - }, - { - "@type": "/kava.committee.v1beta1.MemberCommittee", - "base_committee": { - "id": "5", - "description": "Kava God Committee (testing only)", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { "@type": "/kava.committee.v1beta1.GodPermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "604800s", - "tally_option": "TALLY_OPTION_FIRST_PAST_THE_POST" - } - } - ], - "proposals": [ - { - "content": { - "@type": "/kava.kavadist.v1beta1.CommunityPoolMultiSpendProposal", - "title": "Test", - "description": "Test", - "recipient_list": [ - { - "address": "kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2", - "amount": [{ "denom": "ukava", "amount": "10" }] - } - ] - }, - "id": "1", - "committee_id": "2", - "deadline": "2021-11-24T18:48:08.693415Z" - }, - { - "content": { - "@type": "/cosmos.distribution.v1beta1.CommunityPoolSpendProposal", - "title": "Community Pool Spend", - "description": "Fund the community pool.", - "recipient": "kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2", - "amount": [{ "denom": "ukava", "amount": "10" }] - }, - "id": "1", - "committee_id": "2", - "deadline": "2021-11-24T18:49:44.219341Z" - }, - { - "content": { - "@type": "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", - "title": "Test", - "description": "Test", - "plan": { - "name": "Test", - "time": "0001-01-01T00:00:00Z", - "height": "100", - "info": "", - "upgraded_client_state": null - } - }, - "id": "1", - "committee_id": "2", - "deadline": "2021-11-24T18:49:44.219693Z" - } - ], - "votes": [ - { - "proposal_id": "1", - "voter": "kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2", - "vote_type": "VOTE_TYPE_YES" - }, - { - "proposal_id": "1", - "voter": "kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2", - "vote_type": "VOTE_TYPE_ABSTAIN" - } - ] - }, - "crisis": { "constant_fee": { "denom": "ukava", "amount": "1333000000" } }, - "distribution": { - "params": { - "community_tax": "0.000000000000000000", - "base_proposer_reward": "0.010000000000000000", - "bonus_proposer_reward": "0.040000000000000000", - "withdraw_addr_enabled": true - }, - "fee_pool": { "community_pool": [] }, - "delegator_withdraw_infos": [], - "previous_proposer": "", - "outstanding_rewards": [], - "validator_accumulated_commissions": [], - "validator_historical_rewards": [], - "validator_current_rewards": [], - "delegator_starting_infos": [], - "validator_slash_events": [] - }, - "evidence": { "evidence": [] }, - "genutil": { - "gen_txs": [ - { - "type": "cosmos-sdk/StdTx", - "value": { - "fee": { "amount": [], "gas": "200000" }, - "memo": "23a5601684fa065ce9d953af5fc7d4347050ad14@192.168.0.14:26656", - "msg": [ - { - "type": "cosmos-sdk/MsgCreateValidator", - "value": { - "commission": { - "max_change_rate": "0.010000000000000000", - "max_rate": "0.200000000000000000", - "rate": "0.100000000000000000" - }, - "delegator_address": "kava1ypjp0m04pyp73hwgtc0dgkx0e9rrydecm054da", - "description": { - "details": "", - "identity": "", - "moniker": "validator", - "security_contact": "", - "website": "" - }, - "min_self_delegation": "1", - "pubkey": "kavavalconspub1zcjduepqvfq6egzgfmdkd6k7cqhsvsfr4lhsp6adh4uurxgkhec8h7amxcjq7gjum4", - "validator_address": "kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42", - "value": { "amount": "1000000000", "denom": "ukava" } - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "AgIWiZpdEVb69gfaqyNjbA0b3oe+nRS9BZ9gH/I6oGz+" - }, - "signature": "K9ZA2OYWrX2WSanMnbMHIaFIABWxhcrFjAnyF1U83nlFsVuEmUyGYMOmwo4vac54nkY4pfSzNreu5EnJxniUHA==" - } - ] - } - } - ] - }, - "gov": { - "starting_proposal_id": "1", - "deposits": [], - "votes": [], - "proposals": [], - "deposit_params": { - "min_deposit": [{ "denom": "ukava", "amount": "200000000" }], - "max_deposit_period": "600s" - }, - "voting_params": { "voting_period": "600s" }, - "tally_params": { - "quorum": "0.334000000000000000", - "threshold": "0.500000000000000000", - "veto_threshold": "0.334000000000000000" - } - }, - "mint": { - "minter": { - "inflation": "0.020000000000000000", - "annual_provisions": "0.000000000000000000" - }, - "params": { - "mint_denom": "ukava", - "inflation_rate_change": "0.130000000000000000", - "inflation_max": "0.130000000000000000", - "inflation_min": "0.010000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" - } - }, - "params": null, - "slashing": { - "params": { - "signed_blocks_window": "100", - "min_signed_per_window": "0.010000000000000000", - "downtime_jail_duration": "600s", - "slash_fraction_double_sign": "0.050000000000000000", - "slash_fraction_downtime": "0.000100000000000000" - }, - "signing_infos": [], - "missed_blocks": [] - }, - "staking": { - "params": { - "unbonding_time": "3600s", - "max_validators": 100, - "max_entries": 7, - "historical_entries": 10000, - "bond_denom": "ukava" - }, - "last_total_power": "0", - "last_validator_powers": [], - "validators": [], - "delegations": [], - "unbonding_delegations": [], - "redelegations": [], - "exported": false - }, - "kavadist": { - "params": { - "active": true, - "periods": [ - { - "end": "2021-06-01T14:00:00Z", - "inflation": "1.000000000000000000", - "start": "2020-06-01T14:00:00Z" - }, - { - "end": "2022-06-01T14:00:00Z", - "inflation": "1.000000002293273137", - "start": "2021-06-01T14:00:00Z" - }, - { - "end": "2023-06-01T14:00:00Z", - "inflation": "1.000000001167363430", - "start": "2022-06-01T14:00:00Z" - }, - { - "end": "2024-06-01T14:00:00Z", - "inflation": "1.000000000782997609", - "start": "2023-06-01T14:00:00Z" - } - ] - }, - "previous_block_time": "2021-11-05T21:13:12.856088470Z" - }, - "swap": { - "params": { - "allowed_pools": [ - { - "token_a": "bnb", - "token_b": "usdx" - }, - { - "token_a": "btcb", - "token_b": "usdx" - }, - { - "token_a": "busd", - "token_b": "usdx" - }, - { - "token_a": "hard", - "token_b": "usdx" - }, - { - "token_a": "swp", - "token_b": "usdx" - }, - { - "token_a": "ukava", - "token_b": "usdx" - }, - { - "token_a": "usdx", - "token_b": "xrpb" - } - ], - "swap_fee": "0.001500000000000000" - }, - "pool_records": [ - { - "pool_id": "ukava:usdx", - "reserves_a": { - "amount": "583616549439", - "denom": "ukava" - }, - "reserves_b": { - "amount": "3431399443511", - "denom": "usdx" - }, - "total_shares": "1398497336200" - }, - { - "pool_id": "usdx:xrpb", - "reserves_a": { - "amount": "843639517257", - "denom": "usdx" - }, - "reserves_b": { - "amount": "72251274276145", - "denom": "xrpb" - }, - "total_shares": "7739661881008" - } - ], - "share_records": [ - { - "depositor": "kava1l77xymdt2ya0rl2mludkny5aqmr67u88ymgdje", - "pool_id": "usdx:xrpb", - "shares_owned": "29034728969" - }, - { - "depositor": "kava1llqkrdr69wc76cuxpx6j06x9pt63f52f7mlcye", - "pool_id": "busd:usdx", - "shares_owned": "24905307583" - }, - { - "depositor": "kava1ll3ndyv333aet6mzqltu5wdepqhyme8umv34es", - "pool_id": "hard:usdx", - "shares_owned": "708138421" - }, - { - "depositor": "kava1ll3ndyv333aet6mzqltu5wdepqhyme8umv34es", - "pool_id": "ukava:usdx", - "shares_owned": "3427014047" - } - ] - }, - "capability": { - "index": "1", - "owners": [] - }, - "transfer": { - "denom_traces": [], - "params": { - "receive_enabled": true, - "send_enabled": true - }, - "port_id": "transfer" - }, - "ibc": { - "channel_genesis": { - "ack_sequences": [], - "acknowledgements": [], - "channels": [], - "commitments": [], - "next_channel_sequence": "0", - "receipts": [], - "recv_sequences": [], - "send_sequences": [] - }, - "client_genesis": { - "clients": [], - "clients_consensus": [], - "clients_metadata": [], - "create_localhost": false, - "next_client_sequence": "0", - "params": { - "allowed_clients": ["06-solomachine", "07-tendermint"] - } - }, - "connection_genesis": { - "client_connection_paths": [], - "connections": [], - "next_connection_sequence": "0", - "params": { - "max_expected_time_per_block": "30000000000" - } - } - }, - "upgrade": {} - } -} diff --git a/migrate/v0_17/testdata/genesis-v17.json b/migrate/v0_17/testdata/genesis-v17.json deleted file mode 100644 index 752890e5..00000000 --- a/migrate/v0_17/testdata/genesis-v17.json +++ /dev/null @@ -1,2366 +0,0 @@ -{ - "genesis_time": "2022-05-25T17:00:00Z", - "chain_id": "kava_2222-10", - "initial_height": "1", - "consensus_params": { - "block": { - "max_bytes": "200000", - "max_gas": "20000000", - "time_iota_ms": "1000" - }, - "evidence": { - "max_age_num_blocks": "100000", - "max_age_duration": "172800000000000", - "max_bytes": "50000" - }, - "validator": { "pub_key_types": ["ed25519"] }, - "version": { "app_version": "1" } - }, - "app_hash": "", - "app_state": { - "auction": { - "next_auction_id": "12", - "params": { - "max_auction_duration": "172800s", - "forward_bid_duration": "86400s", - "reverse_bid_duration": "3600s", - "increment_surplus": "0.010000000000000000", - "increment_debt": "0.010000000000000000", - "increment_collateral": "0.010000000000000000" - }, - "auctions": [ - { - "@type": "/kava.auction.v1beta1.CollateralAuction", - "base_auction": { - "id": "3795", - "initiator": "hard", - "lot": { "denom": "bnb", "amount": "1" }, - "bidder": "", - "bid": { "denom": "bnb", "amount": "0" }, - "has_received_bids": false, - "end_time": "9000-01-01T00:00:00Z", - "max_end_time": "9000-01-01T00:00:00Z" - }, - "corresponding_debt": { "denom": "debt", "amount": "0" }, - "max_bid": { "denom": "bnb", "amount": "1" }, - "lot_returns": { - "addresses": ["kava1eevfnzkf2mt6feyttyzh6ektclauq7zlayefwf"], - "weights": ["100"] - } - }, - { - "@type": "/kava.auction.v1beta1.SurplusAuction", - "base_auction": { - "id": "3796", - "initiator": "hard", - "lot": { "denom": "bnb", "amount": "1" }, - "bidder": "", - "bid": { "denom": "bnb", "amount": "0" }, - "has_received_bids": false, - "end_time": "9000-01-01T00:00:00Z", - "max_end_time": "9000-01-01T00:00:00Z" - } - }, - { - "@type": "/kava.auction.v1beta1.DebtAuction", - "base_auction": { - "id": "3895", - "initiator": "hard", - "lot": { "denom": "bnb", "amount": "1" }, - "bidder": "", - "bid": { "denom": "bnb", "amount": "0" }, - "has_received_bids": false, - "end_time": "9000-01-01T00:00:00Z", - "max_end_time": "9000-01-01T00:00:00Z" - }, - "corresponding_debt": { "denom": "debt", "amount": "0" } - } - ] - }, - "auth": { - "params": { - "max_memo_characters": "256", - "tx_sig_limit": "7", - "tx_size_cost_per_byte": "10", - "sig_verify_cost_ed25519": "590", - "sig_verify_cost_secp256k1": "1000" - }, - "accounts": [ - { - "@type": "/cosmos.auth.v1beta1.ModuleAccount", - "base_account": { - "address": "kava1cj7njkw2g9fqx4e768zc75dp9sks8u9znxrf0w", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - "name": "kavadist", - "permissions": ["minter"] - }, - { - "@type": "/cosmos.auth.v1beta1.ModuleAccount", - "base_account": { - "address": "kava1a42xtwfzphuuu9mdp0es6633wu5mm8fhm789v3", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - "name": "hard", - "permissions": ["minter", "burner"] - }, - { - "@type": "/cosmos.auth.v1beta1.ModuleAccount", - "base_account": { - "address": "kava1mfru9azs5nua2wxcd4sq64g5nt7nn4n8s2w8cu", - "pub_key": null, - "account_number": "32", - "sequence": "0" - }, - "name": "swap", - "permissions": [] - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1ypjp0m04pyp73hwgtc0dgkx0e9rrydecm054da", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1agcvt07tcw0tglu0hmwdecsnuxp2yd45f3avgm", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1z3ytjpr6ancl8gw80z6f47z9smug7986x29vtj", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.vesting.v1beta1.PeriodicVestingAccount", - "base_vesting_account": { - "base_account": { - "address": "kava1jun8w7meyvgxl68fsjrsdhnv87zursv7dwlgee", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - "original_vesting": [{ "denom": "swp", "amount": "14791312" }], - "delegated_free": [], - "delegated_vesting": [], - "end_time": "1665767828" - }, - "start_time": "1653498000", - "vesting_periods": [ - { - "length": "12269828", - "amount": [{ "denom": "swp", "amount": "14791312" }] - } - ] - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1h932rkxxhwf0gz3gq23qsnedgax5vwuxw4ufhy", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1kla4wl0ccv7u85cemvs3y987hqk0afcv7vue84", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava14q5sawxdxtpap5x5sgzj7v4sp3ucncjlpuk3hs", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1tfvn5t8qwngqd2q427za2mel48pcus3z9u73fl", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1j9je7f6s0v6k7dmgv6u5k5ru202f5ffsc7af04", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1ektgdyy0z23qqnd67ns3qvfzgfgjd5xe82lf5c", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1g33w0mh4mjllhaj3y4dcwkwquxgwrma9ga5t94", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1ynf22ap74j6znl503a56y23x5stfr0aw5kntp8", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava173w2zz287s36ewnnkf4mjansnthnnsz7rtrxqc", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1fwfwmt6vupf3m9uvpdsuuc4dga8p5dtl4npcqz", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1sw54s6gq76acm35ls6m5c0kr93dstgrh6eftld", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1t4dvu32e309pzhmdn3aqcjlj79h9876plynrfm", - "pub_key": null, - "account_number": "0", - "sequence": "0" - }, - { - "@type": "/cosmos.auth.v1beta1.BaseAccount", - "address": "kava1wuzhkn2f8nqe2aprnwt3jkjvvr9m7dlkpumtz2", - "pub_key": null, - "account_number": "0", - "sequence": "0" - } - ] - }, - "bank": { - "params": { "send_enabled": [], "default_send_enabled": true }, - "balances": [ - { - "address": "kava1cj7njkw2g9fqx4e768zc75dp9sks8u9znxrf0w", - "coins": [ - { "denom": "hard", "amount": "1000000000000" }, - { "denom": "swp", "amount": "1000000000000" }, - { "denom": "ukava", "amount": "1000000000000" } - ] - }, - { - "address": "kava1a42xtwfzphuuu9mdp0es6633wu5mm8fhm789v3", - "coins": [{ "denom": "usdx", "amount": "1000000000" }] - }, - { - "address": "kava1mfru9azs5nua2wxcd4sq64g5nt7nn4n8s2w8cu", - "coins": [ - { "denom": "ukava", "amount": "1000000000000" }, - { "denom": "usdx", "amount": "4100000000000" } - ] - }, - { - "address": "kava1ypjp0m04pyp73hwgtc0dgkx0e9rrydecm054da", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1agcvt07tcw0tglu0hmwdecsnuxp2yd45f3avgm", - "coins": [ - { "denom": "bnb", "amount": "100000000000000" }, - { "denom": "ukava", "amount": "1000000000000" } - ] - }, - { - "address": "kava1z3ytjpr6ancl8gw80z6f47z9smug7986x29vtj", - "coins": [ - { "denom": "ukava", "amount": "565077579" }, - { "denom": "usdx", "amount": "1363200" } - ] - }, - { - "address": "kava1jun8w7meyvgxl68fsjrsdhnv87zursv7dwlgee", - "coins": [ - { "denom": "swp", "amount": "15915874" }, - { "denom": "ukava", "amount": "1000000" } - ] - }, - { - "address": "kava1h932rkxxhwf0gz3gq23qsnedgax5vwuxw4ufhy", - "coins": [{ "denom": "ukava", "amount": "2499984990625" }] - }, - { - "address": "kava1kla4wl0ccv7u85cemvs3y987hqk0afcv7vue84", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava14q5sawxdxtpap5x5sgzj7v4sp3ucncjlpuk3hs", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1tfvn5t8qwngqd2q427za2mel48pcus3z9u73fl", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1j9je7f6s0v6k7dmgv6u5k5ru202f5ffsc7af04", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1ektgdyy0z23qqnd67ns3qvfzgfgjd5xe82lf5c", - "coins": [ - { "denom": "bnb", "amount": "10000000000000000" }, - { "denom": "btcb", "amount": "10000000000000000" }, - { "denom": "busd", "amount": "10000000000000000" }, - { "denom": "hard", "amount": "1000000000000000000" }, - { "denom": "hbtc", "amount": "10000000000000000" }, - { "denom": "swp", "amount": "1000000000000000000" }, - { "denom": "ukava", "amount": "10000000000000000" }, - { "denom": "usdx", "amount": "100000000000000000" }, - { "denom": "xrpb", "amount": "10000000000000000" } - ] - }, - { - "address": "kava1g33w0mh4mjllhaj3y4dcwkwquxgwrma9ga5t94", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1ynf22ap74j6znl503a56y23x5stfr0aw5kntp8", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava173w2zz287s36ewnnkf4mjansnthnnsz7rtrxqc", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1fwfwmt6vupf3m9uvpdsuuc4dga8p5dtl4npcqz", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1sw54s6gq76acm35ls6m5c0kr93dstgrh6eftld", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1t4dvu32e309pzhmdn3aqcjlj79h9876plynrfm", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - }, - { - "address": "kava1wuzhkn2f8nqe2aprnwt3jkjvvr9m7dlkpumtz2", - "coins": [{ "denom": "ukava", "amount": "1000000000000" }] - } - ], - "supply": [], - "denom_metadata": [] - }, - "bep3": { - "params": { - "asset_params": [ - { - "denom": "btcb", - "coin_id": "0", - "supply_limit": { - "limit": "100000000000", - "time_limited": false, - "time_period": "0s", - "time_based_limit": "0" - }, - "active": true, - "deputy_address": "kava1kla4wl0ccv7u85cemvs3y987hqk0afcv7vue84", - "fixed_fee": "2", - "min_swap_amount": "3", - "max_swap_amount": "2000000000", - "min_block_lock": "24686", - "max_block_lock": "86400" - }, - { - "denom": "xrpb", - "coin_id": "144", - "supply_limit": { - "limit": "2000000000000000", - "time_limited": false, - "time_period": "0s", - "time_based_limit": "0" - }, - "active": true, - "deputy_address": "kava14q5sawxdxtpap5x5sgzj7v4sp3ucncjlpuk3hs", - "fixed_fee": "100000", - "min_swap_amount": "100001", - "max_swap_amount": "250000000000000", - "min_block_lock": "24686", - "max_block_lock": "86400" - }, - { - "denom": "bnb", - "coin_id": "714", - "supply_limit": { - "limit": "100000000000000", - "time_limited": false, - "time_period": "0s", - "time_based_limit": "0" - }, - "active": true, - "deputy_address": "kava1agcvt07tcw0tglu0hmwdecsnuxp2yd45f3avgm", - "fixed_fee": "1000", - "min_swap_amount": "1001", - "max_swap_amount": "500000000000", - "min_block_lock": "24686", - "max_block_lock": "86400" - }, - { - "denom": "busd", - "coin_id": "727", - "supply_limit": { - "limit": "2000000000000000", - "time_limited": false, - "time_period": "0s", - "time_based_limit": "0" - }, - "active": true, - "deputy_address": "kava1j9je7f6s0v6k7dmgv6u5k5ru202f5ffsc7af04", - "fixed_fee": "20000", - "min_swap_amount": "20001", - "max_swap_amount": "100000000000000", - "min_block_lock": "24686", - "max_block_lock": "86400" - } - ] - }, - "atomic_swaps": [ - { - "amount": [ - { - "amount": "1999955998", - "denom": "btcb" - } - ], - "closed_block": "1", - "cross_chain": true, - "direction": "SWAP_DIRECTION_INCOMING", - "expire_height": "838627", - "random_number_hash": "6F1CF8F2E13A0C0F0A359F54E47E4E265D766B8E006D2F00BDF994ABDEF1E9E4", - "recipient": "kava1fl2hs6y9vz986g5v52pdan9ga923n9mn5cxxkw", - "recipient_other_chain": "bnb1xz3xqf4p2ygrw9lhp5g5df4ep4nd20vsywnmpr", - "sender": "kava14qsmvzprqvhwmgql9fr0u3zv9n2qla8zhnm5pc", - "sender_other_chain": "bnb19k9wuv2j7c7ck8tmc7kav0r0cnt3esmkrpf25x", - "status": "SWAP_STATUS_COMPLETED", - "timestamp": "1636034914" - }, - { - "amount": [ - { - "amount": "19000000000", - "denom": "bnb" - } - ], - "closed_block": "1", - "cross_chain": true, - "direction": "SWAP_DIRECTION_OUTGOING", - "expire_height": "1736797", - "random_number_hash": "280EB832A37F2265CC82F3957CE603AAD57BAD7038B876A1F28953AFA29FA1C3", - "recipient": "kava1r4v2zdhdalfj2ydazallqvrus9fkphmglhn6u6", - "recipient_other_chain": "bnb18nsgj50zvc4uq93w4j0ltz5gaxhwv7aq4qnq0p", - "sender": "kava1zw6gg4ztvly7zf25pa33mclav3spvj3ympxxna", - "sender_other_chain": "bnb1jh7uv2rm6339yue8k4mj9406k3509kr4wt5nxn", - "status": "SWAP_STATUS_COMPLETED", - "timestamp": "1641976566" - }, - { - "amount": [ - { - "amount": "999595462080", - "denom": "busd" - } - ], - "closed_block": "787122", - "cross_chain": true, - "direction": "SWAP_DIRECTION_INCOMING", - "expire_height": "1", - "random_number_hash": "BFB7CC82DA0E0C8556AC37843F5AB136B9A7A066054368F5948944282B414D83", - "recipient": "kava1eufgf0w9d7hf5mgtek4zr2upkxag9stmzx6unl", - "recipient_other_chain": "bnb10zq89008gmedc6rrwzdfukjk94swynd7dl97w8", - "sender": "kava1hh4x3a4suu5zyaeauvmv7ypf7w9llwlfufjmuu", - "sender_other_chain": "bnb1vl3wn4x8kqajg2j9wxa5y5amgzdxchutkxr6at", - "status": "SWAP_STATUS_EXPIRED", - "timestamp": "1635694492" - }, - { - "amount": [ - { - "amount": "999595462080", - "denom": "busd" - } - ], - "closed_block": "787122", - "cross_chain": true, - "direction": "SWAP_DIRECTION_OUTGOING", - "expire_height": "1", - "random_number_hash": "BFB7CC82DA0E0C8556AC37843F5AB136B9A7A066054368F5948944282B414D83", - "recipient": "kava1hh4x3a4suu5zyaeauvmv7ypf7w9llwlfufjmuu", - "recipient_other_chain": "bnb1vl3wn4x8kqajg2j9wxa5y5amgzdxchutkxr6at", - "sender": "kava1eufgf0w9d7hf5mgtek4zr2upkxag9stmzx6unl", - "sender_other_chain": "bnb10zq89008gmedc6rrwzdfukjk94swynd7dl97w8", - "status": "SWAP_STATUS_EXPIRED", - "timestamp": "1635694492" - }, - { - "amount": [ - { - "amount": "1000000", - "denom": "btcb" - } - ], - "closed_block": "0", - "cross_chain": true, - "direction": "SWAP_DIRECTION_OUTGOING", - "expire_height": "24687", - "random_number_hash": "A74EA1AB58D312FDF1E872D18583CACCF294E639DDA4F303939E9ADCEC081D93", - "recipient": "kava14qsmvzprqvhwmgql9fr0u3zv9n2qla8zhnm5pc", - "recipient_other_chain": "bnb1lhk5ndlgf5wz55t8k35cqj6h9l3m4l5ek2w7q6", - "sender": "kava1d2u28azje7rhqyjtxc2ex8q0cxxpw7dfm7ltq5", - "sender_other_chain": "bnb1xz3xqf4p2ygrw9lhp5g5df4ep4nd20vsywnmpr", - "status": "SWAP_STATUS_OPEN", - "timestamp": "1641934114" - }, - { - "amount": [ - { - "amount": "1000000", - "denom": "btcb" - } - ], - "closed_block": "0", - "cross_chain": true, - "direction": "SWAP_DIRECTION_INCOMING", - "expire_height": "1", - "random_number_hash": "39E9ADCEC081D93A74EA1A83CACCF294E639DDA4F3039B58D312FDF1E872D185", - "recipient": "kava1d2u28azje7rhqyjtxc2ex8q0cxxpw7dfm7ltq5", - "recipient_other_chain": "bnb1xz3xqf4p2ygrw9lhp5g5df4ep4nd20vsywnmpr", - "sender": "kava14qsmvzprqvhwmgql9fr0u3zv9n2qla8zhnm5pc", - "sender_other_chain": "bnb1lhk5ndlgf5wz55t8k35cqj6h9l3m4l5ek2w7q6", - "status": "SWAP_STATUS_EXPIRED", - "timestamp": "1641934114" - } - ], - "supplies": [ - { - "incoming_supply": { "denom": "bnb", "amount": "0" }, - "outgoing_supply": { "denom": "bnb", "amount": "0" }, - "current_supply": { "denom": "bnb", "amount": "30467559434006" }, - "time_limited_current_supply": { "denom": "bnb", "amount": "0" }, - "time_elapsed": "0s" - } - ], - "previous_block_time": "1970-01-01T00:00:00Z" - }, - "bridge": { - "params": { - "bridge_enabled": false, - "enabled_erc20_tokens": [], - "relayer": "", - "enabled_conversion_pairs": [] - }, - "erc20_bridge_pairs": [], - "next_withdraw_sequence": "1" - }, - "capability": { "index": "1", "owners": [] }, - "cdp": { - "params": { - "collateral_params": [ - { - "denom": "bnb", - "type": "bnb-a", - "liquidation_ratio": "1.500000000000000000", - "debt_limit": { "denom": "usdx", "amount": "20000000000000" }, - "stability_fee": "1.000000000782997700", - "auction_size": "50000000000", - "liquidation_penalty": "0.050000000000000000", - "spot_market_id": "bnb:usd", - "liquidation_market_id": "bnb:usd:30", - "keeper_reward_percentage": "0.010000000000000000", - "check_collateralization_index_count": "10", - "conversion_factor": "8" - }, - { - "denom": "hbtc", - "type": "hbtc-a", - "liquidation_ratio": "1.500000000000000000", - "debt_limit": { "denom": "usdx", "amount": "10000000000000" }, - "stability_fee": "1.000000001547125958", - "auction_size": "1000000000000", - "liquidation_penalty": "0.075000000000000000", - "spot_market_id": "btc:usd", - "liquidation_market_id": "btc:usd:30", - "keeper_reward_percentage": "0.010000000000000000", - "check_collateralization_index_count": "10", - "conversion_factor": "8" - } - ], - "debt_param": { - "denom": "usdx", - "reference_asset": "usd", - "conversion_factor": "6", - "debt_floor": "10000000" - }, - "global_debt_limit": { "denom": "usdx", "amount": "393000000000000" }, - "surplus_auction_threshold": "500000000000", - "surplus_auction_lot": "10000000000", - "debt_auction_threshold": "100000000000", - "debt_auction_lot": "10000000000", - "circuit_breaker": false - }, - "cdps": [ - { - "id": "19", - "owner": "kava1u4p6a2yse66ewfndw4j7ppsa777vdcex3m4n9s", - "type": "bnb-a", - "collateral": { "denom": "bnb", "amount": "40911876" }, - "principal": { "denom": "usdx", "amount": "10016831" }, - "accumulated_fees": { "denom": "usdx", "amount": "36088" }, - "fees_updated": "2021-11-05T21:13:12.856088470Z", - "interest_factor": "1.002881426571765716" - }, - { - "id": "22", - "owner": "kava1e8xjfylam4nvugcmkxwvuxh22uvvad5vknu4yh", - "type": "bnb-a", - "collateral": { "denom": "bnb", "amount": "88268546" }, - "principal": { "denom": "usdx", "amount": "10000000" }, - "accumulated_fees": { "denom": "usdx", "amount": "156662" }, - "fees_updated": "2021-11-05T21:13:12.856088470Z", - "interest_factor": "1.002881426571765716" - } - ], - "deposits": [ - { - "cdp_id": "19", - "depositor": "kava1u4p6a2yse66ewfndw4j7ppsa777vdcex3m4n9s", - "amount": { "denom": "bnb", "amount": "40911876" } - }, - { - "cdp_id": "22", - "depositor": "kava1e8xjfylam4nvugcmkxwvuxh22uvvad5vknu4yh", - "amount": { "denom": "bnb", "amount": "88268546" } - } - ], - "starting_cdp_id": "1", - "debt_denom": "debt", - "gov_denom": "ukava", - "previous_accumulation_times": [ - { - "collateral_type": "bnb-a", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z", - "interest_factor": "1.002881426571765716" - } - ], - "total_principals": [ - { "collateral_type": "bnb-a", "total_principal": "9285009581820" } - ] - }, - "committee": { - "next_proposal_id": "1", - "committees": [ - { - "@type": "/kava.committee.v1beta1.MemberCommittee", - "base_committee": { - "id": "1", - "description": "Kava Stability Committee", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { - "@type": "/kava.committee.v1beta1.ParamsChangePermission", - "allowed_params_changes": [ - { - "subspace": "auction", - "key": "IncrementSurplus", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "auction", - "key": "IncrementDebt", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "auction", - "key": "IncrementCollateral", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "GlobalDebtLimit", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "SurplusThreshold", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "SurplusLot", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "DebtThreshold", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "DebtLot", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "DistributionFrequency", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "Active", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "kavadist", - "key": "Active", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "hard", - "key": "MinimumBorrowUSDValue", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "cdp", - "key": "CollateralParams", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "type", - "val": "bnb-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "busd-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "busd-b", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "btcb-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "xrpb-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "ukava-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "hard-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "hbtc-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "swp-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - }, - { - "key": "type", - "val": "ust-a", - "allowed_subparam_attr_changes": [ - "auction_size", - "check_collateralization_index_count", - "debt_limit", - "keeper_reward_percentage", - "stability_fee" - ] - } - ] - }, - { - "subspace": "cdp", - "key": "DebtParam", - "single_subparam_allowed_attrs": ["debt_floor"], - "multi_subparams_requirements": [] - }, - { - "subspace": "bep3", - "key": "AssetParams", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "denom", - "val": "bnb", - "allowed_subparam_attr_changes": [ - "active", - "limit", - "max_swap_amount", - "min_block_lock" - ] - }, - { - "key": "denom", - "val": "busd", - "allowed_subparam_attr_changes": [ - "active", - "coin_id", - "limit", - "max_swap_amount", - "min_block_lock" - ] - }, - { - "key": "denom", - "val": "btcb", - "allowed_subparam_attr_changes": [ - "active", - "limit", - "max_swap_amount", - "min_block_lock" - ] - }, - { - "key": "denom", - "val": "xrpb", - "allowed_subparam_attr_changes": [ - "active", - "limit", - "max_swap_amount", - "min_block_lock" - ] - } - ] - }, - { - "subspace": "pricefeed", - "key": "Markets", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "market_id", - "val": "bnb:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "bnb:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "btc:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "btc:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "xrp:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "xrp:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "busd:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "busd:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "kava:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "kava:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "hard:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "hard:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "usdx:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "usdx:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "usdx:usd:720", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "swp:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "swp:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "atom:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "atom:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "akt:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "akt:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "luna:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "luna:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "osmo:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "osmo:usd:30", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "ust:usd", - "allowed_subparam_attr_changes": ["active"] - }, - { - "key": "market_id", - "val": "ust:usd:30", - "allowed_subparam_attr_changes": ["active"] - } - ] - }, - { - "subspace": "hard", - "key": "MoneyMarkets", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "denom", - "val": "bnb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "busd", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "btcb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "xrpb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "usdx", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "ukava", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "hard", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "swp", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor" - ] - }, - { - "key": "denom", - "val": "ibc/799FDD409719A1122586A629AE8FCA17380351A51C1F47A80A1B8E7F2A491098", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - } - ] - }, - { - "subspace": "auction", - "key": "ForwardBidDuration", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "auction", - "key": "ReverseBidDuration", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - } - ] - }, - { "@type": "/kava.committee.v1beta1.TextPermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "600s", - "tally_option": "TALLY_OPTION_FIRST_PAST_THE_POST" - } - }, - { - "@type": "/kava.committee.v1beta1.MemberCommittee", - "base_committee": { - "id": "2", - "description": "Kava Safety Committee", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { "@type": "/kava.committee.v1beta1.SoftwareUpgradePermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "604800s", - "tally_option": "TALLY_OPTION_FIRST_PAST_THE_POST" - } - }, - { - "@type": "/kava.committee.v1beta1.TokenCommittee", - "base_committee": { - "id": "3", - "description": "Hard Governance Committee", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { - "@type": "/kava.committee.v1beta1.ParamsChangePermission", - "allowed_params_changes": [ - { - "subspace": "hard", - "key": "MinimumBorrowUSDValue", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "HardSupplyRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "HardBorrowRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "DelegatorRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "hard", - "key": "MoneyMarkets", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [ - { - "key": "denom", - "val": "bnb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "busd", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "btcb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "xrpb", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "usdx", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "ukava", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "hard", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "swp", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - }, - { - "key": "denom", - "val": "ibc/799FDD409719A1122586A629AE8FCA17380351A51C1F47A80A1B8E7F2A491098", - "allowed_subparam_attr_changes": [ - "borrow_limit", - "interest_rate_model", - "keeper_reward_percentage", - "reserve_factor", - "spot_market_id" - ] - } - ] - } - ] - }, - { "@type": "/kava.committee.v1beta1.TextPermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "600s", - "tally_option": "TALLY_OPTION_DEADLINE" - }, - "quorum": "0.330000000000000000", - "tally_denom": "hard" - }, - { - "@type": "/kava.committee.v1beta1.TokenCommittee", - "base_committee": { - "id": "4", - "description": "Swp Governance Committee", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { - "@type": "/kava.committee.v1beta1.ParamsChangePermission", - "allowed_params_changes": [ - { - "subspace": "swap", - "key": "AllowedPools", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "swap", - "key": "SwapFee", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "DelegatorRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - }, - { - "subspace": "incentive", - "key": "SwapRewardPeriods", - "single_subparam_allowed_attrs": [], - "multi_subparams_requirements": [] - } - ] - }, - { "@type": "/kava.committee.v1beta1.TextPermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "600s", - "tally_option": "TALLY_OPTION_DEADLINE" - }, - "quorum": "0.330000000000000000", - "tally_denom": "swp" - }, - { - "@type": "/kava.committee.v1beta1.MemberCommittee", - "base_committee": { - "id": "5", - "description": "Kava God Committee (testing only)", - "members": ["kava1n96qpdfcz2m7y364ewk8srv9zuq6ucwduyjaag"], - "permissions": [ - { "@type": "/kava.committee.v1beta1.GodPermission" } - ], - "vote_threshold": "0.500000000000000000", - "proposal_duration": "604800s", - "tally_option": "TALLY_OPTION_FIRST_PAST_THE_POST" - } - } - ], - "proposals": [ - { - "content": { - "@type": "/kava.kavadist.v1beta1.CommunityPoolMultiSpendProposal", - "title": "Test", - "description": "Test", - "recipient_list": [ - { - "address": "kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2", - "amount": [{ "denom": "ukava", "amount": "10" }] - } - ] - }, - "id": "1", - "committee_id": "2", - "deadline": "2021-11-24T18:48:08.693415Z" - }, - { - "content": { - "@type": "/cosmos.distribution.v1beta1.CommunityPoolSpendProposal", - "title": "Community Pool Spend", - "description": "Fund the community pool.", - "recipient": "kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2", - "amount": [{ "denom": "ukava", "amount": "10" }] - }, - "id": "1", - "committee_id": "2", - "deadline": "2021-11-24T18:49:44.219341Z" - }, - { - "content": { - "@type": "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", - "title": "Test", - "description": "Test", - "plan": { - "name": "Test", - "time": "0001-01-01T00:00:00Z", - "height": "100", - "info": "", - "upgraded_client_state": null - } - }, - "id": "1", - "committee_id": "2", - "deadline": "2021-11-24T18:49:44.219693Z" - } - ], - "votes": [ - { - "proposal_id": "1", - "voter": "kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2", - "vote_type": "VOTE_TYPE_YES" - }, - { - "proposal_id": "1", - "voter": "kava1ze7y9qwdddejmy7jlw4cymqqlt2wh05yhwmrv2", - "vote_type": "VOTE_TYPE_ABSTAIN" - } - ] - }, - "crisis": { "constant_fee": { "denom": "ukava", "amount": "1333000000" } }, - "distribution": { - "params": { - "community_tax": "0.000000000000000000", - "base_proposer_reward": "0.010000000000000000", - "bonus_proposer_reward": "0.040000000000000000", - "withdraw_addr_enabled": true - }, - "fee_pool": { "community_pool": [] }, - "delegator_withdraw_infos": [], - "previous_proposer": "", - "outstanding_rewards": [], - "validator_accumulated_commissions": [], - "validator_historical_rewards": [], - "validator_current_rewards": [], - "delegator_starting_infos": [], - "validator_slash_events": [] - }, - "evidence": { "evidence": [] }, - "evm": { - "accounts": [], - "params": { - "evm_denom": "akava", - "enable_create": true, - "enable_call": true, - "extra_eips": [], - "chain_config": { - "homestead_block": "0", - "dao_fork_block": "0", - "dao_fork_support": true, - "eip150_block": "0", - "eip150_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "eip155_block": "0", - "eip158_block": "0", - "byzantium_block": "0", - "constantinople_block": "0", - "petersburg_block": "0", - "istanbul_block": "0", - "muir_glacier_block": "0", - "berlin_block": "0", - "london_block": null, - "arrow_glacier_block": null, - "merge_fork_block": null - } - } - }, - "evmutil": { "accounts": [] }, - "genutil": { - "gen_txs": [ - { - "type": "cosmos-sdk/StdTx", - "value": { - "fee": { "amount": [], "gas": "200000" }, - "memo": "23a5601684fa065ce9d953af5fc7d4347050ad14@192.168.0.14:26656", - "msg": [ - { - "type": "cosmos-sdk/MsgCreateValidator", - "value": { - "commission": { - "max_change_rate": "0.010000000000000000", - "max_rate": "0.200000000000000000", - "rate": "0.100000000000000000" - }, - "delegator_address": "kava1ypjp0m04pyp73hwgtc0dgkx0e9rrydecm054da", - "description": { - "details": "", - "identity": "", - "moniker": "validator", - "security_contact": "", - "website": "" - }, - "min_self_delegation": "1", - "pubkey": "kavavalconspub1zcjduepqvfq6egzgfmdkd6k7cqhsvsfr4lhsp6adh4uurxgkhec8h7amxcjq7gjum4", - "validator_address": "kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42", - "value": { "amount": "1000000000", "denom": "ukava" } - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "AgIWiZpdEVb69gfaqyNjbA0b3oe+nRS9BZ9gH/I6oGz+" - }, - "signature": "K9ZA2OYWrX2WSanMnbMHIaFIABWxhcrFjAnyF1U83nlFsVuEmUyGYMOmwo4vac54nkY4pfSzNreu5EnJxniUHA==" - } - ] - } - } - ] - }, - "gov": { - "starting_proposal_id": "1", - "deposits": [], - "votes": [], - "proposals": [], - "deposit_params": { - "min_deposit": [{ "denom": "ukava", "amount": "200000000" }], - "max_deposit_period": "600s" - }, - "voting_params": { "voting_period": "600s" }, - "tally_params": { - "quorum": "0.334000000000000000", - "threshold": "0.500000000000000000", - "veto_threshold": "0.334000000000000000" - } - }, - "hard": { - "params": { - "money_markets": [ - { - "denom": "usdx", - "borrow_limit": { - "has_max_limit": false, - "maximum_limit": "20000000.000000000000000000", - "loan_to_value": "0.800000000000000000" - }, - "spot_market_id": "usdx:usd", - "conversion_factor": "1000000", - "interest_rate_model": { - "base_rate_apy": "0.050000000000000000", - "base_multiplier": "0.100000000000000000", - "kink": "0.800000000000000000", - "jump_multiplier": "0.500000000000000000" - }, - "reserve_factor": "0.000000000000000000", - "keeper_reward_percentage": "0.050000000000000000" - }, - { - "denom": "ukava", - "borrow_limit": { - "has_max_limit": false, - "maximum_limit": "100000000000.000000000000000000", - "loan_to_value": "0.800000000000000000" - }, - "spot_market_id": "kava:usd", - "conversion_factor": "1000000", - "interest_rate_model": { - "base_rate_apy": "0.050000000000000000", - "base_multiplier": "2.000000000000000000", - "kink": "0.850000000000000000", - "jump_multiplier": "10.000000000000000000" - }, - "reserve_factor": "0.100000000000000000", - "keeper_reward_percentage": "0.010000000000000000" - }, - { - "denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - "borrow_limit": { - "has_max_limit": true, - "maximum_limit": "25000000000.000000000000000000", - "loan_to_value": "0.500000000000000000" - }, - "spot_market_id": "atom:usd:30", - "conversion_factor": "1000000", - "interest_rate_model": { - "base_rate_apy": "0.000000000000000000", - "base_multiplier": "0.050000000000000000", - "kink": "0.800000000000000000", - "jump_multiplier": "5.000000000000000000" - }, - "reserve_factor": "0.025000000000000000", - "keeper_reward_percentage": "0.020000000000000000" - } - ], - "minimum_borrow_usd_value": "10.000000000000000000" - }, - "previous_accumulation_times": [ - { - "collateral_type": "btcb", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z", - "supply_interest_factor": "1.000205165357775358", - "borrow_interest_factor": "1.002233592784895182" - } - ], - "deposits": [ - { - "depositor": "kava1qq9ustlc0nv4aew275w93g4qy5zahqgxf5mwv9", - "amount": [ - { "denom": "bnb", "amount": "162103943" }, - { "denom": "btcb", "amount": "19428483" } - ], - "index": [{ "denom": "bnb", "value": "1.001740185031830285" }] - } - ], - "borrows": [ - { - "borrower": "kava1qq9ustlc0nv4aew275w93g4qy5zahqgxf5mwv9", - "amount": [ - { "denom": "usdx", "amount": "146724966" }, - { "denom": "xrpb", "amount": "541061835659" } - ], - "index": [ - { "denom": "usdx", "value": "1.000156840239586720" }, - { "denom": "xrpb", "value": "1.002063063678030789" } - ] - } - ], - "total_supplied": [{ "denom": "bnb", "amount": "1246173151758" }], - "total_borrowed": [{ "denom": "busd", "amount": "704609324351367" }], - "total_reserves": [{ "denom": "xrpb", "amount": "711656301126744" }] - }, - "ibc": { - "channel_genesis": { - "ack_sequences": [], - "acknowledgements": [], - "channels": [], - "commitments": [], - "next_channel_sequence": "0", - "receipts": [], - "recv_sequences": [], - "send_sequences": [] - }, - "client_genesis": { - "clients": [], - "clients_consensus": [], - "clients_metadata": [], - "create_localhost": false, - "next_client_sequence": "0", - "params": { "allowed_clients": ["06-solomachine", "07-tendermint"] } - }, - "connection_genesis": { - "client_connection_paths": [], - "connections": [], - "next_connection_sequence": "0", - "params": { "max_expected_time_per_block": "30000000000" } - } - }, - "incentive": { - "params": { - "usdx_minting_reward_periods": [ - { - "active": true, - "collateral_type": "bnb-a", - "start": "2021-07-20T14:00:00Z", - "end": "2024-10-16T14:00:00Z", - "rewards_per_second": { "denom": "ukava", "amount": "122354" } - }, - { - "active": true, - "collateral_type": "hard-a", - "start": "2021-07-20T14:00:00Z", - "end": "2024-10-16T14:00:00Z", - "rewards_per_second": { "denom": "ukava", "amount": "23809" } - } - ], - "hard_supply_reward_periods": [ - { - "active": true, - "collateral_type": "bnb", - "start": "2021-07-20T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [{ "denom": "hard", "amount": "123455" }] - }, - { - "active": true, - "collateral_type": "hard", - "start": "2021-07-20T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [{ "denom": "hard", "amount": "123455" }] - } - ], - "hard_borrow_reward_periods": [ - { - "active": true, - "collateral_type": "bnb", - "start": "2020-01-01T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [{ "denom": "hard", "amount": "12345" }] - }, - { - "active": true, - "collateral_type": "btcb", - "start": "2020-01-01T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [{ "denom": "hard", "amount": "12345" }] - } - ], - "delegator_reward_periods": [ - { - "active": true, - "collateral_type": "ukava", - "start": "2020-01-01T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [ - { "denom": "hard", "amount": "316880" }, - { "denom": "swp", "amount": "316880" } - ] - } - ], - "swap_reward_periods": [ - { - "active": true, - "collateral_type": "ukava:usdx", - "start": "2021-07-14T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [ - { "denom": "swp", "amount": "316880" }, - { "denom": "ukava", "amount": "31688" } - ] - }, - { - "active": true, - "collateral_type": "swp:usdx", - "start": "2021-07-14T00:00:00Z", - "end": "2024-01-01T00:00:00Z", - "rewards_per_second": [ - { "denom": "swp", "amount": "616880" }, - { "denom": "ukava", "amount": "31688" } - ] - } - ], - "claim_multipliers": [ - { - "denom": "hard", - "multipliers": [ - { - "name": "small", - "months_lockup": "1", - "factor": "0.200000000000000000" - }, - { - "name": "large", - "months_lockup": "12", - "factor": "1.000000000000000000" - } - ] - }, - { - "denom": "swp", - "multipliers": [ - { - "name": "small", - "months_lockup": "1", - "factor": "0.100000000000000000" - }, - { - "name": "large", - "months_lockup": "12", - "factor": "1.000000000000000000" - } - ] - } - ], - "claim_end": "2025-01-01T00:00:00Z", - "savings_reward_periods": [] - }, - "usdx_reward_state": { - "accumulation_times": [ - { - "collateral_type": "bnb-a", - "previous_accumulation_time": "2021-06-10T16:43:11.679705Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "bnb-a", - "reward_indexes": [ - { - "collateral_type": "ukava", - "reward_factor": "0.043949244534927716" - } - ] - } - ] - }, - "hard_supply_reward_state": { - "accumulation_times": [ - { - "collateral_type": "bnb", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z" - }, - { - "collateral_type": "hard", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "bnb", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "32.458112657412585027" - } - ] - }, - { - "collateral_type": "hard", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "35.000000000000000000" - } - ] - } - ] - }, - "hard_borrow_reward_state": { - "accumulation_times": [ - { - "collateral_type": "btcb", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "btcb", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "88.582200355378048199" - } - ] - } - ] - }, - "delegator_reward_state": { - "accumulation_times": [ - { - "collateral_type": "ukava", - "previous_accumulation_time": "2021-11-05T21:13:12.856088470Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "ukava", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "0.078380843036861815" - }, - { - "collateral_type": "swp", - "reward_factor": "0.013629025935176301" - } - ] - } - ] - }, - "swap_reward_state": { - "accumulation_times": [ - { - "collateral_type": "btcb-a", - "previous_accumulation_time": "2021-06-10T16:43:11.679705Z" - } - ], - "multi_reward_indexes": [ - { - "collateral_type": "btcb:usdx", - "reward_indexes": [ - { - "collateral_type": "swp", - "reward_factor": "6.145396761233172901" - } - ] - } - ] - }, - "usdx_minting_claims": [ - { - "base_claim": { - "owner": "kava1qptt5vu26cmxpmv0hf2tnnmf293x266pjcsjar", - "reward": { "denom": "ukava", "amount": "550" } - }, - "reward_indexes": [ - { - "collateral_type": "bnb-a", - "reward_factor": "0.043949244534927716" - }, - { - "collateral_type": "btcb-a", - "reward_factor": "0.046551281526135881" - } - ] - } - ], - "hard_liquidity_provider_claims": [ - { - "base_claim": { - "owner": "kava1qqqvdyv8w0xdu7gjdtt598q78gtgqyukct4yz2", - "reward": [{ "denom": "hard", "amount": "1747514" }] - }, - "supply_reward_indexes": [ - { - "collateral_type": "bnb", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "22.000000000000000000" - } - ] - }, - { - "collateral_type": "hard", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "30.000000000000000000" - } - ] - } - ], - "borrow_reward_indexes": [ - { - "collateral_type": "btc", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "88.582200355378048199" - } - ] - } - ] - }, - { - "base_claim": { - "owner": "kava1w2uj6rpejrma47vjx4rcghh3fndhmrvs6pmxph", - "reward": [{ "amount": "1747514", "denom": "hard" }] - }, - "borrow_reward_indexes": [], - "supply_reward_indexes": [] - } - ], - "delegator_claims": [ - { - "base_claim": { - "owner": "kava1qqqvdyv8w0xdu7gjdtt598q78gtgqyukct4yz2", - "reward": [] - }, - "reward_indexes": [ - { - "collateral_type": "ukava", - "reward_indexes": [ - { - "collateral_type": "hard", - "reward_factor": "0.000000000000000000" - }, - { - "collateral_type": "swp", - "reward_factor": "0.000000100000000000" - } - ] - } - ] - } - ], - "savings_claims": [], - "savings_reward_state": { - "accumulation_times": [], - "multi_reward_indexes": [] - }, - "swap_claims": [ - { - "base_claim": { - "owner": "kava1qzease8mre5adak7wcc2twh6ryh9evnxpr6caj", - "reward": [{ "denom": "swp", "amount": "1960368" }] - }, - "reward_indexes": [ - { - "collateral_type": "busd:usdx", - "reward_indexes": [ - { - "collateral_type": "swp", - "reward_factor": "0.018083281889996318" - } - ] - } - ] - } - ] - }, - "issuance": { - "params": { - "assets": [ - { - "blockable": true, - "blocked_addresses": [], - "denom": "hbtc", - "owner": "kava1dmm9zpdnm6mfhywzt9sstm4p33y0cnsd0m673z", - "paused": false, - "rate_limit": { "active": false, "limit": "0", "time_period": "0s" } - } - ] - }, - "supplies": [ - { - "current_supply": { "denom": "ukava", "amount": "100" }, - "time_elapsed": "3600s" - }, - { - "current_supply": { "denom": "bnb", "amount": "300" }, - "time_elapsed": "300s" - } - ] - }, - "kavadist": { - "params": { - "active": true, - "periods": [ - { - "end": "2021-06-01T14:00:00Z", - "inflation": "1.000000000000000000", - "start": "2020-06-01T14:00:00Z" - }, - { - "end": "2022-06-01T14:00:00Z", - "inflation": "1.000000002293273137", - "start": "2021-06-01T14:00:00Z" - }, - { - "end": "2023-06-01T14:00:00Z", - "inflation": "1.000000001167363430", - "start": "2022-06-01T14:00:00Z" - }, - { - "end": "2024-06-01T14:00:00Z", - "inflation": "1.000000000782997609", - "start": "2023-06-01T14:00:00Z" - } - ] - }, - "previous_block_time": "2021-11-05T21:13:12.856088470Z" - }, - "mint": { - "minter": { - "inflation": "0.020000000000000000", - "annual_provisions": "0.000000000000000000" - }, - "params": { - "mint_denom": "ukava", - "inflation_rate_change": "0.130000000000000000", - "inflation_max": "0.130000000000000000", - "inflation_min": "0.010000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" - } - }, - "params": null, - "pricefeed": { - "params": { - "markets": [ - { - "active": true, - "base_asset": "bnb", - "market_id": "bnb:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "bnb", - "market_id": "bnb:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "atom", - "market_id": "atom:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "atom", - "market_id": "atom:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "akt", - "market_id": "akt:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "akt", - "market_id": "akt:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "luna", - "market_id": "luna:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "luna", - "market_id": "luna:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "osmo", - "market_id": "osmo:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "osmo", - "market_id": "osmo:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "ust", - "market_id": "ust:usd", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - }, - { - "active": true, - "base_asset": "ust", - "market_id": "ust:usd:30", - "oracles": ["kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em"], - "quote_asset": "usd" - } - ] - }, - "posted_prices": [ - { - "expiry": "2022-07-20T00:00:00Z", - "market_id": "bnb:usd", - "oracle_address": "kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em", - "price": "215.962650000000001782" - }, - { - "expiry": "2022-07-20T00:00:00Z", - "market_id": "bnb:usd:30", - "oracle_address": "kava1acge4tcvhf3q6fh53fgwaa7vsq40wvx6wn50em", - "price": "217.962650000000001782" - } - ] - }, - "slashing": { - "params": { - "signed_blocks_window": "100", - "min_signed_per_window": "0.010000000000000000", - "downtime_jail_duration": "600s", - "slash_fraction_double_sign": "0.050000000000000000", - "slash_fraction_downtime": "0.000100000000000000" - }, - "signing_infos": [], - "missed_blocks": [] - }, - "staking": { - "params": { - "unbonding_time": "3600s", - "max_validators": 100, - "max_entries": 7, - "historical_entries": 10000, - "bond_denom": "ukava" - }, - "last_total_power": "0", - "last_validator_powers": [], - "validators": [], - "delegations": [], - "unbonding_delegations": [], - "redelegations": [], - "exported": false - }, - "swap": { - "params": { - "allowed_pools": [ - { "token_a": "bnb", "token_b": "usdx" }, - { "token_a": "btcb", "token_b": "usdx" }, - { "token_a": "busd", "token_b": "usdx" }, - { "token_a": "hard", "token_b": "usdx" }, - { "token_a": "swp", "token_b": "usdx" }, - { "token_a": "ukava", "token_b": "usdx" }, - { "token_a": "usdx", "token_b": "xrpb" } - ], - "swap_fee": "0.001500000000000000" - }, - "pool_records": [ - { - "pool_id": "ukava:usdx", - "reserves_a": { "amount": "583616549439", "denom": "ukava" }, - "reserves_b": { "amount": "3431399443511", "denom": "usdx" }, - "total_shares": "1398497336200" - }, - { - "pool_id": "usdx:xrpb", - "reserves_a": { "amount": "843639517257", "denom": "usdx" }, - "reserves_b": { "amount": "72251274276145", "denom": "xrpb" }, - "total_shares": "7739661881008" - } - ], - "share_records": [ - { - "depositor": "kava1l77xymdt2ya0rl2mludkny5aqmr67u88ymgdje", - "pool_id": "usdx:xrpb", - "shares_owned": "29034728969" - }, - { - "depositor": "kava1llqkrdr69wc76cuxpx6j06x9pt63f52f7mlcye", - "pool_id": "busd:usdx", - "shares_owned": "24905307583" - }, - { - "depositor": "kava1ll3ndyv333aet6mzqltu5wdepqhyme8umv34es", - "pool_id": "hard:usdx", - "shares_owned": "708138421" - }, - { - "depositor": "kava1ll3ndyv333aet6mzqltu5wdepqhyme8umv34es", - "pool_id": "ukava:usdx", - "shares_owned": "3427014047" - } - ] - }, - "transfer": { - "denom_traces": [], - "params": { "receive_enabled": true, "send_enabled": true }, - "port_id": "transfer" - }, - "upgrade": {}, - "feemarket": { - "params": { - "no_base_fee": true, - "base_fee_change_denominator": 8, - "elasticity_multiplier": 2, - "enable_height": "0", - "base_fee": "1000000000" - }, - "block_gas": "0" - }, - "authz": { - "authorization": [] - }, - "savings": { - "deposits": [], - "params": { - "supported_denoms": [] - } - } - } -} diff --git a/proto/kava/evmutil/v1beta1/conversion_pair.proto b/proto/kava/evmutil/v1beta1/conversion_pair.proto new file mode 100644 index 00000000..0d95f4d9 --- /dev/null +++ b/proto/kava/evmutil/v1beta1/conversion_pair.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package kava.evmutil.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/kava-labs/kava/x/evmutil/types"; +option (gogoproto.equal_all) = true; +option (gogoproto.verbose_equal_all) = true; + +// ConversionPair defines a Kava ERC20 address and corresponding denom that is +// allowed to be converted between ERC20 and sdk.Coin +message ConversionPair { + option (gogoproto.goproto_getters) = false; + + // ERC20 address of the token on the Kava EVM + bytes kava_erc20_address = 1 [(gogoproto.customname) = "KavaERC20Address", (gogoproto.casttype) = "HexBytes"]; + + // Denom of the corresponding sdk.Coin + string denom = 2; +} diff --git a/proto/kava/evmutil/v1beta1/genesis.proto b/proto/kava/evmutil/v1beta1/genesis.proto index d6b8346d..cd595079 100644 --- a/proto/kava/evmutil/v1beta1/genesis.proto +++ b/proto/kava/evmutil/v1beta1/genesis.proto @@ -3,19 +3,24 @@ package kava.evmutil.v1beta1; import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; +import "kava/evmutil/v1beta1/conversion_pair.proto"; -option go_package = "github.com/kava-labs/kava/x/evmutil/types"; +option go_package = "github.com/kava-labs/kava/x/evmutil/types"; +option (gogoproto.equal_all) = true; +option (gogoproto.verbose_equal_all) = true; // GenesisState defines the evmutil module's genesis state. message GenesisState { option (gogoproto.goproto_getters) = false; repeated Account accounts = 1 [(gogoproto.nullable) = false]; + + // params defines all the parameters of the module. + Params params = 2 [(gogoproto.nullable) = false]; } // BalanceAccount defines an account in the evmutil module. message Account { - option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; bytes address = 1 [ @@ -30,3 +35,11 @@ message Account { (gogoproto.nullable) = false ]; } + +// Params defines the evmutil module params +message Params { + // enabled_conversion_pairs defines the list of conversion pairs allowed to be + // converted between Kava ERC20 and sdk.Coin + repeated ConversionPair enabled_conversion_pairs = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "ConversionPairs"]; +} diff --git a/proto/kava/evmutil/v1beta1/query.proto b/proto/kava/evmutil/v1beta1/query.proto new file mode 100644 index 00000000..c8e633bd --- /dev/null +++ b/proto/kava/evmutil/v1beta1/query.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package kava.evmutil.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "kava/evmutil/v1beta1/genesis.proto"; + +option go_package = "github.com/kava-labs/kava/x/evmutil/types"; +option (gogoproto.equal_all) = true; +option (gogoproto.verbose_equal_all) = true; + +// Query defines the gRPC querier service for evmutil module +service Query { + // Params queries all parameters of the evmutil module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/kava/evmutil/v1beta1/params"; + } +} + +// QueryParamsRequest defines the request type for querying x/evmutil parameters. +message QueryParamsRequest {} + +// QueryParamsResponse defines the response type for querying x/evmutil parameters. +message QueryParamsResponse { + Params params = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/kava/evmutil/v1beta1/tx.proto b/proto/kava/evmutil/v1beta1/tx.proto new file mode 100644 index 00000000..d1c4fb72 --- /dev/null +++ b/proto/kava/evmutil/v1beta1/tx.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; +package kava.evmutil.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/kava-labs/kava/x/evmutil/types"; +option (gogoproto.equal_all) = true; +option (gogoproto.verbose_equal_all) = true; + +// Msg defines the evmutil Msg service. +service Msg { + // ConvertCoinToERC20 defines a method for converting sdk.Coin to Kava ERC20. + rpc ConvertCoinToERC20(MsgConvertCoinToERC20) returns (MsgConvertCoinToERC20Response); + + // ConvertERC20ToCoin defines a method for converting Kava ERC20 to sdk.Coin. + rpc ConvertERC20ToCoin(MsgConvertERC20ToCoin) returns (MsgConvertERC20ToCoinResponse); +} + +// MsgConvertCoinToERC20 defines a conversion from sdk.Coin to Kava ERC20. +message MsgConvertCoinToERC20 { + // Kava bech32 address initiating the conversion. + string initiator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // EVM 0x hex address that will receive the converted Kava ERC20 tokens. + string receiver = 2; + // Amount is the sdk.Coin amount to convert. + cosmos.base.v1beta1.Coin amount = 3; +} + +// MsgConvertCoinToERC20Response defines the response value from Msg/ConvertCoinToERC20. +message MsgConvertCoinToERC20Response {} + +// MsgConvertERC20ToCoin defines a conversion from Kava ERC20 to sdk.Coin. +message MsgConvertERC20ToCoin { + // EVM 0x hex address initiating the conversion. + string initiator = 1; + // Kava bech32 address that will receive the converted sdk.Coin. + string receiver = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // EVM 0x hex address of the ERC20 contract. + string kava_erc20_address = 3 [(gogoproto.customname) = "KavaERC20Address"]; + // ERC20 token amount to convert. + string amount = 4 [ + (cosmos_proto.scalar) = "cosmos.Int", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false + ]; +} + +// MsgConvertERC20ToCoinResponse defines the response value from +// Msg/MsgConvertERC20ToCoin. +message MsgConvertERC20ToCoinResponse {} diff --git a/x/evmutil/client/cli/address.go b/x/evmutil/client/cli/address.go new file mode 100644 index 00000000..d9fca5e1 --- /dev/null +++ b/x/evmutil/client/cli/address.go @@ -0,0 +1,73 @@ +package cli + +import ( + "context" + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/kava-labs/kava/x/evmutil/types" +) + +// ParseAddrFromHexOrBech32 parses a string address that can be either a hex or +//Bech32 string. +func ParseAddrFromHexOrBech32(addrString string) (common.Address, error) { + if common.IsHexAddress(addrString) { + return common.HexToAddress(addrString), nil + } + + cfg := sdk.GetConfig() + + if !strings.HasPrefix(addrString, cfg.GetBech32AccountAddrPrefix()) { + return common.Address{}, fmt.Errorf("receiver '%s' is not a hex or bech32 address (prefix does not match)", addrString) + } + + accAddr, err := sdk.AccAddressFromBech32(addrString) + if err != nil { + return common.Address{}, fmt.Errorf("receiver '%s' is not a hex or bech32 address (could not parse as bech32 string)", addrString) + } + + return common.BytesToAddress(accAddr), nil + +} + +// ParseOrQueryConversionPairAddress returns an EVM address of the provided +// ERC20 contract address string or denom. If an address string, just returns +// the parsed address. If a denom, fetches params, searches the enabled +// conversion pairs, and returns corresponding ERC20 contract address. +func ParseOrQueryConversionPairAddress( + queryClient types.QueryClient, + addrOrDenom string, +) (common.Address, error) { + if common.IsHexAddress(addrOrDenom) { + return common.HexToAddress(addrOrDenom), nil + } + + if err := sdk.ValidateDenom(addrOrDenom); err != nil { + return common.Address{}, fmt.Errorf( + "Kava ERC20 '%s' is not a valid hex address or denom", + addrOrDenom, + ) + } + + // Valid denom, try looking up as denom to get corresponding Kava ERC20 address + paramsRes, err := queryClient.Params( + context.Background(), + &types.QueryParamsRequest{}, + ) + if err != nil { + return common.Address{}, err + } + + for _, pair := range paramsRes.Params.EnabledConversionPairs { + if pair.Denom == addrOrDenom { + return pair.GetAddress().Address, nil + } + } + + return common.Address{}, fmt.Errorf( + "Kava ERC20 '%s' is not a valid hex address or denom (did not match any denoms in queried enabled conversion pairs)", + addrOrDenom, + ) +} diff --git a/x/evmutil/client/cli/evm.go b/x/evmutil/client/cli/evm.go new file mode 100644 index 00000000..5bd2abdf --- /dev/null +++ b/x/evmutil/client/cli/evm.go @@ -0,0 +1,129 @@ +package cli + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/tharsis/ethermint/crypto/hd" + "github.com/tharsis/ethermint/server/config" + etherminttypes "github.com/tharsis/ethermint/types" + evmtypes "github.com/tharsis/ethermint/x/evm/types" + feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types" +) + +// CanSignEthTx returns an error if the signing key algorithm is not eth_secp256k1. +func CanSignEthTx(ctx client.Context) error { + keyInfo, err := ctx.Keyring.KeyByAddress(ctx.FromAddress) + if err != nil { + return err + } + + if keyInfo.GetAlgo() != hd.EthSecp256k1Type { + return fmt.Errorf("from address does not support %v", hd.EthSecp256k1Type) + } + + return nil +} + +// PackContractCallData creates a smart contract method call data with the +// provided method and args. +func PackContractCallData(abi abi.ABI, method string, args ...interface{}) ([]byte, error) { + data, err := abi.Pack(method, args...) + if err != nil { + return nil, fmt.Errorf("failed to create transaction data: %w", err) + } + + return data, nil +} + +// CreateEthCallContractTx creates and signs a Eth transaction wrapped in a +// cosmos Tx. +func CreateEthCallContractTx( + ctx client.Context, + contractAddr *common.Address, + data []byte, +) (signing.Tx, error) { + evmQueryClient := evmtypes.NewQueryClient(ctx) + feemarketQueryClient := feemarkettypes.NewQueryClient(ctx) + + chainID, err := etherminttypes.ParseChainID(ctx.ChainID) + if err != nil { + return nil, fmt.Errorf("failed to parse chain ID: %w", err) + } + + evmParamsRes, err := evmQueryClient.Params(context.Background(), &evmtypes.QueryParamsRequest{}) + if err != nil { + return nil, fmt.Errorf("failed to fetch evm params: %w", err) + } + + // Estimate Gas + from := common.BytesToAddress(ctx.FromAddress.Bytes()) + transactionArgs := evmtypes.TransactionArgs{ + From: &from, + To: contractAddr, + Data: (*hexutil.Bytes)(&data), + } + + args, err := json.Marshal(transactionArgs) + if err != nil { + return nil, fmt.Errorf("failed to marshal transaction args for gas estimate: %w", err) + } + + res, err := evmQueryClient.EstimateGas(context.Background(), &evmtypes.EthCallRequest{ + Args: args, + GasCap: config.DefaultGasCap, + }) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas from EVM: %w", err) + } + + // Fetch base fee + basefeeRes, err := feemarketQueryClient.BaseFee( + context.Background(), + &feemarkettypes.QueryBaseFeeRequest{}, + ) + if err != nil { + return nil, fmt.Errorf("failed to fetch basefee from feemarket: %w", err) + } + + // Fetch account nonce, ignore error to use use 0 nonce if first tx + _, accSeq, _ := ctx.AccountRetriever.GetAccountNumberSequence(ctx, ctx.FromAddress) + + // Create MsgEthereumTx + ethTx := evmtypes.NewTx( + chainID, + accSeq, // nonce + contractAddr, // to + nil, // amount + res.Gas, // gasLimit + nil, // gasPrice + basefeeRes.BaseFee.BigInt(), // gasFeeCap + big.NewInt(1), // gasTipCap + data, // input + ðtypes.AccessList{}, + ) + + // Must set from address before signing + ethTx.From = from.String() + + // Sign Ethereum TX (not the cosmos Msg) + signer := ethtypes.LatestSignerForChainID(chainID) + + // Must sign with a `/ethermint.crypto.v1.ethsecp256k1.PubKey` and not + // `/cosmos.crypto.secp256k1.PubKey` or this will panic with the following: + // panic: wrong size for signature: got 64, want 65 + if err := ethTx.Sign(signer, ctx.Keyring); err != nil { + return nil, err + } + + return ethTx.BuildTx(ctx.TxConfig.NewTxBuilder(), evmParamsRes.Params.EvmDenom) +} diff --git a/x/evmutil/client/cli/query.go b/x/evmutil/client/cli/query.go new file mode 100644 index 00000000..14b387da --- /dev/null +++ b/x/evmutil/client/cli/query.go @@ -0,0 +1,63 @@ +package cli + +import ( + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + "github.com/kava-labs/kava/x/evmutil/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd() *cobra.Command { + evmutilQueryCmd := &cobra.Command{ + Use: "evmutil", + Short: "Querying commands for the evmutil module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmds := []*cobra.Command{ + QueryParamsCmd(), + } + + for _, cmd := range cmds { + flags.AddQueryFlagsToCmd(cmd) + } + + evmutilQueryCmd.AddCommand(cmds...) + + return evmutilQueryCmd +} + +// QueryParamsCmd queries the evmutil module parameters +func QueryParamsCmd() *cobra.Command { + return &cobra.Command{ + Use: "params", + Short: "Query the evmutil module parameters", + Example: fmt.Sprintf( + "%[1]s q %[2]s params", + version.AppName, types.ModuleName, + ), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Params) + }, + } +} diff --git a/x/evmutil/client/cli/tx.go b/x/evmutil/client/cli/tx.go new file mode 100644 index 00000000..88392ecf --- /dev/null +++ b/x/evmutil/client/cli/tx.go @@ -0,0 +1,122 @@ +package cli + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + + "github.com/kava-labs/kava/x/evmutil/types" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmds := []*cobra.Command{ + getCmdMsgConvertCoinToERC20(), + getCmdConvertERC20ToCoin(), + } + + for _, cmd := range cmds { + flags.AddTxFlagsToCmd(cmd) + } + + txCmd.AddCommand(cmds...) + + return txCmd +} + +func getCmdMsgConvertCoinToERC20() *cobra.Command { + return &cobra.Command{ + Use: "convert-coin-to-erc20 [Kava ERC20 address] [coin]", + Short: "converts sdk.Coin to erc20 tokens on Kava eth co-chain", + Example: fmt.Sprintf( + `%s tx %s convert-coin-to-erc20 0x7Bbf300890857b8c241b219C6a489431669b3aFA 500000000erc20/usdc --from --gas 2000000`, + version.AppName, types.ModuleName, + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + receiver := args[0] + if !common.IsHexAddress(receiver) { + return fmt.Errorf("receiver '%s' is an invalid hex address", args[0]) + } + + coin, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return err + } + + signer := clientCtx.GetFromAddress() + msg := types.NewMsgConvertCoinToERC20(signer.String(), receiver, coin) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } +} + +func getCmdConvertERC20ToCoin() *cobra.Command { + return &cobra.Command{ + Use: "convert-erc20-to-coin [Kava receiver address] [Kava ERC20 address] [amount]", + Short: "burns ERC20 tokens on Kava EVM co-chain and unlocks on Ethereum", + Example: fmt.Sprintf(` +%[1]s tx %[2]s convert-erc20-to-coin kava10wlnqzyss4accfqmyxwx5jy5x9nfkwh6qm7n4t 0xeA7100edA2f805356291B0E55DaD448599a72C6d 1000000000000000 --from --gas 1000000 +`, version.AppName, types.ModuleName, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + receiver, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return fmt.Errorf("receiver '%s' is not a bech32 address", args[0]) + } + + signer := clientCtx.GetFromAddress() + initiator, err := ParseAddrFromHexOrBech32(signer.String()) + if err != nil { + return err + } + + amount, ok := sdk.NewIntFromString(args[2]) + if !ok { + return fmt.Errorf("amount '%s' is invalid", args[2]) + } + + if !common.IsHexAddress(args[1]) { + return fmt.Errorf("contractAddr '%s' is not a hex address", args[1]) + } + contractAddr := types.NewInternalEVMAddress(common.HexToAddress(args[1])) + msg := types.NewMsgConvertERC20ToCoin(types.NewInternalEVMAddress(initiator), receiver, contractAddr, amount) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } +} diff --git a/x/evmutil/client/cli/tx_util.go b/x/evmutil/client/cli/tx_util.go new file mode 100644 index 00000000..e496e668 --- /dev/null +++ b/x/evmutil/client/cli/tx_util.go @@ -0,0 +1,86 @@ +package cli + +import ( + "bufio" + "fmt" + "os" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// GenerateOrBroadcastTx checks CLI flags and generates or broadcasts a +// transaction this is used over tx.GenerateOrBroadcastTxCLI as it does not sign +// the message. +func GenerateOrBroadcastTx(clientCtx client.Context, signingTx signing.Tx) error { + // These manual flag checks are required as we use broadcast the tx + // directly via BroadcastTx instead of tx.GenerateOrBroadcastTxCLI + // which handles flags for us. + + if clientCtx.GenerateOnly { + if err := PrintTx(clientCtx, signingTx); err != nil { + return err + } + } + + if err := ConfirmTx(clientCtx, signingTx); err != nil { + return err + } + + txBytes, err := clientCtx.TxConfig.TxEncoder()(signingTx) + if err != nil { + return err + } + + res, err := clientCtx.BroadcastTx(txBytes) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) +} + +// PrintTx outputs a signing.Tx in JSON format, ie. when the GenerateOnly flag +// is enabled. +func PrintTx(clientCtx client.Context, signingTx signing.Tx) error { + json, err := clientCtx.TxConfig.TxJSONEncoder()(signingTx) + if err != nil { + return err + } + + return clientCtx.PrintString(fmt.Sprintf("%s\n", json)) +} + +// ConfirmTx outputs the transaction to be signed and requests confirmation +// if the SkipConfirm flag is not enabled. +func ConfirmTx(clientCtx client.Context, signingTx signing.Tx) error { + if clientCtx.SkipConfirm { + return nil + } + + out, err := clientCtx.TxConfig.TxJSONEncoder()(signingTx) + if err != nil { + return err + } + + _, err = fmt.Fprintf(os.Stderr, "%s\n\n", out) + if err != nil { + return err + } + + buf := bufio.NewReader(os.Stdin) + ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf, os.Stderr) + if err != nil { + return err + } + + if !ok { + _, err = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction") + if err != nil { + return err + } + } + + return nil +} diff --git a/x/evmutil/genesis.go b/x/evmutil/genesis.go index ef6d473c..80a0d1b1 100644 --- a/x/evmutil/genesis.go +++ b/x/evmutil/genesis.go @@ -15,6 +15,8 @@ func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, gs *types.GenesisState) panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err)) } + keeper.SetParams(ctx, gs.Params) + for _, account := range gs.Accounts { keeper.SetAccount(ctx, account) } @@ -23,5 +25,5 @@ func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, gs *types.GenesisState) // ExportGenesis returns a GenesisState for a given context and keeper. func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState { accounts := keeper.GetAllAccounts(ctx) - return types.NewGenesisState(accounts) + return types.NewGenesisState(accounts, keeper.GetParams(ctx)) } diff --git a/x/evmutil/genesis_test.go b/x/evmutil/genesis_test.go index de2646f6..cd29145e 100644 --- a/x/evmutil/genesis_test.go +++ b/x/evmutil/genesis_test.go @@ -20,9 +20,12 @@ func (suite *genesisTestSuite) SetupTest() { } func (s *genesisTestSuite) TestInitGenesis_SetAccounts() { - gs := types.NewGenesisState([]types.Account{ - {Address: s.Addrs[0], Balance: sdk.NewInt(100)}, - }) + gs := types.NewGenesisState( + []types.Account{ + {Address: s.Addrs[0], Balance: sdk.NewInt(100)}, + }, + types.DefaultParams(), + ) accounts := s.Keeper.GetAllAccounts(s.Ctx) s.Require().Len(accounts, 0) evmutil.InitGenesis(s.Ctx, s.Keeper, gs) @@ -33,10 +36,30 @@ func (s *genesisTestSuite) TestInitGenesis_SetAccounts() { s.Require().Equal(account.Balance, sdk.NewInt(100)) } +func (s *genesisTestSuite) TestInitGenesis_SetParams() { + params := types.DefaultParams() + conversionPair := types.ConversionPair{ + KavaERC20Address: testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").Bytes(), + Denom: "weth", + } + params.EnabledConversionPairs = []types.ConversionPair{conversionPair} + gs := types.NewGenesisState( + []types.Account{}, + params, + ) + evmutil.InitGenesis(s.Ctx, s.Keeper, gs) + params = s.Keeper.GetParams(s.Ctx) + s.Require().Len(params.EnabledConversionPairs, 1) + s.Require().Equal(conversionPair, params.EnabledConversionPairs[0]) +} + func (s *genesisTestSuite) TestInitGenesis_ValidateFail() { - gs := types.NewGenesisState([]types.Account{ - {Address: s.Addrs[0], Balance: sdk.NewInt(-100)}, - }) + gs := types.NewGenesisState( + []types.Account{ + {Address: s.Addrs[0], Balance: sdk.NewInt(-100)}, + }, + types.DefaultParams(), + ) s.Require().Panics(func() { evmutil.InitGenesis(s.Ctx, s.Keeper, gs) }) @@ -50,8 +73,16 @@ func (s *genesisTestSuite) TestExportGenesis() { for _, account := range accounts { s.Keeper.SetAccount(s.Ctx, account) } + params := types.DefaultParams() + params.EnabledConversionPairs = []types.ConversionPair{ + { + KavaERC20Address: testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").Bytes(), + Denom: "weth"}, + } + s.Keeper.SetParams(s.Ctx, params) gs := evmutil.ExportGenesis(s.Ctx, s.Keeper) s.Require().Equal(gs.Accounts, accounts) + s.Require().Equal(params, gs.Params) } func TestGenesisTestSuite(t *testing.T) { diff --git a/x/evmutil/keeper/conversion.go b/x/evmutil/keeper/conversion.go new file mode 100644 index 00000000..213c6819 --- /dev/null +++ b/x/evmutil/keeper/conversion.go @@ -0,0 +1,223 @@ +package keeper + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/kava-labs/kava/x/evmutil/types" +) + +// MintConversionPairCoin mints the given amount of a ConversionPair denom and +// sends it to the provided address. +func (k Keeper) MintConversionPairCoin( + ctx sdk.Context, + pair types.ConversionPair, + amount *big.Int, + recipient sdk.AccAddress, +) (sdk.Coin, error) { + coin := sdk.NewCoin(pair.Denom, sdk.NewIntFromBigInt(amount)) + coins := sdk.NewCoins(coin) + + if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, coins); err != nil { + return sdk.Coin{}, err + } + + if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, coins); err != nil { + return sdk.Coin{}, err + } + + return coin, nil +} + +// BurnConversionPairCoin transfers the provided amount to the module account +// then burns it. +func (k Keeper) BurnConversionPairCoin( + ctx sdk.Context, + pair types.ConversionPair, + coin sdk.Coin, + account sdk.AccAddress, +) error { + coins := sdk.NewCoins(coin) + + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, account, types.ModuleName, coins); err != nil { + return err + } + + if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, coins); err != nil { + return err + } + + return nil +} + +// ConvertCoinToERC20 converts an sdk.Coin from the originating account to an +// ERC20 to the receiver account. +func (k Keeper) ConvertCoinToERC20( + ctx sdk.Context, + initiatorAccount sdk.AccAddress, + receiverAccount types.InternalEVMAddress, + coin sdk.Coin, +) error { + pair, err := k.GetEnabledConversionPairFromDenom(ctx, coin.Denom) + if err != nil { + // Coin not in enabled conversion pair list + return err + } + + if err := k.BurnConversionPairCoin(ctx, pair, coin, initiatorAccount); err != nil { + return err + } + + if err := k.UnlockERC20Tokens(ctx, pair, coin.Amount.BigInt(), receiverAccount); err != nil { + return err + } + + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeConvertCoinToERC20, + sdk.NewAttribute(types.AttributeKeyInitiator, initiatorAccount.String()), + sdk.NewAttribute(types.AttributeKeyReceiver, receiverAccount.String()), + sdk.NewAttribute(types.AttributeKeyERC20Address, pair.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyAmount, coin.String()), + )) + + return nil +} + +// ConvertERC20ToCoin converts an ERC20 coin from the originating account to an +// sdk.Coin to the receiver account. +func (k Keeper) ConvertERC20ToCoin( + ctx sdk.Context, + initiator types.InternalEVMAddress, + receiver sdk.AccAddress, + contractAddr types.InternalEVMAddress, + amount sdk.Int, +) error { + // Check that the contract is enabled to convert to coin + pair, err := k.GetEnabledConversionPairFromERC20Address(ctx, contractAddr) + if err != nil { + // contract not in enabled conversion pair list + return err + } + + // lock erc20 tokens + if err := k.LockERC20Tokens(ctx, pair, amount.BigInt(), initiator); err != nil { + return err + } + + // mint conversion pair coin + coin, err := k.MintConversionPairCoin(ctx, pair, amount.BigInt(), receiver) + if err != nil { + return err + } + + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeConvertERC20ToCoin, + sdk.NewAttribute(types.AttributeKeyERC20Address, contractAddr.String()), + sdk.NewAttribute(types.AttributeKeyInitiator, initiator.String()), + sdk.NewAttribute(types.AttributeKeyReceiver, receiver.String()), + sdk.NewAttribute(types.AttributeKeyAmount, coin.String()), + )) + + return nil +} + +// UnlockERC20Tokens transfers the given amount of a conversion pair ERC20 token +// to the provided account. +func (k Keeper) UnlockERC20Tokens( + ctx sdk.Context, + pair types.ConversionPair, + amount *big.Int, + receiver types.InternalEVMAddress, +) error { + contractAddr := pair.GetAddress() + startBal, err := k.QueryERC20BalanceOf(ctx, contractAddr, receiver) + if err != nil { + return sdkerrors.Wrapf(types.ErrEVMCall, "failed to retrieve balance %s", err.Error()) + } + res, err := k.CallEVM( + ctx, + types.ERC20MintableBurnableContract.ABI, // abi + types.ModuleEVMAddress, // from addr + pair.GetAddress(), // contract addr + "transfer", // method + // Transfer ERC20 args + receiver.Address, + amount, + ) + if err != nil { + return err + } + + // validate end bal + endBal, err := k.QueryERC20BalanceOf(ctx, contractAddr, receiver) + if err != nil { + return sdkerrors.Wrapf(types.ErrEVMCall, "failed to retrieve balance %s", err.Error()) + } + expectedEndBal := big.NewInt(0).Add(startBal, amount) + if expectedEndBal.Cmp(endBal) != 0 { + return sdkerrors.Wrapf( + types.ErrBalanceInvariance, + "invalid token balance - expected: %v, actual: %v", + expectedEndBal, endBal, + ) + } + + // Check for unexpected `Approval` event in logs + if err := k.monitorApprovalEvent(res); err != nil { + return err + } + + return err +} + +// LockERC20Tokens transfers the given amount of a conversion pair ERC20 token +// from the initiator account to the module account. +func (k Keeper) LockERC20Tokens( + ctx sdk.Context, + pair types.ConversionPair, + amount *big.Int, + initiator types.InternalEVMAddress, +) error { + contractAddr := pair.GetAddress() + initiatorStartBal, err := k.QueryERC20BalanceOf(ctx, contractAddr, initiator) + if err != nil { + return sdkerrors.Wrapf(types.ErrEVMCall, "failed to retrieve balance %s", err.Error()) + } + + res, err := k.CallEVM( + ctx, + types.ERC20MintableBurnableContract.ABI, // abi + initiator.Address, // from addr + contractAddr, // contract addr + "transfer", // method + // Transfer ERC20 args + types.ModuleEVMAddress, + amount, + ) + if err != nil { + return err + } + + // validate end bal + initiatorEndBal, err := k.QueryERC20BalanceOf(ctx, contractAddr, initiator) + if err != nil { + return sdkerrors.Wrapf(types.ErrEVMCall, "failed to retrieve balance %s", err.Error()) + } + expectedEndBal := big.NewInt(0).Sub(initiatorStartBal, amount) + if expectedEndBal.Cmp(initiatorEndBal) != 0 { + return sdkerrors.Wrapf( + types.ErrBalanceInvariance, + "invalid token balance - expected: %v, actual: %v", + expectedEndBal, initiatorEndBal, + ) + } + + // Check for unexpected `Approval` event in logs + if err := k.monitorApprovalEvent(res); err != nil { + return err + } + + return err +} diff --git a/x/evmutil/keeper/conversion_test.go b/x/evmutil/keeper/conversion_test.go new file mode 100644 index 00000000..f5edc77f --- /dev/null +++ b/x/evmutil/keeper/conversion_test.go @@ -0,0 +1,345 @@ +package keeper_test + +import ( + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" + + "github.com/kava-labs/kava/x/evmutil/testutil" + "github.com/kava-labs/kava/x/evmutil/types" +) + +type ConversionTestSuite struct { + testutil.Suite +} + +func TestConversionTestSuite(t *testing.T) { + suite.Run(t, new(ConversionTestSuite)) +} + +func (suite *ConversionTestSuite) TestMint() { + pair := types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000A"), + "erc20/usdc", + ) + + amount := big.NewInt(100) + recipient := suite.Key1.PubKey().Address().Bytes() + + coin, err := suite.Keeper.MintConversionPairCoin(suite.Ctx, pair, amount, recipient) + suite.Require().NoError(err) + suite.Require().Equal(sdk.NewCoin(pair.Denom, sdk.NewIntFromBigInt(amount)), coin) + + bal := suite.App.GetBankKeeper().GetBalance(suite.Ctx, recipient, pair.Denom) + suite.Require().Equal(amount, bal.Amount.BigInt(), "minted amount should increase balance") +} + +func (suite *ConversionTestSuite) TestBurn_InsufficientBalance() { + pair := types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000A"), + "erc20/usdc", + ) + + amount := sdk.NewInt(100) + recipient := suite.Key1.PubKey().Address().Bytes() + + err := suite.Keeper.BurnConversionPairCoin(suite.Ctx, pair, sdk.NewCoin(pair.Denom, amount), recipient) + suite.Require().Error(err) + suite.Require().Equal("0erc20/usdc is smaller than 100erc20/usdc: insufficient funds", err.Error()) +} + +func (suite *ConversionTestSuite) TestBurn() { + pair := types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000A"), + "erc20/usdc", + ) + + amount := sdk.NewInt(100) + recipient := suite.Key1.PubKey().Address().Bytes() + + coin, err := suite.Keeper.MintConversionPairCoin(suite.Ctx, pair, amount.BigInt(), recipient) + suite.Require().NoError(err) + suite.Require().Equal(sdk.NewCoin(pair.Denom, amount), coin) + + bal := suite.App.GetBankKeeper().GetBalance(suite.Ctx, recipient, pair.Denom) + suite.Require().Equal(amount, bal.Amount, "minted amount should increase balance") + + err = suite.Keeper.BurnConversionPairCoin(suite.Ctx, pair, sdk.NewCoin(pair.Denom, amount), recipient) + suite.Require().NoError(err) + + bal = suite.App.GetBankKeeper().GetBalance(suite.Ctx, recipient, pair.Denom) + suite.Require().Equal(sdk.ZeroInt(), bal.Amount, "balance should be zero after burn") +} + +func (suite *ConversionTestSuite) TestUnlockERC20Tokens() { + contractAddr := suite.DeployERC20() + + pair := types.NewConversionPair( + contractAddr, + "erc20/usdc", + ) + + amount := big.NewInt(100) + recipient := types.NewInternalEVMAddress(common.BytesToAddress(suite.Key1.PubKey().Address())) + moduleAddr := types.NewInternalEVMAddress(types.ModuleEVMAddress) + + // Mint some initial balance for module account to transfer + err := suite.Keeper.MintERC20( + suite.Ctx, + pair.GetAddress(), // contractAddr + moduleAddr, //receiver + amount, + ) + suite.Require().NoError(err) + + err = suite.Keeper.UnlockERC20Tokens(suite.Ctx, pair, amount, recipient) + suite.Require().NoError(err) + + // Check balance of recipient + bal := suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + recipient, + ) + suite.Require().Equal(amount, bal, "balance should increase by unlock amount") + + // Check balance of module account + bal = suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + moduleAddr, + ) + suite.Require().Equal( + // String() due to non-equal struct values for 0 + big.NewInt(0).String(), + bal.String(), + "balance should decrease module account by unlock amount", + ) +} + +func (suite *ConversionTestSuite) TestUnlockERC20Tokens_Insufficient() { + contractAddr := suite.DeployERC20() + + pair := types.NewConversionPair( + contractAddr, + "erc20/usdc", + ) + + amount := big.NewInt(100) + recipient := types.NewInternalEVMAddress(common.BytesToAddress(suite.Key1.PubKey().Address())) + + // Module account has 0 balance, cannot unlock + err := suite.Keeper.UnlockERC20Tokens(suite.Ctx, pair, amount, recipient) + suite.Require().Error(err) + suite.Require().Contains(err.Error(), "execution reverted: ERC20: transfer amount exceeds balance") +} + +func (suite *ConversionTestSuite) TestConvertCoinToERC20() { + contractAddr := suite.DeployERC20() + + pair := types.NewConversionPair( + contractAddr, + "erc20/usdc", + ) + + amount := big.NewInt(100) + originAcc := sdk.AccAddress(suite.Key1.PubKey().Address().Bytes()) + recipientAcc := types.NewInternalEVMAddress(common.BytesToAddress(suite.Key2.PubKey().Address())) + moduleAddr := types.NewInternalEVMAddress(types.ModuleEVMAddress) + + // Starting balance of origin account + coin, err := suite.Keeper.MintConversionPairCoin(suite.Ctx, pair, amount, originAcc) + suite.Require().NoError(err) + suite.Require().Equal(sdk.NewCoin(pair.Denom, sdk.NewIntFromBigInt(amount)), coin) + + // Mint same initial balance for module account as backing erc20 supply + err = suite.Keeper.MintERC20( + suite.Ctx, + pair.GetAddress(), // contractAddr + moduleAddr, //receiver + amount, + ) + suite.Require().NoError(err) + + err = suite.Keeper.ConvertCoinToERC20( + suite.Ctx, + originAcc, + recipientAcc, + sdk.NewCoin(pair.Denom, sdk.NewIntFromBigInt(amount)), + ) + suite.Require().NoError(err) + + // Source should decrease + bal := suite.App.GetBankKeeper().GetBalance(suite.Ctx, originAcc, pair.Denom) + suite.Require().Equal(sdk.ZeroInt(), bal.Amount, "conversion should decrease source balance") + + // Module bal should also decrease + moduleBal := suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + moduleAddr, + ) + suite.Require().Equal( + // String() due to non-equal struct values for 0 + big.NewInt(0).String(), + moduleBal.String(), + "balance should decrease module account by unlock amount", + ) + + // Recipient balance should increase by same amount + recipientBal := suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + recipientAcc, + ) + suite.Require().Equal( + // String() due to non-equal struct values for 0 + amount, + recipientBal, + "recipient balance should increase", + ) + + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + types.EventTypeConvertCoinToERC20, + sdk.NewAttribute(types.AttributeKeyInitiator, originAcc.String()), + sdk.NewAttribute(types.AttributeKeyReceiver, recipientAcc.String()), + sdk.NewAttribute(types.AttributeKeyERC20Address, pair.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyAmount, coin.String()), + )) +} + +func (suite *ConversionTestSuite) TestConvertCoinToERC20_InsufficientBalance() { + contractAddr := suite.DeployERC20() + + pair := types.NewConversionPair( + contractAddr, + "erc20/usdc", + ) + + amount := big.NewInt(100) + originAcc := sdk.AccAddress(suite.Key1.PubKey().Address().Bytes()) + recipientAcc := types.NewInternalEVMAddress(common.BytesToAddress(suite.Key2.PubKey().Address())) + + err := suite.Keeper.ConvertCoinToERC20( + suite.Ctx, + originAcc, + recipientAcc, + sdk.NewCoin(pair.Denom, sdk.NewIntFromBigInt(amount)), + ) + + suite.Require().Error(err) + suite.Require().Equal("0erc20/usdc is smaller than 100erc20/usdc: insufficient funds", err.Error()) +} + +func (suite *ConversionTestSuite) TestConvertCoinToERC20_NotEnabled() { + contractAddr := suite.DeployERC20() + + pair := types.NewConversionPair( + contractAddr, + "erc20/notenabled", + ) + + amount := big.NewInt(100) + originAcc := sdk.AccAddress(suite.Key1.PubKey().Address().Bytes()) + recipientAcc := types.NewInternalEVMAddress(common.BytesToAddress(suite.Key2.PubKey().Address())) + + err := suite.Keeper.ConvertCoinToERC20( + suite.Ctx, + originAcc, + recipientAcc, + sdk.NewCoin(pair.Denom, sdk.NewIntFromBigInt(amount)), + ) + + suite.Require().Error(err) + suite.Require().Equal("erc20/notenabled: ERC20 token not enabled to convert to sdk.Coin", err.Error()) +} + +func (suite *ConversionTestSuite) TestConvertERC20ToCoin() { + contractAddr := suite.DeployERC20() + + pair := types.NewConversionPair( + contractAddr, + "erc20/usdc", + ) + + totalAmt := big.NewInt(100) + userAddr := sdk.AccAddress(suite.Key1.PubKey().Address().Bytes()) + userEvmAddr := types.NewInternalEVMAddress(common.BytesToAddress(suite.Key1.PubKey().Address())) + + // Mint same initial balance for user account + err := suite.Keeper.MintERC20( + suite.Ctx, + pair.GetAddress(), // contractAddr + userEvmAddr, //receiver + totalAmt, + ) + suite.Require().NoError(err) + + convertAmt := sdk.NewInt(50) + err = suite.Keeper.ConvertERC20ToCoin( + suite.Ctx, + userEvmAddr, + userAddr, + pair.GetAddress(), + convertAmt, + ) + suite.Require().NoError(err) + + // bank balance should decrease + bal := suite.App.GetBankKeeper().GetBalance(suite.Ctx, userAddr, pair.Denom) + suite.Require().Equal(convertAmt, bal.Amount, "conversion should decrease source balance") + + // Module bal should also decrease + userBal := suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + userEvmAddr, + ) + suite.Require().Equal( + // String() due to non-equal struct values for 0 + big.NewInt(50).String(), + userBal.String(), + "balance should decrease module account by unlock amount", + ) + + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + types.EventTypeConvertERC20ToCoin, + sdk.NewAttribute(types.AttributeKeyERC20Address, pair.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyInitiator, userEvmAddr.String()), + sdk.NewAttribute(types.AttributeKeyReceiver, userAddr.String()), + sdk.NewAttribute(types.AttributeKeyAmount, sdk.NewCoin(pair.Denom, convertAmt).String()), + ), + ) +} + +func (suite *ConversionTestSuite) TestConvertERC20ToCoin_EmptyContract() { + contractAddr := testutil.MustNewInternalEVMAddressFromString("0x15932E26f5BD4923d46a2b205191C4b5d5f43FE3") + pair := types.NewConversionPair( + contractAddr, + "erc20/usdc", + ) + + userAddr := sdk.AccAddress(suite.Key1.PubKey().Address().Bytes()) + userEvmAddr := types.NewInternalEVMAddress(common.BytesToAddress(suite.Key1.PubKey().Address())) + convertAmt := sdk.NewInt(100) + + // Trying to convert erc20 from an empty contract should fail + err := suite.Keeper.ConvertERC20ToCoin( + suite.Ctx, + userEvmAddr, + userAddr, + pair.GetAddress(), + convertAmt, + ) + suite.Require().Error(err) + suite.Require().ErrorContains(err, "contract call failed: method 'balanceOf'") + + // bank balance should not change + bal := suite.App.GetBankKeeper().GetBalance(suite.Ctx, userAddr, pair.Denom) + suite.Require().Equal(sdk.ZeroInt(), bal.Amount) +} diff --git a/x/evmutil/keeper/erc20.go b/x/evmutil/keeper/erc20.go new file mode 100644 index 00000000..c51b5538 --- /dev/null +++ b/x/evmutil/keeper/erc20.go @@ -0,0 +1,144 @@ +package keeper + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + evmtypes "github.com/tharsis/ethermint/x/evm/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/kava-labs/kava/x/evmutil/types" +) + +const ( + erc20BalanceOfMethod = "balanceOf" +) + +// DeployTestMintableERC20Contract deploys an ERC20 contract on the EVM as the +// module account and returns the address of the contract. This contract has +// minting permissions for the module account. +// Derived from tharsis/evmos +// https://github.com/tharsis/evmos/blob/ee54f496551df937915ff6f74a94732a35abc505/x/erc20/keeper/evm.go +func (k Keeper) DeployTestMintableERC20Contract( + ctx sdk.Context, + name string, + symbol string, + decimals uint8, +) (types.InternalEVMAddress, error) { + ctorArgs, err := types.ERC20MintableBurnableContract.ABI.Pack( + "", // Empty string for contract constructor + name, + symbol, + decimals, + ) + if err != nil { + return types.InternalEVMAddress{}, sdkerrors.Wrapf(err, "token %v is invalid", name) + } + + data := make([]byte, len(types.ERC20MintableBurnableContract.Bin)+len(ctorArgs)) + copy( + data[:len(types.ERC20MintableBurnableContract.Bin)], + types.ERC20MintableBurnableContract.Bin, + ) + copy( + data[len(types.ERC20MintableBurnableContract.Bin):], + ctorArgs, + ) + + nonce, err := k.accountKeeper.GetSequence(ctx, types.ModuleEVMAddress.Bytes()) + if err != nil { + return types.InternalEVMAddress{}, err + } + + contractAddr := crypto.CreateAddress(types.ModuleEVMAddress, nonce) + _, err = k.CallEVMWithData(ctx, types.ModuleEVMAddress, nil, data) + if err != nil { + return types.InternalEVMAddress{}, fmt.Errorf("failed to deploy ERC20 for %s: %w", name, err) + } + + return types.NewInternalEVMAddress(contractAddr), nil +} + +// MintERC20 mints the given amount of an ERC20 token to an address. This is +// unchecked and should only be called after permission and enabled ERC20 checks. +func (k Keeper) MintERC20( + ctx sdk.Context, + contractAddr types.InternalEVMAddress, + receiver types.InternalEVMAddress, + amount *big.Int, +) error { + _, err := k.CallEVM( + ctx, + types.ERC20MintableBurnableContract.ABI, + types.ModuleEVMAddress, + contractAddr, + "mint", + // Mint ERC20 args + receiver.Address, + amount, + ) + + return err +} + +func (k Keeper) QueryERC20BalanceOf( + ctx sdk.Context, + contractAddr types.InternalEVMAddress, + account types.InternalEVMAddress, +) (*big.Int, error) { + res, err := k.CallEVM( + ctx, + types.ERC20MintableBurnableContract.ABI, + types.ModuleEVMAddress, + contractAddr, + erc20BalanceOfMethod, + // balanceOf ERC20 args + account.Address, + ) + if err != nil { + return nil, err + } + + if res.Failed() { + if res.VmError == vm.ErrExecutionReverted.Error() { + // Unpacks revert + return nil, evmtypes.NewExecErrorWithReason(res.Ret) + } + + return nil, status.Error(codes.Internal, res.VmError) + } + + anyOutput, err := types.ERC20MintableBurnableContract.ABI.Unpack(erc20BalanceOfMethod, res.Ret) + if err != nil { + return nil, fmt.Errorf( + "failed to unpack method %v response: %w", + erc20BalanceOfMethod, + err, + ) + } + + if len(anyOutput) != 1 { + return nil, fmt.Errorf( + "invalid ERC20 %v call return outputs %v, expected %v", + erc20BalanceOfMethod, + len(anyOutput), + 1, + ) + } + + bal, ok := anyOutput[0].(*big.Int) + if !ok { + return nil, fmt.Errorf( + "invalid ERC20 return type %T, expected %T", + anyOutput[0], + &big.Int{}, + ) + } + + return bal, nil +} diff --git a/x/evmutil/keeper/erc20_test.go b/x/evmutil/keeper/erc20_test.go new file mode 100644 index 00000000..b50dca24 --- /dev/null +++ b/x/evmutil/keeper/erc20_test.go @@ -0,0 +1,86 @@ +package keeper_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" + + "github.com/kava-labs/kava/x/evmutil/testutil" + "github.com/kava-labs/kava/x/evmutil/types" +) + +type ERC20TestSuite struct { + testutil.Suite + + contractAddr types.InternalEVMAddress +} + +func TestERC20TestSuite(t *testing.T) { + suite.Run(t, new(ERC20TestSuite)) +} + +func (suite *ERC20TestSuite) SetupTest() { + suite.Suite.SetupTest() + suite.contractAddr = suite.DeployERC20() +} + +func (suite *ERC20TestSuite) TestERC20QueryBalanceOf_Empty() { + bal, err := suite.Keeper.QueryERC20BalanceOf( + suite.Ctx, + suite.contractAddr, + suite.Key1Addr, + ) + suite.Require().NoError(err) + suite.Require().True(bal.Cmp(big.NewInt(0)) == 0, "balance should be 0") +} + +func (suite *ERC20TestSuite) TestERC20QueryBalanceOf_NonEmpty() { + // Mint some tokens for the address + err := suite.Keeper.MintERC20( + suite.Ctx, + suite.contractAddr, + suite.Key1Addr, + big.NewInt(10), + ) + suite.Require().NoError(err) + + bal, err := suite.Keeper.QueryERC20BalanceOf( + suite.Ctx, + suite.contractAddr, + suite.Key1Addr, + ) + suite.Require().NoError(err) + suite.Require().Equal(big.NewInt(10), bal, "balance should be 10") +} + +func (suite *ERC20TestSuite) TestERC20Mint() { + contractAddr := suite.DeployERC20() + + // We can't test mint by module account like the Unauthorized test as we + // cannot sign as the module account. Instead, we test the keeper method for + // minting. + + receiver := common.BytesToAddress(suite.Key2.PubKey().Address()) + amount := big.NewInt(1234) + err := suite.App.GetEvmutilKeeper().MintERC20(suite.Ctx, contractAddr, types.NewInternalEVMAddress(receiver), amount) + suite.Require().NoError(err) + + // Query ERC20.balanceOf() + addr := common.BytesToAddress(suite.Key1.PubKey().Address()) + res, err := suite.QueryContract( + types.ERC20MintableBurnableContract.ABI, + addr, + suite.Key1, + contractAddr, + "balanceOf", + receiver, + ) + suite.Require().NoError(err) + suite.Require().Len(res, 1) + + balance, ok := res[0].(*big.Int) + suite.Require().True(ok, "balanceOf should respond with *big.Int") + suite.Require().Equal(big.NewInt(1234), balance) +} diff --git a/x/evmutil/keeper/evm.go b/x/evmutil/keeper/evm.go new file mode 100644 index 00000000..f91fd521 --- /dev/null +++ b/x/evmutil/keeper/evm.go @@ -0,0 +1,152 @@ +// Derived from https://github.com/tharsis/evmos/blob/0bfaf0db7be47153bc651e663176ba8deca960b5/x/erc20/keeper/evm.go +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keeper + +import ( + "encoding/json" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/tharsis/ethermint/server/config" + evmtypes "github.com/tharsis/ethermint/x/evm/types" + + "github.com/kava-labs/kava/x/evmutil/types" +) + +// CallEVM performs a smart contract method call using given args +func (k Keeper) CallEVM( + ctx sdk.Context, + abi abi.ABI, + from common.Address, + contract types.InternalEVMAddress, + method string, + args ...interface{}, +) (*evmtypes.MsgEthereumTxResponse, error) { + data, err := abi.Pack(method, args...) + if err != nil { + return nil, sdkerrors.Wrap( + types.ErrABIPack, + sdkerrors.Wrap(err, "failed to create transaction data").Error(), + ) + } + + resp, err := k.CallEVMWithData(ctx, from, &contract, data) + if err != nil { + return nil, sdkerrors.Wrapf(err, "contract call failed: method '%s', contract '%s'", method, contract) + } + return resp, nil +} + +// CallEVMWithData performs a smart contract method call using contract data +// Derived from tharsis/evmos +// https://github.com/tharsis/evmos/blob/ee54f496551df937915ff6f74a94732a35abc505/x/erc20/keeper/evm.go +func (k Keeper) CallEVMWithData( + ctx sdk.Context, + from common.Address, + contract *types.InternalEVMAddress, + data []byte, +) (*evmtypes.MsgEthereumTxResponse, error) { + nonce, err := k.accountKeeper.GetSequence(ctx, from.Bytes()) + if err != nil { + return nil, err + } + + // To param needs to be nil to correctly apply txs to create contracts + // Default common.Address value is 0x0000000000000000000000000000000000000000, not nil + // which Ethermint handles differently -- erc20_test will fail + // https://github.com/tharsis/ethermint/blob/caa1c5a6c6b7ed8ba4aaf6e0b0848f6be5ba6668/x/evm/keeper/state_transition.go#L357 + var to *common.Address + if contract != nil { + to = &contract.Address + } + + transactionArgs := evmtypes.TransactionArgs{ + From: &from, + To: to, + Data: (*hexutil.Bytes)(&data), + } + + args, err := json.Marshal(transactionArgs) + if err != nil { + return nil, err + } + + // EstimateGas applies the transaction against current block state to get + // optimal gas value. Since this is done right before the ApplyMessage + // below, it should essentially do the same thing but without affecting + // state. While this is an *estimate* in regular use, this should be an + // accurate exact amount in this case, as both the chain state and tx used + // to estimate and apply are the exact same (ie. no txs between estimate and + // apply, tx order is the same, etc.) + gasRes, err := k.evmKeeper.EstimateGas(sdk.WrapSDKContext(ctx), &evmtypes.EthCallRequest{ + Args: args, + GasCap: config.DefaultGasCap, + }) + if err != nil { + return nil, sdkerrors.Wrap(evmtypes.ErrVMExecution, err.Error()) + } + + msg := ethtypes.NewMessage( + from, + to, + nonce, + big.NewInt(0), // amount + gasRes.Gas, // gasLimit + big.NewInt(0), // gasFeeCap + big.NewInt(0), // gasTipCap + big.NewInt(0), // gasPrice + data, + ethtypes.AccessList{}, // AccessList + true, // checkNonce + ) + + res, err := k.evmKeeper.ApplyMessage(ctx, msg, evmtypes.NewNoOpTracer(), true) + if err != nil { + return nil, err + } + + if res.Failed() { + return nil, sdkerrors.Wrap(evmtypes.ErrVMExecution, res.VmError) + } + + return res, nil +} + +// monitorApprovalEvent returns an error if the given transactions logs include +// an unexpected `Approval` event +func (k Keeper) monitorApprovalEvent(res *evmtypes.MsgEthereumTxResponse) error { + if res == nil || len(res.Logs) == 0 { + return nil + } + + logApprovalSig := []byte("Approval(address,address,uint256)") + logApprovalSigHash := crypto.Keccak256Hash(logApprovalSig) + + for _, log := range res.Logs { + if log.Topics[0] == logApprovalSigHash.Hex() { + return sdkerrors.Wrapf( + types.ErrUnexpectedContractEvent, "unexpected contract Approval event", + ) + } + } + + return nil +} diff --git a/x/evmutil/keeper/grpc_query.go b/x/evmutil/keeper/grpc_query.go new file mode 100644 index 00000000..93dc82c4 --- /dev/null +++ b/x/evmutil/keeper/grpc_query.go @@ -0,0 +1,35 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/x/evmutil/types" +) + +type queryServer struct { + keeper Keeper +} + +// NewQueryServerImpl creates a new server for handling gRPC queries. +func NewQueryServerImpl(k Keeper) types.QueryServer { + return &queryServer{keeper: k} +} + +var _ types.QueryServer = queryServer{} + +// Params queries module params +func (s queryServer) Params(stdCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(stdCtx) + params := s.keeper.GetParams(ctx) + + return &types.QueryParamsResponse{Params: params}, nil +} diff --git a/x/evmutil/keeper/grpc_query_test.go b/x/evmutil/keeper/grpc_query_test.go new file mode 100644 index 00000000..c010cb39 --- /dev/null +++ b/x/evmutil/keeper/grpc_query_test.go @@ -0,0 +1,37 @@ +package keeper_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/kava-labs/kava/x/evmutil/keeper" + "github.com/kava-labs/kava/x/evmutil/testutil" + "github.com/kava-labs/kava/x/evmutil/types" +) + +type GrpcQueryTestSuite struct { + testutil.Suite + + msgServer types.MsgServer +} + +func (suite *GrpcQueryTestSuite) SetupTest() { + suite.Suite.SetupTest() + suite.msgServer = keeper.NewMsgServerImpl(suite.App.GetEvmutilKeeper()) +} + +func TestGrpcQueryTestSuite(t *testing.T) { + suite.Run(t, new(GrpcQueryTestSuite)) +} + +func (suite *GrpcQueryTestSuite) TestQueryParams() { + params, err := suite.QueryClient.Params( + context.Background(), + &types.QueryParamsRequest{}, + ) + suite.Require().NoError(err) + + suite.Require().Len(params.Params.EnabledConversionPairs, 1) +} diff --git a/x/evmutil/keeper/invariants.go b/x/evmutil/keeper/invariants.go index 1a7d0109..84c5db64 100644 --- a/x/evmutil/keeper/invariants.go +++ b/x/evmutil/keeper/invariants.go @@ -11,6 +11,8 @@ import ( func RegisterInvariants(ir sdk.InvariantRegistry, bankK types.BankKeeper, k Keeper) { ir.RegisterRoute(types.ModuleName, "fully-backed", FullyBackedInvariant(bankK, k)) ir.RegisterRoute(types.ModuleName, "small-balances", SmallBalancesInvariant(bankK, k)) + // Disable this invariant due to some issues with it requiring some staking params to be set in genesis. + // ir.RegisterRoute(types.ModuleName, "backed-conversion-coins", BackedCoinsInvariant(bankK, k)) } // AllInvariants runs all invariants of the swap module @@ -19,8 +21,10 @@ func AllInvariants(bankK types.BankKeeper, k Keeper) sdk.Invariant { if res, stop := FullyBackedInvariant(bankK, k)(ctx); stop { return res, stop } - res, stop := SmallBalancesInvariant(bankK, k)(ctx) - return res, stop + if res, stop := BackedCoinsInvariant(bankK, k)(ctx); stop { + return res, stop + } + return SmallBalancesInvariant(bankK, k)(ctx) } } @@ -64,3 +68,40 @@ func SmallBalancesInvariant(_ types.BankKeeper, k Keeper) sdk.Invariant { return message, broken } } + +// BackedCoinsInvariant iterates all conversion pairs and asserts that the +// sdk.Coin balances are less than the module ERC20 balance. +// **Note:** This compares <= and not == as anyone can send tokens to the +// ERC20 contract address and break the invariant if a strict equal check. +func BackedCoinsInvariant(_ types.BankKeeper, k Keeper) sdk.Invariant { + broken := false + message := sdk.FormatInvariant( + types.ModuleName, + "backed coins broken", + "coin supply is greater than module account ERC20 tokens", + ) + + return func(ctx sdk.Context) (string, bool) { + params := k.GetParams(ctx) + for _, pair := range params.EnabledConversionPairs { + erc20Balance, err := k.QueryERC20BalanceOf( + ctx, + pair.GetAddress(), + types.NewInternalEVMAddress(types.ModuleEVMAddress), + ) + if err != nil { + panic(err) + } + + supply := k.bankKeeper.GetSupply(ctx, pair.Denom) + + // Must be true: sdk.Coin supply < ERC20 balanceOf(module account) + if supply.Amount.BigInt().Cmp(erc20Balance) > 0 { + broken = true + break + } + } + + return message, broken + } +} diff --git a/x/evmutil/keeper/invariants_test.go b/x/evmutil/keeper/invariants_test.go index e963c46f..d34719dd 100644 --- a/x/evmutil/keeper/invariants_test.go +++ b/x/evmutil/keeper/invariants_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "math/big" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -14,7 +15,8 @@ import ( type invariantTestSuite struct { testutil.Suite - invariants map[string]map[string]sdk.Invariant + invariants map[string]map[string]sdk.Invariant + contractAddr types.InternalEVMAddress } func TestInvariantTestSuite(t *testing.T) { @@ -23,6 +25,9 @@ func TestInvariantTestSuite(t *testing.T) { func (suite *invariantTestSuite) SetupTest() { suite.Suite.SetupTest() + + suite.contractAddr = suite.DeployERC20() + suite.invariants = make(map[string]map[string]sdk.Invariant) keeper.RegisterInvariants(suite, suite.BankKeeper, suite.Keeper) } @@ -40,6 +45,23 @@ func (suite *invariantTestSuite) SetupValidState() { sdk.NewCoin("ukava", sdk.NewInt(2)), // ( sum of all minor balances ) / conversion multiplier ), ) + + err := suite.Keeper.MintERC20(suite.Ctx, suite.contractAddr, suite.Key1Addr, big.NewInt(1000000)) + suite.Require().NoError(err) + + // key1 ERC20 bal -10000, sdk.Coin +1000 + // Module account balance 0 -> 1000 + _, err = suite.Keeper.CallEVM( + suite.Ctx, + types.ERC20MintableBurnableContract.ABI, + suite.Key1Addr.Address, + suite.contractAddr, + "convertToCoin", + // convertToCoin ERC20 args + suite.Key1Addr.Address, + big.NewInt(1000), + ) + suite.Require().NoError(err) } // RegisterRoutes implements sdk.InvariantRegistry diff --git a/x/evmutil/keeper/keeper.go b/x/evmutil/keeper/keeper.go index 9a3d5880..c1d67598 100644 --- a/x/evmutil/keeper/keeper.go +++ b/x/evmutil/keeper/keeper.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/kava-labs/kava/x/evmutil/types" ) @@ -13,16 +14,37 @@ import ( // Keeper of the evmutil store. // This keeper stores additional data related to evm accounts. type Keeper struct { - cdc codec.Codec - storeKey sdk.StoreKey + cdc codec.Codec + storeKey sdk.StoreKey + paramSubspace paramtypes.Subspace + bankKeeper types.BankKeeper + evmKeeper types.EvmKeeper + accountKeeper types.AccountKeeper } // NewKeeper creates an evmutil keeper. -func NewKeeper(cdc codec.Codec, storeKey sdk.StoreKey) Keeper { - return Keeper{ - cdc: cdc, - storeKey: storeKey, +func NewKeeper( + cdc codec.Codec, + storeKey sdk.StoreKey, + params paramtypes.Subspace, + bk types.BankKeeper, + ak types.AccountKeeper, +) Keeper { + if !params.HasKeyTable() { + params = params.WithKeyTable(types.ParamKeyTable()) } + + return Keeper{ + cdc: cdc, + storeKey: storeKey, + paramSubspace: params, + bankKeeper: bk, + accountKeeper: ak, + } +} + +func (k *Keeper) SetEvmKeeper(evmKeeper types.EvmKeeper) { + k.evmKeeper = evmKeeper } // GetAllAccounts returns all accounts. diff --git a/x/evmutil/keeper/msg_server.go b/x/evmutil/keeper/msg_server.go new file mode 100644 index 00000000..13263250 --- /dev/null +++ b/x/evmutil/keeper/msg_server.go @@ -0,0 +1,104 @@ +package keeper + +import ( + "context" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/x/evmutil/types" +) + +type msgServer struct { + keeper Keeper +} + +// NewMsgServerImpl returns an implementation of the evmutil MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +// ConvertCoinToERC20 handles a MsgConvertCoinToERC20 message to convert +// sdk.Coin to Kava EVM tokens. +func (s msgServer) ConvertCoinToERC20( + goCtx context.Context, + msg *types.MsgConvertCoinToERC20, +) (*types.MsgConvertCoinToERC20Response, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + initiator, err := sdk.AccAddressFromBech32(msg.Initiator) + if err != nil { + return nil, fmt.Errorf("invalid Initiator address: %w", err) + } + + receiver, err := types.NewInternalEVMAddressFromString(msg.Receiver) + if err != nil { + return nil, fmt.Errorf("invalid Receiver address: %w", err) + } + + if err := s.keeper.ConvertCoinToERC20( + ctx, + initiator, + receiver, + *msg.Amount, + ); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Initiator), + ), + ) + + return &types.MsgConvertCoinToERC20Response{}, nil +} + +// ConvertERC20ToCoin handles a MsgConvertERC20ToCoin message to convert +// sdk.Coin to Kava EVM tokens. +func (s msgServer) ConvertERC20ToCoin( + goCtx context.Context, + msg *types.MsgConvertERC20ToCoin, +) (*types.MsgConvertERC20ToCoinResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + initiator, err := types.NewInternalEVMAddressFromString(msg.Initiator) + if err != nil { + return nil, fmt.Errorf("invalid initiator address: %w", err) + } + + receiver, err := sdk.AccAddressFromBech32(msg.Receiver) + if err != nil { + return nil, fmt.Errorf("invalid receiver address: %w", err) + } + + contractAddr, err := types.NewInternalEVMAddressFromString(msg.KavaERC20Address) + if err != nil { + return nil, fmt.Errorf("invalid contract address: %w", err) + } + + if err := s.keeper.ConvertERC20ToCoin( + ctx, + initiator, + receiver, + contractAddr, + msg.Amount, + ); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Initiator), + ), + ) + + return &types.MsgConvertERC20ToCoinResponse{}, nil +} diff --git a/x/evmutil/keeper/msg_server_test.go b/x/evmutil/keeper/msg_server_test.go new file mode 100644 index 00000000..4cc3737a --- /dev/null +++ b/x/evmutil/keeper/msg_server_test.go @@ -0,0 +1,266 @@ +package keeper_test + +import ( + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common/math" + "github.com/kava-labs/kava/x/evmutil/keeper" + "github.com/kava-labs/kava/x/evmutil/testutil" + "github.com/kava-labs/kava/x/evmutil/types" + "github.com/stretchr/testify/suite" +) + +type MsgServerSuite struct { + testutil.Suite + + msgServer types.MsgServer +} + +func (suite *MsgServerSuite) SetupTest() { + suite.Suite.SetupTest() + suite.msgServer = keeper.NewMsgServerImpl(suite.App.GetEvmutilKeeper()) +} + +func TestMsgServerSuite(t *testing.T) { + suite.Run(t, new(MsgServerSuite)) +} + +func (suite *MsgServerSuite) TestConvertCoinToERC20() { + invoker, err := sdk.AccAddressFromBech32("kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz") + suite.Require().NoError(err) + + err = suite.App.FundAccount(suite.Ctx, invoker, sdk.NewCoins(sdk.NewCoin("erc20/usdc", sdk.NewInt(10000)))) + suite.Require().NoError(err) + + contractAddr := suite.DeployERC20() + + pair := types.NewConversionPair( + contractAddr, + "erc20/usdc", + ) + + // Module account should have starting balance + pairStartingBal := big.NewInt(10000) + err = suite.Keeper.MintERC20( + suite.Ctx, + pair.GetAddress(), // contractAddr + types.NewInternalEVMAddress(types.ModuleEVMAddress), //receiver + pairStartingBal, + ) + suite.Require().NoError(err) + + type errArgs struct { + expectPass bool + contains string + } + + tests := []struct { + name string + msg types.MsgConvertCoinToERC20 + errArgs errArgs + }{ + { + "valid", + types.NewMsgConvertCoinToERC20( + invoker.String(), + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + sdk.NewCoin("erc20/usdc", sdk.NewInt(1234)), + ), + errArgs{ + expectPass: true, + }, + }, + { + "invalid - odd length hex address", + types.NewMsgConvertCoinToERC20( + invoker.String(), + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc", + sdk.NewCoin("erc20/usdc", sdk.NewInt(1234)), + ), + errArgs{ + expectPass: false, + contains: "invalid Receiver address: string is not a hex address", + }, + }, + // Amount coin is not validated by msg_server, but on msg itself + } + + for _, tc := range tests { + suite.Run(tc.name, func() { + _, err := suite.msgServer.ConvertCoinToERC20(sdk.WrapSDKContext(suite.Ctx), &tc.msg) + + if tc.errArgs.expectPass { + suite.Require().NoError(err) + + bal := suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + testutil.MustNewInternalEVMAddressFromString(tc.msg.Receiver), + ) + + suite.Require().Equal(tc.msg.Amount.Amount.BigInt(), bal, "balance should match converted amount") + + // msg server event + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, tc.msg.Initiator), + )) + + // keeper event + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + types.EventTypeConvertCoinToERC20, + sdk.NewAttribute(types.AttributeKeyInitiator, tc.msg.Initiator), + sdk.NewAttribute(types.AttributeKeyReceiver, tc.msg.Receiver), + sdk.NewAttribute(types.AttributeKeyERC20Address, pair.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyAmount, tc.msg.Amount.String()), + )) + } else { + suite.Require().Error(err) + suite.Require().Contains(err.Error(), tc.errArgs.contains) + } + }) + } +} + +func (suite *MsgServerSuite) TestConvertERC20ToCoin() { + contractAddr := suite.DeployERC20() + pair := types.NewConversionPair( + contractAddr, + "erc20/usdc", + ) + + // give invoker account some erc20 usdc to begin with + invoker := testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + pairStartingBal := big.NewInt(10_000_000) + err := suite.Keeper.MintERC20( + suite.Ctx, + pair.GetAddress(), // contractAddr + invoker, //receiver + pairStartingBal, + ) + suite.Require().NoError(err) + + invokerCosmosAddr, err := sdk.AccAddressFromHex(invoker.String()[2:]) + suite.Require().NoError(err) + + // create user account, otherwise `CallEVMWithData` will fail due to failing to get user account when finding its sequence. + err = suite.App.FundAccount(suite.Ctx, invokerCosmosAddr, sdk.NewCoins(sdk.NewCoin(pair.Denom, sdk.ZeroInt()))) + suite.Require().NoError(err) + + type errArgs struct { + expectPass bool + contains string + } + + tests := []struct { + name string + msg types.MsgConvertERC20ToCoin + approvalAmount *big.Int + errArgs errArgs + }{ + { + "valid", + types.NewMsgConvertERC20ToCoin( + invoker, + invokerCosmosAddr, + contractAddr, + sdk.NewInt(10_000), + ), + math.MaxBig256, + errArgs{ + expectPass: true, + }, + }, + { + "invalid - invalid hex address", + types.MsgConvertERC20ToCoin{ + Initiator: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc", + Receiver: invokerCosmosAddr.String(), + KavaERC20Address: contractAddr.String(), + Amount: sdk.NewInt(10_000), + }, + math.MaxBig256, + errArgs{ + expectPass: false, + contains: "invalid initiator address: string is not a hex address", + }, + }, + { + "invalid - insufficient coins", + types.NewMsgConvertERC20ToCoin( + invoker, + invokerCosmosAddr, + contractAddr, + sdk.NewIntFromBigInt(pairStartingBal).Add(sdk.OneInt()), + ), + math.MaxBig256, + errArgs{ + expectPass: false, + contains: "transfer amount exceeds balance", + }, + }, + { + "invalid - contract address", + types.NewMsgConvertERC20ToCoin( + invoker, + invokerCosmosAddr, + testutil.MustNewInternalEVMAddressFromString("0x7Bbf300890857b8c241b219C6a489431669b3aFA"), + sdk.NewInt(10_000), + ), + math.MaxBig256, + errArgs{ + expectPass: false, + contains: "ERC20 token not enabled to convert to sdk.Coin", + }, + }, + } + + for _, tc := range tests { + suite.Run(tc.name, func() { + _, err := suite.msgServer.ConvertERC20ToCoin(sdk.WrapSDKContext(suite.Ctx), &tc.msg) + + if tc.errArgs.expectPass { + suite.Require().NoError(err) + + // validate user balance after conversion + bal := suite.GetERC20BalanceOf( + types.ERC20MintableBurnableContract.ABI, + pair.GetAddress(), + testutil.MustNewInternalEVMAddressFromString(tc.msg.Initiator), + ) + expectedBal := sdk.NewIntFromBigInt(pairStartingBal).Sub(tc.msg.Amount) + suite.Require().Equal(expectedBal.BigInt(), bal, "user erc20 balance is invalid") + + // validate user coin balance + coinBal := suite.App.GetBankKeeper().GetBalance(suite.Ctx, invokerCosmosAddr, pair.Denom) + suite.Require().Equal(tc.msg.Amount, coinBal.Amount, "user coin balance is invalid") + + // msg server event + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, tc.msg.Initiator), + )) + + // keeper event + suite.EventsContains(suite.GetEvents(), + sdk.NewEvent( + types.EventTypeConvertERC20ToCoin, + sdk.NewAttribute(types.AttributeKeyERC20Address, pair.GetAddress().String()), + sdk.NewAttribute(types.AttributeKeyInitiator, tc.msg.Initiator), + sdk.NewAttribute(types.AttributeKeyReceiver, tc.msg.Receiver), + sdk.NewAttribute(types.AttributeKeyAmount, sdk.NewCoin(pair.Denom, tc.msg.Amount).String()), + )) + } else { + suite.Require().Error(err) + suite.Require().Contains(err.Error(), tc.errArgs.contains) + } + }) + } +} diff --git a/x/evmutil/keeper/params.go b/x/evmutil/keeper/params.go new file mode 100644 index 00000000..c75506c0 --- /dev/null +++ b/x/evmutil/keeper/params.go @@ -0,0 +1,51 @@ +package keeper + +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/kava-labs/kava/x/evmutil/types" +) + +// GetParams returns the total set of evm parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSubspace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the evm parameters to the param space. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSubspace.SetParamSet(ctx, ¶ms) +} + +// GetEnabledConversionPairFromERC20Address returns an ConversionPair from the internal contract address. +func (k Keeper) GetEnabledConversionPairFromERC20Address( + ctx sdk.Context, + address types.InternalEVMAddress, +) (types.ConversionPair, error) { + params := k.GetParams(ctx) + for _, pair := range params.EnabledConversionPairs { + if bytes.Equal(pair.KavaERC20Address, address.Bytes()) { + return pair, nil + } + } + + return types.ConversionPair{}, sdkerrors.Wrap(types.ErrConversionNotEnabled, address.String()) +} + +// GetEnabledConversionPairFromDenom returns an ConversionPair from the sdk.Coin denom. +func (k Keeper) GetEnabledConversionPairFromDenom( + ctx sdk.Context, + denom string, +) (types.ConversionPair, error) { + params := k.GetParams(ctx) + for _, pair := range params.EnabledConversionPairs { + if pair.Denom == denom { + return pair, nil + } + } + + return types.ConversionPair{}, sdkerrors.Wrap(types.ErrConversionNotEnabled, denom) +} diff --git a/x/evmutil/keeper/params_test.go b/x/evmutil/keeper/params_test.go new file mode 100644 index 00000000..e6c74c99 --- /dev/null +++ b/x/evmutil/keeper/params_test.go @@ -0,0 +1,36 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/kava-labs/kava/x/evmutil/testutil" + "github.com/kava-labs/kava/x/evmutil/types" +) + +type ParamsTestSuite struct { + testutil.Suite +} + +func TestParamsSuite(t *testing.T) { + suite.Run(t, new(ParamsTestSuite)) +} + +func (suite *ParamsTestSuite) TestEnabledConversionPair() { + pairAddr := testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + expPair := types.ConversionPair{ + KavaERC20Address: pairAddr.Bytes(), + Denom: "weth", + } + params := types.DefaultParams() + params.EnabledConversionPairs = []types.ConversionPair{expPair} + suite.Keeper.SetParams(suite.Ctx, params) + + actualPair, err := suite.Keeper.GetEnabledConversionPairFromERC20Address( + suite.Ctx, + pairAddr, + ) + suite.Require().NoError(err) + suite.Require().Equal(expPair, actualPair) +} diff --git a/x/evmutil/module.go b/x/evmutil/module.go index bdff6665..fdfb5e26 100644 --- a/x/evmutil/module.go +++ b/x/evmutil/module.go @@ -1,6 +1,7 @@ package evmutil import ( + "context" "encoding/json" "github.com/gorilla/mux" @@ -15,6 +16,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/kava-labs/kava/x/evmutil/client/cli" "github.com/kava-labs/kava/x/evmutil/keeper" "github.com/kava-labs/kava/x/evmutil/types" ) @@ -44,7 +46,9 @@ func (AppModuleBasic) Name() string { func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} // RegisterInterfaces registers the module's interface types -func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) {} +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} // DefaultGenesis default genesis state func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { @@ -66,13 +70,21 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncod func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) {} // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for evmutil module. -func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {} +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { + panic(err) + } +} // GetTxCmd returns evmutil module's root tx command. -func (a AppModuleBasic) GetTxCmd() *cobra.Command { return nil } +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} // GetQueryCmd returns evmutil module's root query command. -func (AppModuleBasic) GetQueryCmd() *cobra.Command { return nil } +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} // ---------------------------------------------------------------------------- // AppModule @@ -104,7 +116,7 @@ func (am AppModule) Name() string { func (am AppModule) Route() sdk.Route { return sdk.Route{} } // QuerierRoute returns evmutil module's query routing key. -func (AppModule) QuerierRoute() string { return types.ModuleName } +func (AppModule) QuerierRoute() string { return "" } // LegacyQuerierHandler returns evmutil module's Querier. func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { @@ -113,7 +125,10 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd // RegisterServices registers a GRPC query service to respond to the // module-specific GRPC queries. -func (am AppModule) RegisterServices(cfg module.Configurator) {} +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper)) +} // RegisterInvariants registers evmutil module's invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { diff --git a/x/evmutil/testutil/suite.go b/x/evmutil/testutil/suite.go index 7d154312..9b415b6c 100644 --- a/x/evmutil/testutil/suite.go +++ b/x/evmutil/testutil/suite.go @@ -1,29 +1,63 @@ package testutil import ( + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math/big" + "reflect" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "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" tmtime "github.com/tendermint/tendermint/types/time" + "github.com/tendermint/tendermint/version" + "github.com/tharsis/ethermint/crypto/ethsecp256k1" + "github.com/tharsis/ethermint/server/config" + etherminttests "github.com/tharsis/ethermint/tests" + etherminttypes "github.com/tharsis/ethermint/types" evmtypes "github.com/tharsis/ethermint/x/evm/types" + feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types" "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/x/evmutil/keeper" + "github.com/kava-labs/kava/x/evmutil/types" ) type Suite struct { suite.Suite - App app.TestApp - Ctx sdk.Context - BankKeeper bankkeeper.Keeper - AccountKeeper authkeeper.AccountKeeper - Keeper keeper.Keeper - EvmBankKeeper keeper.EvmBankKeeper - Addrs []sdk.AccAddress - EvmModuleAddr sdk.AccAddress + App app.TestApp + Ctx sdk.Context + Address common.Address + BankKeeper bankkeeper.Keeper + AccountKeeper authkeeper.AccountKeeper + Keeper keeper.Keeper + EvmBankKeeper keeper.EvmBankKeeper + Addrs []sdk.AccAddress + EvmModuleAddr sdk.AccAddress + QueryClient types.QueryClient + QueryClientEvm evmtypes.QueryClient + Key1 *ethsecp256k1.PrivKey + Key1Addr types.InternalEVMAddress + Key2 *ethsecp256k1.PrivKey } func (suite *Suite) SetupTest() { @@ -37,13 +71,114 @@ func (suite *Suite) SetupTest() { suite.EvmBankKeeper = keeper.NewEvmBankKeeper(tApp.GetEvmutilKeeper(), suite.BankKeeper, suite.AccountKeeper) suite.EvmModuleAddr = suite.AccountKeeper.GetModuleAddress(evmtypes.ModuleName) + // test evm user keys that have no minting permissions + key1, err := ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + suite.Key1 = key1 + suite.Key1Addr = types.NewInternalEVMAddress(common.BytesToAddress(suite.Key1.PubKey().Address())) + suite.Key2, err = ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + _, addrs := app.GeneratePrivKeyAddressPairs(4) suite.Addrs = addrs evmGenesis := evmtypes.DefaultGenesisState() evmGenesis.Params.EvmDenom = "akava" - gs := app.GenesisState{evmtypes.ModuleName: suite.App.AppCodec().MustMarshalJSON(evmGenesis)} - suite.App.InitializeFromGenesisStates(gs) + + feemarketGenesis := feemarkettypes.DefaultGenesisState() + feemarketGenesis.Params.EnableHeight = 1 + feemarketGenesis.Params.NoBaseFee = false + + cdc := suite.App.AppCodec() + coins := sdk.NewCoins(sdk.NewInt64Coin("ukava", 1000_000_000_000_000_000)) + authGS := app.NewFundedGenStateWithSameCoins(cdc, coins, []sdk.AccAddress{ + sdk.AccAddress(suite.Key1.PubKey().Address()), + sdk.AccAddress(suite.Key2.PubKey().Address()), + }) + + gs := app.GenesisState{ + evmtypes.ModuleName: cdc.MustMarshalJSON(evmGenesis), + feemarkettypes.ModuleName: cdc.MustMarshalJSON(feemarketGenesis), + } + suite.App.InitializeFromGenesisStates(authGS, gs) + + // consensus key - needed to set up evm module + consPriv, err := ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + consAddress := sdk.ConsAddress(consPriv.PubKey().Address()) + + // InitializeFromGenesisStates commits first block so we start at 2 here + suite.Ctx = suite.App.NewContext(false, tmproto.Header{ + Height: suite.App.LastBlockHeight() + 1, + ChainID: "kavatest_1-1", + 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")), + }) + + // We need to set the validator as calling the EVM looks up the validator address + // https://github.com/tharsis/ethermint/blob/f21592ebfe74da7590eb42ed926dae970b2a9a3f/x/evm/keeper/state_transition.go#L487 + // evmkeeper.EVMConfig() will return error "failed to load evm config" if not set + acc := ðerminttypes.EthAccount{ + BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.Address.Bytes()), nil, 0, 0), + CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(), + } + suite.AccountKeeper.SetAccount(suite.Ctx, acc) + valAddr := sdk.ValAddress(suite.Address.Bytes()) + validator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{}) + suite.Require().NoError(err) + err = suite.App.GetStakingKeeper().SetValidatorByConsAddr(suite.Ctx, validator) + suite.Require().NoError(err) + suite.App.GetStakingKeeper().SetValidator(suite.Ctx, validator) + + // add conversion pair for first module deployed contract to evmutil params + suite.Keeper.SetParams(suite.Ctx, types.NewParams( + types.NewConversionPairs( + types.NewConversionPair( + // First contract this module deploys + MustNewInternalEVMAddressFromString("0x15932E26f5BD4923d46a2b205191C4b5d5f43FE3"), + "erc20/usdc", + ), + ), + )) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry()) + evmtypes.RegisterQueryServer(queryHelper, suite.App.GetEvmKeeper()) + suite.QueryClientEvm = evmtypes.NewQueryClient(queryHelper) + types.RegisterQueryServer(queryHelper, keeper.NewQueryServerImpl(suite.App.GetEvmutilKeeper())) + suite.QueryClient = types.NewQueryClient(queryHelper) + + // We need to commit so that the ethermint feemarket beginblock runs to set the minfee + // feeMarketKeeper.GetBaseFee() will return nil otherwise + suite.Commit() +} + +func (suite *Suite) Commit() { + _ = suite.App.Commit() + header := suite.Ctx.BlockHeader() + header.Height += 1 + suite.App.BeginBlock(abci.RequestBeginBlock{ + Header: header, + }) + + // update ctx + suite.Ctx = suite.App.NewContext(false, header) } func (suite *Suite) FundAccountWithKava(addr sdk.AccAddress, coins sdk.Coins) { @@ -72,3 +207,206 @@ func (suite *Suite) FundModuleAccountWithKava(moduleName string, coins sdk.Coins suite.Require().NoError(err) } } + +func (suite *Suite) DeployERC20() types.InternalEVMAddress { + // make sure module account is created + // qq: any better ways to do this? + suite.App.FundModuleAccount( + suite.Ctx, + types.ModuleName, + sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(0))), + ) + + contractAddr, err := suite.Keeper.DeployTestMintableERC20Contract(suite.Ctx, "USDC", "USDC", uint8(18)) + suite.Require().NoError(err) + suite.Require().Greater(len(contractAddr.Address), 0) + return contractAddr +} + +func (suite *Suite) GetERC20BalanceOf( + contractAbi abi.ABI, + contractAddr types.InternalEVMAddress, + accountAddr types.InternalEVMAddress, +) *big.Int { + // Query ERC20.balanceOf() + addr := common.BytesToAddress(suite.Key1.PubKey().Address()) + res, err := suite.QueryContract( + types.ERC20MintableBurnableContract.ABI, + addr, + suite.Key1, + contractAddr, + "balanceOf", + accountAddr.Address, + ) + suite.Require().NoError(err) + suite.Require().Len(res, 1) + + balance, ok := res[0].(*big.Int) + suite.Require().True(ok, "balanceOf should respond with *big.Int") + return balance +} + +func (suite *Suite) QueryContract( + contractAbi abi.ABI, + from common.Address, + fromKey *ethsecp256k1.PrivKey, + contract types.InternalEVMAddress, + method string, + args ...interface{}, +) ([]interface{}, error) { + // Pack query args + data, err := contractAbi.Pack(method, args...) + suite.Require().NoError(err) + + // Send TX + res, err := suite.SendTx(contract, from, fromKey, data) + suite.Require().NoError(err) + + // Check for VM errors and unpack returned data + switch res.VmError { + case vm.ErrExecutionReverted.Error(): + response, err := abi.UnpackRevert(res.Ret) + suite.Require().NoError(err) + + return nil, errors.New(response) + case "": // No error, continue + default: + panic(fmt.Sprintf("unhandled vm error response: %v", res.VmError)) + } + + // Unpack response + unpackedRes, err := contractAbi.Unpack(method, res.Ret) + suite.Require().NoErrorf(err, "failed to unpack method %v response", method) + + return unpackedRes, nil +} + +// SendTx submits a transaction to the block. +func (suite *Suite) SendTx( + contractAddr types.InternalEVMAddress, + from common.Address, + signerKey *ethsecp256k1.PrivKey, + transferData []byte, +) (*evmtypes.MsgEthereumTxResponse, error) { + ctx := sdk.WrapSDKContext(suite.Ctx) + chainID := suite.App.GetEvmKeeper().ChainID() + + args, err := json.Marshal(&evmtypes.TransactionArgs{ + To: &contractAddr.Address, + From: &from, + Data: (*hexutil.Bytes)(&transferData), + }) + if err != nil { + return nil, err + } + gasRes, err := suite.QueryClientEvm.EstimateGas(ctx, &evmtypes.EthCallRequest{ + Args: args, + GasCap: config.DefaultGasCap, + }) + if err != nil { + return nil, err + } + + nonce := suite.App.GetEvmKeeper().GetNonce(suite.Ctx, suite.Address) + + baseFee := suite.App.GetFeeMarketKeeper().GetBaseFee(suite.Ctx) + suite.Require().NotNil(baseFee, "base fee is nil") + + // Mint the max gas to the FeeCollector to ensure balance in case of refund + suite.MintFeeCollector(sdk.NewCoins( + sdk.NewCoin( + "ukava", + sdk.NewInt(baseFee.Int64()*int64(gasRes.Gas*2)), + ))) + + ercTransferTx := evmtypes.NewTx( + chainID, + nonce, + &contractAddr.Address, + nil, // amount + gasRes.Gas*2, // gasLimit, TODO: runs out of gas with just res.Gas, ex: estimated was 21572 but used 24814 + nil, // gasPrice + suite.App.GetFeeMarketKeeper().GetBaseFee(suite.Ctx), // gasFeeCap + big.NewInt(1), // gasTipCap + transferData, + ðtypes.AccessList{}, // accesses + ) + + ercTransferTx.From = hex.EncodeToString(signerKey.PubKey().Address()) + err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), etherminttests.NewSigner(signerKey)) + if err != nil { + return nil, err + } + + rsp, err := suite.App.GetEvmKeeper().EthereumTx(ctx, ercTransferTx) + if err != nil { + return nil, err + } + // Do not check vm error here since we want to check for errors later + + return rsp, nil +} + +func (suite *Suite) MintFeeCollector(coins sdk.Coins) { + err := suite.App.GetBankKeeper().MintCoins(suite.Ctx, minttypes.ModuleName, coins) + suite.Require().NoError(err) + err = suite.App.GetBankKeeper().SendCoinsFromModuleToModule( + suite.Ctx, + minttypes.ModuleName, + authtypes.FeeCollectorName, + coins, + ) + suite.Require().NoError(err) +} + +// GetEvents returns emitted events on the sdk context +func (suite *Suite) GetEvents() sdk.Events { + return suite.Ctx.EventManager().Events() +} + +// EventsContains asserts that the expected event is in the provided events +func (suite *Suite) EventsContains(events sdk.Events, expectedEvent sdk.Event) { + foundMatch := false + for _, event := range events { + if event.Type == expectedEvent.Type { + if reflect.DeepEqual(attrsToMap(expectedEvent.Attributes), attrsToMap(event.Attributes)) { + foundMatch = true + } + } + } + + suite.Truef(foundMatch, "event of type %s not found or did not match", expectedEvent.Type) +} + +// EventsDoNotContain asserts that the event is **not** is in the provided events +func (suite *Suite) EventsDoNotContain(events sdk.Events, eventType string) { + foundMatch := false + for _, event := range events { + if event.Type == eventType { + foundMatch = true + } + } + + suite.Falsef(foundMatch, "event of type %s should not be found, but was found", eventType) +} + +func attrsToMap(attrs []abci.EventAttribute) []sdk.Attribute { + out := []sdk.Attribute{} + + for _, attr := range attrs { + out = append(out, sdk.NewAttribute(string(attr.Key), string(attr.Value))) + } + + return out +} + +// MustNewInternalEVMAddressFromString returns a new InternalEVMAddress from a +// hex string. This will panic if the input hex string is invalid. +func MustNewInternalEVMAddressFromString(addrStr string) types.InternalEVMAddress { + addr, err := types.NewInternalEVMAddressFromString(addrStr) + if err != nil { + panic(err) + } + + return addr +} diff --git a/x/evmutil/types/address.go b/x/evmutil/types/address.go new file mode 100644 index 00000000..bfdb0ce6 --- /dev/null +++ b/x/evmutil/types/address.go @@ -0,0 +1,33 @@ +package types + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" +) + +// InternalEVMAddress is a type alias of common.Address to represent an address +// on the Kava EVM. +type InternalEVMAddress struct { + common.Address +} + +// NewInternalEVMAddress returns a new InternalEVMAddress from a common.Address. +func NewInternalEVMAddress(addr common.Address) InternalEVMAddress { + return InternalEVMAddress{ + Address: addr, + } +} + +// NewInternalEVMAddressFromString returns a new InternalEVMAddress from a hex +// string. Returns an error if hex string is invalid. +func NewInternalEVMAddressFromString(addrStr string) (InternalEVMAddress, error) { + if !common.IsHexAddress(addrStr) { + return InternalEVMAddress{}, fmt.Errorf("string is not a hex address %v", addrStr) + } + + // common.HexToAddress ignores hex decoding errors + addr := common.HexToAddress(addrStr) + + return NewInternalEVMAddress(addr), nil +} diff --git a/x/evmutil/types/bytes.go b/x/evmutil/types/bytes.go new file mode 100644 index 00000000..d390f3c2 --- /dev/null +++ b/x/evmutil/types/bytes.go @@ -0,0 +1,25 @@ +package types + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// HexBytes represents a byte slice that marshals into a 0x representation +type HexBytes []byte + +// MarshalJSON marshals HexBytes into a 0x json string +func (b HexBytes) MarshalJSON() ([]byte, error) { + return json.Marshal(hexutil.Encode(b)) +} + +// UnmarshalJSON unmarshals a 0x json string into bytes +func (b *HexBytes) UnmarshalJSON(input []byte) error { + return (*hexutil.Bytes)(b).UnmarshalJSON(input) +} + +// String implements Stringer and returns the 0x representation +func (b HexBytes) String() string { + return hexutil.Encode(b) +} diff --git a/x/evmutil/types/bytes_test.go b/x/evmutil/types/bytes_test.go new file mode 100644 index 00000000..1109180c --- /dev/null +++ b/x/evmutil/types/bytes_test.go @@ -0,0 +1,32 @@ +package types_test + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/kava-labs/kava/x/evmutil/types" + "github.com/stretchr/testify/require" +) + +func TestHexBytes(t *testing.T) { + testCases := []struct { + input types.HexBytes + output string + }{ + {[]byte{}, "0x"}, // empty slice is 0x + {[]byte{0}, "0x00"}, + {[]byte{1}, "0x01"}, + {[]byte{1, 2, 3}, "0x010203"}, + {[]byte{255}, "0xff"}, + {[]byte{16, 16}, "0x1010"}, + {[]byte{16, 16}, "0x1010"}, + } + + for _, tc := range testCases { + bz, err := json.Marshal(tc.input) + require.NoError(t, err) + + require.Equal(t, fmt.Sprintf("\"%s\"", tc.output), string(bz)) + } +} diff --git a/x/evmutil/types/codec.go b/x/evmutil/types/codec.go new file mode 100644 index 00000000..766df8df --- /dev/null +++ b/x/evmutil/types/codec.go @@ -0,0 +1,36 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterLegacyAminoCodec registers the necessary evmutil interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgConvertCoinToERC20{}, "evmutil/MsgConvertCoinToERC20", nil) + cdc.RegisterConcrete(&MsgConvertERC20ToCoin{}, "evmutil/MsgConvertERC20ToCoin", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgConvertCoinToERC20{}, + &MsgConvertERC20ToCoin{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + amino.Seal() +} diff --git a/x/evmutil/types/contract.go b/x/evmutil/types/contract.go new file mode 100644 index 00000000..6eb815c8 --- /dev/null +++ b/x/evmutil/types/contract.go @@ -0,0 +1,48 @@ +// Derived from https://github.com/tharsis/evmos/blob/0bfaf0db7be47153bc651e663176ba8deca960b5/contracts/erc20.go +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + // Embed ERC20 JSON files + _ "embed" + "encoding/json" + + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/tharsis/ethermint/x/evm/types" +) + +var ( + //go:embed ethermint_json/ERC20MintableBurnable.json + ERC20MintableBurnableJSON []byte + + // ERC20MintableBurnableContract is the compiled erc20 contract + ERC20MintableBurnableContract evmtypes.CompiledContract + + // ERC20MintableBurnableAddress is the erc20 module address + ERC20MintableBurnableAddress common.Address +) + +func init() { + ERC20MintableBurnableAddress = ModuleEVMAddress + + err := json.Unmarshal(ERC20MintableBurnableJSON, &ERC20MintableBurnableContract) + if err != nil { + panic(err) + } + + if len(ERC20MintableBurnableContract.Bin) == 0 { + panic("load contract failed") + } +} diff --git a/x/evmutil/types/conversion_pair.go b/x/evmutil/types/conversion_pair.go new file mode 100644 index 00000000..d6716df4 --- /dev/null +++ b/x/evmutil/types/conversion_pair.go @@ -0,0 +1,89 @@ +package types + +import ( + bytes "bytes" + "encoding/hex" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" +) + +// NewConversionPair returns a new ConversionPair. +func NewConversionPair(address InternalEVMAddress, denom string) ConversionPair { + return ConversionPair{ + KavaERC20Address: address.Address.Bytes(), + Denom: denom, + } +} + +// GetAddress returns the InternalEVMAddress of the Kava ERC20 address. +func (pair ConversionPair) GetAddress() InternalEVMAddress { + return NewInternalEVMAddress(common.BytesToAddress(pair.KavaERC20Address)) +} + +// Validate returns an error if the ConversionPair is invalid. +func (pair ConversionPair) Validate() error { + if err := sdk.ValidateDenom(pair.Denom); err != nil { + return fmt.Errorf("conversion pair denom invalid: %v", err) + } + + if len(pair.KavaERC20Address) != common.AddressLength { + return fmt.Errorf("address length is %v but expected %v", len(pair.KavaERC20Address), common.AddressLength) + } + + if bytes.Equal(pair.KavaERC20Address, common.Address{}.Bytes()) { + return fmt.Errorf("address cannot be zero value %v", hex.EncodeToString(pair.KavaERC20Address)) + } + + return nil +} + +// ConversionPairs defines a slice of ConversionPair. +type ConversionPairs []ConversionPair + +// NewConversionPairs returns ConversionPairs from the provided values. +func NewConversionPairs(pairs ...ConversionPair) ConversionPairs { + return ConversionPairs(pairs) +} + +func (pairs ConversionPairs) Validate() error { + // Check for duplicates for both addrs and denoms + addrs := map[string]bool{} + denoms := map[string]bool{} + + for _, pair := range pairs { + if addrs[hex.EncodeToString(pair.KavaERC20Address)] { + return fmt.Errorf( + "found duplicate enabled conversion pair internal ERC20 address %s", + hex.EncodeToString(pair.KavaERC20Address), + ) + } + + if denoms[pair.Denom] { + return fmt.Errorf( + "found duplicate enabled conversion pair denom %s", + pair.Denom, + ) + } + + if err := pair.Validate(); err != nil { + return err + } + + addrs[hex.EncodeToString(pair.KavaERC20Address)] = true + denoms[pair.Denom] = true + } + + return nil +} + +// validateConversionPairs validates an interface as ConversionPairs +func validateConversionPairs(i interface{}) error { + pairs, ok := i.(ConversionPairs) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return pairs.Validate() +} diff --git a/x/evmutil/types/conversion_pair.pb.go b/x/evmutil/types/conversion_pair.pb.go new file mode 100644 index 00000000..ffc5c5c4 --- /dev/null +++ b/x/evmutil/types/conversion_pair.pb.go @@ -0,0 +1,428 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: kava/evmutil/v1beta1/conversion_pair.proto + +package types + +import ( + bytes "bytes" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ConversionPair defines a Kava ERC20 address and corresponding denom that is +// allowed to be converted between ERC20 and sdk.Coin +type ConversionPair struct { + // ERC20 address of the token on the Kava EVM + KavaERC20Address HexBytes `protobuf:"bytes,1,opt,name=kava_erc20_address,json=kavaErc20Address,proto3,casttype=HexBytes" json:"kava_erc20_address,omitempty"` + // Denom of the corresponding sdk.Coin + Denom string `protobuf:"bytes,2,opt,name=denom,proto3" json:"denom,omitempty"` +} + +func (m *ConversionPair) Reset() { *m = ConversionPair{} } +func (m *ConversionPair) String() string { return proto.CompactTextString(m) } +func (*ConversionPair) ProtoMessage() {} +func (*ConversionPair) Descriptor() ([]byte, []int) { + return fileDescriptor_e1396d08199817d0, []int{0} +} +func (m *ConversionPair) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConversionPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConversionPair.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConversionPair) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConversionPair.Merge(m, src) +} +func (m *ConversionPair) XXX_Size() int { + return m.Size() +} +func (m *ConversionPair) XXX_DiscardUnknown() { + xxx_messageInfo_ConversionPair.DiscardUnknown(m) +} + +var xxx_messageInfo_ConversionPair proto.InternalMessageInfo + +func init() { + proto.RegisterType((*ConversionPair)(nil), "kava.evmutil.v1beta1.ConversionPair") +} + +func init() { + proto.RegisterFile("kava/evmutil/v1beta1/conversion_pair.proto", fileDescriptor_e1396d08199817d0) +} + +var fileDescriptor_e1396d08199817d0 = []byte{ + // 264 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xca, 0x4e, 0x2c, 0x4b, + 0xd4, 0x4f, 0x2d, 0xcb, 0x2d, 0x2d, 0xc9, 0xcc, 0xd1, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, + 0xd4, 0x4f, 0xce, 0xcf, 0x2b, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0x8b, 0x2f, 0x48, 0xcc, 0x2c, + 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x01, 0xa9, 0xd5, 0x83, 0xaa, 0xd5, 0x83, 0xaa, + 0x95, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, 0xd0, 0x07, 0xb1, 0x20, 0x6a, 0x95, 0x6a, 0xb8, + 0xf8, 0x9c, 0xe1, 0x86, 0x04, 0x24, 0x66, 0x16, 0x09, 0xf9, 0x71, 0x09, 0x81, 0xf4, 0xc7, 0xa7, + 0x16, 0x25, 0x1b, 0x19, 0xc4, 0x27, 0xa6, 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x4b, 0x30, 0x2a, 0x30, + 0x6a, 0xf0, 0x38, 0x29, 0x3c, 0xba, 0x27, 0x2f, 0xe0, 0x9d, 0x58, 0x96, 0xe8, 0x1a, 0xe4, 0x6c, + 0x64, 0xe0, 0x08, 0x91, 0xfb, 0x75, 0x4f, 0x9e, 0xc3, 0x23, 0xb5, 0xc2, 0xa9, 0xb2, 0x24, 0xb5, + 0x38, 0x48, 0x00, 0xa4, 0xd7, 0x15, 0xa4, 0x15, 0x2a, 0x2b, 0x24, 0xc2, 0xc5, 0x9a, 0x92, 0x9a, + 0x97, 0x9f, 0x2b, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x19, 0x04, 0xe1, 0x58, 0xb1, 0x74, 0x2c, 0x90, + 0x67, 0x70, 0xf2, 0x7e, 0xf0, 0x50, 0x8e, 0x71, 0xc5, 0x23, 0x39, 0xc6, 0x13, 0x8f, 0xe4, 0x18, + 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, + 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0xd2, 0x4c, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, + 0xcf, 0xd5, 0x07, 0x19, 0xad, 0x9b, 0x93, 0x98, 0x54, 0x0c, 0x66, 0xe9, 0x57, 0xc0, 0x83, 0xa3, + 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0xec, 0x23, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x60, 0x36, 0x37, 0x7f, 0x2b, 0x01, 0x00, 0x00, +} + +func (this *ConversionPair) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*ConversionPair) + if !ok { + that2, ok := that.(ConversionPair) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *ConversionPair") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *ConversionPair but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *ConversionPair but is not nil && this == nil") + } + if !bytes.Equal(this.KavaERC20Address, that1.KavaERC20Address) { + return fmt.Errorf("KavaERC20Address this(%v) Not Equal that(%v)", this.KavaERC20Address, that1.KavaERC20Address) + } + if this.Denom != that1.Denom { + return fmt.Errorf("Denom this(%v) Not Equal that(%v)", this.Denom, that1.Denom) + } + return nil +} +func (this *ConversionPair) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ConversionPair) + if !ok { + that2, ok := that.(ConversionPair) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !bytes.Equal(this.KavaERC20Address, that1.KavaERC20Address) { + return false + } + if this.Denom != that1.Denom { + return false + } + return true +} +func (m *ConversionPair) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConversionPair) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConversionPair) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintConversionPair(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0x12 + } + if len(m.KavaERC20Address) > 0 { + i -= len(m.KavaERC20Address) + copy(dAtA[i:], m.KavaERC20Address) + i = encodeVarintConversionPair(dAtA, i, uint64(len(m.KavaERC20Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintConversionPair(dAtA []byte, offset int, v uint64) int { + offset -= sovConversionPair(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ConversionPair) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.KavaERC20Address) + if l > 0 { + n += 1 + l + sovConversionPair(uint64(l)) + } + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovConversionPair(uint64(l)) + } + return n +} + +func sovConversionPair(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozConversionPair(x uint64) (n int) { + return sovConversionPair(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ConversionPair) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConversionPair + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConversionPair: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConversionPair: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KavaERC20Address", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConversionPair + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConversionPair + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConversionPair + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.KavaERC20Address = append(m.KavaERC20Address[:0], dAtA[iNdEx:postIndex]...) + if m.KavaERC20Address == nil { + m.KavaERC20Address = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConversionPair + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthConversionPair + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConversionPair + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConversionPair(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConversionPair + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipConversionPair(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowConversionPair + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowConversionPair + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowConversionPair + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthConversionPair + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupConversionPair + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthConversionPair + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthConversionPair = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowConversionPair = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupConversionPair = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/evmutil/types/conversion_pairs_test.go b/x/evmutil/types/conversion_pairs_test.go new file mode 100644 index 00000000..0e349d1d --- /dev/null +++ b/x/evmutil/types/conversion_pairs_test.go @@ -0,0 +1,233 @@ +package types_test + +import ( + "testing" + + "github.com/kava-labs/kava/x/evmutil/testutil" + "github.com/kava-labs/kava/x/evmutil/types" + "github.com/stretchr/testify/require" +) + +func TestConversionPairValidate(t *testing.T) { + type errArgs struct { + expectPass bool + contains string + } + tests := []struct { + name string + giveAddress types.InternalEVMAddress + giveDenom string + errArgs errArgs + }{ + { + "valid", + testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), + "weth", + errArgs{ + expectPass: true, + }, + }, + { + "invalid - empty denom", + testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), + "", + errArgs{ + expectPass: false, + contains: "conversion pair denom invalid: invalid denom", + }, + }, + { + "invalid - zero address", + testutil.MustNewInternalEVMAddressFromString("0x0000000000000000000000000000000000000000"), + "weth", + errArgs{ + expectPass: false, + contains: "address cannot be zero value", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + pair := types.NewConversionPair(tc.giveAddress, tc.giveDenom) + + err := pair.Validate() + + if tc.errArgs.expectPass { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errArgs.contains) + } + }) + } +} + +func TestConversionPairValidate_Direct(t *testing.T) { + type errArgs struct { + expectPass bool + contains string + } + tests := []struct { + name string + givePair types.ConversionPair + errArgs errArgs + }{ + { + "valid", + types.ConversionPair{ + KavaERC20Address: testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").Bytes(), + Denom: "weth", + }, + errArgs{ + expectPass: true, + }, + }, + + { + "invalid - length", + types.ConversionPair{ + KavaERC20Address: []byte{1}, + Denom: "weth", + }, + errArgs{ + expectPass: false, + contains: "address length is 1 but expected 20", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.givePair.Validate() + + if tc.errArgs.expectPass { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errArgs.contains) + } + }) + } +} + +func TestConversionPair_GetAddress(t *testing.T) { + addr := testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + + pair := types.NewConversionPair( + addr, + "weth", + ) + + require.Equal(t, types.HexBytes(addr.Bytes()), pair.KavaERC20Address, "struct address should match input bytes") + require.Equal(t, addr, pair.GetAddress(), "get internal address should match input bytes") +} + +func TestConversionPairs_Validate(t *testing.T) { + type errArgs struct { + expectPass bool + contains string + } + tests := []struct { + name string + givePairs types.ConversionPairs + errArgs errArgs + }{ + { + "valid", + types.NewConversionPairs( + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), + "weth", + ), + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000A"), + "kava", + ), + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000B"), + "usdc", + ), + ), + errArgs{ + expectPass: true, + }, + }, + { + "invalid - duplicate address", + types.NewConversionPairs( + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), + "weth", + ), + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), + "kava", + ), + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000B"), + "usdc", + ), + ), + errArgs{ + expectPass: false, + contains: "found duplicate enabled conversion pair internal ERC20 address", + }, + }, + { + "invalid - duplicate denom", + types.NewConversionPairs( + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), + "weth", + ), + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000A"), + "kava", + ), + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000B"), + "kava", + ), + ), + errArgs{ + expectPass: false, + contains: "found duplicate enabled conversion pair denom kava", + }, + }, + { + "invalid - invalid pair", + types.NewConversionPairs( + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), + "weth", + ), + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x0000000000000000000000000000000000000000"), + "usdc", + ), + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x000000000000000000000000000000000000000B"), + "kava", + ), + ), + errArgs{ + expectPass: false, + contains: "address cannot be zero value", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.givePairs.Validate() + + if tc.errArgs.expectPass { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errArgs.contains) + } + }) + } +} diff --git a/x/evmutil/types/errors.go b/x/evmutil/types/errors.go new file mode 100644 index 00000000..417faa40 --- /dev/null +++ b/x/evmutil/types/errors.go @@ -0,0 +1,14 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// errors +var ( + ErrABIPack = sdkerrors.Register(ModuleName, 2, "contract ABI pack failed") + ErrEVMCall = sdkerrors.Register(ModuleName, 3, "EVM call unexpected error") + ErrConversionNotEnabled = sdkerrors.Register(ModuleName, 4, "ERC20 token not enabled to convert to sdk.Coin") + ErrBalanceInvariance = sdkerrors.Register(ModuleName, 5, "post EVM transfer balance invariant failed") + ErrUnexpectedContractEvent = sdkerrors.Register(ModuleName, 6, "unexpected contract event") +) diff --git a/x/evmutil/types/ethermint_json/ERC20MintableBurnable.json b/x/evmutil/types/ethermint_json/ERC20MintableBurnable.json new file mode 100644 index 00000000..4a86254c --- /dev/null +++ b/x/evmutil/types/ethermint_json/ERC20MintableBurnable.json @@ -0,0 +1,4 @@ +{ + "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"decimals_\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"toKavaAddr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ConvertToCoin\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"toAddr\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdraw\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"toKavaAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"convertToCoin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"toAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"toAddr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "bin": "60a06040523480156200001157600080fd5b5060405162002430380380620024308339818101604052810190620000379190620003fd565b828281600390805190602001906200005192919062000172565b5080600490805190602001906200006a92919062000172565b5050506200008d62000081620000a460201b60201c565b620000ac60201b60201c565b8060ff1660808160ff1681525050505050620004fc565b600033905090565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b8280546200018090620004c6565b90600052602060002090601f016020900481019282620001a45760008555620001f0565b82601f10620001bf57805160ff1916838001178555620001f0565b82800160010185558215620001f0579182015b82811115620001ef578251825591602001919060010190620001d2565b5b509050620001ff919062000203565b5090565b5b808211156200021e57600081600090555060010162000204565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200028b8262000240565b810181811067ffffffffffffffff82111715620002ad57620002ac62000251565b5b80604052505050565b6000620002c262000222565b9050620002d0828262000280565b919050565b600067ffffffffffffffff821115620002f357620002f262000251565b5b620002fe8262000240565b9050602081019050919050565b60005b838110156200032b5780820151818401526020810190506200030e565b838111156200033b576000848401525b50505050565b6000620003586200035284620002d5565b620002b6565b9050828152602081018484840111156200037757620003766200023b565b5b620003848482856200030b565b509392505050565b600082601f830112620003a457620003a362000236565b5b8151620003b684826020860162000341565b91505092915050565b600060ff82169050919050565b620003d781620003bf565b8114620003e357600080fd5b50565b600081519050620003f781620003cc565b92915050565b6000806000606084860312156200041957620004186200022c565b5b600084015167ffffffffffffffff8111156200043a576200043962000231565b5b62000448868287016200038c565b935050602084015167ffffffffffffffff8111156200046c576200046b62000231565b5b6200047a868287016200038c565b92505060406200048d86828701620003e6565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620004df57607f821691505b60208210811415620004f657620004f562000497565b5b50919050565b608051611f186200051860003960006104890152611f186000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c8063715018a6116100a2578063a457c2d711610071578063a457c2d7146102cf578063a9059cbb146102ff578063dd62ed3e1461032f578063f2fde38b1461035f578063f3fef3a31461037b57610116565b8063715018a61461026d5780638da5cb5b146102775780639555a9421461029557806395d89b41146102b157610116565b8063313ce567116100e9578063313ce567146101b757806339509351146101d55780633ffb6de41461020557806340c10f191461022157806370a082311461023d57610116565b806306fdde031461011b578063095ea7b31461013957806318160ddd1461016957806323b872dd14610187575b600080fd5b610123610397565b60405161013091906114e6565b60405180910390f35b610153600480360381019061014e91906115a1565b610429565b60405161016091906115fc565b60405180910390f35b61017161044c565b60405161017e9190611626565b60405180910390f35b6101a1600480360381019061019c9190611641565b610456565b6040516101ae91906115fc565b60405180910390f35b6101bf610485565b6040516101cc91906116b0565b60405180910390f35b6101ef60048036038101906101ea91906115a1565b6104ad565b6040516101fc91906115fc565b60405180910390f35b61021f600480360381019061021a91906115a1565b610557565b005b61023b600480360381019061023691906115a1565b6105d2565b005b610257600480360381019061025291906116cb565b61065c565b6040516102649190611626565b60405180910390f35b6102756106a4565b005b61027f61072c565b60405161028c9190611707565b60405180910390f35b6102af60048036038101906102aa9190611641565b610756565b005b6102b96107d5565b6040516102c691906114e6565b60405180910390f35b6102e960048036038101906102e491906115a1565b610867565b6040516102f691906115fc565b60405180910390f35b610319600480360381019061031491906115a1565b610951565b60405161032691906115fc565b60405180910390f35b61034960048036038101906103449190611722565b610974565b6040516103569190611626565b60405180910390f35b610379600480360381019061037491906116cb565b6109fb565b005b610395600480360381019061039091906115a1565b610af3565b005b6060600380546103a690611791565b80601f01602080910402602001604051908101604052809291908181526020018280546103d290611791565b801561041f5780601f106103f45761010080835404028352916020019161041f565b820191906000526020600020905b81548152906001019060200180831161040257829003601f168201915b5050505050905090565b600080610434610b66565b9050610441818585610b6e565b600191505092915050565b6000600254905090565b600080610461610b66565b905061046e858285610d39565b610479858585610dc5565b60019150509392505050565b60007f0000000000000000000000000000000000000000000000000000000000000000905090565b6000806104b8610b66565b905061054c818585600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461054791906117f2565b610b6e565b600191505092915050565b6105693361056361072c565b83610dc5565b8173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fe8854061d746326f096b71ed157108719c11403892b4ed81e2ec551e842edcf7836040516105c69190611626565b60405180910390a35050565b6105da610b66565b73ffffffffffffffffffffffffffffffffffffffff166105f861072c565b73ffffffffffffffffffffffffffffffffffffffff161461064e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161064590611894565b60405180910390fd5b6106588282611046565b5050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6106ac610b66565b73ffffffffffffffffffffffffffffffffffffffff166106ca61072c565b73ffffffffffffffffffffffffffffffffffffffff1614610720576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161071790611894565b60405180910390fd5b61072a60006111a6565b565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b610761833383610d39565b61076b838261126c565b8173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb836040516107c89190611626565b60405180910390a3505050565b6060600480546107e490611791565b80601f016020809104026020016040519081016040528092919081815260200182805461081090611791565b801561085d5780601f106108325761010080835404028352916020019161085d565b820191906000526020600020905b81548152906001019060200180831161084057829003601f168201915b5050505050905090565b600080610872610b66565b90506000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905083811015610938576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161092f90611926565b60405180910390fd5b6109458286868403610b6e565b60019250505092915050565b60008061095c610b66565b9050610969818585610dc5565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610a03610b66565b73ffffffffffffffffffffffffffffffffffffffff16610a2161072c565b73ffffffffffffffffffffffffffffffffffffffff1614610a77576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a6e90611894565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610ae7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ade906119b8565b60405180910390fd5b610af0816111a6565b50565b610afd338261126c565b8173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb83604051610b5a9190611626565b60405180910390a35050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610bde576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bd590611a4a565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610c4e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c4590611adc565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92583604051610d2c9190611626565b60405180910390a3505050565b6000610d458484610974565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610dbf5781811015610db1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610da890611b48565b60405180910390fd5b610dbe8484848403610b6e565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610e35576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e2c90611bda565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610ea5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e9c90611c6c565b60405180910390fd5b610eb0838383611443565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610f36576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f2d90611cfe565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610fc991906117f2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161102d9190611626565b60405180910390a3611040848484611448565b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156110b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110ad90611d6a565b60405180910390fd5b6110c260008383611443565b80600260008282546110d491906117f2565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461112991906117f2565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161118e9190611626565b60405180910390a36111a260008383611448565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156112dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112d390611dfc565b60405180910390fd5b6112e882600083611443565b60008060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561136e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161136590611e8e565b60405180910390fd5b8181036000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008282546113c59190611eae565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161142a9190611626565b60405180910390a361143e83600084611448565b505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561148757808201518184015260208101905061146c565b83811115611496576000848401525b50505050565b6000601f19601f8301169050919050565b60006114b88261144d565b6114c28185611458565b93506114d2818560208601611469565b6114db8161149c565b840191505092915050565b6000602082019050818103600083015261150081846114ad565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006115388261150d565b9050919050565b6115488161152d565b811461155357600080fd5b50565b6000813590506115658161153f565b92915050565b6000819050919050565b61157e8161156b565b811461158957600080fd5b50565b60008135905061159b81611575565b92915050565b600080604083850312156115b8576115b7611508565b5b60006115c685828601611556565b92505060206115d78582860161158c565b9150509250929050565b60008115159050919050565b6115f6816115e1565b82525050565b600060208201905061161160008301846115ed565b92915050565b6116208161156b565b82525050565b600060208201905061163b6000830184611617565b92915050565b60008060006060848603121561165a57611659611508565b5b600061166886828701611556565b935050602061167986828701611556565b925050604061168a8682870161158c565b9150509250925092565b600060ff82169050919050565b6116aa81611694565b82525050565b60006020820190506116c560008301846116a1565b92915050565b6000602082840312156116e1576116e0611508565b5b60006116ef84828501611556565b91505092915050565b6117018161152d565b82525050565b600060208201905061171c60008301846116f8565b92915050565b6000806040838503121561173957611738611508565b5b600061174785828601611556565b925050602061175885828601611556565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806117a957607f821691505b602082108114156117bd576117bc611762565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006117fd8261156b565b91506118088361156b565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561183d5761183c6117c3565b5b828201905092915050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b600061187e602083611458565b915061188982611848565b602082019050919050565b600060208201905081810360008301526118ad81611871565b9050919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000611910602583611458565b915061191b826118b4565b604082019050919050565b6000602082019050818103600083015261193f81611903565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006119a2602683611458565b91506119ad82611946565b604082019050919050565b600060208201905081810360008301526119d181611995565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000611a34602483611458565b9150611a3f826119d8565b604082019050919050565b60006020820190508181036000830152611a6381611a27565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000611ac6602283611458565b9150611ad182611a6a565b604082019050919050565b60006020820190508181036000830152611af581611ab9565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b6000611b32601d83611458565b9150611b3d82611afc565b602082019050919050565b60006020820190508181036000830152611b6181611b25565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000611bc4602583611458565b9150611bcf82611b68565b604082019050919050565b60006020820190508181036000830152611bf381611bb7565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611c56602383611458565b9150611c6182611bfa565b604082019050919050565b60006020820190508181036000830152611c8581611c49565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000611ce8602683611458565b9150611cf382611c8c565b604082019050919050565b60006020820190508181036000830152611d1781611cdb565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000611d54601f83611458565b9150611d5f82611d1e565b602082019050919050565b60006020820190508181036000830152611d8381611d47565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b6000611de6602183611458565b9150611df182611d8a565b604082019050919050565b60006020820190508181036000830152611e1581611dd9565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b6000611e78602283611458565b9150611e8382611e1c565b604082019050919050565b60006020820190508181036000830152611ea781611e6b565b9050919050565b6000611eb98261156b565b9150611ec48361156b565b925082821015611ed757611ed66117c3565b5b82820390509291505056fea26469706673582212205e81ecf22949f6494b0e01ec43283836589d0f6b691b8c0558b33a68e74084e964736f6c63430008090033" +} diff --git a/x/evmutil/types/events.go b/x/evmutil/types/events.go new file mode 100644 index 00000000..9cab19e4 --- /dev/null +++ b/x/evmutil/types/events.go @@ -0,0 +1,18 @@ +package types + +// Events for the module +const ( + AttributeValueCategory = ModuleName + + // Event Types + EventTypeConvertERC20ToCoin = "convert_erc20_to_coin" + EventTypeConvertCoinToERC20 = "convert_coin_to_erc20" + + // Event Attributes - Common + AttributeKeyReceiver = "receiver" + AttributeKeyAmount = "amount" + + // Event Attributes - Conversions + AttributeKeyInitiator = "initiator" + AttributeKeyERC20Address = "erc20_address" +) diff --git a/x/evmutil/types/expected_keepers.go b/x/evmutil/types/expected_keepers.go index 815bf3a3..2de33125 100644 --- a/x/evmutil/types/expected_keepers.go +++ b/x/evmutil/types/expected_keepers.go @@ -1,19 +1,32 @@ package types import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/vm" evmtypes "github.com/tharsis/ethermint/x/evm/types" ) // AccountKeeper defines the expected account keeper interface type AccountKeeper interface { GetModuleAddress(moduleName string) sdk.AccAddress + GetSequence(sdk.Context, sdk.AccAddress) (uint64, error) } // BankKeeper defines the expected bank keeper interface type BankKeeper interface { evmtypes.BankKeeper + GetSupply(ctx sdk.Context, denom string) sdk.Coin SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error } + +// EvmKeeper defines the expected interface needed to make EVM transactions. +type EvmKeeper interface { + // This is actually a gRPC query method + EstimateGas(ctx context.Context, req *evmtypes.EthCallRequest) (*evmtypes.EstimateGasResponse, error) + ApplyMessage(ctx sdk.Context, msg core.Message, tracer vm.EVMLogger, commit bool) (*evmtypes.MsgEthereumTxResponse, error) +} diff --git a/x/evmutil/types/genesis.go b/x/evmutil/types/genesis.go index 8d5ef4a2..f656fe0e 100644 --- a/x/evmutil/types/genesis.go +++ b/x/evmutil/types/genesis.go @@ -7,9 +7,10 @@ import ( ) // NewGenesisState returns a new genesis state object for the module. -func NewGenesisState(accounts []Account) *GenesisState { +func NewGenesisState(accounts []Account, params Params) *GenesisState { return &GenesisState{ Accounts: accounts, + Params: params, } } @@ -17,6 +18,7 @@ func NewGenesisState(accounts []Account) *GenesisState { func DefaultGenesisState() *GenesisState { return NewGenesisState( []Account{}, + DefaultParams(), ) } @@ -34,6 +36,11 @@ func (gs GenesisState) Validate() error { seenAccounts[account.Address.String()] = true } + + if err := gs.Params.Validate(); err != nil { + return err + } + return nil } diff --git a/x/evmutil/types/genesis.pb.go b/x/evmutil/types/genesis.pb.go index b9659a60..8f12d959 100644 --- a/x/evmutil/types/genesis.pb.go +++ b/x/evmutil/types/genesis.pb.go @@ -4,6 +4,7 @@ package types import ( + bytes "bytes" fmt "fmt" _ "github.com/cosmos/cosmos-proto" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" @@ -28,6 +29,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState defines the evmutil module's genesis state. type GenesisState struct { Accounts []Account `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts"` + // params defines all the parameters of the module. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -103,9 +106,57 @@ func (m *Account) XXX_DiscardUnknown() { var xxx_messageInfo_Account proto.InternalMessageInfo +// Params defines the evmutil module params +type Params struct { + // enabled_conversion_pairs defines the list of conversion pairs allowed to be + // converted between Kava ERC20 and sdk.Coin + EnabledConversionPairs ConversionPairs `protobuf:"bytes,4,rep,name=enabled_conversion_pairs,json=enabledConversionPairs,proto3,castrepeated=ConversionPairs" json:"enabled_conversion_pairs"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_d916ab97b8e628c2, []int{2} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetEnabledConversionPairs() ConversionPairs { + if m != nil { + return m.EnabledConversionPairs + } + return nil +} + func init() { proto.RegisterType((*GenesisState)(nil), "kava.evmutil.v1beta1.GenesisState") proto.RegisterType((*Account)(nil), "kava.evmutil.v1beta1.Account") + proto.RegisterType((*Params)(nil), "kava.evmutil.v1beta1.Params") } func init() { @@ -113,30 +164,231 @@ func init() { } var fileDescriptor_d916ab97b8e628c2 = []byte{ - // 331 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xca, 0x4e, 0x2c, 0x4b, - 0xd4, 0x4f, 0x2d, 0xcb, 0x2d, 0x2d, 0xc9, 0xcc, 0xd1, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, - 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, - 0x01, 0xa9, 0xd1, 0x83, 0xaa, 0xd1, 0x83, 0xaa, 0x91, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, - 0xd0, 0x07, 0xb1, 0x20, 0x6a, 0xa5, 0x24, 0x93, 0xf3, 0x8b, 0x73, 0xf3, 0x8b, 0xe3, 0x21, 0x12, - 0x10, 0x0e, 0x44, 0x4a, 0x29, 0x94, 0x8b, 0xc7, 0x1d, 0x62, 0x6e, 0x70, 0x49, 0x62, 0x49, 0xaa, - 0x90, 0x3d, 0x17, 0x47, 0x62, 0x72, 0x72, 0x7e, 0x69, 0x5e, 0x49, 0xb1, 0x04, 0xa3, 0x02, 0xb3, - 0x06, 0xb7, 0x91, 0xac, 0x1e, 0x36, 0x9b, 0xf4, 0x1c, 0x21, 0xaa, 0x9c, 0x58, 0x4e, 0xdc, 0x93, - 0x67, 0x08, 0x82, 0x6b, 0xb2, 0x62, 0xe9, 0x58, 0x20, 0xcf, 0xa0, 0x74, 0x9e, 0x91, 0x8b, 0x1d, - 0xaa, 0x42, 0x28, 0x89, 0x8b, 0x3d, 0x31, 0x25, 0xa5, 0x28, 0xb5, 0x18, 0x64, 0x22, 0xa3, 0x06, - 0x8f, 0x93, 0xc7, 0xaf, 0x7b, 0xf2, 0xba, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, - 0xb9, 0x50, 0x07, 0x41, 0x29, 0xdd, 0xe2, 0x94, 0x6c, 0xfd, 0x92, 0xca, 0x82, 0xd4, 0x62, 0x90, - 0x15, 0x8e, 0x10, 0x8d, 0x97, 0xb6, 0xe8, 0x0a, 0x43, 0x9d, 0x0d, 0x15, 0x71, 0xaa, 0x2c, 0x49, - 0x2d, 0x0e, 0x82, 0x19, 0x2c, 0x14, 0xc6, 0xc5, 0x9e, 0x94, 0x98, 0x93, 0x98, 0x97, 0x9c, 0x2a, - 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0xe9, 0x64, 0x03, 0x72, 0xd6, 0xad, 0x7b, 0xf2, 0x6a, 0x44, 0xd8, - 0xe3, 0x99, 0x57, 0x72, 0x69, 0x8b, 0x2e, 0x17, 0xd4, 0x02, 0xcf, 0xbc, 0x92, 0x20, 0x98, 0x61, - 0x56, 0x1c, 0x20, 0xdf, 0xbc, 0x58, 0x20, 0xcf, 0xe0, 0xe4, 0x7c, 0xe2, 0x91, 0x1c, 0xe3, 0x85, - 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, - 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x9a, 0x48, 0x56, 0x80, 0x82, 0x4a, 0x37, 0x27, 0x31, 0xa9, 0x18, - 0xcc, 0xd2, 0xaf, 0x80, 0x47, 0x22, 0xd8, 0xa6, 0x24, 0x36, 0x70, 0xa0, 0x1b, 0x03, 0x02, 0x00, - 0x00, 0xff, 0xff, 0x21, 0x8b, 0xca, 0xd4, 0xe1, 0x01, 0x00, 0x00, + // 434 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0x8b, 0xd3, 0x40, + 0x14, 0xc7, 0x33, 0x5a, 0x5a, 0x9d, 0x5d, 0x10, 0xe2, 0xa2, 0x71, 0xd1, 0xc9, 0x52, 0x44, 0xaa, + 0x90, 0x84, 0x5d, 0x6f, 0x45, 0x90, 0xc6, 0x83, 0x16, 0x2f, 0x25, 0x82, 0x07, 0x2f, 0x65, 0x92, + 0x0c, 0x31, 0x34, 0x99, 0x09, 0x99, 0x69, 0xb4, 0x47, 0x6f, 0x1e, 0xf5, 0x1b, 0x78, 0x14, 0xcf, + 0xfd, 0x10, 0x05, 0x2f, 0xa5, 0x27, 0xf1, 0x50, 0x6b, 0xfa, 0x2d, 0x3c, 0x49, 0x32, 0xd3, 0xa2, + 0x25, 0x87, 0x3d, 0x75, 0xfa, 0xe6, 0xf7, 0x7f, 0xef, 0xff, 0x7f, 0x19, 0xd8, 0x9d, 0xe0, 0x02, + 0x3b, 0xa4, 0x48, 0xa7, 0x22, 0x4e, 0x9c, 0xe2, 0xdc, 0x27, 0x02, 0x9f, 0x3b, 0x11, 0xa1, 0x84, + 0xc7, 0xdc, 0xce, 0x72, 0x26, 0x98, 0x7e, 0x52, 0x31, 0xb6, 0x62, 0x6c, 0xc5, 0x9c, 0x9e, 0x44, + 0x2c, 0x62, 0x35, 0xe0, 0x54, 0x27, 0xc9, 0x9e, 0xde, 0x09, 0x18, 0x4f, 0x19, 0x1f, 0xcb, 0x0b, + 0xf9, 0x47, 0x5d, 0x3d, 0x6a, 0x1c, 0x15, 0x30, 0x5a, 0x90, 0x9c, 0xc7, 0x8c, 0x8e, 0x33, 0x1c, + 0xe7, 0x92, 0xed, 0x7e, 0x06, 0xf0, 0xf8, 0xb9, 0x34, 0xf1, 0x4a, 0x60, 0x41, 0xf4, 0xa7, 0xf0, + 0x1a, 0x0e, 0x02, 0x36, 0xa5, 0x82, 0x1b, 0xe0, 0xec, 0x6a, 0xef, 0xe8, 0xe2, 0x9e, 0xdd, 0x64, + 0xcb, 0x1e, 0x48, 0xca, 0x6d, 0x2d, 0xd6, 0xa6, 0xe6, 0xed, 0x45, 0x7a, 0x1f, 0xb6, 0x33, 0x9c, + 0xe3, 0x94, 0x1b, 0x57, 0xce, 0x40, 0xef, 0xe8, 0xe2, 0x6e, 0xb3, 0x7c, 0x54, 0x33, 0x4a, 0xad, + 0x14, 0xfd, 0xd6, 0xc7, 0x2f, 0xa6, 0xd6, 0xfd, 0x0e, 0x60, 0x47, 0x75, 0xd7, 0x7d, 0xd8, 0xc1, + 0x61, 0x98, 0x13, 0x5e, 0xb9, 0x01, 0xbd, 0x63, 0xf7, 0xc5, 0x9f, 0xb5, 0x69, 0x45, 0xb1, 0x78, + 0x3b, 0xf5, 0xed, 0x80, 0xa5, 0x2a, 0xb9, 0xfa, 0xb1, 0x78, 0x38, 0x71, 0xc4, 0x2c, 0x23, 0xbc, + 0xb2, 0x37, 0x90, 0xc2, 0xd5, 0xdc, 0xba, 0xa9, 0xf6, 0xa3, 0x2a, 0xee, 0x4c, 0x10, 0xee, 0xed, + 0x1a, 0xeb, 0xaf, 0x61, 0xc7, 0xc7, 0x09, 0xa6, 0x01, 0xa9, 0x2d, 0x5f, 0x77, 0x9f, 0x54, 0xa6, + 0x7e, 0xae, 0xcd, 0x07, 0x97, 0x98, 0x33, 0xa4, 0x62, 0x35, 0xb7, 0xa0, 0x1a, 0x30, 0xa4, 0xc2, + 0xdb, 0x35, 0x53, 0x69, 0x3e, 0x00, 0xd8, 0x96, 0x61, 0xf5, 0x77, 0xd0, 0x20, 0x14, 0xfb, 0x09, + 0x09, 0xc7, 0x07, 0x5f, 0x83, 0x1b, 0xad, 0x7a, 0xd7, 0xf7, 0x9b, 0x97, 0xf5, 0x6c, 0x4f, 0x8f, + 0x70, 0x9c, 0xbb, 0xb7, 0x2b, 0x7f, 0xdf, 0x7e, 0x99, 0x37, 0xfe, 0xaf, 0x73, 0xef, 0x96, 0x6a, + 0x7f, 0x50, 0x77, 0x5f, 0x6e, 0x7e, 0x23, 0xf0, 0xb5, 0x44, 0x60, 0x51, 0x22, 0xb0, 0x2c, 0x11, + 0xd8, 0x94, 0x08, 0x7c, 0xda, 0x22, 0x6d, 0xb9, 0x45, 0xda, 0x8f, 0x2d, 0xd2, 0xde, 0x3c, 0xfc, + 0x27, 0x6a, 0x65, 0xc1, 0x4a, 0xb0, 0xcf, 0xeb, 0x93, 0xf3, 0x7e, 0xff, 0x94, 0xea, 0xc4, 0x7e, + 0xbb, 0x7e, 0x39, 0x8f, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd6, 0x08, 0x73, 0x42, 0xd2, 0x02, + 0x00, 0x00, } +func (this *GenesisState) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*GenesisState) + if !ok { + that2, ok := that.(GenesisState) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *GenesisState") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *GenesisState but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *GenesisState but is not nil && this == nil") + } + if len(this.Accounts) != len(that1.Accounts) { + return fmt.Errorf("Accounts this(%v) Not Equal that(%v)", len(this.Accounts), len(that1.Accounts)) + } + for i := range this.Accounts { + if !this.Accounts[i].Equal(&that1.Accounts[i]) { + return fmt.Errorf("Accounts this[%v](%v) Not Equal that[%v](%v)", i, this.Accounts[i], i, that1.Accounts[i]) + } + } + if !this.Params.Equal(&that1.Params) { + return fmt.Errorf("Params this(%v) Not Equal that(%v)", this.Params, that1.Params) + } + return nil +} +func (this *GenesisState) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*GenesisState) + if !ok { + that2, ok := that.(GenesisState) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Accounts) != len(that1.Accounts) { + return false + } + for i := range this.Accounts { + if !this.Accounts[i].Equal(&that1.Accounts[i]) { + return false + } + } + if !this.Params.Equal(&that1.Params) { + return false + } + return true +} +func (this *Account) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*Account) + if !ok { + that2, ok := that.(Account) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *Account") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *Account but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *Account but is not nil && this == nil") + } + if !bytes.Equal(this.Address, that1.Address) { + return fmt.Errorf("Address this(%v) Not Equal that(%v)", this.Address, that1.Address) + } + if !this.Balance.Equal(that1.Balance) { + return fmt.Errorf("Balance this(%v) Not Equal that(%v)", this.Balance, that1.Balance) + } + return nil +} +func (this *Account) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Account) + if !ok { + that2, ok := that.(Account) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !bytes.Equal(this.Address, that1.Address) { + return false + } + if !this.Balance.Equal(that1.Balance) { + return false + } + return true +} +func (this *Params) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *Params") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *Params but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *Params but is not nil && this == nil") + } + if len(this.EnabledConversionPairs) != len(that1.EnabledConversionPairs) { + return fmt.Errorf("EnabledConversionPairs this(%v) Not Equal that(%v)", len(this.EnabledConversionPairs), len(that1.EnabledConversionPairs)) + } + for i := range this.EnabledConversionPairs { + if !this.EnabledConversionPairs[i].Equal(&that1.EnabledConversionPairs[i]) { + return fmt.Errorf("EnabledConversionPairs this[%v](%v) Not Equal that[%v](%v)", i, this.EnabledConversionPairs[i], i, that1.EnabledConversionPairs[i]) + } + } + return nil +} +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.EnabledConversionPairs) != len(that1.EnabledConversionPairs) { + return false + } + for i := range this.EnabledConversionPairs { + if !this.EnabledConversionPairs[i].Equal(&that1.EnabledConversionPairs[i]) { + return false + } + } + return true +} func (m *GenesisState) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -157,6 +409,16 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 if len(m.Accounts) > 0 { for iNdEx := len(m.Accounts) - 1; iNdEx >= 0; iNdEx-- { { @@ -214,6 +476,43 @@ func (m *Account) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.EnabledConversionPairs) > 0 { + for iNdEx := len(m.EnabledConversionPairs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.EnabledConversionPairs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + return len(dAtA) - i, nil +} + func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { offset -= sovGenesis(v) base := offset @@ -237,6 +536,8 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -255,6 +556,21 @@ func (m *Account) Size() (n int) { return n } +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.EnabledConversionPairs) > 0 { + for _, e := range m.EnabledConversionPairs { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + func sovGenesis(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -324,6 +640,39 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) @@ -463,6 +812,90 @@ func (m *Account) Unmarshal(dAtA []byte) error { } return nil } +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EnabledConversionPairs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EnabledConversionPairs = append(m.EnabledConversionPairs, ConversionPair{}) + if err := m.EnabledConversionPairs[len(m.EnabledConversionPairs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipGenesis(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/evmutil/types/genesis_test.go b/x/evmutil/types/genesis_test.go index 94b1c2a1..aad157fb 100644 --- a/x/evmutil/types/genesis_test.go +++ b/x/evmutil/types/genesis_test.go @@ -4,6 +4,7 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" "github.com/kava-labs/kava/app" @@ -16,41 +17,53 @@ func TestGenesisState_Validate(t *testing.T) { name string accounts []types.Account success bool + params types.Params }{ { - "dup addresses", - []types.Account{ + name: "dup addresses", + accounts: []types.Account{ {Address: addrs[0], Balance: sdk.NewInt(100)}, {Address: addrs[0], Balance: sdk.NewInt(150)}, }, - false, + success: false, }, { - "empty account address", - []types.Account{ + name: "empty account address", + accounts: []types.Account{ {Balance: sdk.NewInt(100)}, }, - false, + success: false, }, { - "negative account balance", - []types.Account{ + name: "negative account balance", + accounts: []types.Account{ {Address: addrs[0], Balance: sdk.NewInt(-100)}, }, - false, + success: false, }, { - "valid state", - []types.Account{ + name: "invalid params", + accounts: []types.Account{ {Address: addrs[0], Balance: sdk.NewInt(100)}, {Address: addrs[1], Balance: sdk.NewInt(150)}, }, - true, + params: types.NewParams(types.NewConversionPairs( + types.NewConversionPair(types.NewInternalEVMAddress(common.HexToAddress("0xinvalidaddress")), "weth"), + )), + success: false, + }, + { + name: "valid state", + accounts: []types.Account{ + {Address: addrs[0], Balance: sdk.NewInt(100)}, + {Address: addrs[1], Balance: sdk.NewInt(150)}, + }, + success: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gs := types.NewGenesisState(tt.accounts) + gs := types.NewGenesisState(tt.accounts, tt.params) err := gs.Validate() if tt.success { require.NoError(t, err) diff --git a/x/evmutil/types/keys.go b/x/evmutil/types/keys.go index a3805430..15e61cc9 100644 --- a/x/evmutil/types/keys.go +++ b/x/evmutil/types/keys.go @@ -3,13 +3,18 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/common" ) const ( // ModuleName name that will be used throughout the module ModuleName = "evmutil" - StoreKey = "utilevm" // cannot be emvutil due to collision with x/evm + StoreKey = "utilevm" // note: cannot be emvutil due to collision with x/evm + + // RouterKey Top level router key + RouterKey = ModuleName ) var AccountStoreKeyPrefix = []byte{0x00} // prefix for keys that store accounts @@ -18,3 +23,10 @@ var AccountStoreKeyPrefix = []byte{0x00} // prefix for keys that store accounts func AccountStoreKey(addr sdk.AccAddress) []byte { return append(AccountStoreKeyPrefix, address.MustLengthPrefix(addr)...) } + +// ModuleAddress is the native module address for EVM +var ModuleEVMAddress common.Address + +func init() { + ModuleEVMAddress = common.BytesToAddress(authtypes.NewModuleAddress(ModuleName).Bytes()) +} diff --git a/x/evmutil/types/msg.go b/x/evmutil/types/msg.go new file mode 100644 index 00000000..0fd123f9 --- /dev/null +++ b/x/evmutil/types/msg.go @@ -0,0 +1,146 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + "github.com/ethereum/go-ethereum/common" +) + +// ensure Msg interface compliance at compile time +var ( + _ sdk.Msg = &MsgConvertCoinToERC20{} + _ sdk.Msg = &MsgConvertERC20ToCoin{} + _ legacytx.LegacyMsg = &MsgConvertCoinToERC20{} + _ legacytx.LegacyMsg = &MsgConvertERC20ToCoin{} +) + +// legacy message types +const ( + TypeMsgConvertCoinToERC20 = "evmutil_convert_coin_to_erc20" + TypeMsgConvertERC20ToCoin = "evmutil_convert_erc20_to_coin" +) + +// NewMsgConvertCoinToERC20 returns a new MsgConvertCoinToERC20 +func NewMsgConvertCoinToERC20( + initiator string, + receiver string, + amount sdk.Coin, +) MsgConvertCoinToERC20 { + return MsgConvertCoinToERC20{ + Initiator: initiator, + Receiver: receiver, + Amount: &amount, + } +} + +// GetSigners returns the addresses of signers that must sign. +func (msg MsgConvertCoinToERC20) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Initiator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} + +// ValidateBasic does a simple validation check that doesn't require access to any other information. +func (msg MsgConvertCoinToERC20) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Initiator) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error()) + } + + if !common.IsHexAddress(msg.Receiver) { + return sdkerrors.Wrap( + sdkerrors.ErrInvalidAddress, + "Receiver is not a valid hex address", + ) + } + + if msg.Amount.IsZero() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "amount cannot be zero") + } + + // Checks for negative + return msg.Amount.Validate() +} + +// GetSignBytes implements the LegacyMsg.GetSignBytes method. +func (msg MsgConvertCoinToERC20) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +// Route implements the LegacyMsg.Route method. +func (msg MsgConvertCoinToERC20) Route() string { + return RouterKey +} + +// Type implements the LegacyMsg.Type method. +func (msg MsgConvertCoinToERC20) Type() string { + return TypeMsgConvertCoinToERC20 +} + +// NewMsgConvertERC20ToCoin returns a new MsgConvertERC20ToCoin +func NewMsgConvertERC20ToCoin( + initiator InternalEVMAddress, + receiver sdk.AccAddress, + contractAddr InternalEVMAddress, + amount sdk.Int, +) MsgConvertERC20ToCoin { + return MsgConvertERC20ToCoin{ + Initiator: initiator.String(), + Receiver: receiver.String(), + KavaERC20Address: contractAddr.String(), + Amount: amount, + } +} + +// GetSigners returns the addresses of signers that must sign. +func (msg MsgConvertERC20ToCoin) GetSigners() []sdk.AccAddress { + addr := common.HexToAddress(msg.Initiator) + sender := sdk.AccAddress(addr.Bytes()) + return []sdk.AccAddress{sender} +} + +// ValidateBasic does a simple validation check that doesn't require access to any other information. +func (msg MsgConvertERC20ToCoin) ValidateBasic() error { + if !common.IsHexAddress(msg.Initiator) { + return sdkerrors.Wrap( + sdkerrors.ErrInvalidAddress, + "initiator is not a valid hex address", + ) + } + + if !common.IsHexAddress(msg.KavaERC20Address) { + return sdkerrors.Wrap( + sdkerrors.ErrInvalidAddress, + "erc20 contract address is not a valid hex address", + ) + } + + _, err := sdk.AccAddressFromBech32(msg.Receiver) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver is not a valid bech32 address") + } + + if msg.Amount.IsNil() || msg.Amount.LTE(sdk.ZeroInt()) { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "amount cannot be zero or less") + } + + return nil +} + +// GetSignBytes implements the LegacyMsg.GetSignBytes method. +func (msg MsgConvertERC20ToCoin) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +// Route implements the LegacyMsg.Route method. +func (msg MsgConvertERC20ToCoin) Route() string { + return RouterKey +} + +// Type implements the LegacyMsg.Type method. +func (msg MsgConvertERC20ToCoin) Type() string { + return TypeMsgConvertERC20ToCoin +} diff --git a/x/evmutil/types/msg_test.go b/x/evmutil/types/msg_test.go new file mode 100644 index 00000000..b8dba5c0 --- /dev/null +++ b/x/evmutil/types/msg_test.go @@ -0,0 +1,199 @@ +package types_test + +import ( + "testing" + + "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/evmutil/types" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestMsgConvertCoinToERC20(t *testing.T) { + app.SetSDKConfig() + + type errArgs struct { + expectPass bool + contains string + } + + tests := []struct { + name string + giveInitiator string + giveReceiver string + giveAmount sdk.Coin + errArgs errArgs + }{ + { + "valid", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + sdk.NewCoin("erc20/weth", sdk.NewInt(1234)), + errArgs{ + expectPass: true, + }, + }, + { + "invalid - odd length hex address", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc", + sdk.NewCoin("erc20/weth", sdk.NewInt(1234)), + errArgs{ + expectPass: false, + contains: "Receiver is not a valid hex address: invalid address", + }, + }, + { + "invalid - zero amount", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + sdk.NewCoin("erc20/weth", sdk.NewInt(0)), + errArgs{ + expectPass: false, + contains: "amount cannot be zero", + }, + }, + { + "invalid - negative amount", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // Create manually so there is no validation + sdk.Coin{Denom: "erc20/weth", Amount: sdk.NewInt(-1234)}, + errArgs{ + expectPass: false, + contains: "negative coin amount", + }, + }, + { + "invalid - empty denom", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + sdk.Coin{Denom: "", Amount: sdk.NewInt(-1234)}, + errArgs{ + expectPass: false, + contains: "invalid denom", + }, + }, + { + "invalid - invalid denom", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + sdk.Coin{Denom: "h", Amount: sdk.NewInt(-1234)}, + errArgs{ + expectPass: false, + contains: "invalid denom", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + msg := types.NewMsgConvertCoinToERC20( + tc.giveInitiator, + tc.giveReceiver, + tc.giveAmount, + ) + err := msg.ValidateBasic() + + if tc.errArgs.expectPass { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errArgs.contains) + } + }) + } +} + +func TestMsgConvertERC20ToCoin(t *testing.T) { + app.SetSDKConfig() + + type errArgs struct { + expectPass bool + contains string + } + + tests := []struct { + name string + receiver string + initiator string + contractAddr string + amount sdk.Int + errArgs errArgs + }{ + { + "valid", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0x404F9466d758eA33eA84CeBE9E444b06533b369e", + sdk.NewInt(1234), + errArgs{ + expectPass: true, + }, + }, + { + "invalid - odd length hex address", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc", + "0x404F9466d758eA33eA84CeBE9E444b06533b369e", + sdk.NewInt(1234), + errArgs{ + expectPass: false, + contains: "initiator is not a valid hex address", + }, + }, + { + "invalid - zero amount", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0x404F9466d758eA33eA84CeBE9E444b06533b369e", + sdk.NewInt(0), + errArgs{ + expectPass: false, + contains: "amount cannot be zero", + }, + }, + { + "invalid - negative amount", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0x404F9466d758eA33eA84CeBE9E444b06533b369e", + sdk.NewInt(-1234), + errArgs{ + expectPass: false, + contains: "amount cannot be zero or less", + }, + }, + { + "invalid - invalid contract address", + "kava123fxg0l602etulhhcdm0vt7l57qya5wjcrwhzz", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0x404F9466d758eA33eA84CeBE9E444b06533b369", + sdk.NewInt(1234), + errArgs{ + expectPass: false, + contains: "erc20 contract address is not a valid hex address", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + msg := types.MsgConvertERC20ToCoin{ + Initiator: tc.initiator, + Receiver: tc.receiver, + KavaERC20Address: tc.contractAddr, + Amount: tc.amount, + } + err := msg.ValidateBasic() + + if tc.errArgs.expectPass { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errArgs.contains) + } + }) + } +} diff --git a/x/evmutil/types/params.go b/x/evmutil/types/params.go new file mode 100644 index 00000000..6b229ae0 --- /dev/null +++ b/x/evmutil/types/params.go @@ -0,0 +1,48 @@ +package types + +import ( + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// Parameter keys and default values +var ( + KeyEnabledConversionPairs = []byte("EnabledConversionPairs") + DefaultConversionPairs = ConversionPairs{} +) + +// ParamKeyTable for evmutil module. +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// ParamSetPairs implements the ParamSet interface and returns all the key/value +// pairs pairs of the evmutil module's parameters. +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyEnabledConversionPairs, &p.EnabledConversionPairs, validateConversionPairs), + } +} + +// NewParams returns new evmutil module Params. +func NewParams( + conversionPairs ConversionPairs, +) Params { + return Params{ + EnabledConversionPairs: conversionPairs, + } +} + +// DefaultParams returns the default parameters for evmutil. +func DefaultParams() Params { + return NewParams( + DefaultConversionPairs, + ) +} + +// Validate returns an error if the Parmas is invalid. +func (p *Params) Validate() error { + if err := p.EnabledConversionPairs.Validate(); err != nil { + return err + } + return nil +} diff --git a/x/evmutil/types/params_test.go b/x/evmutil/types/params_test.go new file mode 100644 index 00000000..9a417fe5 --- /dev/null +++ b/x/evmutil/types/params_test.go @@ -0,0 +1,75 @@ +package types_test + +import ( + bytes "bytes" + "testing" + + "github.com/stretchr/testify/suite" + "sigs.k8s.io/yaml" + + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + + "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/evmutil/testutil" + "github.com/kava-labs/kava/x/evmutil/types" +) + +type ParamsTestSuite struct { + suite.Suite +} + +func (suite *ParamsTestSuite) SetupTest() { + app.SetSDKConfig() +} + +func (suite *ParamsTestSuite) TestDefault() { + defaultParams := types.DefaultParams() + suite.Require().NoError(defaultParams.Validate()) +} + +func (suite *ParamsTestSuite) TestMarshalYAML() { + conversionPairs := types.NewConversionPairs( + types.NewConversionPair( + testutil.MustNewInternalEVMAddressFromString("0x0000000000000000000000000000000000000001"), + "usdc", + ), + ) + + p := types.NewParams( + conversionPairs, + ) + + data, err := yaml.Marshal(p) + suite.Require().NoError(err) + + var params map[string]interface{} + err = yaml.Unmarshal(data, ¶ms) + suite.Require().NoError(err) + _, ok := params["enabled_conversion_pairs"] + suite.Require().True(ok, "enabled_conversion_pairs should exist in yaml") +} + +func (suite *ParamsTestSuite) TestParamSetPairs_EnabledConversionPairs() { + suite.Require().Equal([]byte("EnabledConversionPairs"), types.KeyEnabledConversionPairs) + defaultParams := types.DefaultParams() + + var paramSetPair *paramstypes.ParamSetPair + for _, pair := range defaultParams.ParamSetPairs() { + if bytes.Equal(pair.Key, types.KeyEnabledConversionPairs) { + paramSetPair = &pair + break + } + } + suite.Require().NotNil(paramSetPair) + + pairs, ok := paramSetPair.Value.(*types.ConversionPairs) + suite.Require().True(ok) + suite.Require().Equal(pairs, &defaultParams.EnabledConversionPairs) + + suite.Require().Nil(paramSetPair.ValidatorFn(*pairs)) + suite.Require().EqualError(paramSetPair.ValidatorFn(struct{}{}), "invalid parameter type: struct {}") +} + +func TestParamsTestSuite(t *testing.T) { + suite.Run(t, new(ParamsTestSuite)) +} diff --git a/x/evmutil/types/query.pb.go b/x/evmutil/types/query.pb.go new file mode 100644 index 00000000..e1e52b90 --- /dev/null +++ b/x/evmutil/types/query.pb.go @@ -0,0 +1,638 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: kava/evmutil/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryParamsRequest defines the request type for querying x/evmutil parameters. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4a8d0512331709e7, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse defines the response type for querying x/evmutil parameters. +type QueryParamsResponse struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4a8d0512331709e7, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "kava.evmutil.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "kava.evmutil.v1beta1.QueryParamsResponse") +} + +func init() { proto.RegisterFile("kava/evmutil/v1beta1/query.proto", fileDescriptor_4a8d0512331709e7) } + +var fileDescriptor_4a8d0512331709e7 = []byte{ + // 294 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x90, 0x31, 0x4b, 0xc4, 0x30, + 0x18, 0x86, 0x1b, 0xd1, 0x0e, 0x71, 0x8b, 0x1d, 0xa4, 0x94, 0x78, 0x14, 0x87, 0xbb, 0xc1, 0x84, + 0x3b, 0x37, 0xc7, 0x5b, 0x5d, 0xbc, 0x1b, 0xdd, 0x52, 0x09, 0xb1, 0xd8, 0xf6, 0xeb, 0x35, 0x69, + 0xf1, 0x56, 0x9d, 0x05, 0xc1, 0x3f, 0xe1, 0x4f, 0xb9, 0xf1, 0xc0, 0xc5, 0x49, 0xce, 0xd6, 0x1f, + 0x22, 0x6d, 0x8a, 0x20, 0x76, 0x70, 0x0b, 0x5f, 0x9e, 0xef, 0x79, 0xdf, 0x04, 0x8f, 0xee, 0x44, + 0x25, 0xb8, 0xac, 0xd2, 0xd2, 0xc4, 0x09, 0xaf, 0xa6, 0x91, 0x34, 0x62, 0xca, 0x57, 0xa5, 0x2c, + 0xd6, 0x2c, 0x2f, 0xc0, 0x00, 0xf1, 0x5a, 0x82, 0xf5, 0x04, 0xeb, 0x09, 0xdf, 0x53, 0xa0, 0xa0, + 0x03, 0x78, 0x7b, 0xb2, 0xac, 0x1f, 0x28, 0x00, 0x95, 0x48, 0x2e, 0xf2, 0x98, 0x8b, 0x2c, 0x03, + 0x23, 0x4c, 0x0c, 0x99, 0xee, 0x6f, 0xc3, 0xc1, 0x2c, 0x25, 0x33, 0xa9, 0xe3, 0x9e, 0x09, 0x3d, + 0x4c, 0x16, 0x6d, 0xf8, 0x95, 0x28, 0x44, 0xaa, 0x97, 0x72, 0x55, 0x4a, 0x6d, 0xc2, 0x05, 0x3e, + 0xfa, 0x35, 0xd5, 0x39, 0x64, 0x5a, 0x92, 0x0b, 0xec, 0xe6, 0xdd, 0xe4, 0x18, 0x8d, 0xd0, 0xf8, + 0x70, 0x16, 0xb0, 0xa1, 0xae, 0xcc, 0x6e, 0xcd, 0xf7, 0x37, 0x1f, 0x27, 0xce, 0xb2, 0xdf, 0x98, + 0x3d, 0x21, 0x7c, 0xd0, 0x39, 0xc9, 0x23, 0xc2, 0xae, 0x45, 0xc8, 0x78, 0x58, 0xf0, 0xb7, 0x91, + 0x3f, 0xf9, 0x07, 0x69, 0x5b, 0x86, 0xa7, 0x0f, 0x6f, 0x5f, 0x2f, 0x7b, 0x94, 0x04, 0x7c, 0xf0, + 0xfd, 0xb6, 0xcf, 0xfc, 0x72, 0xf7, 0x49, 0xd1, 0x6b, 0x4d, 0xd1, 0xa6, 0xa6, 0x68, 0x5b, 0x53, + 0xb4, 0xab, 0x29, 0x7a, 0x6e, 0xa8, 0xb3, 0x6d, 0xa8, 0xf3, 0xde, 0x50, 0xe7, 0x7a, 0xa2, 0x62, + 0x73, 0x5b, 0x46, 0xec, 0x06, 0xd2, 0xce, 0x74, 0x96, 0x88, 0x48, 0x5b, 0xe7, 0xfd, 0x8f, 0xd5, + 0xac, 0x73, 0xa9, 0x23, 0xb7, 0xfb, 0xcc, 0xf3, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xeb, 0xa2, + 0x8d, 0x8c, 0xde, 0x01, 0x00, 0x00, +} + +func (this *QueryParamsRequest) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*QueryParamsRequest) + if !ok { + that2, ok := that.(QueryParamsRequest) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *QueryParamsRequest") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *QueryParamsRequest but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *QueryParamsRequest but is not nil && this == nil") + } + return nil +} +func (this *QueryParamsRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*QueryParamsRequest) + if !ok { + that2, ok := that.(QueryParamsRequest) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + return true +} +func (this *QueryParamsResponse) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*QueryParamsResponse) + if !ok { + that2, ok := that.(QueryParamsResponse) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *QueryParamsResponse") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *QueryParamsResponse but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *QueryParamsResponse but is not nil && this == nil") + } + if !this.Params.Equal(&that1.Params) { + return fmt.Errorf("Params this(%v) Not Equal that(%v)", this.Params, that1.Params) + } + return nil +} +func (this *QueryParamsResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*QueryParamsResponse) + if !ok { + that2, ok := that.(QueryParamsResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.Params.Equal(&that1.Params) { + return false + } + return true +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Params queries all parameters of the evmutil module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/kava.evmutil.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params queries all parameters of the evmutil module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/kava.evmutil.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "kava.evmutil.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "kava/evmutil/v1beta1/query.proto", +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/evmutil/types/query.pb.gw.go b/x/evmutil/types/query.pb.gw.go new file mode 100644 index 00000000..6e382189 --- /dev/null +++ b/x/evmutil/types/query.pb.gw.go @@ -0,0 +1,148 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: kava/evmutil/v1beta1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"kava", "evmutil", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/evmutil/types/tx.pb.go b/x/evmutil/types/tx.pb.go new file mode 100644 index 00000000..fc3cc007 --- /dev/null +++ b/x/evmutil/types/tx.pb.go @@ -0,0 +1,1378 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: kava/evmutil/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgConvertCoinToERC20 defines a conversion from sdk.Coin to Kava ERC20. +type MsgConvertCoinToERC20 struct { + // Kava bech32 address initiating the conversion. + Initiator string `protobuf:"bytes,1,opt,name=initiator,proto3" json:"initiator,omitempty"` + // EVM 0x hex address that will receive the converted Kava ERC20 tokens. + Receiver string `protobuf:"bytes,2,opt,name=receiver,proto3" json:"receiver,omitempty"` + // Amount is the sdk.Coin amount to convert. + Amount *types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` +} + +func (m *MsgConvertCoinToERC20) Reset() { *m = MsgConvertCoinToERC20{} } +func (m *MsgConvertCoinToERC20) String() string { return proto.CompactTextString(m) } +func (*MsgConvertCoinToERC20) ProtoMessage() {} +func (*MsgConvertCoinToERC20) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82783c6c58f89c, []int{0} +} +func (m *MsgConvertCoinToERC20) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConvertCoinToERC20) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConvertCoinToERC20.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgConvertCoinToERC20) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConvertCoinToERC20.Merge(m, src) +} +func (m *MsgConvertCoinToERC20) XXX_Size() int { + return m.Size() +} +func (m *MsgConvertCoinToERC20) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConvertCoinToERC20.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConvertCoinToERC20 proto.InternalMessageInfo + +func (m *MsgConvertCoinToERC20) GetInitiator() string { + if m != nil { + return m.Initiator + } + return "" +} + +func (m *MsgConvertCoinToERC20) GetReceiver() string { + if m != nil { + return m.Receiver + } + return "" +} + +func (m *MsgConvertCoinToERC20) GetAmount() *types.Coin { + if m != nil { + return m.Amount + } + return nil +} + +// MsgConvertCoinToERC20Response defines the response value from Msg/ConvertCoinToERC20. +type MsgConvertCoinToERC20Response struct { +} + +func (m *MsgConvertCoinToERC20Response) Reset() { *m = MsgConvertCoinToERC20Response{} } +func (m *MsgConvertCoinToERC20Response) String() string { return proto.CompactTextString(m) } +func (*MsgConvertCoinToERC20Response) ProtoMessage() {} +func (*MsgConvertCoinToERC20Response) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82783c6c58f89c, []int{1} +} +func (m *MsgConvertCoinToERC20Response) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConvertCoinToERC20Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConvertCoinToERC20Response.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgConvertCoinToERC20Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConvertCoinToERC20Response.Merge(m, src) +} +func (m *MsgConvertCoinToERC20Response) XXX_Size() int { + return m.Size() +} +func (m *MsgConvertCoinToERC20Response) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConvertCoinToERC20Response.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConvertCoinToERC20Response proto.InternalMessageInfo + +// MsgConvertERC20ToCoin defines a conversion from Kava ERC20 to sdk.Coin. +type MsgConvertERC20ToCoin struct { + // EVM 0x hex address initiating the conversion. + Initiator string `protobuf:"bytes,1,opt,name=initiator,proto3" json:"initiator,omitempty"` + // Kava bech32 address that will receive the converted sdk.Coin. + Receiver string `protobuf:"bytes,2,opt,name=receiver,proto3" json:"receiver,omitempty"` + // EVM 0x hex address of the ERC20 contract. + KavaERC20Address string `protobuf:"bytes,3,opt,name=kava_erc20_address,json=kavaErc20Address,proto3" json:"kava_erc20_address,omitempty"` + // ERC20 token amount to convert. + Amount github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=amount,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"amount"` +} + +func (m *MsgConvertERC20ToCoin) Reset() { *m = MsgConvertERC20ToCoin{} } +func (m *MsgConvertERC20ToCoin) String() string { return proto.CompactTextString(m) } +func (*MsgConvertERC20ToCoin) ProtoMessage() {} +func (*MsgConvertERC20ToCoin) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82783c6c58f89c, []int{2} +} +func (m *MsgConvertERC20ToCoin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConvertERC20ToCoin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConvertERC20ToCoin.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgConvertERC20ToCoin) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConvertERC20ToCoin.Merge(m, src) +} +func (m *MsgConvertERC20ToCoin) XXX_Size() int { + return m.Size() +} +func (m *MsgConvertERC20ToCoin) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConvertERC20ToCoin.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConvertERC20ToCoin proto.InternalMessageInfo + +func (m *MsgConvertERC20ToCoin) GetInitiator() string { + if m != nil { + return m.Initiator + } + return "" +} + +func (m *MsgConvertERC20ToCoin) GetReceiver() string { + if m != nil { + return m.Receiver + } + return "" +} + +func (m *MsgConvertERC20ToCoin) GetKavaERC20Address() string { + if m != nil { + return m.KavaERC20Address + } + return "" +} + +// MsgConvertERC20ToCoinResponse defines the response value from +// Msg/MsgConvertERC20ToCoin. +type MsgConvertERC20ToCoinResponse struct { +} + +func (m *MsgConvertERC20ToCoinResponse) Reset() { *m = MsgConvertERC20ToCoinResponse{} } +func (m *MsgConvertERC20ToCoinResponse) String() string { return proto.CompactTextString(m) } +func (*MsgConvertERC20ToCoinResponse) ProtoMessage() {} +func (*MsgConvertERC20ToCoinResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82783c6c58f89c, []int{3} +} +func (m *MsgConvertERC20ToCoinResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConvertERC20ToCoinResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConvertERC20ToCoinResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgConvertERC20ToCoinResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConvertERC20ToCoinResponse.Merge(m, src) +} +func (m *MsgConvertERC20ToCoinResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgConvertERC20ToCoinResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConvertERC20ToCoinResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConvertERC20ToCoinResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgConvertCoinToERC20)(nil), "kava.evmutil.v1beta1.MsgConvertCoinToERC20") + proto.RegisterType((*MsgConvertCoinToERC20Response)(nil), "kava.evmutil.v1beta1.MsgConvertCoinToERC20Response") + proto.RegisterType((*MsgConvertERC20ToCoin)(nil), "kava.evmutil.v1beta1.MsgConvertERC20ToCoin") + proto.RegisterType((*MsgConvertERC20ToCoinResponse)(nil), "kava.evmutil.v1beta1.MsgConvertERC20ToCoinResponse") +} + +func init() { proto.RegisterFile("kava/evmutil/v1beta1/tx.proto", fileDescriptor_6e82783c6c58f89c) } + +var fileDescriptor_6e82783c6c58f89c = []byte{ + // 469 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xb1, 0x6e, 0x13, 0x31, + 0x18, 0x8e, 0x5b, 0x54, 0x11, 0xb3, 0x54, 0x56, 0x90, 0xd2, 0x88, 0x3a, 0x55, 0x06, 0x54, 0x84, + 0xe2, 0x6b, 0x52, 0xc4, 0xc4, 0x42, 0xa2, 0x0e, 0x55, 0xd5, 0xe5, 0xc8, 0xc4, 0x12, 0xf9, 0x2e, + 0xd6, 0x61, 0xb5, 0x67, 0x47, 0xb6, 0x73, 0x2a, 0x0f, 0xc0, 0xce, 0x13, 0x30, 0xf3, 0x00, 0x7d, + 0x88, 0x8e, 0x55, 0x27, 0xc4, 0x10, 0x95, 0xcb, 0x33, 0xb0, 0x23, 0xfb, 0x9c, 0xeb, 0x09, 0x4e, + 0x44, 0x9d, 0x62, 0xe7, 0xfb, 0xfe, 0xff, 0xff, 0xbe, 0xef, 0x3f, 0xc3, 0xfd, 0x0b, 0x9a, 0xd1, + 0x80, 0x65, 0xe9, 0xc2, 0xf0, 0xcb, 0x20, 0x1b, 0x44, 0xcc, 0xd0, 0x41, 0x60, 0xae, 0xc8, 0x5c, + 0x49, 0x23, 0x51, 0xcb, 0xc2, 0xc4, 0xc3, 0xc4, 0xc3, 0x9d, 0x56, 0x22, 0x13, 0xe9, 0x08, 0x81, + 0x3d, 0x15, 0xdc, 0x0e, 0x8e, 0xa5, 0x4e, 0xa5, 0x0e, 0x22, 0xaa, 0x59, 0xd9, 0x29, 0x96, 0x5c, + 0x78, 0x7c, 0xaf, 0xc0, 0xa7, 0x45, 0x61, 0x71, 0x29, 0xa0, 0xde, 0x37, 0x00, 0x9f, 0x9f, 0xeb, + 0x64, 0x2c, 0x45, 0xc6, 0x94, 0x19, 0x4b, 0x2e, 0x26, 0xf2, 0x24, 0x1c, 0x0f, 0x8f, 0xd0, 0x5b, + 0xd8, 0xe4, 0x82, 0x1b, 0x4e, 0x8d, 0x54, 0x6d, 0x70, 0x00, 0x0e, 0x9b, 0xa3, 0xf6, 0xdd, 0x75, + 0xbf, 0xe5, 0xcb, 0xdf, 0xcf, 0x66, 0x8a, 0x69, 0xfd, 0xc1, 0x28, 0x2e, 0x92, 0xf0, 0x81, 0x8a, + 0x3a, 0xf0, 0xa9, 0x62, 0x31, 0xe3, 0x19, 0x53, 0xed, 0x2d, 0x5b, 0x16, 0x96, 0x77, 0x34, 0x80, + 0x3b, 0x34, 0x95, 0x0b, 0x61, 0xda, 0xdb, 0x07, 0xe0, 0xf0, 0xd9, 0x70, 0x8f, 0xf8, 0x6e, 0x56, + 0xf9, 0xda, 0x24, 0xb1, 0x2a, 0x42, 0x4f, 0xec, 0x75, 0xe1, 0x7e, 0xad, 0xbe, 0x90, 0xe9, 0xb9, + 0x14, 0x9a, 0xf5, 0xbe, 0x6c, 0x55, 0x1d, 0x38, 0x6c, 0x22, 0x2d, 0x11, 0xbd, 0xf8, 0xc7, 0x41, + 0x55, 0xe7, 0x9b, 0xbf, 0x75, 0xfe, 0xc7, 0xde, 0x83, 0x83, 0x11, 0x44, 0x76, 0x31, 0x53, 0xa6, + 0xe2, 0xe1, 0xd1, 0x94, 0x16, 0x2c, 0xe7, 0xa6, 0x39, 0x6a, 0xe5, 0xcb, 0xee, 0xee, 0x19, 0xcd, + 0xa8, 0x13, 0xe1, 0x3b, 0x84, 0xbb, 0x96, 0x7f, 0x62, 0xe9, 0xfe, 0x1f, 0x34, 0x29, 0x53, 0x78, + 0xe2, 0xea, 0xde, 0xdd, 0x2c, 0xbb, 0x8d, 0x9f, 0xcb, 0xee, 0xcb, 0x84, 0x9b, 0x4f, 0x8b, 0x88, + 0xc4, 0x32, 0xf5, 0x4b, 0xf2, 0x3f, 0x7d, 0x3d, 0xbb, 0x08, 0xcc, 0xe7, 0x39, 0xd3, 0xe4, 0x54, + 0x98, 0xbb, 0xeb, 0x3e, 0xf4, 0x2a, 0x4f, 0x85, 0xa9, 0x0f, 0xaa, 0x12, 0xc3, 0x3a, 0xa8, 0xe1, + 0x6f, 0x00, 0xb7, 0xcf, 0x75, 0x82, 0x32, 0x88, 0x6a, 0xd6, 0xfd, 0x9a, 0xd4, 0x7d, 0x70, 0xa4, + 0x36, 0xfb, 0xce, 0xf1, 0x23, 0xc8, 0xeb, 0xf9, 0x95, 0xb9, 0xd5, 0x25, 0x6d, 0x9c, 0x5b, 0x21, + 0x6f, 0x9e, 0x5b, 0xe3, 0x7b, 0x74, 0x76, 0xff, 0x0b, 0x83, 0xef, 0x39, 0x06, 0x37, 0x39, 0x06, + 0xb7, 0x39, 0x06, 0xf7, 0x39, 0x06, 0x5f, 0x57, 0xb8, 0x71, 0xbb, 0xc2, 0x8d, 0x1f, 0x2b, 0xdc, + 0xf8, 0xf8, 0xaa, 0x12, 0xbc, 0x1d, 0xd0, 0xbf, 0xa4, 0x91, 0x76, 0xa7, 0xe0, 0xaa, 0x7c, 0xa1, + 0x2e, 0xff, 0x68, 0xc7, 0x3d, 0x9b, 0xe3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x21, 0x5f, 0xa5, + 0x78, 0xbe, 0x03, 0x00, 0x00, +} + +func (this *MsgConvertCoinToERC20) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*MsgConvertCoinToERC20) + if !ok { + that2, ok := that.(MsgConvertCoinToERC20) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *MsgConvertCoinToERC20") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *MsgConvertCoinToERC20 but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *MsgConvertCoinToERC20 but is not nil && this == nil") + } + if this.Initiator != that1.Initiator { + return fmt.Errorf("Initiator this(%v) Not Equal that(%v)", this.Initiator, that1.Initiator) + } + if this.Receiver != that1.Receiver { + return fmt.Errorf("Receiver this(%v) Not Equal that(%v)", this.Receiver, that1.Receiver) + } + if !this.Amount.Equal(that1.Amount) { + return fmt.Errorf("Amount this(%v) Not Equal that(%v)", this.Amount, that1.Amount) + } + return nil +} +func (this *MsgConvertCoinToERC20) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgConvertCoinToERC20) + if !ok { + that2, ok := that.(MsgConvertCoinToERC20) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Initiator != that1.Initiator { + return false + } + if this.Receiver != that1.Receiver { + return false + } + if !this.Amount.Equal(that1.Amount) { + return false + } + return true +} +func (this *MsgConvertCoinToERC20Response) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*MsgConvertCoinToERC20Response) + if !ok { + that2, ok := that.(MsgConvertCoinToERC20Response) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *MsgConvertCoinToERC20Response") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *MsgConvertCoinToERC20Response but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *MsgConvertCoinToERC20Response but is not nil && this == nil") + } + return nil +} +func (this *MsgConvertCoinToERC20Response) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgConvertCoinToERC20Response) + if !ok { + that2, ok := that.(MsgConvertCoinToERC20Response) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + return true +} +func (this *MsgConvertERC20ToCoin) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*MsgConvertERC20ToCoin) + if !ok { + that2, ok := that.(MsgConvertERC20ToCoin) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *MsgConvertERC20ToCoin") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *MsgConvertERC20ToCoin but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *MsgConvertERC20ToCoin but is not nil && this == nil") + } + if this.Initiator != that1.Initiator { + return fmt.Errorf("Initiator this(%v) Not Equal that(%v)", this.Initiator, that1.Initiator) + } + if this.Receiver != that1.Receiver { + return fmt.Errorf("Receiver this(%v) Not Equal that(%v)", this.Receiver, that1.Receiver) + } + if this.KavaERC20Address != that1.KavaERC20Address { + return fmt.Errorf("KavaERC20Address this(%v) Not Equal that(%v)", this.KavaERC20Address, that1.KavaERC20Address) + } + if !this.Amount.Equal(that1.Amount) { + return fmt.Errorf("Amount this(%v) Not Equal that(%v)", this.Amount, that1.Amount) + } + return nil +} +func (this *MsgConvertERC20ToCoin) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgConvertERC20ToCoin) + if !ok { + that2, ok := that.(MsgConvertERC20ToCoin) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Initiator != that1.Initiator { + return false + } + if this.Receiver != that1.Receiver { + return false + } + if this.KavaERC20Address != that1.KavaERC20Address { + return false + } + if !this.Amount.Equal(that1.Amount) { + return false + } + return true +} +func (this *MsgConvertERC20ToCoinResponse) VerboseEqual(that interface{}) error { + if that == nil { + if this == nil { + return nil + } + return fmt.Errorf("that == nil && this != nil") + } + + that1, ok := that.(*MsgConvertERC20ToCoinResponse) + if !ok { + that2, ok := that.(MsgConvertERC20ToCoinResponse) + if ok { + that1 = &that2 + } else { + return fmt.Errorf("that is not of type *MsgConvertERC20ToCoinResponse") + } + } + if that1 == nil { + if this == nil { + return nil + } + return fmt.Errorf("that is type *MsgConvertERC20ToCoinResponse but is nil && this != nil") + } else if this == nil { + return fmt.Errorf("that is type *MsgConvertERC20ToCoinResponse but is not nil && this == nil") + } + return nil +} +func (this *MsgConvertERC20ToCoinResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgConvertERC20ToCoinResponse) + if !ok { + that2, ok := that.(MsgConvertERC20ToCoinResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + return true +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // ConvertCoinToERC20 defines a method for converting sdk.Coin to Kava ERC20. + ConvertCoinToERC20(ctx context.Context, in *MsgConvertCoinToERC20, opts ...grpc.CallOption) (*MsgConvertCoinToERC20Response, error) + // ConvertERC20ToCoin defines a method for converting Kava ERC20 to sdk.Coin. + ConvertERC20ToCoin(ctx context.Context, in *MsgConvertERC20ToCoin, opts ...grpc.CallOption) (*MsgConvertERC20ToCoinResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) ConvertCoinToERC20(ctx context.Context, in *MsgConvertCoinToERC20, opts ...grpc.CallOption) (*MsgConvertCoinToERC20Response, error) { + out := new(MsgConvertCoinToERC20Response) + err := c.cc.Invoke(ctx, "/kava.evmutil.v1beta1.Msg/ConvertCoinToERC20", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ConvertERC20ToCoin(ctx context.Context, in *MsgConvertERC20ToCoin, opts ...grpc.CallOption) (*MsgConvertERC20ToCoinResponse, error) { + out := new(MsgConvertERC20ToCoinResponse) + err := c.cc.Invoke(ctx, "/kava.evmutil.v1beta1.Msg/ConvertERC20ToCoin", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // ConvertCoinToERC20 defines a method for converting sdk.Coin to Kava ERC20. + ConvertCoinToERC20(context.Context, *MsgConvertCoinToERC20) (*MsgConvertCoinToERC20Response, error) + // ConvertERC20ToCoin defines a method for converting Kava ERC20 to sdk.Coin. + ConvertERC20ToCoin(context.Context, *MsgConvertERC20ToCoin) (*MsgConvertERC20ToCoinResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) ConvertCoinToERC20(ctx context.Context, req *MsgConvertCoinToERC20) (*MsgConvertCoinToERC20Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConvertCoinToERC20 not implemented") +} +func (*UnimplementedMsgServer) ConvertERC20ToCoin(ctx context.Context, req *MsgConvertERC20ToCoin) (*MsgConvertERC20ToCoinResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConvertERC20ToCoin not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_ConvertCoinToERC20_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConvertCoinToERC20) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConvertCoinToERC20(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/kava.evmutil.v1beta1.Msg/ConvertCoinToERC20", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConvertCoinToERC20(ctx, req.(*MsgConvertCoinToERC20)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ConvertERC20ToCoin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConvertERC20ToCoin) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConvertERC20ToCoin(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/kava.evmutil.v1beta1.Msg/ConvertERC20ToCoin", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConvertERC20ToCoin(ctx, req.(*MsgConvertERC20ToCoin)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "kava.evmutil.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ConvertCoinToERC20", + Handler: _Msg_ConvertCoinToERC20_Handler, + }, + { + MethodName: "ConvertERC20ToCoin", + Handler: _Msg_ConvertERC20ToCoin_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "kava/evmutil/v1beta1/tx.proto", +} + +func (m *MsgConvertCoinToERC20) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgConvertCoinToERC20) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConvertCoinToERC20) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Amount != nil { + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Receiver) > 0 { + i -= len(m.Receiver) + copy(dAtA[i:], m.Receiver) + i = encodeVarintTx(dAtA, i, uint64(len(m.Receiver))) + i-- + dAtA[i] = 0x12 + } + if len(m.Initiator) > 0 { + i -= len(m.Initiator) + copy(dAtA[i:], m.Initiator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Initiator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgConvertCoinToERC20Response) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgConvertCoinToERC20Response) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConvertCoinToERC20Response) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgConvertERC20ToCoin) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgConvertERC20ToCoin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConvertERC20ToCoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.KavaERC20Address) > 0 { + i -= len(m.KavaERC20Address) + copy(dAtA[i:], m.KavaERC20Address) + i = encodeVarintTx(dAtA, i, uint64(len(m.KavaERC20Address))) + i-- + dAtA[i] = 0x1a + } + if len(m.Receiver) > 0 { + i -= len(m.Receiver) + copy(dAtA[i:], m.Receiver) + i = encodeVarintTx(dAtA, i, uint64(len(m.Receiver))) + i-- + dAtA[i] = 0x12 + } + if len(m.Initiator) > 0 { + i -= len(m.Initiator) + copy(dAtA[i:], m.Initiator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Initiator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgConvertERC20ToCoinResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgConvertERC20ToCoinResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConvertERC20ToCoinResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgConvertCoinToERC20) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Initiator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Receiver) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Amount != nil { + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgConvertCoinToERC20Response) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgConvertERC20ToCoin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Initiator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Receiver) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.KavaERC20Address) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgConvertERC20ToCoinResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgConvertCoinToERC20) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgConvertCoinToERC20: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConvertCoinToERC20: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Initiator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Initiator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Receiver = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Amount == nil { + m.Amount = &types.Coin{} + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgConvertCoinToERC20Response) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgConvertCoinToERC20Response: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConvertCoinToERC20Response: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgConvertERC20ToCoin) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgConvertERC20ToCoin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConvertERC20ToCoin: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Initiator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Initiator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Receiver = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KavaERC20Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.KavaERC20Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgConvertERC20ToCoinResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgConvertERC20ToCoinResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConvertERC20ToCoinResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +)