Skip to content

Commit

Permalink
feat: add price impact calc to swap-estimation-by-denom (#315)
Browse files Browse the repository at this point in the history
* feat: add price impact calc to swap-estimation-by-denom

* fix: lowest amount to fix
  • Loading branch information
cosmic-vagabond authored Dec 20, 2023
1 parent a422efa commit 025ff94
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 120 deletions.
4 changes: 4 additions & 0 deletions docs/static/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38404,6 +38404,8 @@ paths:
signatures required by gogoproto.
weight_balance_ratio:
type: string
price_impact:
type: string
default:
description: An unexpected error response.
schema:
Expand Down Expand Up @@ -82978,6 +82980,8 @@ definitions:
signatures required by gogoproto.
weight_balance_ratio:
type: string
price_impact:
type: string
elys.amm.QuerySwapEstimationResponse:
type: object
properties:
Expand Down
1 change: 1 addition & 0 deletions proto/elys/amm/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ message QuerySwapEstimationByDenomResponse {
string discount = 6 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin available_liquidity = 7 [(gogoproto.nullable) = false ] ;
string weight_balance_ratio = 8 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
string price_impact = 9 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
}

message QueryAMMPriceRequest {
Expand Down
37 changes: 30 additions & 7 deletions x/amm/keeper/calc_swap_estimation_by_denom.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import (
"github.com/elys-network/elys/x/amm/types"
)

const (
lowestAmountForInitialSpotPriceCalc = 10 // lowest amount to use for initial spot price calculation
)

// CalcSwapEstimationByDenom calculates the swap estimation by denom
func (k Keeper) CalcSwapEstimationByDenom(
ctx sdk.Context,
Expand All @@ -23,34 +27,53 @@ func (k Keeper) CalcSwapEstimationByDenom(
discountOut sdk.Dec,
availableLiquidity sdk.Coin,
weightBonus sdk.Dec,
priceImpact sdk.Dec,
err error,
) {
// if amount denom is equal to denomIn, calculate swap estimation by denomIn
if amount.Denom == denomIn {
inRoute, err := k.CalcInRouteByDenom(ctx, denomIn, denomOut, baseCurrency)
if err != nil {
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), err
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
initialSpotPrice, _, _, _, _, _, err := k.CalcInRouteSpotPrice(ctx, sdk.NewInt64Coin(amount.Denom, lowestAmountForInitialSpotPriceCalc), inRoute, discount, overrideSwapFee)
if err != nil {
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
// check if initialSpotPrice is zero to avoid division by zero
if initialSpotPrice.IsZero() {
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), types.ErrInitialSpotPriceIsZero
}
spotPrice, tokenOut, swapFeeOut, _, availableLiquidity, weightBonus, err := k.CalcInRouteSpotPrice(ctx, amount, inRoute, discount, overrideSwapFee)
if err != nil {
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), err
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
return inRoute, nil, tokenOut, spotPrice, swapFeeOut, discount, availableLiquidity, weightBonus, nil
priceImpact := initialSpotPrice.Sub(spotPrice).Quo(initialSpotPrice)
return inRoute, nil, tokenOut, spotPrice, swapFeeOut, discount, availableLiquidity, weightBonus, priceImpact, nil
}

// if amount denom is equal to denomOut, calculate swap estimation by denomOut
if amount.Denom == denomOut {
outRoute, err := k.CalcOutRouteByDenom(ctx, denomOut, denomIn, baseCurrency)
if err != nil {
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), err
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
initialSpotPrice, _, _, _, _, _, err := k.CalcOutRouteSpotPrice(ctx, sdk.NewInt64Coin(amount.Denom, lowestAmountForInitialSpotPriceCalc), outRoute, discount, overrideSwapFee)
if err != nil {
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
// check if initialSpotPrice is zero to avoid division by zero
if initialSpotPrice.IsZero() {
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), types.ErrInitialSpotPriceIsZero
}
spotPrice, tokenIn, swapFeeOut, _, availableLiquidity, weightBonus, err := k.CalcOutRouteSpotPrice(ctx, amount, outRoute, discount, overrideSwapFee)
if err != nil {
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), err
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
return nil, outRoute, tokenIn, spotPrice, swapFeeOut, discount, availableLiquidity, weightBonus, nil
priceImpact := initialSpotPrice.Sub(spotPrice).Quo(initialSpotPrice)
return nil, outRoute, tokenIn, spotPrice, swapFeeOut, discount, availableLiquidity, weightBonus, priceImpact, nil
}

// if amount denom is neither equal to denomIn nor denomOut, return error
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), types.ErrInvalidDenom
return nil, nil, sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), types.ErrInvalidDenom
}
6 changes: 3 additions & 3 deletions x/amm/keeper/calc_swap_estimation_by_denom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (suite *KeeperTestSuite) TestCalcSwapEstimationByDenom() {
suite.app.AmmKeeper.SetPool(suite.ctx, pool2)

amount := sdk.NewCoin(ptypes.Elys, sdk.NewInt(100))
inRoute, outRoute, tokenOut, spotPrice, _, _, _, _, err := suite.app.AmmKeeper.CalcSwapEstimationByDenom(
inRoute, outRoute, tokenOut, spotPrice, _, _, _, _, _, err := suite.app.AmmKeeper.CalcSwapEstimationByDenom(
suite.ctx,
amount,
ptypes.Elys, "uusda", ptypes.BaseCurrency,
Expand All @@ -105,7 +105,7 @@ func (suite *KeeperTestSuite) TestCalcSwapEstimationByDenom() {
suite.Require().NotZero(spotPrice)

amount = sdk.NewCoin("uusda", sdk.NewInt(100))
inRoute, outRoute, tokenOut, spotPrice, _, _, _, _, err = suite.app.AmmKeeper.CalcSwapEstimationByDenom(
inRoute, outRoute, tokenOut, spotPrice, _, _, _, _, _, err = suite.app.AmmKeeper.CalcSwapEstimationByDenom(
suite.ctx,
amount,
ptypes.Elys, "uusda", ptypes.BaseCurrency,
Expand All @@ -119,7 +119,7 @@ func (suite *KeeperTestSuite) TestCalcSwapEstimationByDenom() {

// Test no routes
amount = sdk.NewCoin("invalid", sdk.NewInt(1000))
_, _, _, _, _, _, _, _, err = suite.app.AmmKeeper.CalcSwapEstimationByDenom(
_, _, _, _, _, _, _, _, _, err = suite.app.AmmKeeper.CalcSwapEstimationByDenom(
suite.ctx, amount,
ptypes.Elys, "uusda", ptypes.BaseCurrency,
sdk.ZeroDec(),
Expand Down
2 changes: 1 addition & 1 deletion x/amm/keeper/msg_server_swap_by_denom.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (k msgServer) SwapByDenom(goCtx context.Context, msg *types.MsgSwapByDenom)
}
baseCurrency := entry.Denom

inRoute, outRoute, _, spotPrice, _, _, _, _, err := k.CalcSwapEstimationByDenom(ctx, msg.Amount, msg.DenomIn, msg.DenomOut, baseCurrency, msg.Discount, sdk.ZeroDec())
inRoute, outRoute, _, spotPrice, _, _, _, _, _, err := k.CalcSwapEstimationByDenom(ctx, msg.Amount, msg.DenomIn, msg.DenomOut, baseCurrency, msg.Discount, sdk.ZeroDec())
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion x/amm/keeper/query_swap_estimation_by_denom.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (k Keeper) SwapEstimationByDenom(goCtx context.Context, req *types.QuerySwa

_ = baseCurrency

inRoute, outRoute, amount, spotPrice, swapFee, discount, availableLiquidity, weightBonus, err := k.CalcSwapEstimationByDenom(ctx, req.Amount, req.DenomIn, req.DenomOut, baseCurrency, req.Discount, sdk.ZeroDec())
inRoute, outRoute, amount, spotPrice, swapFee, discount, availableLiquidity, weightBonus, priceImpact, err := k.CalcSwapEstimationByDenom(ctx, req.Amount, req.DenomIn, req.DenomOut, baseCurrency, req.Discount, sdk.ZeroDec())
if err != nil {
return nil, err
}
Expand All @@ -41,5 +41,6 @@ func (k Keeper) SwapEstimationByDenom(goCtx context.Context, req *types.QuerySwa
Discount: discount,
AvailableLiquidity: availableLiquidity,
WeightBalanceRatio: weightBonus,
PriceImpact: priceImpact,
}, nil
}
11 changes: 6 additions & 5 deletions x/amm/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ var (
ErrInvalidPoolId = sdkerrors.Register(ModuleName, 91, "invalid pool id")
ErrInvalidSwapMsgType = sdkerrors.Register(ModuleName, 92, "unexpected swap message type")

ErrSameDenom = sdkerrors.Register(ModuleName, 101, "denom in and denom out are the same")
ErrPoolNotFound = sdkerrors.Register(ModuleName, 102, "pool not found")
ErrAmountTooLow = sdkerrors.Register(ModuleName, 103, "amount too low")
ErrInvalidDenom = sdkerrors.Register(ModuleName, 104, "invalid denom")
ErrInvalidDiscount = sdkerrors.Register(ModuleName, 105, "invalid discount")
ErrSameDenom = sdkerrors.Register(ModuleName, 101, "denom in and denom out are the same")
ErrPoolNotFound = sdkerrors.Register(ModuleName, 102, "pool not found")
ErrAmountTooLow = sdkerrors.Register(ModuleName, 103, "amount too low")
ErrInvalidDenom = sdkerrors.Register(ModuleName, 104, "invalid denom")
ErrInvalidDiscount = sdkerrors.Register(ModuleName, 105, "invalid discount")
ErrInitialSpotPriceIsZero = sdkerrors.Register(ModuleName, 106, "initial spot price is zero")
)

const (
Expand Down
Loading

0 comments on commit 025ff94

Please sign in to comment.