Skip to content

Commit

Permalink
Merge pull request #65 from b-harvest/patch/simulation
Browse files Browse the repository at this point in the history
feat. simulation codes for canto modules
  • Loading branch information
poorphd authored Jul 26, 2024
2 parents 4923125 + 1c5079b commit 471c41a
Show file tree
Hide file tree
Showing 54 changed files with 3,337 additions and 302 deletions.
8 changes: 4 additions & 4 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -758,12 +758,12 @@ func NewCanto(
feemarket.NewAppModule(app.FeeMarketKeeper, feeMarketSs),

// Canto app modules
inflation.NewAppModule(app.InflationKeeper, app.AccountKeeper, *app.StakingKeeper),
erc20.NewAppModule(app.Erc20Keeper, app.AccountKeeper, app.AccountKeeper.AddressCodec()),
inflation.NewAppModule(appCodec, app.InflationKeeper, app.AccountKeeper, *app.StakingKeeper),
erc20.NewAppModule(appCodec, app.Erc20Keeper, app.AccountKeeper, app.BankKeeper, app.EvmKeeper, app.FeeMarketKeeper, app.AccountKeeper.AddressCodec()),
epochs.NewAppModule(appCodec, app.EpochsKeeper),
onboarding.NewAppModule(*app.OnboardingKeeper),
govshuttle.NewAppModule(app.GovshuttleKeeper, app.AccountKeeper, app.AccountKeeper.AddressCodec()),
csr.NewAppModule(app.CSRKeeper, app.AccountKeeper),
govshuttle.NewAppModule(appCodec, app.GovshuttleKeeper, app.AccountKeeper, app.AccountKeeper.AddressCodec()),
csr.NewAppModule(appCodec, app.CSRKeeper, app.AccountKeeper),
coinswap.NewAppModule(appCodec, app.CoinswapKeeper, app.AccountKeeper, app.BankKeeper),
)

Expand Down
16 changes: 16 additions & 0 deletions app/params/weights.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package params

const (
DefaultWeightRegisterCoinProposal int = 5
DefaultWeightRegisterERC20Proposal int = 5
DefaultWeightToggleTokenConversionProposal int = 5
DefaultWeightLendingMarketProposal int = 5
DefaultWeightTreasuryProposal int = 5

DefaultWeightMsgConvertCoin int = 20
DefaultWeightMsgConvertErc20 int = 20

DefaultWeightMsgSwapOrder int = 50
DefaultWeightMsgAddLiquidity int = 10
DefaultWeightMsgRemoveLiquidity int = 10
)
15 changes: 10 additions & 5 deletions x/coinswap/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"math/rand"

"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -40,6 +39,11 @@ type AppModuleBasic struct {
cdc codec.Codec
}

// NewAppModuleBasic return a new AppModuleBasic
func NewAppModuleBasic(cdc codec.Codec) AppModuleBasic {
return AppModuleBasic{cdc: cdc}
}

// Name returns the coinswap module's name.
func (AppModuleBasic) Name() string { return types.ModuleName }

Expand Down Expand Up @@ -100,7 +104,7 @@ type AppModule struct {
// NewAppModule creates a new AppModule object
func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{cdc: cdc},
AppModuleBasic: NewAppModuleBasic(cdc),
keeper: keeper,
accountKeeper: accountKeeper,
bankKeeper: bankKeeper,
Expand Down Expand Up @@ -157,13 +161,14 @@ func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.We
return nil
}

// RandomizedParams creates randomized coinswap param changes for the simulator.
func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.LegacyParamChange {
return simulation.ParamChanges(r)
// ProposalMsgs returns msgs used for governance proposals for simulations.
func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs()
}

// RegisterStoreDecoder registers a decoder for coinswap module's types
func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc)
}

// WeightedOperations returns the all the coinswap module operations with their respective weights.
Expand Down
37 changes: 34 additions & 3 deletions x/coinswap/simulation/decoder.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
package simulation

import (
"bytes"
"fmt"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/kv"

"github.com/Canto-Network/Canto/v7/x/coinswap/types"
)

