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

feat(reward): Make reward calculation logics into separate package #480

Closed
wants to merge 3 commits into from
Closed
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
1 change: 1 addition & 0 deletions contract/p/gnoswap/reward/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/gnoswap/reward
171 changes: 171 additions & 0 deletions contract/p/gnoswap/reward/reward.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package reward

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

u256 "gno.land/p/gnoswap/uint256"
)

type RewardInfo interface {
StartHeight() uint64
SetStartHeight(h uint64)

PriceDebt() *u256.Uint
SetPriceDebt(d *u256.Uint)

Amount() uint64
SetAmount(a uint64)

Claimed() uint64
SetClaimed(c uint64)
}

type BaseRewardInfo struct {
startHeight uint64
priceDebt *u256.Uint
amount uint64
claimed uint64
}

func (b *BaseRewardInfo) StartHeight() uint64 { return b.startHeight }
func (b *BaseRewardInfo) SetStartHeight(h uint64) { b.startHeight = h }
func (b *BaseRewardInfo) PriceDebt() *u256.Uint { return b.priceDebt }
func (b *BaseRewardInfo) SetPriceDebt(d *u256.Uint) { b.priceDebt = d }
func (b *BaseRewardInfo) Amount() uint64 { return b.amount }
func (b *BaseRewardInfo) SetAmount(a uint64) { b.amount = a }
func (b *BaseRewardInfo) Claimed() uint64 { return b.claimed }
func (b *BaseRewardInfo) SetClaimed(c uint64) { b.claimed = c }

type BaseRewardState struct {
info *avl.Tree
TotalStake uint64
PriceAccumulation *u256.Uint
}

func NewBaseRewardState() *BaseRewardState {
return &BaseRewardState{
info: avl.NewTree(),
TotalStake: 0,
PriceAccumulation: u256.Zero(),
}
}

func (s *BaseRewardState) Info(key string) (RewardInfo, bool) {
v, exists := s.info.Get(key)
if !exists {
return nil, false
}

return v.(RewardInfo), true
}

func (s *BaseRewardState) SetInfo(key string, r RewardInfo) {
s.info.Set(key, r)
}

func (s *BaseRewardState) PriceAccumulationUint64() uint64 {
return u256.Zero().Rsh(s.PriceAccumulation, 128).Uint64()
}

func (s *BaseRewardState) CalculateReward(key string) uint64 {
r, ok := s.Info(key)
if !ok {
return 0
}

diff := u256.Zero().Sub(s.PriceAccumulation, r.PriceDebt())
reward := u256.Zero().Mul(diff, u256.NewUint(r.Amount()))
reward = reward.Rsh(reward, 128)

alreadyClaimed := r.Claimed()
calculated := reward.Uint64()
if calculated <= alreadyClaimed {
return 0
}

return calculated - alreadyClaimed
}

func (s *BaseRewardState) AddStake(key string, amount uint64, currentHeight uint64) {
s.Finalize(currentHeight)
s.TotalStake += amount

if r, ok := s.Info(key); ok {
newDebt := updatePriceDebt(r.PriceDebt(), s.PriceAccumulation, amount, s.TotalStake)

r.SetPriceDebt(newDebt)
r.SetAmount(r.Amount() + amount)

s.SetInfo(key, r)
return
}

newInfo := &BaseRewardInfo{
startHeight: currentHeight,
priceDebt: s.PriceAccumulation.Clone(),
amount: amount,
claimed: 0,
}

s.SetInfo(key, newInfo)
}

func (s *BaseRewardState) RemoveStake(key string, amount uint64, currentHeight uint64) uint64 {
s.Finalize(currentHeight)

reward := s.deductReward(key)

s.info.Remove(key)
if s.TotalStake < amount {
s.TotalStake = 0
} else {
s.TotalStake -= amount
}

return reward
}

func (s *BaseRewardState) Claim(key string, currentHeight uint64) uint64 {
s.Finalize(currentHeight)
return s.deductReward(key)
}

func (s *BaseRewardState) Finalize(currentHeight uint64) {
// no-op: will be override
}

func (s *BaseRewardState) deductReward(key string) uint64 {
reward := s.CalculateReward(key)
if reward == 0 {
return 0
}

// update
r, ok := s.Info(key)
if !ok {
return 0
}

r.SetClaimed(r.Claimed() + reward)
s.SetInfo(key, r)

return reward
}

func updatePriceDebt(
oldDebt, priceAcc *u256.Uint,
addedAmount uint64,
newTotalStake uint64,
) *u256.Uint {
if newTotalStake == 0 {
return oldDebt
}

add := u256.Zero().Mul(priceAcc, u256.NewUint(addedAmount))
add = add.Div(add, u256.NewUint(newTotalStake))

newDebt := oldDebt.Clone()
newDebt.Add(newDebt, add)

return newDebt
}
7 changes: 7 additions & 0 deletions contract/r/gnoswap/launchpad/_helper_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ var (
adminAddr = consts.ADMIN
adminUser = common.AddrToUser(adminAddr)
adminRealm = std.NewUserRealm(adminAddr)

fooPath string = "gno.land/r/onbloc/foo"
barPath string = "gno.land/r/onbloc/bar"
bazPath string = "gno.land/r/onbloc/baz"
quxPath string = "gno.land/r/onbloc/qux"

oblPath string = "gno.land/r/onbloc/obl"
)

// MockXGNSToken implements basic functionality for testing xgns token
Expand Down
Loading