From 7a6858e84400b367ee08320f7b78586328dbd76d Mon Sep 17 00:00:00 2001 From: jelysn Date: Fri, 23 Jun 2023 15:16:10 +0800 Subject: [PATCH] - Add feature to set initial LP token price to be - Add unit test for MsgServerCreatePool - Add utility to setup price of few assets on keeper test --- x/amm/keeper/initialize_pool.go | 9 ++ x/amm/keeper/keeper_test.go | 38 ++++- x/amm/keeper/msg_server_create_pool.go | 2 - x/amm/keeper/msg_server_create_pool_test.go | 163 ++++++++++++++++++++ x/amm/types/create_pool.go | 1 - 5 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 x/amm/keeper/msg_server_create_pool_test.go diff --git a/x/amm/keeper/initialize_pool.go b/x/amm/keeper/initialize_pool.go index a3853b78f..0b18ae306 100644 --- a/x/amm/keeper/initialize_pool.go +++ b/x/amm/keeper/initialize_pool.go @@ -15,6 +15,15 @@ import ( // - Records total liquidity increase // - Calls the AfterPoolCreated hook func (k Keeper) InitializePool(ctx sdk.Context, pool *types.Pool, sender sdk.AccAddress) (err error) { + tvl, err := pool.TVL(ctx, k.oracleKeeper) + if err != nil { + return err + } + + if tvl.IsPositive() { + pool.TotalShares = sdk.NewCoin(pool.TotalShares.Denom, tvl.Mul(sdk.NewDecFromInt(types.OneShare)).RoundInt()) + } + // Mint the initial pool shares share token to the sender err = k.MintPoolShareToAccount(ctx, *pool, sender, pool.GetTotalShares().Amount) if err != nil { diff --git a/x/amm/keeper/keeper_test.go b/x/amm/keeper/keeper_test.go index e6d36a6e9..d72f1308d 100644 --- a/x/amm/keeper/keeper_test.go +++ b/x/amm/keeper/keeper_test.go @@ -3,14 +3,13 @@ package keeper_test import ( "testing" - "github.com/stretchr/testify/suite" - - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - simapp "github.com/elys-network/elys/app" + oracletypes "github.com/elys-network/elys/x/oracle/types" + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/crypto/ed25519" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) const ( @@ -36,3 +35,32 @@ func (suite *KeeperTestSuite) SetupTest() { func TestKeeperSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } + +func (suite *KeeperTestSuite) SetupStableCoinPrices() { + // prices set for USDT and USDC + provider := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) + suite.app.OracleKeeper.SetAssetInfo(suite.ctx, oracletypes.AssetInfo{ + Denom: "uusdc", + Display: "USDC", + Decimal: 6, + }) + suite.app.OracleKeeper.SetAssetInfo(suite.ctx, oracletypes.AssetInfo{ + Denom: "uusdt", + Display: "USDT", + Decimal: 6, + }) + suite.app.OracleKeeper.SetPrice(suite.ctx, oracletypes.Price{ + Asset: "USDC", + Price: sdk.NewDec(1), + Source: "elys", + Provider: provider.String(), + Timestamp: uint64(suite.ctx.BlockTime().Unix()), + }) + suite.app.OracleKeeper.SetPrice(suite.ctx, oracletypes.Price{ + Asset: "USDT", + Price: sdk.NewDec(1), + Source: "elys", + Provider: provider.String(), + Timestamp: uint64(suite.ctx.BlockTime().Unix()), + }) +} diff --git a/x/amm/keeper/msg_server_create_pool.go b/x/amm/keeper/msg_server_create_pool.go index e8f5dc308..6615f27b4 100644 --- a/x/amm/keeper/msg_server_create_pool.go +++ b/x/amm/keeper/msg_server_create_pool.go @@ -14,8 +14,6 @@ import ( func (k msgServer) CreatePool(goCtx context.Context, msg *types.MsgCreatePool) (*types.MsgCreatePoolResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - _ = ctx - poolId, err := k.Keeper.CreatePool(ctx, msg) if err != nil { return &types.MsgCreatePoolResponse{}, err diff --git a/x/amm/keeper/msg_server_create_pool_test.go b/x/amm/keeper/msg_server_create_pool_test.go new file mode 100644 index 000000000..284c1e239 --- /dev/null +++ b/x/amm/keeper/msg_server_create_pool_test.go @@ -0,0 +1,163 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/elys-network/elys/x/amm/keeper" + "github.com/elys-network/elys/x/amm/types" + "github.com/tendermint/tendermint/crypto/ed25519" +) + +func (suite *KeeperTestSuite) TestMsgServerCreatePool() { + for _, tc := range []struct { + desc string + senderInitBalance sdk.Coins + poolParams types.PoolParams + poolAssets []types.PoolAsset + expSenderBalance sdk.Coins + expTotalLiquidity sdk.Coins + expLpCommitment sdk.Coin + expPass bool + }{ + { + desc: "zero tvl pool creation", + senderInitBalance: sdk.Coins{sdk.NewInt64Coin("ueden", 1000000), sdk.NewInt64Coin("uelys", 1000000)}, + poolParams: types.PoolParams{ + SwapFee: sdk.ZeroDec(), + ExitFee: sdk.ZeroDec(), + UseOracle: false, + WeightBreakingFeeMultiplier: sdk.ZeroDec(), + SlippageReduction: sdk.ZeroDec(), + LpFeePortion: sdk.ZeroDec(), + StakingFeePortion: sdk.ZeroDec(), + WeightRecoveryFeePortion: sdk.ZeroDec(), + ThresholdWeightDifference: sdk.ZeroDec(), + FeeDenom: "", + }, + poolAssets: []types.PoolAsset{ + { + Token: sdk.NewInt64Coin("ueden", 1000000), + Weight: sdk.OneInt(), + }, + { + Token: sdk.NewInt64Coin("uelys", 1000000), + Weight: sdk.OneInt(), + }, + }, + expSenderBalance: sdk.Coins{}, + expLpCommitment: sdk.NewCoin("amm/pool/0", sdk.NewInt(100).Mul(types.OneShare)), + expPass: true, + }, + { + desc: "positive tvl pool creation", + senderInitBalance: sdk.Coins{sdk.NewInt64Coin("ueden", 1000000), sdk.NewInt64Coin("uusdc", 1000000)}, + poolParams: types.PoolParams{ + SwapFee: sdk.ZeroDec(), + ExitFee: sdk.ZeroDec(), + UseOracle: false, + WeightBreakingFeeMultiplier: sdk.ZeroDec(), + SlippageReduction: sdk.ZeroDec(), + LpFeePortion: sdk.ZeroDec(), + StakingFeePortion: sdk.ZeroDec(), + WeightRecoveryFeePortion: sdk.ZeroDec(), + ThresholdWeightDifference: sdk.ZeroDec(), + FeeDenom: "", + }, + poolAssets: []types.PoolAsset{ + { + Token: sdk.NewInt64Coin("ueden", 1000000), + Weight: sdk.OneInt(), + }, + { + Token: sdk.NewInt64Coin("uusdc", 1000000), + Weight: sdk.OneInt(), + }, + }, + expSenderBalance: sdk.Coins{}, + expLpCommitment: sdk.NewCoin("amm/pool/0", sdk.NewInt(2).Mul(types.OneShare)), + expPass: true, + }, + { + desc: "not enough balance to create pool", + senderInitBalance: sdk.Coins{sdk.NewInt64Coin("ueden", 1000000)}, + poolParams: types.PoolParams{ + SwapFee: sdk.ZeroDec(), + ExitFee: sdk.ZeroDec(), + UseOracle: false, + WeightBreakingFeeMultiplier: sdk.ZeroDec(), + SlippageReduction: sdk.ZeroDec(), + LpFeePortion: sdk.ZeroDec(), + StakingFeePortion: sdk.ZeroDec(), + WeightRecoveryFeePortion: sdk.ZeroDec(), + ThresholdWeightDifference: sdk.ZeroDec(), + FeeDenom: "", + }, + poolAssets: []types.PoolAsset{ + { + Token: sdk.NewInt64Coin("ueden", 1000000), + Weight: sdk.OneInt(), + }, + { + Token: sdk.NewInt64Coin("uusdc", 1000000), + Weight: sdk.OneInt(), + }, + }, + expSenderBalance: sdk.Coins{}, + expLpCommitment: sdk.Coin{}, + expPass: false, + }, + } { + suite.Run(tc.desc, func() { + suite.SetupTest() + suite.SetupStableCoinPrices() + + // bootstrap accounts + sender := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) + + // bootstrap balances + err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, tc.senderInitBalance) + suite.Require().NoError(err) + err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, minttypes.ModuleName, sender, tc.senderInitBalance) + suite.Require().NoError(err) + + // execute function + msgServer := keeper.NewMsgServerImpl(suite.app.AmmKeeper) + resp, err := msgServer.CreatePool( + sdk.WrapSDKContext(suite.ctx), + &types.MsgCreatePool{ + Sender: sender.String(), + PoolParams: &tc.poolParams, + PoolAssets: tc.poolAssets, + }) + if !tc.expPass { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().Equal(resp.PoolID, uint64(0)) + + pools := suite.app.AmmKeeper.GetAllPool(suite.ctx) + suite.Require().Len(pools, 1) + suite.Require().Equal(pools[0].PoolId, uint64(0)) + suite.Require().Equal(pools[0].PoolParams, tc.poolParams) + suite.Require().Equal(pools[0].TotalShares.Amount.String(), tc.expLpCommitment.Amount.String()) + + totalWeight := sdk.ZeroInt() + for _, poolAsset := range tc.poolAssets { + totalWeight = totalWeight.Add(poolAsset.Weight) + } + suite.Require().Equal(pools[0].TotalWeight.String(), totalWeight.MulRaw(types.GuaranteedWeightPrecision).String()) + + // check balance change on sender + balances := suite.app.BankKeeper.GetAllBalances(suite.ctx, sender) + suite.Require().Equal(balances.String(), tc.expSenderBalance.String()) + + // check lp token commitment + commitments, found := suite.app.CommitmentKeeper.GetCommitments(suite.ctx, sender.String()) + suite.Require().True(found) + suite.Require().Len(commitments.CommittedTokens, 1) + suite.Require().Equal(commitments.CommittedTokens[0].Denom, tc.expLpCommitment.Denom) + suite.Require().Equal(commitments.CommittedTokens[0].Amount.String(), tc.expLpCommitment.Amount.String()) + } + }) + } +} diff --git a/x/amm/types/create_pool.go b/x/amm/types/create_pool.go index e42ac4e3e..62fc2247d 100644 --- a/x/amm/types/create_pool.go +++ b/x/amm/types/create_pool.go @@ -5,7 +5,6 @@ import ( ) func (msg *MsgCreatePool) CreatePool(ctx sdk.Context, poolID uint64) (*Pool, error) { - // poolAssets := make([]PoolAsset, len(msg.PoolAssets)) pool, err := NewBalancerPool(poolID, *msg.PoolParams, msg.PoolAssets, ctx.BlockTime()) return &pool, err }