Skip to content

Commit

Permalink
Merge pull request #176 from gnoswap-labs/GSW-875-feat-use-compute-ro…
Browse files Browse the repository at this point in the history
…ute-logic-from-uniswap-for-router-to-calculate-ratio

GSW-875 feat use compute route logic from uniswap for router to calculate ratio
  • Loading branch information
notJoon committed Feb 26, 2024
2 parents 928b905 + 182c0ce commit d463822
Show file tree
Hide file tree
Showing 20 changed files with 658 additions and 356 deletions.
5 changes: 3 additions & 2 deletions _test/live_test.mk
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ ADDR_GOV := g1kmat25auuqf0h5qvd4q7s707r8let5sky4tr76
TX_EXPIRE := 9999999999

NOW := $(shell date +%s)
INCENTIVE_START := $(shell expr $(NOW) + 160) # GIVE ENOUGH TIME TO EXECUTE PREVIOUS TXS
INCENTIVE_END := $(shell expr $(NOW) + 160 + 7776000) # 7776000 SECONDS = 90 DAY
INCENTIVE_START := $(shell expr $(NOW) + 360) # GIVE ENOUGH TIME TO EXECUTE PREVIOUS TXS
INCENTIVE_END := $(shell expr $(NOW) + 360 + 7776000) # 7776000 SECONDS = 90 DAY

MAKEFILE := $(shell realpath $(firstword $(MAKEFILE_LIST)))
GNOLAND_RPC_URL ?= localhost:26657
Expand All @@ -38,6 +38,7 @@ help:
all: wait deploy faucet approve pool-setup position-mint staker-stake router-swap staker-unstake done

.PHONY: deploy
# deploy: deploy-foo deploy-bar deploy-baz deploy-qux deploy-gns deploy-obl deploy-gnft deploy-const deploy-common deploy-gov deploy-pool deploy-position deploy-staker deploy-router deploy-wrapper
deploy: deploy-foo deploy-bar deploy-baz deploy-qux deploy-wugnot deploy-gns deploy-obl deploy-gnft deploy-const deploy-common deploy-gov deploy-pool deploy-position deploy-staker deploy-router deploy-wrapper

.PHONY: faucet
Expand Down
196 changes: 78 additions & 118 deletions router/_RPC_api.gno
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import (
"gno.land/p/demo/ufmt"

"gno.land/r/demo/consts"
p "gno.land/r/demo/pool"
pl "gno.land/r/demo/pool"
)

