swap_inner.gno
3.23 Kb · 124 lines
1package router
2
3import (
4 "gno.land/r/gnoswap/access/v1"
5 "gno.land/r/gnoswap/router"
6
7 i256 "gno.land/p/gnoswap/int256/v1"
8 u256 "gno.land/p/gnoswap/uint256/v1"
9 "gno.land/p/gnoswap/utils/v1"
10
11 pl "gno.land/r/gnoswap/pool"
12)
13
14const (
15 MIN_SQRT_RATIO_PLUS_ONE string = "4295128740"
16 MAX_SQRT_RATIO_MINUS_ONE string = "1461446703485210103287273052203988822378723970341"
17)
18
19// swapInner executes the core swap logic by interacting with the pool contract.
20// Returns poolRecv (tokens received by pool) and poolOut (tokens sent by pool).
21func (r *routerV1) swapInner(
22 _ int,
23 rlm realm,
24 amountSpecified int64,
25 recipient address,
26 sqrtPriceLimitX96 *u256.Uint,
27 data SwapCallbackData,
28) (int64, int64) {
29 token0Path, token1Path := data.tokenIn, data.tokenOut
30 zeroForOne := data.tokenIn < data.tokenOut
31
32 if !zeroForOne {
33 token0Path, token1Path = token1Path, token0Path
34 }
35
36 sqrtPriceLimitStr := calculateSqrtPriceLimitToStr(zeroForOne, sqrtPriceLimitX96)
37
38 amount0Str, amount1Str := pl.Swap(
39 cross(rlm),
40 token0Path,
41 token1Path,
42 data.fee,
43 recipient,
44 zeroForOne,
45 utils.FormatInt(amountSpecified),
46 sqrtPriceLimitStr,
47 func(cur realm, amount0Delta, amount1Delta int64, _ *pl.CallbackMarker) error {
48 // assert is pool to prevent unauthorized callback
49 caller := cur.Previous().Address()
50 access.AssertIsPool(caller)
51
52 return router.SwapCallback(cross(cur), token0Path, token1Path, amount0Delta, amount1Delta, data.payer)
53 },
54 )
55
56 amount0 := i256.MustFromDecimal(amount0Str)
57 amount1 := i256.MustFromDecimal(amount1Str)
58
59 poolOut, poolRecv := i256MinMax(amount0, amount1)
60 if poolRecv.IsOverflow() || poolOut.IsOverflow() {
61 panic("overflow in swapInner")
62 }
63
64 return poolRecv.Int64(), poolOut.Int64()
65}
66
67// swapDryInner performs a dry-run of a swap operation without executing it.
68func (r *routerV1) swapDryInner(
69 amountSpecified int64,
70 sqrtPriceLimitX96 *u256.Uint,
71 data SwapCallbackData,
72) (int64, int64) {
73 token0Path, token1Path := data.tokenIn, data.tokenOut
74 zeroForOne := data.tokenIn < data.tokenOut
75
76 if !zeroForOne {
77 token0Path, token1Path = token1Path, token0Path
78 }
79
80 sqrtPriceLimitStr := calculateSqrtPriceLimitToStr(zeroForOne, sqrtPriceLimitX96)
81
82 // check possible
83 amount0Str, amount1Str, err := pl.DrySwap(
84 token0Path,
85 token1Path,
86 data.fee,
87 zeroForOne,
88 utils.FormatInt(amountSpecified),
89 sqrtPriceLimitStr,
90 )
91 if err != nil {
92 panic(err)
93 }
94
95 amount0 := i256.MustFromDecimal(amount0Str)
96 amount1 := i256.MustFromDecimal(amount1Str)
97 if amount0.Sign() <= 0 && amount1.Sign() <= 0 {
98 panic(makeErrorWithDetails(
99 errInsufficientLiquidity,
100 "swap entirely within 0-liquidity region",
101 ))
102 }
103
104 poolOut, poolRecv := i256MinMax(amount0, amount1)
105 if poolRecv.IsOverflow() || poolOut.IsOverflow() {
106 panic("overflow in swapDryInner")
107 }
108
109 return poolRecv.Int64(), poolOut.Int64()
110}
111
112// calculateSqrtPriceLimitToStr returns the price-limit string for a swap operation.
113// A non-zero caller limit is formatted as-is; a zero limit uses the global
114// TickMath boundary for the swap direction.
115func calculateSqrtPriceLimitToStr(zeroForOne bool, sqrtPriceLimitX96 *u256.Uint) string {
116 if !sqrtPriceLimitX96.IsZero() {
117 return sqrtPriceLimitX96.ToString()
118 }
119
120 if zeroForOne {
121 return MIN_SQRT_RATIO_PLUS_ONE
122 }
123 return MAX_SQRT_RATIO_MINUS_ONE
124}