Skip to content

Commit

Permalink
feat: simulation for coinswap module
Browse files Browse the repository at this point in the history
  • Loading branch information
poorphd committed Aug 23, 2023
1 parent 0b1bd63 commit c09fe06
Show file tree
Hide file tree
Showing 10 changed files with 479 additions and 10 deletions.
4 changes: 4 additions & 0 deletions app/params/weights.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ const (

DefaultWeightMsgConvertCoin int = 20
DefaultWeightMsgConvertErc20 int = 20

DefaultWeightMsgSwapOrder int = 10
DefaultWeightMsgAddLiquidity int = 20
DefaultWeightMsgRemoveLiquidity int = 10
)
1 change: 1 addition & 0 deletions x/coinswap/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange {

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

}

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]))
}
}
}
59 changes: 59 additions & 0 deletions x/coinswap/simulation/decoder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package simulation_test

import (
"fmt"
"testing"

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

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

func TestCoinSwapStore(t *testing.T) {
cdc := simapp.MakeTestEncodingConfig()
dec := simulation.NewDecodeStore(cdc.Marshaler)

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.Marshaler.MustMarshal(&pool)},
{Key: []byte(types.KeyPoolLptDenom), Value: cdc.Marshaler.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)
}
})
}
}
74 changes: 73 additions & 1 deletion x/coinswap/simulation/genesis.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,80 @@
package simulation

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

"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) sdk.Dec {
return sdk.NewDecWithPrec(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) sdk.Dec {
return sdk.NewDecWithPrec(int64(simtypes.RandIntBetween(r, 0, 10)), 3)
}

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

func generateRandomMaxSwapAmount(r *rand.Rand) sdk.Coins {
return sdk.NewCoins(
sdk.NewCoin(types.UsdcIBCDenom, sdk.NewIntWithDecimal(int64(simtypes.RandIntBetween(r, 1, 100)), 6)),
sdk.NewCoin(types.UsdtIBCDenom, sdk.NewIntWithDecimal(int64(simtypes.RandIntBetween(r, 1, 100)), 6)),
sdk.NewCoin(types.EthIBCDenom, sdk.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(
simState.Cdc, fee, &genesis.Params.Fee, simState.Rand,
func(r *rand.Rand) { genesis.Params.Fee = generateRandomFee(r) },
)

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

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

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

simState.AppParams.GetOrGenerate(
simState.Cdc, 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)

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

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

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: 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, sdk.NewDecWithPrec(4, 3), genState.Params.Fee)
require.Equal(t, sdk.NewInt64Coin(sdk.DefaultBondDenom, 163511), genState.Params.PoolCreationFee)
require.Equal(t, sdk.NewDecWithPrec(6, 3), genState.Params.TaxRate)
require.Equal(t, sdk.NewIntWithDecimal(3310, 18), genState.Params.MaxStandardCoinPerPool)
require.Equal(t, sdk.NewCoins(
sdk.NewCoin(types.UsdcIBCDenom, sdk.NewIntWithDecimal(70, 6)),
sdk.NewCoin(types.UsdtIBCDenom, sdk.NewIntWithDecimal(52, 6)),
sdk.NewCoin(types.EthIBCDenom, sdk.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 c09fe06

Please sign in to comment.