Skip to content

Commit

Permalink
doc: getMaxTick, getMinTick
Browse files Browse the repository at this point in the history
  • Loading branch information
notJoon committed Dec 21, 2024
1 parent c01b94c commit 432af54
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 69 deletions.
100 changes: 34 additions & 66 deletions router/router.gno
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ const (
SINGLE_HOP_ROUTE int = 1
)

// type Router interface {
// ExactInSwapRoute(ExactInParams) (string, string)
// ExactOutSwapRoute(ExactOutParams) (string, string)
// }

// SwapRoute swaps the input token to the output token and returns the result amount
// If swapType is EXACT_IN, it returns the amount of output token ≈ amount of user to receive
// If swapType is EXACT_OUT, it returns the amount of input token ≈ amount of user to pay
Expand Down Expand Up @@ -510,33 +505,6 @@ func validateRoutesAndQuotes(routes, quotes []string) error {
return nil
}

func processRoutes(routes, quotes []string, amountSpecified *i256.Int, swapType string) (*u256.Uint, *u256.Uint) {
resultAmountIn := u256.Zero()
resultAmountOut := u256.Zero()

for i, route := range routes {
numHops := strings.Count(route, POOL_SEPARATOR) + 1
quote, _ := strconv.Atoi(quotes[i])

assertHopsInRange(numHops)

toSwap := i256.Zero().Mul(amountSpecified, i256.NewInt(int64(quote)))
toSwap = toSwap.Div(toSwap, i256.NewInt(100))

var amountIn, amountOut *u256.Uint
if numHops == 1 {
amountIn, amountOut = handleSingleSwap(route, toSwap)
} else {
amountIn, amountOut = handleMultiSwap(swapType, route, numHops, toSwap)
}

resultAmountIn = new(u256.Uint).Add(resultAmountIn, amountIn)
resultAmountOut = new(u256.Uint).Add(resultAmountOut, amountOut)
}

return resultAmountIn, resultAmountOut
}

