Skip to content

Commit

Permalink
Separate function for ositions.
Browse files Browse the repository at this point in the history
  • Loading branch information
stas committed Feb 29, 2024
1 parent f297b18 commit d6ca32b
Showing 1 changed file with 111 additions and 73 deletions.
184 changes: 111 additions & 73 deletions contracts/LpSugar.vy
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ MAX_TOKENS: constant(uint256) = 2000
MAX_LPS: constant(uint256) = 500
MAX_EPOCHS: constant(uint256) = 200
MAX_REWARDS: constant(uint256) = 16
MAX_POSITIONS: constant(uint256) = 10
MAX_POSITIONS: constant(uint256) = 500
MAX_PRICES: constant(uint256) = 20
WEEK: constant(uint256) = 7 * 24 * 60 * 60
Q96: constant(uint160) = 2**96

# Slot0 from CLPool.sol
struct Slot:
Expand All @@ -31,16 +32,17 @@ struct GaugeFees:
token0: uint128
token1: uint128

# Position Principal from Slipstream SugarHelper
struct PositionPrincipal:
struct Amounts:
amount0: uint256
amount1: uint256

# Position from NonfungiblePositionManager.sol (NFT)
struct PositionData:
nonce: uint96
operator: address
poolId: uint80
token0: address
token1: address
tickSpacing: uint24
tickLower: int24
tickUpper: int24
liquidity: uint128
Expand All @@ -64,6 +66,7 @@ struct TickInfo:

struct Position:
id: uint256 # NFT ID on v3, 0 on v2
lp: address
liquidity: uint256 # Liquidity amount on v3, amount of LP tokens on v2
staked: uint256 # liq amount staked on v3, amount of staked LP tokens on v2
amount0: uint256 # amount of unstaked token0 on both v2 and v3
Expand Down Expand Up @@ -131,8 +134,6 @@ struct Lp:
token0_fees: uint256
token1_fees: uint256

positions: DynArray[Position, MAX_POSITIONS]

struct LpEpochReward:
token: address
amount: uint256
Expand Down Expand Up @@ -170,7 +171,7 @@ interface IPoolFactory:
def allPoolsLength() -> uint256: view
def allPools(_index: uint256) -> address: view
def getFee(_pool_addr: address, _stable: bool) -> uint256: view
def getPool(_token0: address, _token1: address, _fee: uint24) -> address: view
def getPool(_token0: address, _token1: address, _fee: int24) -> address: view

interface IPool:
def token0() -> address: view
Expand Down Expand Up @@ -238,17 +239,17 @@ interface IReward:
def earned(_token: address, _venft_id: uint256) -> uint256: view

interface ISlipstreamHelper:
def getAmountsForLiquidity(_ratio: uint160, _ratioA: uint160, _ratioB: uint160, _liquidity: uint128) -> PositionPrincipal: view
def getAmountsForLiquidity(_ratio: uint160, _ratioA: uint160, _ratioB: uint160, _liquidity: uint128) -> Amounts: view
def getSqrtRatioAtTick(_tick: int24) -> uint160: view
def principal(_nfpm: address, _position_id: uint256, _ratio: uint160) -> PositionPrincipal: view
def poolFees(_pool: address, _liquidity: uint128, _current_tick: int24, _lower_tick: int24, _upper_tick: int24) -> PositionPrincipal: view
def principal(_nfpm: address, _position_id: uint256, _ratio: uint160) -> Amounts: view
def poolFees(_pool: address, _liquidity: uint128, _current_tick: int24, _lower_tick: int24, _upper_tick: int24) -> Amounts: view

# Vars
registry: public(IFactoryRegistry)
voter: public(IVoter)
convertor: public(address)
nfpm: public(INFPositionManager)
slipstream_helper: public(ISlipstreamHelper)
cl_helper: public(ISlipstreamHelper)

# Methods

Expand All @@ -262,7 +263,7 @@ def __init__(_voter: address, _registry: address, _convertor: address, \
self.registry = IFactoryRegistry(_registry)
self.nfpm = INFPositionManager(_nfpm)
self.convertor = _convertor
self.slipstream_helper = ISlipstreamHelper(_slipstream_helper)
self.cl_helper = ISlipstreamHelper(_slipstream_helper)

@internal
@view
Expand Down Expand Up @@ -448,6 +449,23 @@ def _token(_address: address, _account: address) -> Token:
listed: self.voter.isWhitelistedToken(_address)
})