type TokenPathPrice struct {
Path string `json:"path"`
Price bigint `json:"price"`
type TokenRatio struct {
Token string `json:"token"`
Ratio bigint `json:"ratio"`
}

type ResponseQueryBase struct {
Expand All @@ -24,7 +24,7 @@ type ResponseQueryBase struct {

type ResponseGetRatiosFromBase struct {
Stat ResponseQueryBase `json:"stat"`
Response []TokenPathPrice `json:"response"`
Response []TokenRatio `json:"response"`
}

func ApiGetRatiosFromBase() string {
Expand All @@ -33,7 +33,7 @@ func ApiGetRatiosFromBase() string {
Timestamp: GetTimestamp(),
}

ratios := getRatiosFromBase(3)
ratios := findRatios(3)
r := ResponseGetRatiosFromBase{
Stat: qb,
Response: ratios,
Expand All @@ -47,109 +47,105 @@ func ApiGetRatiosFromBase() string {
return string(rr)
}

func getRatiosFromBase(maxHops int) []TokenPathPrice {
tokenPrice := make(map[string]bigint, 0)

// BASE
tokenPrice[consts.WRAPPED_WUGNOT] = consts.Q96 // ~= 1

// ELSE
tokenList := getTokenList()
for _, token := range tokenList {
if token != consts.WRAPPED_WUGNOT {
_swapPaths := findSwapPaths(token, consts.WRAPPED_WUGNOT, maxHops)
swapPaths := strings.Split(_swapPaths, "_FIN_")
swapPaths = swapPaths[:len(swapPaths)-1]
numSwapPaths := len(swapPaths)

thisTokenPriceX96 := bigint(0)
if numSwapPaths < 1 { // NO CONNECTION TO BASE
tokenPrice[token] = 0
} else {
for _, swapPath := range swapPaths {
numPools := strings.Count(swapPath, ",") / 2

switch numPools {
case 0:
thisTokenPriceX96 = 0
case 1:
priceRatio := calculateTokenPrice(token, swapPath, numPools, 0, 1)
thisTokenPriceX96 += priceRatio
case 2:
priceRatio := calculateTokenPrice(token, swapPath, numPools, 0, 1)
thisTokenPriceX96 += priceRatio
case 3:
priceRatio := calculateTokenPrice(token, swapPath, numPools, 0, 1)
thisTokenPriceX96 += priceRatio
default:
thisTokenPriceX96 = 0
}
func findRatios(maxHops int) []TokenRatio {
var tokenRatio = make(map[string]bigint, 0)
// WGNOT
tokenRatio[consts.WRAPPED_WUGNOT] = consts.Q96 // ~= 1

tokens := getTokenList()

pools := findCandidatePools()

for _, token := range tokens {
if token == consts.WRAPPED_WUGNOT {
continue
}

routes := computeAllRoutes(consts.WRAPPED_WUGNOT, token, maxHops, pools)

if len(routes) == 0 {
// NO ROUTES FOUND => SET RATIO TO 0
tokenRatio[token] = 0
} else {
numRoutes := len(routes)

var _tokenRatioX96 bigint

for _, route := range routes {
numHops := len(route.route)

switch numHops {
case 1, 2, 3:
priceRatio := calculateTokenRatio(token, route.route, 0, 1)
_tokenRatioX96 += priceRatio
default:
_tokenRatioX96 = 0
}
avgPriceX96 := thisTokenPriceX96 / bigint(numSwapPaths)
tokenPrice[token] = avgPriceX96
}
avgPriceX96 := _tokenRatioX96 / bigint(numRoutes)
tokenRatio[token] = avgPriceX96

}
// TOKEN ENDS
}
// LOOP FIN

tokenPrices := []TokenPathPrice{}
for token, price := range tokenPrice {
tokenPrices = append(tokenPrices, TokenPathPrice{
Path: token,
Price: price,
var tokenRatios = []TokenRatio{}
for token, ratio := range tokenRatio {
tokenRatios = append(tokenRatios, TokenRatio{
Token: token,
Ratio: ratio,
})
}

return tokenPrices
return tokenRatios
}

func calculateTokenPrice(currentToken, swapPath string, numPools, proceed int, currentPriceX96 bigint) bigint {
currentPoolPathKey := makePoolPath(swapPath, proceed)
currentPool := p.GetPoolFromPoolPath(currentPoolPathKey)
func calculateTokenRatio(currentToken string, routes []PoolWithMeta, proceed int, priceX96 bigint) bigint {
poolPath := routes[len(routes)-proceed-1].poolPath
pool := pl.GetPoolFromPoolPath(poolPath)

poolToken0 := currentPool.PoolGetToken0Path()
poolToken1 := currentPool.PoolGetToken1Path()
token0Path := pool.PoolGetToken0Path()
token1Path := pool.PoolGetToken1Path()

if poolToken0 == currentToken {
currentSqrtPriceX96 := currentPool.PoolGetSlot0SqrtPriceX96()
if token1Path == currentToken {
poolSqrtPriceX96 := pool.PoolGetSlot0SqrtPriceX96()

currentPriceX96 *= currentSqrtPriceX96 * currentSqrtPriceX96
currentToken = poolToken1
} else if poolToken1 == currentToken {
currentTick := currentPool.PoolGetSlot0Tick()
oppositeTick := -currentTick
priceX96 *= poolSqrtPriceX96 * poolSqrtPriceX96
currentToken = token0Path
} else if token0Path == currentToken {
poolTick := pool.PoolGetSlot0Tick()
oppositeTick := -poolTick
oppositeSqrtPriceX96 := common.TickMathGetSqrtRatioAtTick(oppositeTick)

currentPriceX96 *= oppositeSqrtPriceX96 * oppositeSqrtPriceX96
currentToken = poolToken0
priceX96 *= oppositeSqrtPriceX96 * oppositeSqrtPriceX96
currentToken = token1Path
} else {
panic("[ROUTER] _RPC_api.gno__calculateTokenPrice() || wrong condition")
// wrong condition
// panic("[ROUTER] _RPC_api.gno__calculateTokenRatio() || wrong condition")
return 0

}

if proceed == numPools-1 {
for {
if currentPriceX96 < (consts.Q96 * 2) {
return currentPriceX96
proceed += 1

if proceed == len(routes) { // numHops
for { // remove as much X96 as possible
tempPriceX96 := priceX96
priceX96 /= consts.Q96

if priceX96 < consts.MIN_PRICE {
return tempPriceX96
}
currentPriceX96 /= consts.Q96
}
}

return calculateTokenPrice(currentToken, swapPath, numPools, proceed+1, currentPriceX96)
}

func sqrt(x bigint, n int) bigint {
result := bigint(1)
for i := 0; i < n; i++ {
result *= x
}
return result
return calculateTokenRatio(currentToken, routes, proceed, priceX96)
}

func getTokenList() []string {
seen := make(map[string]bool)
uniqueTokenList := []string{}
poolList := p.PoolGetPoolList()
poolList := pl.PoolGetPoolList()

for _, poolPath := range poolList {
token0Path, token1Path, _ := poolPathWithFeeDivide(poolPath)
Expand All @@ -166,26 +162,6 @@ func getTokenList() []string {
return uniqueTokenList
}

func makePoolPath(poolPath string, poolIndex int) string {
poolDatas := strings.Split(poolPath, ",")
// Calculate the indices for token paths and fee based on poolIndex.
baseIndex := poolIndex * 2
if baseIndex+2 >= len(poolDatas) {
panic(ufmt.Sprintf("[ROUTER] _RPC_api.gno__makePoolPath() || index out of range for pool index: %d", poolIndex))
}

token0Path := poolDatas[baseIndex]
token1Path := poolDatas[baseIndex+2]
fee := poolDatas[baseIndex+1]

// Ensure the tokens are in a consistent order.
if token0Path > token1Path {
token0Path, token1Path = token1Path, token0Path
}

return token0Path + ":" + token1Path + ":" + fee
}

func poolPathWithFeeDivide(poolPath string) (string, string, int) {
poolPathSplit := strings.Split(poolPath, ":")
require(len(poolPathSplit) == 3, ufmt.Sprintf("[ROUTER] _RPC_api.gno__poolPathWithFeeDivide() || len(poolPathSplit) != 3, poolPath: %s", poolPath))
Expand All @@ -197,19 +173,3 @@ func poolPathWithFeeDivide(poolPath string) (string, string, int) {

return poolPathSplit[0], poolPathSplit[1], feeInt
}

func singlePoolPathWithFeeDivide(poolPath string) (string, string) {
singlePoolPathSplit := strings.Split(poolPath, ":")
require(len(singlePoolPathSplit) == 2, ufmt.Sprintf("[ROUTER] _RPC_api.gno__singlePoolPathWithFeeDivide || len(singlePoolPathSplit) != 2, poolPath: %s", poolPath))

return singlePoolPathSplit[0], singlePoolPathSplit[1]
}

func removeItemFromStringArray(s []string, r string) []string {
for i, v := range s {
if v == r {
return append(s[:i], s[i+1:]...)
}
}
return s
}
32 changes: 15 additions & 17 deletions router/_TEST_INIT_basic_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ import (
"gno.land/r/demo/obl"

"gno.land/r/demo/wugnot"

"gno.land/r/demo/consts"
)

var (
test1 std.Address
poolAddr, posAddr, stakerAddr std.Address
test1 std.Address

barPath = "gno.land/r/demo/bar"
bazPath = "gno.land/r/demo/baz"
fooPath = "gno.land/r/demo/foo"
gnsPath = "gno.land/r/demo/gns"
oblPath = "gno.land/r/demo/obl"
quxPath = "gno.land/r/demo/qux"
wgnotPath = "gno.land/r/demo/wugnot"

Expand All @@ -36,30 +38,26 @@ var (

func init() {
test1 = std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5")

// prepare ugnot
testBanker := std.GetBanker(std.BankerTypeRealmIssue)
testBanker.IssueCoin(test1, "ugnot", 500_000_000_000_000)

poolAddr = std.DerivePkgAddr("gno.land/r/demo/pool")
posAddr = std.DerivePkgAddr("gno.land/r/demo/position")
stakerAddr = std.DerivePkgAddr("gno.land/r/demo/staker")

std.TestSetPrevAddr(test1)
gns.Approve(a2u(consts.POOL_ADDR), 500_000_000_000_000)

gns.Approve(a2u(poolAddr), 500_000_000_000_000)

bar.Approve(a2u(poolAddr), 500_000_000_000_000)
baz.Approve(a2u(poolAddr), 500_000_000_000_000)
foo.Approve(a2u(poolAddr), 500_000_000_000_000)
qux.Approve(a2u(poolAddr), 500_000_000_000_000)
bar.Approve(a2u(consts.POOL_ADDR), 500_000_000_000_000)
baz.Approve(a2u(consts.POOL_ADDR), 500_000_000_000_000)
foo.Approve(a2u(consts.POOL_ADDR), 500_000_000_000_000)
qux.Approve(a2u(consts.POOL_ADDR), 500_000_000_000_000)

obl.Approve(a2u(stakerAddr), 500_000_000_000_000) // to create external incentive
wugnot.Approve(a2u(stakerAddr), 500_000_000_000_000) // to create (native) external incentive
obl.Approve(a2u(consts.STAKER_ADDR), 500_000_000_000_000) // to create external incentive
wugnot.Approve(a2u(consts.STAKER_ADDR), 500_000_000_000_000) // to create (native) external incentive

wugnot.Approve(a2u(poolAddr), 500_000_000_000_000)
wugnot.Approve(a2u(consts.POOL_ADDR), 500_000_000_000_000)

std.TestSetPrevAddr(std.Address("g1paqttvcjcluuya9n9twyw7yacv54mt7ld3gvzm")) // IRA, r3v4_xxx: CHANGE WHEN DEPLOYING TO OFFICIAL NETWORK
gns.Approve(a2u(stakerAddr), 500_000_000_000_000) // to create internal incentive
std.TestSetPrevAddr(consts.INTERNAL_REWARD_ACCOUNT) // r3v4_xxx: CHANGE WHEN DEPLOYING TO OFFICIAL NETWORK
gns.Approve(a2u(consts.STAKER_ADDR), 500_000_000_000_000) // to create internal incentive
}

/* HELPER */
Expand Down
Loading

0 comments on commit d463822

Please sign in to comment.