Skip to content

Commit

Permalink
Merge pull request #154 from gnoswap-labs/GSW-856-feat-mutex-lock
Browse files Browse the repository at this point in the history
GSW-856 feat: mutex lock
  • Loading branch information
notJoon authored Feb 7, 2024
2 parents 8f2f0ed + 0a30238 commit d36f146
Show file tree
Hide file tree
Showing 17 changed files with 109 additions and 95 deletions.
6 changes: 3 additions & 3 deletions pool/_RPC_api.gno
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ func ApiGetPools() string {
return string(rr)
}

func rpcMakePool(poolKey string) RpcPool {
func rpcMakePool(poolPath string) RpcPool {
rpcPool := RpcPool{}
pool := GetPoolFromPoolKey(poolKey)
pool := GetPoolFromPoolPath(poolPath)

rpcPool.PoolPath = poolKey
rpcPool.PoolPath = poolPath

rpcPool.Token0Path = pool.token0Path
rpcPool.Token1Path = pool.token1Path
Expand Down
6 changes: 3 additions & 3 deletions pool/_RPC_dry.gno
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
)

func DrySwap(
pToken0Path string,
pToken1Path string,
token0Path string,
token1Path string,
pFee uint16,
recipient std.Address,
zeroForOne bool,
Expand All @@ -22,7 +22,7 @@ func DrySwap(
return 0, 0, false
}

pool := GetPool(pToken0Path, pToken1Path, pFee)
pool := GetPool(token0Path, token1Path, pFee)
slot0Start := pool.slot0

if zeroForOne {
Expand Down
1 change: 0 additions & 1 deletion pool/bit_math.gno
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package pool

import (
"gno.land/p/demo/ufmt"

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

Expand Down
1 change: 0 additions & 1 deletion pool/emergency_halt.gno
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"std"

"gno.land/p/demo/ufmt"

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

Expand Down
90 changes: 46 additions & 44 deletions pool/pool.gno
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,26 @@ import (
"std"

"gno.land/p/demo/ufmt"

"gno.land/r/demo/consts"

g "gno.land/r/demo/gov"
)

// only position contract can call this function
func Mint(
pToken0Path string,
pToken1Path string,
pFee uint16,
token0Path string,
token1Path string,
fee uint16,
recipient std.Address,
tickLower int32,
tickUpper int32,
liquidityAmount bigint,
) (bigint, bigint) {
require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Mint() || PrevRealmPath(%s) == \"gno.land/r/demo/position\"", PrevRealmPath()))
require(PrevRealmPath() == consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Mint() || PrevRealmPath(%s) == \"gno.land/r/demo/position\"", PrevRealmPath()))

require(liquidityAmount > 0, ufmt.Sprintf("[POOL] pool.gno__Mint() || liquidityAmount(%d) > 0", liquidityAmount))

pool := GetPool(pToken0Path, pToken1Path, pFee)
pool := GetPool(token0Path, token1Path, fee)
_, amount0Int, amount1Int := pool.modifyPosition(
ModifyPositionParams{
recipient, // owner
Expand Down Expand Up @@ -87,18 +87,18 @@ func Mint(

// only position contract can call this function
func Burn(
pToken0Path string,
pToken1Path string,
pFee uint16,
token0Path string,
token1Path string,
fee uint16,
tickLower int32,
tickUpper int32,
amount bigint,
) (bigint, bigint) {
require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Burn() || caller(%s) must be position contract", PrevRealmPath()))
require(PrevRealmPath() == consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Burn() || caller(%s) must be position contract", PrevRealmPath()))

requireUnsigned(amount, ufmt.Sprintf("[POOL] pool.gno__Burn() || amount(%d) >= 0", amount))

pool := GetPool(pToken0Path, pToken1Path, pFee)
pool := GetPool(token0Path, token1Path, fee)

position, amount0Int, amount1Int := pool.modifyPosition(
ModifyPositionParams{
Expand All @@ -121,73 +121,75 @@ func Burn(
key := positionGetKey(PrevRealmAddr(), tickLower, tickUpper)
pool.positions[key] = position

// actual token transfer happens in Collect()
return amount0, amount1
}

// only position contract can call this function
func Collect(
pToken0Path string,
pToken1Path string,
pFee uint16,
token0Path string,
token1Path string,
fee uint16,
recipient std.Address,
tickLower int32,
tickUpper int32,
amount0Requested bigint,
amount1Requested bigint,
) (bigint, bigint) {
require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Collect() || caller(%s) must be position contract(gno.land/r/demo/position)", PrevRealmPath()))
require(PrevRealmPath() == consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Collect() || caller(%s) must be position contract(gno.land/r/demo/position)", PrevRealmPath()))

requireUnsigned(amount0Requested, ufmt.Sprintf("pool.gno__Collect() || amount0Requested(%d) >= 0", amount0Requested))
requireUnsigned(amount1Requested, ufmt.Sprintf("pool.gno__Collect() || amount1Requested(%d) >= 0", amount1Requested))

pool := GetPool(pToken0Path, pToken1Path, pFee)
pool := GetPool(token0Path, token1Path, fee)

key := positionGetKey(PrevRealmAddr(), tickLower, tickUpper)
position, exist := pool.positions[key]
require(exist, ufmt.Sprintf("[POOL] pool.gno__Collect() || position(%s) does not exist", key))

// Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0
amount0 := min(amount0Requested, position.tokensOwed0)
amount0 = min(amount0, pool.balances.token0)
requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount0(%d) >= 0", amount0))

amount1 := min(amount1Requested, position.tokensOwed1)
requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount1(%d) >= 0", amount1))

require(pool.balances.token0 >= amount0, ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token0(%d) >= amount0(%d)", pool.balances.token0, amount0))
// Update state first then transfer
position.tokensOwed0 -= amount0
pool.balances.token0 -= amount0
transferByRegisterCall(pool.token0Path, recipient, uint64(amount0))
requireUnsigned(pool.balances.token0, ufmt.Sprintf("[POOL] pool.gno__Burn() || pool.balances.token0(%d) >= 0", pool.balances.token0))

require(pool.balances.token1 >= amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token1(%d) >= amount1(%d)", pool.balances.token1, amount1))
transferByRegisterCall(pool.token1Path, recipient, uint64(amount1))
// Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0
amount1 := min(amount1Requested, position.tokensOwed1)
amount1 = min(amount1, pool.balances.token1)
requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount1(%d) >= 0", amount1))

// adjust position
position.tokensOwed0 -= amount0
// Update state first then transfer
position.tokensOwed1 -= amount1
pool.positions[key] = position

// adjust pool
pool.balances.token0 -= amount0
pool.balances.token1 -= amount1

requireUnsigned(pool.balances.token0, ufmt.Sprintf("[POOL] pool.gno__Burn() || pool.balances.token0(%d) >= 0", pool.balances.token0))
transferByRegisterCall(pool.token1Path, recipient, uint64(amount1))
requireUnsigned(pool.balances.token1, ufmt.Sprintf("[POOL] pool.gno__Burn() || pool.balances.token1(%d) >= 0", pool.balances.token1))

pool.positions[key] = position

return amount0, amount1
}

func Swap(
pToken0Path string,
pToken1Path string,
pFee uint16,
token0Path string,
token1Path string,
fee uint16,
recipient std.Address,
zeroForOne bool,
amountSpecified bigint,
sqrtPriceLimitX96 bigint,
payer std.Address, // router
) (bigint, bigint) {
require(PrevRealmPath() == "gno.land/r/demo/router", ufmt.Sprintf("[POOL] pool.gno__Swap() || caller(%s) must be router contract(gno.land/r/demo/router)", PrevRealmPath()))
require(PrevRealmPath() == consts.ROUTER_PATH, ufmt.Sprintf("[POOL] pool.gno__Swap() || caller(%s) must be router contract(gno.land/r/demo/router)", PrevRealmPath()))

// early panic
require(amountSpecified != 0, "[POOL] pool.gno__Swap() || amountSpecified can't be zero")

pool := GetPool(pToken0Path, pToken1Path, pFee)
pool := GetPool(token0Path, token1Path, fee)
slot0Start := pool.slot0
require(slot0Start.unlocked, "[POOL] pool.gno__Swap() || slot0 must be unlocked")

Expand Down Expand Up @@ -377,9 +379,9 @@ func Swap(
if zeroForOne {
// payer > pool
balance0Before := bigint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()))
ok := transferFromByRegisterCall(pool.token0Path, payer, consts.ADDR_POOL, uint64(amount0))
ok := transferFromByRegisterCall(pool.token0Path, payer, consts.POOL_ADDR, uint64(amount0))
if !ok {
panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token0Path, payer, ADDR_POOL, uint64(amount0)) failed")
panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token0Path, payer, POOL_ADDR, uint64(amount0)) failed")
}

require(
Expand All @@ -393,7 +395,7 @@ func Swap(
require(pool.balances.token0 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%d) >= 0__#1", pool.balances.token0))

if amount1 < 0 { // pool > recipient
require(pool.balances.token1 > (-amount1), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%d) > (-1 * amount1)(%d)", pool.balances.token1, (-amount1)))
require(pool.balances.token1 > -amount1, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%d) > (-1 * amount1)(%d)", pool.balances.token1, (-amount1)))
ok := transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1))
if !ok {
panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) failed")
Expand All @@ -406,9 +408,9 @@ func Swap(
} else {
// payer > pool
balance1Before := bigint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()))
ok := transferFromByRegisterCall(pool.token1Path, payer, consts.ADDR_POOL, uint64(amount1))
ok := transferFromByRegisterCall(pool.token1Path, payer, consts.POOL_ADDR, uint64(amount1))
if !ok {
panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token1Path, payer, ADDR_POOL, uint64(amount1)) failed")
panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token1Path, payer, POOL_ADDR, uint64(amount1)) failed")
}

require(
Expand Down Expand Up @@ -460,9 +462,9 @@ func SetFeeProtocol(

// ADMIN
func CollectProtocol(
pToken0Path string,
pToken1Path string,
pFee uint16,
token0Path string,
token1Path string,
fee uint16,
recipient std.Address,
amount0Requested bigint,
amount1Requested bigint,
Expand All @@ -471,7 +473,7 @@ func CollectProtocol(
requireUnsigned(amount1Requested, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount1Requested(%d) >= 0", amount1Requested))
require(isAdmin(PrevRealmAddr()), ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || caller(%s) must be admin", PrevRealmAddr()))

pool := GetPool(pToken0Path, pToken1Path, pFee)
pool := GetPool(token0Path, token1Path, fee)

amount0 := min(amount0Requested, pool.protocolFees.token0)
requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount0(%d) >= 0", amount0))
Expand Down
55 changes: 28 additions & 27 deletions pool/pool_manager.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import (

"gno.land/p/demo/ufmt"

"gno.land/r/demo/consts"

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

var (
admins []std.Address
initialized bool = false

feeAmountTickSpacing map[uint16]int32 = make(map[uint16]int32) // map[fee_amount]tick_spacing
pools map[string]*Pool = make(map[string]*Pool) // map[pool_key]*Pool
feeAmountTickSpacing map[uint16]int32 = make(map[uint16]int32) // map[feeAmount]tick_spacing
pools map[string]*Pool = make(map[string]*Pool) // map[poolPath]*Pool
)

func InitManual() {
Expand All @@ -28,16 +30,16 @@ func InitManual() {
}

func CreatePool(
tokenAPath string,
tokenBPath string,
token0Path string,
token1Path string,
fee uint16,
sqrtPriceX96 bigint,
) *Pool {
require(initialized, "[POOl] pool_manager.gno__CreatePool() || contract must be initialized")
require(tokenAPath != tokenBPath, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || token pair cannot be the same__tokenAPath(%s) != tokenBPath(%s)", tokenAPath, tokenBPath))
require(token0Path != token1Path, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || token pair cannot be the same__token0Path(%s) != token1Path(%s)", token0Path, token1Path))

if tokenBPath < tokenAPath {
tokenAPath, tokenBPath = tokenBPath, tokenAPath
if token1Path < token0Path {
token0Path, token1Path = token1Path, token0Path
tick := -(TickMathGetTickAtSqrtRatio(sqrtPriceX96))
sqrtPriceX96 = TickMathGetSqrtRatioAtTick(tick)
}
Expand All @@ -46,42 +48,41 @@ func CreatePool(
tickSpacing := feeAmountTickSpacing[fee]
require(tickSpacing > 0, ufmt.Sprintf("[POOL] pool_manager.gno__CreatePool() || tickSpacing(%d) > 0", tickSpacing))

// calculate poolKey
poolKey := GetPoolKey(tokenAPath, tokenBPath, fee)
// calculate poolPath
poolPath := GetPoolPath(token0Path, token1Path, fee)

// check whether the pool already exist
pool, exist := pools[poolKey]
require(!exist, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || pool(%s) already exist", poolKey))
pool, exist := pools[poolPath]
require(!exist, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || pool(%s) already exist", poolPath))

if !exist {
// 500 GNS as creation fee
// recipent is same address that receives protocol fee
// r3v4_xxx: change address when publish
gns.TransferFrom(a2u(std.GetOrigCaller()), a2u(std.Address("g1vaekzh6lta047h6lta047h6lta047h6lutmjdk")), 500)
// recipient is same address that receives protocol fee
// r3v4_xxx: change admin address when publish
gns.TransferFrom(a2u(std.GetOrigCaller()), a2u(consts.GNOSWAP_ADMIN), consts.POOL_CREATION_FEE)

pool = newPool(tokenAPath, tokenBPath, fee, tickSpacing, sqrtPriceX96)
pools[poolKey] = pool
pool = newPool(token0Path, token1Path, fee, tickSpacing, sqrtPriceX96)
pools[poolPath] = pool
}

return pool
}

func GetPool(token0, token1 string, fee uint16) *Pool {
poolKey := GetPoolKey(token0, token1, fee)
pool, exist := pools[poolKey]
require(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPool() || pool(%s) not found", poolKey))
func GetPool(token0Path, token1Path string, fee uint16) *Pool {
poolPath := GetPoolPath(token0Path, token1Path, fee)
pool, exist := pools[poolPath]
require(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPool() || pool(%s) not found", poolPath))

return pool
}

func GetPoolFromPoolKey(poolKey string) *Pool {
pool, exist := pools[poolKey]
require(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPoolFromPoolKey() || pool(%s) not found", poolKey))
func GetPoolFromPoolPath(poolPath string) *Pool {
pool, exist := pools[poolPath]
require(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPoolFromPoolPath() || pool(%s) not found", poolPath))

return pool
}

func GetPoolKey(token0Path, token1Path string, fee uint16) string {
func GetPoolPath(token0Path, token1Path string, fee uint16) string {
if token0Path < token1Path {
return token0Path + ":" + token1Path + ":" + strconv.Itoa(int(fee))
} else {
Expand All @@ -94,7 +95,7 @@ func AddAdmin(addr std.Address) {
if isAdmin(caller) {
admins = append(admins, addr)
} else {
panic("[POOL] pool_manager.gno__AddAdmin() || caller is not admin") // r3v4_xx: detailed error message ??
panic("[POOL] pool_manager.gno__AddAdmin() || caller is not admin")
}
}

Expand All @@ -112,7 +113,7 @@ func RemoveAdmin(addr std.Address) {
}
}
} else {
panic("[POOL] pool_manager.gno__RemoveAdmin() || caller is not admin") // r3v4_xx: detailed error message ??
panic("[POOL] pool_manager.gno__RemoveAdmin() || caller is not admin")
}
}

Expand Down
Loading

0 comments on commit d36f146

Please sign in to comment.