-
Notifications
You must be signed in to change notification settings - Fork 341
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(amm): amm swaps rewards UT (#1120)
- Loading branch information
Showing
4 changed files
with
267 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package apptesting | ||
|
||
import ( | ||
"math/big" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
gammkeeper "github.com/osmosis-labs/osmosis/v15/x/gamm/keeper" | ||
"github.com/osmosis-labs/osmosis/v15/x/gamm/pool-models/balancer" | ||
gammtypes "github.com/osmosis-labs/osmosis/v15/x/gamm/types" | ||
poolmanagertypes "github.com/osmosis-labs/osmosis/v15/x/poolmanager/types" | ||
) | ||
|
||
// 10^18 multiplier | ||
var EXP = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) | ||
|
||
// Default sender for amm messages | ||
var sender = sdk.MustAccAddressFromBech32(alice) | ||
|
||
var DefaultAcctFunds sdk.Coins = sdk.NewCoins( | ||
sdk.NewCoin("adym", EXP.Mul(sdk.NewInt(1_000_000))), | ||
sdk.NewCoin("foo", EXP.Mul(sdk.NewInt(1_000_000))), | ||
sdk.NewCoin("bar", EXP.Mul(sdk.NewInt(1_000_000))), | ||
sdk.NewCoin("baz", EXP.Mul(sdk.NewInt(1_000_000))), | ||
) | ||
|
||
var DefaultPoolParams = balancer.PoolParams{ | ||
SwapFee: sdk.NewDec(0), | ||
ExitFee: sdk.NewDec(0), | ||
} | ||
|
||
var DefaultPoolAssets = []balancer.PoolAsset{ | ||
{ | ||
Weight: sdk.NewInt(100), | ||
Token: sdk.NewCoin("foo", EXP.Mul(sdk.NewInt(500))), | ||
}, | ||
{ | ||
Weight: sdk.NewInt(100), | ||
Token: sdk.NewCoin("adym", EXP.Mul(sdk.NewInt(500))), | ||
}, | ||
} | ||
|
||
// PrepareCustomPool sets up a Balancer pool with an array of assets and given parameters | ||
// This is the generic method called by other PreparePool wrappers | ||
// It funds the sender account with DefaultAcctFunds | ||
func (s *KeeperTestHelper) PrepareCustomPool(assets []balancer.PoolAsset, params balancer.PoolParams) uint64 { | ||
s.FundAcc(sender, DefaultAcctFunds) | ||
|
||
msg := balancer.NewMsgCreateBalancerPool(sender, params, assets, "") | ||
poolId, err := s.App.PoolManagerKeeper.CreatePool(s.Ctx, msg) | ||
s.NoError(err) | ||
return poolId | ||
} | ||
|
||
// PrepareDefaultPool sets up a pool with default pool assets and parameters. | ||
func (s *KeeperTestHelper) PrepareDefaultPool() uint64 { | ||
poolId := s.PrepareCustomPool(DefaultPoolAssets, DefaultPoolParams) | ||
|
||
spotPrice, err := s.App.GAMMKeeper.CalculateSpotPrice(s.Ctx, poolId, "foo", "adym") | ||
s.NoError(err) | ||
s.Equal(sdk.NewDec(1).String(), spotPrice.String()) | ||
|
||
return poolId | ||
} | ||
|
||
// PreparePoolWithCoins returns a pool consisted of given coins with equal weight and default pool parameters. | ||
func (s *KeeperTestHelper) PreparePoolWithCoins(coins sdk.Coins) uint64 { | ||
poolAssets := coinsToAssets(coins) | ||
return s.PrepareCustomPool(poolAssets, DefaultPoolParams) | ||
} | ||
|
||
// PreparePoolWithPoolParams sets up a pool with given poolParams and default pool assets. | ||
func (s *KeeperTestHelper) PreparePoolWithPoolParams(poolParams balancer.PoolParams) uint64 { | ||
return s.PrepareCustomPool(DefaultPoolAssets, poolParams) | ||
} | ||
|
||
// PrepareCustomPoolFromCoins sets up a Balancer pool with an array of coins and given parameters | ||
// The coins are converted to pool assets where each asset has a weight of 1. | ||
func (s *KeeperTestHelper) PrepareCustomPoolFromCoins(coins sdk.Coins, params balancer.PoolParams) uint64 { | ||
poolAssets := coinsToAssets(coins) | ||
return s.PrepareCustomPool(poolAssets, params) | ||
} | ||
|
||
func coinsToAssets(coins sdk.Coins) []balancer.PoolAsset { | ||
var poolAssets []balancer.PoolAsset | ||
for _, coin := range coins { | ||
poolAsset := balancer.PoolAsset{ | ||
Weight: sdk.NewInt(1), | ||
Token: coin, | ||
} | ||
poolAssets = append(poolAssets, poolAsset) | ||
} | ||
return poolAssets | ||
} | ||
|
||
func (s *KeeperTestHelper) RunBasicSwap(poolId uint64, from string, swapIn sdk.Coin, outDenom string) { | ||
msg := gammtypes.MsgSwapExactAmountIn{ | ||
Sender: from, | ||
Routes: []poolmanagertypes.SwapAmountInRoute{{PoolId: poolId, TokenOutDenom: outDenom}}, | ||
TokenIn: swapIn, | ||
TokenOutMinAmount: sdk.ZeroInt(), | ||
} | ||
|
||
gammMsgServer := gammkeeper.NewMsgServerImpl(s.App.GAMMKeeper) | ||
_, err := gammMsgServer.SwapExactAmountIn(sdk.WrapSDKContext(s.Ctx), &msg) | ||
s.Require().NoError(err) | ||
} | ||
|
||
func (s *KeeperTestHelper) RunBasicExit(poolId uint64, shares sdk.Int, from string) (out sdk.Coins) { | ||
msg := gammtypes.MsgExitPool{ | ||
Sender: from, | ||
PoolId: poolId, | ||
ShareInAmount: shares, | ||
TokenOutMins: sdk.NewCoins(), | ||
} | ||
|
||
gammMsgServer := gammkeeper.NewMsgServerImpl(s.App.GAMMKeeper) | ||
res, err := gammMsgServer.ExitPool(sdk.WrapSDKContext(s.Ctx), &msg) | ||
s.Require().NoError(err) | ||
return res.TokenOut | ||
} | ||
|
||
// RunBasicJoin joins the pool with 10% of the total pool shares | ||
func (s *KeeperTestHelper) RunBasicJoin(poolId uint64, from string) (shares sdk.Int, cost sdk.Coins) { | ||
pool, err := s.App.GAMMKeeper.GetPoolAndPoke(s.Ctx, poolId) | ||
s.Require().NoError(err) | ||
|
||
totalPoolShare := pool.GetTotalShares() | ||
msg := gammtypes.MsgJoinPool{ | ||
Sender: from, | ||
PoolId: poolId, | ||
ShareOutAmount: totalPoolShare.Quo(sdk.NewInt(10)), | ||
TokenInMaxs: sdk.NewCoins(), | ||
} | ||
|
||
gammMsgServer := gammkeeper.NewMsgServerImpl(s.App.GAMMKeeper) | ||
res, err := gammMsgServer.JoinPool(sdk.WrapSDKContext(s.Ctx), &msg) | ||
s.Require().NoError(err) | ||
|
||
return res.ShareOutAmount, res.TokenIn | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package keeper_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/osmosis-labs/osmosis/v15/x/gamm/pool-models/balancer" | ||
"github.com/stretchr/testify/suite" | ||
|
||
cometbftproto "github.com/cometbft/cometbft/proto/tendermint/types" | ||
"github.com/dymensionxyz/dymension/v3/app/apptesting" | ||
"github.com/dymensionxyz/dymension/v3/testutil/sample" | ||
) | ||
|
||
type KeeperTestSuite struct { | ||
apptesting.KeeperTestHelper | ||
} | ||
|
||
func TestKeeperTestSuite(t *testing.T) { | ||
suite.Run(t, new(KeeperTestSuite)) | ||
} | ||
|
||
func (s *KeeperTestSuite) SetupTest() { | ||
app := apptesting.Setup(s.T(), false) | ||
ctx := app.GetBaseApp().NewContext(false, cometbftproto.Header{}) | ||
|
||
// set txfees basedenom | ||
err := app.TxFeesKeeper.SetBaseDenom(ctx, "adym") | ||
s.Require().NoError(err) | ||
|
||
s.App = app | ||
s.Ctx = ctx | ||
} | ||
|
||
func (s *KeeperTestSuite) TestSwapsRevenue() { | ||
// Create a pool with 100_000 DYM and 100_000 FOO | ||
poolCoins := sdk.NewCoins( | ||
sdk.NewCoin("adym", apptesting.EXP.Mul(sdk.NewInt(100_000))), | ||
sdk.NewCoin("foo", apptesting.EXP.Mul(sdk.NewInt(100_000))), | ||
) | ||
|
||
testCases := []struct { | ||
name string | ||
swapFee sdk.Dec | ||
takerFee sdk.Dec | ||
expRevenue bool | ||
}{ | ||
{ | ||
name: "1% swap fee, 1% taker fee", | ||
swapFee: sdk.NewDecWithPrec(1, 2), // 1% | ||
takerFee: sdk.NewDecWithPrec(1, 2), // 1% | ||
expRevenue: true, | ||
}, | ||
{ | ||
name: "1% swap fee, no taker fee", | ||
swapFee: sdk.NewDecWithPrec(1, 2), // 1% | ||
takerFee: sdk.ZeroDec(), // 0% | ||
expRevenue: true, | ||
}, | ||
{ | ||
name: "0% swap fee, 1% taker fee", | ||
swapFee: sdk.ZeroDec(), // 0% | ||
takerFee: sdk.NewDecWithPrec(1, 2), // 1% | ||
expRevenue: false, | ||
}, | ||
{ | ||
name: "0% swap fee, no taker fee", | ||
swapFee: sdk.ZeroDec(), // 0% | ||
takerFee: sdk.ZeroDec(), // 0% | ||
expRevenue: false, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
s.Run(tc.name, func() { | ||
params := s.App.GAMMKeeper.GetParams(s.Ctx) | ||
params.TakerFee = tc.takerFee | ||
s.App.GAMMKeeper.SetParams(s.Ctx, params) | ||
|
||
poolId := s.PrepareCustomPoolFromCoins(poolCoins, balancer.PoolParams{ | ||
SwapFee: tc.swapFee, | ||
ExitFee: sdk.ZeroDec(), | ||
}) | ||
|
||
// join pool | ||
addr := sample.Acc() | ||
s.FundAcc(addr, apptesting.DefaultAcctFunds) | ||
shares, _ := s.RunBasicJoin(poolId, addr.String()) | ||
|
||
// check position | ||
p, _ := s.App.GAMMKeeper.GetPool(s.Ctx, poolId) | ||
pool := p.(*balancer.Pool) // nolint: errcheck | ||
position, err := pool.CalcExitPoolCoinsFromShares(s.Ctx, shares, sdk.ZeroDec()) | ||
s.Require().NoError(err) | ||
liquidity := pool.GetTotalPoolLiquidity(s.Ctx) | ||
spot, err := s.App.GAMMKeeper.CalculateSpotPrice(s.Ctx, poolId, "foo", "adym") | ||
s.Require().NoError(err) | ||
s.T().Logf("positionBefore: %s, liquidity: %s, spot: %s", position, liquidity, spot) | ||
|
||
// swap tokens (swap 5 DYM for FOO) and vice versa | ||
s.RunBasicSwap(poolId, addr.String(), sdk.NewCoin("adym", apptesting.EXP.Mul(sdk.NewInt(5))), "foo") | ||
s.RunBasicSwap(poolId, addr.String(), sdk.NewCoin("foo", apptesting.EXP.Mul(sdk.NewInt(5))), "adym") | ||
|
||
// check position | ||
p, _ = s.App.GAMMKeeper.GetPool(s.Ctx, poolId) | ||
pool = p.(*balancer.Pool) // nolint: errcheck | ||
liquidity = pool.GetTotalPoolLiquidity(s.Ctx) | ||
positionAfter, err := pool.CalcExitPoolCoinsFromShares(s.Ctx, shares, sdk.ZeroDec()) | ||
s.Require().NoError(err) | ||
spot, err = s.App.GAMMKeeper.CalculateSpotPrice(s.Ctx, poolId, "foo", "adym") | ||
s.Require().NoError(err) | ||
s.T().Logf("positionAfterSwap: %s, liquidity: %s, spot: %s", positionAfter, liquidity, spot) | ||
|
||
// assert | ||
if tc.expRevenue { | ||
s.True(positionAfter.IsAllGT(position), fmt.Sprintf("positionBefore: %s, positionAfter: %s", position, positionAfter)) | ||
} else { | ||
s.True(positionAfter.IsAnyGT(position), fmt.Sprintf("positionBefore: %s, positionAfter: %s", position, positionAfter)) | ||
} | ||
}) | ||
} | ||
} |