Smart Contract
PPPV7
A.3ddb31a3880d1d8f.PPPV7
1/**
2
3# This smart contract is designed for a comprehensive on-chain points system within the Increment.Fi ecosystem.
4# It integrates various DeFi infrastructures including Lending, Swap, Liquid Staking, and Farming.
5
6*/
7
8import FungibleToken from 0xf233dcee88fe0abe
9// Lending
10import LendingInterfaces from 0x2df970b6cdee5735
11import LendingConfig from 0x2df970b6cdee5735
12// Swap
13import SwapConfig from 0xb78ef7afa52ff906
14import SwapInterfaces from 0xb78ef7afa52ff906
15// Liquid Staking
16import LiquidStaking from 0xd6f80565193ad727
17// Farm
18import Staking from 0x1b77ba4b414de352
19import StakingNFT from 0x1b77ba4b414de352
20import FLOAT from 0x2d4c3caffbeab845
21
22// Oracle
23import PublicPriceOracle from 0xec67451f8a58216a
24
25// Referral System
26import RV3 from 0x3ddb31a3880d1d8f
27
28pub contract PPPV7 {
29
30 // Total supply of points in existence
31 access(all) var _totalSupply: UFix64
32
33 // Mapping of user addresses to their base points amount.
34 access(self) let _pointsBase: {Address: UFix64}
35
36 // Snapshot of points for each user at a specific historical moment.
37 access(self) let _pointsHistorySnapshot: {Address: UFix64}
38
39 // Mapping from referrer addresses to the total points they've earned from their referees.
40 // {Referrer: TotalPointsFromReferees}
41 access(self) let _totalPointsAsReferrer: {Address: UFix64}
42
43 // {Referrer: {Referee: PointsFrom}}
44 access(self) let _pointsFromReferees: {Address: {Address: UFix64}}
45
46 // Mapping of user addresses to the points they've boosted as referees.
47 access(self) let _pointsAsReferee: {Address: UFix64}
48
49 // Mapping of user addresses to the points they've boosted as core members of the Increment.
50 access(self) let _pointsAsCoreMember: {Address: UFix64}
51
52 // A blacklist of user addresses that are excluded from earning points.
53 access(self) let _userBlacklist: {Address: Bool}
54
55 // A whitelist of swap pool addresses that are eligible for point earnings.
56 access(self) let _swapPoolWhitelist: {Address: Bool}
57
58 // Configuration for points earning rates based on various criteria such as lending supply, etc.
59 /*
60 {
61 "LendingSupply": {
62 0.0 : 0.001, // supply usd amount: 0.0 ~ 1000.0 -> 0.001
63 1000.0 : 0.002, // supply usd amount: 1000.0 ~ 10000.0 -> 0.002
64 10000.0 : 0.003 // supply usd amount: 10000.0 ~ Max -> 0.003
65 }
66 }
67 */
68 access(self) let _pointsRate: {String: AnyStruct}
69
70 // The duration in seconds for each points rate session, affecting how points accumulate over time.
71 access(self) let _secondsPerPointsRateSession: UFix64
72
73 // Mapping of user states, storing various metrics such as last update timestamp for points calculation.
74 access(self) let _userStates: {Address: {String: UFix64}}
75
76 // Indicates whether a user has claimed their points from the historical snapshot.
77 access(self) let _ifClaimHistorySnapshot: {Address: Bool}
78 access(self) var _claimEndTimestamp: UFix64
79
80 // A list of user addresses ranked by their total points, representing the top users.
81 access(self) var _topUsers: [Address]
82
83 //
84 access(self) let _reservedFields: {String: AnyStruct}
85
86 /// Events
87 access(all) event PointsMinted(userAddr: Address, amount: UFix64, source: String, param: {String: String})
88 access(all) event PointsBurned(userAddr: Address, amount: UFix64, source: String, param: {String: String})
89 access(all) event StateUpdated(userAddr: Address, state: {String: UFix64})
90 access(all) event PointsRateChanged(source: String, ori: UFix64, new: UFix64)
91 access(all) event PointsTierRateChanged(source: String, ori: {UFix64: UFix64}, new: {UFix64: UFix64})
92 access(all) event ClaimSnapshotPoints(userAddr: Address, amount: UFix64)
93 access(all) event TopUsersChanged(ori: [Address], new: [Address])
94 access(all) event SetSnapshotPoints(userAddr: Address, pre: UFix64, new: UFix64)
95
96 // Method to calculate a user's total balance of points.
97 access(all) view fun balanceOf(_ userAddr: Address): UFix64 {
98 // Base points plus any unclaimed historical snapshot points, points as referrer, referee, core member,
99 // and newly accrued points since the last update.
100 return self.getBasePoints(userAddr)
101 +
102 (self.ifClaimHistorySnapshot(userAddr)? self.getHistorySnapshotPoints(userAddr): 0.0)
103 +
104 self.getPointsAsReferrer(userAddr)
105 +
106 self.getPointsAsReferee(userAddr)
107 +
108 self.getPointsAsCoreMember(userAddr)
109 +
110 self.calculateNewPointsSinceLastUpdate(userAddr: userAddr) * (1.0 + self.getBasePointsBoostRate(userAddr)) // accured points
111 }
112
113 // Mint Points
114 access(self) fun _mint(targetAddr: Address, amount: UFix64) {
115 if self._userBlacklist.containsKey(targetAddr) {
116 return
117 }
118
119 // mint points
120 if self._pointsBase.containsKey(targetAddr) == false {
121 self._pointsBase[targetAddr] = 0.0
122 }
123 self._pointsBase[targetAddr] = self._pointsBase[targetAddr]! + amount
124
125 // Attempt to retrieve the referrer address for the target address. If a referrer exists...
126 let referrerAddr: Address? = RV3.getReferrerByReferee(referee: targetAddr)
127 if referrerAddr != nil {
128 // Calculate and mint referee boost points.
129 let boostAmountAsReferee = amount * self.getPointsRate_RefereeUp()
130 if boostAmountAsReferee > 0.0 {
131 if self._pointsAsReferee.containsKey(targetAddr) == false {
132 self._pointsAsReferee[targetAddr] = boostAmountAsReferee
133 } else {
134 self._pointsAsReferee[targetAddr] = self._pointsAsReferee[targetAddr]! + boostAmountAsReferee
135 }
136 emit PointsMinted(
137 userAddr: targetAddr,
138 amount: boostAmountAsReferee,
139 source: "AsReferee",
140 param: {}
141 )
142 self._totalSupply = self._totalSupply + boostAmountAsReferee
143 }
144
145 // Calculate and mint referrer boost points.
146 let boostAmountForReferrer = amount * self.getPointsRate_ReferrerUp()
147 if boostAmountForReferrer > 0.0 && self._userBlacklist.containsKey(referrerAddr!) == false {
148 if self._totalPointsAsReferrer.containsKey(referrerAddr!) == false {
149 self._totalPointsAsReferrer[referrerAddr!] = boostAmountForReferrer
150 } else {
151 self._totalPointsAsReferrer[referrerAddr!] = self._totalPointsAsReferrer[referrerAddr!]! + boostAmountForReferrer
152 }
153 if self._pointsFromReferees.containsKey(referrerAddr!) == false {
154 self._pointsFromReferees[referrerAddr!] = {}
155 }
156 if self._pointsFromReferees[referrerAddr!]!.containsKey(targetAddr) == false {
157 self._pointsFromReferees[referrerAddr!]!.insert(key: targetAddr, boostAmountForReferrer)
158 } else {
159 self._pointsFromReferees[referrerAddr!]!.insert(key: targetAddr, self._pointsFromReferees[referrerAddr!]![targetAddr]! + boostAmountForReferrer)
160 }
161 if self._pointsBase.containsKey(referrerAddr!) == false {
162 self._pointsBase[referrerAddr!] = 0.0
163 }
164 emit PointsMinted(
165 userAddr: referrerAddr!,
166 amount: boostAmountForReferrer,
167 source: "AsReferrer",
168 param: {}
169 )
170 self._totalSupply = self._totalSupply + boostAmountForReferrer
171 }
172 }
173
174 // Calculate and mint core member boost points if the target address is a core member.
175 if self.isCoreMember(targetAddr) {
176 let boostAmountAsCoreMember = amount * self.getPointsRate_CoreMember()
177 if boostAmountAsCoreMember > 0.0 {
178 if self._pointsAsCoreMember.containsKey(targetAddr) == false {
179 self._pointsAsCoreMember[targetAddr] = boostAmountAsCoreMember
180 } else {
181 self._pointsAsCoreMember[targetAddr] = self._pointsAsCoreMember[targetAddr]! + boostAmountAsCoreMember
182 }
183 emit PointsMinted(
184 userAddr: targetAddr,
185 amount: boostAmountAsCoreMember,
186 source: "AsCoreMember",
187 param: {}
188 )
189 self._totalSupply = self._totalSupply + boostAmountAsCoreMember
190 }
191 }
192
193 // Update the total supply of points by adding the minted amount.
194 self._totalSupply = self._totalSupply + amount;
195 }
196
197 // Function to calculate new points earned by a user since their last update.
198 access(all) view fun calculateNewPointsSinceLastUpdate(userAddr: Address): UFix64 {
199 let lastUpdateTimestamp = self.getUserState_LastUpdateTimestamp(userAddr: userAddr)
200 if lastUpdateTimestamp == 0.0 { return 0.0 }
201 if self._userBlacklist.containsKey(userAddr) { return 0.0 }
202
203 // Lending Supply
204 let accuredLendingSupplyPoints = self.calculateNewPointsSinceLastUpdate_LendingSupply(userAddr: userAddr)
205 // Lending Borrow
206 let accuredLendingBorrowPoints = self.calculateNewPointsSinceLastUpdate_LendingBorrow(userAddr: userAddr)
207 // stFlow Holdings
208 let accuredStFlowHoldingPoints = self.calculateNewPointsSinceLastUpdate_stFlowHolding(userAddr: userAddr)
209 // Swap LP
210 let accuredSwapLPPoints = self.calculateNewPointsSinceLastUpdate_SwapLP(userAddr: userAddr)
211
212 // Sums up the accrued points from all activities, adjusting for any base points boost rate applicable to the user.
213 return accuredLendingSupplyPoints + accuredLendingBorrowPoints + accuredStFlowHoldingPoints + accuredSwapLPPoints
214
215 }
216
217 // Updates the state of a user, including their related balance based on DeFi activities.
218 access(all) fun updateUserState(userAddr: Address) {
219 if self._userBlacklist.containsKey(userAddr) {
220 return
221 }
222
223 let lastUpdateTimestamp = self.getUserState_LastUpdateTimestamp(userAddr: userAddr)
224 let duration = getCurrentBlock().timestamp - lastUpdateTimestamp
225 let durationStr = duration.toString()
226
227 if duration > 0.0 {
228 // Calculate new points accrued from lending supply activities.
229 let accuredLendingSupplyPoints = self.calculateNewPointsSinceLastUpdate_LendingSupply(userAddr: userAddr)
230 if (accuredLendingSupplyPoints > 0.0) {
231 emit PointsMinted(
232 userAddr: userAddr,
233 amount: accuredLendingSupplyPoints,
234 source: "LendingSupply",
235 param: {
236 "SupplyUsdValue": self.getUserState_LendingSupply(userAddr: userAddr).toString(),
237 "Duration": durationStr
238 }
239 )
240 }
241
242 // Similar calculations and event emissions for lending borrow, stFlow holding, and Swap LP activities.
243 // Lending Borrow
244 let accuredLendingBorrowPoints = self.calculateNewPointsSinceLastUpdate_LendingBorrow(userAddr: userAddr)
245 if (accuredLendingBorrowPoints > 0.0) {
246 emit PointsMinted(
247 userAddr: userAddr,
248 amount: accuredLendingBorrowPoints,
249 source: "LendingBorrow",
250 param: {
251 "BorrowUsdValue": self.getUserState_LendingBorrow(userAddr: userAddr).toString(),
252 "Duration": durationStr
253 }
254 )
255 }
256
257 // stFlow Holding
258 let accuredStFlowHoldingPoints = self.calculateNewPointsSinceLastUpdate_stFlowHolding(userAddr: userAddr)
259 if (accuredStFlowHoldingPoints > 0.0) {
260 emit PointsMinted(
261 userAddr: userAddr,
262 amount: accuredStFlowHoldingPoints,
263 source: "stFlowHolding",
264 param: {
265 "stFlowHoldingBalance": self.getUserState_stFlowHolding(userAddr: userAddr).toString(),
266 "Duration": durationStr
267 }
268 )
269 }
270
271 // Swap LP
272 let accuredSwapLPPoints = self.calculateNewPointsSinceLastUpdate_SwapLP(userAddr: userAddr)
273 if (accuredSwapLPPoints > 0.0) {
274 emit PointsMinted(
275 userAddr: userAddr,
276 amount: accuredSwapLPPoints,
277 source: "SwapLP",
278 param: {
279 "SwapLPUsdValue": self.getUserState_SwapLPUsd(userAddr: userAddr).toString(),
280 "Duration": durationStr
281 }
282 )
283 }
284
285 // Mint points for the total accrued from all activities.
286 let accuredPointsToMint =
287 accuredLendingSupplyPoints +
288 accuredLendingBorrowPoints +
289 accuredStFlowHoldingPoints +
290 accuredSwapLPPoints
291 if (accuredPointsToMint > 0.0) {
292 self._mint(targetAddr: userAddr, amount: accuredPointsToMint)
293 }
294 }
295
296 // Fetch updated on-chain user states for various DeFi activities.
297 let states = self.fetchOnchainUserStates(userAddr: userAddr)
298 let totalSupplyAmountInUsd = states[0]
299 let totalBorrowAmountInUsd = states[1]
300 let stFlowTotalBalance = states[2]
301 let totalLpBalanceUsd = states[3]
302 let totalLpAmount = states[4]
303
304 // Update user states with the latest data.
305 if self._userStates.containsKey(userAddr)
306 || totalSupplyAmountInUsd > 0.0 || totalBorrowAmountInUsd > 0.0
307 || stFlowTotalBalance > 0.0
308 || totalLpBalanceUsd > 0.0
309 {
310 if self._userStates.containsKey(userAddr) == false {
311 self._userStates[userAddr] = {}
312 }
313
314 self.setUserState_LendingSupply(userAddr: userAddr, supplyAmount: totalSupplyAmountInUsd)
315 self.setUserState_LendingBorrow(userAddr: userAddr, borrowAmount: totalBorrowAmountInUsd)
316 self.setUserState_stFlowHolding(userAddr: userAddr, stFlowBalance: stFlowTotalBalance)
317 self.setUserState_SwapLPUsd(userAddr: userAddr, lpUsd: totalLpBalanceUsd)
318 self.setUserState_SwapLPAmount(userAddr: userAddr, lpAmount: totalLpAmount)
319
320 self.setUserState_LastUpdateTimestamp(userAddr: userAddr, timestamp: getCurrentBlock().timestamp)
321
322 //
323 emit StateUpdated(userAddr: userAddr, state: self._userStates[userAddr]!)
324 }
325 }
326
327 // Function to fetch and calculate a user's state across different DeFi protocols.
328 access(all) view fun fetchOnchainUserStates(userAddr: Address): [UFix64] {
329 // Oracle Price
330 let oraclePrices: {String: UFix64} = { // OracleAddress -> Token Price
331 "Flow": PublicPriceOracle.getLatestPrice(oracleAddr: 0xe385412159992e11),
332 "stFlow": PublicPriceOracle.getLatestPrice(oracleAddr: 0x031dabc5ba1d2932),
333 "USDC": PublicPriceOracle.getLatestPrice(oracleAddr: 0xf5d12412c09d2470)
334 }
335
336 // Access the lending comptroller to fetch market addresses and calculate the user's supply and borrow in USD.
337 let lendingComptrollerRef = getAccount(0xf80cb737bfe7c792).getCapability<&{LendingInterfaces.ComptrollerPublic}>(LendingConfig.ComptrollerPublicPath).borrow()!
338 let marketAddrs: [Address] = lendingComptrollerRef.getAllMarkets()
339 let lendingOracleRef = getAccount(0x72d3a05910b6ffa3).getCapability<&{LendingInterfaces.OraclePublic}>(LendingConfig.OraclePublicPath).borrow()!
340 var totalSupplyAmountInUsd = 0.0
341 var totalBorrowAmountInUsd = 0.0
342 for poolAddr in marketAddrs {
343 let poolRef = lendingComptrollerRef.getPoolPublicRef(poolAddr: poolAddr)
344 let poolOraclePrice = lendingOracleRef.getUnderlyingPrice(pool: poolAddr)
345 let res: [UInt256; 5] = poolRef.getAccountRealtimeScaled(account: userAddr)
346 let supplyAmount = SwapConfig.ScaledUInt256ToUFix64(res[0] * res[1] / SwapConfig.scaleFactor)
347 let borrowAmount = SwapConfig.ScaledUInt256ToUFix64(res[2])
348 totalSupplyAmountInUsd = totalSupplyAmountInUsd + supplyAmount * poolOraclePrice
349 totalBorrowAmountInUsd = totalBorrowAmountInUsd + borrowAmount * poolOraclePrice
350 }
351
352 // Liquid Staking State
353 // Calculate the user's stFlow token balance by checking their vault capability.
354 var stFlowTotalBalance = 0.0
355 let stFlowVaultCap = getAccount(userAddr).getCapability<&{FungibleToken.Balance}>(/public/stFlowTokenBalance)
356 if stFlowVaultCap.check() {
357 // Prevent fake stFlow token vault
358 if stFlowVaultCap.borrow()!.getType().identifier == "A.d6f80565193ad727.stFlowToken.Vault" {
359 stFlowTotalBalance = stFlowVaultCap.borrow()!.balance
360 }
361 }
362
363 // Calculate the user's total LP balance in USD and total LP amount.
364 let lpPrices: {Address: UFix64} = {}
365 var totalLpBalanceUsd = 0.0
366 var totalLpAmount = 0.0
367 let lpTokenCollectionCap = getAccount(userAddr).getCapability<&{SwapInterfaces.LpTokenCollectionPublic}>(SwapConfig.LpTokenCollectionPublicPath)
368 if lpTokenCollectionCap.check() {
369 // Prevent fake lp token vault
370 if lpTokenCollectionCap.borrow()!.getType().identifier == "A.b063c16cac85dbd1.SwapFactory.LpTokenCollection" {
371 let lpTokenCollectionRef = lpTokenCollectionCap.borrow()!
372 let liquidityPairAddrs = lpTokenCollectionRef.getAllLPTokens()
373 for pairAddr in liquidityPairAddrs {
374 //
375 if self._swapPoolWhitelist.containsKey(pairAddr) == false {
376 continue
377 }
378
379 var lpTokenAmount = lpTokenCollectionRef.getLpTokenBalance(pairAddr: pairAddr)
380 let pairInfo = getAccount(pairAddr).getCapability<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair).borrow()!.getPairInfo()
381 // Cal lp price
382 var lpPrice = 0.0
383 if lpPrices.containsKey(pairAddr) {
384 lpPrice = lpPrices[pairAddr]!
385 } else {
386 lpPrice = self.calValidLpPrice(pairInfo: pairInfo, oraclePrices: oraclePrices)
387 lpPrices[pairAddr] = lpPrice
388 }
389 if lpPrice == 0.0 || lpTokenAmount == 0.0 { continue }
390 totalLpBalanceUsd = totalLpBalanceUsd + lpPrice * lpTokenAmount
391 totalLpAmount = totalLpAmount + lpTokenAmount
392 }
393 }
394 }
395
396 // Swap LP in Farm & stFlow in Farm
397 // let farmCollectionRef = getAccount(0x1b77ba4b414de352).getCapability<&{Staking.PoolCollectionPublic}>(Staking.CollectionPublicPath).borrow()!
398 // let userFarmIds = Staking.getUserStakingIds(address: userAddr)
399 // for farmPoolId in userFarmIds {
400 // let farmPool = farmCollectionRef.getPool(pid: farmPoolId)
401 // let farmPoolInfo = farmPool.getPoolInfo()
402 // let userInfo = farmPool.getUserInfo(address: userAddr)!
403 // if farmPoolInfo.status == "0" || farmPoolInfo.status == "1" || farmPoolInfo.status == "2" {
404 // let acceptTokenKey = farmPoolInfo.acceptTokenKey
405 // let acceptTokenName = acceptTokenKey.slice(from: 19, upTo: acceptTokenKey.length)
406 // let userFarmAmount = userInfo.stakingAmount
407 // // add stFlow holding balance
408 // if acceptTokenKey == "A.d6f80565193ad727.stFlowToken" {
409 // stFlowTotalBalance = stFlowTotalBalance + userFarmAmount
410 // continue
411 // }
412 // if userFarmAmount == 0.0 {
413 // continue
414 // }
415 // // add lp holding balance
416 // let swapPoolAddress = self.type2address(acceptTokenKey)
417 // if acceptTokenName == "SwapPair" {
418 // let swapPoolInfo = getAccount(swapPoolAddress).getCapability<&{SwapInterfaces.PairPublic}>(SwapConfig.PairPublicPath).borrow()!.getPairInfo()
419 // var lpPrice = 0.0
420 // if lpPrices.containsKey(swapPoolAddress) {
421 // lpPrice = lpPrices[swapPoolAddress]!
422 // } else {
423 // lpPrice = self.calValidLpPrice(pairInfo: swapPoolInfo, oraclePrices: oraclePrices)
424 // lpPrices[swapPoolAddress] = lpPrice
425 // }
426 // totalLpBalanceUsd = totalLpBalanceUsd + userFarmAmount * lpPrice
427 // totalLpAmount = totalLpAmount + userFarmAmount
428 // }
429 // }
430 // }
431
432 return [
433 totalSupplyAmountInUsd,
434 totalBorrowAmountInUsd,
435 stFlowTotalBalance,
436 totalLpBalanceUsd,
437 totalLpAmount
438 ]
439 }
440
441 access(all) fun claimSnapshotPoints(userCertificateCap: Capability<&{LendingInterfaces.IdentityCertificate}>) {
442 pre {
443 self._claimEndTimestamp > getCurrentBlock().timestamp: "The claim period has concluded."
444 }
445 let userAddr: Address = userCertificateCap.borrow()!.owner!.address
446 assert(self.getHistorySnapshotPoints(userAddr) > 0.0, message: "Nothing to claim")
447 self._ifClaimHistorySnapshot[userAddr] = true
448
449 emit ClaimSnapshotPoints(userAddr: userAddr, amount: self.getHistorySnapshotPoints(userAddr))
450 }
451
452 access(all) view fun getBasePointsLength(): Int {
453 return self._pointsBase.length
454 }
455
456 access(all) view fun getSlicedBasePointsAddrs(from: Int, to: Int): [Address] {
457 let len = self._userStates.length
458 let endIndex = to > len ? len : to
459 var curIndex = from
460 return self._pointsBase.keys.slice(from: from, upTo: to)
461 }
462
463 access(all) view fun getBasePoints(_ userAddr: Address): UFix64 {
464 return self._pointsBase.containsKey(userAddr)? self._pointsBase[userAddr]! : 0.0
465 }
466
467 access(all) view fun getHistorySnapshotPoints(_ userAddr: Address): UFix64 {
468 return self._pointsHistorySnapshot.containsKey(userAddr)? self._pointsHistorySnapshot[userAddr]! : 0.0
469 }
470
471 access(all) view fun getPointsAsReferrer(_ userAddr: Address): UFix64 {
472 return self._totalPointsAsReferrer.containsKey(userAddr)? self._totalPointsAsReferrer[userAddr]!: 0.0
473 }
474
475 access(all) view fun getPointsAsReferee(_ userAddr: Address): UFix64 {
476 return self._pointsAsReferee.containsKey(userAddr)? self._pointsAsReferee[userAddr]!: 0.0
477 }
478
479 access(all) view fun getLastestPointsAsReferee(_ userAddr: Address): UFix64 {
480 let referrerAddr = RV3.getReferrerByReferee(referee: userAddr)
481 let lastUpdatePointsAsReferee = self.getPointsAsReferee(userAddr);
482 let latestPointsAsReferee =
483 (referrerAddr != nil? self.getPointsRate_RefereeUp() : 0.0)
484 *
485 self.calculateNewPointsSinceLastUpdate(userAddr: userAddr)
486 return lastUpdatePointsAsReferee + latestPointsAsReferee;
487 }
488
489 access(all) view fun getPointsAsCoreMember(_ userAddr: Address): UFix64 {
490 return self._pointsAsCoreMember.containsKey(userAddr)? self._pointsAsCoreMember[userAddr]!: 0.0
491 }
492
493 access(all) view fun getLastestPointsAsCoreMember(_ userAddr: Address): UFix64 {
494 let lastUpdatePointsAsCoreMember = self.getPointsAsCoreMember(userAddr);
495 let latestPointsAsCoreMember =
496 (self.isCoreMember(userAddr)? self.getPointsRate_CoreMember() : 0.0)
497 *
498 self.calculateNewPointsSinceLastUpdate(userAddr: userAddr)
499 return lastUpdatePointsAsCoreMember + latestPointsAsCoreMember;
500 }
501
502 access(all) view fun getPointsAndTimestamp(_ userAddr: Address): [UFix64; 3] {
503 return [self.balanceOf(userAddr), self.getPointsAsReferrer(userAddr), self.getUserState_LastUpdateTimestamp(userAddr: userAddr)]
504 }
505
506 access(all) view fun getUserInfo(_ userAddr: Address): [UFix64] {
507 return [
508 self.balanceOf(userAddr),
509 self.getPointsAsReferrer(userAddr),
510 self.getUserState_LastUpdateTimestamp(userAddr: userAddr),
511 self.getUserState_LendingSupply(userAddr: userAddr),
512 self.getUserState_LendingBorrow(userAddr: userAddr),
513 self.getUserState_stFlowHolding(userAddr: userAddr),
514 self.getUserState_SwapLPUsd(userAddr: userAddr),
515 self.getUserState_SwapVolume(userAddr: userAddr)
516 ]
517 }
518
519 access(all) view fun getUserInfosLength(): Int {
520 return self._userStates.length
521 }
522
523 access(all) view fun getSlicedUserInfos(from: Int, to: Int): {Address: [UFix64]} {
524 let len = self._userStates.length
525 let endIndex = to > len ? len : to
526 var curIndex = from
527 let res: {Address: [UFix64]} = {}
528 while curIndex < endIndex {
529 let userAddr: Address = self._userStates.keys[curIndex]
530 res[userAddr] = self.getUserInfo(userAddr)
531 curIndex = curIndex + 1
532 }
533 return res
534 }
535
536
537
538 access(all) view fun isCoreMember(_ userAddr: Address): Bool {
539 let coreMemberEventID: UInt64 = 326723707
540 let floatCollection = getAccount(userAddr).getCapability(FLOAT.FLOATCollectionPublicPath).borrow<&FLOAT.Collection{FLOAT.CollectionPublic}>()
541 if floatCollection == nil {
542 return false
543 }
544 let nftID: [UInt64] = floatCollection!.ownedIdsFromEvent(eventId: coreMemberEventID)
545 if floatCollection!.getType().identifier == "A.2d4c3caffbeab845.FLOAT.Collection" && nftID.length > 0{
546 return true
547 }
548 let poolCollectionCap = getAccount(StakingNFT.address).getCapability<&{StakingNFT.PoolCollectionPublic}>(StakingNFT.CollectionPublicPath).borrow()!
549 let count = StakingNFT.poolCount
550 var idx: UInt64 = 0
551 while idx < count {
552 let pool = poolCollectionCap.getPool(pid: idx)
553 let poolExtraParams = pool.getExtraParams()
554 if (poolExtraParams.containsKey("eventId") && poolExtraParams["eventId"]! as! UInt64 == coreMemberEventID) {
555 let userInfo = pool.getUserInfo(address: userAddr)
556 if userInfo != nil && userInfo!.stakedNftIds.length > 0 {
557 return true
558 }
559 }
560 idx = idx + 1
561 }
562 return false
563 }
564
565 access(all) view fun ifClaimHistorySnapshot(_ userAddr: Address): Bool {
566 if self._ifClaimHistorySnapshot.containsKey(userAddr) == true {
567 return self._ifClaimHistorySnapshot[userAddr]!
568 }
569 return false;
570 }
571
572 access(all) view fun getBasePointsBoostRate(_ userAddr: Address): UFix64 {
573 let referrerAddr = RV3.getReferrerByReferee(referee: userAddr)
574 return (self.isCoreMember(userAddr)? self.getPointsRate_CoreMember() : 0.0)
575 +
576 (referrerAddr != nil? self.getPointsRate_RefereeUp() : 0.0)
577 }
578
579 // Accure Lending Supply
580 access(all) view fun calculateNewPointsSinceLastUpdate_LendingSupply(userAddr: Address): UFix64 {
581 let lastUpdateTimestamp = self.getUserState_LastUpdateTimestamp(userAddr: userAddr)
582 var accuredPoints = 0.0
583 if lastUpdateTimestamp == 0.0 { return 0.0 }
584 let currUpdateTimestamp = getCurrentBlock().timestamp
585 let duration = currUpdateTimestamp - lastUpdateTimestamp
586 if duration > 0.0 {
587 let supplyAmountUsd = self.getUserState_LendingSupply(userAddr: userAddr)
588 accuredPoints = supplyAmountUsd * self.getPointsRate_LendingSupply(amount: supplyAmountUsd) / self._secondsPerPointsRateSession * duration
589 }
590 return accuredPoints
591 }
592
593 // Accure Lending Borrow
594 access(all) view fun calculateNewPointsSinceLastUpdate_LendingBorrow(userAddr: Address): UFix64 {
595 let lastUpdateTimestamp = self.getUserState_LastUpdateTimestamp(userAddr: userAddr)
596 var accuredPoints = 0.0
597 if lastUpdateTimestamp == 0.0 { return 0.0 }
598 let currUpdateTimestamp = getCurrentBlock().timestamp
599 let duration = currUpdateTimestamp - lastUpdateTimestamp
600 if duration > 0.0 {
601 let borrowAmountUsd = self.getUserState_LendingBorrow(userAddr: userAddr)
602 accuredPoints = borrowAmountUsd * self.getPointsRate_LendingBorrow(amount: borrowAmountUsd) / self._secondsPerPointsRateSession * duration
603 }
604 return accuredPoints
605 }
606
607 // Accure Liquid Staking - stFlowHolding
608 access(self) view fun calculateNewPointsSinceLastUpdate_stFlowHolding(userAddr: Address): UFix64 {
609 let lastUpdateTimestamp = self.getUserState_LastUpdateTimestamp(userAddr: userAddr)
610 var accuredPoints = 0.0
611 if lastUpdateTimestamp == 0.0 { return 0.0 }
612 let currUpdateTimestamp = getCurrentBlock().timestamp
613 let duration = currUpdateTimestamp - lastUpdateTimestamp
614 if duration > 0.0 {
615 let stFlowHolding = self.getUserState_stFlowHolding(userAddr: userAddr)
616 accuredPoints = stFlowHolding * self.getPointsRate_stFlowHolding(amount: stFlowHolding) / self._secondsPerPointsRateSession * duration
617 }
618 return accuredPoints
619 }
620
621 // Accure Swap LP
622 access(self) view fun calculateNewPointsSinceLastUpdate_SwapLP(userAddr: Address): UFix64 {
623 let lastUpdateTimestamp = self.getUserState_LastUpdateTimestamp(userAddr: userAddr)
624 var accuredPoints = 0.0
625 if lastUpdateTimestamp == 0.0 { return 0.0 }
626 let currUpdateTimestamp = getCurrentBlock().timestamp
627 let duration = currUpdateTimestamp - lastUpdateTimestamp
628 if duration > 0.0 {
629 let swapLP = self.getUserState_SwapLPUsd(userAddr: userAddr)
630 accuredPoints = swapLP * self.getPointsRate_SwapLP(amount: swapLP) / self._secondsPerPointsRateSession * duration
631 }
632 return accuredPoints
633 }
634
635 access(all) view fun getTopUsers(): [Address] { return self._topUsers }
636
637 access(all) view fun getSwapPoolWhiltlist(): {Address: Bool} { return self._swapPoolWhitelist }
638
639 access(all) view fun getUserBlacklist(): {Address: Bool} { return self._userBlacklist }
640
641 // Get Points Rate
642 access(all) view fun getPointsRate(): {String: AnyStruct} { return self._pointsRate }
643 access(all) view fun getPointsRate_LendingSupply(amount: UFix64): UFix64 { return self.calculateTierRateByAmount(amount: amount, tier: self._pointsRate["LendingSupply"]! as! {UFix64: UFix64}) }
644 access(all) view fun getPointsRate_LendingBorrow(amount: UFix64): UFix64 { return self.calculateTierRateByAmount(amount: amount, tier: self._pointsRate["LendingBorrow"]! as! {UFix64: UFix64}) }
645 access(all) view fun getPointsRate_stFlowHolding(amount: UFix64): UFix64 { return self.calculateTierRateByAmount(amount: amount, tier: self._pointsRate["stFlowHolding"]! as! {UFix64: UFix64}) }
646 access(all) view fun getPointsRate_SwapLP(amount: UFix64): UFix64 { return self.calculateTierRateByAmount(amount: amount, tier: self._pointsRate["SwapLP"]! as! {UFix64: UFix64}) }
647 access(all) view fun getPointsRate_SwapVolume(amount: UFix64): UFix64 { return self.calculateTierRateByAmount(amount: amount, tier: self._pointsRate["SwapVolume"]! as! {UFix64: UFix64}) }
648 access(all) view fun getPointsRate_ReferrerUp(): UFix64 { return self._pointsRate["ReferrerUp"]! as! UFix64 }
649 access(all) view fun getPointsRate_RefereeUp(): UFix64 { return self._pointsRate["RefereeUp"]! as! UFix64 }
650 access(all) view fun getPointsRate_CoreMember(): UFix64 { return self._pointsRate["CoreMember"]! as! UFix64 }
651
652 // Get User State
653 access(all) view fun getUserState(userAddr: Address): {String: UFix64} { return self._userStates.containsKey(userAddr)? self._userStates[userAddr]! : {} }
654 access(all) view fun getUserState_LastUpdateTimestamp(userAddr: Address): UFix64 { return self._userStates.containsKey(userAddr)? (self._userStates[userAddr]!.containsKey("LastUpdateTimestamp")? self._userStates[userAddr]!["LastUpdateTimestamp"]! : 0.0): 0.0 }
655 access(all) view fun getUserState_LendingSupply(userAddr: Address): UFix64 { return self._userStates.containsKey(userAddr)? (self._userStates[userAddr]!.containsKey("LendingTotalSupplyUsd")? self._userStates[userAddr]!["LendingTotalSupplyUsd"]! : 0.0): 0.0 }
656 access(all) view fun getUserState_LendingBorrow(userAddr: Address): UFix64 { return self._userStates.containsKey(userAddr)? (self._userStates[userAddr]!.containsKey("LendingTotalBorrowUsd")? self._userStates[userAddr]!["LendingTotalBorrowUsd"]! : 0.0): 0.0 }
657 access(all) view fun getUserState_stFlowHolding(userAddr: Address): UFix64 { return self._userStates.containsKey(userAddr)? (self._userStates[userAddr]!.containsKey("stFlowHolding")? self._userStates[userAddr]!["stFlowHolding"]! : 0.0): 0.0 }
658 access(all) view fun getUserState_SwapLPUsd(userAddr: Address): UFix64 { return self._userStates.containsKey(userAddr)? (self._userStates[userAddr]!.containsKey("SwapLpUsd")? self._userStates[userAddr]!["SwapLpUsd"]! : 0.0): 0.0 }
659 access(all) view fun getUserState_SwapLPAmount(userAddr: Address): UFix64 { return self._userStates.containsKey(userAddr)? (self._userStates[userAddr]!.containsKey("SwapLpAmount")? self._userStates[userAddr]!["SwapLpAmount"]! : 0.0): 0.0 }
660 access(all) view fun getUserState_SwapVolume(userAddr: Address): UFix64 { return self._userStates.containsKey(userAddr)? (self._userStates[userAddr]!.containsKey("SwapVolumeUsd")? self._userStates[userAddr]!["SwapVolumeUsd"]! : 0.0): 0.0 }
661
662 access(self) fun setUserState_LastUpdateTimestamp(userAddr: Address, timestamp: UFix64) { self._userStates[userAddr]!.insert(key: "LastUpdateTimestamp", timestamp) }
663 access(self) fun setUserState_LendingSupply(userAddr: Address, supplyAmount: UFix64) { self._userStates[userAddr]!.insert(key: "LendingTotalSupplyUsd", supplyAmount) }
664 access(self) fun setUserState_LendingBorrow(userAddr: Address, borrowAmount: UFix64) { self._userStates[userAddr]!.insert(key: "LendingTotalBorrowUsd", borrowAmount) }
665 access(self) fun setUserState_stFlowHolding(userAddr: Address, stFlowBalance: UFix64) { self._userStates[userAddr]!.insert(key: "stFlowHolding", stFlowBalance) }
666 access(self) fun setUserState_SwapLPUsd(userAddr: Address, lpUsd: UFix64) { self._userStates[userAddr]!.insert(key: "SwapLpUsd", lpUsd) }
667 access(self) fun setUserState_SwapLPAmount(userAddr: Address, lpAmount: UFix64) { self._userStates[userAddr]!.insert(key: "SwapLpAmount", lpAmount) }
668 access(self) fun setUserState_SwapVolume(userAddr: Address, volume: UFix64) { self._userStates[userAddr]!.insert(key: "SwapVolumeUsd", volume) }
669
670 access(all) view fun calculateTierRateByAmount(amount: UFix64, tier: {UFix64: UFix64}): UFix64 {
671 var rate = 0.0
672 var maxThreshold = 0.0
673 for threshold in tier.keys {
674 if amount >= threshold && threshold >= maxThreshold {
675 rate = tier[threshold]!
676 maxThreshold = threshold
677 }
678 }
679 return rate
680 }
681
682 // Calculates the price of an LP token based on the reserves of the pair and the current prices of the underlying tokens.
683 access(all) view fun calValidLpPrice(pairInfo: [AnyStruct], oraclePrices: {String: UFix64}): UFix64 {
684 var reserveAmount = 0.0
685 var reservePrice = 0.0
686 var lpPrice = 0.0
687 if pairInfo[0] as! String == "A.b19436aae4d94622.FiatToken" {reserveAmount = pairInfo[2] as! UFix64; reservePrice = oraclePrices["USDC"]!}
688 else if pairInfo[1] as! String == "A.b19436aae4d94622.FiatToken" {reserveAmount = pairInfo[3] as! UFix64; reservePrice = oraclePrices["USDC"]!}
689 else if pairInfo[0] as! String == "A.1654653399040a61.FlowToken" {reserveAmount = pairInfo[2] as! UFix64; reservePrice = oraclePrices["Flow"]!}
690 else if pairInfo[1] as! String == "A.1654653399040a61.FlowToken" {reserveAmount = pairInfo[3] as! UFix64; reservePrice = oraclePrices["Flow"]!}
691 else if pairInfo[0] as! String == "A.d6f80565193ad727.stFlowToken" {reserveAmount = pairInfo[2] as! UFix64; reservePrice = oraclePrices["stFlow"]!}
692 else if pairInfo[1] as! String == "A.d6f80565193ad727.stFlowToken" {reserveAmount = pairInfo[3] as! UFix64; reservePrice = oraclePrices["stFlow"]!}
693 if reservePrice > 0.0 && reserveAmount > 1000.0 {
694 lpPrice = reserveAmount * reservePrice * 2.0 / (pairInfo[5] as! UFix64)
695 }
696 return lpPrice
697 }
698
699 // A.0xabc.Toke -> 0xabc
700 access(all) view fun type2address(_ type: String): Address {
701 let address = type.slice(from: 2, upTo: 18)
702 var r: UInt64 = 0
703 var bytes = address.decodeHex()
704 while bytes.length>0{
705 r = r + (UInt64(bytes.removeFirst()) << UInt64(bytes.length*8))
706 }
707 return Address(r)
708 }
709
710 access(all) view fun getClaimEndTimestamp(): UFix64 {
711 return self._claimEndTimestamp
712 }
713
714 /// Admin
715 ///
716 access(all) resource Admin {
717 // Set Points Rate
718 access(all) fun setPointsRate_stFlowHoldingTier(tierRate: {UFix64: UFix64}) {
719 emit PointsTierRateChanged(source: "stFlowHolding", ori: PPPV7._pointsRate["stFlowHolding"]! as! {UFix64: UFix64}, new: tierRate)
720 PPPV7._pointsRate["stFlowHolding"] = tierRate
721 }
722 access(all) fun setPointsRate_LendingSupplyTier(tierRate: {UFix64: UFix64}) {
723 emit PointsTierRateChanged(source: "LendingSupply", ori: PPPV7._pointsRate["LendingSupply"]! as! {UFix64: UFix64}, new: tierRate)
724 PPPV7._pointsRate["LendingSupply"] = tierRate
725 }
726 access(all) fun setPointsRate_LendingBorrowTier(tierRate: {UFix64: UFix64}) {
727 emit PointsTierRateChanged(source: "LendingBorrow", ori: PPPV7._pointsRate["LendingBorrow"]! as! {UFix64: UFix64}, new: tierRate)
728 PPPV7._pointsRate["LendingBorrow"] = tierRate
729 }
730 access(all) fun setPointsRate_SwapLPTier(tierRate: {UFix64: UFix64}) {
731 emit PointsTierRateChanged(source: "SwapLP", ori: PPPV7._pointsRate["SwapLP"]! as! {UFix64: UFix64}, new: tierRate)
732 PPPV7._pointsRate["SwapLP"] = tierRate
733 }
734 access(all) fun setPointsRate_SwapVolumeTier(tierRate: {UFix64: UFix64}) {
735 emit PointsTierRateChanged(source: "SwapVolume", ori: PPPV7._pointsRate["SwapVolume"]! as! {UFix64: UFix64}, new: tierRate)
736 PPPV7._pointsRate["SwapVolume"] = tierRate
737 }
738 access(all) fun setPointsRate_ReferrerUp(rate: UFix64) {
739 emit PointsRateChanged(source: "ReferrerUp", ori: PPPV7._pointsRate["ReferrerUp"]! as! UFix64, new: rate)
740 PPPV7._pointsRate["ReferrerUp"] = rate
741 }
742 access(all) fun setPointsRate_RefereeUp(rate: UFix64) {
743 emit PointsRateChanged(source: "RefereeUp", ori: PPPV7._pointsRate["RefereeUp"]! as! UFix64, new: rate)
744 PPPV7._pointsRate["RefereeUp"] = rate
745 }
746 access(all) fun setPointsRate_CoreMember(rate: UFix64) {
747 emit PointsRateChanged(source: "CoreMember", ori: PPPV7._pointsRate["CoreMember"]! as! UFix64, new: rate)
748 PPPV7._pointsRate["CoreMember"] = rate
749 }
750
751 // Add Swap Pool in Whiltelist
752 access(all) fun addSwapPoolInWhiltelist(poolAddr: Address) {
753 PPPV7._swapPoolWhitelist[poolAddr] = true
754 }
755 // Remove Swap Pool in Whitelist
756 access(all) fun removeSwapPoolInWhiltelist(poolAddr: Address) {
757 PPPV7._swapPoolWhitelist.remove(key: poolAddr)
758 }
759
760 // Set history snapshot points
761 access(all) fun setHistorySnapshotPoints(userAddr: Address, newSnapshotBalance: UFix64) {
762 if PPPV7._pointsHistorySnapshot.containsKey(userAddr) == false {
763 PPPV7._pointsHistorySnapshot[userAddr] = 0.0
764 }
765 if PPPV7._pointsBase.containsKey(userAddr) == false {
766 PPPV7._pointsBase[userAddr] = 0.0
767 }
768 let preSnapshotBalance = PPPV7._pointsHistorySnapshot[userAddr]!
769 emit SetSnapshotPoints(userAddr: userAddr, pre: preSnapshotBalance, new: newSnapshotBalance)
770 if preSnapshotBalance == newSnapshotBalance {
771 return
772 }
773 if preSnapshotBalance < newSnapshotBalance {
774 emit PointsMinted(userAddr: userAddr, amount: newSnapshotBalance - preSnapshotBalance, source: "HistorySnapshot", param: {"PreBalance": preSnapshotBalance.toString(), "NewBalance": newSnapshotBalance.toString()})
775 } else {
776 emit PointsBurned(userAddr: userAddr, amount: preSnapshotBalance - newSnapshotBalance, source: "HistorySnapshot", param: {"PreBalance": preSnapshotBalance.toString(), "NewBalance": newSnapshotBalance.toString()})
777 }
778 PPPV7._totalSupply = PPPV7._totalSupply - preSnapshotBalance + newSnapshotBalance
779 PPPV7._pointsHistorySnapshot[userAddr] = newSnapshotBalance
780 }
781 access(all) fun setClaimEndTimestamp(endTime: UFix64) {
782 PPPV7._claimEndTimestamp = endTime
783 }
784
785 // Ban user
786 access(all) fun addUserBlackList(userAddr: Address) {
787 PPPV7._userBlacklist[userAddr] = true
788 }
789 access(all) fun removeUserBlackList(userAddr: Address) {
790 PPPV7._userBlacklist.remove(key: userAddr)
791 }
792
793 access(all) fun addVolumePoints(userAddr: Address, volumePoints: UFix64, volumeUsd: UFix64) {
794 PPPV7._mint(targetAddr: userAddr, amount: volumePoints)
795
796 if PPPV7._userStates.containsKey(userAddr) == false {
797 PPPV7._userStates[userAddr] = {}
798 }
799 PPPV7.setUserState_SwapVolume(userAddr: userAddr, volume: PPPV7.getUserState_SwapVolume(userAddr: userAddr) + volumeUsd)
800 emit PointsMinted(
801 userAddr: userAddr,
802 amount: volumePoints,
803 source: "SwapVolume",
804 param: {
805 "VolumeUsd": volumeUsd.toString()
806 }
807 )
808 emit StateUpdated(userAddr: userAddr, state: PPPV7._userStates[userAddr]!)
809 }
810
811 access(all) fun reconcileBasePoints(userAddr: Address, newBasePoints: UFix64) {
812 // TODO process referral points
813 if PPPV7._pointsBase.containsKey(userAddr) == false {
814 PPPV7._pointsBase[userAddr] = 0.0
815 }
816 let preBasePoints = PPPV7._pointsBase[userAddr]!
817 if preBasePoints == newBasePoints {
818 return
819 }
820 if preBasePoints < newBasePoints {
821 emit PointsMinted(userAddr: userAddr, amount: newBasePoints - preBasePoints, source: "Reconcile", param: {"PreBaseBalance": preBasePoints.toString(), "NewBaseBalance": newBasePoints.toString()})
822 } else {
823 emit PointsBurned(userAddr: userAddr, amount: preBasePoints - newBasePoints, source: "Reconcile", param: {"PreBaseBalance": preBasePoints.toString(), "NewBaseBalance": newBasePoints.toString()})
824 }
825 PPPV7._totalSupply = PPPV7._totalSupply - preBasePoints + newBasePoints
826 PPPV7._pointsBase[userAddr] = newBasePoints
827 }
828
829 access(all) fun updateTopUsers(addrs: [Address]) {
830 emit TopUsersChanged(ori: PPPV7._topUsers, new: addrs)
831 PPPV7._topUsers = addrs
832 }
833
834 // TODO
835 access(all) fun testResetIfSnapshot(userAddr: Address) {
836 PPPV7._ifClaimHistorySnapshot[userAddr] = false
837 }
838 }
839
840 init() {
841 self._totalSupply = 0.0
842 self._secondsPerPointsRateSession = 3600.0
843 self._pointsBase = {}
844 self._pointsHistorySnapshot = {}
845 self._totalPointsAsReferrer = {}
846 self._pointsFromReferees = {}
847 self._pointsAsReferee = {}
848 self._pointsAsCoreMember = {}
849 self._topUsers = []
850 self._pointsRate = {
851 "stFlowHolding": {
852 0.0: 0.0,
853 1.0: 0.001
854 },
855 "LendingSupply": {
856 0.0: 0.0,
857 1.0: 0.00007,
858 10.0: 0.000072,
859 100.0: 0.000074,
860 1000.0: 0.000076,
861 10000.0: 0.000074,
862 100000.0: 0.000072,
863 1000000.0: 0.00007
864 },
865 "LendingBorrow": {
866 0.0: 0.0,
867 1.0: 0.00035,
868 10.0: 0.00036,
869 100.0: 0.00037,
870 1000.0: 0.00038,
871 10000.0: 0.00037,
872 100000.0: 0.00036,
873 1000000.0: 0.00035
874 },
875 "SwapLP": {
876 0.0: 0.0,
877 1.0: 0.0001,
878 10.0: 0.0002,
879 100.0: 0.001,
880 1000.0: 0.002,
881 10000.0: 0.001,
882 100000.0: 0.0002,
883 1000000.0: 0.0001
884 },
885 "SwapVolume": {
886 0.0: 0.0,
887 1.0: 0.002
888 },
889 "ReferrerUp": 0.05,
890 "RefereeUp": 0.05,
891 "CoreMember": 0.2
892 }
893 self._swapPoolWhitelist = {
894 0xfa82796435e15832: true, // FLOW-USDC
895 0xc353b9d685ec427d: true, // FLOW-stFLOW stable
896 0xa06c38beec9cf0e8: true, // FLOW-DUST
897 0xbfb26bb8adf90399: true // FLOW-SLOPPY
898 }
899
900 self._userStates = {}
901 self._userBlacklist = {}
902 self._ifClaimHistorySnapshot = {}
903 self._claimEndTimestamp = getCurrentBlock().timestamp + 86400.0 * 60.0
904
905 self._reservedFields = {}
906
907 destroy <-self.account.load<@AnyResource>(from: /storage/pointsAdmin)
908 self.account.save(<-create Admin(), to: /storage/pointsAdmin)
909 }
910}
911