Skip to content

Commit

Permalink
refactor: Use Clone data in function calls to protect original data
Browse files Browse the repository at this point in the history
  • Loading branch information
onlyhyde committed Dec 15, 2024
1 parent 5654380 commit ff6eb54
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 44 deletions.
127 changes: 83 additions & 44 deletions _deploy/p/gnoswap/pool/swap_math.gno
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,34 @@ import (
u256 "gno.land/p/gnoswap/uint256"
)

// SwapMathComputeSwapStepStr computes the next sqrt price, amount in, amount out, and fee amount
// Computes the result of swapping some amount in, or amount out, given the parameters of the swap
// The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive
//
// input:
// - sqrtRatioCurrentX96: the current sqrt price of the pool
// - sqrtRatioTargetX96: The price that cannot be exceeded, from which the direction of the swap is inferred
// - liquidity: The usable liquidity of the pool
// - amountRemaining: How much input or output amount is remaining to be swapped in/out
// - feePips: The fee taken from the input amount, expressed in hundredths of a bip
//
// output:
// - sqrtRatioNextX96: The price after swapping the amount in/out, not to exceed the price target
// - amountIn: The amount to be swapped in, of either token0 or token1, based on the direction of the swap
// - amountOut: The amount to be received, of either token0 or token1, based on the direction of the swap
// - feeAmount: The amount of input that will be taken as a fee
func SwapMathComputeSwapStepStr(
sqrtRatioCurrentX96 *u256.Uint, // uint160
sqrtRatioTargetX96 *u256.Uint, // uint160
liquidity *u256.Uint, // uint128
amountRemaining *i256.Int, // int256
sqrtRatioCurrentX96 *u256.Uint,
sqrtRatioTargetX96 *u256.Uint,
liquidity *u256.Uint,
amountRemaining *i256.Int,
feePips uint64,
) (string, string, string, string) { // (sqrtRatioNextX96, amountIn, amountOut, feeAmount *u256.Uint)
isToken1Expensive := sqrtRatioCurrentX96.Gte(sqrtRatioTargetX96)
) (string, string, string, string) {
if sqrtRatioCurrentX96 == nil || sqrtRatioTargetX96 == nil || liquidity == nil || amountRemaining == nil {
panic("SwapMathComputeSwapStepStr: invalid input")
}

zeroForOne := sqrtRatioCurrentX96.Gte(sqrtRatioTargetX96)

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

if exactIn {
amountRemainingLessFee := u256.MulDiv(amountRemaining.Abs(), u256.NewUint(1000000-feePips), u256.NewUint(1000000))

if isToken1Expensive {
amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true)
if zeroForOne {
amountIn = sqrtPriceMathGetAmount0DeltaHelper(
sqrtRatioTargetX96.Clone(),
sqrtRatioCurrentX96.Clone(),
liquidity.Clone(),
true)
} else {
amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true)
amountIn = sqrtPriceMathGetAmount1DeltaHelper(
sqrtRatioCurrentX96.Clone(),
sqrtRatioTargetX96.Clone(),
liquidity.Clone(),
true)
}

if amountRemainingLessFee.Gte(amountIn) {
sqrtRatioNextX96 = sqrtRatioTargetX96
sqrtRatioNextX96 = sqrtRatioTargetX96.Clone()
} else {
sqrtRatioNextX96 = sqrtPriceMathGetNextSqrtPriceFromInput(
sqrtRatioCurrentX96,
liquidity,
amountRemainingLessFee,
isToken1Expensive,
sqrtRatioCurrentX96.Clone(),
liquidity.Clone(),
amountRemainingLessFee.Clone(),
zeroForOne,
)
}

} else {
if isToken1Expensive {
amountOut = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false)
if zeroForOne {
amountOut = sqrtPriceMathGetAmount1DeltaHelper(
sqrtRatioTargetX96.Clone(),
sqrtRatioCurrentX96.Clone(),
liquidity.Clone(),
false)
} else {
amountOut = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false)
amountOut = sqrtPriceMathGetAmount0DeltaHelper(
sqrtRatioCurrentX96.Clone(),
sqrtRatioTargetX96.Clone(),
liquidity.Clone(),
false)
}

