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 <rhuairahrighairidh@users.noreply.github.com>

Co-authored-by: Ruaridh <rhuairahrighairidh@users.noreply.github.com>
This commit is contained in:
Draco Li 2022-08-23 13:04:40 -04:00 committed by GitHub
parent ed9991d44a
commit 2b123bf007
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 7738 additions and 5885 deletions

View File

@ -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),
)

777
app/ante/eip712_test.go Normal file
View File

@ -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(&etherminttypes.ExtensionOptionsWeb3Tx{
FeePayer: from.String(),
TypedDataChainID: ethChainId,
FeePayerSig: signature,
})
suite.Require().NoError(err)
suite.clientCtx.TxConfig.SignModeHandler()
txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder)
suite.Require().True(ok)
builder.SetExtensionOptions(option)
builder.SetFeeAmount(gasAmount)
builder.SetGasLimit(gas)
sigsV2 := signing.SignatureV2{
PubKey: pubKey,
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
},
Sequence: nonce,
}
err = builder.SetSignatures(sigsV2)
suite.Require().NoError(err)
err = builder.SetMsgs(msgs...)
suite.Require().NoError(err)
return builder
}
func (suite *EIP712TestSuite) SetupTest() {
tApp := app.NewTestApp()
suite.tApp = tApp
cdc := tApp.AppCodec()
suite.evmutilKeeper = tApp.GetEvmutilKeeper()
addr, privkey := tests.NewAddrKey()
suite.testAddr = sdk.AccAddress(addr.Bytes())
suite.testPrivKey = privkey
suite.testEVMAddr = evmutiltestutil.MustNewInternalEVMAddressFromString(addr.String())
addr2, privKey2 := tests.NewAddrKey()
suite.testPrivKey2 = privKey2
suite.testAddr2 = sdk.AccAddress(addr2.Bytes())
suite.testEVMAddr2 = evmutiltestutil.MustNewInternalEVMAddressFromString(addr2.String())
encodingConfig := app.MakeEncodingConfig()
suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
suite.ethSigner = ethtypes.LatestSignerForChainID(tApp.GetEvmKeeper().ChainID())
// Genesis states
evmGs := evmtypes.NewGenesisState(
evmtypes.NewParams("akava", 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 := &etherminttypes.EthAccount{
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(consAddress.Bytes()), nil, 0, 0),
CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(),
}
tApp.GetAccountKeeper().SetAccount(ctx, valAcc)
_, testAddresses := app.GeneratePrivKeyAddressPairs(1)
valAddr := sdk.ValAddress(testAddresses[0].Bytes())
validator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{})
suite.Require().NoError(err)
err = tApp.GetStakingKeeper().SetValidatorByConsAddr(ctx, validator)
suite.Require().NoError(err)
tApp.GetStakingKeeper().SetValidator(ctx, validator)
// Deploy an ERC20 contract for USDC
contractAddr := suite.deployUSDCERC20(tApp, ctx)
pair := evmutiltypes.NewConversionPair(
contractAddr,
USDCCoinDenom,
)
suite.usdcEVMAddr = pair.GetAddress()
// 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(&etherminttypes.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))
}

View File

@ -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],

View File

@ -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.

View File

@ -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.
<a name="kava/evmutil/v1beta1/conversion_pair.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## kava/evmutil/v1beta1/conversion_pair.proto
<a name="kava.evmutil.v1beta1.ConversionPair"></a>
### 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 |
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<!-- end services -->
<a name="kava/evmutil/v1beta1/genesis.proto"></a>
<p align="right"><a href="#top">Top</a></p>
@ -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. |
<a name="kava.evmutil.v1beta1.Params"></a>
### 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.
<a name="kava/evmutil/v1beta1/query.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## kava/evmutil/v1beta1/query.proto
<a name="kava.evmutil.v1beta1.QueryParamsRequest"></a>
### QueryParamsRequest
QueryParamsRequest defines the request type for querying x/evmutil parameters.
<a name="kava.evmutil.v1beta1.QueryParamsResponse"></a>
### QueryParamsResponse
QueryParamsResponse defines the response type for querying x/evmutil parameters.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `params` | [Params](#kava.evmutil.v1beta1.Params) | | |
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<a name="kava.evmutil.v1beta1.Query"></a>
### 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|
<!-- end services -->
<a name="kava/evmutil/v1beta1/tx.proto"></a>
<p align="right"><a href="#top">Top</a></p>
## kava/evmutil/v1beta1/tx.proto
<a name="kava.evmutil.v1beta1.MsgConvertCoinToERC20"></a>
### 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. |
<a name="kava.evmutil.v1beta1.MsgConvertCoinToERC20Response"></a>
### MsgConvertCoinToERC20Response
MsgConvertCoinToERC20Response defines the response value from Msg/ConvertCoinToERC20.
<a name="kava.evmutil.v1beta1.MsgConvertERC20ToCoin"></a>
### 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. |
<a name="kava.evmutil.v1beta1.MsgConvertERC20ToCoinResponse"></a>
### MsgConvertERC20ToCoinResponse
MsgConvertERC20ToCoinResponse defines the response value from
Msg/MsgConvertERC20ToCoin.
<!-- end messages -->
<!-- end enums -->
<!-- end HasExtensions -->
<a name="kava.evmutil.v1beta1.Msg"></a>
### 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. | |
<!-- end services -->
<a name="kava/hard/v1beta1/hard.proto"></a>
<p align="right"><a href="#top">Top</a></p>

6
go.mod
View File

@ -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
)