// DecodeStore unmarshals the KVPair's Value to the corresponding htlc type
func DecodeStore(kvA, kvB kv.Pair) string {
return ""
// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's
// Value to the corresponding farming type.
func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string {
return func(kvA, kvB kv.Pair) string {
switch {
case bytes.Equal(kvA.Key[:], []byte(types.KeyPool)):
var pA, pB types.Pool
cdc.MustUnmarshal(kvA.Value, &pA)
cdc.MustUnmarshal(kvB.Value, &pB)
return fmt.Sprintf("%v\n%v", pA, pB)

case bytes.Equal(kvA.Key[:], []byte(types.KeyNextPoolSequence)):
var seqA, seqB uint64
seqA = sdk.BigEndianToUint64(kvA.Value)
seqB = sdk.BigEndianToUint64(kvB.Value)
return fmt.Sprintf("%v\n%v", seqA, seqB)

case bytes.Equal(kvA.Key[:], []byte(types.KeyPoolLptDenom)):
var pA, pB types.Pool
cdc.MustUnmarshal(kvA.Value, &pA)
cdc.MustUnmarshal(kvB.Value, &pB)
return fmt.Sprintf("%v\n%v", pA, pB)

default:
panic(fmt.Sprintf("invalid coinswap key prefix %X", kvA.Key[:1]))
}
}
}
61 changes: 61 additions & 0 deletions x/coinswap/simulation/decoder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package simulation_test

import (
"fmt"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"

module "github.com/Canto-Network/Canto/v7/x/coinswap"
"github.com/Canto-Network/Canto/v7/x/coinswap/simulation"
"github.com/Canto-Network/Canto/v7/x/coinswap/types"
"github.com/cosmos/cosmos-sdk/types/kv"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
)

func TestCoinSwapStore(t *testing.T) {
encodingConfig := moduletestutil.MakeTestEncodingConfig(module.AppModuleBasic{})
cdc := encodingConfig.Codec
dec := simulation.NewDecodeStore(cdc)

pool := types.Pool{
Id: types.GetPoolId("denom1"),
StandardDenom: "denom2",
CounterpartyDenom: "denom1",
EscrowAddress: types.GetReservePoolAddr("lptDenom").String(),
LptDenom: "lptDenom",
}

sequence := uint64(1)

kvPairs := kv.Pairs{
Pairs: []kv.Pair{
{Key: []byte(types.KeyPool), Value: cdc.MustMarshal(&pool)},
{Key: []byte(types.KeyPoolLptDenom), Value: cdc.MustMarshal(&pool)},
{Key: []byte(types.KeyNextPoolSequence), Value: sdk.Uint64ToBigEndian(sequence)},
{Key: []byte{0x99}, Value: []byte{0x99}},
},
}

tests := []struct {
name string
expectedLog string
}{
{"Pool", fmt.Sprintf("%v\n%v", pool, pool)},
{"PoolLptDenom", fmt.Sprintf("%v\n%v", pool, pool)},
{"NextPoolSequence", fmt.Sprintf("%v\n%v", sequence, sequence)},
{"other", ""},
}
for i, tt := range tests {
i, tt := i, tt
t.Run(tt.name, func(t *testing.T) {
switch i {
case len(tests) - 1:
require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name)
default:
require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name)
}
})
}
}
75 changes: 74 additions & 1 deletion x/coinswap/simulation/genesis.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,81 @@
package simulation

import (
"encoding/json"
"fmt"
"math/rand"

sdkmath "cosmossdk.io/math"
"github.com/Canto-Network/Canto/v7/x/coinswap/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
)

// simulation parameter constants
const (
fee = "fee"
poolCreationFee = "pool_creation_fee"
taxRate = "tax_rate"
maxStandardCoinPerPool = "max_standard_coin_per_pool"
maxSwapAmount = "max_swap_amount"
)

func generateRandomFee(r *rand.Rand) sdkmath.LegacyDec {
return sdkmath.LegacyNewDecWithPrec(int64(simtypes.RandIntBetween(r, 0, 10)), 3)
}

func generateRandomPoolCreationFee(r *rand.Rand) sdk.Coin {
return sdk.NewInt64Coin(sdk.DefaultBondDenom, int64(simtypes.RandIntBetween(r, 0, 1000000)))
}

func generateRandomTaxRate(r *rand.Rand) sdkmath.LegacyDec {
return sdkmath.LegacyNewDecWithPrec(int64(simtypes.RandIntBetween(r, 0, 10)), 3)
}

func generateRandomMaxStandardCoinPerPool(r *rand.Rand) sdkmath.Int {
return sdkmath.NewIntWithDecimal(int64(simtypes.RandIntBetween(r, 0, 10000)), 18)
}

