Skip to content

Commit ff6eb54

Browse files
author
0xTopaz
committed
refactor: Use Clone data in function calls to protect original data
1 parent 5654380 commit ff6eb54

File tree

2 files changed

+145
-44
lines changed

2 files changed

+145
-44
lines changed

_deploy/p/gnoswap/pool/swap_math.gno

Lines changed: 83 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,34 @@ import (
55
u256 "gno.land/p/gnoswap/uint256"
66
)
77

8+
// SwapMathComputeSwapStepStr computes the next sqrt price, amount in, amount out, and fee amount
9+
// Computes the result of swapping some amount in, or amount out, given the parameters of the swap
10+
// The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive
11+
//
12+
// input:
13+
// - sqrtRatioCurrentX96: the current sqrt price of the pool
14+
// - sqrtRatioTargetX96: The price that cannot be exceeded, from which the direction of the swap is inferred
15+
// - liquidity: The usable liquidity of the pool
16+
// - amountRemaining: How much input or output amount is remaining to be swapped in/out
17+
// - feePips: The fee taken from the input amount, expressed in hundredths of a bip
18+
//
19+
// output:
20+
// - sqrtRatioNextX96: The price after swapping the amount in/out, not to exceed the price target
21+
// - amountIn: The amount to be swapped in, of either token0 or token1, based on the direction of the swap
22+
// - amountOut: The amount to be received, of either token0 or token1, based on the direction of the swap
23+
// - feeAmount: The amount of input that will be taken as a fee
824
func SwapMathComputeSwapStepStr(
9-
sqrtRatioCurrentX96 *u256.Uint, // uint160
10-
sqrtRatioTargetX96 *u256.Uint, // uint160
11-
liquidity *u256.Uint, // uint128
12-
amountRemaining *i256.Int, // int256
25+
sqrtRatioCurrentX96 *u256.Uint,
26+
sqrtRatioTargetX96 *u256.Uint,
27+
liquidity *u256.Uint,
28+
amountRemaining *i256.Int,
1329
feePips uint64,
14-
) (string, string, string, string) { // (sqrtRatioNextX96, amountIn, amountOut, feeAmount *u256.Uint)
15-
isToken1Expensive := sqrtRatioCurrentX96.Gte(sqrtRatioTargetX96)
30+
) (string, string, string, string) {
31+
if sqrtRatioCurrentX96 == nil || sqrtRatioTargetX96 == nil || liquidity == nil || amountRemaining == nil {
32+
panic("SwapMathComputeSwapStepStr: invalid input")
33+
}
34+
35+
zeroForOne := sqrtRatioCurrentX96.Gte(sqrtRatioTargetX96)
1636

1737
// POSTIVIE == EXACT_IN => Estimated AmountOut
1838
// NEGATIVE == EXACT_OUT => Estimated AmountIn
@@ -25,75 +45,94 @@ func SwapMathComputeSwapStepStr(
2545

2646
if exactIn {
2747
amountRemainingLessFee := u256.MulDiv(amountRemaining.Abs(), u256.NewUint(1000000-feePips), u256.NewUint(1000000))
28-
29-
if isToken1Expensive {
30-
amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true)
48+
if zeroForOne {
49+
amountIn = sqrtPriceMathGetAmount0DeltaHelper(
50+
sqrtRatioTargetX96.Clone(),
51+
sqrtRatioCurrentX96.Clone(),
52+
liquidity.Clone(),
53+
true)
3154
} else {
32-
amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true)
55+
amountIn = sqrtPriceMathGetAmount1DeltaHelper(
56+
sqrtRatioCurrentX96.Clone(),
57+
sqrtRatioTargetX96.Clone(),
58+
liquidity.Clone(),
59+
true)
3360
}
3461

3562
if amountRemainingLessFee.Gte(amountIn) {
36-
sqrtRatioNextX96 = sqrtRatioTargetX96
63+
sqrtRatioNextX96 = sqrtRatioTargetX96.Clone()
3764
} else {
3865
sqrtRatioNextX96 = sqrtPriceMathGetNextSqrtPriceFromInput(
39-
sqrtRatioCurrentX96,
40-
liquidity,
41-
amountRemainingLessFee,
42-
isToken1Expensive,
66+
sqrtRatioCurrentX96.Clone(),
67+
liquidity.Clone(),
68+
amountRemainingLessFee.Clone(),
69+
zeroForOne,
4370
)
4471
}
45-
4672
} else {
47-
if isToken1Expensive {
48-
amountOut = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false)
73+
if zeroForOne {
74+
amountOut = sqrtPriceMathGetAmount1DeltaHelper(
75+
sqrtRatioTargetX96.Clone(),
76+
sqrtRatioCurrentX96.Clone(),
77+
liquidity.Clone(),
78+
false)
4979
} else {
50-
amountOut = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false)
80+
amountOut = sqrtPriceMathGetAmount0DeltaHelper(
81+
sqrtRatioCurrentX96.Clone(),
82+
sqrtRatioTargetX96.Clone(),
83+
liquidity.Clone(),
84+
false)
5185
}
5286

5387
if amountRemaining.Abs().Gte(amountOut) {
54-
sqrtRatioNextX96 = sqrtRatioTargetX96
88+
sqrtRatioNextX96 = sqrtRatioTargetX96.Clone()
5589
} else {
5690
sqrtRatioNextX96 = sqrtPriceMathGetNextSqrtPriceFromOutput(
57-
sqrtRatioCurrentX96,
58-
liquidity,
91+
sqrtRatioCurrentX96.Clone(),
92+
liquidity.Clone(),
5993
amountRemaining.Abs(),
60-
isToken1Expensive,
94+
zeroForOne,
6195
)
6296
}
6397
}
6498

