protocol_fee_reward_manager.gno
8.49 Kb · 243 lines
1package staker
2
3import (
4 bptree "gno.land/p/nt/bptree/v0"
5 ufmt "gno.land/p/nt/ufmt/v0"
6
7 u256 "gno.land/p/gnoswap/uint256/v1"
8)
9
10// ProtocolFeeRewardManager distributes protocol fees to stakers.
11//
12// Protocol fees arrive in many tokens, so the manager keeps one accumulator per token
13// instead of a single one. A stake change never touches those accumulators: it advances
14// the accrual epoch, records the total stake in force from that epoch, and appends one
15// event for an ordinary add/remove. Redelegate performs a remove plus an add and
16// therefore appends two events. The per-token work happens when a token is collected,
17// so stake-change cost does not depend on how many tokens ever collected a fee.
18type ProtocolFeeRewardManager struct {
19 // rewardStates maps a reward ID to the ProtocolFeeRewardState tracking that staker
20 rewardStates *bptree.BPTree // rewardID -> *ProtocolFeeRewardState
21 // tokenAccumulators maps a token path to its ProtocolFeeTokenAccumulator
22 tokenAccumulators *bptree.BPTree // tokenPath -> *ProtocolFeeTokenAccumulator
23 // currentEpoch is the accrual epoch in force, mirrored from protocol_fee
24 currentEpoch int64
25 // totalStakedAmount is the total amount currently staked
26 totalStakedAmount int64
27 // totalStakedHistory records the total staked amount in force from each epoch
28 totalStakedHistory *UintTree // epoch -> int64
29 // accumulatedTimestamp is the last timestamp at which the accrual state moved: a fold or a stake change
30 accumulatedTimestamp int64
31}
32
33// NewProtocolFeeRewardManager creates a new instance of ProtocolFeeRewardManager.
34//
35// Returns:
36// - *ProtocolFeeRewardManager: new protocol fee reward manager instance
37func NewProtocolFeeRewardManager() *ProtocolFeeRewardManager {
38 totalStakedHistory := NewUintTree()
39 totalStakedHistory.Set(0, int64(0))
40
41 return &ProtocolFeeRewardManager{
42 rewardStates: bptree.NewBPTreeN(16),
43 tokenAccumulators: bptree.NewBPTreeN(16),
44 currentEpoch: 0,
45 totalStakedAmount: 0,
46 totalStakedHistory: totalStakedHistory,
47 accumulatedTimestamp: 0,
48 }
49}
50
51/* Getters */
52
53// GetRewardState retrieves the reward state identified by rewardID.
54// Missing IDs return nil and false; a stored value of the wrong type returns
55// an error.
56//
57// Parameters:
58// - rewardID: identifier of the staker reward state to retrieve
59//
60// Returns:
61// - *ProtocolFeeRewardState: stored reward state, or nil when rewardID is absent or invalid
62// - bool: true when a correctly typed reward state was found
63// - error: nil when absent or found; a cast error when the stored value has the wrong type
64func (p *ProtocolFeeRewardManager) GetRewardState(rewardID string) (*ProtocolFeeRewardState, bool, error) {
65 ri := p.rewardStates.Get(rewardID)
66 if ri == nil {
67 return nil, false, nil
68 }
69 rs, castOk := ri.(*ProtocolFeeRewardState)
70 if !castOk {
71 return nil, false, ufmt.Errorf(errFailedToCastRewardState, ri)
72 }
73 return rs, true, nil
74}
75
76// GetTokenAccumulator returns the accumulator of tokenPath, or false when the token
77// never had a fee folded in.
78//
79// Parameters:
80// - tokenPath: token package path whose protocol-fee accumulator is requested
81//
82// Returns:
83// - *ProtocolFeeTokenAccumulator: accumulator for tokenPath, or nil when none exists
84// - bool: true when tokenPath has a folded-fee accumulator
85func (p *ProtocolFeeRewardManager) GetTokenAccumulator(tokenPath string) (*ProtocolFeeTokenAccumulator, bool) {
86 ai := p.tokenAccumulators.Get(tokenPath)
87 if ai == nil {
88 return nil, false
89 }
90 accumulator, castOk := ai.(*ProtocolFeeTokenAccumulator)
91 if !castOk {
92 panic(ufmt.Sprintf("failed to cast token accumulator: %T", ai))
93 }
94 return accumulator, true
95}
96
97// GetTokenPaths returns every token path that had a fee folded in, in key order.
98//
99// Returns:
100// - []string: token paths present in the accumulator tree, ordered by tree key
101func (p *ProtocolFeeRewardManager) GetTokenPaths() []string {
102 tokenPaths := make([]string, 0, p.tokenAccumulators.Size())
103 p.tokenAccumulators.Iterate("", "", func(key string, _ any) bool {
104 tokenPaths = append(tokenPaths, key)
105 return false
106 })
107 return tokenPaths
108}
109
110// GetAccumulatedProtocolFeeX128PerStake returns the accumulated fee per stake of tokenPath
111// (scaled by 2^128), or nil when the token never had a fee folded in.
112//
113// Parameters:
114// - tokenPath: token package path whose accumulated per-stake fee is requested
115//
116// Returns:
117// - *u256.Uint: accumulated fee per stake scaled by 2^128, or nil when tokenPath has no accumulator
118func (p *ProtocolFeeRewardManager) GetAccumulatedProtocolFeeX128PerStake(tokenPath string) *u256.Uint {
119 accumulator, ok := p.GetTokenAccumulator(tokenPath)
120 if !ok {
121 return nil
122 }
123 return accumulator.GetAccumulatedX128PerStake()
124}
125
126// GetProtocolFeeAmount returns the total fee amount folded in for tokenPath.
127//
128// Parameters:
129// - tokenPath: token package path whose folded protocol-fee amount is requested
130//
131// Returns:
132// - int64: total fee amount folded for tokenPath, or zero when no accumulator exists
133func (p *ProtocolFeeRewardManager) GetProtocolFeeAmount(tokenPath string) int64 {
134 accumulator, ok := p.GetTokenAccumulator(tokenPath)
135 if !ok {
136 return 0
137 }
138 return accumulator.GetProtocolFeeAmount()
139}
140
141// GetCurrentEpoch returns the accrual epoch currently in force.
142//
143// Returns:
144// - int64: current protocol-fee accrual epoch
145func (p *ProtocolFeeRewardManager) GetCurrentEpoch() int64 {
146 return p.currentEpoch
147}
148
149// GetTotalStakedAmount returns the total amount currently staked.
150//
151// Returns:
152// - int64: current total staked amount
153func (p *ProtocolFeeRewardManager) GetTotalStakedAmount() int64 {
154 return p.totalStakedAmount
155}
156
157// GetTotalStakedAmountAt returns the total staked amount in force during epoch,
158// using the latest recorded stake amount at or before that epoch.
159//
160// Parameters:
161// - epoch: accrual epoch whose effective total stake is requested
162//
163// Returns:
164// - int64: total stake recorded at or before epoch, or zero for a negative epoch
165func (p *ProtocolFeeRewardManager) GetTotalStakedAmountAt(epoch int64) int64 {
166 if epoch < 0 {
167 return 0
168 }
169
170 totalStakedAmount := int64(0)
171 p.totalStakedHistory.ReverseIterate(0, epoch, func(_ int64, value any) bool {
172 amount, castOk := value.(int64)
173 if !castOk {
174 panic(ufmt.Sprintf("failed to cast total staked amount: %T", value))
175 }
176 totalStakedAmount = amount
177 return true
178 })
179
180 return totalStakedAmount
181}
182
183// GetAccumulatedTimestamp returns the last timestamp at which fee accrual
184// state moved through a fold or stake change.
185//
186// Returns:
187// - int64: last accumulated-state update timestamp
188func (p *ProtocolFeeRewardManager) GetAccumulatedTimestamp() int64 {
189 return p.accumulatedTimestamp
190}
191
192/* Setters */
193
194// SetRewardState stores rewardState under rewardID.
195//
196// Parameters:
197// - rewardID: identifier under which the staker reward state is stored
198// - rewardState: reward state to associate with rewardID
199func (p *ProtocolFeeRewardManager) SetRewardState(rewardID string, rewardState *ProtocolFeeRewardState) {
200 p.rewardStates.Set(rewardID, rewardState)
201}
202
203// SetTokenAccumulator stores accumulator under tokenPath.
204//
205// Parameters:
206// - tokenPath: token package path used as the accumulator key
207// - accumulator: protocol-fee accumulator to store for tokenPath
208func (p *ProtocolFeeRewardManager) SetTokenAccumulator(tokenPath string, accumulator *ProtocolFeeTokenAccumulator) {
209 p.tokenAccumulators.Set(tokenPath, accumulator)
210}
211
212// SetCurrentEpoch records the accrual epoch currently in force.
213//
214// Parameters:
215// - epoch: new current protocol-fee accrual epoch
216func (p *ProtocolFeeRewardManager) SetCurrentEpoch(epoch int64) {
217 p.currentEpoch = epoch
218}
219
220// SetTotalStakedAmount records the total amount currently staked.
221//
222// Parameters:
223// - totalStakedAmount: new total staked amount
224func (p *ProtocolFeeRewardManager) SetTotalStakedAmount(totalStakedAmount int64) {
225 p.totalStakedAmount = totalStakedAmount
226}
227
228// SetTotalStakedAmountAt records the total staked amount in force from epoch on.
229//
230// Parameters:
231// - epoch: first accrual epoch at which totalStakedAmount is in force
232// - totalStakedAmount: total stake amount to record from epoch onward
233func (p *ProtocolFeeRewardManager) SetTotalStakedAmountAt(epoch int64, totalStakedAmount int64) {
234 p.totalStakedHistory.Set(epoch, totalStakedAmount)
235}
236
237// SetAccumulatedTimestamp records the timestamp at which accrual state last moved.
238//
239// Parameters:
240// - accumulatedTimestamp: timestamp to record for the latest fold or stake change
241func (p *ProtocolFeeRewardManager) SetAccumulatedTimestamp(accumulatedTimestamp int64) {
242 p.accumulatedTimestamp = accumulatedTimestamp
243}