Kind Realm (renderable)
Name v1
Namespace gnoswap / launchpad
Exported functions 1
Module gno.land/r/gnoswap/launchpad/v1
gno 0.9 reward_manager.gno gno
⧉
1 package launchpad 2 3 import ( 4 "errors" 5 6 ufmt "gno.land/p/nt/ufmt/v0" 7 "gno.land/r/gnoswap/launchpad" 8 NewLaunchpadV1 (launchpadStore interface {GetDepositCounter func() *gno.land/r/gnoswap/launchpad.Counter; GetDeposits func() *gno.land/p/nt/bptree/v0.BPTree; GetProjectRecipients func() *gno.land/p/nt/bptree/v0.BPTree; GetProjectTierRewardManagers func() *gno.land/p/nt/bptree/v0.BPTree; GetProjects func() *gno.land/p/nt/bptree/v0.BPTree; GetTotalGNSStakedAmount func() int64; HasDepositCounterStoreKey func() bool; HasDepositsKey func() bool; HasProjectRecipientsKey func() bool; HasProjectTierRewardManagersKey func() bool; HasProjectsKey func() bool; HasTotalGNSStakedAmountKey func() bool; NextDepositID func() string; SetDepositCounter func(int, .uverse.realm, *gno.land/r/gnoswap/launchpad.Counter) .uverse.error; SetDeposits func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetProjectRecipients func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetProjectTierRewardManagers func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetProjects func(int, .uverse.realm, *gno.land/p/nt/bptree/v0.BPTree) .uverse.error; SetTotalGNSStakedAmount func(int, .uverse.realm, int64) .uverse.error}) interface {CollectDepositGns func(int, .uverse.realm, string) (int64, .uverse.error); CollectEmissionReward func(int, .uverse.realm); CollectProtocolFee func(int, .uverse.realm); CollectProtocolFeeReward func(int, .uverse.realm, string); CollectRewardByDepositId func(int, .uverse.realm, string) int64; CreateProject func(int, .uverse.realm, string, string, .uverse.address, int64, string, string, int64, int64, int64, int64) string; DepositGns func(int, .uverse.realm, string, int64, string) string; GetCurrentDepositId func() int64; GetDeposit func(string) (gno.land/r/gnoswap/launchpad.Deposit, .uverse.error); GetDepositAmount func(string) (int64, .uverse.error); GetDepositCount func() int; GetDepositCreatedAt func(string) (int64, .uverse.error); GetDepositCreatedHeight func(string) (int64, .uverse.error); GetDepositEndTime func(string) (int64, .uverse.error); GetDepositProjectID func(string) (string, .uverse.error); GetDepositProjectTierID func(string) (string, .uverse.error); GetDepositTier func(string) (int64, .uverse.error); GetDepositWithdrawnHeight func(string) (int64, .uverse.error); GetDepositWithdrawnTime func(string) (int64, .uverse.error); GetProjectActiveStatus func(string) (bool, .uverse.error); GetProjectCondition func(string, string) (*gno.land/r/gnoswap/launchpad.ProjectCondition, .uverse.error); GetProjectCreatedAt func(string) (int64, .uverse.error); GetProjectCreatedHeight func(string) (int64, .uverse.error); GetProjectDepositAmount func(string) (int64, .uverse.error); GetProjectName func(string) (string, .uverse.error); GetProjectRecipient func(string) (.uverse.address, .uverse.error); GetProjectTier func(string, int64) (*gno.land/r/gnoswap/launchpad.ProjectTier, .uverse.error); GetProjectTierDistributeAmountPerSecondX128 func(string, int64) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetProjectTierEndTime func(string, int64) (int64, .uverse.error); GetProjectTierRewardAccumulatedDistributeAmount func(string) (int64, .uverse.error); GetProjectTierRewardAccumulatedRewardPerDepositX128 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetProjectTierRewardAccumulatedTime func(string) (int64, .uverse.error); GetProjectTierRewardClaimableDuration func(string) (int64, .uverse.error); GetProjectTierRewardDistributeAmountPerSecondX128 func(string) (*gno.land/p/gnoswap/uint256/v1.Uint, .uverse.error); GetProjectTierRewardDistributeEndTime func(string) (int64, .uverse.error); GetProjectTierRewardDistributeStartTime func(string) (int64, .uverse.error); GetProjectTierRewardManager func(string) (*gno.land/r/gnoswap/launchpad.RewardManager, .uverse.error); GetProjectTierRewardManagerCount func() int; GetProjectTierRewardTotalClaimedAmount func(string) (int64, .uverse.error); GetProjectTierRewardTotalDistributeAmount func(string) (int64, .uverse.error); GetProjectTierRewards func(string, int64) *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetProjectTierStartTime func(string, int64) (int64, .uverse.error); GetProjectTierTotalCollectedAmount func(string, int64) (int64, .uverse.error); GetProjectTierTotalDepositAmount func(string, int64) (int64, .uverse.error); GetProjectTierTotalDepositCount func(string, int64) (int64, .uverse.error); GetProjectTierTotalDistributeAmount func(string, int64) (int64, .uverse.error); GetProjectTierTotalWithdrawAmount func(string, int64) (int64, .uverse.error); GetProjectTierTotalWithdrawCount func(string, int64) (int64, .uverse.error); GetProjectTiersRatios func(string) (map[int64]int64, .uverse.error); GetProjectTokenPath func(string) (string, .uverse.error); GetProjects func() *gno.land/p/nt/bptree/rotree/v0.ReadOnlyTree; GetRewardState func(string, string) (*gno.land/r/gnoswap/launchpad.RewardState, .uverse.error); GetTotalGNSStakedAmount func() int64; Render func(string) string; TransferLeftFromProjectByAdmin func(int, .uverse.realm, string, .uverse.address) int64}
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
This realm does not provide renderable output (vm/qrender → NoRenderDeclError).
View on gnoweb ↗ 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.
9 gnsmath "gno.land/p/gnoswap/gnsmath/v1"
10 u256 "gno.land/p/gnoswap/uint256/v1"
11 )
12
13 // Helper functions for RewardManager
14
15 func isRewardManagerInitialized(r *launchpad.RewardManager) bool {
16 return r.Rewards().Size() > 0
17 }
18
19 func getDepositRewardState(r *launchpad.RewardManager, depositId string ) (*launchpad.RewardState, error ) {
20 rewardStateI := r.Rewards().Get(depositId)
21 if rewardStateI == nil {
22 return nil , makeErrorWithDetails(errNotExistDeposit, ufmt.Sprintf( "(%s)" , depositId))
23 }
24
25 rewardState, ok := rewardStateI.(*launchpad.RewardState)
26 if !ok {
27 return nil , ufmt.Errorf( "failed to cast rewardState to *launchpad.RewardState: %T" , rewardStateI)
28 }
29
30 return rewardState, nil
31 }
32
33 func calculateRewardPerDepositX128(r *launchpad.RewardManager, rewardPerSecondX128 *u256.Uint, totalStaked int64 , currentTime int64 ) (*u256.Uint, error ) {
34 accumulatedTime := r.AccumulatedTime()
35 if r.DistributeStartTime() > accumulatedTime {
36 accumulatedTime = r.DistributeStartTime()
37 }
38
39 // not started yet
40 if currentTime < accumulatedTime {
41 return u256.Zero(), nil
42 }
43
44 // past distribute end time
45 if accumulatedTime > r.DistributeEndTime() {
46 return u256.Zero(), nil
47 }
48
49 // past distribute end time, set to distribute end time
50 if currentTime > r.DistributeEndTime() {
51 currentTime = r.DistributeEndTime()
52 }
53
54 if rewardPerSecondX128.IsZero() {
55 return nil , makeErrorWithDetails(
56 errNoLeftReward,
57 ufmt.Sprintf( "rewardPerSecond(%d)" , rewardPerSecondX128),
58 )
59 }
60
61 // no left reward
62 if totalStaked == 0 {
63 return u256.Zero(), nil
64 }
65
66 // timeDuration * rewardPerSecond / totalStaked
67 timeDuration := currentTime - accumulatedTime
68 rewardPerDepositX128 := u256.MulDiv(
69 u256.NewUintFromInt64(timeDuration),
70 rewardPerSecondX128,
71 u256.NewUintFromInt64(totalStaked),
72 )
73
74 return rewardPerDepositX128, nil
75 }
76
77 func addRewardStateByDeposit(r *launchpad.RewardManager, deposit launchpad.Deposit) *launchpad.RewardState {
78 claimableTime := deposit.CreatedAt() + r.RewardClaimableDuration()
79 if claimableTime > r.DistributeEndTime() {
80 claimableTime = r.DistributeEndTime()
81 }
82
83 rewardState := launchpad.NewRewardState(
84 r.AccumulatedRewardPerDepositX128().Clone(),
85 deposit.DepositAmount(),
86 deposit.CreatedAt(),
87 r.DistributeEndTime(),
88 claimableTime,
89 )
90
91 // if the first deposit, set the distribute start time
92 if !isRewardManagerInitialized(r) {
93 rewardState.SetDistributeStartTime(r.DistributeStartTime())
94 rewardState.SetDistributeEndTime(r.DistributeEndTime())
95 rewardState.SetAccumulatedTime(r.DistributeStartTime())
96 rewardState.SetPriceDebtX128(u256.Zero())
97 }
98
99 return addRewardState(r, deposit, rewardState)
100 }
101
102 func addRewardState(r *launchpad.RewardManager, deposit launchpad.Deposit, rewardState *launchpad.RewardState) *launchpad.RewardState {
103 addActiveRewardState(r, rewardState)
104 r.SetReward(deposit.ID(), rewardState)
105
106 return rewardState
107 }
108
109 func removeRewardState(r *launchpad.RewardManager, depositId string ) error {
110 rewardState, err := getDepositRewardState(r, depositId)
111 if err != nil {
112 return err
113 }
114
115 removeActiveRewardState(r, rewardState)
116 r.RemoveReward(depositId)
117 return nil
118 }
119
120 func addActiveRewardState(r *launchpad.RewardManager, rewardState *launchpad.RewardState) {
121 debt, overflow := u256.Zero().MulOverflow(
122 rewardState.PriceDebtX128(),
123 u256.NewUintFromInt64(rewardState.DepositAmount()),
124 )
125 if overflow {
126 panic(errors.New(errOverflow))
127 }
128
129 r.SetActiveDepositAmount(gnsmath.SafeAddInt64(r.ActiveDepositAmount(), rewardState.DepositAmount()))
130 r.SetActivePriceDebtX128(u256.Zero().Add(r.ActivePriceDebtX128(), debt))
131 }
132
133 func removeActiveRewardState(r *launchpad.RewardManager, rewardState *launchpad.RewardState) {
134 debt, overflow := u256.Zero().MulOverflow(
135 rewardState.PriceDebtX128(),
136 u256.NewUintFromInt64(rewardState.DepositAmount()),
137 )
138 if overflow {
139 panic(errors.New(errOverflow))
140 }
141
142 r.SetActiveDepositAmount(gnsmath.SafeSubInt64(r.ActiveDepositAmount(), rewardState.DepositAmount()))
143 r.SetActiveClaimedAmount(gnsmath.SafeSubInt64(r.ActiveClaimedAmount(), rewardState.ClaimedAmount()))
144 r.SetActivePriceDebtX128(u256.Zero().Sub(r.ActivePriceDebtX128(), debt))
145 }
146
147 func addRewardPerDepositX128(r *launchpad.RewardManager, rewardPerDepositX128 *u256.Uint, currentTime int64 ) error {
148 if rewardPerDepositX128.IsZero() {
149 return nil
150 }
151
152 if r.AccumulatedTime() > currentTime || r.DistributeStartTime() > currentTime {
153 return nil
154 }
155
156 if currentTime > r.DistributeEndTime() {
157 currentTime = r.DistributeEndTime()
158 }
159
160 accumulated := u256.Zero().Add(r.AccumulatedRewardPerDepositX128(), rewardPerDepositX128)
161 r.SetAccumulatedRewardPerDepositX128(accumulated)
162 r.SetAccumulatedTime(currentTime)
163
164 return nil
165 }
166
167 // updateRewardPerDepositX128 updates the reward per deposit state.
168 // This function calculates and updates the accumulated reward per deposit
169 // based on the current total deposit amount and time.
170 //
171 // Parameters:
172 // - totalDepositAmount (int64): Current total deposit amount
173 // - time (int64): Current timestamp
174 //
175 // Returns:
176 // - error: If the update fails
177 func updateRewardPerDepositX128(r *launchpad.RewardManager, totalDepositAmount int64 , currentTime int64 ) error {
178 if currentTime <= 0 {
179 return makeErrorWithDetails(errInvalidTime, "time must be positive" )
180 }
181
182 // Calculate and update rewards
183 rewardPerDepositX128, err := calculateRewardPerDepositX128(
184 r,
185 r.DistributeAmountPerSecondX128(),
186 totalDepositAmount,
187 currentTime,
188 )
189 if err != nil {
190 return err
191 }
192
193 err = addRewardPerDepositX128(r, rewardPerDepositX128, currentTime)
194 if err != nil {
195 return err
196 }
197
198 return nil
199 }
200
201 func updateDistributeAmountPerSecondX128(r *launchpad.RewardManager, totalDistributeAmount int64 , distributeStartTime int64 , distributeEndTime int64 ) {
202 // Use time duration for per-second calculation
203 timeDuration := distributeEndTime - distributeStartTime
204 if timeDuration <= 0 {
205 return
206 }
207
208 totalDistributeAmountX128 := u256.Zero().Lsh(
209 u256.NewUintFromInt64(totalDistributeAmount),
210 128 ,
211 )
212
213 // Divide by time duration in seconds
214 amountPerSecondX128 := u256.Zero().Div(
215 totalDistributeAmountX128,
216 u256.NewUintFromInt64(timeDuration),
217 )
218
219 r.SetDistributeAmountPerSecondX128(amountPerSecondX128)
220 r.SetDistributeStartTime(distributeStartTime)
221 r.SetDistributeEndTime(distributeEndTime)
222 }
223
224 // collectReward processes the reward collection for a specific deposit.
225 // This function ensures that the reward collection is valid and updates
226 // the claimed amount accordingly.
227 //
228 // Parameters:
229 // - depositId (string): The ID of the deposit
230 // - currentTime (int64): Current timestamp
231 //
232 // Returns:
233 // - int64: The amount of reward collected
234 // - error: If the collection fails
235 func collectReward(r *launchpad.RewardManager, depositId string , currentTime int64 ) ( int64 , error ) {
236 if currentTime < r.AccumulatedTime() {
237 return 0 , makeErrorWithDetails(
238 errInvalidRewardState,
239 ufmt.Sprintf( "currentTime %d is less than AccumulatedTime %d" , currentTime, r.AccumulatedTime()),
240 )
241 }
242
243 rewardState, err := getDepositRewardState(r, depositId)
244 if err != nil {
245 return 0 , err
246 }
247
248 if !isRewardStateClaimable(rewardState, currentTime) {
249 return 0 , makeErrorWithDetails(
250 errInvalidRewardState,
251 ufmt.Sprintf( "currentTime %d is less than claimableTime %d" , currentTime, rewardState.ClaimableTime()),
252 )
253 }
254
255 if currentTime < rewardState.DistributeStartTime() {
256 return 0 , makeErrorWithDetails(
257 errInvalidRewardState,
258 ufmt.Sprintf( "currentTime %d is less than DistributeStartTime %d" , currentTime, rewardState.DistributeStartTime()),
259 )
260 }
261
262 claimableReward := calculateClaimableReward(rewardState, r.AccumulatedRewardPerDepositX128())
263 if claimableReward == 0 {
264 return 0 , nil
265 }
266
267 rewardState.SetClaimedAmount(rewardState.ClaimedAmount() + claimableReward)
268 rewards := r.Rewards()
269 rewards.Set(depositId, rewardState)
270 r.SetRewards(rewards)
271 r.SetTotalClaimedAmount(r.TotalClaimedAmount() + claimableReward)
272 r.SetActiveClaimedAmount(gnsmath.SafeAddInt64(r.ActiveClaimedAmount(), claimableReward))
273
274 return claimableReward, nil
275 }
276
277 // newRewardManager returns a pointer to a new RewardManager with the given values.
278 func newRewardManager(
279 totalDistributeAmount int64 ,
280 distributeStartTime int64 ,
281 distributeEndTime int64 ,
282 rewardCollectableDuration int64 ,
283 ) *launchpad.RewardManager {
284 manager := launchpad.NewRewardManager(totalDistributeAmount, distributeStartTime, distributeEndTime, rewardCollectableDuration)
285
286 updateDistributeAmountPerSecondX128(manager, totalDistributeAmount, distributeStartTime, distributeEndTime)
287
288 return manager
289 }
290
291 // calculateMaximumClaimableRewardsForActiveDeposits reserves every active
292 // claim without traversing the attacker-controlled reward tree. Aggregating
293 // before Q128 truncation retains rounding dust but cannot under-reserve claims.
294 func calculateMaximumClaimableRewardsForActiveDeposits(r *launchpad.RewardManager) int64 {
295 reward, overflow := u256.Zero().MulOverflow(
296 r.AccumulatedRewardPerDepositX128(),
297 u256.NewUintFromInt64(r.ActiveDepositAmount()),
298 )
299 if overflow {
300 panic(errors.New(errOverflow))
301 }
302
303 reward = reward.Sub(reward, r.ActivePriceDebtX128()).Rsh(reward, 128 )
304 totalReward := gnsmath.SafeConvertToInt64(reward)
305 if totalReward <= r.ActiveClaimedAmount() {
306 return 0
307 }
308
309 return gnsmath.SafeSubInt64(totalReward, r.ActiveClaimedAmount())
310 }
311