Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add slippage on join/exit pool - oracle pool #179

Merged
merged 3 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 5 additions & 61 deletions x/amm/keeper/abci.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package keeper

import (
"fmt"
"strings"
"time"

Expand All @@ -22,9 +21,8 @@ func (k Keeper) GetStackedSlippage(ctx sdk.Context, poolId uint64) sdk.Dec {
}

func (k Keeper) ApplySwapRequest(ctx sdk.Context, msg sdk.Msg) error {
switch msg.(type) {
switch msg := msg.(type) {
case *types.MsgSwapExactAmountIn:
msg := msg.(*types.MsgSwapExactAmountIn)
sender, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return err
Expand All @@ -35,7 +33,6 @@ func (k Keeper) ApplySwapRequest(ctx sdk.Context, msg sdk.Msg) error {
}
return nil
case *types.MsgSwapExactAmountOut:
msg := msg.(*types.MsgSwapExactAmountOut)
sender, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return err
Expand All @@ -46,17 +43,15 @@ func (k Keeper) ApplySwapRequest(ctx sdk.Context, msg sdk.Msg) error {
}
return nil
default:
return fmt.Errorf("unexpected swap message")
return types.ErrInvalidSwapMsgType
}
}

func (k Keeper) DeleteSwapRequest(ctx sdk.Context, msg sdk.Msg, index uint64) {
switch msg.(type) {
switch msg := msg.(type) {
case *types.MsgSwapExactAmountIn:
msg := msg.(*types.MsgSwapExactAmountIn)
k.DeleteSwapExactAmountInRequest(ctx, msg, index)
case *types.MsgSwapExactAmountOut:
msg := msg.(*types.MsgSwapExactAmountOut)
k.DeleteSwapExactAmountOutRequest(ctx, msg, index)
}
}
Expand All @@ -72,12 +67,10 @@ func (k Keeper) SelectOneSwapRequest(ctx sdk.Context, sprefix []byte) (sdk.Msg,

func (k Keeper) SelectReverseSwapRequest(ctx sdk.Context, msg sdk.Msg) (sdk.Msg, uint64) {
sprefix := []byte{}
switch msg.(type) {
switch msg := msg.(type) {
case *types.MsgSwapExactAmountIn:
msg := msg.(*types.MsgSwapExactAmountIn)
sprefix = types.TKeyPrefixSwapExactAmountInPrefix(msg)
case *types.MsgSwapExactAmountOut:
msg := msg.(*types.MsgSwapExactAmountOut)
sprefix = types.TKeyPrefixSwapExactAmountOutPrefix(msg)
}

Expand All @@ -90,12 +83,10 @@ func (k Keeper) SelectReverseSwapRequest(ctx sdk.Context, msg sdk.Msg) (sdk.Msg,
}

func (k Keeper) FirstPoolId(msg sdk.Msg) uint64 {
switch msg.(type) {
switch msg := msg.(type) {
case *types.MsgSwapExactAmountIn:
msg := msg.(*types.MsgSwapExactAmountIn)
return types.FirstPoolIdFromSwapExactAmountIn(msg)
case *types.MsgSwapExactAmountOut:
msg := msg.(*types.MsgSwapExactAmountOut)
return types.FirstPoolIdFromSwapExactAmountOut(msg)
}
return 0
Expand Down Expand Up @@ -176,51 +167,4 @@ func (k Keeper) EndBlocker(ctx sdk.Context) {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker)

k.ExecuteSwapRequests(ctx)
// swapInRequests := k.GetAllSwapExactAmountInRequests(ctx)
// for _, msg := range swapInRequests {
// sender, err := sdk.AccAddressFromBech32(msg.Sender)
// if err != nil {
// continue
// }

// cacheCtx, write := ctx.CacheContext()
// _, err = k.RouteExactAmountIn(cacheCtx, sender, msg.Routes, msg.TokenIn, math.Int(msg.TokenOutMinAmount))
// if err != nil {
// continue
// }
// write()

// // Swap event is handled elsewhere
// ctx.EventManager().EmitEvents(sdk.Events{
// sdk.NewEvent(
// sdk.EventTypeMessage,
// sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
// sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
// ),
// })

// }
// swapOutRequests := k.GetAllSwapExactAmountOutRequests(ctx)
// for _, msg := range swapOutRequests {
// sender, err := sdk.AccAddressFromBech32(msg.Sender)
// if err != nil {
// continue
// }

// cacheCtx, write := ctx.CacheContext()
// _, err = k.RouteExactAmountOut(cacheCtx, sender, msg.Routes, msg.TokenInMaxAmount, msg.TokenOut)
// if err != nil {
// continue
// }
// write()

// // Swap event is handled elsewhere
// ctx.EventManager().EmitEvents(sdk.Events{
// sdk.NewEvent(
// sdk.EventTypeMessage,
// sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
// sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
// ),
// })
// }
}
4 changes: 1 addition & 3 deletions x/amm/keeper/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,16 +276,14 @@ func (suite *KeeperTestSuite) TestExecuteSwapRequests() {

msgServer := keeper.NewMsgServerImpl(suite.app.AmmKeeper)
for _, msg := range tc.swapMsgs {
switch msg.(type) {
switch msg := msg.(type) {
case *types.MsgSwapExactAmountIn:
msg := msg.(*types.MsgSwapExactAmountIn)
_, err := msgServer.SwapExactAmountIn(
sdk.WrapSDKContext(suite.ctx),
msg,
)
suite.Require().NoError(err)
case *types.MsgSwapExactAmountOut:
msg := msg.(*types.MsgSwapExactAmountOut)
_, err := msgServer.SwapExactAmountOut(
sdk.WrapSDKContext(suite.ctx),
msg,
Expand Down
6 changes: 2 additions & 4 deletions x/amm/keeper/batch_processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ func (k Keeper) GetLastSwapRequestIndex(ctx sdk.Context) uint64 {
}

// SetSwapExactAmountInRequests stores swap exact amount in request
func (k Keeper) SetSwapExactAmountInRequests(ctx sdk.Context, msg *types.MsgSwapExactAmountIn, index uint64) error {
func (k Keeper) SetSwapExactAmountInRequests(ctx sdk.Context, msg *types.MsgSwapExactAmountIn, index uint64) {
store := prefix.NewStore(ctx.TransientStore(k.transientStoreKey), types.KeyPrefix(types.TSwapExactAmountInKey))
b := k.cdc.MustMarshal(msg)
store.Set(types.TKeyPrefixSwapExactAmountIn(msg, index), b)
return nil
}

// DeleteSwapExactAmountInRequest removes a swap exact amount in request
Expand Down Expand Up @@ -65,11 +64,10 @@ func (k Keeper) GetFirstSwapExactAmountInRequest(ctx sdk.Context, sprefix []byte
}

// SetSwapExactAmountInRequests stores swap exact amount out request
func (k Keeper) SetSwapExactAmountOutRequests(ctx sdk.Context, msg *types.MsgSwapExactAmountOut, index uint64) error {
func (k Keeper) SetSwapExactAmountOutRequests(ctx sdk.Context, msg *types.MsgSwapExactAmountOut, index uint64) {
store := prefix.NewStore(ctx.TransientStore(k.transientStoreKey), types.KeyPrefix(types.TSwapExactAmountOutKey))
b := k.cdc.MustMarshal(msg)
store.Set(types.TKeyPrefixSwapExactAmountOut(msg, index), b)
return nil
}

// DeleteSwapExactAmountOutRequest deletes a swap exact amount out request
Expand Down
8 changes: 4 additions & 4 deletions x/amm/keeper/msg_server_exit_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ func (suite *KeeperTestSuite) TestMsgServerExitPool() {
},
shareInAmount: types.OneShare.Quo(sdk.NewInt(10)),
tokenOutDenom: "uusdt",
minAmountsOut: sdk.Coins{sdk.NewInt64Coin("uusdt", 97368)},
expSenderBalance: sdk.Coins{sdk.NewInt64Coin("uusdt", 97368)},
minAmountsOut: sdk.Coins{sdk.NewInt64Coin("uusdt", 95114)},
expSenderBalance: sdk.Coins{sdk.NewInt64Coin("uusdt", 95114)},
expPass: true,
},
{
Expand All @@ -100,8 +100,8 @@ func (suite *KeeperTestSuite) TestMsgServerExitPool() {
},
shareInAmount: types.OneShare.Quo(sdk.NewInt(10)),
tokenOutDenom: "uusdc",
minAmountsOut: sdk.Coins{sdk.NewInt64Coin("uusdc", 100000)},
expSenderBalance: sdk.Coins{sdk.NewInt64Coin("uusdc", 100000)},
minAmountsOut: sdk.Coins{sdk.NewInt64Coin("uusdc", 99197)},
expSenderBalance: sdk.Coins{sdk.NewInt64Coin("uusdc", 99197)},
expPass: true,
},
} {
Expand Down
25 changes: 23 additions & 2 deletions x/amm/keeper/msg_server_join_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (suite *KeeperTestSuite) TestMsgServerJoinPool() {
ThresholdWeightDifference: sdk.NewDecWithPrec(2, 1), // 20%
FeeDenom: "uusdc",
},
shareOutAmount: sdk.NewInt(833333333333333333), // weight breaking fee
shareOutAmount: sdk.NewInt(694444166666666666), // weight breaking fee
expSenderBalance: sdk.Coins{},
expTokenIn: sdk.Coins{sdk.NewInt64Coin("uusdt", 1000000)},
expPass: true,
Expand All @@ -99,11 +99,32 @@ func (suite *KeeperTestSuite) TestMsgServerJoinPool() {
ThresholdWeightDifference: sdk.NewDecWithPrec(2, 1), // 20%
FeeDenom: "uusdc",
},
shareOutAmount: sdk.NewInt(1250000000000000000), // weight breaking fee
shareOutAmount: sdk.NewInt(805987500000000000), // weight recovery direction
expSenderBalance: sdk.Coins{},
expTokenIn: sdk.Coins{sdk.NewInt64Coin("uusdt", 1000000)},
expPass: true,
},
{
desc: "oracle pool join - zero slippage add liquidity",
senderInitBalance: sdk.Coins{sdk.NewInt64Coin("uusdc", 1500000), sdk.NewInt64Coin("uusdt", 500000)},
poolInitBalance: sdk.Coins{sdk.NewInt64Coin("uusdc", 1500000), sdk.NewInt64Coin("uusdt", 500000)},
poolParams: types.PoolParams{
SwapFee: sdk.ZeroDec(),
ExitFee: sdk.ZeroDec(),
UseOracle: true,
WeightBreakingFeeMultiplier: sdk.NewDecWithPrec(1, 0), // 1.00
ExternalLiquidityRatio: sdk.NewDec(1),
LpFeePortion: sdk.ZeroDec(),
StakingFeePortion: sdk.ZeroDec(),
WeightRecoveryFeePortion: sdk.ZeroDec(),
ThresholdWeightDifference: sdk.NewDecWithPrec(2, 1), // 20%
FeeDenom: "uusdc",
},
shareOutAmount: sdk.NewInt(2000000000000000000),
expSenderBalance: sdk.Coins{},
expTokenIn: sdk.Coins{sdk.NewInt64Coin("uusdc", 1500000), sdk.NewInt64Coin("uusdt", 500000)},
expPass: true,
},
} {
suite.Run(tc.desc, func() {
suite.SetupTest()
Expand Down
66 changes: 63 additions & 3 deletions x/amm/types/calc_exit_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,66 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

func CalcExitValueWithoutSlippage(ctx sdk.Context, oracleKeeper OracleKeeper, pool Pool, exitingShares sdk.Int, tokenOutDenom string) (sdk.Dec, error) {
tvl, err := pool.TVL(ctx, oracleKeeper)
if err != nil {
return sdk.ZeroDec(), err
}

totalShares := pool.GetTotalShares()
var refundedShares sdk.Dec
refundedShares = sdk.NewDecFromInt(exitingShares)
exitValue := tvl.Mul(refundedShares).Quo(sdk.NewDecFromInt(totalShares.Amount))

if exitingShares.GTE(totalShares.Amount) {
return sdk.ZeroDec(), sdkerrors.Wrapf(ErrLimitMaxAmount, ErrMsgFormatSharesLargerThanMax, exitingShares, totalShares)
}

shareOutRatio := refundedShares.QuoInt(totalShares.Amount)
// exitedCoins = shareOutRatio * pool liquidity
exitedCoins := sdk.Coins{}
poolLiquidity := pool.GetTotalPoolLiquidity()

for _, asset := range poolLiquidity {
// round down here, due to not wanting to over-exit
exitAmt := shareOutRatio.MulInt(asset.Amount).TruncateInt()
if exitAmt.LTE(sdk.ZeroInt()) {
continue
}
if exitAmt.GTE(asset.Amount) {
return sdk.ZeroDec(), errors.New("too many shares out")
}
exitedCoins = exitedCoins.Add(sdk.NewCoin(asset.Denom, exitAmt))
}

slippageValue := sdk.ZeroDec()
for _, exitedCoin := range exitedCoins {
if exitedCoin.Denom == tokenOutDenom {
continue
}
inTokenPrice := oracleKeeper.GetAssetPriceFromDenom(ctx, exitedCoin.Denom)
if inTokenPrice.IsZero() {
return sdk.ZeroDec(), fmt.Errorf("token price not set: %s", exitedCoin.Denom)
}
resizedAmount := sdk.NewDecFromInt(exitedCoin.Amount).
Quo(pool.PoolParams.ExternalLiquidityRatio).RoundInt()
slippageAmount, err := pool.CalcGivenInSlippage(
ctx,
oracleKeeper,
&pool,
sdk.Coins{sdk.NewCoin(exitedCoin.Denom, resizedAmount)},
tokenOutDenom,
)
if err != nil {
return sdk.ZeroDec(), err
}

slippageValue = slippageValue.Add(slippageAmount.Mul(inTokenPrice))
}
exitValueWithoutSlippage := exitValue.Sub(slippageValue)
return exitValueWithoutSlippage, nil
}

// CalcExitPool returns how many tokens should come out, when exiting k LP shares against a "standard" CFMM
func CalcExitPool(ctx sdk.Context, oracleKeeper OracleKeeper, pool Pool, exitingShares sdk.Int, tokenOutDenom string) (sdk.Coins, error) {
totalShares := pool.GetTotalShares()
Expand All @@ -27,13 +87,13 @@ func CalcExitPool(ctx sdk.Context, oracleKeeper OracleKeeper, pool Pool, exiting

if pool.PoolParams.UseOracle && tokenOutDenom != "" {
initialWeightDistance := pool.WeightDistanceFromTarget(ctx, oracleKeeper, pool.PoolAssets)
tvl, err := pool.TVL(ctx, oracleKeeper)
tokenPrice := oracleKeeper.GetAssetPriceFromDenom(ctx, tokenOutDenom)
exitValueWithoutSlippage, err := CalcExitValueWithoutSlippage(ctx, oracleKeeper, pool, exitingShares, tokenOutDenom)
if err != nil {
return sdk.Coins{}, err
}

tokenPrice := oracleKeeper.GetAssetPriceFromDenom(ctx, tokenOutDenom)
oracleOutAmount := tvl.Mul(refundedShares).Quo(sdk.NewDecFromInt(totalShares.Amount)).Quo(tokenPrice)
oracleOutAmount := exitValueWithoutSlippage.Quo(tokenPrice)

newAssetPools, err := pool.NewPoolAssetsAfterSwap(
sdk.Coins{},
Expand Down
3 changes: 2 additions & 1 deletion x/amm/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ var (

ErrTooManyTokensOut = sdkerrors.Register(ModuleName, 31, "tx is trying to get more tokens out of the pool than exist")

ErrInvalidPoolId = sdkerrors.Register(ModuleName, 91, "invalid pool id")
ErrInvalidPoolId = sdkerrors.Register(ModuleName, 91, "invalid pool id")
ErrInvalidSwapMsgType = sdkerrors.Register(ModuleName, 92, "unexpected swap message type")
)

const (
Expand Down
16 changes: 6 additions & 10 deletions x/amm/types/key_batch_txs.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
package types

import (
"encoding/binary"
fmt "fmt"
"strings"

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

var _ binary.ByteOrder

const (
TLastSwapRequestIndex = "last-swap-request-index"
TSwapExactAmountInKey = "batch/swap-exact-amount-in"
TSwapExactAmountOutKey = "batch/swap-exact-amount-out"
)

func TKeyPrefixSwapExactAmountInPrefix(m *MsgSwapExactAmountIn) []byte {
prefix := []byte(m.TokenIn.Denom + "/")
prefix := []byte(fmt.Sprintf("%s/", m.TokenIn.Denom))
routeKeys := []string{}
for _, route := range m.Routes[:1] {
routeKeys = append(routeKeys, fmt.Sprintf("%d/%s", route.PoolId, route.TokenOutDenom))
Expand All @@ -27,8 +24,8 @@ func TKeyPrefixSwapExactAmountInPrefix(m *MsgSwapExactAmountIn) []byte {
}

func FirstPoolIdFromSwapExactAmountIn(m *MsgSwapExactAmountIn) uint64 {
for _, route := range m.Routes {
return route.PoolId
if len(m.Routes) > 0 {
return m.Routes[0].PoolId
}
return 0
}
Expand All @@ -39,12 +36,11 @@ func TKeyPrefixSwapExactAmountIn(m *MsgSwapExactAmountIn, index uint64) []byte {
}

func TKeyPrefixSwapExactAmountOutPrefix(m *MsgSwapExactAmountOut) []byte {
prefix := []byte("/" + m.TokenOut.Denom)
prefix := []byte(fmt.Sprintf("/%s", m.TokenOut.Denom))
routeKeys := []string{}
for i := len(m.Routes) - 1; i >= 0; i-- {
route := m.Routes[i]
if len(m.Routes) > 0 {
route := m.Routes[len(m.Routes)-1]
routeKeys = append(routeKeys, fmt.Sprintf("%s/%d", route.TokenInDenom, route.PoolId))
break
}
prefix = append([]byte(strings.Join(routeKeys, "/")), prefix...)
return prefix
Expand Down
Loading
Loading