Skip to content

Commit

Permalink
Merge pull request #144 from gnoswap-labs/GSW-782-minimize-gap-betwee…
Browse files Browse the repository at this point in the history
…n-dry-actual-route-swap

GSW-782 fix: minimize gap between dry ≈ actual route swap
  • Loading branch information
notJoon authored Jan 4, 2024
2 parents 7c03783 + 2c3cfad commit 79f419b
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 102 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ func TestPositionMint(t *testing.T) {
// Mint
tokenId, liquidity, amount0, amount1 := pos.Mint(barPath, bazPath, fee500, int32(12000), int32(15000), bigint(100_000), bigint(100_000), 0, 0, max_timeout)
shouldEQ(t, tokenId, uint64(1))
shouldEQ(t, liquidity, bigint(1308149))
shouldEQ(t, amount0, bigint(99999)) // ONLY BAR
shouldEQ(t, amount1, bigint(0)) // NO BAZ
shouldEQ(t, liquidity, bigint(1308150))
shouldEQ(t, amount0, bigint(100000)) // ONLY BAR
shouldEQ(t, amount1, bigint(0)) // NO BAZ
}

func TestDrySwapRouteBarBazExactIn(t *testing.T) {
Expand All @@ -50,7 +50,7 @@ func TestDrySwapRouteBarBazExactIn(t *testing.T) {
"100", // quoteArr
)

shouldEQ(t, dryResult, bigint(0))
shouldEQ(t, dryResult, bigint(-1))
}

func TestSwapRouteBarBazExactIn(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ func TestPositionMintQuxGnot(t *testing.T) {
std.TestSetOrigCaller(test1)

// send
std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil)
std.TestSetOrigSend(std.Coins{{"ugnot", 999999}}, nil)
testBanker := std.GetBanker(std.BankerTypeRealmIssue)
testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999)
testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 999999)

// Deposit(wrap)
std.TestSetPrevAddr(test1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ func TestPositionMintGnsGnot(t *testing.T) {
std.TestSetOrigCaller(test1)

// send
std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil)
std.TestSetOrigSend(std.Coins{{"ugnot", 999999}}, nil)
testBanker := std.GetBanker(std.BankerTypeRealmIssue)
testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999)
testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 999999)

// Deposit(wrap)
std.TestSetPrevAddr(test1)
Expand All @@ -52,9 +52,9 @@ func TestPositionMintGnotBar(t *testing.T) {
std.TestSetOrigCaller(test1)

// send
std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil)
std.TestSetOrigSend(std.Coins{{"ugnot", 999999}}, nil)
testBanker := std.GetBanker(std.BankerTypeRealmIssue)
testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999)
testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 999999)

