Skip to content

Commit

Permalink
Merge pull request #16 from dymensionxyz/13-implement-auto-swap-and-b…
Browse files Browse the repository at this point in the history
…urn-for-taker-fee

feat: implement auto swap and burn for taker fee
  • Loading branch information
mtsitrin authored Nov 29, 2023
2 parents b5d496e + 5b8fffc commit c3f5b51
Show file tree
Hide file tree
Showing 5 changed files with 479 additions and 237 deletions.
4 changes: 2 additions & 2 deletions x/gamm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (server msgServer) SwapExactAmountIn(goCtx context.Context, msg *types.MsgS
return nil, err
}

err = server.keeper.chargeTakerFee(ctx, takerFeesCoins, sender)
err = server.keeper.chargeTakerFeeSwapAmountIn(ctx, takerFeesCoins, sender, msg.Routes)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -221,7 +221,7 @@ func (server msgServer) SwapExactAmountOut(goCtx context.Context, msg *types.Msg
tokenInCoin := sdk.NewCoin(msg.Routes[0].TokenInDenom, tokenInAmount)
tokenInAmountWithTakerFee, takerFeeCoin := server.keeper.AddTakerFee(tokenInCoin, takerFee)

err = server.keeper.chargeTakerFee(ctx, takerFeeCoin, sender)
err = server.keeper.chargeTakerFeeSwapAmountOut(ctx, takerFeeCoin, sender, msg.Routes, msg.TokenOut.Denom)
if err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package keeper_test
import (
sdk "github.com/cosmos/cosmos-sdk/types"

banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"

"github.com/osmosis-labs/osmosis/v15/x/gamm/keeper"
"github.com/osmosis-labs/osmosis/v15/x/gamm/types"
poolmanagertypes "github.com/osmosis-labs/osmosis/v15/x/poolmanager/types"
Expand All @@ -27,6 +29,7 @@ func (suite *KeeperTestSuite) TestSwapExactAmountIn_Events() {
tokenIn sdk.Coin
tokenOutMinAmount sdk.Int
expectError bool
expectedBurnEvents bool
expectedSwapEvents int
expectedMessageEvents int
}{
Expand All @@ -43,10 +46,24 @@ func (suite *KeeperTestSuite) TestSwapExactAmountIn_Events() {
TokenOutDenom: "bar",
},
},
tokenIn: sdk.NewCoin("foo", sdk.NewInt(tokenIn)),
tokenIn: sdk.NewCoin("udym", sdk.NewInt(tokenIn)),
tokenOutMinAmount: sdk.NewInt(tokenInMinAmount),
expectedSwapEvents: 1,
expectedMessageEvents: 3, // 1 gamm + 2 events emitted by other keeper methods.
expectedBurnEvents: true,
expectedMessageEvents: 4, // 1 gamm + 2 bank send for swap + 1 bank send when burn
},
"one hop - taker fee swap": {
routes: []poolmanagertypes.SwapAmountInRoute{
{
PoolId: 1,
TokenOutDenom: "udym",
},
},
tokenIn: sdk.NewCoin("bar", sdk.NewInt(tokenIn)),
tokenOutMinAmount: sdk.NewInt(tokenInMinAmount),
expectedSwapEvents: 2,
expectedBurnEvents: true,
expectedMessageEvents: 6, // 1 gamm + 2 bank send for swap + 2 taker fee swap + 1 bank send when burn
},
"two hops": {
routes: []poolmanagertypes.SwapAmountInRoute{
Expand All @@ -59,10 +76,28 @@ func (suite *KeeperTestSuite) TestSwapExactAmountIn_Events() {
TokenOutDenom: "baz",
},
},
tokenIn: sdk.NewCoin("foo", sdk.NewInt(tokenIn)),
tokenIn: sdk.NewCoin("udym", sdk.NewInt(tokenIn)),
tokenOutMinAmount: sdk.NewInt(tokenInMinAmount),
expectedSwapEvents: 2,
expectedMessageEvents: 5, // 1 gamm + 4 events emitted by other keeper methods.
expectedBurnEvents: true,
expectedMessageEvents: 6, // 1 gamm + 4 swap + 1 burn
},
"two hops - taker fee swap": {
routes: []poolmanagertypes.SwapAmountInRoute{
{
PoolId: 1,
TokenOutDenom: "bar",
},
{
PoolId: 2,
TokenOutDenom: "udym",
},
},
tokenIn: sdk.NewCoin("foo", sdk.NewInt(tokenIn)),
tokenOutMinAmount: sdk.NewInt(tokenInMinAmount),
expectedBurnEvents: true,
expectedSwapEvents: 4, //2 for the swap + 2 for taker fee
expectedMessageEvents: 10, // 1 gamm + 8 swap + 1 burn
},
"invalid - two hops, denom does not exist": {
routes: []poolmanagertypes.SwapAmountInRoute{
Expand Down Expand Up @@ -106,9 +141,12 @@ func (suite *KeeperTestSuite) TestSwapExactAmountIn_Events() {
suite.NoError(err)
suite.NotNil(response)
}
if tc.expectedBurnEvents {
suite.AssertEventEmitted(ctx, banktypes.EventTypeCoinBurn, 1)
}

suite.AssertEventEmitted(ctx, types.TypeEvtTokenSwapped, tc.expectedSwapEvents)
// suite.AssertEventEmitted(ctx, sdk.EventTypeMessage, tc.expectedMessageEvents)
suite.AssertEventEmitted(ctx, sdk.EventTypeMessage, tc.expectedMessageEvents)
})
}
}
Expand All @@ -118,40 +156,55 @@ func (suite *KeeperTestSuite) TestSwapExactAmountIn_Events() {
func (suite *KeeperTestSuite) TestSwapExactAmountOut_Events() {
const (
tokenInMaxAmount = int64Max
tokenOut = 5
tokenOut = 500
)

testcases := map[string]struct {
routes []poolmanagertypes.SwapAmountOutRoute
tokenOut sdk.Coin
tokenInMaxAmount sdk.Int
expectError bool
expectedBurnEvents bool
expectedSwapEvents int
expectedMessageEvents int
}{
"zero hops": {
routes: []poolmanagertypes.SwapAmountOutRoute{},
tokenOut: sdk.NewCoin("foo", sdk.NewInt(tokenOut)),
tokenInMaxAmount: sdk.NewInt(tokenInMaxAmount),
expectError: true,
},
"one hop": {
// "zero hops": {
// routes: []poolmanagertypes.SwapAmountOutRoute{},
// tokenOut: sdk.NewCoin("foo", sdk.NewInt(tokenOut)),
// tokenInMaxAmount: sdk.NewInt(tokenInMaxAmount),
// expectError: true,
// },
// "one hop": {
// routes: []poolmanagertypes.SwapAmountOutRoute{
// {
// PoolId: 1,
// TokenInDenom: "udym",
// },
// },
// tokenOut: sdk.NewCoin("foo", sdk.NewInt(tokenOut)),
// tokenInMaxAmount: sdk.NewInt(tokenInMaxAmount),
// expectedBurnEvents: true,
// expectedSwapEvents: 1,
// expectedMessageEvents: 4, // 1 gamm + 2 for swap + 1 for burn
// },
"one hop - with taker fee": {
routes: []poolmanagertypes.SwapAmountOutRoute{
{
PoolId: 1,
TokenInDenom: "bar",
},
},
tokenOut: sdk.NewCoin("foo", sdk.NewInt(tokenOut)),
tokenOut: sdk.NewCoin("udym", sdk.NewInt(tokenOut)),
tokenInMaxAmount: sdk.NewInt(tokenInMaxAmount),
expectedSwapEvents: 1,
expectedMessageEvents: 3, // 1 gamm + 2 events emitted by other keeper methods.
expectedBurnEvents: true,
expectedSwapEvents: 2,
expectedMessageEvents: 6, // 1 gamm + 2 for swap + 2 swap for taker fee + 1 for send to burn
},
"two hops": {
routes: []poolmanagertypes.SwapAmountOutRoute{
{
PoolId: 1,
TokenInDenom: "bar",
TokenInDenom: "udym",
},
{
PoolId: 2,
Expand All @@ -160,8 +213,26 @@ func (suite *KeeperTestSuite) TestSwapExactAmountOut_Events() {
},
tokenOut: sdk.NewCoin("foo", sdk.NewInt(tokenOut)),
tokenInMaxAmount: sdk.NewInt(tokenInMaxAmount),
expectedBurnEvents: true,
expectedSwapEvents: 2,
expectedMessageEvents: 5, // 1 gamm + 4 events emitted by other keeper methods.
expectedMessageEvents: 6, // 1 gamm + 4 for swap + 1 for send to community
},
"two hops - taker fee swap": {
routes: []poolmanagertypes.SwapAmountOutRoute{
{
PoolId: 1,
TokenInDenom: "bar",
},
{
PoolId: 2,
TokenInDenom: "udym",
},
},
tokenOut: sdk.NewCoin("foo", sdk.NewInt(tokenOut)),
tokenInMaxAmount: sdk.NewInt(tokenInMaxAmount),
expectedBurnEvents: true,
expectedSwapEvents: 3,
expectedMessageEvents: 8, // 1 gamm + 4 for swap + 2 for taker fee swap + 1 for send to burn
},
"invalid - two hops, denom does not exist": {
routes: []poolmanagertypes.SwapAmountOutRoute{
Expand Down Expand Up @@ -206,8 +277,12 @@ func (suite *KeeperTestSuite) TestSwapExactAmountOut_Events() {
suite.NotNil(response)
}

if tc.expectedBurnEvents {
suite.AssertEventEmitted(ctx, banktypes.EventTypeCoinBurn, 1)
}

suite.AssertEventEmitted(ctx, types.TypeEvtTokenSwapped, tc.expectedSwapEvents)
// suite.AssertEventEmitted(ctx, sdk.EventTypeMessage, tc.expectedMessageEvents)
suite.AssertEventEmitted(ctx, sdk.EventTypeMessage, tc.expectedMessageEvents)
})
}
}
Expand Down
99 changes: 0 additions & 99 deletions x/gamm/keeper/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,6 @@ func (k Keeper) setPool(ctx sdk.Context, pool poolmanagertypes.PoolI) error {
return nil
}

// OverwritePoolV15MigrationUnsafe is a temporary method for calling from the v15 upgrade handler
// for balancer to stableswap pool migration. Do not use for other purposes.
func (k Keeper) OverwritePoolV15MigrationUnsafe(ctx sdk.Context, pool poolmanagertypes.PoolI) error {
return k.setPool(ctx, pool)
}

func (k Keeper) DeletePool(ctx sdk.Context, poolId uint64) error {
store := ctx.KVStore(k.storeKey)
poolKey := types.GetKeyPrefixPools(poolId)
Expand All @@ -121,98 +115,6 @@ func (k Keeper) DeletePool(ctx sdk.Context, poolId uint64) error {
return nil
}

// CleanupBalancerPool destructs a pool and refund all the assets according to
// the shares held by the accounts. CleanupBalancerPool should not be called during
// the chain execution time, as it iterates the entire account balances.
// TODO: once SDK v0.46.0, use https://github.com/cosmos/cosmos-sdk/pull/9611
//
// All locks on this pool share must be unlocked prior to execution. Use LockupKeeper.ForceUnlock
// on remaining locks before calling this function.
// func (k Keeper) CleanupBalancerPool(ctx sdk.Context, poolIds []uint64, excludedModules []string) (err error) {
// pools := make(map[string]types.CFMMPoolI)
// totalShares := make(map[string]sdk.Int)
// for _, poolId := range poolIds {
// pool, err := k.GetPool(ctx, poolId)
// if err != nil {
// return err
// }
// shareDenom := pool.GetTotalShares().Denom
// pools[shareDenom] = pool
// totalShares[shareDenom] = pool.GetTotalShares().Amount
// }

// moduleAccounts := make(map[string]string)
// for _, module := range excludedModules {
// moduleAccounts[string(authtypes.NewModuleAddress(module))] = module
// }

// // first iterate through the share holders and burn them
// k.bankKeeper.IterateAllBalances(ctx, func(addr sdk.AccAddress, coin sdk.Coin) (stop bool) {
// if coin.Amount.IsZero() {
// return
// }

// pool, ok := pools[coin.Denom]
// if !ok {
// return
// }

// // track the iterated shares
// pool.SubTotalShares(coin.Amount)
// pools[coin.Denom] = pool

// // check if the shareholder is a module
// if _, ok = moduleAccounts[coin.Denom]; ok {
// return
// }

// // Burn the share tokens
// err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, sdk.Coins{coin})
// if err != nil {
// return true
// }

// err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.Coins{coin})
// if err != nil {
// return true
// }

// // Refund assets
// for _, asset := range pool.GetAllPoolAssets() {
// // lpShareEquivalentTokens = (amount in pool) * (your shares) / (total shares)
// lpShareEquivalentTokens := asset.Token.Amount.Mul(coin.Amount).Quo(totalShares[coin.Denom])
// if lpShareEquivalentTokens.IsZero() {
// continue
// }
// err = k.bankKeeper.SendCoins(
// ctx, pool.GetAddress(), addr, sdk.Coins{{asset.Token.Denom, lpShareEquivalentTokens}})
// if err != nil {
// return true
// }
// }

// return false
// })

// if err != nil {
// return err
// }

// for _, pool := range pools {
// // sanity check
// if !pool.GetTotalShares().IsZero() {
// panic("pool total share should be zero after cleanup")
// }

// err = k.DeletePool(ctx, pool.GetId())
// if err != nil {
// return err
// }
// }

// return nil
// }

// GetPoolDenom retrieves the pool based on PoolId and
// returns the coin denoms that it holds.
func (k Keeper) GetPoolDenoms(ctx sdk.Context, poolId uint64) ([]string, error) {
Expand Down Expand Up @@ -271,7 +173,6 @@ func (k Keeper) GetPoolType(ctx sdk.Context, poolId uint64) (poolmanagertypes.Po
// convertToCFMMPool converts PoolI to CFMMPoolI by casting the input.
// Returns the pool of the CFMMPoolI or error if the given pool does not implement
// CFMMPoolI.
// nolint: unused
func convertToCFMMPool(pool poolmanagertypes.PoolI) (types.CFMMPoolI, error) {
cfmmPool, ok := pool.(types.CFMMPoolI)
if !ok {
Expand Down
Loading

0 comments on commit c3f5b51

Please sign in to comment.