44
go.sum
View File

@ -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=

View File

@ -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
},
}

View File

@ -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 = ""

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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).

View File

@ -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)
}

View File

@ -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
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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 (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"];
}

View File

@ -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];
}

View File

@ -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 {}

View File

@ -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,
)
}

129
x/evmutil/client/cli/evm.go Normal file
View File

@ -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
&ethtypes.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)
}

View File

@ -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)
},
}
}

122
x/evmutil/client/cli/tx.go Normal file
View File

@ -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 <key> --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 <key> --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)
},
}
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -20,9 +20,12 @@ func (suite *genesisTestSuite) SetupTest() {
}
func (s *genesisTestSuite) TestInitGenesis_SetAccounts() {
gs := types.NewGenesisState([]types.Account{
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{
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) {

View File

@ -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
}

View File

@ -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)
}

144
x/evmutil/keeper/erc20.go Normal file
View File

@ -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
}

View File

@ -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)
}

152
x/evmutil/keeper/evm.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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,9 +21,11 @@ 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)
if res, stop := BackedCoinsInvariant(bankK, k)(ctx); stop {
return res, stop
}
return SmallBalancesInvariant(bankK, k)(ctx)
}
}
// FullyBackedInvariant ensures all minor balances are backed by the coins in the module account.
@ -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
}
}

View File

@ -1,6 +1,7 @@
package keeper_test
import (
"math/big"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -15,6 +16,7 @@ import (
type invariantTestSuite struct {
testutil.Suite
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

View File

@ -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"
)
@ -15,16 +16,37 @@ import (
type Keeper struct {
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 {
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.
func (k Keeper) GetAllAccounts(ctx sdk.Context) (accounts []types.Account) {
k.IterateAllAccounts(ctx, func(account types.Account) bool {

View File

@ -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
}

View File

@ -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)
}
})
}
}

View File

@ -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, &params)
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, &params)
}
// 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)
}

View File

@ -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)
}

View File

@ -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) {

View File

@ -1,16 +1,44 @@
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 {
@ -18,12 +46,18 @@ type Suite struct {
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 := &etherminttypes.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,
&ethtypes.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
}

View File

@ -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
}

25
x/evmutil/types/bytes.go Normal file
View File

@ -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)
}

View File

@ -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))
}
}

36
x/evmutil/types/codec.go Normal file
View File

@ -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()
}

View File

@ -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")
}
}

View File

@ -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()
}

View File

@ -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")
)

View File

@ -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)
}
})
}
}

14
x/evmutil/types/errors.go Normal file
View File

@ -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")
)

File diff suppressed because one or more lines are too long

18
x/evmutil/types/events.go Normal file
View File

@ -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"
)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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())
}

146
x/evmutil/types/msg.go Normal file
View File

@ -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
}

199
x/evmutil/types/msg_test.go Normal file
View File

@ -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)
}
})
}
}

48
x/evmutil/types/params.go Normal file
View File

@ -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
}

View File

@ -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, &params)
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))
}

638
x/evmutil/types/query.pb.go Normal file
View File

@ -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")
)

View File

@ -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
)

1378
x/evmutil/types/tx.pb.go Normal file

File diff suppressed because it is too large Load Diff