// Deposit(wrap)
std.TestSetPrevAddr(test1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,5 @@ func TestwapRouteQuxBarExactOut(t *testing.T) {
99999, // tokenAmountLimit
)

shouldEQ(t, swapResult, bigint(7356))
shouldEQ(t, swapResult, bigint(7350))
}
5 changes: 4 additions & 1 deletion router/consts.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ const (
MIN_PRICE bigint = 4295128740 // MIN_SQRT_RATIO + 1
MAX_PRICE bigint = 1461446703485210103287273052203988822378723970341 // MAX_SQRT_RATIO - 1

Q96 bigint = 79228162514264337593543950336 // 2 ** 96
Q96 bigint = 79228162514264337593543950336 // 2 ** 96
MAX_UINT256 bigint = 115792089237316195423570985008687907853269984665640564039457584007913129639935

MAX_UINT64 uint64 = 18446744073709551615

ADDR_POOL std.Address = std.DerivePkgAddr("gno.land/r/demo/pool")
ADDR_ROUTER std.Address = std.DerivePkgAddr("gno.land/r/demo/router")
Expand Down
50 changes: 36 additions & 14 deletions router/router.gno
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ func DrySwapRoute(

// check route length ( should be 1 ~ 7 )
routes := strings.Split(strRouteArr, ",")
require(1 <= len(routes) && len(routes) <= 7, ufmt.Sprintf("[ROUTER] router.gno__DrySwapRoute() || len(routes) should be 1 ~ 7 (len(routes):%d)", len(routes)))
require(1 <= len(routes) && len(routes) <= 7, ufmt.Sprintf("[ROUTER] router.gno__DrySwapRoute() || len(routes) should be 1 ~ 7 (len(routes)[%d])", len(routes)))

// check if routes length and quotes length are same
quotes := strings.Split(quoteArr, ",")
require(len(routes) == len(quotes), "[ROUTER] router.gno__DrySwapRoute() || len(routes) != len(quotes)")
require(len(routes) == len(quotes), ufmt.Sprintf("[ROUTER] router.gno__DrySwapRoute() || len(routes[%d]) != len(quotes[%d])", len(routes), len(quotes)))

// check if quotes are up to 100%
quotesSum := 0
Expand All @@ -43,7 +43,7 @@ func DrySwapRoute(
}
require(quotesSum == 100, "[ROUTER] router.gno__DrySwapRoute() || quotesSum != 100")

resultAmount := bigint(0)
var resultAmountIn, resultAmountOut bigint
for i, route := range routes {
numHops := strings.Count(route, "*POOL*") + 1
quote, _ := strconv.Atoi(quotes[i])
Expand All @@ -54,14 +54,31 @@ func DrySwapRoute(
toSwap := amountSpecified * bigint(quote) / bigint(100)

if numHops == 1 { // SINGLE
resultAmount += handleSingleSwap(route, toSwap, true)
amountIn, amountOut := handleSingleSwap(route, toSwap, true)
resultAmountIn += amountIn
resultAmountOut += amountOut
} else if 2 <= numHops && numHops <= 3 { // MULTI
resultAmount += handleMultiSwap(swapType, route, numHops, toSwap, true)
amountIn, amountOut := handleMultiSwap(swapType, route, numHops, toSwap, true)
resultAmountIn += amountIn
resultAmountOut += amountOut
} else {
panic("[ROUTER] router.gno__DrySwapRoute() || numHops should be 1 ~ 3")
}
}
return resultAmount

if swapType == ExactIn {
if resultAmountIn != amountSpecified {
return -1 // if pool doesn't have enough output token amount to swap against input token amount
}
return resultAmountOut
}

if swapType == ExactOut {
if resultAmountOut < amountSpecified { // if pool doesn't user wanted amount of output token
return -1
}
return resultAmountIn
}
}

func SwapRoute(
Expand Down Expand Up @@ -100,7 +117,7 @@ func SwapRoute(
}
require(quotesSum == 100, "[ROUTER] router.gno__SwapRoute() || quotesSum != 100")

resultAmount := bigint(0)
var resultAmountIn, resultAmountOut bigint
for i, route := range routes {
numHops := strings.Count(route, "*POOL*") + 1
quote, _ := strconv.Atoi(quotes[i])
Expand All @@ -111,23 +128,28 @@ func SwapRoute(
toSwap := amountSpecified * bigint(quote) / bigint(100)

if numHops == 1 { // SINGLE
resultAmount += handleSingleSwap(route, toSwap, false)
amountIn, amountOut := handleSingleSwap(route, toSwap, false)
resultAmountIn += amountIn
resultAmountOut += amountOut
} else if 2 <= numHops && numHops <= 3 { // MULTI
resultAmount += handleMultiSwap(swapType, route, numHops, toSwap, false)
amountIn, amountOut := handleMultiSwap(swapType, route, numHops, toSwap, false)
resultAmountIn += amountIn
resultAmountOut += amountOut
} else {
panic("[ROUTER] router.gno__SwapRoute() || numHops should be 1 ~ 3")
}
}

if swapType == ExactIn {
require(tokenAmountLimit <= resultAmount, ufmt.Sprintf("[ROUTER] router.gno__SwapRoute() || too few received (expected minimum output:%d, actual output:%d)", tokenAmountLimit, resultAmount))
require(tokenAmountLimit <= resultAmountOut, ufmt.Sprintf("[ROUTER] router.gno__SwapRoute() || too few received (expected minimum output:%d, actual output:%d)", tokenAmountLimit, resultAmountOut))
return resultAmountOut
} else { // EXACT_OUT
require(resultAmount <= tokenAmountLimit, ufmt.Sprintf("[ROUTER] router.gno__SwapRoute() || too much spend (expected maximum input:%d, actual input:%d)", tokenAmountLimit, resultAmount))
require(resultAmountIn <= tokenAmountLimit, ufmt.Sprintf("[ROUTER] router.gno__SwapRoute() || too much spend (expected maximum input:%d, actual input:%d)", tokenAmountLimit, resultAmountIn))
return resultAmountIn
}
return resultAmount
}

func handleSingleSwap(route string, amountSpecified bigint, isDry bool) bigint {
func handleSingleSwap(route string, amountSpecified bigint, isDry bool) (amountIn, amountOut bigint) {
input, output, fee := getDataForSinglePath(route)
singleParams := SingleSwapParams{
tokenIn: input,
Expand All @@ -142,7 +164,7 @@ func handleSingleSwap(route string, amountSpecified bigint, isDry bool) bigint {
return singleSwap(singleParams)
}

func handleMultiSwap(swapType SwapType, route string, numHops int, amountSpecified bigint, isDry bool) bigint {
func handleMultiSwap(swapType SwapType, route string, numHops int, amountSpecified bigint, isDry bool) (amountIn, amountOut bigint) {
switch swapType {
case ExactIn:
input, output, fee := getDataForMultiPath(route, 0) // first data
Expand Down
68 changes: 19 additions & 49 deletions router/swap_inner.gno
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func _swap(
recipient std.Address,
sqrtPriceLimitX96 bigint,
data SwapCallbackData,
) (amountResult bigint) {
) (amountPoolRecv, amountPoolOut bigint) {
// prepare
zeroForOne := data.tokenIn < data.tokenOut

Expand All @@ -23,20 +23,8 @@ func _swap(
}
}

// dry swap -> esteimate amount -> approve exact amount
approveAmount0, approveAmount1, _ := p.DrySwap(
data.tokenIn,
data.tokenOut,
data.fee,

recipient,
zeroForOne,
amountSpecified,
sqrtPriceLimitX96,
)
toApproveAmount := max(abs(approveAmount0), abs(approveAmount1))

// ROUTER approves POOL as spender
toApproveAmount := MAX_UINT64
approveByRegisterCall(data.tokenIn, ADDR_POOL, toApproveAmount)
approveByRegisterCall(data.tokenOut, ADDR_POOL, toApproveAmount)

Expand All @@ -53,29 +41,15 @@ func _swap(
data.payer,
)

if amountSpecified > 0 {
if zeroForOne { // return amount, user recvs ~= pool sends
amountResult = -amount1
} else {
amountResult = -amount0
}
} else {
if zeroForOne { // return amount, user sends ~= pool recvs
amountResult = amount0
} else {
amountResult = amount1
}
}

return amountResult
return absBigint(maxBigint(amount0, amount1)), absBigint(minBigint(amount0, amount1))
}

func _swapDry(
amountSpecified bigint,
recipient std.Address,
sqrtPriceLimitX96 bigint,
data SwapCallbackData,
) (amountResult bigint) {
) (amountPoolRecv, amountPoolOut bigint) {
zeroForOne := data.tokenIn < data.tokenOut

if sqrtPriceLimitX96 == 0 {
Expand All @@ -98,31 +72,27 @@ func _swapDry(
sqrtPriceLimitX96,
)
if !ok {
return 0
return 0, 0
}

if amountSpecified > 0 { // EXACT_IN,
// input from user: tokenIn, tokenInAmount, tokenOut
// return: return tokenOutAmount(in positive) ≈ pool sends ≈ user recvs
if zeroForOne {
return absBigint(amount1)
} else {
return absBigint(amount0)
}
} else { // EXACT_OUT
// input from user: tokenIn, tokenOut, tokenOutAmount
// return: return tokenInAmount(in positive) ≈ user sends ≈ pool recvs
if zeroForOne {
return absBigint(amount0)
} else {
return absBigint(amount1)
}
return absBigint(maxBigint(amount0, amount1)), absBigint(minBigint(amount0, amount1))
}

func max(a, b uint64) uint64 {
if a > b {
return a
}
return b
}

return amountResult
func minBigint(a, b bigint) bigint {
if a < b {
return a
}
return b
}

func max(a, b uint64) uint64 {
func maxBigint(a, b bigint) bigint {
if a > b {
return a
}
Expand Down
Loading

0 comments on commit 79f419b

Please sign in to comment.