mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-20 15:05:21 +00:00
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:
parent
ed9991d44a
commit
2b123bf007
@ -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
777
app/ante/eip712_test.go
Normal 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(ðerminttypes.ExtensionOptionsWeb3Tx{
|
||||
FeePayer: from.String(),
|
||||
TypedDataChainID: ethChainId,
|
||||
FeePayerSig: signature,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.clientCtx.TxConfig.SignModeHandler()
|
||||
txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
|
||||
builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder)
|
||||
suite.Require().True(ok)
|
||||
|
||||
builder.SetExtensionOptions(option)
|
||||
builder.SetFeeAmount(gasAmount)
|
||||
builder.SetGasLimit(gas)
|
||||
|
||||
sigsV2 := signing.SignatureV2{
|
||||
PubKey: pubKey,
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
},
|
||||
Sequence: nonce,
|
||||
}
|
||||
|
||||
err = builder.SetSignatures(sigsV2)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = builder.SetMsgs(msgs...)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
func (suite *EIP712TestSuite) SetupTest() {
|
||||
tApp := app.NewTestApp()
|
||||
suite.tApp = tApp
|
||||
cdc := tApp.AppCodec()
|
||||
suite.evmutilKeeper = tApp.GetEvmutilKeeper()
|
||||
|
||||
addr, privkey := tests.NewAddrKey()
|
||||
suite.testAddr = sdk.AccAddress(addr.Bytes())
|
||||
suite.testPrivKey = privkey
|
||||
suite.testEVMAddr = evmutiltestutil.MustNewInternalEVMAddressFromString(addr.String())
|
||||
addr2, privKey2 := tests.NewAddrKey()
|
||||
suite.testPrivKey2 = privKey2
|
||||
suite.testAddr2 = sdk.AccAddress(addr2.Bytes())
|
||||
suite.testEVMAddr2 = evmutiltestutil.MustNewInternalEVMAddressFromString(addr2.String())
|
||||
|
||||
encodingConfig := app.MakeEncodingConfig()
|
||||
suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
|
||||
suite.ethSigner = ethtypes.LatestSignerForChainID(tApp.GetEvmKeeper().ChainID())
|
||||
|
||||
// Genesis states
|
||||
evmGs := evmtypes.NewGenesisState(
|
||||
evmtypes.NewParams("akava", true, true, evmtypes.DefaultChainConfig()),
|
||||
nil,
|
||||
)
|
||||
|
||||
feemarketGenesis := feemarkettypes.DefaultGenesisState()
|
||||
feemarketGenesis.Params.EnableHeight = 1
|
||||
feemarketGenesis.Params.NoBaseFee = false
|
||||
|
||||
cdpGenState := cdptypes.DefaultGenesisState()
|
||||
cdpGenState.Params.GlobalDebtLimit = sdk.NewInt64Coin("usdx", 53000000000000)
|
||||
cdpGenState.Params.CollateralParams = cdptypes.CollateralParams{
|
||||
{
|
||||
Denom: USDCCoinDenom,
|
||||
Type: USDCCDPType,
|
||||
LiquidationRatio: sdk.MustNewDecFromStr("1.01"),
|
||||
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
|
||||
StabilityFee: sdk.OneDec(),
|
||||
AuctionSize: sdk.NewIntFromUint64(10000000000),
|
||||
LiquidationPenalty: sdk.MustNewDecFromStr("0.05"),
|
||||
CheckCollateralizationIndexCount: sdk.NewInt(10),
|
||||
KeeperRewardPercentage: sdk.MustNewDecFromStr("0.01"),
|
||||
SpotMarketID: "usdc:usd",
|
||||
LiquidationMarketID: "usdc:usd:30",
|
||||
ConversionFactor: sdk.NewInt(18),
|
||||
},
|
||||
}
|
||||
|
||||
hardGenState := hardtypes.DefaultGenesisState()
|
||||
hardGenState.Params.MoneyMarkets = []hardtypes.MoneyMarket{
|
||||
{
|
||||
Denom: "usdx",
|
||||
BorrowLimit: hardtypes.BorrowLimit{
|
||||
HasMaxLimit: true,
|
||||
MaximumLimit: sdk.MustNewDecFromStr("100000000000"),
|
||||
LoanToValue: sdk.MustNewDecFromStr("1"),
|
||||
},
|
||||
SpotMarketID: "usdx:usd",
|
||||
ConversionFactor: sdk.NewInt(1_000_000),
|
||||
InterestRateModel: hardtypes.InterestRateModel{
|
||||
BaseRateAPY: sdk.MustNewDecFromStr("0.05"),
|
||||
BaseMultiplier: sdk.MustNewDecFromStr("2"),
|
||||
Kink: sdk.MustNewDecFromStr("0.8"),
|
||||
JumpMultiplier: sdk.MustNewDecFromStr("10"),
|
||||
},
|
||||
ReserveFactor: sdk.MustNewDecFromStr("0.05"),
|
||||
KeeperRewardPercentage: sdk.ZeroDec(),
|
||||
},
|
||||
}
|
||||
|
||||
pricefeedGenState := pricefeedtypes.DefaultGenesisState()
|
||||
pricefeedGenState.Params.Markets = []pricefeedtypes.Market{
|
||||
{
|
||||
MarketID: "usdx:usd",
|
||||
BaseAsset: "usdx",
|
||||
QuoteAsset: "usd",
|
||||
Oracles: []sdk.AccAddress{},
|
||||
Active: true,
|
||||
},
|
||||
{
|
||||
MarketID: "usdc:usd",
|
||||
BaseAsset: "usdc",
|
||||
QuoteAsset: "usd",
|
||||
Oracles: []sdk.AccAddress{},
|
||||
Active: true,
|
||||
},
|
||||
{
|
||||
MarketID: "usdc:usd:30",
|
||||
BaseAsset: "usdc",
|
||||
QuoteAsset: "usd",
|
||||
Oracles: []sdk.AccAddress{},
|
||||
Active: true,
|
||||
},
|
||||
}
|
||||
pricefeedGenState.PostedPrices = []pricefeedtypes.PostedPrice{
|
||||
{
|
||||
MarketID: "usdx:usd",
|
||||
OracleAddress: sdk.AccAddress{},
|
||||
Price: sdk.MustNewDecFromStr("1.00"),
|
||||
Expiry: time.Now().Add(1 * time.Hour),
|
||||
},
|
||||
{
|
||||
MarketID: "usdc:usd",
|
||||
OracleAddress: sdk.AccAddress{},
|
||||
Price: sdk.MustNewDecFromStr("1.00"),
|
||||
Expiry: time.Now().Add(1 * time.Hour),
|
||||
},
|
||||
{
|
||||
MarketID: "usdc:usd:30",
|
||||
OracleAddress: sdk.AccAddress{},
|
||||
Price: sdk.MustNewDecFromStr("1.00"),
|
||||
Expiry: time.Now().Add(1 * time.Hour),
|
||||
},
|
||||
}
|
||||
|
||||
genState := app.GenesisState{
|
||||
evmtypes.ModuleName: cdc.MustMarshalJSON(evmGs),
|
||||
feemarkettypes.ModuleName: cdc.MustMarshalJSON(feemarketGenesis),
|
||||
cdptypes.ModuleName: cdc.MustMarshalJSON(&cdpGenState),
|
||||
hardtypes.ModuleName: cdc.MustMarshalJSON(&hardGenState),
|
||||
pricefeedtypes.ModuleName: cdc.MustMarshalJSON(&pricefeedGenState),
|
||||
}
|
||||
|
||||
// funds our test accounts with some ukava
|
||||
coinsGenState := app.NewFundedGenStateWithSameCoins(
|
||||
tApp.AppCodec(),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 1e9)),
|
||||
[]sdk.AccAddress{suite.testAddr, suite.testAddr2},
|
||||
)
|
||||
|
||||
tApp.InitializeFromGenesisStatesWithTimeAndChainID(
|
||||
time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
ChainID,
|
||||
genState,
|
||||
coinsGenState,
|
||||
)
|
||||
|
||||
// consensus key
|
||||
consPriv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
consAddress := sdk.ConsAddress(consPriv.PubKey().Address())
|
||||
|
||||
ctx := tApp.NewContext(false, tmproto.Header{
|
||||
Height: tApp.LastBlockHeight() + 1,
|
||||
ChainID: ChainID,
|
||||
Time: time.Now().UTC(),
|
||||
ProposerAddress: consAddress.Bytes(),
|
||||
Version: tmversion.Consensus{
|
||||
Block: version.BlockProtocol,
|
||||
},
|
||||
LastBlockId: tmproto.BlockID{
|
||||
Hash: tmhash.Sum([]byte("block_id")),
|
||||
PartSetHeader: tmproto.PartSetHeader{
|
||||
Total: 11,
|
||||
Hash: tmhash.Sum([]byte("partset_header")),
|
||||
},
|
||||
},
|
||||
AppHash: tmhash.Sum([]byte("app")),
|
||||
DataHash: tmhash.Sum([]byte("data")),
|
||||
EvidenceHash: tmhash.Sum([]byte("evidence")),
|
||||
ValidatorsHash: tmhash.Sum([]byte("validators")),
|
||||
NextValidatorsHash: tmhash.Sum([]byte("next_validators")),
|
||||
ConsensusHash: tmhash.Sum([]byte("consensus")),
|
||||
LastResultsHash: tmhash.Sum([]byte("last_result")),
|
||||
})
|
||||
suite.ctx = ctx
|
||||
|
||||
// We need to set the validator as calling the EVM looks up the validator address
|
||||
// https://github.com/tharsis/ethermint/blob/f21592ebfe74da7590eb42ed926dae970b2a9a3f/x/evm/keeper/state_transition.go#L487
|
||||
// evmkeeper.EVMConfig() will return error "failed to load evm config" if not set
|
||||
valAcc := ðerminttypes.EthAccount{
|
||||
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(consAddress.Bytes()), nil, 0, 0),
|
||||
CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(),
|
||||
}
|
||||
tApp.GetAccountKeeper().SetAccount(ctx, valAcc)
|
||||
_, testAddresses := app.GeneratePrivKeyAddressPairs(1)
|
||||
valAddr := sdk.ValAddress(testAddresses[0].Bytes())
|
||||
validator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{})
|
||||
suite.Require().NoError(err)
|
||||
err = tApp.GetStakingKeeper().SetValidatorByConsAddr(ctx, validator)
|
||||
suite.Require().NoError(err)
|
||||
tApp.GetStakingKeeper().SetValidator(ctx, validator)
|
||||
|
||||
// Deploy an ERC20 contract for USDC
|
||||
contractAddr := suite.deployUSDCERC20(tApp, ctx)
|
||||
pair := evmutiltypes.NewConversionPair(
|
||||
contractAddr,
|
||||
USDCCoinDenom,
|
||||
)
|
||||
suite.usdcEVMAddr = pair.GetAddress()
|
||||
|
||||
// Add a contract to evmutil conversion pair
|
||||
suite.evmutilKeeper.SetParams(suite.ctx, evmutiltypes.NewParams(
|
||||
evmutiltypes.NewConversionPairs(
|
||||
evmutiltypes.NewConversionPair(
|
||||
// First contract evmutil module deploys
|
||||
evmutiltestutil.MustNewInternalEVMAddressFromString("0x15932E26f5BD4923d46a2b205191C4b5d5f43FE3"),
|
||||
"erc20/usdc",
|
||||
),
|
||||
),
|
||||
))
|
||||
|
||||
// allow msgs through evm eip712
|
||||
evmKeeper := suite.tApp.GetEvmKeeper()
|
||||
params := evmKeeper.GetParams(suite.ctx)
|
||||
params.EIP712AllowedMsgs = []evmtypes.EIP712AllowedMsg{
|
||||
{
|
||||
MsgTypeUrl: "/kava.evmutil.v1beta1.MsgConvertERC20ToCoin",
|
||||
MsgValueTypeName: "MsgValueEVMConvertERC20ToCoin",
|
||||
ValueTypes: []evmtypes.EIP712MsgAttrType{
|
||||
{Name: "initiator", Type: "string"},
|
||||
{Name: "receiver", Type: "string"},
|
||||
{Name: "kava_erc20_address", Type: "string"},
|
||||
{Name: "amount", Type: "string"},
|
||||
},
|
||||
},
|
||||
{
|
||||
MsgTypeUrl: "/kava.cdp.v1beta1.MsgCreateCDP",
|
||||
MsgValueTypeName: "MsgValueCDPCreate",
|
||||
ValueTypes: []evmtypes.EIP712MsgAttrType{
|
||||
{Name: "sender", Type: "string"},
|
||||
{Name: "collateral", Type: "Coin"},
|
||||
{Name: "principal", Type: "Coin"},
|
||||
{Name: "collateral_type", Type: "string"},
|
||||
},
|
||||
},
|
||||
{
|
||||
MsgTypeUrl: "/kava.cdp.v1beta1.MsgDeposit",
|
||||
MsgValueTypeName: "MsgValueCDPDeposit",
|
||||
ValueTypes: []evmtypes.EIP712MsgAttrType{
|
||||
{Name: "depositor", Type: "string"},
|
||||
{Name: "owner", Type: "string"},
|
||||
{Name: "collateral", Type: "Coin"},
|
||||
{Name: "collateral_type", Type: "string"},
|
||||
},
|
||||
},
|
||||
{
|
||||
MsgTypeUrl: "/kava.hard.v1beta1.MsgDeposit",
|
||||
MsgValueTypeName: "MsgValueHardDeposit",
|
||||
ValueTypes: []evmtypes.EIP712MsgAttrType{
|
||||
{Name: "depositor", Type: "string"},
|
||||
{Name: "amount", Type: "Coin[]"},
|
||||
},
|
||||
},
|
||||
{
|
||||
MsgTypeUrl: "/kava.evmutil.v1beta1.MsgConvertCoinToERC20",
|
||||
MsgValueTypeName: "MsgValueEVMConvertCoinToERC20",
|
||||
ValueTypes: []evmtypes.EIP712MsgAttrType{
|
||||
{Name: "initiator", Type: "string"},
|
||||
{Name: "receiver", Type: "string"},
|
||||
{Name: "amount", Type: "Coin"},
|
||||
},
|
||||
},
|
||||
{
|
||||
MsgTypeUrl: "/kava.cdp.v1beta1.MsgRepayDebt",
|
||||
MsgValueTypeName: "MsgValueCDPRepayDebt",
|
||||
ValueTypes: []evmtypes.EIP712MsgAttrType{
|
||||
{Name: "sender", Type: "string"},
|
||||
{Name: "collateral_type", Type: "string"},
|
||||
{Name: "payment", Type: "Coin"},
|
||||
},
|
||||
},
|
||||
{
|
||||
MsgTypeUrl: "/kava.hard.v1beta1.MsgWithdraw",
|
||||
MsgValueTypeName: "MsgValueHardWithdraw",
|
||||
ValueTypes: []evmtypes.EIP712MsgAttrType{
|
||||
{Name: "depositor", Type: "string"},
|
||||
{Name: "amount", Type: "Coin[]"},
|
||||
},
|
||||
},
|
||||
}
|
||||
evmKeeper.SetParams(suite.ctx, params)
|
||||
|
||||
// give test address 50k erc20 usdc to begin with
|
||||
initBal := suite.getEVMAmount(50_000)
|
||||
err = suite.evmutilKeeper.MintERC20(
|
||||
ctx,
|
||||
pair.GetAddress(), // contractAddr
|
||||
suite.testEVMAddr, //receiver
|
||||
initBal.BigInt(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
err = suite.evmutilKeeper.MintERC20(
|
||||
ctx,
|
||||
pair.GetAddress(), // contractAddr
|
||||
suite.testEVMAddr2, //receiver
|
||||
initBal.BigInt(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// We need to commit so that the ethermint feemarket beginblock runs to set the minfee
|
||||
// feeMarketKeeper.GetBaseFee() will return nil otherwise
|
||||
suite.Commit()
|
||||
|
||||
// set base fee
|
||||
suite.tApp.GetFeeMarketKeeper().SetBaseFee(suite.ctx, big.NewInt(100))
|
||||
}
|
||||
|
||||
func (suite *EIP712TestSuite) Commit() {
|
||||
_ = suite.tApp.Commit()
|
||||
header := suite.ctx.BlockHeader()
|
||||
header.Height += 1
|
||||
suite.tApp.BeginBlock(abci.RequestBeginBlock{
|
||||
Header: header,
|
||||
})
|
||||
|
||||
// update ctx
|
||||
suite.ctx = suite.tApp.NewContext(false, header)
|
||||
}
|
||||
|
||||
func (suite *EIP712TestSuite) deployUSDCERC20(app app.TestApp, ctx sdk.Context) evmutiltypes.InternalEVMAddress {
|
||||
// make sure module account is created
|
||||
suite.tApp.FundModuleAccount(
|
||||
suite.ctx,
|
||||
evmutiltypes.ModuleName,
|
||||
sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(0))),
|
||||
)
|
||||
|
||||
contractAddr, err := suite.evmutilKeeper.DeployTestMintableERC20Contract(suite.ctx, "USDC", "USDC", uint8(18))
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Greater(len(contractAddr.Address), 0)
|
||||
return contractAddr
|
||||
}
|
||||
|
||||
func (suite *EIP712TestSuite) TestEIP712Tx() {
|
||||
encodingConfig := app.MakeEncodingConfig()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
usdcDepositAmt int64
|
||||
usdxToMintAmt int64
|
||||
updateTx func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder
|
||||
updateMsgs func(msgs []sdk.Msg) []sdk.Msg
|
||||
expectedCode uint32
|
||||
failCheckTx bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "processes deposit eip712 messages successfully",
|
||||
usdcDepositAmt: 100,
|
||||
usdxToMintAmt: 99,
|
||||
},
|
||||
{
|
||||
name: "fails when convertion more erc20 usdc than balance",
|
||||
usdcDepositAmt: 51_000,
|
||||
usdxToMintAmt: 100,
|
||||
errMsg: "transfer amount exceeds balance",
|
||||
},
|
||||
{
|
||||
name: "fails when minting more usdx than allowed",
|
||||
usdcDepositAmt: 100,
|
||||
usdxToMintAmt: 100,
|
||||
errMsg: "proposed collateral ratio is below liquidation ratio",
|
||||
},
|
||||
{
|
||||
name: "fails when trying to convert usdc for another address",
|
||||
usdcDepositAmt: 100,
|
||||
usdxToMintAmt: 90,
|
||||
errMsg: "unauthorized",
|
||||
failCheckTx: true,
|
||||
updateMsgs: func(msgs []sdk.Msg) []sdk.Msg {
|
||||
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
|
||||
suite.testEVMAddr2,
|
||||
suite.testAddr,
|
||||
suite.usdcEVMAddr,
|
||||
suite.getEVMAmount(100),
|
||||
)
|
||||
msgs[0] = &convertMsg
|
||||
return msgs
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fails when trying to convert erc20 for non-whitelisted contract",
|
||||
usdcDepositAmt: 100,
|
||||
usdxToMintAmt: 90,
|
||||
errMsg: "ERC20 token not enabled to convert to sdk.Coin",
|
||||
updateMsgs: func(msgs []sdk.Msg) []sdk.Msg {
|
||||
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
|
||||
suite.testEVMAddr,
|
||||
suite.testAddr,
|
||||
suite.testEVMAddr2,
|
||||
suite.getEVMAmount(100),
|
||||
)
|
||||
msgs[0] = &convertMsg
|
||||
return msgs
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fails when signer tries to send messages with invalid signature",
|
||||
usdcDepositAmt: 100,
|
||||
usdxToMintAmt: 90,
|
||||
failCheckTx: true,
|
||||
errMsg: "tx intended signer does not match the given signer",
|
||||
updateTx: func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder {
|
||||
var option *codectypes.Any
|
||||
option, _ = codectypes.NewAnyWithValue(ðerminttypes.ExtensionOptionsWeb3Tx{
|
||||
FeePayer: suite.testAddr.String(),
|
||||
TypedDataChainID: 1,
|
||||
FeePayerSig: []byte("sig"),
|
||||
})
|
||||
builder, _ := txBuilder.(authtx.ExtensionOptionsTxBuilder)
|
||||
builder.SetExtensionOptions(option)
|
||||
return txBuilder
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fails when insufficient gas fees are provided",
|
||||
usdcDepositAmt: 100,
|
||||
usdxToMintAmt: 90,
|
||||
errMsg: "insufficient funds",
|
||||
updateTx: func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder {
|
||||
bk := suite.tApp.GetBankKeeper()
|
||||
gasCoins := bk.GetBalance(suite.ctx, suite.testAddr, "ukava")
|
||||
suite.tApp.GetBankKeeper().SendCoins(suite.ctx, suite.testAddr, suite.testAddr2, sdk.NewCoins(gasCoins))
|
||||
return txBuilder
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fails when invalid chain id is provided",
|
||||
usdcDepositAmt: 100,
|
||||
usdxToMintAmt: 90,
|
||||
failCheckTx: true,
|
||||
errMsg: "invalid chain-id",
|
||||
updateTx: func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder {
|
||||
gasAmt := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(20)))
|
||||
return suite.createTestEIP712CosmosTxBuilder(
|
||||
suite.testAddr, suite.testPrivKey, "kavatest_12-1", uint64(helpers.DefaultGenTxGas*10), gasAmt, msgs,
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fails when invalid fee payer is provided",
|
||||
usdcDepositAmt: 100,
|
||||
usdxToMintAmt: 90,
|
||||
failCheckTx: true,
|
||||
errMsg: "invalid pubkey",
|
||||
updateTx: func(txBuilder client.TxBuilder, msgs []sdk.Msg) client.TxBuilder {
|
||||
gasAmt := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(20)))
|
||||
return suite.createTestEIP712CosmosTxBuilder(
|
||||
suite.testAddr2, suite.testPrivKey2, ChainID, uint64(helpers.DefaultGenTxGas*10), gasAmt, msgs,
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest()
|
||||
|
||||
// create messages to convert, mint, and deposit to lend
|
||||
usdcAmt := suite.getEVMAmount(tc.usdcDepositAmt)
|
||||
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
|
||||
suite.testEVMAddr,
|
||||
suite.testAddr,
|
||||
suite.usdcEVMAddr,
|
||||
usdcAmt,
|
||||
)
|
||||
usdxAmt := sdk.NewInt(1_000_000).Mul(sdk.NewInt(tc.usdxToMintAmt))
|
||||
mintMsg := cdptypes.NewMsgCreateCDP(
|
||||
suite.testAddr,
|
||||
sdk.NewCoin(USDCCoinDenom, usdcAmt),
|
||||
sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt),
|
||||
USDCCDPType,
|
||||
)
|
||||
lendMsg := hardtypes.NewMsgDeposit(
|
||||
suite.testAddr,
|
||||
sdk.NewCoins(sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt)),
|
||||
)
|
||||
msgs := []sdk.Msg{
|
||||
&convertMsg,
|
||||
&mintMsg,
|
||||
&lendMsg,
|
||||
}
|
||||
if tc.updateMsgs != nil {
|
||||
msgs = tc.updateMsgs(msgs)
|
||||
}
|
||||
|
||||
gasAmt := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(20)))
|
||||
txBuilder := suite.createTestEIP712CosmosTxBuilder(
|
||||
suite.testAddr, suite.testPrivKey, ChainID, uint64(helpers.DefaultGenTxGas*10), gasAmt, msgs,
|
||||
)
|
||||
if tc.updateTx != nil {
|
||||
txBuilder = tc.updateTx(txBuilder, msgs)
|
||||
}
|
||||
txBytes, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
resCheckTx := suite.tApp.CheckTx(
|
||||
abci.RequestCheckTx{
|
||||
Tx: txBytes,
|
||||
Type: abci.CheckTxType_New,
|
||||
},
|
||||
)
|
||||
if !tc.failCheckTx {
|
||||
suite.Require().Equal(resCheckTx.Code, uint32(0), resCheckTx.Log)
|
||||
} else {
|
||||
suite.Require().NotEqual(resCheckTx.Code, uint32(0), resCheckTx.Log)
|
||||
suite.Require().Contains(resCheckTx.Log, tc.errMsg)
|
||||
}
|
||||
|
||||
resDeliverTx := suite.tApp.DeliverTx(
|
||||
abci.RequestDeliverTx{
|
||||
Tx: txBytes,
|
||||
},
|
||||
)
|
||||
|
||||
if tc.errMsg == "" {
|
||||
suite.Require().Equal(resDeliverTx.Code, uint32(0), resDeliverTx.Log)
|
||||
|
||||
// validate user cosmos erc20/usd balance
|
||||
bk := suite.tApp.GetBankKeeper()
|
||||
amt := bk.GetBalance(suite.ctx, suite.testAddr, USDCCoinDenom)
|
||||
suite.Require().Equal(sdk.ZeroInt(), amt.Amount)
|
||||
|
||||
// validate cdp
|
||||
cdp, found := suite.tApp.GetCDPKeeper().GetCdpByOwnerAndCollateralType(suite.ctx, suite.testAddr, USDCCDPType)
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(suite.testAddr, cdp.Owner)
|
||||
suite.Require().Equal(sdk.NewCoin(USDCCoinDenom, suite.getEVMAmount(100)), cdp.Collateral)
|
||||
suite.Require().Equal(sdk.NewCoin("usdx", sdk.NewInt(99_000_000)), cdp.Principal)
|
||||
|
||||
// validate hard
|
||||
hardDeposit, found := suite.tApp.GetHardKeeper().GetDeposit(suite.ctx, suite.testAddr)
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(suite.testAddr, hardDeposit.Depositor)
|
||||
suite.Require().Equal(sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(99_000_000))), hardDeposit.Amount)
|
||||
} else {
|
||||
suite.Require().NotEqual(resDeliverTx.Code, uint32(0), resCheckTx.Log)
|
||||
suite.Require().Contains(resDeliverTx.Log, tc.errMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *EIP712TestSuite) TestEIP712Tx_DepositAndWithdraw() {
|
||||
encodingConfig := app.MakeEncodingConfig()
|
||||
|
||||
// create deposit msgs
|
||||
usdcAmt := suite.getEVMAmount(100)
|
||||
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
|
||||
suite.testEVMAddr,
|
||||
suite.testAddr,
|
||||
suite.usdcEVMAddr,
|
||||
usdcAmt,
|
||||
)
|
||||
usdxAmt := sdk.NewInt(1_000_000).Mul(sdk.NewInt(99))
|
||||
mintMsg := cdptypes.NewMsgCreateCDP(
|
||||
suite.testAddr,
|
||||
sdk.NewCoin(USDCCoinDenom, usdcAmt),
|
||||
sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt),
|
||||
USDCCDPType,
|
||||
)
|
||||
lendMsg := hardtypes.NewMsgDeposit(
|
||||
suite.testAddr,
|
||||
sdk.NewCoins(sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt)),
|
||||
)
|
||||
depositMsgs := []sdk.Msg{
|
||||
&convertMsg,
|
||||
&mintMsg,
|
||||
&lendMsg,
|
||||
}
|
||||
|
||||
// deliver deposit msg
|
||||
gasAmt := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(20)))
|
||||
txBuilder := suite.createTestEIP712CosmosTxBuilder(
|
||||
suite.testAddr, suite.testPrivKey, ChainID, uint64(helpers.DefaultGenTxGas*10), gasAmt, depositMsgs,
|
||||
)
|
||||
txBytes, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
|
||||
suite.Require().NoError(err)
|
||||
resDeliverTx := suite.tApp.DeliverTx(
|
||||
abci.RequestDeliverTx{
|
||||
Tx: txBytes,
|
||||
},
|
||||
)
|
||||
suite.Require().Equal(resDeliverTx.Code, uint32(0), resDeliverTx.Log)
|
||||
|
||||
// validate hard
|
||||
hardDeposit, found := suite.tApp.GetHardKeeper().GetDeposit(suite.ctx, suite.testAddr)
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(suite.testAddr, hardDeposit.Depositor)
|
||||
suite.Require().Equal(sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(99_000_000))), hardDeposit.Amount)
|
||||
|
||||
// validate erc20 balance
|
||||
coinBal, err := suite.evmutilKeeper.QueryERC20BalanceOf(suite.ctx, suite.usdcEVMAddr, suite.testEVMAddr)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(suite.getEVMAmount(49_900).BigInt(), coinBal)
|
||||
|
||||
// withdraw msgs
|
||||
withdrawConvertMsg := evmutiltypes.NewMsgConvertCoinToERC20(
|
||||
suite.testAddr.String(),
|
||||
suite.testEVMAddr.String(),
|
||||
sdk.NewCoin(USDCCoinDenom, usdcAmt),
|
||||
)
|
||||
cdpWithdrawMsg := cdptypes.NewMsgRepayDebt(
|
||||
suite.testAddr,
|
||||
USDCCDPType,
|
||||
sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt),
|
||||
)
|
||||
hardWithdrawMsg := hardtypes.NewMsgWithdraw(
|
||||
suite.testAddr,
|
||||
sdk.NewCoins(sdk.NewCoin(cdptypes.DefaultStableDenom, usdxAmt)),
|
||||
)
|
||||
withdrawMsgs := []sdk.Msg{
|
||||
&hardWithdrawMsg,
|
||||
&cdpWithdrawMsg,
|
||||
&withdrawConvertMsg,
|
||||
}
|
||||
|
||||
// deliver withdraw msg
|
||||
txBuilder = suite.createTestEIP712CosmosTxBuilder(
|
||||
suite.testAddr, suite.testPrivKey, ChainID, uint64(helpers.DefaultGenTxGas*10), gasAmt, withdrawMsgs,
|
||||
)
|
||||
txBytes, err = encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
|
||||
suite.Require().NoError(err)
|
||||
resDeliverTx = suite.tApp.DeliverTx(
|
||||
abci.RequestDeliverTx{
|
||||
Tx: txBytes,
|
||||
},
|
||||
)
|
||||
suite.Require().Equal(resDeliverTx.Code, uint32(0), resDeliverTx.Log)
|
||||
|
||||
// validate hard & cdp should be repayed
|
||||
_, found = suite.tApp.GetHardKeeper().GetDeposit(suite.ctx, suite.testAddr)
|
||||
suite.Require().False(found)
|
||||
_, found = suite.tApp.GetCDPKeeper().GetCdpByOwnerAndCollateralType(suite.ctx, suite.testAddr, USDCCDPType)
|
||||
suite.Require().False(found)
|
||||
|
||||
// validate user cosmos erc20/usd balance
|
||||
bk := suite.tApp.GetBankKeeper()
|
||||
amt := bk.GetBalance(suite.ctx, suite.testAddr, USDCCoinDenom)
|
||||
suite.Require().Equal(sdk.ZeroInt(), amt.Amount)
|
||||
|
||||
// validate erc20 balance
|
||||
coinBal, err = suite.evmutilKeeper.QueryERC20BalanceOf(suite.ctx, suite.usdcEVMAddr, suite.testEVMAddr)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(suite.getEVMAmount(50_000).BigInt(), coinBal)
|
||||
}
|
||||
|
||||
func TestEIP712Suite(t *testing.T) {
|
||||
suite.Run(t, new(EIP712TestSuite))
|
||||
}
|
@ -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],
|
||||
|
@ -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.
|
||||
|
@ -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
6
go.mod
@ -12,7 +12,7 @@ require (
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/kava-labs/kava-bridge v0.2.0
|
||||
github.com/kava-labs/kava-bridge v0.2.1-0.20220707160130-6199d30c5fe6
|
||||
github.com/spf13/cast v1.4.1
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/stretchr/testify v1.7.1
|
||||
@ -158,7 +158,7 @@ replace (
|
||||
// Use the cosmos keyring code
|
||||
github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76
|
||||
// Use cosmos-sdk fork with backported fix for unsafe-reset-all
|
||||
github.com/cosmos/cosmos-sdk => github.com/kava-labs/cosmos-sdk v0.45.4-kava.1
|
||||
github.com/cosmos/cosmos-sdk => github.com/kava-labs/cosmos-sdk v0.45.4-kava.3
|
||||
// See https://github.com/cosmos/cosmos-sdk/pull/10401, https://github.com/cosmos/cosmos-sdk/commit/0592ba6158cd0bf49d894be1cef4faeec59e8320
|
||||
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0
|
||||
// Use the cosmos modified protobufs
|
||||
@ -166,7 +166,7 @@ replace (
|
||||
// Use rocksdb 7.1.2
|
||||
github.com/tendermint/tm-db => github.com/kava-labs/tm-db v0.6.7-kava.1
|
||||
// Use ethermint fork that respects min-gas-price with NoBaseFee true and london enabled
|
||||
github.com/tharsis/ethermint => github.com/Kava-Labs/ethermint v0.14.0-kava-v17.3
|
||||
github.com/tharsis/ethermint => github.com/Kava-Labs/ethermint v0.14.0-kava-v17.5
|
||||
// Make sure that we use grpc compatible with cosmos
|
||||
google.golang.org/grpc => google.golang.org/grpc v1.33.2
|
||||
)
|
||||
|
44
go.sum
44
go.sum
@ -1,4 +1,5 @@
|
||||
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
|
||||
bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
@ -100,14 +101,9 @@ github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t
|
||||
github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/Kava-Labs/ethermint v0.14.0-kava-v17.3 h1:4+Lg+qmJAL8ey9ns85WqxQkYCCKvN8RyuoxQ7CHJRPU=
|
||||
github.com/Kava-Labs/ethermint v0.14.0-kava-v17.3/go.mod h1:k6rs7PRm04GhQZhCmwYLJGQ5czvgbZqcrqpR630+Rik=
|
||||
github.com/Kava-Labs/ethermint v0.14.0-kava-v17.5 h1:oQH7YQ5MBExEbFQZ95UMdXimYLYHAc9su2yIsJ9+ISs=
|
||||
github.com/Kava-Labs/ethermint v0.14.0-kava-v17.5/go.mod h1:/x9PysLnuLxE1/33pHf8W+TQJFJh7XE49g05uJoQ7kw=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||
@ -135,6 +131,7 @@ github.com/adlio/schema v1.3.0 h1:eSVYLxYWbm/6ReZBCkLw4Fz7uqC+ZNoPvA39bOwi52A=
|
||||
github.com/adlio/schema v1.3.0/go.mod h1:51QzxkpeFs6lRY11kPye26IaFPOV+HqEj01t5aXXKfs=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@ -147,8 +144,6 @@ github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2uc
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
|
||||
github.com/aokoli/goutils v1.1.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
|
||||
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
@ -186,6 +181,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/binance-chain/tss-lib v1.3.3/go.mod h1:xfM6gCPA61WIV5q5tK9Acdv46n1QJLhXnZ4eD17hJpI=
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
|
||||
@ -195,6 +191,7 @@ github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnC
|
||||
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
|
||||
github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
|
||||
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
|
||||
github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo=
|
||||
@ -313,6 +310,7 @@ github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6Uh
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
|
||||
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
|
||||
github.com/decred/dcrd/dcrec/edwards/v2 v2.0.0/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
|
||||
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
|
||||
@ -363,9 +361,7 @@ github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
||||
github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM=
|
||||
github.com/ethereum/go-ethereum v1.10.4/go.mod h1:nEE0TP5MtxGzOMd7egIrbPJMQBnhVU3ELNxhBglIzhg=
|
||||
github.com/ethereum/go-ethereum v1.10.16 h1:3oPrumn0bCW/idjcxMn5YYVCdK7VzJYIvwGZUGLEaoc=
|
||||
@ -487,7 +483,6 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@ -682,8 +677,6 @@ github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25
|
||||
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
|
||||
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
||||
github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
|
||||
@ -694,8 +687,6 @@ github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU=
|
||||
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
|
||||
github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
|
||||
@ -775,10 +766,10 @@ github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F
|
||||
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
|
||||
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||
github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||
github.com/kava-labs/cosmos-sdk v0.45.4-kava.1 h1:nGL6b6eracc8+FGWbJ/NexFGorI8j6DBMwZ6KvEqteA=
|
||||
github.com/kava-labs/cosmos-sdk v0.45.4-kava.1/go.mod h1:WOqtDxN3eCCmnYLVla10xG7lEXkFjpTaqm2a2WasgCc=
|
||||
github.com/kava-labs/kava-bridge v0.2.0 h1:5t9AsPUy9Riwi+gRLgozhv0dut/xQcvRF+yIB+wLuo8=
|
||||
github.com/kava-labs/kava-bridge v0.2.0/go.mod h1:APqFV9V/rmkm3MnJ27R/CGOjteCX+3mVuRTYE70v7Yw=
|
||||
github.com/kava-labs/cosmos-sdk v0.45.4-kava.3 h1:U4esIl4rzu9sApLFYGwbccPQTQdRg6C9exUVrokqSHY=
|
||||
github.com/kava-labs/cosmos-sdk v0.45.4-kava.3/go.mod h1:WOqtDxN3eCCmnYLVla10xG7lEXkFjpTaqm2a2WasgCc=
|
||||
github.com/kava-labs/kava-bridge v0.2.1-0.20220707160130-6199d30c5fe6 h1:uhdPatSr1VLKvVWTaphkielTlEWF/MvTwmp3uCHgjPo=
|
||||
github.com/kava-labs/kava-bridge v0.2.1-0.20220707160130-6199d30c5fe6/go.mod h1:PNkTAuIw+PhMjlNr2EKPo2AUMcGNwcUIcJHaMhf+qAM=
|
||||
github.com/kava-labs/tm-db v0.6.7-kava.1 h1:7cVYlvWx1yP+gGdaAWcfm6NwMLzf4z6DxXguWn3+O3w=
|
||||
github.com/kava-labs/tm-db v0.6.7-kava.1/go.mod h1:HVZfZzWXuqWseXQVplxsWXK6kLHLkk3kQB6c+nuSZvk=
|
||||
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM=
|
||||
@ -918,7 +909,6 @@ github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T
|
||||
github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
@ -995,7 +985,6 @@ github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
@ -1010,7 +999,6 @@ github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGg
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
|
||||
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
@ -1063,8 +1051,6 @@ github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo=
|
||||
github.com/mwitkow/go-proto-validators v0.3.2/go.mod h1:ej0Qp0qMgHN/KtDyUt+Q1/tA7a5VarXUOUxD+oeD30w=
|
||||
github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
@ -1145,8 +1131,10 @@ github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ=
|
||||
github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/otiai10/primes v0.0.0-20180210170552-f6d2a1ba97c4/go.mod h1:UmSP7QeU3XmAdGu5+dnrTJqjBc+IscpVZkQzk473cjM=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
@ -1236,9 +1224,6 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/pseudomuto/protoc-gen-doc v1.5.1/go.mod h1:XpMKYg6zkcpgfpCfQ8GcWBDRtRxOmMR5w7pz4Xo+dYM=
|
||||
github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q=
|
||||
github.com/pseudomuto/protokit v0.2.1/go.mod h1:gt7N5Rz2flBzYafvaxyIxMZC0TTF5jDZfRnw25hAAyo=
|
||||
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
|
||||
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
|
||||
github.com/raulk/clock v1.1.0/go.mod h1:3MpVxdZ/ODBQDxbN+kzshf5OSZwPjtMDx6BBXBmOeY0=
|
||||
@ -1375,7 +1360,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@ -1973,7 +1957,6 @@ google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E=
|
||||
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
|
||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||
google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
|
||||
@ -1990,7 +1973,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
|
@ -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
|
||||
},
|
||||
}
|
||||
|
@ -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 = ""
|
@ -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
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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).
|
@ -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)
|
||||
}
|
@ -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
|
||||
```
|
2327
migrate/v0_17/testdata/genesis-v16.json
vendored
2327
migrate/v0_17/testdata/genesis-v16.json
vendored
File diff suppressed because it is too large
Load Diff
2366
migrate/v0_17/testdata/genesis-v17.json
vendored
2366
migrate/v0_17/testdata/genesis-v17.json
vendored
File diff suppressed because it is too large
Load Diff
20
proto/kava/evmutil/v1beta1/conversion_pair.proto
Normal file
20
proto/kava/evmutil/v1beta1/conversion_pair.proto
Normal 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;
|
||||
}
|
@ -3,19 +3,24 @@ package kava.evmutil.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "kava/evmutil/v1beta1/conversion_pair.proto";
|
||||
|
||||
option go_package = "github.com/kava-labs/kava/x/evmutil/types";
|
||||
option go_package = "github.com/kava-labs/kava/x/evmutil/types";
|
||||
option (gogoproto.equal_all) = true;
|
||||
option (gogoproto.verbose_equal_all) = true;
|
||||
|
||||
// GenesisState defines the evmutil module's genesis state.
|
||||
message GenesisState {
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
repeated Account accounts = 1 [(gogoproto.nullable) = false];
|
||||
|
||||
// params defines all the parameters of the module.
|
||||
Params params = 2 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// BalanceAccount defines an account in the evmutil module.
|
||||
message Account {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
bytes address = 1 [
|
||||
@ -30,3 +35,11 @@ message Account {
|
||||
(gogoproto.nullable) = false
|
||||
];
|
||||
}
|
||||
|
||||
// Params defines the evmutil module params
|
||||
message Params {
|
||||
// enabled_conversion_pairs defines the list of conversion pairs allowed to be
|
||||
// converted between Kava ERC20 and sdk.Coin
|
||||
repeated ConversionPair enabled_conversion_pairs = 4
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "ConversionPairs"];
|
||||
}
|
||||
|
26
proto/kava/evmutil/v1beta1/query.proto
Normal file
26
proto/kava/evmutil/v1beta1/query.proto
Normal 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];
|
||||
}
|
52
proto/kava/evmutil/v1beta1/tx.proto
Normal file
52
proto/kava/evmutil/v1beta1/tx.proto
Normal 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 {}
|
73
x/evmutil/client/cli/address.go
Normal file
73
x/evmutil/client/cli/address.go
Normal 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
129
x/evmutil/client/cli/evm.go
Normal 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
|
||||
ðtypes.AccessList{},
|
||||
)
|
||||
|
||||
// Must set from address before signing
|
||||
ethTx.From = from.String()
|
||||
|
||||
// Sign Ethereum TX (not the cosmos Msg)
|
||||
signer := ethtypes.LatestSignerForChainID(chainID)
|
||||
|
||||
// Must sign with a `/ethermint.crypto.v1.ethsecp256k1.PubKey` and not
|
||||
// `/cosmos.crypto.secp256k1.PubKey` or this will panic with the following:
|
||||
// panic: wrong size for signature: got 64, want 65
|
||||
if err := ethTx.Sign(signer, ctx.Keyring); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ethTx.BuildTx(ctx.TxConfig.NewTxBuilder(), evmParamsRes.Params.EvmDenom)
|
||||
}
|
63
x/evmutil/client/cli/query.go
Normal file
63
x/evmutil/client/cli/query.go
Normal 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
122
x/evmutil/client/cli/tx.go
Normal 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)
|
||||
},
|
||||
}
|
||||
}
|
86
x/evmutil/client/cli/tx_util.go
Normal file
86
x/evmutil/client/cli/tx_util.go
Normal 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
|
||||
}
|
@ -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))
|
||||
}
|
||||
|
@ -20,9 +20,12 @@ func (suite *genesisTestSuite) SetupTest() {
|
||||
}
|
||||
|
||||
func (s *genesisTestSuite) TestInitGenesis_SetAccounts() {
|
||||
gs := types.NewGenesisState([]types.Account{
|
||||
{Address: s.Addrs[0], Balance: sdk.NewInt(100)},
|
||||
})
|
||||
gs := types.NewGenesisState(
|
||||
[]types.Account{
|
||||
{Address: s.Addrs[0], Balance: sdk.NewInt(100)},
|
||||
},
|
||||
types.DefaultParams(),
|
||||
)
|
||||
accounts := s.Keeper.GetAllAccounts(s.Ctx)
|
||||
s.Require().Len(accounts, 0)
|
||||
evmutil.InitGenesis(s.Ctx, s.Keeper, gs)
|
||||
@ -33,10 +36,30 @@ func (s *genesisTestSuite) TestInitGenesis_SetAccounts() {
|
||||
s.Require().Equal(account.Balance, sdk.NewInt(100))
|
||||
}
|
||||
|
||||
func (s *genesisTestSuite) TestInitGenesis_SetParams() {
|
||||
params := types.DefaultParams()
|
||||
conversionPair := types.ConversionPair{
|
||||
KavaERC20Address: testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").Bytes(),
|
||||
Denom: "weth",
|
||||
}
|
||||
params.EnabledConversionPairs = []types.ConversionPair{conversionPair}
|
||||
gs := types.NewGenesisState(
|
||||
[]types.Account{},
|
||||
params,
|
||||
)
|
||||
evmutil.InitGenesis(s.Ctx, s.Keeper, gs)
|
||||
params = s.Keeper.GetParams(s.Ctx)
|
||||
s.Require().Len(params.EnabledConversionPairs, 1)
|
||||
s.Require().Equal(conversionPair, params.EnabledConversionPairs[0])
|
||||
}
|
||||
|
||||
func (s *genesisTestSuite) TestInitGenesis_ValidateFail() {
|
||||
gs := types.NewGenesisState([]types.Account{
|
||||
{Address: s.Addrs[0], Balance: sdk.NewInt(-100)},
|
||||
})
|
||||
gs := types.NewGenesisState(
|
||||
[]types.Account{
|
||||
{Address: s.Addrs[0], Balance: sdk.NewInt(-100)},
|
||||
},
|
||||
types.DefaultParams(),
|
||||
)
|
||||
s.Require().Panics(func() {
|
||||
evmutil.InitGenesis(s.Ctx, s.Keeper, gs)
|
||||
})
|
||||
@ -50,8 +73,16 @@ func (s *genesisTestSuite) TestExportGenesis() {
|
||||
for _, account := range accounts {
|
||||
s.Keeper.SetAccount(s.Ctx, account)
|
||||
}
|
||||
params := types.DefaultParams()
|
||||
params.EnabledConversionPairs = []types.ConversionPair{
|
||||
{
|
||||
KavaERC20Address: testutil.MustNewInternalEVMAddressFromString("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").Bytes(),
|
||||
Denom: "weth"},
|
||||
}
|
||||
s.Keeper.SetParams(s.Ctx, params)
|
||||
gs := evmutil.ExportGenesis(s.Ctx, s.Keeper)
|
||||
s.Require().Equal(gs.Accounts, accounts)
|
||||
s.Require().Equal(params, gs.Params)
|
||||
}
|
||||
|
||||
func TestGenesisTestSuite(t *testing.T) {
|
||||
|
223
x/evmutil/keeper/conversion.go
Normal file
223
x/evmutil/keeper/conversion.go
Normal 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
|
||||
}
|
345
x/evmutil/keeper/conversion_test.go
Normal file
345
x/evmutil/keeper/conversion_test.go
Normal 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
144
x/evmutil/keeper/erc20.go
Normal 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
|
||||
}
|
86
x/evmutil/keeper/erc20_test.go
Normal file
86
x/evmutil/keeper/erc20_test.go
Normal 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
152
x/evmutil/keeper/evm.go
Normal 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
|
||||
}
|
35
x/evmutil/keeper/grpc_query.go
Normal file
35
x/evmutil/keeper/grpc_query.go
Normal 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
|
||||
}
|
37
x/evmutil/keeper/grpc_query_test.go
Normal file
37
x/evmutil/keeper/grpc_query_test.go
Normal 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)
|
||||
}
|
@ -11,6 +11,8 @@ import (
|
||||
func RegisterInvariants(ir sdk.InvariantRegistry, bankK types.BankKeeper, k Keeper) {
|
||||
ir.RegisterRoute(types.ModuleName, "fully-backed", FullyBackedInvariant(bankK, k))
|
||||
ir.RegisterRoute(types.ModuleName, "small-balances", SmallBalancesInvariant(bankK, k))
|
||||
// Disable this invariant due to some issues with it requiring some staking params to be set in genesis.
|
||||
// ir.RegisterRoute(types.ModuleName, "backed-conversion-coins", BackedCoinsInvariant(bankK, k))
|
||||
}
|
||||
|
||||
// AllInvariants runs all invariants of the swap module
|
||||
@ -19,8 +21,10 @@ func AllInvariants(bankK types.BankKeeper, k Keeper) sdk.Invariant {
|
||||
if res, stop := FullyBackedInvariant(bankK, k)(ctx); stop {
|
||||
return res, stop
|
||||
}
|
||||
res, stop := SmallBalancesInvariant(bankK, k)(ctx)
|
||||
return res, stop
|
||||
if res, stop := BackedCoinsInvariant(bankK, k)(ctx); stop {
|
||||
return res, stop
|
||||
}
|
||||
return SmallBalancesInvariant(bankK, k)(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,3 +68,40 @@ func SmallBalancesInvariant(_ types.BankKeeper, k Keeper) sdk.Invariant {
|
||||
return message, broken
|
||||
}
|
||||
}
|
||||
|
||||
// BackedCoinsInvariant iterates all conversion pairs and asserts that the
|
||||
// sdk.Coin balances are less than the module ERC20 balance.
|
||||
// **Note:** This compares <= and not == as anyone can send tokens to the
|
||||
// ERC20 contract address and break the invariant if a strict equal check.
|
||||
func BackedCoinsInvariant(_ types.BankKeeper, k Keeper) sdk.Invariant {
|
||||
broken := false
|
||||
message := sdk.FormatInvariant(
|
||||
types.ModuleName,
|
||||
"backed coins broken",
|
||||
"coin supply is greater than module account ERC20 tokens",
|
||||
)
|
||||
|
||||
return func(ctx sdk.Context) (string, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, pair := range params.EnabledConversionPairs {
|
||||
erc20Balance, err := k.QueryERC20BalanceOf(
|
||||
ctx,
|
||||
pair.GetAddress(),
|
||||
types.NewInternalEVMAddress(types.ModuleEVMAddress),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
supply := k.bankKeeper.GetSupply(ctx, pair.Denom)
|
||||
|
||||
// Must be true: sdk.Coin supply < ERC20 balanceOf(module account)
|
||||
if supply.Amount.BigInt().Cmp(erc20Balance) > 0 {
|
||||
broken = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return message, broken
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -14,7 +15,8 @@ import (
|
||||
|
||||
type invariantTestSuite struct {
|
||||
testutil.Suite
|
||||
invariants map[string]map[string]sdk.Invariant
|
||||
invariants map[string]map[string]sdk.Invariant
|
||||
contractAddr types.InternalEVMAddress
|
||||
}
|
||||
|
||||
func TestInvariantTestSuite(t *testing.T) {
|
||||
@ -23,6 +25,9 @@ func TestInvariantTestSuite(t *testing.T) {
|
||||
|
||||
func (suite *invariantTestSuite) SetupTest() {
|
||||
suite.Suite.SetupTest()
|
||||
|
||||
suite.contractAddr = suite.DeployERC20()
|
||||
|
||||
suite.invariants = make(map[string]map[string]sdk.Invariant)
|
||||
keeper.RegisterInvariants(suite, suite.BankKeeper, suite.Keeper)
|
||||
}
|
||||
@ -40,6 +45,23 @@ func (suite *invariantTestSuite) SetupValidState() {
|
||||
sdk.NewCoin("ukava", sdk.NewInt(2)), // ( sum of all minor balances ) / conversion multiplier
|
||||
),
|
||||
)
|
||||
|
||||
err := suite.Keeper.MintERC20(suite.Ctx, suite.contractAddr, suite.Key1Addr, big.NewInt(1000000))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// key1 ERC20 bal -10000, sdk.Coin +1000
|
||||
// Module account balance 0 -> 1000
|
||||
_, err = suite.Keeper.CallEVM(
|
||||
suite.Ctx,
|
||||
types.ERC20MintableBurnableContract.ABI,
|
||||
suite.Key1Addr.Address,
|
||||
suite.contractAddr,
|
||||
"convertToCoin",
|
||||
// convertToCoin ERC20 args
|
||||
suite.Key1Addr.Address,
|
||||
big.NewInt(1000),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
// RegisterRoutes implements sdk.InvariantRegistry
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/evmutil/types"
|
||||
)
|
||||
@ -13,16 +14,37 @@ import (
|
||||
// Keeper of the evmutil store.
|
||||
// This keeper stores additional data related to evm accounts.
|
||||
type Keeper struct {
|
||||
cdc codec.Codec
|
||||
storeKey sdk.StoreKey
|
||||
cdc codec.Codec
|
||||
storeKey sdk.StoreKey
|
||||
paramSubspace paramtypes.Subspace
|
||||
bankKeeper types.BankKeeper
|
||||
evmKeeper types.EvmKeeper
|
||||
accountKeeper types.AccountKeeper
|
||||
}
|
||||
|
||||
// NewKeeper creates an evmutil keeper.
|
||||
func NewKeeper(cdc codec.Codec, storeKey sdk.StoreKey) Keeper {
|
||||
return Keeper{
|
||||
cdc: cdc,
|
||||
storeKey: storeKey,
|
||||
func NewKeeper(
|
||||
cdc codec.Codec,
|
||||
storeKey sdk.StoreKey,
|
||||
params paramtypes.Subspace,
|
||||
bk types.BankKeeper,
|
||||
ak types.AccountKeeper,
|
||||
) Keeper {
|
||||
if !params.HasKeyTable() {
|
||||
params = params.WithKeyTable(types.ParamKeyTable())
|
||||
}
|
||||
|
||||
return Keeper{
|
||||
cdc: cdc,
|
||||
storeKey: storeKey,
|
||||
paramSubspace: params,
|
||||
bankKeeper: bk,
|
||||
accountKeeper: ak,
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Keeper) SetEvmKeeper(evmKeeper types.EvmKeeper) {
|
||||
k.evmKeeper = evmKeeper
|
||||
}
|
||||
|
||||
// GetAllAccounts returns all accounts.
|
||||
|
104
x/evmutil/keeper/msg_server.go
Normal file
104
x/evmutil/keeper/msg_server.go
Normal 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
|
||||
}
|
266
x/evmutil/keeper/msg_server_test.go
Normal file
266
x/evmutil/keeper/msg_server_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
51
x/evmutil/keeper/params.go
Normal file
51
x/evmutil/keeper/params.go
Normal 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, ¶ms)
|
||||
return params
|
||||
}
|
||||
|
||||
// SetParams sets the evm parameters to the param space.
|
||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||
k.paramSubspace.SetParamSet(ctx, ¶ms)
|
||||
}
|
||||
|
||||
// GetEnabledConversionPairFromERC20Address returns an ConversionPair from the internal contract address.
|
||||
func (k Keeper) GetEnabledConversionPairFromERC20Address(
|
||||
ctx sdk.Context,
|
||||
address types.InternalEVMAddress,
|
||||
) (types.ConversionPair, error) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, pair := range params.EnabledConversionPairs {
|
||||
if bytes.Equal(pair.KavaERC20Address, address.Bytes()) {
|
||||
return pair, nil
|
||||
}
|
||||
}
|
||||
|
||||
return types.ConversionPair{}, sdkerrors.Wrap(types.ErrConversionNotEnabled, address.String())
|
||||
}
|
||||
|
||||
// GetEnabledConversionPairFromDenom returns an ConversionPair from the sdk.Coin denom.
|
||||
func (k Keeper) GetEnabledConversionPairFromDenom(
|
||||
ctx sdk.Context,
|
||||
denom string,
|
||||
) (types.ConversionPair, error) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, pair := range params.EnabledConversionPairs {
|
||||
if pair.Denom == denom {
|
||||
return pair, nil
|
||||
}
|
||||
}
|
||||
|
||||
return types.ConversionPair{}, sdkerrors.Wrap(types.ErrConversionNotEnabled, denom)
|
||||
}
|
36
x/evmutil/keeper/params_test.go
Normal file
36
x/evmutil/keeper/params_test.go
Normal 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)
|
||||
}
|
@ -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) {
|
||||
|
@ -1,29 +1,63 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
|
||||
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
||||
"github.com/tharsis/ethermint/server/config"
|
||||
etherminttests "github.com/tharsis/ethermint/tests"
|
||||
etherminttypes "github.com/tharsis/ethermint/types"
|
||||
evmtypes "github.com/tharsis/ethermint/x/evm/types"
|
||||
feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/evmutil/keeper"
|
||||
"github.com/kava-labs/kava/x/evmutil/types"
|
||||
)
|
||||
|
||||
type Suite struct {
|
||||
suite.Suite
|
||||
|
||||
App app.TestApp
|
||||
Ctx sdk.Context
|
||||
BankKeeper bankkeeper.Keeper
|
||||
AccountKeeper authkeeper.AccountKeeper
|
||||
Keeper keeper.Keeper
|
||||
EvmBankKeeper keeper.EvmBankKeeper
|
||||
Addrs []sdk.AccAddress
|
||||
EvmModuleAddr sdk.AccAddress
|
||||
App app.TestApp
|
||||
Ctx sdk.Context
|
||||
Address common.Address
|
||||
BankKeeper bankkeeper.Keeper
|
||||
AccountKeeper authkeeper.AccountKeeper
|
||||
Keeper keeper.Keeper
|
||||
EvmBankKeeper keeper.EvmBankKeeper
|
||||
Addrs []sdk.AccAddress
|
||||
EvmModuleAddr sdk.AccAddress
|
||||
QueryClient types.QueryClient
|
||||
QueryClientEvm evmtypes.QueryClient
|
||||
Key1 *ethsecp256k1.PrivKey
|
||||
Key1Addr types.InternalEVMAddress
|
||||
Key2 *ethsecp256k1.PrivKey
|
||||
}
|
||||
|
||||
func (suite *Suite) SetupTest() {
|
||||
@ -37,13 +71,114 @@ func (suite *Suite) SetupTest() {
|
||||
suite.EvmBankKeeper = keeper.NewEvmBankKeeper(tApp.GetEvmutilKeeper(), suite.BankKeeper, suite.AccountKeeper)
|
||||
suite.EvmModuleAddr = suite.AccountKeeper.GetModuleAddress(evmtypes.ModuleName)
|
||||
|
||||
// test evm user keys that have no minting permissions
|
||||
key1, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
suite.Key1 = key1
|
||||
suite.Key1Addr = types.NewInternalEVMAddress(common.BytesToAddress(suite.Key1.PubKey().Address()))
|
||||
suite.Key2, err = ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(4)
|
||||
suite.Addrs = addrs
|
||||
|
||||
evmGenesis := evmtypes.DefaultGenesisState()
|
||||
evmGenesis.Params.EvmDenom = "akava"
|
||||
gs := app.GenesisState{evmtypes.ModuleName: suite.App.AppCodec().MustMarshalJSON(evmGenesis)}
|
||||
suite.App.InitializeFromGenesisStates(gs)
|
||||
|
||||
feemarketGenesis := feemarkettypes.DefaultGenesisState()
|
||||
feemarketGenesis.Params.EnableHeight = 1
|
||||
feemarketGenesis.Params.NoBaseFee = false
|
||||
|
||||
cdc := suite.App.AppCodec()
|
||||
coins := sdk.NewCoins(sdk.NewInt64Coin("ukava", 1000_000_000_000_000_000))
|
||||
authGS := app.NewFundedGenStateWithSameCoins(cdc, coins, []sdk.AccAddress{
|
||||
sdk.AccAddress(suite.Key1.PubKey().Address()),
|
||||
sdk.AccAddress(suite.Key2.PubKey().Address()),
|
||||
})
|
||||
|
||||
gs := app.GenesisState{
|
||||
evmtypes.ModuleName: cdc.MustMarshalJSON(evmGenesis),
|
||||
feemarkettypes.ModuleName: cdc.MustMarshalJSON(feemarketGenesis),
|
||||
}
|
||||
suite.App.InitializeFromGenesisStates(authGS, gs)
|
||||
|
||||
// consensus key - needed to set up evm module
|
||||
consPriv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
consAddress := sdk.ConsAddress(consPriv.PubKey().Address())
|
||||
|
||||
// InitializeFromGenesisStates commits first block so we start at 2 here
|
||||
suite.Ctx = suite.App.NewContext(false, tmproto.Header{
|
||||
Height: suite.App.LastBlockHeight() + 1,
|
||||
ChainID: "kavatest_1-1",
|
||||
Time: time.Now().UTC(),
|
||||
ProposerAddress: consAddress.Bytes(),
|
||||
Version: tmversion.Consensus{
|
||||
Block: version.BlockProtocol,
|
||||
},
|
||||
LastBlockId: tmproto.BlockID{
|
||||
Hash: tmhash.Sum([]byte("block_id")),
|
||||
PartSetHeader: tmproto.PartSetHeader{
|
||||
Total: 11,
|
||||
Hash: tmhash.Sum([]byte("partset_header")),
|
||||
},
|
||||
},
|
||||
AppHash: tmhash.Sum([]byte("app")),
|
||||
DataHash: tmhash.Sum([]byte("data")),
|
||||
EvidenceHash: tmhash.Sum([]byte("evidence")),
|
||||
ValidatorsHash: tmhash.Sum([]byte("validators")),
|
||||
NextValidatorsHash: tmhash.Sum([]byte("next_validators")),
|
||||
ConsensusHash: tmhash.Sum([]byte("consensus")),
|
||||
LastResultsHash: tmhash.Sum([]byte("last_result")),
|
||||
})
|
||||
|
||||
// We need to set the validator as calling the EVM looks up the validator address
|
||||
// https://github.com/tharsis/ethermint/blob/f21592ebfe74da7590eb42ed926dae970b2a9a3f/x/evm/keeper/state_transition.go#L487
|
||||
// evmkeeper.EVMConfig() will return error "failed to load evm config" if not set
|
||||
acc := ðerminttypes.EthAccount{
|
||||
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.Address.Bytes()), nil, 0, 0),
|
||||
CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(),
|
||||
}
|
||||
suite.AccountKeeper.SetAccount(suite.Ctx, acc)
|
||||
valAddr := sdk.ValAddress(suite.Address.Bytes())
|
||||
validator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{})
|
||||
suite.Require().NoError(err)
|
||||
err = suite.App.GetStakingKeeper().SetValidatorByConsAddr(suite.Ctx, validator)
|
||||
suite.Require().NoError(err)
|
||||
suite.App.GetStakingKeeper().SetValidator(suite.Ctx, validator)
|
||||
|
||||
// add conversion pair for first module deployed contract to evmutil params
|
||||
suite.Keeper.SetParams(suite.Ctx, types.NewParams(
|
||||
types.NewConversionPairs(
|
||||
types.NewConversionPair(
|
||||
// First contract this module deploys
|
||||
MustNewInternalEVMAddressFromString("0x15932E26f5BD4923d46a2b205191C4b5d5f43FE3"),
|
||||
"erc20/usdc",
|
||||
),
|
||||
),
|
||||
))
|
||||
|
||||
queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry())
|
||||
evmtypes.RegisterQueryServer(queryHelper, suite.App.GetEvmKeeper())
|
||||
suite.QueryClientEvm = evmtypes.NewQueryClient(queryHelper)
|
||||
types.RegisterQueryServer(queryHelper, keeper.NewQueryServerImpl(suite.App.GetEvmutilKeeper()))
|
||||
suite.QueryClient = types.NewQueryClient(queryHelper)
|
||||
|
||||
// We need to commit so that the ethermint feemarket beginblock runs to set the minfee
|
||||
// feeMarketKeeper.GetBaseFee() will return nil otherwise
|
||||
suite.Commit()
|
||||
}
|
||||
|
||||
func (suite *Suite) Commit() {
|
||||
_ = suite.App.Commit()
|
||||
header := suite.Ctx.BlockHeader()
|
||||
header.Height += 1
|
||||
suite.App.BeginBlock(abci.RequestBeginBlock{
|
||||
Header: header,
|
||||
})
|
||||
|
||||
// update ctx
|
||||
suite.Ctx = suite.App.NewContext(false, header)
|
||||
}
|
||||
|
||||
func (suite *Suite) FundAccountWithKava(addr sdk.AccAddress, coins sdk.Coins) {
|
||||
@ -72,3 +207,206 @@ func (suite *Suite) FundModuleAccountWithKava(moduleName string, coins sdk.Coins
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *Suite) DeployERC20() types.InternalEVMAddress {
|
||||
// make sure module account is created
|
||||
// qq: any better ways to do this?
|
||||
suite.App.FundModuleAccount(
|
||||
suite.Ctx,
|
||||
types.ModuleName,
|
||||
sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(0))),
|
||||
)
|
||||
|
||||
contractAddr, err := suite.Keeper.DeployTestMintableERC20Contract(suite.Ctx, "USDC", "USDC", uint8(18))
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Greater(len(contractAddr.Address), 0)
|
||||
return contractAddr
|
||||
}
|
||||
|
||||
func (suite *Suite) GetERC20BalanceOf(
|
||||
contractAbi abi.ABI,
|
||||
contractAddr types.InternalEVMAddress,
|
||||
accountAddr types.InternalEVMAddress,
|
||||
) *big.Int {
|
||||
// Query ERC20.balanceOf()
|
||||
addr := common.BytesToAddress(suite.Key1.PubKey().Address())
|
||||
res, err := suite.QueryContract(
|
||||
types.ERC20MintableBurnableContract.ABI,
|
||||
addr,
|
||||
suite.Key1,
|
||||
contractAddr,
|
||||
"balanceOf",
|
||||
accountAddr.Address,
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Len(res, 1)
|
||||
|
||||
balance, ok := res[0].(*big.Int)
|
||||
suite.Require().True(ok, "balanceOf should respond with *big.Int")
|
||||
return balance
|
||||
}
|
||||
|
||||
func (suite *Suite) QueryContract(
|
||||
contractAbi abi.ABI,
|
||||
from common.Address,
|
||||
fromKey *ethsecp256k1.PrivKey,
|
||||
contract types.InternalEVMAddress,
|
||||
method string,
|
||||
args ...interface{},
|
||||
) ([]interface{}, error) {
|
||||
// Pack query args
|
||||
data, err := contractAbi.Pack(method, args...)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Send TX
|
||||
res, err := suite.SendTx(contract, from, fromKey, data)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Check for VM errors and unpack returned data
|
||||
switch res.VmError {
|
||||
case vm.ErrExecutionReverted.Error():
|
||||
response, err := abi.UnpackRevert(res.Ret)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
return nil, errors.New(response)
|
||||
case "": // No error, continue
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled vm error response: %v", res.VmError))
|
||||
}
|
||||
|
||||
// Unpack response
|
||||
unpackedRes, err := contractAbi.Unpack(method, res.Ret)
|
||||
suite.Require().NoErrorf(err, "failed to unpack method %v response", method)
|
||||
|
||||
return unpackedRes, nil
|
||||
}
|
||||
|
||||
// SendTx submits a transaction to the block.
|
||||
func (suite *Suite) SendTx(
|
||||
contractAddr types.InternalEVMAddress,
|
||||
from common.Address,
|
||||
signerKey *ethsecp256k1.PrivKey,
|
||||
transferData []byte,
|
||||
) (*evmtypes.MsgEthereumTxResponse, error) {
|
||||
ctx := sdk.WrapSDKContext(suite.Ctx)
|
||||
chainID := suite.App.GetEvmKeeper().ChainID()
|
||||
|
||||
args, err := json.Marshal(&evmtypes.TransactionArgs{
|
||||
To: &contractAddr.Address,
|
||||
From: &from,
|
||||
Data: (*hexutil.Bytes)(&transferData),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gasRes, err := suite.QueryClientEvm.EstimateGas(ctx, &evmtypes.EthCallRequest{
|
||||
Args: args,
|
||||
GasCap: config.DefaultGasCap,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := suite.App.GetEvmKeeper().GetNonce(suite.Ctx, suite.Address)
|
||||
|
||||
baseFee := suite.App.GetFeeMarketKeeper().GetBaseFee(suite.Ctx)
|
||||
suite.Require().NotNil(baseFee, "base fee is nil")
|
||||
|
||||
// Mint the max gas to the FeeCollector to ensure balance in case of refund
|
||||
suite.MintFeeCollector(sdk.NewCoins(
|
||||
sdk.NewCoin(
|
||||
"ukava",
|
||||
sdk.NewInt(baseFee.Int64()*int64(gasRes.Gas*2)),
|
||||
)))
|
||||
|
||||
ercTransferTx := evmtypes.NewTx(
|
||||
chainID,
|
||||
nonce,
|
||||
&contractAddr.Address,
|
||||
nil, // amount
|
||||
gasRes.Gas*2, // gasLimit, TODO: runs out of gas with just res.Gas, ex: estimated was 21572 but used 24814
|
||||
nil, // gasPrice
|
||||
suite.App.GetFeeMarketKeeper().GetBaseFee(suite.Ctx), // gasFeeCap
|
||||
big.NewInt(1), // gasTipCap
|
||||
transferData,
|
||||
ðtypes.AccessList{}, // accesses
|
||||
)
|
||||
|
||||
ercTransferTx.From = hex.EncodeToString(signerKey.PubKey().Address())
|
||||
err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), etherminttests.NewSigner(signerKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rsp, err := suite.App.GetEvmKeeper().EthereumTx(ctx, ercTransferTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Do not check vm error here since we want to check for errors later
|
||||
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func (suite *Suite) MintFeeCollector(coins sdk.Coins) {
|
||||
err := suite.App.GetBankKeeper().MintCoins(suite.Ctx, minttypes.ModuleName, coins)
|
||||
suite.Require().NoError(err)
|
||||
err = suite.App.GetBankKeeper().SendCoinsFromModuleToModule(
|
||||
suite.Ctx,
|
||||
minttypes.ModuleName,
|
||||
authtypes.FeeCollectorName,
|
||||
coins,
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
// GetEvents returns emitted events on the sdk context
|
||||
func (suite *Suite) GetEvents() sdk.Events {
|
||||
return suite.Ctx.EventManager().Events()
|
||||
}
|
||||
|
||||
// EventsContains asserts that the expected event is in the provided events
|
||||
func (suite *Suite) EventsContains(events sdk.Events, expectedEvent sdk.Event) {
|
||||
foundMatch := false
|
||||
for _, event := range events {
|
||||
if event.Type == expectedEvent.Type {
|
||||
if reflect.DeepEqual(attrsToMap(expectedEvent.Attributes), attrsToMap(event.Attributes)) {
|
||||
foundMatch = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suite.Truef(foundMatch, "event of type %s not found or did not match", expectedEvent.Type)
|
||||
}
|
||||
|
||||
// EventsDoNotContain asserts that the event is **not** is in the provided events
|
||||
func (suite *Suite) EventsDoNotContain(events sdk.Events, eventType string) {
|
||||
foundMatch := false
|
||||
for _, event := range events {
|
||||
if event.Type == eventType {
|
||||
foundMatch = true
|
||||
}
|
||||
}
|
||||
|
||||
suite.Falsef(foundMatch, "event of type %s should not be found, but was found", eventType)
|
||||
}
|
||||
|
||||
func attrsToMap(attrs []abci.EventAttribute) []sdk.Attribute {
|
||||
out := []sdk.Attribute{}
|
||||
|
||||
for _, attr := range attrs {
|
||||
out = append(out, sdk.NewAttribute(string(attr.Key), string(attr.Value)))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// MustNewInternalEVMAddressFromString returns a new InternalEVMAddress from a
|
||||
// hex string. This will panic if the input hex string is invalid.
|
||||
func MustNewInternalEVMAddressFromString(addrStr string) types.InternalEVMAddress {
|
||||
addr, err := types.NewInternalEVMAddressFromString(addrStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
33
x/evmutil/types/address.go
Normal file
33
x/evmutil/types/address.go
Normal 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
25
x/evmutil/types/bytes.go
Normal 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)
|
||||
}
|
32
x/evmutil/types/bytes_test.go
Normal file
32
x/evmutil/types/bytes_test.go
Normal 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
36
x/evmutil/types/codec.go
Normal 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()
|
||||
}
|
48
x/evmutil/types/contract.go
Normal file
48
x/evmutil/types/contract.go
Normal 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")
|
||||
}
|
||||
}
|
89
x/evmutil/types/conversion_pair.go
Normal file
89
x/evmutil/types/conversion_pair.go
Normal 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()
|
||||
}
|
428
x/evmutil/types/conversion_pair.pb.go
Normal file
428
x/evmutil/types/conversion_pair.pb.go
Normal 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")
|
||||
)
|
233
x/evmutil/types/conversion_pairs_test.go
Normal file
233
x/evmutil/types/conversion_pairs_test.go
Normal 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
14
x/evmutil/types/errors.go
Normal 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
18
x/evmutil/types/events.go
Normal 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"
|
||||
)
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
146
x/evmutil/types/msg.go
Normal 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
199
x/evmutil/types/msg_test.go
Normal 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
48
x/evmutil/types/params.go
Normal 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
|
||||
}
|
75
x/evmutil/types/params_test.go
Normal file
75
x/evmutil/types/params_test.go
Normal 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, ¶ms)
|
||||
suite.Require().NoError(err)
|
||||
_, ok := params["enabled_conversion_pairs"]
|
||||
suite.Require().True(ok, "enabled_conversion_pairs should exist in yaml")
|
||||
}
|
||||
|
||||
func (suite *ParamsTestSuite) TestParamSetPairs_EnabledConversionPairs() {
|
||||
suite.Require().Equal([]byte("EnabledConversionPairs"), types.KeyEnabledConversionPairs)
|
||||
defaultParams := types.DefaultParams()
|
||||
|
||||
var paramSetPair *paramstypes.ParamSetPair
|
||||
for _, pair := range defaultParams.ParamSetPairs() {
|
||||
if bytes.Equal(pair.Key, types.KeyEnabledConversionPairs) {
|
||||
paramSetPair = &pair
|
||||
break
|
||||
}
|
||||
}
|
||||
suite.Require().NotNil(paramSetPair)
|
||||
|
||||
pairs, ok := paramSetPair.Value.(*types.ConversionPairs)
|
||||
suite.Require().True(ok)
|
||||
suite.Require().Equal(pairs, &defaultParams.EnabledConversionPairs)
|
||||
|
||||
suite.Require().Nil(paramSetPair.ValidatorFn(*pairs))
|
||||
suite.Require().EqualError(paramSetPair.ValidatorFn(struct{}{}), "invalid parameter type: struct {}")
|
||||
}
|
||||
|
||||
func TestParamsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ParamsTestSuite))
|
||||
}
|
638
x/evmutil/types/query.pb.go
Normal file
638
x/evmutil/types/query.pb.go
Normal 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")
|
||||
)
|
148
x/evmutil/types/query.pb.gw.go
Normal file
148
x/evmutil/types/query.pb.gw.go
Normal 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
1378
x/evmutil/types/tx.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user