Skip to content

Commit

Permalink
GSW-872 feat: detailed condition to check staking reward
Browse files Browse the repository at this point in the history
  • Loading branch information
r3v4s committed Feb 19, 2024
1 parent 2cc6fe2 commit 128dd4f
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 31 deletions.
23 changes: 14 additions & 9 deletions staker/reward_math.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,24 @@ const (
TIMESTAMP_360DAYS = 31104000
)

type userClaimedReward map[std.Address]map[string]bigint // address -> rewardTokenPath -> rewardTokenAmount
type userClaimedReward map[std.Address]map[uint64]map[string]bigint // address -> lpTokenId -> rewardTokenPath -> rewardTokenAmount
var userClaimedRewards = make(userClaimedReward)

func userClaimedRewardAmount(address std.Address, rewardTokenPath string, rewardAmount bigint, claimeRequest bool) bigint {
func userClaimedRewardAmount(address std.Address, tokenId uint64, rewardTokenPath string, rewardAmount bigint, claimeRequest bool) bigint {
userClaimed, ok := userClaimedRewards[address] // if user(address) ever claimed reward
if !ok {
userClaimedRewards[address] = make(map[string]bigint)
userClaimedRewards[address] = make(map[uint64]map[string]bigint)
}

claimedReward, ok := userClaimedRewards[address][rewardTokenPath] // if user(address) ever claimed rewardTokenPath
claimedPosition, ok := userClaimedRewards[address][tokenId] // if user(address) ever claimed reward from tokenId
if !ok {
userClaimedRewards[address][tokenId] = make(map[string]bigint)
}

claimedReward, ok := userClaimedRewards[address][tokenId][rewardTokenPath] // if user(address) ever claimed rewardTokenPath
if !ok {
if claimeRequest { // if user is claiming rewardTokenPath
userClaimedRewards[address][rewardTokenPath] = rewardAmount // add rewardTokenPath to userClaimedRewards
userClaimedRewards[address][tokenId][rewardTokenPath] = rewardAmount // add rewardTokenPath to userClaimedRewards
return rewardAmount
} else { // if user is not claiming rewardTokenPath
return rewardAmount
Expand All @@ -39,7 +44,7 @@ func userClaimedRewardAmount(address std.Address, rewardTokenPath string, reward
var claimableAmount bigint
if claimeRequest { // if user is claiming rewardTokenPath
claimableAmount = rewardAmount - claimedReward
userClaimedRewards[address][rewardTokenPath] += claimableAmount
userClaimedRewards[address][tokenId][rewardTokenPath] += claimableAmount
return claimableAmount
} else { // if user is not claiming rewardTokenPath
claimableAmount = rewardAmount - claimedReward
Expand Down Expand Up @@ -83,12 +88,12 @@ func rewardMathComputeInternalRewardAmount(tokenId uint64, deposit Deposit) bigi

finalRewardQ := myWholeRewardX96 * bigint(rewardRatio)
finalReward := finalRewardQ / consts.Q96 / consts.Q96 / consts.Q96 / consts.Q96 / consts.Q96 / 100 / 100
// why do we need to divide by (Q96 ^ 5)
// divide by (Q96 ^ 5)
// #1 ~ 2: used by getPoolTierAndRatio()
// #3 : used by poolRewardPerBlockX96
// #4 ~ 5: used by getMyLiquidityRatio()

// why do we need to divide by (100 ^ 2)
// divide by (100 ^ 2)
// #1: to convert `listTierRatio()` return value to percentage
// #2: to convert `getRewardRatio()` return value to percentage

Expand All @@ -105,7 +110,7 @@ func rewardMathComputeExternalRewardAmount(tokenId uint64, deposit Deposit, ince
monthlyReward = incentive.rewardAmount / 3
case incentiveDuration > TIMESTAMP_90DAYS:
// 1 second reward == total reward amount / reward duration
monthlyReward = incentive.rewardAmount / bigint(incentiveDuration)
monthlyReward = incentive.rewardAmount / bigint(incentiveDuration) * 2592000 // 30 days
default:
panic(ufmt.Sprintf("[STAKER] reward_math.gno || incentiveDuration(%d) should be at least 90 days", incentiveDuration))
}
Expand Down
42 changes: 20 additions & 22 deletions staker/staker.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ import (
"gno.land/p/demo/common"
"gno.land/p/demo/ufmt"

gnft "gno.land/r/demo/gnft" // GNFT, Gnoswap NFT
gns "gno.land/r/demo/gns" // GNS, Gnoswap Share
"gno.land/r/demo/consts"

pl "gno.land/r/demo/pool"
pos "gno.land/r/demo/position"
)
"gno.land/r/demo/gnft"
"gno.land/r/demo/gns"

const (
INTERNAL_REWARD_ACCOUNT = std.Address("g1paqttvcjcluuya9n9twyw7yacv54mt7ld3gvzm") // r3v4_xxx: CHANGE WHEN DEPLOYING TO OFFICIAL NETWORK
pl "gno.land/r/demo/pool"
pn "gno.land/r/demo/position"
)

var (
Expand Down Expand Up @@ -106,11 +104,11 @@ func StakeToken(tokenId uint64) (string, bigint, bigint) {
)

// check tokenId has liquidity or not
liquidity := pos.PositionGetPositionLiquidity(tokenId)
liquidity := pn.PositionGetPositionLiquidity(tokenId)
require(liquidity > 0, ufmt.Sprintf("[STAKER] staker.gno__StakeToken() || tokenId(%d) has no liquidity", tokenId))

// check pool path from tokenid
poolKey := pos.PositionGetPositionPoolKey(tokenId)
poolKey := pn.PositionGetPositionPoolKey(tokenId)

// staked status
deposit := deposits[tokenId]
Expand All @@ -135,13 +133,13 @@ func CollectReward(
require(PrevRealmAddr() == deposit.owner, ufmt.Sprintf("[STAKER] staker.gno__CollectReward() || only owner(%s) can collect reward from tokenId(%d), PrevRealmAddr()(%s)", deposit.owner, tokenId, PrevRealmAddr()))

// poolPath to collect reward
poolPath := pos.PositionGetPositionPoolKey(tokenId)
poolPath := pn.PositionGetPositionPoolKey(tokenId)

// get all external reward list for this pool
for _, incentiveId := range poolIncentives[poolPath] {
incentive := incentives[incentiveId]
externalReward := rewardMathComputeExternalRewardAmount(tokenId, deposit, incentive) // external reward
externalReward = userClaimedRewardAmount(deposit.owner, incentive.rewardToken, bigint(externalReward), true)
externalReward = userClaimedRewardAmount(deposit.owner, tokenId, incentive.rewardToken, bigint(externalReward), true)

if externalReward > 0 {
transferByRegisterCall(incentive.rewardToken, deposit.owner, uint64(externalReward))
Expand All @@ -153,11 +151,11 @@ func CollectReward(

// default `Internal` reward
internalGNS := rewardMathComputeInternalRewardAmount(tokenId, deposit)
internalGNS = userClaimedRewardAmount(deposit.owner, "gno.land/r/demo/gns", bigint(internalGNS), true)
internalGNS = userClaimedRewardAmount(deposit.owner, tokenId, consts.GNS_PATH, bigint(internalGNS), true)

if internalGNS > 0 {
// transfer it
gns.TransferFrom(a2u(INTERNAL_REWARD_ACCOUNT), a2u(deposit.owner), uint64(internalGNS))
gns.TransferFrom(a2u(consts.INTERNAL_REWARD_ACCOUNT), a2u(deposit.owner), uint64(internalGNS))
}

}
Expand All @@ -170,13 +168,13 @@ func UnstakeToken(tokenId uint64) (string, bigint, bigint) {
require(PrevRealmAddr() == deposit.owner, ufmt.Sprintf("[STAKER] staker.gno__UnstakeToken() || only owner(%s) can unstake their token(%d), PrevRealmAddr()(%s)", deposit.owner, tokenId, PrevRealmAddr()))

// poolPath to unstake lp token
poolPath := pos.PositionGetPositionPoolKey(tokenId)
poolPath := pn.PositionGetPositionPoolKey(tokenId)

// get all external reward list for this pool
for _, incentiveId := range poolIncentives[poolPath] {
incentive := incentives[incentiveId]
externalReward := rewardMathComputeExternalRewardAmount(tokenId, deposit, incentive) // external reward
externalReward = userClaimedRewardAmount(deposit.owner, incentive.rewardToken, externalReward, true)
externalReward = userClaimedRewardAmount(deposit.owner, tokenId, incentive.rewardToken, externalReward, true)

if externalReward > 0 {
transferByRegisterCall(incentive.rewardToken, deposit.owner, uint64(externalReward))
Expand All @@ -188,10 +186,10 @@ func UnstakeToken(tokenId uint64) (string, bigint, bigint) {

// default `Internal` reward
internalGNS := rewardMathComputeInternalRewardAmount(tokenId, deposit)
internalGNS = userClaimedRewardAmount(deposit.owner, "gno.land/r/demo/gns", internalGNS, true)
internalGNS = userClaimedRewardAmount(deposit.owner, tokenId, consts.GNS_PATH, internalGNS, true)
if internalGNS > 0 {
// transfer it
gns.TransferFrom(a2u(INTERNAL_REWARD_ACCOUNT), a2u(deposit.owner), uint64(internalGNS))
gns.TransferFrom(a2u(consts.INTERNAL_REWARD_ACCOUNT), a2u(deposit.owner), uint64(internalGNS))
}

// unstaked status
Expand All @@ -214,7 +212,7 @@ func EndExternalIncentive(refundee, targetPoolPath, rewardToken string) {
// 1. admin can end incentive without refund
// 2. refundee can end incentive with refund
switch GetOrigCaller() {
case std.Address("g16kvq0mra3atvr07lkdwc2x6jqmna8a4kt0e85d"): // ADMIN
case consts.GNOSWAP_ADMIN: // r3v4_xxx:
// admin can end incentive without refund
incentives = deleteFromIncentives(incentives, incentiveId)
for i, v := range poolIncentives[targetPoolPath] {
Expand Down Expand Up @@ -281,18 +279,18 @@ func deleteFromIncentives(m map[string]Incentive, key string) map[string]Incenti
}

func getTokenPairBalanceFromPosition(tokenId uint64) (bigint, bigint) {
poolKey := pos.PositionGetPositionPoolKey(tokenId)
poolKey := pn.PositionGetPositionPoolKey(tokenId)

pool := pl.GetPoolFromPoolPath(poolKey)
currentX96 := pool.PoolGetSlot0SqrtPriceX96()
lowerX96 := common.TickMathGetSqrtRatioAtTick(pos.PositionGetPositionTickLower(tokenId))
upperX96 := common.TickMathGetSqrtRatioAtTick(pos.PositionGetPositionTickUpper(tokenId))
lowerX96 := common.TickMathGetSqrtRatioAtTick(pn.PositionGetPositionTickLower(tokenId))
upperX96 := common.TickMathGetSqrtRatioAtTick(pn.PositionGetPositionTickUpper(tokenId))

token0Balance, token1Balance := common.LiquidityAmountsGetAmountsForLiquidity(
currentX96,
lowerX96,
upperX96,
pos.PositionGetPositionLiquidity(tokenId),
pn.PositionGetPositionLiquidity(tokenId),
)

return token0Balance, token1Balance
Expand Down

0 comments on commit 128dd4f

Please sign in to comment.