Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GSW-793 feat support increase decrease liquidity for positions #177

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion position/liquidity_management.gno
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func addLiquidity(params AddLiquidityParams) (bigint, bigint, bigint) {
params.amount0Desired,
params.amount1Desired,
)
liquidity += 1 // adjust for rounding errors
liquidity += 2 // r3v4_xxx: adjust for rounding errors
notJoon marked this conversation as resolved.
Show resolved Hide resolved

pToken0, pToken1, pFee := poolKeyDivide(params.poolKey)
amount0, amount1 := pl.Mint(
Expand Down
208 changes: 185 additions & 23 deletions position/position.gno
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ var (
nextId uint64 = 1
)

func (p Position) isClear() bool {
return p.liquidity == 0 && p.tokensOwed0 == 0 && p.tokensOwed1 == 0
}

func Mint(
token0 string,
token1 string,
Expand Down Expand Up @@ -96,44 +92,199 @@ func mint(params MintParams) (uint64, bigint, bigint, bigint) {
return tokenId, liquidity, amount0, amount1
}

func CollectFee(tokenId uint64) (uint64, bigint, bigint, string) { // tokenId, tokensOwed0, tokensOwed1, poolPath
func IncreaseLiquidity(
tokenId uint64,
amount0Desired bigint,
amount1Desired bigint,
amount0Min bigint,
amount1Min bigint,
deadline bigint,
) (uint64, bigint, bigint, bigint) { // tokenId, liquidity, amount0, amount1
increaseLiquidityParams := IncreaseLiquidityParams{
tokenId: tokenId,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired,
amount0Min: amount0Min,
amount1Min: amount1Min,
deadline: deadline,
}

return increaseLiquidity(increaseLiquidityParams)
}

func increaseLiquidity(params IncreaseLiquidityParams) (uint64, bigint, bigint, bigint) {
// verify tokenId exists
require(exists(tokenId), ufmt.Sprintf("[POSITION] position.gno__CollectFee() || tokenId(%d) doesn't exist", tokenId))
require(exists(params.tokenId), ufmt.Sprintf("[POSITION] position.gno__increaseLiquidity() || tokenId(%d) doesn't exist", params.tokenId))

// verify owner or approved
isAuthorizedForToken(tokenId)
// MUST BE OWNER TO DECREASE LIQUIDITY
// can not be approved address > staked position can be modified
owner := gnft.OwnerOf(tid(params.tokenId))
require(owner == GetOrigCaller(), ufmt.Sprintf("[POSITION] position.gno__decreaseLiquidity() || only owner can decrease liquidity__owner(%s) == GetOrigCaller(%s)", owner, GetOrigCaller()))

position := positions[tokenId]
tokensOwed0, tokensOwed1 := position.tokensOwed0, position.tokensOwed1
checkDeadline(params.deadline)

pToken0, pToken1, pFee := poolKeyDivide(position.poolKey)
pl.Burn(pToken0, pToken1, pFee, position.tickLower, position.tickUpper, 0) // burn '0' liquidity to collect fee
position := positions[params.tokenId]
liquidity, amount0, amount1 := addLiquidity(
AddLiquidityParams{
poolKey: position.poolKey,
recipient: GetOrigPkgAddr(), // MUST BE POSITION
tickLower: position.tickLower,
tickUpper: position.tickUpper,
amount0Desired: params.amount0Desired,
amount1Desired: params.amount1Desired,
amount0Min: params.amount0Min,
amount1Min: params.amount1Min,
},
)

pool := pl.GetPoolFromPoolPath(position.poolKey)

positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper)

feeGrowthInside0LastX128, feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey)

position.tokensOwed0 += (feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128) * position.liquidity / consts.Q128
position.tokensOwed1 += (feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128) * position.liquidity / consts.Q128
notJoon marked this conversation as resolved.
Show resolved Hide resolved

position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128
position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128
position.liquidity += liquidity

positions[params.tokenId] = position

return params.tokenId, liquidity, amount0, amount1
}

func DecreaseLiquidity(
tokenId uint64,
liquidity bigint,
amount0Min bigint,
amount1Min bigint,
deadline bigint,
) (uint64, bigint, bigint, bigint) { // tokenId, liquidity, amount0, amount1
decreaseLiquidityParams := DecreaseLiquidityParams{
tokenId: tokenId,
liquidity: liquidity,
amount0Min: amount0Min,
amount1Min: amount1Min,
deadline: deadline,
}

return decreaseLiquidity(decreaseLiquidityParams)
}

func decreaseLiquidity(params DecreaseLiquidityParams) (uint64, bigint, bigint, bigint) {
// verify tokenId
require(exists(params.tokenId), ufmt.Sprintf("[POSITION] position.gno__decreaseLiquidity() || tokenId(%d) doesn't exist", params.tokenId))

// MUST BE OWNER TO DECREASE LIQUIDITY ( can not be approved address )
owner := gnft.OwnerOf(tid(params.tokenId))
require(owner == GetOrigCaller(), ufmt.Sprintf("[POSITION] position.gno__decreaseLiquidity() || only owner can decrease liquidity__owner(%s) == GetOrigCaller(%s)", owner, GetOrigCaller()))

checkDeadline(params.deadline)

require(params.liquidity >= 0, ufmt.Sprintf("[POSITION] position.gno__decreaseLiquidity() || liquidity(%d) >= 0", params.liquidity))

position := positions[params.tokenId]

positionLiquidity := position.liquidity
if positionLiquidity < params.liquidity {
// if too many liquidity requested, decrease to entire liquidity
params.liquidity = positionLiquidity
}

pToken0, pToken1, pFee := poolKeyDivide(position.poolKey)
pool := pl.GetPoolFromPoolPath(position.poolKey)

burnedAmount0, burnedAmount1 := pl.Burn(pToken0, pToken1, pFee, position.tickLower, position.tickUpper, params.liquidity)
require(burnedAmount0 >= params.amount0Min && burnedAmount1 >= params.amount1Min, ufmt.Sprintf("[POSITION] position.gno__decreaseLiquidity() || burnedAmount0(%d) >= amount0Min(%d) && burnedAmount1(%d) >= amount1Min(%d)", burnedAmount0, params.amount0Min, burnedAmount1, params.amount1Min))

positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper)
feeGrowthInside0LastX128, feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey)

