/
multihop.go
98 lines (82 loc) · 2.9 KB
/
multihop.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
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/osmosis-labs/osmosis/v8/x/gamm/types"
)
// MultihopSwapExactAmountIn 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) MultihopSwapExactAmountIn(
ctx sdk.Context,
sender sdk.AccAddress,
routes []types.SwapAmountInRoute,
tokenIn sdk.Coin,
tokenOutMinAmount sdk.Int,
) (tokenOutAmount sdk.Int, err error) {
for i, route := range routes {
_outMinAmount := sdk.NewInt(1)
if len(routes)-1 == i {
_outMinAmount = tokenOutMinAmount
}
tokenOutAmount, _, err = k.SwapExactAmountIn(ctx, sender, route.PoolId, tokenIn, route.TokenOutDenom, _outMinAmount)
if err != nil {
return sdk.Int{}, err
}
tokenIn = sdk.NewCoin(route.TokenOutDenom, tokenOutAmount)
}
return
}
// MultihopSwapExactAmountOut 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) MultihopSwapExactAmountOut(
ctx sdk.Context,
sender sdk.AccAddress,
routes []types.SwapAmountOutRoute,
tokenInMaxAmount sdk.Int,
tokenOut sdk.Coin,
) (tokenInAmount sdk.Int, err error) {
insExpected, err := k.createMultihopExpectedSwapOuts(ctx, routes, tokenOut)
if err != nil {
return sdk.Int{}, err
}
insExpected[0] = tokenInMaxAmount
for i, route := range routes {
_tokenOut := tokenOut
if i != len(routes)-1 {
_tokenOut = sdk.NewCoin(routes[i+1].TokenInDenom, insExpected[i+1])
}
_tokenInAmount, _, err := k.SwapExactAmountOut(ctx, sender, route.PoolId, route.TokenInDenom, insExpected[i], _tokenOut)
if err != nil {
return sdk.Int{}, err
}
if i == 0 {
tokenInAmount = _tokenInAmount
}
}
return
}
// TODO: Document this function
func (k Keeper) createMultihopExpectedSwapOuts(ctx sdk.Context, routes []types.SwapAmountOutRoute, tokenOut sdk.Coin) ([]sdk.Int, error) {
insExpected := make([]sdk.Int, len(routes))
for i := len(routes) - 1; i >= 0; i-- {
route := routes[i]
pool, inAsset, outAsset, err :=
k.getPoolAndInOutAssets(ctx, route.PoolId, route.TokenInDenom, tokenOut.Denom)
if err != nil {
return nil, err
}
tokenInAmount := calcInGivenOut(
inAsset.Token.Amount.ToDec(),
inAsset.Weight.ToDec(),
outAsset.Token.Amount.ToDec(),
outAsset.Weight.ToDec(),
tokenOut.Amount.ToDec(),
pool.GetPoolSwapFee(),
).TruncateInt()
insExpected[i] = tokenInAmount
tokenOut = sdk.NewCoin(route.TokenInDenom, tokenInAmount)
}
return insExpected, nil
}