-
Notifications
You must be signed in to change notification settings - Fork 44
/
calc_out_route_spot_price.go
113 lines (88 loc) · 4.1 KB
/
calc_out_route_spot_price.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/amm/types"
)
// CalcOutRouteSpotPrice calculates the spot price of the given token and out route
func (k Keeper) CalcOutRouteSpotPrice(ctx sdk.Context, tokenOut sdk.Coin, routes []*types.SwapAmountOutRoute, discount sdk.Dec, overrideSwapFee sdk.Dec) (sdk.Dec, sdk.Dec, sdk.Coin, sdk.Dec, sdk.Dec, sdk.Coin, sdk.Dec, sdk.Dec, error) {
if len(routes) == 0 {
return sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), types.ErrEmptyRoutes
}
// Start with the initial token input
tokensOut := sdk.Coins{tokenOut}
// The final token in denom
var tokenInDenom string
// Initialize the total discounted swap fee
totalDiscountedSwapFee := sdk.ZeroDec()
// Track the total available liquidity in the pool for final token out denom
var availableLiquidity sdk.Coin
weightBonus := sdk.ZeroDec()
slippage := sdk.ZeroDec()
for _, route := range routes {
poolId := route.PoolId
tokenInDenom = route.TokenInDenom
pool, found := k.GetPool(ctx, poolId)
if !found {
return sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), types.ErrPoolNotFound
}
// Get Pool swap fee
swapFee := pool.GetPoolParams().SwapFee
// Override swap fee if applicable
if overrideSwapFee.IsPositive() {
swapFee = overrideSwapFee
}
// Apply discount
swapFee = types.ApplyDiscount(swapFee, discount)
// Calculate the total discounted swap fee
totalDiscountedSwapFee = totalDiscountedSwapFee.Add(swapFee)
// Estimate swap
snapshot := k.GetPoolSnapshotOrSet(ctx, pool)
cacheCtx, _ := ctx.CacheContext()
swapResult, swapSlippage, _, weightBalanceBonus, err := k.SwapInAmtGivenOut(cacheCtx, pool.PoolId, k.oracleKeeper, &snapshot, tokensOut, tokenInDenom, swapFee)
if err != nil {
return sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
if swapResult.IsZero() {
return sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), types.ErrAmountTooLow
}
// Use the current swap result as the input for the next iteration
tokensOut = sdk.Coins{swapResult}
// Get the available liquidity for the final token in denom
_, poolAsset, err := pool.GetPoolAssetAndIndex(tokenInDenom)
if err != nil {
return sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
availableLiquidity = poolAsset.Token
weightBonus = weightBonus.Add(weightBalanceBonus)
slippage = slippage.Add(swapSlippage)
}
// Ensure tokenIn.Amount is not zero to avoid division by zero
if tokenOut.IsZero() {
return sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), types.ErrAmountTooLow
}
// Calculate the spot price given the initial token in and the final token in
impactedPrice := sdk.NewDecFromInt(tokensOut[0].Amount).Quo(sdk.NewDecFromInt(tokenOut.Amount))
// Calculate spot price with GetTokenARate
spotPrice := sdk.OneDec()
tokenOutDenom := tokenOut.Denom
for _, route := range routes {
poolId := route.PoolId
tokenInDenom = route.TokenInDenom
pool, found := k.GetPool(ctx, poolId)
if !found {
return sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), types.ErrPoolNotFound
}
// Estimate swap
snapshot := k.GetPoolSnapshotOrSet(ctx, pool)
rate, err := pool.GetTokenARate(ctx, k.oracleKeeper, &snapshot, tokenInDenom, tokenOutDenom, k.accountedPoolKeeper)
if err != nil {
return sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), err
}
// set new tokenIn denom for multihop
tokenOutDenom = tokenInDenom
spotPrice = spotPrice.Mul(rate)
}
// Calculate the token in amount
tokenIn := tokensOut[0]
return spotPrice, impactedPrice, tokenIn, totalDiscountedSwapFee, discount, availableLiquidity, slippage, weightBonus, nil
}