tokensOwed0 += (feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128) * position.liquidity / consts.Q128
tokensOwed1 += (feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128) * position.liquidity / consts.Q128
position.tokensOwed0 += burnedAmount0 + ((feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128) * position.liquidity / consts.Q128)
position.tokensOwed1 += burnedAmount1 + ((feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128) * position.liquidity / consts.Q128)
notJoon marked this conversation as resolved.
Show resolved Hide resolved

position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128
position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128

position.liquidity = positionLiquidity - params.liquidity

positions[params.tokenId] = position

// GIVE BACK TO USER
amount0, amount1 := pl.Collect(
pToken0,
pToken1,
pFee,
GetOrigCaller(),
position.tickLower,
position.tickUpper,
tokensOwed0,
tokensOwed1,
burnedAmount0,
burnedAmount1,
)

position.tokensOwed0 -= amount0
position.tokensOwed1 -= amount1

positions[params.tokenId] = position

if position.isClear() {
// burnNFT(params.tokenId) // actual burn
burnPosition(params.tokenId) // just update flag
}

return params.tokenId, params.liquidity, amount0, amount1
}

func CollectFee(tokenId uint64) (uint64, bigint, bigint, string) { // tokenId, tokensOwed0, tokensOwed1, poolPath
// verify tokenId exists
require(exists(tokenId), ufmt.Sprintf("[POSITION] position.gno__CollectFee() || tokenId(%d) doesn't exist", tokenId))

// verify owner or approved
isAuthorizedForToken(tokenId)

position := positions[tokenId]

token0, token1, fee := poolKeyDivide(position.poolKey)
burnedAmount0, burnedAmount1 := pl.Burn(
token0,
token1,
fee,
position.tickLower,
position.tickUpper,
0, // burn '0' liquidity to collect fee
)

tokensOwed0, tokensOwed1 := position.tokensOwed0, position.tokensOwed1

// r3v4_xxx: DOES THIS NEED FOR COLLECT FEE
// positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper)
// pool := pl.GetPoolFromPoolPath(position.poolKey)
// feeGrowthInside0LastX128, feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey)

// tokensOwed0 += (feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128) * position.liquidity / consts.Q128
// tokensOwed1 += (feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128) * position.liquidity / consts.Q128

// position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128
// position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128

amount0, amount1 := pl.Collect(
token0,
token1,
fee,
GetOrigCaller(),
position.tickLower,
position.tickUpper,
burnedAmount0,
burnedAmount1,
)

// r3v4_xxx: DOES THIS NEED FOR COLLECT FEE
position.tokensOwed0, position.tokensOwed1 = tokensOwed0-amount0, tokensOwed1-amount1
positions[tokenId] = position

return tokenId, tokensOwed0, tokensOwed1, position.poolKey
return tokenId, amount0, amount1, position.poolKey
}

