Smart Contract

IncrementPoints

A.2093c0861ff1bd80.IncrementPoints

Deployed

1w ago
Feb 19, 2026, 08:48:48 AM UTC

Dependents

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