@external
@view
def positions(_account: address) -> DynArray[Position, MAX_POSITIONS]:
"""
@notice Returns a collection of positions
@param _account The account to fetch positions for
@return Array for Lp structs
"""
positions: DynArray[Position, MAX_POSITIONS] = \
empty(DynArray[Position, MAX_POSITIONS])

if _account == empty(address):
return positions

# positions = self._cl_positions(_account, pool_data[0])
return positions

@external
@view
def all(_limit: uint256, _offset: uint256, _account: address) \
Expand Down Expand Up @@ -495,7 +513,6 @@ def byIndex(_index: uint256, _account: address) -> Lp:
offset = _index - 1

pools: DynArray[address[4], MAX_POOLS] = self._pools(1, offset)

pool_data: address[4] = pools[_index]
pool: IPool = IPool(pool_data[1])
token0: address = pool.token0()
Expand Down Expand Up @@ -579,6 +596,7 @@ def _byData(_data: address[4], _token0: address, _token1: address, \
positions.append(
Position({
id: 0,
lp: pool.address,
liquidity: acc_balance,
staked: acc_staked,
amount0: (acc_balance * reserve0) / pool_liquidity,
Expand Down Expand Up @@ -628,10 +646,77 @@ def _byData(_data: address[4], _token0: address, _token1: address, \
unstaked_fee: 0,
token0_fees: token0_fees,
token1_fees: token1_fees,

positions: positions
})

@internal
@view
def _cl_positions(_account: address, _factory: address) \
-> DynArray[Position, MAX_POSITIONS]:
factory: IPoolFactory = IPoolFactory(_factory)
positions_count: uint256 = 0
positions: DynArray[Position, MAX_POSITIONS] = \
empty(DynArray[Position, MAX_POSITIONS])

if _account != empty(address):
positions_count = self.nfpm.balanceOf(_account)

for index in range(0, MAX_POSITIONS):
if index >= positions_count:
break

staked: bool = False
pos: Position = empty(Position)
pos.id = self.nfpm.tokenOfOwnerByIndex(_account, index)

data: PositionData = self.nfpm.positions(pos.id)

pos.liquidity = convert(data.liquidity, uint256)
pos.tick_lower = data.tickLower
pos.tick_upper = data.tickUpper

pos.price_lower = self.cl_helper.getSqrtRatioAtTick(pos.tick_lower)
pos.price_upper = self.cl_helper.getSqrtRatioAtTick(pos.tick_upper)
pos.price_lower = (pos.price_lower / Q96)**2
pos.price_upper = (pos.price_lower / Q96)**2

pos.unstaked_earned0 = convert(data.tokensOwed0, uint256)
pos.unstaked_earned1 = convert(data.tokensOwed1, uint256)

pos.lp = factory.getPool(
data.token0,
data.token1,
convert(data.tickSpacing, int24)
)

if pos.lp == empty(address):
continue

pool: IPool = IPool(pos.lp)
gauge: ICLGauge = ICLGauge(self.voter.gauges(pos.lp))
slot: Slot = pool.slot0()

amounts: Amounts = self.cl_helper.principal(
self.nfpm.address, pos.id, slot.sqrtPriceX96
)
pos.amount0 = amounts.amount0
pos.amount1 = amounts.amount1

if gauge.address != empty(address):
pos.emissions_earned = gauge.earned(_account, pos.id)
staked = gauge.stakedContains(_account, pos.id)

if staked:
pos.staked = pos.liquidity
pos.staked0 = pos.amount0
pos.staked1 = pos.amount1
pos.amount0 = 0
pos.amount1 = 0
pos.liquidity = 0

positions.append(pos)

return positions

@internal
@view
def _byDataCL(_data: address[4], _token0: address, _token1: address, \
Expand All @@ -652,7 +737,6 @@ def _byDataCL(_data: address[4], _token0: address, _token1: address, \
emissions_token: address = empty(address)
token0: IERC20 = IERC20(_token0)
token1: IERC20 = IERC20(_token1)
positions_count: uint256 = 0
staked0: uint256 = 0
staked1: uint256 = 0
tick_spacing: int24 = pool.tickSpacing()
Expand All @@ -662,78 +746,34 @@ def _byDataCL(_data: address[4], _token0: address, _token1: address, \
token1_fees: uint256 = 0

slot: Slot = pool.slot0()
positions: DynArray[Position, MAX_POSITIONS] = \
empty(DynArray[Position, MAX_POSITIONS])

if _account != empty(address):
positions_count = self.nfpm.balanceOf(_account)

if gauge.address == empty(address):
unstaked_fees: PositionPrincipal = self.slipstream_helper.poolFees(_data[1], pool_liquidity, slot.tick, slot.tick, slot.tick + tick_spacing)
unstaked_fees: Amounts = self.cl_helper.poolFees(
_data[1], pool_liquidity, slot.tick, slot.tick, slot.tick + tick_spacing
)
token0_fees = unstaked_fees.amount0
token1_fees = unstaked_fees.amount1
else:
fee_voting_reward = gauge.feesVotingReward()
emissions_token = gauge.rewardToken()

ratio_a: uint160 = self.slipstream_helper.getSqrtRatioAtTick(slot.tick)
ratio_b: uint160 = self.slipstream_helper.getSqrtRatioAtTick(slot.tick + tick_spacing)
tick_staked: PositionPrincipal = self.slipstream_helper.getAmountsForLiquidity(slot.sqrtPriceX96, ratio_a, ratio_b, gauge_liquidity)
ratio_a: uint160 = self.cl_helper.getSqrtRatioAtTick(slot.tick)
ratio_b: uint160 = self.cl_helper.getSqrtRatioAtTick(slot.tick + tick_spacing)
tick_staked: Amounts = self.cl_helper.getAmountsForLiquidity(
slot.sqrtPriceX96, ratio_a, ratio_b, gauge_liquidity
)
staked0 = tick_staked.amount0
staked1 = tick_staked.amount1

staked_fees: PositionPrincipal = self.slipstream_helper.poolFees(_data[1], gauge_liquidity, slot.tick, slot.tick, slot.tick + tick_spacing)
staked_fees: Amounts = self.cl_helper.poolFees(
_data[1], gauge_liquidity, slot.tick, slot.tick, slot.tick + tick_spacing
)
token0_fees = staked_fees.amount0
token1_fees = staked_fees.amount1

if gauge_alive:
emissions = gauge.rewardRate()

for index in range(0, MAX_POSITIONS):
if index >= positions_count:
break

position_id: uint256 = self.nfpm.tokenOfOwnerByIndex(_account, index)
position_data: PositionData = self.nfpm.positions(position_id)
position_principal: PositionPrincipal = self.slipstream_helper.principal(self.nfpm.address, position_id, slot.sqrtPriceX96)
position_amount0: uint256 = 0
position_amount1: uint256 = 0
position_staked0: uint256 = 0
position_staked1: uint256 = 0

emissions_earned: uint256 = 0
staked: bool = False

if gauge.address != empty(address):
emissions_earned = gauge.earned(_account, position_id)
staked = gauge.stakedContains(_account, position_id)

if staked:
position_staked0 = position_principal.amount0
position_staked1 = position_principal.amount1
else:
position_amount0 = position_principal.amount0
position_amount1 = position_principal.amount1

positions.append(
Position({
id: position_id,
liquidity: convert(position_data.liquidity, uint256),
staked: convert(staked, uint256),
amount0: position_amount0,
amount1: position_amount1,
staked0: position_staked0,
staked1: position_staked1,
unstaked_earned0: convert(position_data.tokensOwed0, uint256),
unstaked_earned1: convert(position_data.tokensOwed1, uint256),
emissions_earned: emissions_earned,
tick_lower: position_data.tickLower,
tick_upper: position_data.tickUpper,
price_lower: self.slipstream_helper.getSqrtRatioAtTick(position_data.tickLower),
price_upper: self.slipstream_helper.getSqrtRatioAtTick(position_data.tickUpper),
})
)

return Lp({
lp: pool.address,
symbol: "",
Expand Down Expand Up @@ -767,8 +807,6 @@ def _byDataCL(_data: address[4], _token0: address, _token1: address, \
unstaked_fee: convert(pool.unstakedFee(), uint256),
token0_fees: token0_fees,
token1_fees: token1_fees,

positions: positions
})

@external
Expand Down

0 comments on commit d6ca32b

Please sign in to comment.