func Burn(tokenId uint64) (uint64, bigint, bigint, bigint, string) { // tokenId, liquidity, amount0, amount1, poolPath
Expand All @@ -149,8 +300,15 @@ func Burn(tokenId uint64) (uint64, bigint, bigint, bigint, string) { // tokenId,

pool := pl.GetPoolFromPoolPath(position.poolKey) // poolKey == poolPath

pToken0, pToken1, pFee := poolKeyDivide(position.poolKey)
burnAmount0, burnAmount1 := pl.Burn(pToken0, pToken1, pFee, position.tickLower, position.tickUpper, positionLiquidity)
token0, token1, fee := poolKeyDivide(position.poolKey)
burnAmount0, burnAmount1 := pl.Burn(
token0,
token1,
fee,
position.tickLower,
position.tickUpper,
positionLiquidity,
)

positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper)
feeGrowthInside0LastX128, feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey)
Expand All @@ -165,9 +323,9 @@ func Burn(tokenId uint64) (uint64, bigint, bigint, bigint, string) { // tokenId,
positions[tokenId] = position

collectAmount0, collectAmount1 := pl.Collect(
pToken0,
pToken1,
pFee,
token0,
token1,
fee,
GetOrigCaller(),
position.tickLower,
position.tickUpper,
Expand All @@ -180,7 +338,7 @@ func Burn(tokenId uint64) (uint64, bigint, bigint, bigint, string) { // tokenId,

positions[tokenId] = position

// burnNFT(tokenId) // DO NOT BURN NFT
// burnNFT(tokenId) // DO NOT BURN TO SHOW CLOSED POSITION
burnPosition(tokenId) // JUST UPDATE FLAG

return tokenId, positionLiquidity, collectAmount0, collectAmount1, position.poolKey
Expand Down Expand Up @@ -223,3 +381,7 @@ func deleteFromPositions(m map[uint64]Position, key uint64) map[uint64]Position

return m
}

func (p Position) isClear() bool {
return p.liquidity == 0 && p.tokensOwed0 == 0 && p.tokensOwed1 == 0
}
23 changes: 16 additions & 7 deletions position/type.gno
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type Position struct {
burned bool
}

type MintParams struct { // r3v4_xxx: private?
type MintParams struct {
token0 string
token1 string
fee uint16
Expand All @@ -39,7 +39,7 @@ type MintParams struct { // r3v4_xxx: private?
deadline bigint
}

type AddLiquidityParams struct { // r3v4_xxx: private?
type AddLiquidityParams struct {
poolKey string
recipient std.Address // XXX de facto: hardcoded to nft manager contract address
tickLower int32
Expand All @@ -50,7 +50,7 @@ type AddLiquidityParams struct { // r3v4_xxx: private?
amount1Min bigint
}

type IncreaseLiquidityParams struct { // r3v4_xxx: private?
type IncreaseLiquidityParams struct {
tokenId uint64
amount0Desired bigint
amount1Desired bigint
Expand All @@ -59,8 +59,17 @@ type IncreaseLiquidityParams struct { // r3v4_xxx: private?
deadline bigint
}

type DecreaseLiquidityParams struct { // r3v4_xxx: private?
tokenId uint64
liquidity bigint
deadline bigint
type DecreaseLiquidityParams struct {
tokenId uint64
liquidity bigint
amount0Min bigint
amount1Min bigint
deadline bigint
}

type CollectParams struct {
tokenId uint64
recipient std.Address
amount0Max bigint
amount1Max bigint
}