package testenv import ( "encoding/json" "strings" "time" // helpers // tendermint "cosmossdk.io/log" sdkmath "cosmossdk.io/math" // adminmodule adminmoduletypes "github.com/cosmos/admin-module/v2/x/adminmodule/types" // cometbft abci "github.com/cometbft/cometbft/abci/types" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" tmtypes "github.com/cometbft/cometbft/types" dbm "github.com/cosmos/cosmos-db" // cosmos sdk "github.com/cosmos/cosmos-sdk/baseapp" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" // interchain security ccvconsumertypes "github.com/cosmos/interchain-security/v5/x/ccv/consumer/types" ccvtypes "github.com/cosmos/interchain-security/v5/x/ccv/types" // wasmd wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" // neutron "github.com/neutron-org/neutron/v5/app" dexmoduletypes "github.com/neutron-org/neutron/v5/x/dex/types" tokenfactorytypes "github.com/neutron-org/neutron/v5/x/tokenfactory/types" // slinky compression "github.com/skip-mev/slinky/abci/strategies/codec" "github.com/skip-mev/slinky/abci/testutils" slinkytypes "github.com/skip-mev/slinky/pkg/types" marketmaptypes "github.com/skip-mev/slinky/x/marketmap/types" oraclekeeper "github.com/skip-mev/slinky/x/oracle/keeper" oracletypes "github.com/skip-mev/slinky/x/oracle/types" ) type TestEnv struct { App *app.App Ctx sdk.Context ParamTypesRegistry ParamTypeRegistry ValPrivs secp256k1.PrivKey Validator []byte NodeHome string } type DebugAppOptions map[string]interface{} func (m DebugAppOptions) Get(key string) interface{} { v, ok := m[key] if !ok { return nil } return v } func NewDebugAppOptionsWithFlagHome() servertypes.AppOptions { return DebugAppOptions{ server.FlagTrace: true, } } func NewNeutronApp(nodeHome string) *app.App { db := dbm.NewMemDB() encCfg := app.MakeEncodingConfig() var emptyWasmOpts []wasmkeeper.Option return app.New( log.NewNopLogger(), db, nil, true, map[int64]bool{}, nodeHome, 0, encCfg, NewDebugAppOptionsWithFlagHome(), emptyWasmOpts, baseapp.SetChainID("neutron-666"), ) } func InitChain(appInstance *app.App) (sdk.Context, secp256k1.PrivKey) { sdk.DefaultBondDenom = "untrn" genesisState, valPriv := GenesisStateWithValSet(appInstance) encCfg := app.MakeEncodingConfig() // Set up Wasm genesis state wasmGen := wasmtypes.GenesisState{ Params: wasmtypes.Params{ // Allow store code without gov CodeUploadAccess: wasmtypes.AllowEverybody, InstantiateDefaultPermission: wasmtypes.AccessTypeEverybody, }, } genesisState[wasmtypes.ModuleName] = encCfg.Marshaler.MustMarshalJSON(&wasmGen) // set staking genesis state stakingGenesisState := stakingtypes.GenesisState{} appInstance.AppCodec().UnmarshalJSON(genesisState[stakingtypes.ModuleName], &stakingGenesisState) stateBytes, err := json.MarshalIndent(genesisState, "", " ") requireNoErr(err) consensusParams := simtestutil.DefaultConsensusParams consensusParams.Block = &tmproto.BlockParams{ MaxBytes: 22020096, MaxGas: -1, } consensusParams.Abci = &tmproto.ABCIParams{ VoteExtensionsEnableHeight: 2, } // replace sdk.DefaultDenom with "untrn", a bit of a hack, needs improvement stateBytes = []byte(strings.Replace(string(stateBytes), "\"stake\"", "\"untrn\"", -1)) appInstance.InitChain( &abci.RequestInitChain{ Validators: []abci.ValidatorUpdate{}, ConsensusParams: consensusParams, AppStateBytes: stateBytes, ChainId: "neutron-666", }, ) ctx := appInstance.NewUncachedContext(false, tmproto.Header{Height: 0, ChainID: "neutron-666", Time: time.Now().UTC()}) // for each stakingGenesisState.Validators for _, validator := range stakingGenesisState.Validators { consAddr, err := validator.GetConsAddr() requireNoErr(err) signingInfo := slashingtypes.NewValidatorSigningInfo( consAddr, ctx.BlockHeight(), 0, time.Unix(0, 0), false, 0, ) appInstance.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, signingInfo) } return ctx, valPriv } func GenesisStateWithValSet(appInstance *app.App) (app.GenesisState, secp256k1.PrivKey) { privVal := NewPV() pubKey, _ := privVal.GetPubKey() validator := tmtypes.NewValidator(pubKey, 1) valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) valAcc := authtypes.NewBaseAccountWithAddress(pubKey.Address().Bytes()) // generate genesis account senderPrivKey := secp256k1.GenPrivKey() senderPrivKey.PubKey().Address() acc := authtypes.NewBaseAccountWithAddress(senderPrivKey.PubKey().Address().Bytes()) ////////////////////// validatorBalance := banktypes.Balance{ Address: valAcc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin("untrn", sdkmath.NewInt(1000000000000000000))), } balances := []banktypes.Balance{validatorBalance} genesisState := app.NewDefaultGenesisState(appInstance.AppCodec()) genAccs := []authtypes.GenesisAccount{acc, valAcc} authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) genesisState[authtypes.ModuleName] = appInstance.AppCodec().MustMarshalJSON(authGenesis) // set adminmodule genesis state adminGen := adminmoduletypes.GenesisState{ Admins: []string{valAcc.Address}, } genesisState[adminmoduletypes.ModuleName] = appInstance.AppCodec().MustMarshalJSON(&adminGen) // set marketmap genesis state marketmapGen := marketmaptypes.GenesisState{ Params: marketmaptypes.Params{ MarketAuthorities: []string{valAcc.Address}, Admin: valAcc.Address, }, } genesisState[marketmaptypes.ModuleName] = appInstance.AppCodec().MustMarshalJSON(&marketmapGen) // set oracle genesis state oracleGen := oracletypes.GenesisState{ CurrencyPairGenesis: []oracletypes.CurrencyPairGenesis{ { CurrencyPair: slinkytypes.CurrencyPair{ Base: "ATOM", Quote: "USDT", }, CurrencyPairPrice: &oracletypes.QuotePrice{Price: sdkmath.NewInt(4480000)}, Nonce: 0, Id: 0, }, }, NextId: 1, } genesisState[oracletypes.ModuleName] = appInstance.AppCodec().MustMarshalJSON(&oracleGen) validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) bondAmt := sdk.DefaultPowerReduction initValPowers := []abci.ValidatorUpdate{} for _, val := range valSet.Validators { pk, _ := cryptocodec.FromCmtPubKeyInterface(val.PubKey) pkAny, _ := codectypes.NewAnyWithValue(pk) validator := stakingtypes.Validator{ OperatorAddress: sdk.ValAddress(val.Address).String(), ConsensusPubkey: pkAny, Jailed: false, Status: stakingtypes.Bonded, Tokens: bondAmt, DelegatorShares: sdkmath.LegacyOneDec(), Description: stakingtypes.Description{}, UnbondingHeight: int64(0), UnbondingTime: time.Unix(0, 0).UTC(), Commission: stakingtypes.NewCommission(sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec()), MinSelfDelegation: sdkmath.ZeroInt(), } validators = append(validators, validator) delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].String(), val.Address.String(), sdkmath.LegacyOneDec())) // add initial validator powers so consumer InitGenesis runs correctly pub, _ := val.ToProto() initValPowers = append(initValPowers, abci.ValidatorUpdate{ Power: val.VotingPower, PubKey: pub.PubKey, }) } // set validators and delegations stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) genesisState[stakingtypes.ModuleName] = appInstance.AppCodec().MustMarshalJSON(stakingGenesis) // initialValset := []abci.ValidatorUpdate{{PubKey: tmProtoPublicKey, Power: 100}} ccvGenesis := ccvconsumertypes.DefaultGenesisState() ccvGenesis.Params.Enabled = true ccvGenesis.PreCCV = false ccvGenesis.Provider = ccvtypes.ProviderInfo{ InitialValSet: initValPowers, } genesisState[ccvconsumertypes.ModuleName] = appInstance.AppCodec().MustMarshalJSON(ccvGenesis) totalSupply := sdk.NewCoins() for _, b := range balances { // add genesis acc tokens to total supply totalSupply = totalSupply.Add(b.Coins...) } for range delegations { // add delegated tokens to total supply totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) } // add bonded amount to bonded pool module account balances = append(balances, banktypes.Balance{ Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, }) // update total supply bankGenesis := banktypes.NewGenesisState( banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}, []banktypes.SendEnabled{}, ) genesisState[banktypes.ModuleName] = appInstance.AppCodec().MustMarshalJSON(bankGenesis) _, err := tmtypes.PB2TM.ValidatorUpdates(initValPowers) if err != nil { panic("failed to get vals") } return genesisState, secp256k1.PrivKey{Key: privVal.PrivKey.Bytes()} } func CreateExtendedVoteInfo(val secp256k1.PrivKey, prices map[uint64][]byte) []byte { ca := sdk.ConsAddress(val.PubKey().Address()) // Create the vote extensions handler that will be used to extend and verify // vote extensions (i.e. oracle data). veCodec := compression.NewCompressionVoteExtensionCodec( compression.NewDefaultVoteExtensionCodec(), compression.NewZLibCompressor(), ) extCommitCodec := compression.NewCompressionExtendedCommitCodec( compression.NewDefaultExtendedCommitCodec(), compression.NewZStdCompressor(), ) vote, err := testutils.CreateExtendedVoteInfo( ca, prices, veCodec, ) requireNoErr(err) _, extCommitInfoBz, err := testutils.CreateExtendedCommitInfo( []abci.ExtendedVoteInfo{vote}, extCommitCodec, ) requireNoErr(err) return extCommitInfoBz } func (env *TestEnv) GetValidatorPrivateKey() []byte { return env.Validator } func (env *TestEnv) SetDefaultValidator(consAddr sdk.ConsAddress) { signingInfo := slashingtypes.NewValidatorSigningInfo( consAddr, env.Ctx.BlockHeight(), 0, time.Unix(0, 0), false, 0, ) env.App.SlashingKeeper.SetValidatorSigningInfo(env.Ctx, consAddr, signingInfo) } func (env *TestEnv) FundAccount(ctx sdk.Context, bankKeeper bankkeeper.Keeper, addr sdk.AccAddress, amounts sdk.Coins) error { if err := bankKeeper.MintCoins(ctx, dexmoduletypes.ModuleName, amounts); err != nil { return err } return bankKeeper.SendCoinsFromModuleToAccount(ctx, dexmoduletypes.ModuleName, addr, amounts) } func GetCurrentPriceAndPairMapping(ctx sdk.Context, oracle oraclekeeper.Keeper, base, quote string) (sdkmath.Int, uint64, error) { ccyPair := slinkytypes.CurrencyPair{ Base: base, Quote: quote, } pairs, err := oracle.GetCurrencyPairMapping(ctx) if err != nil { return sdkmath.ZeroInt(), 0, err } pairIndex := uint64(0) for idx, pair := range pairs { if pair.Base == base && pair.Quote == quote { pairIndex = idx } } res, err := oracle.GetPriceForCurrencyPair(ctx, ccyPair) if err != nil { return sdkmath.ZeroInt(), pairIndex, nil } return res.Price, pairIndex, nil } func (env *TestEnv) SetupParamTypes() { pReg := env.ParamTypesRegistry pReg.RegisterParamSet(&tokenfactorytypes.Params{}) } func requireNoErr(err error) { if err != nil { panic(err) } }