func generateRandomMaxSwapAmount(r *rand.Rand) sdk.Coins {
return sdk.NewCoins(
sdk.NewCoin(types.UsdcIBCDenom, sdkmath.NewIntWithDecimal(int64(simtypes.RandIntBetween(r, 1, 100)), 6)),
sdk.NewCoin(types.UsdtIBCDenom, sdkmath.NewIntWithDecimal(int64(simtypes.RandIntBetween(r, 1, 100)), 6)),
sdk.NewCoin(types.EthIBCDenom, sdkmath.NewIntWithDecimal(int64(simtypes.RandIntBetween(r, 1, 100)), 16)),
)
}

// RandomizedGenState generates a random GenesisState for coinswap
func RandomizedGenState(simState *module.SimulationState) {}
func RandomizedGenState(simState *module.SimulationState) {
genesis := types.DefaultGenesisState()

simState.AppParams.GetOrGenerate(
fee, &genesis.Params.Fee, simState.Rand,
func(r *rand.Rand) { genesis.Params.Fee = generateRandomFee(r) },
)

simState.AppParams.GetOrGenerate(
poolCreationFee, &genesis.Params.PoolCreationFee, simState.Rand,
func(r *rand.Rand) { genesis.Params.PoolCreationFee = generateRandomPoolCreationFee(r) },
)

simState.AppParams.GetOrGenerate(
taxRate, &genesis.Params.TaxRate, simState.Rand,
func(r *rand.Rand) { genesis.Params.TaxRate = generateRandomTaxRate(r) },
)

simState.AppParams.GetOrGenerate(
maxStandardCoinPerPool, &genesis.Params.MaxStandardCoinPerPool, simState.Rand,
func(r *rand.Rand) { genesis.Params.MaxStandardCoinPerPool = generateRandomMaxStandardCoinPerPool(r) },
)

simState.AppParams.GetOrGenerate(
maxSwapAmount, &genesis.Params.MaxSwapAmount, simState.Rand,
func(r *rand.Rand) { genesis.Params.MaxSwapAmount = generateRandomMaxSwapAmount(r) },
)

bz, _ := json.MarshalIndent(&genesis, "", " ")
fmt.Printf("Selected randomly generated coinswap parameters:\n%s\n", bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesis)

}
81 changes: 81 additions & 0 deletions x/coinswap/simulation/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package simulation_test

import (
"encoding/json"
"math/rand"
"testing"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"

"github.com/Canto-Network/Canto/v7/x/coinswap/simulation"
"github.com/Canto-Network/Canto/v7/x/coinswap/types"
)

func TestRandomizedGenState(t *testing.T) {
interfaceRegistry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)

s := rand.NewSource(2)
r := rand.New(s)

simState := module.SimulationState{
AppParams: make(simtypes.AppParams),
Cdc: cdc,
Rand: r,
NumBonded: 3,
Accounts: simtypes.RandomAccounts(r, 3),
InitialStake: math.NewInt(1000),
GenState: make(map[string]json.RawMessage),
}

simulation.RandomizedGenState(&simState)

var genState types.GenesisState
simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &genState)

require.Equal(t, math.LegacyNewDecWithPrec(4, 3), genState.Params.Fee)
require.Equal(t, sdk.NewInt64Coin(sdk.DefaultBondDenom, 163511), genState.Params.PoolCreationFee)
require.Equal(t, math.LegacyNewDecWithPrec(6, 3), genState.Params.TaxRate)
require.Equal(t, math.NewIntWithDecimal(3310, 18), genState.Params.MaxStandardCoinPerPool)
require.Equal(t, sdk.NewCoins(
sdk.NewCoin(types.UsdcIBCDenom, math.NewIntWithDecimal(70, 6)),
sdk.NewCoin(types.UsdtIBCDenom, math.NewIntWithDecimal(52, 6)),
sdk.NewCoin(types.EthIBCDenom, math.NewIntWithDecimal(65, 16)),
), genState.Params.MaxSwapAmount)

}

// TestInvalidGenesisState tests invalid genesis states.
func TestInvalidGenesisState(t *testing.T) {
interfaceRegistry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)

s := rand.NewSource(1)
r := rand.New(s)

// all these tests will panic
tests := []struct {
simState module.SimulationState
panicMsg string
}{
{ // panic => reason: incomplete initialization of the simState
module.SimulationState{}, "invalid memory address or nil pointer dereference"},
{ // panic => reason: incomplete initialization of the simState
module.SimulationState{
AppParams: make(simtypes.AppParams),
Cdc: cdc,
Rand: r,
}, "assignment to entry in nil map"},
}

for _, tt := range tests {
require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg)
}
}
Loading

0 comments on commit 471c41a

Please sign in to comment.