swap_inner.gno
3.26 Kb · 126 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 assertIsRouterProxyRealm(0, rlm)
30
31 token0Path, token1Path := data.tokenIn, data.tokenOut
32 zeroForOne := data.tokenIn < data.tokenOut
33
34 if !zeroForOne {
35 token0Path, token1Path = token1Path, token0Path
36 }
37
38 sqrtPriceLimitStr := calculateSqrtPriceLimitToStr(zeroForOne, sqrtPriceLimitX96)
39
40 amount0Str, amount1Str := pl.Swap(
41 cross(rlm),
42 token0Path,
43 token1Path,
44 data.fee,
45 recipient,
46 zeroForOne,
47 utils.FormatInt(amountSpecified),
48 sqrtPriceLimitStr,
49 func(cur realm, amount0Delta, amount1Delta int64, _ *pl.CallbackMarker) error {
50 // assert is pool to prevent unauthorized callback
51 caller := cur.Previous().Address()
52 access.AssertIsPool(caller)
53
54 return router.SwapCallback(cross(cur), token0Path, token1Path, amount0Delta, amount1Delta, data.payer)
55 },
56 )
57
58 amount0 := i256.MustFromDecimal(amount0Str)
59 amount1 := i256.MustFromDecimal(amount1Str)
60
61 poolOut, poolRecv := i256MinMax(amount0, amount1)
62 if poolRecv.IsOverflow() || poolOut.IsOverflow() {
63 panic("overflow in swapInner")
64 }
65
66 return poolRecv.Int64(), poolOut.Int64()
67}
68
69// swapDryInner performs a dry-run of a swap operation without executing it.
70func (r *routerV1) swapDryInner(
71 amountSpecified int64,
72 sqrtPriceLimitX96 *u256.Uint,
73 data SwapCallbackData,
74) (int64, int64) {
75 token0Path, token1Path := data.tokenIn, data.tokenOut
76 zeroForOne := data.tokenIn < data.tokenOut
77
78 if !zeroForOne {
79 token0Path, token1Path = token1Path, token0Path
80 }
81
82 sqrtPriceLimitStr := calculateSqrtPriceLimitToStr(zeroForOne, sqrtPriceLimitX96)
83
84 // check possible
85 amount0Str, amount1Str, err := pl.DrySwap(
86 token0Path,
87 token1Path,
88 data.fee,
89 zeroForOne,
90 utils.FormatInt(amountSpecified),
91 sqrtPriceLimitStr,
92 )
93 if err != nil {
94 panic(err)
95 }
96
97 amount0 := i256.MustFromDecimal(amount0Str)
98 amount1 := i256.MustFromDecimal(amount1Str)
99 if amount0.Sign() <= 0 && amount1.Sign() <= 0 {
100 panic(makeErrorWithDetails(
101 errInsufficientLiquidity,
102 "swap entirely within 0-liquidity region",
103 ))
104 }
105
106 poolOut, poolRecv := i256MinMax(amount0, amount1)
107 if poolRecv.IsOverflow() || poolOut.IsOverflow() {
108 panic("overflow in swapDryInner")
109 }
110
111 return poolRecv.Int64(), poolOut.Int64()
112}
113
114// calculateSqrtPriceLimitToStr returns the price-limit string for a swap operation.
115// A non-zero caller limit is formatted as-is; a zero limit uses the global
116// TickMath boundary for the swap direction.
117func calculateSqrtPriceLimitToStr(zeroForOne bool, sqrtPriceLimitX96 *u256.Uint) string {
118 if !sqrtPriceLimitX96.IsZero() {
119 return sqrtPriceLimitX96.ToString()
120 }
121
122 if zeroForOne {
123 return MIN_SQRT_RATIO_PLUS_ONE
124 }
125 return MAX_SQRT_RATIO_MINUS_ONE
126}