func handleSingleSwap(route string, amountSpecified *i256.Int) (*u256.Uint, *u256.Uint) {
input, output, fee := getDataForSinglePath(route)
singleParams := SingleSwapParams{
Expand All @@ -549,6 +517,40 @@ func handleSingleSwap(route string, amountSpecified *i256.Int) (*u256.Uint, *u25
return singleSwap(singleParams)
}

func handleMultiSwap(swapType string, route string, numHops int, amountSpecified *i256.Int) (*u256.Uint, *u256.Uint) {
switch swapType {
case ExactIn:
input, output, fee := getDataForMultiPath(route, 0) // first data
swapParams := SwapParams{
tokenIn: input,
tokenOut: output,
fee: fee,
recipient: std.PrevRealm().Addr(),
amountSpecified: amountSpecified,
}

return multiSwap(swapParams, 0, numHops, route) // iterate here

case ExactOut:
input, output, fee := getDataForMultiPath(route, numHops-1) // last data
swapParams := SwapParams{
tokenIn: input,
tokenOut: output,
fee: fee,
recipient: std.PrevRealm().Addr(),
amountSpecified: amountSpecified,
}

return multiSwapNegative(swapParams, numHops-1, route) // iterate here

default:
panic(addDetailToError(
errInvalidSwapType,
ufmt.Sprintf("unknown swapType(%s)", swapType),
))
}
}

func finalizeSwap(inputToken, outputToken string, resultAmountIn, resultAmountOut *u256.Uint, swapType string, tokenAmountLimit *u256.Uint, userBeforeWugnotBalance, userWrappedWugnot uint64, amountSpecified *u256.Uint) (string, string) {
if swapType == ExactOut && resultAmountOut.Lt(amountSpecified) {
panic(addDetailToError(
Expand Down Expand Up @@ -602,37 +604,3 @@ func finalizeSwap(inputToken, outputToken string, resultAmountIn, resultAmountOu
intAmountOut := i256.FromUint256(afterFee)
return resultAmountIn.ToString(), i256.Zero().Neg(intAmountOut).ToString()
}

func handleMultiSwap(swapType string, route string, numHops int, amountSpecified *i256.Int) (*u256.Uint, *u256.Uint) {
switch swapType {
case ExactIn:
input, output, fee := getDataForMultiPath(route, 0) // first data
swapParams := SwapParams{
tokenIn: input,
tokenOut: output,
fee: fee,
recipient: std.PrevRealm().Addr(),
amountSpecified: amountSpecified,
}

return multiSwap(swapParams, 0, numHops, route) // iterate here

case ExactOut:
input, output, fee := getDataForMultiPath(route, numHops-1) // last data
swapParams := SwapParams{
tokenIn: input,
tokenOut: output,
fee: fee,
recipient: std.PrevRealm().Addr(),
amountSpecified: amountSpecified,
}

return multiSwapNegative(swapParams, numHops-1, route) // iterate here

default:
panic(addDetailToError(
errInvalidSwapType,
ufmt.Sprintf("unknown swapType(%s)", swapType),
))
}
}
46 changes: 43 additions & 3 deletions router/swap_inner.gno
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,26 @@ func calculateSqrtPriceLimitForSwap(zeroForOne bool, fee uint32, sqrtPriceLimitX
//
// Fee tier to min tick mapping demonstrates varying levels of price granularity:
//
// Tick Range Visualization:
// ## How these values are calculated?
//
// The Tick bounds in Uniswap V3 are derived from the desired price range and precisions:
// 1. Price Range: Uniswap V3 uses the formula price = 1.0001^tick
// 2. The minimum tick is calculated to represent a very small but non-zero price:
// - Let min_tick = log(minimum_price) / log(1.0001)
// - The minimum price is chosen to be 2^-128 ≈ 2.9387e-39
// - Therefor, min_tick = log(2^-128) / log(1.0001) ≈ -887272
//
// ### Tick Spacing Adjustment
//
// - Each fee tier has different tick spacing for efficiency
// - The actual minimum tick is rounded to the nearest tick spacing:
// * 0.01% fee -> spacing of 1 -> -887272
// * 0.05% fee -> spacing of 10 -> -887270
// * 0.30% fee -> spacing of 60 -> -887220
// * 1.00% fee -> spacing of 200 -> -887200
//
// ## Tick Range Visualization:
//
// ```
// 0
// Fee Tier Min Tick | Max Tick Tick Spacing
Expand All @@ -174,6 +193,7 @@ func calculateSqrtPriceLimitForSwap(zeroForOne bool, fee uint32, sqrtPriceLimitX
// ```
//
// Tick spacing determines the granularity of price points:
//
// - Smaller tick spacing (1) = More precise price points
// Example for 0.01% fee tier:
// ```
Expand All @@ -198,6 +218,9 @@ func calculateSqrtPriceLimitForSwap(zeroForOne bool, fee uint32, sqrtPriceLimitX
//
// Panic:
// - If the fee tier is not supported
//
// Reference:
// - https://blog.uniswap.org/uniswap-v3-math-primer
func getMinTick(fee uint32) int32 {
switch fee {
case 100:
Expand All @@ -211,13 +234,27 @@ func getMinTick(fee uint32) int32 {
default:
panic(addDetailToError(
errInvalidPoolFeeTier,
ufmt.Sprintf("swapInner.gno__getMaxTick() || unknown fee(%d)", fee),
ufmt.Sprintf("unknown fee(%d)", fee),
))
}
}

// getMaxTick returns the maximum tick value for a given fee tier.
//
// ## How these values are calculated?
//
// The max tick values are the exact negatives of min tick values because:
// 1. Price symmetry: If min_price = 2^-128, then max_price = 2^128
// 2. Using the same formula: max_tick = log(2^128) / log(1.0001) ≈ 887272
//
// ### Tick Spacing Relationship:
//
// The max ticks follow the same spacing rules as min ticks:
// * 0.01% fee -> +887272 (finest granularity)
// * 0.05% fee -> +887270 (10-tick spacing)
// * 0.30% fee -> +887220 (60-tick spacing)
// * 1.00% fee -> +887200 (coarsest granularity)
//
// Parameters:
// - fee: Fee tier in basis points
//
Expand All @@ -226,6 +263,9 @@ func getMinTick(fee uint32) int32 {
//
// Panic:
// - If the fee tier is not supported
//
// Reference:
// - https://blog.uniswap.org/uniswap-v3-math-primer
func getMaxTick(fee uint32) int32 {
switch fee {
case 100:
Expand All @@ -239,7 +279,7 @@ func getMaxTick(fee uint32) int32 {
default:
panic(addDetailToError(
errInvalidPoolFeeTier,
ufmt.Sprintf("swapInner.gno__getMaxTick() || unknown fee(%d)", fee),
ufmt.Sprintf("unknown fee(%d)", fee),
))
}
}

0 comments on commit 432af54

Please sign in to comment.