Skip to content

Commit

Permalink
Generalize gamm Inverse relationship test , add Inverse relationship …
Browse files Browse the repository at this point in the history
…test for stableswap (#1456)

* Generalize inverse testing from balancer package to pool-models

* Add stableswap inverse testing

* fixed failing lint test

* Update x/gamm/pool-models/internal/test_helpers/test_helpers.go

Co-authored-by: Roman <[email protected]>

* Add test

* Address Bez's review

* Fix test

* Update x/gamm/pool-models/internal/test_helpers/test_helpers.go

Co-authored-by: Aleksandr Bezobchuk <[email protected]>

Co-authored-by: Xiangan He <[email protected]>
Co-authored-by: Xiangan He <[email protected]>
Co-authored-by: Roman <[email protected]>
Co-authored-by: Aleksandr Bezobchuk <[email protected]>
  • Loading branch information
5 people authored Jul 1, 2022
1 parent c0573d1 commit 2007bab
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 25 deletions.
13 changes: 13 additions & 0 deletions app/apptesting/test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/store/rootmulti"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
Expand All @@ -18,7 +19,10 @@ import (
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtypes "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"

"github.com/osmosis-labs/osmosis/v7/app"
"github.com/osmosis-labs/osmosis/v7/x/gamm/pool-models/balancer"
Expand Down Expand Up @@ -47,6 +51,15 @@ func (s *KeeperTestHelper) Setup() {
s.TestAccs = CreateRandomAccounts(3)
}

func (s *KeeperTestHelper) CreateTestContext() sdk.Context {
db := dbm.NewMemDB()
logger := log.NewNopLogger()

ms := rootmulti.NewStore(db, logger)

return sdk.NewContext(ms, tmtypes.Header{}, false, logger)
}

func (s *KeeperTestHelper) FundAcc(acc sdk.AccAddress, amounts sdk.Coins) {
err := simapp.FundAccount(s.App.BankKeeper, s.Ctx, acc, amounts)
s.Require().NoError(err)
Expand Down
5 changes: 5 additions & 0 deletions x/gamm/pool-models/balancer/amm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import (
"github.com/stretchr/testify/require"

"github.com/osmosis-labs/osmosis/v7/x/gamm/pool-models/balancer"
"github.com/osmosis-labs/osmosis/v7/x/gamm/pool-models/internal/test_helpers"
)

type BalancerTestSuite struct {
test_helpers.CfmmCommonTestSuite
}

func TestBalancerPoolParams(t *testing.T) {
// Tests that creating a pool with the given pair of swapfee and exit fee
// errors or succeeds as intended. Furthermore, it checks that
Expand Down
21 changes: 3 additions & 18 deletions x/gamm/pool-models/balancer/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ func TestGetPoolAssetsByDenom(t *testing.T) {

// TestCalculateAmountOutAndIn_InverseRelationship tests that the same amount of token is guaranteed upon
// sequential operation of CalcInAmtGivenOut and CalcOutAmtGivenIn.
func TestCalculateAmountOutAndIn_InverseRelationship(t *testing.T) {
func (suite *BalancerTestSuite) TestBalancerCalculateAmountOutAndIn_InverseRelationship(t *testing.T) {
type testcase struct {
denomOut string
initialPoolOut int64
Expand Down Expand Up @@ -584,7 +584,7 @@ func TestCalculateAmountOutAndIn_InverseRelationship(t *testing.T) {
for _, tc := range testcases {
for _, swapFee := range swapFeeCases {
t.Run(getTestCaseName(tc, swapFee), func(t *testing.T) {
ctx := createTestContext(t)
ctx := suite.CreateTestContext()

poolAssetOut := balancer.PoolAsset{
Token: sdk.NewInt64Coin(tc.denomOut, tc.initialPoolOut),
Expand All @@ -605,24 +605,9 @@ func TestCalculateAmountOutAndIn_InverseRelationship(t *testing.T) {
pool := createTestPool(t, swapFeeDec, exitFeeDec, poolAssetOut, poolAssetIn)
require.NotNil(t, pool)

initialOut := sdk.NewInt64Coin(poolAssetOut.Token.Denom, tc.initialCalcOut)
initialOutCoins := sdk.NewCoins(initialOut)

sut := func() {
actualTokenIn, err := pool.CalcInAmtGivenOut(ctx, initialOutCoins, poolAssetIn.Token.Denom, swapFeeDec)
require.NoError(t, err)

inverseTokenOut, err := pool.CalcOutAmtGivenIn(ctx, sdk.NewCoins(actualTokenIn), poolAssetOut.Token.Denom, swapFeeDec)
require.NoError(t, err)

require.Equal(t, initialOut.Denom, inverseTokenOut.Denom)

expected := initialOut.Amount.ToDec()
actual := inverseTokenOut.Amount.ToDec()
suite.TestCalculateAmountOutAndIn_InverseRelationship(ctx, pool, poolAssetIn.Token.Denom, poolAssetOut.Token.Denom, tc.initialCalcOut, swapFeeDec)

// allow a rounding error of up to 1 for this relation
tol := sdk.NewDec(1)
require.True(osmoutils.DecApproxEq(t, expected, actual, tol))
}

balancerPool, ok := pool.(*balancer.Pool)
Expand Down
2 changes: 0 additions & 2 deletions x/gamm/pool-models/internal/cfmm_common/lp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ func TestCalcExitPool(t *testing.T) {
stableswap.PoolParams{ExitFee: sdk.ZeroDec()},
twoStablePoolAssets,
"",
time.Now(),
)
require.NoError(t, err)

Expand All @@ -64,7 +63,6 @@ func TestCalcExitPool(t *testing.T) {
stableswap.PoolParams{ExitFee: sdk.MustNewDecFromStr("0.0001")},
twoStablePoolAssets,
"",
time.Now(),
)
require.NoError(t, err)

Expand Down
63 changes: 63 additions & 0 deletions x/gamm/pool-models/internal/test_helpers/test_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package test_helpers

import (
"testing"

"github.com/cosmos/cosmos-sdk/store/rootmulti"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"

"github.com/osmosis-labs/osmosis/v7/osmoutils"
"github.com/osmosis-labs/osmosis/v7/x/gamm/types"
)

// CfmmCommonTestSuite is the common test suite struct of Constant Function Market Maker,
// that pool-models can inherit from.
type CfmmCommonTestSuite struct {
suite.Suite
}

func (suite *CfmmCommonTestSuite) CreateTestContext() sdk.Context {
db := dbm.NewMemDB()
logger := log.NewNopLogger()

ms := rootmulti.NewStore(db, logger)

return sdk.NewContext(ms, tmtypes.Header{}, false, logger)
}

func (suite *CfmmCommonTestSuite) TestCalculateAmountOutAndIn_InverseRelationship(
ctx sdk.Context,
pool types.PoolI,
assetInDenom string,
assetOutDenom string,
initialCalcOut int64,
swapFee sdk.Dec,
) {
initialOut := sdk.NewInt64Coin(assetOutDenom, initialCalcOut)
initialOutCoins := sdk.NewCoins(initialOut)

actualTokenIn, err := pool.CalcInAmtGivenOut(ctx, initialOutCoins, assetInDenom, swapFee)
suite.Require().NoError(err)

inverseTokenOut, err := pool.CalcOutAmtGivenIn(ctx, sdk.NewCoins(actualTokenIn), assetOutDenom, swapFee)
suite.Require().NoError(err)

suite.Require().Equal(initialOut.Denom, inverseTokenOut.Denom)

expected := initialOut.Amount.ToDec()
actual := inverseTokenOut.Amount.ToDec()

// allow a rounding error of up to 1 for this relation
tol := sdk.NewDec(1)
_, approxEqual, _, _, _ := osmoutils.DecApproxEq(suite.T(), expected, actual, tol)
suite.Require().True(approxEqual)
}

func TestCfmmCommonTestSuite(t *testing.T) {
t.Parallel()
suite.Run(t, new(CfmmCommonTestSuite))
}
109 changes: 108 additions & 1 deletion x/gamm/pool-models/stableswap/amm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import (

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

"github.com/osmosis-labs/osmosis/v7/x/gamm/pool-models/internal/test_helpers"
)

type StableSwapTestSuite struct {
test_helpers.CfmmCommonTestSuite
}

// Replace with https://github.com/cosmos/cosmos-sdk/blob/master/types/decimal.go#L892-L895
// once our SDK branch is up to date with it
func decApproxEq(t *testing.T, exp sdk.Dec, actual sdk.Dec, errTolerance sdk.Dec) {
Expand Down Expand Up @@ -45,7 +51,7 @@ func TestCFMMInvariantTwoAssets(t *testing.T) {
// using two-asset cfmm
k0 := cfmmConstant(test.xReserve, test.yReserve)
xOut := solveCfmm(test.xReserve, test.yReserve, test.yIn)
fmt.Println("xOut", xOut)

k1 := cfmmConstant(test.xReserve.Sub(xOut), test.yReserve.Add(test.yIn))
decApproxEq(t, k0, k1, kErrTolerance)

Expand Down Expand Up @@ -100,3 +106,104 @@ func TestCFMMInvariantMultiAssets(t *testing.T) {
decApproxEq(t, k2, k3, kErrTolerance)
}
}

func (suite *StableSwapTestSuite) Test_StableSwap_CalculateAmountOutAndIn_InverseRelationship(t *testing.T) {
type testcase struct {
denomOut string
initialPoolOut int64
initialWeightOut int64
initialCalcOut int64

denomIn string
initialPoolIn int64
initialWeightIn int64
}

// For every test case in testcases, apply a swap fee in swapFeeCases.
testcases := []testcase{
{
denomOut: "uosmo",
initialPoolOut: 1_000_000_000_000,
initialWeightOut: 100,
initialCalcOut: 100,

denomIn: "ion",
initialPoolIn: 1_000_000_000_000,
initialWeightIn: 100,
},
{
denomOut: "uosmo",
initialPoolOut: 1_000,
initialWeightOut: 100,
initialCalcOut: 100,

denomIn: "ion",
initialPoolIn: 1_000_000,
initialWeightIn: 100,
},
{
denomOut: "uosmo",
initialPoolOut: 1_000,
initialWeightOut: 100,
initialCalcOut: 100,

denomIn: "ion",
initialPoolIn: 1_000_000,
initialWeightIn: 100,
},
{
denomOut: "uosmo",
initialPoolOut: 1_000,
initialWeightOut: 200,
initialCalcOut: 100,

denomIn: "ion",
initialPoolIn: 1_000_000,
initialWeightIn: 50,
},
{
denomOut: "uosmo",
initialPoolOut: 1_000_000,
initialWeightOut: 200,
initialCalcOut: 100000,

denomIn: "ion",
initialPoolIn: 1_000_000_000,
initialWeightIn: 50,
},
}

swapFeeCases := []string{"0", "0.001", "0.1", "0.5", "0.99"}

getTestCaseName := func(tc testcase, swapFeeCase string) string {
return fmt.Sprintf("tokenOutInitial: %d, tokenInInitial: %d, initialOut: %d, swapFee: %s",
tc.initialPoolOut,
tc.initialPoolIn,
tc.initialCalcOut,
swapFeeCase,
)
}

for _, tc := range testcases {
for _, swapFee := range swapFeeCases {
t.Run(getTestCaseName(tc, swapFee), func(t *testing.T) {
ctx := suite.CreateTestContext()

poolLiquidityIn := sdk.NewInt64Coin(tc.denomOut, tc.initialPoolOut)
poolLiquidityOut := sdk.NewInt64Coin(tc.denomIn, tc.initialPoolIn)
poolLiquidity := sdk.NewCoins(poolLiquidityIn, poolLiquidityOut)

swapFeeDec, err := sdk.NewDecFromStr(swapFee)
require.NoError(t, err)

exitFeeDec, err := sdk.NewDecFromStr("0")
require.NoError(t, err)

pool := createTestPool(t, poolLiquidity, swapFeeDec, exitFeeDec)
require.NotNil(t, pool)

suite.TestCalculateAmountOutAndIn_InverseRelationship(ctx, pool, poolLiquidityIn.Denom, poolLiquidityOut.Denom, tc.initialCalcOut, swapFeeDec)
})
}
}
}
2 changes: 1 addition & 1 deletion x/gamm/pool-models/stableswap/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (msg MsgCreateStableswapPool) InitialLiquidity() sdk.Coins {
}

func (msg MsgCreateStableswapPool) CreatePool(ctx sdk.Context, poolId uint64) (types.PoolI, error) {
stableswapPool, err := NewStableswapPool(poolId, *msg.PoolParams, msg.InitialPoolLiquidity, msg.FuturePoolGovernor, ctx.BlockTime())
stableswapPool, err := NewStableswapPool(poolId, *msg.PoolParams, msg.InitialPoolLiquidity, msg.FuturePoolGovernor)
if err != nil {
return nil, err
}
Expand Down
4 changes: 1 addition & 3 deletions x/gamm/pool-models/stableswap/stableswap_pool.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package stableswap

import (
"time"

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

"github.com/osmosis-labs/osmosis/v7/x/gamm/types"
Expand All @@ -15,7 +13,7 @@ var _ types.PoolI = &Pool{}
// * len(initialLiquidity) = 2
// * FutureGovernor is valid
// * poolID doesn't already exist
func NewStableswapPool(poolId uint64, stableswapPoolParams PoolParams, initialLiquidity sdk.Coins, futureGovernor string, blockTime time.Time) (Pool, error) {
func NewStableswapPool(poolId uint64, stableswapPoolParams PoolParams, initialLiquidity sdk.Coins, futureGovernor string) (Pool, error) {
pool := Pool{
Address: types.NewPoolAddress(poolId).String(),
Id: poolId,
Expand Down
21 changes: 21 additions & 0 deletions x/gamm/pool-models/stableswap/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package stableswap

import (
"testing"

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

"github.com/osmosis-labs/osmosis/v7/x/gamm/types"
)

func createTestPool(t *testing.T, poolLiquidity sdk.Coins, swapFee, exitFee sdk.Dec) types.PoolI {
pool, err := NewStableswapPool(1, PoolParams{
SwapFee: swapFee,
ExitFee: exitFee,
}, poolLiquidity, "")

require.NoError(t, err)

return &pool
}

0 comments on commit 2007bab

Please sign in to comment.