65-
max := sqrtRatioTargetX96.Eq(sqrtRatioNextX96)
99+
isMax := sqrtRatioTargetX96.Eq(sqrtRatioNextX96)
66100

67-
if isToken1Expensive {
68-
if max && exactIn {
69-
amountIn = amountIn
70-
} else {
71-
amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true)
101+
if zeroForOne {
102+
if !(isMax && exactIn) {
103+
amountIn = sqrtPriceMathGetAmount0DeltaHelper(
104+
sqrtRatioNextX96.Clone(),
105+
sqrtRatioCurrentX96.Clone(),
106+
liquidity.Clone(),
107+
true)
72108
}
73-
74-
if max && !exactIn {
75-
amountOut = amountOut
76-
} else {
77-
amountOut = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false)
109+
if !(isMax && !exactIn) {
110+
amountOut = sqrtPriceMathGetAmount1DeltaHelper(
111+
sqrtRatioNextX96.Clone(),
112+
sqrtRatioCurrentX96.Clone(),
113+
liquidity.Clone(),
114+
false)
78115
}
79116
} else {
80-
if max && exactIn {
81-
amountIn = amountIn
82-
} else {
83-
amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true)
117+
if !(isMax && exactIn) {
118+
amountIn = sqrtPriceMathGetAmount1DeltaHelper(
119+
sqrtRatioCurrentX96.Clone(),
120+
sqrtRatioNextX96.Clone(),
121+
liquidity.Clone(),
122+
true)
84123
}
85-
86-
if max && !exactIn {
87-
amountOut = amountOut
88-
} else {
89-
amountOut = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false)
124+
if !(isMax && !exactIn) {
125+
amountOut = sqrtPriceMathGetAmount0DeltaHelper(
126+
sqrtRatioCurrentX96.Clone(),
127+
sqrtRatioNextX96.Clone(),
128+
liquidity.Clone(),
129+
false)
90130
}
91131
}
92132

93133
if !exactIn && amountOut.Gt(amountRemaining.Abs()) {
94134
amountOut = amountRemaining.Abs()
95135
}
96-
97136
if exactIn && !(sqrtRatioNextX96.Eq(sqrtRatioTargetX96)) {
98137
feeAmount = new(u256.Uint).Sub(amountRemaining.Abs(), amountIn)
99138
} else {

_deploy/p/gnoswap/pool/swap_math_test.gno

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,68 @@ func TestSwapMathComputeSwapStepStr(t *testing.T) {
168168
}
169169
}
170170

171+
func TestSwapMathComputeSwapStepStrFail(t *testing.T) {
172+
tests := []struct {
173+
name string
174+
currentX96, targetX96 *u256.Uint
175+
liquidity *u256.Uint
176+
amountRemaining *i256.Int
177+
feePips uint64
178+
sqrtNextX96 *u256.Uint
179+
chkSqrtNextX96 func(sqrtRatioNextX96, priceTarget *u256.Uint)
180+
amountIn, amountOut, feeAmount string
181+
shouldPanic bool
182+
expectedMessage string
183+
}{
184+
{
185+
name: "input parameter is nil",
186+
currentX96: nil,
187+
targetX96: nil,
188+
liquidity: nil,
189+
amountRemaining: nil,
190+
feePips: 600,
191+
sqrtNextX96: encodePriceSqrt(t, "101", "100"),
192+
chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) {
193+
uassert.True(t, sqrtRatioNextX96.Eq(priceTarget))
194+
},
195+
amountIn: "9975124224178055",
196+
amountOut: "9925619580021728",
197+
feeAmount: "5988667735148",
198+
shouldPanic: true,
199+
expectedMessage: "SwapMathComputeSwapStepStr: invalid input",
200+
},
201+
}
202+
203+
for _, test := range tests {
204+
t.Run(test.name, func(t *testing.T) {
205+
defer func() {
206+
if r := recover(); r != nil {
207+
if test.shouldPanic {
208+
if errMsg, ok := r.(string); ok {
209+
uassert.Equal(t, test.expectedMessage, errMsg)
210+
} else {
211+
t.Errorf("expected a panic with message, got: %v", r)
212+
}
213+
} else {
214+
t.Errorf("unexpected panic: %v", r)
215+
}
216+
} else {
217+
if test.shouldPanic {
218+
t.Errorf("expected a panic, but none occurred")
219+
}
220+
}
221+
}()
222+
223+
SwapMathComputeSwapStepStr(
224+
test.currentX96,
225+
test.targetX96,
226+
test.liquidity,
227+
test.amountRemaining,
228+
test.feePips)
229+
})
230+
}
231+
}
232+
171233
// encodePriceSqrt calculates the sqrt((reserve1 << 192) / reserve0)
172234
func encodePriceSqrt(t *testing.T, reserve1, reserve0 string) *u256.Uint {
173235
t.Helper()

0 commit comments

Comments
 (0)