-
Notifications
You must be signed in to change notification settings - Fork 627
/
Copy pathrouter.go
158 lines (132 loc) · 5.57 KB
/
router.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package swaprouter
import (
sdk "github.com/cosmos/cosmos-sdk/types"
gammtypes "github.com/osmosis-labs/osmosis/v12/x/gamm/types"
"github.com/osmosis-labs/osmosis/v12/x/swaprouter/types"
)
// RouteExactAmountIn defines the input denom and input amount for the first pool,
// the output of the first pool is chained as the input for the next routed pool
// transaction succeeds when final amount out is greater than tokenOutMinAmount defined.
func (k Keeper) RouteExactAmountIn(
ctx sdk.Context,
sender sdk.AccAddress,
routes []types.SwapAmountInRoute,
tokenIn sdk.Coin,
tokenOutMinAmount sdk.Int) (tokenOutAmount sdk.Int, err error) {
// TODO: fix this once proper pool id routing exists
isGamm := true
swapModule := k.withSwapModule(isGamm)
for i, route := range routes {
swapFeeMultiplier := sdk.OneDec()
if types.SwapAmountInRoutes(routes).IsOsmoRoutedMultihop() {
swapFeeMultiplier = gammtypes.MultihopSwapFeeMultiplierForOsmoPools.Clone()
}
// To prevent the multihop swap from being interrupted prematurely, we keep
// the minimum expected output at a very low number until the last pool
_outMinAmount := sdk.NewInt(1)
if len(routes)-1 == i {
_outMinAmount = tokenOutMinAmount
}
// Execute the expected swap on the current routed pool
pool, poolErr := swapModule.GetPool(ctx, route.PoolId)
if poolErr != nil {
return sdk.Int{}, poolErr
}
swapFee := pool.GetSwapFee(ctx).Mul(swapFeeMultiplier)
tokenOutAmount, err = swapModule.SwapExactAmountIn(ctx, sender, pool, tokenIn, route.TokenOutDenom, _outMinAmount, swapFee)
if err != nil {
return sdk.Int{}, err
}
// Chain output of current pool as the input for the next routed pool
tokenIn = sdk.NewCoin(route.TokenOutDenom, tokenOutAmount)
}
return tokenOutAmount, err
}
// RouteExactAmountOut defines the output denom and output amount for the last pool.
// Calculation starts by providing the tokenOutAmount of the final pool to calculate the required tokenInAmount
// the calculated tokenInAmount is used as defined tokenOutAmount of the previous pool, calculating in reverse order of the swap
// Transaction succeeds if the calculated tokenInAmount of the first pool is less than the defined tokenInMaxAmount defined.
func (k Keeper) RouteExactAmountOut(ctx sdk.Context,
sender sdk.AccAddress,
routes []types.SwapAmountOutRoute,
tokenInMaxAmount sdk.Int,
tokenOut sdk.Coin) (tokenInAmount sdk.Int, err error) {
// TODO: fix this once proper pool id routing exists
isGamm := true
swapModule := k.withSwapModule(isGamm)
swapFeeMultiplier := sdk.OneDec()
if types.SwapAmountOutRoutes(routes).IsOsmoRoutedMultihop() {
swapFeeMultiplier = gammtypes.MultihopSwapFeeMultiplierForOsmoPools.Clone()
}
// Determine what the estimated input would be for each pool along the multihop route
insExpected, err := createMultihopExpectedSwapOuts(ctx, swapModule, routes, tokenOut, swapFeeMultiplier)
if err != nil {
return sdk.Int{}, err
}
if len(insExpected) == 0 {
return sdk.Int{}, nil
}
insExpected[0] = tokenInMaxAmount
// Iterates through each routed pool and executes their respective swaps. Note that all of the work to get the return
// value of this method is done when we calculate insExpected – this for loop primarily serves to execute the actual
// swaps on each pool.
for i, route := range routes {
_tokenOut := tokenOut
// If there is one pool left in the route, set the expected output of the current swap
// to the estimated input of the final pool.
if i != len(routes)-1 {
_tokenOut = sdk.NewCoin(routes[i+1].TokenInDenom, insExpected[i+1])
}
// Execute the expected swap on the current routed pool
pool, poolErr := swapModule.GetPool(ctx, route.PoolId)
if poolErr != nil {
return sdk.Int{}, poolErr
}
swapFee := pool.GetSwapFee(ctx).Mul(swapFeeMultiplier)
_tokenInAmount, swapErr := swapModule.SwapExactAmountOut(ctx, sender, pool, route.TokenInDenom, insExpected[i], _tokenOut, swapFee)
if swapErr != nil {
return sdk.Int{}, swapErr
}
// Sets the final amount of tokens that need to be input into the first pool. Even though this is the final return value for the
// whole method and will not change after the first iteration, we still iterate through the rest of the pools to execute their respective
// swaps.
if i == 0 {
tokenInAmount = _tokenInAmount
}
}
return tokenInAmount, nil
}
// createMultihopExpectedSwapOuts defines the output denom and output amount for the last pool in
// the route of pools the caller is intending to hop through in a fixed-output multihop tx. It estimates the input
// amount for this last pool and then chains that input as the output of the previous pool in the route, repeating
// until the first pool is reached. It returns an array of inputs, each of which correspond to a pool ID in the
// route of pools for the original multihop transaction.
// TODO: test this.
func createMultihopExpectedSwapOuts(
ctx sdk.Context,
swapModule types.SwapI,
routes []types.SwapAmountOutRoute,
tokenOut sdk.Coin, swapFeeMultiplier sdk.Dec,
) ([]sdk.Int, error) {
insExpected := make([]sdk.Int, len(routes))
for i := len(routes) - 1; i >= 0; i-- {
route := routes[i]
pool, err := swapModule.GetPool(ctx, route.PoolId)
if err != nil {
return nil, err
}
tokenIn, err := pool.CalcInAmtGivenOut(ctx, sdk.NewCoins(tokenOut), route.TokenInDenom, pool.GetSwapFee(ctx).Mul(swapFeeMultiplier))
if err != nil {
return nil, err
}
insExpected[i] = tokenIn.Amount
tokenOut = tokenIn
}
return insExpected, nil
}
func (k Keeper) withSwapModule(isGamm bool) types.SwapI {
if isGamm {
return k.gammKeeper
}
return k.concentratedKeeper
}