if amountRemaining.Abs().Gte(amountOut) {
sqrtRatioNextX96 = sqrtRatioTargetX96
sqrtRatioNextX96 = sqrtRatioTargetX96.Clone()
} else {
sqrtRatioNextX96 = sqrtPriceMathGetNextSqrtPriceFromOutput(
sqrtRatioCurrentX96,
liquidity,
sqrtRatioCurrentX96.Clone(),
liquidity.Clone(),
amountRemaining.Abs(),
isToken1Expensive,
zeroForOne,
)
}
}

max := sqrtRatioTargetX96.Eq(sqrtRatioNextX96)
isMax := sqrtRatioTargetX96.Eq(sqrtRatioNextX96)

if isToken1Expensive {
if max && exactIn {
amountIn = amountIn
} else {
amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true)
if zeroForOne {
if !(isMax && exactIn) {
amountIn = sqrtPriceMathGetAmount0DeltaHelper(
sqrtRatioNextX96.Clone(),
sqrtRatioCurrentX96.Clone(),
liquidity.Clone(),
true)
}

if max && !exactIn {
amountOut = amountOut
} else {
amountOut = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false)
if !(isMax && !exactIn) {
amountOut = sqrtPriceMathGetAmount1DeltaHelper(
sqrtRatioNextX96.Clone(),
sqrtRatioCurrentX96.Clone(),
liquidity.Clone(),
false)
}
} else {
if max && exactIn {
amountIn = amountIn
} else {
amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true)
if !(isMax && exactIn) {
amountIn = sqrtPriceMathGetAmount1DeltaHelper(
sqrtRatioCurrentX96.Clone(),
sqrtRatioNextX96.Clone(),
liquidity.Clone(),
true)
}

if max && !exactIn {
amountOut = amountOut
} else {
amountOut = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false)
if !(isMax && !exactIn) {
amountOut = sqrtPriceMathGetAmount0DeltaHelper(
sqrtRatioCurrentX96.Clone(),
sqrtRatioNextX96.Clone(),
liquidity.Clone(),
false)
}
}

if !exactIn && amountOut.Gt(amountRemaining.Abs()) {
amountOut = amountRemaining.Abs()
}

if exactIn && !(sqrtRatioNextX96.Eq(sqrtRatioTargetX96)) {
feeAmount = new(u256.Uint).Sub(amountRemaining.Abs(), amountIn)
} else {
Expand Down
62 changes: 62 additions & 0 deletions _deploy/p/gnoswap/pool/swap_math_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,68 @@ func TestSwapMathComputeSwapStepStr(t *testing.T) {
}
}

func TestSwapMathComputeSwapStepStrFail(t *testing.T) {
tests := []struct {
name string
currentX96, targetX96 *u256.Uint
liquidity *u256.Uint
amountRemaining *i256.Int
feePips uint64
sqrtNextX96 *u256.Uint
chkSqrtNextX96 func(sqrtRatioNextX96, priceTarget *u256.Uint)
amountIn, amountOut, feeAmount string
shouldPanic bool
expectedMessage string
}{
{
name: "input parameter is nil",
currentX96: nil,
targetX96: nil,
liquidity: nil,
amountRemaining: nil,
feePips: 600,
sqrtNextX96: encodePriceSqrt(t, "101", "100"),
chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) {
uassert.True(t, sqrtRatioNextX96.Eq(priceTarget))
},
amountIn: "9975124224178055",
amountOut: "9925619580021728",
feeAmount: "5988667735148",
shouldPanic: true,
expectedMessage: "SwapMathComputeSwapStepStr: invalid input",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
if test.shouldPanic {
if errMsg, ok := r.(string); ok {
uassert.Equal(t, test.expectedMessage, errMsg)
} else {
t.Errorf("expected a panic with message, got: %v", r)
}
} else {
t.Errorf("unexpected panic: %v", r)
}
} else {
if test.shouldPanic {
t.Errorf("expected a panic, but none occurred")
}
}
}()

SwapMathComputeSwapStepStr(
test.currentX96,
test.targetX96,
test.liquidity,
test.amountRemaining,
test.feePips)
})
}
}

// encodePriceSqrt calculates the sqrt((reserve1 << 192) / reserve0)
func encodePriceSqrt(t *testing.T, reserve1, reserve0 string) *u256.Uint {
t.Helper()
Expand Down

0 comments on commit ff6eb54

Please sign in to comment.