Kind Realm (renderable)
Name v1
Namespace gnoswap / gov / staker
Exported functions 12
Module gno.land/r/gnoswap/gov/staker/v1
gno 0.9 protocol_fee_reward_manager.gno gno
⧉
1 package staker 2 3 import ( 4 "errors" 5 "math" 6 7 "gno.land/p/gnoswap/gnsmath/v1" 8 NewDelegation (id int64, delegateFrom string, delegateTo string, delegateAmount int64, createdHeight int64, createdAt int64) *gno.land/r/gnoswap/gov/staker.Delegation
NewDelegationManagerResolver (delegationManager *gno.land/r/gnoswap/gov/staker.DelegationManager) *gno.land/r/gnoswap/gov/staker/v1.DelegationManagerResolver
NewDelegationResolver (delegation *gno.land/r/gnoswap/gov/staker.Delegation) *gno.land/r/gnoswap/gov/staker/v1.DelegationResolver
NewDelegationWithdraw (delegationID int64, unDelegateAmount int64, createdHeight int64, createdAt int64, unDelegationLockupPeriod int64) struct{delegationID int64; unDelegateAmount int64; unDelegatedHeight int64; unDelegatedAt int64; collectedAmount int64; collectableTime int64; collectedAt int64; collected bool}
NewDelegationWithdrawResolver (withdraw *gno.land/r/gnoswap/gov/staker.DelegationWithdraw) *gno.land/r/gnoswap/gov/staker/v1.DelegationWithdrawResolver
NewDelegationWithdrawWithoutLockup (delegationID int64, unDelegateAmount int64, createdHeight int64, createdAt int64) struct{delegationID int64; unDelegateAmount int64; unDelegatedHeight int64; unDelegatedAt int64; collectedAmount int64; collectableTime int64; collectedAt int64; collected bool}
NewEmissionRewardManagerResolver (emissionRewardManager *gno.land/r/gnoswap/gov/staker.EmissionRewardManager) *gno.land/r/gnoswap/gov/staker/v1.EmissionRewardManagerResolver
NewEmissionRewardStateResolver (emissionRewardState *gno.land/r/gnoswap/gov/staker.EmissionRewardState) *gno.land/r/gnoswap/gov/staker/v1.EmissionRewardStateResolver
NewGovStakerV1 (store interface {GetAllDelegations func() *gno.land/p/nt/bptree/v0.BPTree; GetDelegation func(int64) (*gno.land/r/gnoswap/gov/staker.Delegation, bool); GetDelegationCounter func() *gno.land/r/gnoswap/gov/staker.Counter; GetDelegationManager func() *gno.land/r/gnoswap/gov/staker.DelegationManager; GetEmissionRewardManager func() *gno.land/r/gnoswap/gov/staker.EmissionRewardManager; GetLaunchpadProjectDeposits func() *gno.land/r/gnoswap/gov/staker.LaunchpadProjectDeposits; GetProtocolFeeRewardManager func() *gno.land/r/gnoswap/gov/staker.ProtocolFeeRewardManager; GetTotalDelegatedAmount func() int64; GetTotalDelegationHistory func() *gno.land/r/gnoswap/gov/staker.UintTree; GetTotalLockedAmount func() int64; GetUnDelegationLockupPeriod func() int64; GetUserDelegationHistory func() *gno.land/p/nt/bptree/v0.BPTree; HasDelegation func(int64) bool; HasDelegationCounterStoreKey func() bool; HasDelegationManagerStoreKey func() bool; HasDelegationsStoreKey func() bool; HasEmissionRewardManagerStoreKey func() bool; HasLaunchpadProjectDepositsStoreKey func() bool; HasProtocolFeeRewardManagerStoreKey func() bool; HasTotalDelegatedAmountStoreKey func() bool; HasTotalDelegationHistoryStoreKey func() bool; HasTotalLockedAmountStoreKey func() bool; HasUnDelegationLockupPeriodStoreKey func() bool; HasUserDelegationHistoryStoreKey func() bool; RemoveDelegation func(int, .uverse.realm, int64) .uverse.error; SetDelegation func(int, .uverse.realm, int64, *gno.land/r/gnoswap/gov/staker.Delegation) .uverse.error; SetDelegationCounter func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.Counter) .uverse.error; SetDelegationManager func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.DelegationManager) .uverse.error; SetDelegations func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetEmissionRewardManager func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.EmissionRewardManager) .uverse.error; SetLaunchpadProjectDeposits func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.LaunchpadProjectDeposits) .uverse.error; SetProtocolFeeRewardManager func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.ProtocolFeeRewardManager) .uverse.error; SetTotalDelegatedAmount func(int, .uverse.realm, int64) .uverse.error; SetTotalDelegationHistory func(int, .uverse.realm, *gno.land/r/gnoswap/gov/staker.UintTree) .uverse.error; SetTotalLockedAmount func(int, .uverse.realm, int64) .uverse.error; SetUnDelegationLockupPeriod func(int, .uverse.realm, int64) .uverse.error; SetUserDelegationHistory func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error}) interface {CleanStakerDelegationSnapshotByAdmin func(int, .uverse.realm, int64, .uverse.address); CollectEmissionReward func(int, .uverse.realm); CollectEmissionRewardFromLaunchPad func(int, .uverse.realm, .uverse.address); CollectProtocolFeeReward func(int, .uverse.realm, string); CollectProtocolFeeRewardFromLaunchPad func(int, .uverse.realm, .uverse.address, string); CollectReward func(int, .uverse.realm); CollectRewardFromLaunchPad func(int, .uverse.realm, .uverse.address); CollectUndelegatedGns func(int, .uverse.realm) int64; Delegate func(int, .uverse.realm, .uverse.address, int64, string) int64; ExistsDelegation func(int64) bool; GetClaimableRewardByAddress func(.uverse.address) (int64, map[string]int64, .uverse.error); GetClaimableRewardByLaunchpad func(.uverse.address) (int64, map[string]int64, .uverse.error); GetClaimableRewardByRewardID func(string) (int64, map[string]int64, .uverse.error); GetCollectableWithdrawAmount func(int64) int64; GetDelegationWithdrawCount func(int64) int; GetDelegationWithdraws func(int64, int, int) ([]gno.land/r/gnoswap/gov/staker.DelegationWithdraw, .uverse.error); GetDelegations func() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetDelegatorDelegations func(.uverse.address) *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetEmissionAccumulatedTimestamp func() int64; GetEmissionAccumulatedX128PerStake func() *gno.land/p/gnoswap/uint256/v1.Uint; GetEmissionDistributedAmount func() int64; GetLaunchpadProjectDeposit func(string) (int64, bool); GetProtocolFeeAccumulatedTimestamp func() int64; GetProtocolFeeAccumulatedX128PerStake func(string) *gno.land/p/gnoswap/uint256/v1.Uint; GetProtocolFeeAmount func(string) int64; GetTotalDelegated func() int64; GetTotalDelegationAmountAtSnapshot func(int64) (int64, bool); GetTotalLockedAmount func() int64; GetTotalxGnsSupply func() int64; GetUnDelegationLockupPeriod func() int64; GetUserDelegationAmountAtSnapshot func(.uverse.address, int64) (int64, bool); GetUserDelegationIDs func(.uverse.address, .uverse.address) []int64; HasDelegationSnapshotsKey func() bool; Redelegate func(int, .uverse.realm, .uverse.address, .uverse.address, int64) int64; Render func(string) string; SetAmountByProjectWallet func(int, .uverse.realm, .uverse.address, int64, bool); SetUnDelegationLockupPeriodByAdmin func(int, .uverse.realm, int64); Undelegate func(int, .uverse.realm, .uverse.address, int64) int64}
NewLaunchpadProjectDepositsResolver (launchpadProjectDeposits *gno.land/r/gnoswap/gov/staker.LaunchpadProjectDeposits) *gno.land/r/gnoswap/gov/staker/v1.LaunchpadProjectDepositsResolver
NewProtocolFeeRewardManagerResolver (manager *gno.land/r/gnoswap/gov/staker.ProtocolFeeRewardManager) *gno.land/r/gnoswap/gov/staker/v1.ProtocolFeeRewardManagerResolver
NewProtocolFeeRewardStateResolver (protocolFeeRewardState *gno.land/r/gnoswap/gov/staker.ProtocolFeeRewardState) *gno.land/r/gnoswap/gov/staker/v1.ProtocolFeeRewardStateResolver
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
Render unavailable
rate limit exceeded — the render query is served by the Gnoland archive RPC.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
u256 "gno.land/p/gnoswap/uint256/v1"
9
10 "gno.land/r/gnoswap/gov/staker"
11 )
12
13 // ProtocolFeeRewardManagerResolver drives the ProtocolFeeRewardManager.
14 //
15 // Stake changes only move the epoch, the total stake history and the staker's own
16 // event list. Fees are folded into a token's accumulator when that token is collected,
17 // each accrual bucket against the total stake in force during its epoch.
18 type ProtocolFeeRewardManagerResolver struct {
19 *staker.ProtocolFeeRewardManager
20 }
21
22 // NewProtocolFeeRewardManagerResolver wraps the shared protocol-fee reward manager.
23 //
24 // Parameters:
25 // - manager: Protocol-fee reward state to resolve and mutate.
26 //
27 // Returns:
28 // - *ProtocolFeeRewardManagerResolver: resolver operating on manager's accounting state.
29 func NewProtocolFeeRewardManagerResolver(manager *staker.ProtocolFeeRewardManager) *ProtocolFeeRewardManagerResolver {
30 return &ProtocolFeeRewardManagerResolver{
31 ProtocolFeeRewardManager: manager,
32 }
33 }
34
35 // addStake records that rewardID staked amount more from epoch on.
36 //
37 // epoch is the accrual epoch protocol_fee just opened for this change, so it must not
38 // be older than the epoch in force.
39 func (self *ProtocolFeeRewardManagerResolver) addStake(rewardID string , amount int64 , epoch int64 ) error {
40 if amount <= 0 {
41 return errors.New(errAmountMustBePositive)
42 }
43 if epoch < self.GetCurrentEpoch() {
44 return errors.New(errInvalidAccrualEpoch)
45 }
46
47 currentTotal := self.GetTotalStakedAmount()
48 if currentTotal > math.MaxInt64-amount {
49 return errors.New(errTotalStakedAmountOverflow)
50 }
51
52 rewardState, ok, err := self.GetRewardState(rewardID)
53 if err != nil {
54 return err
55 }
56 if !ok {
57 rewardState = staker.NewProtocolFeeRewardState()
58 }
59
60 resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
61 if err := resolvedState.addStake(amount, epoch); err != nil {
62 return err
63 }
64
65 self.recordTotalStakedAmount(epoch, gnsmath.SafeAddInt64(currentTotal, amount))
66 self.SetRewardState(rewardID, rewardState)
67
68 return nil
69 }
70
71 // removeStake records that rewardID staked amount less from epoch on.
72 func (self *ProtocolFeeRewardManagerResolver) removeStake(rewardID string , amount int64 , epoch int64 ) error {
73 if amount < 0 {
74 return errors.New(errAmountMustBeNonNegative)
75 }
76 if epoch < self.GetCurrentEpoch() {
77 return errors.New(errInvalidAccrualEpoch)
78 }
79
80 updatedTotal := gnsmath.SafeSubInt64(self.GetTotalStakedAmount(), amount)
81 if updatedTotal < 0 {
82 return errors.New(errRemoveAmountExceedsTotalStaked)
83 }
84
85 rewardState, ok, err := self.GetRewardState(rewardID)
86 if err != nil {
87 return err
88 }
89 if !ok {
90 rewardState = staker.NewProtocolFeeRewardState()
91 }
92
93 resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
94 if err := resolvedState.removeStake(amount, epoch); err != nil {
95 return err
96 }
97
98 self.recordTotalStakedAmount(epoch, updatedTotal)
99 self.SetRewardState(rewardID, rewardState)
100
101 return nil
102 }
103
104 func (self *ProtocolFeeRewardManagerResolver) recordTotalStakedAmount(epoch int64 , totalStakedAmount int64 ) {
105 self.SetCurrentEpoch(epoch)
106 self.SetTotalStakedAmount(totalStakedAmount)
107 self.SetTotalStakedAmountAt(epoch, totalStakedAmount)
108 }
109
110 // applyAccrualBuckets folds consumed accrual buckets of tokenPath into its accumulator.
111 //
112 // epochs and amounts are parallel and in ascending epoch order. Each amount is divided
113 // by the total stake in force during its epoch; with nothing staked the amount is
114 // dropped, the same policy the emission accumulator applies. exhausted reports that
115 // no bucket is left pending, which lets the accumulator mark every closed epoch as
116 // fully folded.
117 func (self *ProtocolFeeRewardManagerResolver) applyAccrualBuckets(
118 tokenPath string ,
119 epochs [] int64 ,
120 amounts [] int64 ,
121 exhausted bool ,
122 currentTimestamp int64 ,
123 ) error {
124 if len(epochs) != len(amounts) {
125 return errors.New(errInvalidAccrualBuckets)
126 }
127
128 accumulator, ok := self.GetTokenAccumulator(tokenPath)
129 if !ok {
130 if len(epochs) == 0 {
131 return nil
132 }
133 accumulator = staker.NewProtocolFeeTokenAccumulator()
134 self.SetTokenAccumulator(tokenPath, accumulator)
135 }
136
137 currentX128 := accumulator.GetAccumulatedX128PerStake().Clone()
138 feeAmount := accumulator.GetProtocolFeeAmount()
139 lastEpoch := int64 (- 1 )
140
141 for i, epoch := range epochs {
142 if epoch < lastEpoch || epoch > self.GetCurrentEpoch() {
143 return errors.New(errInvalidAccrualBuckets)
144 }
145 lastEpoch = epoch
146
147 amount := amounts[i]
148 if amount < 0 {
149 return errors.New(errInvalidAccrualBuckets)
150 }
151 feeAmount = gnsmath.SafeAddInt64(feeAmount, amount)
152
153 totalStakedAmount := self.GetTotalStakedAmountAt(epoch)
154 if totalStakedAmount <= 0 || amount == 0 {
155 continue
156 }
157
158 // Scale the amount by 2^128 before dividing by the total stake. The amount
159 // fits in 63 bits, so the product fits in 191 bits and cannot overflow.
160 amountX128 := u256.Zero().Lsh(u256.NewUintFromInt64(amount), 128 )
161 perStakeX128 := u256.Zero().Div(amountX128, u256.NewUintFromInt64(totalStakedAmount))
162 currentX128 = u256.Zero().Add(currentX128, perStakeX128)
163
164 accumulator.SetAccumulatedX128PerStakeAt(epoch, currentX128)
165 }
166
167 accumulator.SetProtocolFeeAmount(feeAmount)
168
169 // Every epoch before the one in force is closed, so it is fully folded once no bucket
170 // is left. Otherwise only the epochs up to the last consumed bucket are complete.
171 foldedEpoch := accumulator.GetFoldedEpoch()
172 if exhausted {
173 foldedEpoch = maxInt64(foldedEpoch, self.GetCurrentEpoch()- 1 )
174 } else if lastEpoch >= 0 {
175 foldedEpoch = maxInt64(foldedEpoch, minInt64(lastEpoch, self.GetCurrentEpoch()- 1 ))
176 }
177 accumulator.SetFoldedEpoch(foldedEpoch)
178
179 self.SetAccumulatedTimestamp(currentTimestamp)
180
181 return nil
182 }
183
184 // accumulatorView returns a read view of tokenPath's accumulator.
185 //
186 // With pendingComplete false the view reflects the accumulator exactly as folded so
187 // far, which is what a collect must settle against. With pendingComplete true the
188 // given buckets are the whole pending set of the token: they are projected on top, as
189 // applyAccrualBuckets would fold them, and every closed epoch counts as folded, which
190 // is what an exhaustive fold would leave behind. A query uses the latter so that it
191 // reports what a collect would pay even when a previous bounded fold consumed exactly
192 // its limit and left foldedEpoch behind the epoch in force.
193 func (self *ProtocolFeeRewardManagerResolver) accumulatorView(tokenPath string , pendingEpochs [] int64 , pendingAmounts [] int64 , pendingComplete bool ) *protocolFeeAccumulatorView {
194 view := &protocolFeeAccumulatorView{
195 extraEpochs: [] int64 {},
196 extraValues: []*u256.Uint{},
197 current: u256.Zero(),
198 firstEpoch: - 1 ,
199 foldedEpoch: - 1 ,
200 currentEpoch: self.GetCurrentEpoch(),
201 }
202
203 if accumulator, ok := self.GetTokenAccumulator(tokenPath); ok {
204 view.accumulator = accumulator
205 view.current = accumulator.GetAccumulatedX128PerStake().Clone()
206 view.firstEpoch = accumulator.GetFirstEpoch()
207 view.foldedEpoch = accumulator.GetFoldedEpoch()
208 }
209
210 if pendingComplete {
211 // Projecting the whole pending set folds every closed epoch.
212 view.foldedEpoch = maxInt64(view.foldedEpoch, view.currentEpoch- 1 )
213 }
214
215 for i, epoch := range pendingEpochs {
216 totalStakedAmount := self.GetTotalStakedAmountAt(epoch)
217 if totalStakedAmount <= 0 || pendingAmounts[i] <= 0 {
218 continue
219 }
220
221 amountX128 := u256.Zero().Lsh(u256.NewUintFromInt64(pendingAmounts[i]), 128 )
222 perStakeX128 := u256.Zero().Div(amountX128, u256.NewUintFromInt64(totalStakedAmount))
223 view.current = u256.Zero().Add(view.current, perStakeX128)
224 view.extraEpochs = append(view.extraEpochs, epoch)
225 view.extraValues = append(view.extraValues, view.current.Clone())
226 if view.firstEpoch < 0 {
227 view.firstEpoch = epoch
228 }
229 }
230
231 return view
232 }
233
234 // claimTokenReward settles tokenPath for rewardID and returns the reward collected.
235 // See ProtocolFeeRewardStateResolver.claimTokenReward for the maxEvents bound.
236 func (self *ProtocolFeeRewardManagerResolver) claimTokenReward(rewardID string , tokenPath string , maxEvents int64 ) ( int64 , error ) {
237 rewardState, ok, err := self.GetRewardState(rewardID)
238 if err != nil {
239 return 0 , err
240 }
241 if !ok {
242 return 0 , nil
243 }
244
245 resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
246 reward, err := resolvedState.claimTokenReward(tokenPath, self.accumulatorView(tokenPath, nil , nil , false ), maxEvents)
247 if err != nil {
248 return 0 , err
249 }
250
251 self.SetRewardState(rewardID, rewardState)
252
253 return reward, nil
254 }
255
256 // claimRewards settles every folded token for rewardID and returns the reward
257 // collected per token. Its cost grows with the number of tokens that ever collected a
258 // fee; claimTokenReward is the bounded alternative.
259 func (self *ProtocolFeeRewardManagerResolver) claimRewards(rewardID string , currentTimestamp int64 ) ( map [ string ] int64 , error ) {
260 rewards := make( map [ string ] int64 )
261
262 rewardState, ok, err := self.GetRewardState(rewardID)
263 if err != nil {
264 return nil , err
265 }
266 if !ok {
267 return rewards, nil
268 }
269
270 resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
271 if !resolvedState.IsClaimable(currentTimestamp) {
272 return rewards, nil
273 }
274
275 for _, tokenPath := range self.GetTokenPaths() {
276 reward, err := resolvedState.claimTokenReward(tokenPath, self.accumulatorView(tokenPath, nil , nil , false ), 0 )
277 if err != nil {
278 return nil , err
279 }
280 rewards[tokenPath] = reward
281 }
282
283 resolvedState.SetClaimedTimestamp(currentTimestamp)
284 self.SetRewardState(rewardID, rewardState)
285
286 return rewards, nil
287 }
288
289 // GetClaimableRewardAmount returns what collecting tokenPath for rewardID would pay,
290 // with the given pending buckets projected on top of the accumulator.
291 // Parameters:
292 // - rewardID: Identifier of the staker whose claimable reward is quoted.
293 // - tokenPath: Token path whose claimable protocol-fee reward is quoted.
294 // - pendingEpochs: Pending bucket epochs projected on the accumulator.
295 // - pendingAmounts: Pending fee amounts parallel to pendingEpochs.
296 //
297 // Returns:
298 // - int64: protocol-fee amount that collecting tokenPath would pay in token base units.
299 // - error: nil on success; non-nil when reward-state lookup or projection fails.
300 func (self *ProtocolFeeRewardManagerResolver) GetClaimableRewardAmount(rewardID string , tokenPath string , pendingEpochs [] int64 , pendingAmounts [] int64 ) ( int64 , error ) {
301 rewardState, ok, err := self.GetRewardState(rewardID)
302 if err != nil {
303 return 0 , err
304 }
305 if !ok {
306 return 0 , nil
307 }
308
309 resolvedState := NewProtocolFeeRewardStateResolver(rewardState)
310
311 return resolvedState.getClaimableReward(tokenPath, self.accumulatorView(tokenPath, pendingEpochs, pendingAmounts, true )), nil
312 }
313
314 func maxInt64(a, b int64 ) int64 {
315 if a > b {
316 return a
317 }
318 return b
319 }
320
321 func minInt64(a, b int64 ) int64 {
322 if a < b {
323 return a
324 }
325 return b
326 }
327