Smart Contract

MoxyClub

A.123cb47fe122f6e3.MoxyClub

Deployed

3h ago
Feb 26, 2026, 09:44:38 PM UTC

Dependents

0 imports
1import PlayToken from 0x123cb47fe122f6e3
2import ScoreToken from 0x123cb47fe122f6e3
3import MoxyToken from 0x123cb47fe122f6e3
4import MoxyVaultToken from 0x123cb47fe122f6e3
5import LockedMoxyToken from 0x123cb47fe122f6e3
6import LockedMoxyVaultToken from 0x123cb47fe122f6e3
7import MoxyReleaseRounds from 0x123cb47fe122f6e3
8import FungibleToken from 0xf233dcee88fe0abe
9import MoxyProcessQueue from 0x123cb47fe122f6e3
10import MoxyData from 0x123cb47fe122f6e3
11 
12
13pub contract MoxyClub {
14
15    // Initial fee amount paid to association
16    pub var membershipFee: UFix64
17
18    pub event MoxyAccountAdded(address: Address)
19    pub event RoundAdded(name: String)
20
21    // Fee Charged event on MOX transfer
22    pub event FeeCharged(address: Address, amount: UFix64)
23
24    // Treasury repurchase event triggered when Treasury wallet has received MOX tokens
25    // This will enforce PLAY token with the 10% of MOX received on a 2:1 MOX to PLAY
26    // conversion 
27    pub event TreasuryRepurchase(amount: UFix64)
28
29    pub event MOXRewaredDueMVHoldingsTo(address: Address, timestamp: UFix64, amount: UFix64)
30    pub event MOXRewaredDueDailyActivityTo(address: Address, timestamp: UFix64, amount: UFix64)
31    pub event MOXToMVDailyConversionTo(address: Address, timestamp: UFix64, amount: UFix64)
32    pub event MOXRewardedToPoPUserNotGoodStanding(address: Address, timestamp: UFix64, amountMOXY: UFix64, amountPLAY: UFix64)
33
34    pub event MembershipFeeDeducted(address: Address, feeDeducted: UFix64, feeDeductedMOXY: UFix64, remaining: UFix64, moxyToUSDValue: UFix64)
35    
36    pub event MembershipFeeChanged(newAmount: UFix64, newAmountMOXY: UFix64, oldAmount: UFix64, oldAmountMOXY: UFix64, moxyToUSDValue: UFix64)
37
38    //Events to process Start of Round Releases
39    pub event StartingRoundReleaseInitializeProcess(timestamp: UFix64, roundsToProcess: Int, accountsToProcess: Int)
40    pub event FinishedRoundReleaseInitializeProcess(timestamp: UFix64, roundsProcessed: Int, accountsProcessed: Int)
41
42    //Events to process Round Release allocations
43    pub event StartingDailyRoundReleaseAllocationProcess(timestamp: UFix64, accountsToProcess: Int)
44    pub event FinishedDailyRoundReleaseAllocationProcess(timestamp: UFix64, accountsProcessed: Int)
45    pub event NoAddressesToProcessForRoundReleaseAllocation(timestamp: UFix64)
46    pub event BatchDailyRoundReleaseAllocationProcess(timestamp: UFix64, requestedToProcess: Int, accountsProcessed: Int, totalAccounts: Int)
47
48    //Events when paying MV Holdings
49    pub event PaidAlreadyMadeWhenPayingMVHoldingsRewards(address: Address, timestamp: UFix64)
50    pub event AddressNotFoundWhenPayingMVHoldingsRewards(address: Address, timestamp: UFix64)
51    pub event RequestedDateSmallerToLastUpdateWhenPayingMVHoldingsRewards(address: Address, timestamp: UFix64, lastMVHoldingsUpdatedTimestamp: UFix64)
52    pub event StartingRewardsPaymentsDueMVHoldings(timestamp: UFix64, accountsToProcess: Int)
53    pub event NoAddressesToProcessForMVHoldingsProcess(timestamp: UFix64)
54    pub event FinishedRewardsPaymentsDueMVHoldings(timestamp: UFix64, accountsProcessed: Int)
55    pub event BatchRewardsPaymentsDueMVHoldings(timestamp: UFix64, requestedToProcess: Int, accountsProcessed: Int, totalAccounts: Int)
56
57    // Event for Proof of Play (PoP) rewards
58    pub event AddressNotFoundWhenPayingPoPRewards(address: Address, timestamp: UFix64)
59    pub event StartingRewardsPaymentsDuePoP(timestamp: UFix64, accountsToProcess: Int)
60    pub event FinishedRewardsPaymentsDuePoP(timestamp: UFix64, accountsProcessed: Int)
61    pub event RequestedDateSmallerToLastUpdateWhenPayingPoPRewards(address: Address, timestamp: UFix64, lastDailyActivityUpdatedTimestamp: UFix64)
62    pub event NoAddressesToProcessForPoPProcess(timestamp: UFix64)
63    pub event BatchRewardsPaymentsDuePoP(timestamp: UFix64, requestedToProcess: Int, accountsProcessed: Int, totalAccounts: Int)
64
65    //Events when Vaults not found on users storage
66    pub event AccountDoesNotHaveScoreVault(address: Address, message: String)
67    pub event AccountDoesNotHaveMoxyVaultVault(address: Address, message: String)
68
69    // Ecosystem parameters modifications events
70    pub event MOXYToFLOWValueChanged(oldAmount: UFix64, newAmount: UFix64, timestamp: UFix64)
71    pub event MOXYToUSDValueChanged(oldAmount: UFix64, newAmount: UFix64, timestamp: UFix64)
72    pub event TreasuryAddressChanged(newAddress: Address)
73    pub event AssociationAddressChanged(newAddress: Address)
74    
75    // Play and Earn private reference assigned
76    pub event PlayAndEarnReferenceAssigned(address: Address)
77    pub event PlayAndEarnEventAccountAdded(address: Address)
78    
79    
80    // Moxy Controlled Accounts events
81    pub event MoxyControlledAccountAdded(address: Address)
82
83    // Events for process of paying MOXY due MV conversion
84    pub event StartingPayingMOXYDueMVConversion(timestamp: UFix64, accountsToProcess: Int)
85    pub event FinishedPayingMOXYDueMVConversion(timestamp: UFix64, accountsProcessed: Int)
86    pub event MVToMOXYConversionPerformed(address: Address, amount: UFix64, timestamp: UFix64)
87    pub event MVToMOXYConversionAlreadyPerformed(address: Address, timestamp: UFix64, lastUpdated: UFix64)
88    pub event MVToMOXYConversionAlreadyFinished(address: Address, timestamp: UFix64, lastUpdated: UFix64)
89    pub event NoAddressesToProcessMVConversionProcess(timestamp: UFix64)
90    pub event BatchPayingMOXYDueMVConversion(timestamp: UFix64, requestedToProcess: Int, accountsProcessed: Int, totalAccounts: Int)
91
92    // Event when Proof of Play weights are modified
93    pub event PopWeightsChanged(newScoreWeight: UFix64, newDailyScoreWeight: UFix64, newPlayDonationWeight: UFix64)
94
95    // Launch fee events
96    pub event LaunchFeeChanged(newAmount: UFix64)
97    pub event LaunchFeePaid(address: Address, amount: UFix64)
98
99    // Masterclass events
100    pub event MasterclassPricePaid(address: Address, amount: UFix64)
101    pub event MasterclassPriceAdded(classId: String, feeAmount: UFix64)
102    pub event MasterclassPriceUpdated(classId: String, oldFeeAmount: UFix64, newFeeAmount: UFix64)
103    pub event MasterclassPriceRemoved(classId: String)
104
105    // Games Prices
106    pub event GamePricePaid(gameId: String, address: Address, amount: UFix64)
107    pub event GamePriceAdded(gameId: String, feeAmount: UFix64)
108    pub event GamePriceUpdated(gameId: String, oldFeeAmount: UFix64, newFeeAmount: UFix64)
109    pub event GamePriceRemoved(gameId: String)
110
111    // Refund payments from treasury
112    pub event RefundedPayment(totalRefunded: UFix64, toAddress: Address, playBurned: UFix64) 
113
114
115   pub resource MoxyAccount {
116        
117        // Variables for MV Holdings
118        access(contract) var earnedFromMVHoldings: {UFix64:UFix64}
119        access(contract) var totalEarnedFromMVHoldings: UFix64
120        access(contract) var lastMVHoldingsUpdatedTimestamp: UFix64
121
122        // Variables for Daily Activity
123        access(contract) var paidDueActivity: {UFix64:UFix64}
124        access(contract) var totalPaidDueDailyActivity: UFix64
125        pub var dailyActivityUpdatedTimestamp: UFix64
126        
127        // Variables for MV Conversion
128        access(contract) var totalPaidDueMVConversion: UFix64
129
130
131        access(contract) var mvToMOXYConverters: Capability<&{UFix64:MVToMOXYConverter}>?
132        pub var membershipFee: UFix64
133        pub var membershipFeePaid: UFix64
134
135        pub var playAndEarnRef: Capability<&FungibleToken.Vault>?
136
137        pub var goodStandingOnOpenEvents: MoxyData.DictionaryMapped
138
139        pub fun setDailyActivityUpdatedTimestamp(timestamp: UFix64) {
140            self.dailyActivityUpdatedTimestamp = timestamp
141        }
142
143        pub fun setLastMVHoldingsUpdatedTimestamp(timestamp: UFix64) {
144            self.lastMVHoldingsUpdatedTimestamp = timestamp
145        }
146
147        pub fun setMOXEarnedFromMVHoldingsFor(timestamp: UFix64, amount: UFix64) {
148            if (amount > 0.0) {
149                self.earnedFromMVHoldings[timestamp] = amount
150                self.totalEarnedFromMVHoldings = self.totalEarnedFromMVHoldings + amount
151            }
152            self.lastMVHoldingsUpdatedTimestamp = timestamp
153        }
154
155        pub fun updateTotalPaidDueDailyActivity(amount: UFix64) {
156            let timestamp = MoxyData.getTimestampTo0000(timestamp: getCurrentBlock().timestamp)
157            if (self.paidDueActivity[timestamp] == nil) {
158                self.paidDueActivity[timestamp] = 0.0
159            } 
160            self.paidDueActivity[timestamp] = self.paidDueActivity[timestamp]! + amount
161            self.totalPaidDueDailyActivity = self.totalPaidDueDailyActivity + amount
162        }
163
164        pub fun updateTotalPaidDueMVConversion(amount: UFix64) {
165            self.totalPaidDueMVConversion = self.totalPaidDueMVConversion + amount
166        }
167
168        pub fun setMVToMOXYConverters(capabilityRef: Capability<&{UFix64:MVToMOXYConverter}>){
169            self.mvToMOXYConverters = capabilityRef
170        }
171
172        access(contract) fun hasVaildMVToMOXYConverters(address: Address): Bool {
173            var hasValid = true
174            let converters = self.mvToMOXYConverters!.borrow()!
175            for time in converters.keys {
176                let addr = converters[time]?.getOwnerAddress()!
177                hasValid = hasValid && (address == converters[time]?.getOwnerAddress()!)
178            }
179
180            return hasValid
181        }
182
183        pub fun getMVToMOXYTotalInConversion(): UFix64 {
184            var total = 0.0
185            let converters = self.mvToMOXYConverters!.borrow()!
186            for time in converters.keys {
187                let amount = converters[time]?.getTotalToConvertRemaining()!
188                total = total + amount
189            }
190            return total
191        }
192
193        pub fun payMOXDueMVConversionUpto(timestamp: UFix64): UFix64 {
194            var conversionsFinished = true
195            var total = 0.0
196            let converters = self.mvToMOXYConverters!.borrow()!
197            for time in converters.keys {
198                let amount = converters[time]?.payUpto(timestamp: timestamp)!
199                total = total + amount
200            }
201            return total
202        }
203
204        pub fun haveFinishedConversions(): Bool {
205            var conversionsFinished = true
206            let converters = self.mvToMOXYConverters!.borrow()!
207            for time in converters.keys {
208                conversionsFinished = conversionsFinished && converters[time]?.hasFinished()!
209            }
210            return conversionsFinished
211        }
212
213        pub fun getMVToMOXtRequests(): {UFix64: MVToMOXRequestInfo} {
214            let array: {UFix64:MVToMOXRequestInfo} = {} 
215            let converters = self.mvToMOXYConverters!.borrow()!
216            for time in converters.keys {
217                let request = MVToMOXRequestInfo(amount: converters[time]?.conversionAmount!, 
218                    amountReleased: converters[time]?.convertedAmount!, creationTimestamp: converters[time]?.creationTimestamp!, 
219                    lastReleaseTime0000: converters[time]?.lastReleaseTime0000!, finishTimestamp: converters[time]?.getFinishTimestamp()!)
220                array[converters[time]?.creationTimestamp!] = request 
221            }
222            return array
223        }
224
225        pub fun getMembershipFeeRemaining(): UFix64 {
226            return self.membershipFee - self.membershipFeePaid
227        }
228
229        pub fun hasMembershipFeePending(): Bool {
230            return (self.membershipFee - self.membershipFeePaid) > 0.0
231        }
232
233        pub fun updateMembershipFeePaid(amount: UFix64) {
234            self.membershipFeePaid = self.membershipFeePaid + amount
235        }
236
237        pub fun getEarnedFromMVHoldings(): {UFix64: UFix64} {
238            return self.earnedFromMVHoldings
239        }
240
241        pub fun getEarnedFromMVHoldingsFor(timestamp: UFix64): UFix64 {
242            let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
243            if (self.earnedFromMVHoldings[time0000] == nil) {
244                return 0.0
245            }
246            return self.earnedFromMVHoldings[time0000]!
247        }
248
249        pub fun getEarnedFromMVHoldingsForRange(from: UFix64, to: UFix64): {UFix64: UFix64} {
250            let from0000 = MoxyData.getTimestampTo0000(timestamp: from)
251            let to0000 = MoxyData.getTimestampTo0000(timestamp: to)
252
253            let dict: {UFix64:UFix64} = {}
254
255            let day = 86400.0
256            var curr0000 = from0000
257            var i = 0
258            while (curr0000 <= to0000 && i < 30) {
259                var val = self.getEarnedFromMVHoldingsFor(timestamp: curr0000)
260                dict[curr0000] = val
261                curr0000 = curr0000 + day
262                i = i + 1
263            }
264
265            return dict
266        }
267
268        pub fun getPaidDueActivity(): {UFix64: UFix64} {
269            return self.paidDueActivity
270        }
271
272        pub fun getPaidDueDailyActivityFor(timestamp: UFix64): UFix64 {
273            let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
274            if (self.paidDueActivity[time0000] == nil) {
275                return 0.0
276            }
277            return self.paidDueActivity[time0000]!
278        }
279
280        pub fun getPaidDueDailyActivityForRange(from: UFix64, to: UFix64): {UFix64: UFix64} {
281            let from0000 = MoxyData.getTimestampTo0000(timestamp: from)
282            let to0000 = MoxyData.getTimestampTo0000(timestamp: to)
283
284            let dict: {UFix64:UFix64} = {}
285
286            let day = 86400.0
287            var curr0000 = from0000
288            var i = 0
289            while (curr0000 <= to0000 && i < 30) {
290                var val = self.getPaidDueDailyActivityFor(timestamp: curr0000)
291                dict[curr0000] = val
292                curr0000 = curr0000 + day
293                i = i + 1
294            }
295
296            return dict
297        }
298
299        pub fun getTotalEarnedFromMVHoldings(): UFix64 {
300            return self.totalEarnedFromMVHoldings
301                        
302        }
303
304        pub fun getTotalPaidDueDailyActivity(): UFix64 {
305            return self.totalPaidDueDailyActivity
306                        
307        }
308
309        pub fun getTotalPaidDueMVConversion(): UFix64 {
310            return self.totalPaidDueMVConversion
311                        
312        }
313
314        pub fun setPlayAndEarnRef(vaultRef: Capability<&FungibleToken.Vault>) {
315            self.playAndEarnRef = vaultRef
316        }
317
318        pub fun setMVToMOXYConversionsRef(conversionsRef: Capability<&{UFix64:MoxyClub.MVToMOXYConverter}>) {
319            self.mvToMOXYConverters = conversionsRef
320        }
321
322        pub fun setIsGoodStandingOnOpenEvents(value: Bool) {
323            self.goodStandingOnOpenEvents.setValue(value)
324        }
325
326        pub fun isGoodStandingOnOpenEvents():Bool {
327            let value = self.goodStandingOnOpenEvents.valueNow()
328            if (value == nil) {
329                //Not value set yet, default behavior is to be true
330                return true
331            }
332            return value! as! Bool
333        }
334
335        pub fun isGoodStandingOnOpenEventsFor(timestamp: UFix64):Bool {
336            let value = self.goodStandingOnOpenEvents.valueFor(timestamp: timestamp)
337            if (value == nil) {
338                //Not value set yet, default behavior is to be true
339                return true
340            }
341            return value! as! Bool
342        }
343
344        init(){
345            self.dailyActivityUpdatedTimestamp = 0.0
346            self.earnedFromMVHoldings = {}
347            self.totalEarnedFromMVHoldings = 0.0
348            self.paidDueActivity = {}
349            self.totalPaidDueDailyActivity = 0.0
350            self.totalPaidDueMVConversion = 0.0
351            self.lastMVHoldingsUpdatedTimestamp = 0.0
352            self.mvToMOXYConverters = nil
353            self.membershipFee = MoxyClub.membershipFee //Fee is in USD
354            self.membershipFeePaid = 0.0
355            self.playAndEarnRef = nil
356            self.goodStandingOnOpenEvents = MoxyData.DictionaryMapped()
357        }
358    }
359
360    pub resource PlayAndEarnAccount {
361        pub var creationDate: UFix64
362        
363        access(contract) fun setCreationDate(timestamp: UFix64) {
364            self.creationDate = timestamp
365        }
366
367        init() {
368            self.creationDate = getCurrentBlock().timestamp
369        }
370    }     
371
372    pub struct MVToMOXRequestInfo {
373        pub var amount: UFix64
374        pub var amountReleased: UFix64
375        pub var creationTimestamp: UFix64
376        pub var lastReleaseTime0000: UFix64
377        pub var finishTimestamp: UFix64
378        pub var remainingDays: Int
379        pub var remainingAmount: UFix64
380        
381        init(amount: UFix64, amountReleased: UFix64, creationTimestamp: UFix64, lastReleaseTime0000: UFix64, finishTimestamp: UFix64) {
382            self.amount = amount
383            self.amountReleased = amountReleased
384            self.creationTimestamp = creationTimestamp
385            self.lastReleaseTime0000 = lastReleaseTime0000
386            self.finishTimestamp = finishTimestamp
387            self.remainingDays = Int((self.finishTimestamp - self.lastReleaseTime0000) / 86400.0)
388            self.remainingAmount = self.amount - self.amountReleased
389        }
390     }
391
392     pub resource MVToMOXYConverter {
393        pub var creationTimestamp: UFix64
394        pub var conversionAmount: UFix64
395        pub var convertedAmount: UFix64
396        pub var mvConverter: @MoxyVaultToken.MVConverter
397        pub var lockedMOXYVault: @FungibleToken.Vault
398        pub var lastReleaseTime0000: UFix64
399        pub var withdrawalDays: Int
400
401        pub fun payUpto(timestamp: UFix64): UFix64 {
402            let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
403            if (time0000 <= self.lastReleaseTime0000) {
404                log("WARNING: Cannot pay MOXY in MV to MOXY convertion because it has already paid up to the requested date")
405                emit MVToMOXYConversionAlreadyPerformed(address: self.owner!.address, timestamp: time0000, lastUpdated: self.lastReleaseTime0000)
406                return 0.0
407            }
408
409            if (self.hasFinished()) {
410                log("WARNING: Conversion process already finished")
411                emit MVToMOXYConversionAlreadyFinished(address: self.owner!.address, timestamp: time0000, lastUpdated: self.lastReleaseTime0000)
412                return 0.0
413            }
414
415            let days = UFix64(UInt64((time0000 - self.lastReleaseTime0000) / 86400.0))
416            var amount: UFix64 = 0.0 
417
418            // If time is grather than the finish time, the amount to withdraw is
419            // all allowed
420            if (time0000 >= self.getFinishTimestamp()) {
421                // Amount to withdraw is all allowed
422                amount = self.mvConverter.allowedAmount
423            } else {
424                // Amount to withdraw is based on daily pay
425                amount = (self.conversionAmount / UFix64(self.withdrawalDays)) * days
426            }
427            
428            // Burn MV
429            let admin = MoxyClub.account.borrow<&MoxyVaultToken.Administrator>(from: MoxyVaultToken.moxyVaultTokenAdminStorage)
430                ?? panic("Could not borrow a reference to the admin resource")
431            let burner <- admin.createNewBurner()
432            let vault2 <- self.mvConverter.getDailyVault(amount: amount)
433            burner.burnTokens(from: <- vault2)
434            destroy burner
435
436            // Convert Locked MOXY to MOXY
437            // Get the recipient's public account object
438            let recipient = self.lockedMOXYVault.owner!
439
440            // Get a reference to the recipient's Receiver
441            let receiverRef = recipient.getCapability(MoxyToken.moxyTokenReceiverPath)
442                .borrow<&{FungibleToken.Receiver}>()
443                ?? panic("Could not borrow receiver reference to the recipient's Vault")
444
445            let vault3 <- self.lockedMOXYVault.withdraw(amount: amount)
446            // Deposit the withdrawn tokens in the recipient's receiver
447            receiverRef.deposit(from: <- vault3)
448
449            //update converted amount and timestamp
450            self.convertedAmount = self.convertedAmount + amount
451            self.lastReleaseTime0000 = time0000
452
453            emit MVToMOXYConversionPerformed(address: recipient.address, amount: amount, timestamp: time0000)
454
455            return amount
456        }
457
458        pub fun hasFinished(): Bool {
459            return self.convertedAmount >= self.conversionAmount
460        }
461
462        pub fun getFinishTimestamp(): UFix64 {
463            return MoxyData.getTimestampTo0000(timestamp: self.creationTimestamp) + (UFix64(self.withdrawalDays) * 86400.0)
464        }
465
466        pub fun getTotalToConvertRemaining(): UFix64 {
467            return self.conversionAmount - self.convertedAmount
468        }
469
470        pub fun getOwnerAddress(): Address {
471            return self.mvConverter.address
472        }
473
474        init(mvConverter: @MoxyVaultToken.MVConverter, conversionRequest: @LockedMoxyToken.ConversionRequest , timestamp: UFix64, withdrawalDays: Int) {
475            self.creationTimestamp = timestamp
476            self.conversionAmount = mvConverter.allowedAmount
477            self.convertedAmount = 0.0
478            self.mvConverter <- mvConverter
479            self.lockedMOXYVault <- conversionRequest.withdraw()
480            self.lastReleaseTime0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
481            self.withdrawalDays = withdrawalDays
482
483            destroy conversionRequest
484        }
485
486        destroy() {
487            destroy self.mvConverter
488            destroy self.lockedMOXYVault
489        }
490    }
491
492
493    pub resource MoxyEcosystem: MoxyEcosystemInfoInterface, MVToMOXYRequestsInfoInterface, MoxyEcosystemOperations {
494        access(contract) let accounts: @{Address:MoxyAccount}
495        access(contract) let playAndEarnEventAccounts: @{Address:PlayAndEarnAccount}
496        access(contract) let moxyControlledAccounts: {Address:Address}
497        pub var isReleaseStarted: Bool
498
499        /// Fee amount to charge on MOX transactions
500        pub var feeAmountInFLOW: UFix64
501        pub var moxyToFLOWValue: UFix64
502        pub var moxyToUSDValue: UFix64
503        pub var percentFeeToPLAY: UFix64
504
505        // Moxy Controlled Addresses
506        pub var treasuryAddress: Address?
507        pub var associationAddress: Address?
508
509        // Total earned from MV holdings
510        pub var totalEarnedFromMVHoldings: UFix64
511        pub var earnedFromMVHoldings: {UFix64:UFix64}
512        
513        // Total paid due daily activity or also called Proof of Play
514        pub var totalPaidDueDailyActivity: UFix64
515        pub var paidDueDailyActivity: {UFix64:UFix64}
516
517        // Total paid due MV conversion back to MOXY
518        pub var totalPaidDueMVConversion: UFix64
519        pub var paidDueMVConversion: {UFix64:UFix64}
520
521
522        // Maximum percentage for MV Holdings rewards for Locked MV
523        pub var maximumPercentageLockedMV: UFix64
524
525        pub var mvToMOXWithdrawalDays: Int
526
527        // Process Queue handling
528        pub var roundReleaseQueue: @MoxyProcessQueue.Queue
529        pub var mvHoldingsQueue: @MoxyProcessQueue.Queue
530        pub var proofOfPlayQueue: @MoxyProcessQueue.Queue
531        pub var mvToMOXConversionQueue: @MoxyProcessQueue.Queue
532
533        // Proof of Play weight for score, daily score and play donations
534        pub var popScoreWeight: UFix64
535        pub var popDailyScoreWeight: UFix64
536        pub var popPlayDonationWeight: UFix64
537
538        pub var proofOfPlayPercentage: UFix64
539
540        // Launch fee to be paid for users
541        // It's value is in MOXY
542        pub var launchFee: UFix64
543
544        // Masterclass price
545        pub var masterclassPrices: {String:UFix64}
546
547        // Games prices
548        pub var gamesPrices: {String:UFix64}
549
550
551        pub fun getMOXYFeeAmount(): UFix64 {
552            // Fee amount is the double of flow fee with a minimum of 0.000001 MOX
553            var feeInMOXY = self.feeAmountInFLOW * 2.0 * self.moxyToFLOWValue
554            if (feeInMOXY < 0.000001) {
555                feeInMOXY = 0.000001
556            }
557            return feeInMOXY
558        }
559
560        pub fun getMOXYToFLOWValue(): UFix64 {
561            return self.moxyToFLOWValue
562        }
563
564        pub fun getMOXYToUSDValue(): UFix64 {
565            return self.moxyToUSDValue
566        }
567
568        pub fun setMOXYToFLOWValue(amount: UFix64) {
569            let oldValue = self.moxyToFLOWValue
570            self.moxyToFLOWValue = amount
571            emit MOXYToFLOWValueChanged(oldAmount: oldValue, newAmount: amount, timestamp: getCurrentBlock().timestamp)
572        }
573
574        pub fun setMOXYToUSDValue(amount: UFix64) {
575            let oldValue = self.moxyToUSDValue
576            self.moxyToUSDValue = amount
577            emit MOXYToUSDValueChanged(oldAmount: oldValue, newAmount: amount, timestamp: getCurrentBlock().timestamp)
578        }
579
580        
581
582        pub fun setMembershipFeeUSD(amount: UFix64) {
583            let oldAmount = MoxyClub.membershipFee
584            let oldMoxy = oldAmount / self.moxyToUSDValue
585            MoxyClub.membershipFee = amount
586            let newMoxy = amount / self.moxyToUSDValue
587            emit MembershipFeeChanged(newAmount: amount, newAmountMOXY: newMoxy, oldAmount: oldAmount, oldAmountMOXY: oldMoxy, moxyToUSDValue: self.moxyToUSDValue)
588        }
589
590        pub fun setMembershipFeeMOXY(amount: UFix64) {
591            let oldAmount = MoxyClub.membershipFee
592            let oldMoxy = oldAmount / self.moxyToUSDValue
593            let newMoxy = amount
594            MoxyClub.membershipFee = newMoxy * self.moxyToUSDValue
595            emit MembershipFeeChanged(newAmount: amount, newAmountMOXY: newMoxy, oldAmount: oldAmount, oldAmountMOXY: oldMoxy, moxyToUSDValue: self.moxyToUSDValue)
596        }
597
598
599        pub fun setTreasuryAddress(address: Address) {
600            self.treasuryAddress = address
601            emit TreasuryAddressChanged(newAddress: address)
602        }
603
604        pub fun setAssociationAddress(address: Address) {
605            self.associationAddress = address
606            emit AssociationAddressChanged(newAddress: address)
607        }
608
609        pub fun setPlayAndEarnRefTo(address: Address, vaultRef: Capability<&FungibleToken.Vault>) {
610            self.accounts[address]?.setPlayAndEarnRef(vaultRef: vaultRef)
611            emit PlayAndEarnReferenceAssigned(address: address)
612        }
613
614        pub fun setMVToMOXYConversionsRefTo(address: Address, conversionsRef: Capability<&{UFix64:MoxyClub.MVToMOXYConverter}>) {
615            self.accounts[address]?.setMVToMOXYConversionsRef(conversionsRef: conversionsRef)
616            emit PlayAndEarnReferenceAssigned(address: address)
617        }
618
619
620        pub fun setPopWeights(scoreWeight: UFix64, dailyScoreWeight: UFix64, playDonationWeight: UFix64) {
621            pre {
622                scoreWeight + dailyScoreWeight + playDonationWeight == 100.0 : "The sum of three weights should be 100.0"
623            }
624
625            self.popScoreWeight = scoreWeight
626            self.popDailyScoreWeight = dailyScoreWeight
627            self.popPlayDonationWeight = playDonationWeight
628            emit PopWeightsChanged(newScoreWeight: scoreWeight, newDailyScoreWeight: scoreWeight, newPlayDonationWeight: scoreWeight)
629        }
630
631        pub fun setProofOfPlayPercentage(value: UFix64) {
632            pre {
633                value >= 0.0 && value <= 100.0 : "The Proof of Play percentage should be a value between 0.0 and 100.0"
634            }
635            self.proofOfPlayPercentage = value
636        }
637
638        pub fun setLaunchFee(amount: UFix64) {
639            self.launchFee = amount
640            emit LaunchFeeChanged(newAmount: amount)
641        }
642
643        pub fun getLaunchFee(): UFix64 {
644            return self.launchFee
645        }
646
647        pub fun payLaunchFee(fromVault: @FungibleToken.Vault, address: Address) {
648            pre {
649                fromVault.balance == self.launchFee : "Amount to paid does not match with launch fee amount."
650            }
651            let amount = fromVault.balance
652            self.transferMOXY(fromVault: <-fromVault, to: self.treasuryAddress!) 
653            emit LaunchFeePaid(address: address, amount: amount)
654
655        }
656
657        pub fun payMasterclassPrice(classId: String, fromVault: @FungibleToken.Vault, address: Address) {
658            pre {
659                fromVault.balance == self.masterclassPrices[classId] : "Amount to paid does not match with masterclass fee amount."
660            }
661            let amount = fromVault.balance
662            self.transferMOXY(fromVault: <-fromVault, to: self.treasuryAddress!) 
663            emit MasterclassPricePaid(address: address, amount: amount)
664        }
665
666        pub fun addMasterclassPrice(classId: String, feeAmount: UFix64) {
667            if (self.masterclassPrices[classId] != nil) {
668                panic("Masterclass with classId provided already exists.")
669            }
670            self.masterclassPrices[classId] = feeAmount
671            emit MasterclassPriceAdded(classId: classId, feeAmount: feeAmount)
672        }
673
674        pub fun updateMasterclassPrice(classId: String, feeAmount: UFix64) {
675            if (self.masterclassPrices[classId] == nil) {
676                panic("Masterclass with classId provided does not exists.")
677            }
678            let oldFeeAmount = self.masterclassPrices[classId]!
679            self.masterclassPrices[classId] = feeAmount
680            emit MasterclassPriceUpdated(classId: classId, oldFeeAmount: oldFeeAmount, newFeeAmount: feeAmount)
681        }
682
683        pub fun removeMasterclassPrice(classId: String) {
684            self.masterclassPrices.remove(key: classId)
685            emit MasterclassPriceRemoved(classId: classId)
686        }
687
688        pub fun getMasterclassPrice(classId: String): UFix64? {
689            return self.masterclassPrices[classId]
690        }
691        
692        pub fun getMasterclassPrices(): {String: UFix64} {
693            return self.masterclassPrices
694        }
695
696        pub fun payGamePrice(gameId: String, fromVault: @FungibleToken.Vault, address: Address) {
697            pre {
698                fromVault.balance == self.gamesPrices[gameId] : "Amount to paid does not match with game fee amount."
699            }
700            let amount = fromVault.balance
701            self.transferMOXY(fromVault: <-fromVault, to: self.treasuryAddress!) 
702            emit GamePricePaid(gameId: gameId, address: address, amount: amount)
703        }
704
705        access(contract) fun burnPLAYToken(vault: @FungibleToken.Vault) {
706            let admin = MoxyClub.account.borrow<&PlayToken.Administrator>(from: PlayToken.playTokenAdminStorage)
707                ?? panic("Could not borrow a reference to the admin resource")
708
709            let burner <- admin.createNewBurner()
710            burner.burnTokens(from: <- vault)
711            destroy burner
712        }
713
714        pub fun refundPaymentDoneToTreasury(address: Address, moxyVault: @FungibleToken.Vault, playVault: @FungibleToken.Vault) {
715            // Refunds the amount to the given address
716            // from treasury address.
717            
718            let playAmount = playVault.balance
719            let moxyToMint = playAmount * 2.0
720
721            // Burn PLAY
722            self.burnPLAYToken(vault: <-playVault)
723
724            // Mint MOXY token 2::1 with PLAY
725            let mintedVault <- self.mintMOXYTokens(amount: moxyToMint)
726            let receiverVault <- mintedVault.withdraw(amount: mintedVault.balance)
727
728            // Get a reference to the recipient's Receiver
729            let recipient = getAccount(address)
730
731            let receiverRef = recipient.getCapability(MoxyToken.moxyTokenReceiverPath)
732                .borrow<&{FungibleToken.Receiver}>()
733                ?? panic("Could not borrow receiver reference to the recipient's Vault")
734
735            // Deposit the withdrawn tokens in the recipient's receiver
736            receiverVault.deposit(from: <-moxyVault)
737
738            let totalRefunded = receiverVault.balance
739
740            receiverRef.deposit(from: <- receiverVault)
741
742            destroy mintedVault
743
744            emit RefundedPayment(totalRefunded: totalRefunded, toAddress: address, playBurned: playAmount) 
745
746
747        }
748
749        pub fun addGamePrice(gameId: String, feeAmount: UFix64) {
750            if (self.gamesPrices[gameId] != nil) {
751                panic("Game with gameId provided already exists.")
752            }
753            self.gamesPrices[gameId] = feeAmount
754            emit GamePriceAdded(gameId: gameId, feeAmount: feeAmount)
755        }
756
757        pub fun updateGamePrice(gameId: String, feeAmount: UFix64) {
758            if (self.gamesPrices[gameId] == nil) {
759                panic("Game with gameId provided does not exists.")
760            }
761            let oldFeeAmount = self.gamesPrices[gameId]!
762            self.gamesPrices[gameId] = feeAmount
763            emit GamePriceUpdated(gameId: gameId, oldFeeAmount: oldFeeAmount, newFeeAmount: feeAmount)
764        }
765
766        pub fun removeGamePrice(gameId: String) {
767            self.gamesPrices.remove(key: gameId)
768            emit GamePriceRemoved(gameId: gameId)
769        }
770
771        pub fun getGamePrice(gameId: String): UFix64? {
772            return self.gamesPrices[gameId]
773        }
774        
775        pub fun getGamesPrices(): {String: UFix64} {
776            return self.gamesPrices
777        }
778
779        pub fun getProofOfPlayPercentage(): UFix64 {
780            return self.proofOfPlayPercentage
781        }
782
783        pub fun getTreasuryAddress(): Address? {
784            return self.treasuryAddress
785        }
786
787        pub fun getAssociationAddress(): Address? {
788            return self.associationAddress
789        }
790        
791        pub fun hasMembershipFeePendingFor(address: Address): Bool {
792            if (self.accounts[address] == nil) {
793                panic("Address not found in MoxyClub")
794            }
795            return self.accounts[address]?.hasMembershipFeePending()!
796        }
797
798        //Returns the Membership Fee remaining for an address
799        pub fun getMembershipFeeRemainingFor(address: Address): UFix64 {
800            if (self.playAndEarnEventAccounts[address] != nil || self.moxyControlledAccounts[address] != nil) {
801                return 0.0
802            }
803            if (self.accounts[address] == nil) {
804                panic("Address not found in MoxyClub")
805            }
806            return self.accounts[address]?.getMembershipFeeRemaining()!
807        }
808
809        pub fun getMembershipFeeMOXYRemainingFor(address: Address): UFix64 {
810            return self.getMembershipFeeRemainingFor(address: address) / self.moxyToUSDValue
811        }
812
813        pub fun getTotalEarnedFromMVHoldings(): UFix64 {
814            return self.totalEarnedFromMVHoldings
815        }
816
817        pub fun getEarnedFromMVHoldingsForTime(timestamp: UFix64): UFix64 {
818            let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
819            if (self.earnedFromMVHoldings[timestamp] == nil) {
820                return 0.0
821            }
822            return self.earnedFromMVHoldings[time0000]!
823        }
824
825        pub fun getEarnedFromMVHoldingsForTimeRange(from: UFix64, to: UFix64): {UFix64: UFix64} {
826            let from0000 = MoxyData.getTimestampTo0000(timestamp: from)
827            let to0000 = MoxyData.getTimestampTo0000(timestamp: to)
828
829            let dict: {UFix64:UFix64} = {}
830
831            let day = 86400.0
832            var curr0000 = from0000
833            var i = 0
834            while (curr0000 <= to0000 && i < 30) {
835                var val = self.getEarnedFromMVHoldingsForTime(timestamp: curr0000)
836                dict[curr0000] = val
837                curr0000 = curr0000 + day
838                i = i + 1
839            }
840
841            return dict
842        }
843
844        pub fun getTotalPaidDueDailyActivity(): UFix64 {
845            return self.totalPaidDueDailyActivity
846        }
847
848        pub fun getPaidDueDailyActivityForTime(timestamp: UFix64): UFix64 {
849            let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
850            if (self.paidDueDailyActivity[time0000] == nil) {
851                return 0.0
852            }
853            return self.paidDueDailyActivity[time0000]!
854        }
855
856        pub fun getPaidDueDailyActivityForTimeRange(from: UFix64, to: UFix64): {UFix64: UFix64} {
857            let from0000 = MoxyData.getTimestampTo0000(timestamp: from)
858            let to0000 = MoxyData.getTimestampTo0000(timestamp: to)
859
860            let dict: {UFix64:UFix64} = {}
861
862            let day = 86400.0
863            var curr0000 = from0000
864            var i = 0
865            while (curr0000 <= to0000 && i < 30) {
866                var val = self.getPaidDueDailyActivityForTime(timestamp: curr0000)
867                dict[curr0000] = val
868                curr0000 = curr0000 + day
869                i = i + 1
870            }
871
872            return dict
873        }
874
875        pub fun getTotalPaidDueMVConversion(): UFix64 {
876            return self.totalPaidDueMVConversion
877        }
878
879        pub fun getPaidDueMVConversionForTime(timestamp: UFix64): UFix64 {
880            let time0000 = MoxyData.getTimestampTo0000(timestamp:timestamp)
881            if (self.paidDueMVConversion[time0000] == nil) {
882                return 0.0
883            }
884            return self.paidDueMVConversion[time0000]!
885        }
886
887        pub fun getPaidDueMVConversionForTimeRange(from: UFix64, to: UFix64): {UFix64: UFix64} {
888            let from0000 = MoxyData.getTimestampTo0000(timestamp: from)
889            let to0000 = MoxyData.getTimestampTo0000(timestamp: to)
890
891            let dict: {UFix64:UFix64} = {}
892
893            let day = 86400.0
894            var curr0000 = from0000
895            var i = 0
896            while (curr0000 <= to0000 && i < 30) {
897                var val = self.getPaidDueMVConversionForTime(timestamp: curr0000)
898                dict[curr0000] = val
899                curr0000 = curr0000 + day
900                i = i + 1
901            }
902
903            return dict
904        }
905
906        pub fun getEarnedFromMVHoldingsFor(address: Address): {UFix64: UFix64} {
907            if (self.accounts[address] == nil) {
908                panic("Address not found in MoxyClub")
909            }
910            return self.accounts[address]?.getEarnedFromMVHoldings()!
911        }
912
913        pub fun getEarnedFromMVHoldingsForAddressTime(address: Address, timestamp: UFix64): UFix64 {
914            if (self.accounts[address] == nil) {
915                panic("Address not found in MoxyClub")
916            }
917            return self.accounts[address]?.getEarnedFromMVHoldingsFor(timestamp: timestamp)!
918        }
919
920        pub fun getEarnedFromMVHoldingsForAddressTimeRange(address: Address, from: UFix64, to: UFix64): {UFix64:UFix64} {
921            if (self.accounts[address] == nil) {
922                panic("Address not found in MoxyClub")
923            }
924            return self.accounts[address]?.getEarnedFromMVHoldingsForRange(from: from, to: to)!
925        }
926
927        pub fun getPaidDueDailyActivityFor(address: Address): {UFix64: UFix64} {
928            if (self.accounts[address] == nil) {
929                panic("Address not found in MoxyClub")
930            }
931            return self.accounts[address]?.getPaidDueActivity()!
932        }
933
934        pub fun getPaidDueDailyActivityForAddressTime(address: Address, timestamp: UFix64): UFix64 {
935            if (self.accounts[address] == nil) {
936                panic("Address not found in MoxyClub")
937            }
938            return self.accounts[address]?.getPaidDueDailyActivityFor(timestamp: timestamp)!
939        }
940
941        pub fun getPaidDueDailyActivityForAddressTimeRange(address: Address, from: UFix64, to: UFix64): {UFix64:UFix64} {
942            if (self.accounts[address] == nil) {
943                panic("Address not found in MoxyClub")
944            }
945            return self.accounts[address]?.getPaidDueDailyActivityForRange(from: from, to: to)!
946        }
947
948
949        pub fun getTotalEarnedFromMVHoldingsFor(address: Address): UFix64 {
950            if (self.accounts[address] == nil) {
951                panic("Address not found in MoxyClub")
952            }
953            return self.accounts[address]?.getTotalEarnedFromMVHoldings()!
954        }
955
956        pub fun getTotalPaidDueDailyActivityFor(address: Address): UFix64 {
957            if (self.accounts[address] == nil) {
958                panic("Address not found in MoxyClub")
959            }
960            return self.accounts[address]?.getTotalPaidDueDailyActivity()!
961        }
962
963        pub fun getTotalPaidDueMVConversionFor(address: Address): UFix64 {
964            if (self.accounts[address] == nil) {
965                panic("Address not found in MoxyClub")
966            }
967            return self.accounts[address]?.getTotalPaidDueMVConversion()!
968        }
969
970        //Returns the Membership Fee total for an address
971        pub fun getMembershipFeeFor(address: Address): UFix64 {
972            if (self.accounts[address] == nil) {
973                panic("Address not found in MoxyClub")
974            }
975            return self.accounts[address]?.membershipFee!
976        }
977
978        pub fun getMembershipFeeMOXYFor(address: Address): UFix64 {
979            return self.getMembershipFeeFor(address: address) / self.moxyToUSDValue
980        }
981
982        pub fun getMembershipFeeMOXY(): UFix64 {
983            return MoxyClub.membershipFee / self.moxyToUSDValue
984        }
985
986        pub fun isMoxyAccount(address: Address): Bool {
987            return (self.accounts[address] != nil)
988        }
989
990        pub fun isPlayAndEarnEventAccount(address: Address): Bool {
991            return self.playAndEarnEventAccounts[address] != nil
992        }
993
994        pub fun isMoxyControlledAccount(address: Address): Bool {
995            return self.moxyControlledAccounts[address] != nil
996        }
997
998        pub fun isGoodStandingOnOpenEvents(address: Address): Bool {
999            return self.accounts[address]?.isGoodStandingOnOpenEvents()!
1000        }
1001
1002        pub fun addMoxyAccount(address: Address) {
1003            pre {
1004                self.accounts[address] == nil : "Account already added to Moxy Club"
1005                self.checkVaultsTypes(address: address) : "Account is not correctly setup"
1006            }
1007
1008            self.accounts[address] <-! create MoxyAccount()
1009            self.mvHoldingsQueue.addAccount(address: address)
1010            self.proofOfPlayQueue.addAccount(address: address)
1011            
1012            emit MoxyClub.MoxyAccountAdded(address: address)
1013        }
1014
1015        pub fun checkVaultsTypes(address: Address): Bool {
1016            let acct = getAccount(address)
1017
1018            let moxy = acct.getCapability(MoxyToken.moxyTokenReceiverPath).borrow<&{FungibleToken.Receiver}>()
1019            let mv = acct.getCapability(MoxyVaultToken.moxyVaultTokenReceiverTimestampPath).borrow<&{MoxyVaultToken.ReceiverInterface}>()
1020            let play = acct.getCapability(PlayToken.playTokenReceiverPath).borrow<&{FungibleToken.Receiver}>()
1021            let score = acct.getCapability(ScoreToken.scoreTokenReceiverTimestampPath).borrow<&{ScoreToken.ReceiverInterface}>()
1022
1023            return (
1024                moxy != nil && mv != nil &&
1025                play != nil && score != nil &&
1026                moxy!.isInstance(Type<@MoxyToken.Vault>()) &&
1027                mv!.isInstance(Type<@MoxyVaultToken.Vault>()) &&
1028                play!.isInstance(Type<@PlayToken.Vault>()) &&
1029                score!.isInstance(Type<@ScoreToken.Vault>()) 
1030            )
1031        }
1032
1033        pub fun removeMoxyAccount(address: Address) {
1034            if (self.accounts[address] == nil) {
1035                panic("Can't remove. Account not found in Moxy Club")
1036            }
1037
1038            let moxyAccount <- self.accounts.remove(key: address)!
1039            destroy moxyAccount
1040            self.mvHoldingsQueue.removeAccount(address: address)
1041            self.proofOfPlayQueue.removeAccount(address: address)
1042        }
1043
1044        pub fun addPlayAndEarnEventAccount(address: Address){
1045            // Add a Play and Earn Account to the Moxy Ecosystem
1046            if (self.playAndEarnEventAccounts[address] != nil) {
1047                panic("Can't add Play and Earn Event account, acount already added.")
1048            }
1049            self.playAndEarnEventAccounts[address] <-! create PlayAndEarnAccount()
1050            emit MoxyClub.PlayAndEarnEventAccountAdded(address: address)
1051        }
1052
1053        pub fun addMoxyControlledAccount(address: Address){
1054            // Add a Moxy Controlled Account to the Moxy Ecosystem
1055            if (self.moxyControlledAccounts[address] != nil) {
1056                panic("Can't add Moxy Controlled account, acount already added.")
1057            }
1058            self.moxyControlledAccounts[address] = address
1059            emit MoxyClub.MoxyControlledAccountAdded(address: address)
1060        }
1061
1062        pub fun addAccountToRound(roundId: String, address: Address, amount: UFix64) {
1063            let roundManager = self.getRoundsCapability().borrow()!
1064            roundManager.setAddress(roundId: roundId, address: address, amount: amount)
1065            self.roundReleaseQueue.addAccount(address: address)
1066
1067            if (self.isReleaseStarted) {
1068                // Mint $MOXY for the round
1069                let initialReleaseVault <- self.mintMOXYTokens(amount: amount)
1070                roundManager.allocateAfterTGE(roundId: roundId, vault: <-initialReleaseVault, address: address)
1071            }
1072        }
1073
1074        pub fun getPlayBalanceFor(address: Address, timestamp: UFix64): UFix64? {
1075            let acct = getAccount(address)
1076            let vaultRef = acct.getCapability(PlayToken.playTokenDailyBalancePath)
1077                    .borrow<&PlayToken.Vault{PlayToken.DailyBalancesInterface}>()
1078                    ?? panic("Could not borrow Balance reference to the Vault")
1079            return vaultRef.getDailyBalanceFor(timestamp: timestamp)
1080        }
1081 
1082        pub fun getScoreBalanceFor(address: Address, timestamp: UFix64): UFix64? {
1083            let acct = getAccount(address)
1084            let vaultRef = acct.getCapability(ScoreToken.scoreTokenDailyBalancePath)
1085                .borrow<&ScoreToken.Vault{ScoreToken.DailyBalancesInterface}>()
1086                ?? panic("Could not borrow Balance reference to the Vault")
1087            return vaultRef.getDailyBalanceFor(timestamp: timestamp)
1088        }
1089        
1090        pub fun getDailyBalanceChangeFor(address: Address, timestamp: UFix64): Fix64 {
1091            let acct = getAccount(address)
1092            let vaultRef = acct.getCapability(ScoreToken.scoreTokenDailyBalancePath)
1093                .borrow<&ScoreToken.Vault{ScoreToken.DailyBalancesInterface}>()
1094                ?? panic("Could not borrow Balance reference to the Vault")
1095            
1096            return vaultRef.getDailyBalanceChange(timestamp: timestamp)
1097        }
1098
1099        pub fun getScore24TotalSupplyChange(timestamp: UFix64): Fix64 {
1100            return ScoreToken.getDailyChangeTo(timestamp: timestamp)
1101        }
1102
1103
1104        // Collects the initial fixed Memebership Fee (5 MOXY total)
1105        access(contract) fun collectMembershipFee(address: Address, vault: @FungibleToken.Vault): @FungibleToken.Vault {
1106            
1107            let remainingFee = self.getMembershipFeeMOXYRemainingFor(address: address)
1108
1109            var feeToDeduct = remainingFee
1110            if (remainingFee > vault.balance) {
1111                feeToDeduct = vault.balance
1112            }
1113
1114            let association = getAccount(self.associationAddress!)
1115            let associationVaultRef = association.getCapability(MoxyToken.moxyTokenReceiverPath)
1116                    .borrow<&{FungibleToken.Receiver}>()
1117                    ?? panic("Could not borrow Balance reference to the Vault")
1118
1119            let vaultFee <- vault.withdraw(amount: feeToDeduct)
1120            associationVaultRef.deposit(from: <-vaultFee)
1121            
1122            var feeUSD = feeToDeduct * self.moxyToUSDValue
1123            if (feeToDeduct > 0.0 && feeUSD == 0.0) {
1124                feeUSD = 0.00000001
1125            }
1126            self.accounts[address]?.updateMembershipFeePaid(amount: feeUSD)
1127
1128            emit MembershipFeeDeducted(address: address, feeDeducted: feeUSD, feeDeductedMOXY: feeToDeduct, remaining: remainingFee - feeToDeduct, moxyToUSDValue: self.moxyToUSDValue)
1129
1130            return <-vault
1131
1132        }
1133
1134        pub fun calculateRewardsDueMVHoldingsTo(address: Address, timestamp: UFix64): UFix64 {
1135            let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
1136
1137            let acct = getAccount(address)
1138            let vaultRef = acct.getCapability(MoxyVaultToken.moxyVaultTokenDailyBalancePath)
1139                .borrow<&MoxyVaultToken.Vault{MoxyVaultToken.DailyBalancesInterface}>()
1140                ?? panic("Could not borrow Balance reference to the Vault")
1141
1142            let balanceRef = acct.getCapability(MoxyVaultToken.moxyVaultTokenDailyBalancePath)
1143                    .borrow<&MoxyVaultToken.Vault{MoxyVaultToken.DailyBalancesInterface}>()
1144                     ?? panic("Could not borrow Balance reference to the Vault")
1145
1146            let lockedVaultRef = acct.getCapability(MoxyVaultToken.moxyVaultTokenLockedBalancePath)
1147                .borrow<&LockedMoxyVaultToken.LockedVault{LockedMoxyVaultToken.Balance}>()
1148                ?? panic("Could not borrow Balance reference to the Vault")
1149
1150            let totalMV = balanceRef.getDailyBalanceFor(timestamp: timestamp)!
1151            let totalLockedMV = lockedVaultRef.getDailyBalanceFor(timestamp: timestamp)!
1152            let total = totalMV + totalLockedMV
1153
1154            if (total == 0.0) {
1155                return 0.0
1156            }
1157
1158            let balancesChanges = vaultRef.getDailyBalancesChangesUpTo(timestamp: timestamp)
1159            let lockedBalancesChanges = lockedVaultRef.getDailyBalancesChangesUpTo(timestamp: timestamp)
1160
1161            var amnt = 0.0
1162            for value in lockedBalancesChanges.values {
1163                amnt = amnt + value
1164            }
1165
1166            for time in balancesChanges.keys {
1167                let am = balancesChanges[time]!
1168                let diff = Fix64(am + amnt) - Fix64(total)
1169                if (diff > 0.0 ) {
1170                    if (balancesChanges[time]! >= UFix64(diff)) {
1171                        balancesChanges[time] = balancesChanges[time]! - UFix64(diff)
1172                    } else {
1173                        panic("Difference should not be negative. Negative diff =".concat(diff.toString()).concat(" balancesChanges[time]! ").concat(balancesChanges[time]!.toString()) )
1174                    }
1175                }
1176                amnt = amnt + balancesChanges[time]!
1177            }
1178
1179            if (amnt != total) {
1180                panic("Error calculating MV Holdings amount changes does not match with total MV holdings")
1181            }
1182
1183            var rewardsMox = self.getMOXRewardsFromDictionaryFor(time0000: time0000, dictionary: balancesChanges, areLockedMV: false)
1184            var rewardsMoxFromLockedMV = self.getMOXRewardsFromDictionaryFor(time0000: time0000, dictionary: lockedBalancesChanges, areLockedMV: true)
1185
1186            var totalRewards = rewardsMox + rewardsMoxFromLockedMV
1187
1188            return totalRewards
1189        }
1190
1191        access(contract) fun getMOXRewardsFromDictionaryFor(time0000: UFix64, dictionary: {UFix64:UFix64}, areLockedMV: Bool): UFix64 {
1192            // Iterate over all MV allocated over past days
1193            var rewardsMox = 0.0
1194            for time in dictionary.keys {
1195                if (time0000 < time) {
1196                    // Continue on future MV holdings rewards
1197                    // This could be caused due to not running daily process on time
1198                    continue
1199                }
1200                // Daily Linear Appreciation over Time
1201                // days represent the longevity of the tokens
1202                let days = (time0000 - time) / 86400.0
1203                let amount = dictionary[time]!
1204
1205                if (amount == 0.0) {
1206                    // Continue no amount to compute
1207                    continue
1208                }
1209
1210                var percentage = 0.0
1211                if (days >= 0.0  && days <= 90.0 ) {
1212                    percentage = 2.0
1213                }
1214                if (days > 90.0  && days <= 180.0 ) {
1215                    percentage = 4.0
1216                }
1217                if (days > 180.0  && days <= 365.0 ) {
1218                    percentage = 6.0
1219                }
1220                if (days > 365.0 ) {
1221                    percentage = 10.0
1222                }
1223
1224                // For locked MV there is a maximum percentage
1225                if (areLockedMV && percentage > self.maximumPercentageLockedMV) {
1226                    percentage = self.maximumPercentageLockedMV
1227                }
1228
1229                percentage = UFix64(percentage / 100.0 / 365.0)
1230                rewardsMox = rewardsMox + (amount * percentage)
1231            }
1232
1233            return rewardsMox
1234        }
1235
1236        pub fun rewardDueMVHoldings(quantity: Int) {
1237            //It will run for a quantity of addresses depending on the current queue progress
1238            let run <- self.mvHoldingsQueue.lockRunWith(quantity: quantity)
1239            if (run == nil) {
1240                emit NoAddressesToProcessForMVHoldingsProcess(timestamp: getCurrentBlock().timestamp)
1241                destroy run
1242                return
1243            }
1244            let addresses = run?.getCurrentAddresses()!
1245            if (self.mvHoldingsQueue.isAtBeginning()) {
1246                emit StartingRewardsPaymentsDueMVHoldings(timestamp: getCurrentBlock().timestamp, accountsToProcess: self.mvHoldingsQueue.getAccountsQuantity())
1247            }
1248            self.rewardDueMVHoldingsToAddresses(addresses: addresses)
1249            self.mvHoldingsQueue.completeNextAddresses(run: <- run!)
1250            if (self.mvHoldingsQueue.hasFinished()) {
1251                emit FinishedRewardsPaymentsDueMVHoldings(timestamp: getCurrentBlock().timestamp, accountsProcessed: self.mvHoldingsQueue.getAccountsQuantity())
1252            }
1253
1254            emit BatchRewardsPaymentsDueMVHoldings(timestamp: getCurrentBlock().timestamp, requestedToProcess: quantity, accountsProcessed: addresses!.length, totalAccounts: self.mvHoldingsQueue.getAccountsQuantity())
1255        }
1256
1257        pub fun rewardDueMVHoldingsToAddresses(addresses: [Address]) {
1258            for address in addresses {
1259                self.rewardDueMVHoldingsTo(address: address)
1260            }
1261        }
1262
1263        pub fun rewardDueMVHoldingsTo(address: Address) {
1264            let timestamp = getCurrentBlock().timestamp
1265            let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
1266            if (self.accounts[address] == nil) {
1267                log("Address not found in Moxy Club ecosystem")
1268                emit AddressNotFoundWhenPayingMVHoldingsRewards(address: address, timestamp: time0000)
1269                return
1270            }
1271
1272            // Check for already paid account
1273            var lastMVHoldingsUpdatedTimestamp = self.accounts[address]?.lastMVHoldingsUpdatedTimestamp!
1274            if (lastMVHoldingsUpdatedTimestamp == 0.0) {
1275                //Set fist time when converting MOX to MV when is not already set
1276                let acct = getAccount(address)
1277                let mvInfoRef = acct.getCapability(MoxyVaultToken.moxyVaultTokenDailyBalancePath)
1278                    .borrow<&MoxyVaultToken.Vault{MoxyVaultToken.DailyBalancesInterface}>()
1279                if (mvInfoRef == nil) {
1280                    log("Account does not have MoxyVault Vault")
1281                    emit AccountDoesNotHaveMoxyVaultVault(address: address, message: "Could not borrow reference to MV Vault when processing MV Holdings rewards.")
1282                    return
1283                }
1284                let firstTime = mvInfoRef!.getFirstTimestampAdded()
1285                if (firstTime == nil) {
1286                    log("Address does not have MV holdings ".concat(address.toString()))
1287                    return
1288                }
1289                self.accounts[address]?.setLastMVHoldingsUpdatedTimestamp(timestamp: firstTime!)
1290                lastMVHoldingsUpdatedTimestamp = firstTime!
1291            }
1292            
1293            if (lastMVHoldingsUpdatedTimestamp >= time0000) {
1294                log("Requested date is smaller than the last MV Holdings updated date")
1295                emit RequestedDateSmallerToLastUpdateWhenPayingMVHoldingsRewards(address: address, timestamp: time0000, lastMVHoldingsUpdatedTimestamp: lastMVHoldingsUpdatedTimestamp)
1296                return
1297            }
1298
1299            // Get all timestamps from last updated MV Rewards to time0000
1300            let last0000 = MoxyData.getTimestampTo0000(timestamp: lastMVHoldingsUpdatedTimestamp)
1301            var days = (time0000 - last0000) / 86400.0
1302
1303            // Set maximum days to process to five days
1304            if (days > 5.0) {
1305                days = 5.0
1306            }
1307
1308            var i = 0.0
1309            var times: [UFix64] = []
1310            // Iterate maximum five days
1311            while i < days {
1312                i = i + 1.0
1313                times.append(last0000 + (i * 86400.0))
1314            }
1315
1316            for time in times {
1317                if (self.accounts[address]?.earnedFromMVHoldings![time] != nil) {
1318                    log("Rewards already paid to address in requested day")
1319                    emit PaidAlreadyMadeWhenPayingMVHoldingsRewards(address: address, timestamp: time0000)
1320                    continue
1321                }
1322
1323                // Moxy Vault rewards are paid in MOX, calculated by each user's MV holding  
1324                let rewardMOX = self.calculateRewardsDueMVHoldingsTo(address: address, timestamp: time) 
1325                
1326                if (rewardMOX > 0.0) {
1327                    if (self.accounts[address]?.isGoodStandingOnOpenEventsFor(timestamp: time)!) {
1328                        // Mint corresponding MOX tokens to user's account
1329                        self.mintMOXToAddress(address: address, amount: rewardMOX)
1330
1331                        // Update the minted timestamp (MoxyAccount)
1332                        self.accounts[address]?.setMOXEarnedFromMVHoldingsFor(timestamp: time, amount: rewardMOX)
1333
1334                        self.totalEarnedFromMVHoldings = self.totalEarnedFromMVHoldings + rewardMOX
1335                        if (self.earnedFromMVHoldings[time] == nil) {
1336                            self.earnedFromMVHoldings[time] = 0.0
1337                        }
1338                        self.earnedFromMVHoldings[time] = self.earnedFromMVHoldings[time]! + rewardMOX
1339                        emit MOXRewaredDueMVHoldingsTo(address: address, timestamp: time, amount: rewardMOX)
1340                    } else {
1341                        // User has not good standing on open events, so earned rewards will go to improve proof of play
1342                        self.accounts[address]?.setMOXEarnedFromMVHoldingsFor(timestamp: time, amount: 0.0)
1343
1344                        let moxToPLAYVault <- self.mintMOXYTokens(amount: rewardMOX)
1345                        self.convertMOXYtoPLAY(vault: <-moxToPLAYVault, address: self.treasuryAddress!, relation: 1.0)
1346                        emit MOXRewardedToPoPUserNotGoodStanding(address: address, timestamp: time, amountMOXY: rewardMOX, amountPLAY: rewardMOX / 1.0)
1347                    }
1348                }
1349            }
1350        }
1351
1352        pub fun rewardDueDailyActivity(quantity: Int) {
1353            //It will run for a quantity of addresses depending on the current queue progress
1354            let run <- self.proofOfPlayQueue.lockRunWith(quantity: quantity)
1355            if (run == nil) {
1356                emit NoAddressesToProcessForPoPProcess(timestamp: getCurrentBlock().timestamp)
1357                destroy run
1358                return
1359            }
1360            let addresses = run?.getCurrentAddresses()!
1361            
1362            if (self.proofOfPlayQueue.isAtBeginning()) {
1363                emit StartingRewardsPaymentsDuePoP(timestamp: getCurrentBlock().timestamp, accountsToProcess: self.proofOfPlayQueue.getAccountsQuantity())
1364            }
1365            self.rewardDueDailyActivityToAddresses(addresses: addresses)
1366            self.proofOfPlayQueue.completeNextAddresses(run: <-run!)
1367            if (self.proofOfPlayQueue.hasFinished()) {
1368                emit FinishedRewardsPaymentsDuePoP(timestamp: getCurrentBlock().timestamp, accountsProcessed: self.proofOfPlayQueue.getAccountsQuantity())
1369            }
1370            emit BatchRewardsPaymentsDuePoP(timestamp: getCurrentBlock().timestamp, requestedToProcess: quantity, accountsProcessed: addresses.length, totalAccounts: self.proofOfPlayQueue.getAccountsQuantity())
1371        }
1372
1373        pub fun rewardDueDailyActivityToAddresses(addresses: [Address]) {
1374            for address in addresses {
1375                self.rewardDueDailyActivityFor(address: address)
1376            }
1377        }
1378
1379        pub fun rewardDueDailyActivityFor(address: Address) {
1380            let timestamp = getCurrentBlock().timestamp
1381
1382            // SCORE + SCORE24 + PLAY
1383            let timeTo0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
1384
1385            if (self.accounts[address] == nil) {
1386                log("Address not found in Moxy Club ecosystem")
1387                emit AddressNotFoundWhenPayingPoPRewards(address: address, timestamp: timeTo0000)
1388                return
1389            }
1390
1391            // Check for already paid account
1392            var dailyActivityUpdatedTimestamp = self.accounts[address]?.dailyActivityUpdatedTimestamp!
1393            if (dailyActivityUpdatedTimestamp == 0.0) {
1394                //Set fist time when received first  SCORE for PoP
1395                let acct = getAccount(address)
1396                let scoreInfoRef = acct.getCapability(ScoreToken.scoreTokenDailyBalancePath)
1397                    .borrow<&ScoreToken.Vault{ScoreToken.DailyBalancesInterface}>()
1398                if (scoreInfoRef == nil) {
1399                    log("Account does not have Score Vault")
1400                    emit AccountDoesNotHaveScoreVault(address: address, message: "Could not borrow reference to SCORE Vault when processing PoP rewards.")
1401                    return
1402                }
1403                let firstTime = scoreInfoRef!.getFirstTimestampAdded()
1404                if (firstTime == nil) {
1405                    log("Address ".concat(address.toString()).concat(" does not have SCORE records"))
1406                    return
1407                }
1408                self.accounts[address]?.setDailyActivityUpdatedTimestamp(timestamp: firstTime! - 86400.0)  //Subtract one day
1409                dailyActivityUpdatedTimestamp = firstTime!
1410            }
1411            
1412            if (dailyActivityUpdatedTimestamp >= timeTo0000) {
1413                log("Requested date is smaller than the last Daily Activity updated date")
1414                emit RequestedDateSmallerToLastUpdateWhenPayingPoPRewards(address: address, timestamp: timeTo0000, lastDailyActivityUpdatedTimestamp: dailyActivityUpdatedTimestamp)
1415                return
1416            }
1417
1418            let ecosystemScore24ChangeDict:{UFix64:UFix64} = self.getTotalSupply24DueForProcessTo(address: address, toTimestamp: timeTo0000)
1419
1420            for time0000 in ecosystemScore24ChangeDict.keys {
1421                // Pull PLAY from user
1422                var play = self.getPlayBalanceFor(address: address, timestamp: time0000)
1423                // Pull SCORE from user
1424                var score = self.getScoreBalanceFor(address: address, timestamp: time0000)
1425                // Pull SCORE change from user, change should always be positive
1426                var change = UFix64(self.getDailyBalanceChangeFor(address: address, timestamp: time0000))
1427                // Pull totalSupply from PLAY, SCORE and SCORE24 (change in score)
1428                let ecosystemPlayTotalSupply = PlayToken.getTotalSupplyFor(timestamp: time0000)
1429                let ecosystemScoreTotalSupply = ScoreToken.getTotalSupplyFor(timestamp: time0000)
1430                let ecosystemScore24Change = self.getScore24TotalSupplyChange(timestamp: time0000)
1431
1432                if (play == nil) { play = 0.0}
1433                if (score == nil) { score = 0.0}
1434                if (change == nil) { change = 0.0}
1435
1436                let popDaily = ecosystemPlayTotalSupply * (self.proofOfPlayPercentage/100.0) / 365.0
1437
1438                var highScore = 0.0
1439                if (ecosystemScoreTotalSupply > 0.0) {
1440                    highScore = popDaily * (self.popScoreWeight / 100.0) * (score! / ecosystemScoreTotalSupply)
1441                }
1442
1443                var score24Change = 0.0
1444                if (ecosystemScore24Change != 0.0) {
1445                    // If SCORE changed
1446                    score24Change = popDaily * (self.popDailyScoreWeight / 100.0) * (change / UFix64(ecosystemScore24Change))
1447                }
1448
1449                var donationLevelProgression = 0.0
1450                if (ecosystemPlayTotalSupply > 0.0) {
1451                    donationLevelProgression = popDaily * (self.popPlayDonationWeight / 100.0) * (play! / ecosystemPlayTotalSupply)
1452                }
1453
1454                let totalMOX = highScore + score24Change + donationLevelProgression
1455
1456                if (totalMOX > 0.0) {
1457                    // Mint corresponding MOX tokens to user's account
1458                    self.mintMOXToAddress(address: address, amount: totalMOX)
1459                    emit MOXRewaredDueDailyActivityTo(address: address, timestamp: time0000, amount: totalMOX)
1460                    self.totalPaidDueDailyActivity = self.totalPaidDueDailyActivity + totalMOX
1461                    if (self.paidDueDailyActivity[time0000] == nil) {
1462                        self.paidDueDailyActivity[time0000] = 0.0
1463                    }
1464                    self.paidDueDailyActivity[time0000] = self.paidDueDailyActivity[time0000]! + totalMOX
1465                    self.accounts[address]?.updateTotalPaidDueDailyActivity(amount: totalMOX)
1466                }
1467                // Update the minted timestamp (MoxyAccount)
1468                if (self.accounts[address]?.dailyActivityUpdatedTimestamp! < time0000) {
1469                    self.accounts[address]?.setDailyActivityUpdatedTimestamp(timestamp: time0000)
1470                }
1471            }
1472        }
1473
1474        access(contract) fun mintMOXYTokens(amount: UFix64): @FungibleToken.Vault {
1475            let tokenAdmin = MoxyClub.account.borrow<&MoxyToken.Administrator>(from: MoxyToken.moxyTokenAdminStorage)
1476                ?? panic("Signer is not the token admin")
1477            
1478            let minter <- tokenAdmin.createNewMinter(allowedAmount: amount)
1479            let vault <- minter.mintTokens(amount: amount)
1480
1481            destroy minter
1482
1483            return <-vault
1484        }
1485
1486        access(contract) fun mintMOXToAddress(address: Address, amount: UFix64) {
1487            let tokenReceiver = getAccount(address)
1488                .getCapability(MoxyToken.moxyTokenReceiverPath)
1489                .borrow<&{FungibleToken.Receiver}>()
1490                ?? panic("Unable to borrow receiver reference")
1491            
1492            let mintedVault <- self.mintMOXYTokens(amount: amount)
1493
1494            // Mint tokens to user, first deduct Membership Fee amount
1495            if (self.accounts[address]?.hasMembershipFeePending()!) {
1496                let vaultDeducted <- self.collectMembershipFee(address: address, vault: <-mintedVault)
1497                tokenReceiver.deposit(from: <- vaultDeducted)            
1498            } else {
1499                tokenReceiver.deposit(from: <- mintedVault)
1500            }
1501        }
1502
1503        pub fun getTotalSupply24DueForProcessTo(address: Address, toTimestamp: UFix64): {UFix64: UFix64} {
1504            
1505            let fromTimestamp = self.accounts[address]?.dailyActivityUpdatedTimestamp!
1506            let from0000 = MoxyData.getTimestampTo0000(timestamp: fromTimestamp)
1507            let to0000 = MoxyData.getTimestampTo0000(timestamp: toTimestamp)
1508            let day = 86400.0
1509            let acct = getAccount(address)
1510            let vaultRef = acct.getCapability(ScoreToken.scoreTokenDailyBalancePath)
1511                            .borrow<&ScoreToken.Vault{ScoreToken.DailyBalancesInterface}>()
1512                            ?? panic("Could not borrow Balance reference to the Vault (address".concat(acct.address.toString().concat(")")))
1513
1514            // Get all pending since last update
1515            let resu: {UFix64: UFix64} = {}
1516            var curr0000 = from0000
1517
1518            if (curr0000 <= 0.0) {
1519                var first = vaultRef.getFirstTimestampAdded()
1520                if (first == nil) {
1521                    return resu
1522                }
1523                curr0000 = first!
1524            }
1525            if (fromTimestamp == curr0000) {
1526                // Skip to next if start is equal to last registered
1527                curr0000 = curr0000 + day
1528            }
1529
1530            while (curr0000 < to0000) {
1531                var val = vaultRef.getBalanceFor(timestamp: curr0000)
1532                if (val == nil) {
1533                    val = 0.0
1534                }
1535                resu[curr0000] = val
1536                curr0000 = curr0000 + day
1537            }
1538            return resu 
1539        }
1540
1541        pub fun createMVToMOXYConverter(mvConverter: @MoxyVaultToken.MVConverter, conversionRequest: @LockedMoxyToken.ConversionRequest): @MVToMOXYConverter {
1542            if (mvConverter.address != conversionRequest.address) {
1543                panic("Conversion are available only with requests on the same address.")
1544            }
1545
1546            let inConversion = self.accounts[mvConverter.address]?.getMVToMOXYTotalInConversion()
1547            
1548            let acct = getAccount(mvConverter.address)
1549            let vaultRef = acct.getCapability(MoxyVaultToken.moxyVaultTokenBalancePath)
1550                .borrow<&MoxyVaultToken.Vault{FungibleToken.Balance}>()
1551                ?? panic("Could not borrow Balance reference to the Vault")
1552
1553            let available = vaultRef.balance - inConversion!
1554
1555            if (available < mvConverter.allowedAmount) {
1556                panic("Not enough funds to convert MV to MOXY")
1557            }
1558
1559            return <- create MVToMOXYConverter(mvConverter: <-mvConverter, conversionRequest: <- conversionRequest, timestamp: getCurrentBlock().timestamp, withdrawalDays: self.mvToMOXWithdrawalDays)
1560        }
1561
1562        pub fun getMVConverterStorageIdentifier(timestamp: UFix64): String {
1563            return "mvToMOXYConverter".concat(UInt64(timestamp).toString())
1564        }
1565
1566        pub fun registerMVToMOXConversion(address:Address, timestamp: UFix64, amount: UFix64) {
1567            pre {
1568                self.accounts[address]?.hasVaildMVToMOXYConverters(address: address)! : "Address converting does not match with original converter"
1569            }
1570
1571            self.mvToMOXConversionQueue.addAccount(address: address)
1572            emit MOXToMVDailyConversionTo(address: address, timestamp: timestamp, amount: amount)
1573        }
1574
1575        pub fun payMOXDueMVConversion(quantity: Int) {
1576            //It will run for a quantity of addresses depending on the current queue progress
1577            let run <- self.mvToMOXConversionQueue.lockRunWith(quantity: quantity)
1578            if (run == nil) {
1579                emit NoAddressesToProcessMVConversionProcess(timestamp: getCurrentBlock().timestamp)
1580                destroy run
1581                return
1582            }
1583            let addresses = run?.getCurrentAddresses()!
1584            
1585            if (self.mvToMOXConversionQueue.isAtBeginning()) {
1586                emit StartingPayingMOXYDueMVConversion(timestamp: getCurrentBlock().timestamp, accountsToProcess: self.mvToMOXConversionQueue.getAccountsQuantity())
1587            }
1588            self.payMOXDueMVConversionToAddresses(addresses: addresses)
1589            self.mvToMOXConversionQueue.completeNextAddresses(run: <-run!)
1590            if (self.mvToMOXConversionQueue.hasFinished()) {
1591                emit FinishedPayingMOXYDueMVConversion(timestamp: getCurrentBlock().timestamp, accountsProcessed: self.mvHoldingsQueue.getAccountsQuantity())
1592            }
1593            emit BatchRewardsPaymentsDueMVHoldings(timestamp: getCurrentBlock().timestamp, requestedToProcess: quantity, accountsProcessed: addresses.length, totalAccounts: self.mvToMOXConversionQueue.getAccountsQuantity())
1594        }
1595
1596        pub fun payMOXDueMVConversionToAddresses(addresses: [Address]) {
1597            for address in addresses {
1598                self.payMOXDueMVConversionFor(address: address)
1599            }
1600        }
1601
1602        // Pay due MOXY to MV conversion for an specific account up to
1603        // the current timestamp date
1604        pub fun payMOXDueMVConversionFor(address: Address) {
1605            if (!self.accounts[address]?.hasVaildMVToMOXYConverters(address: address)!) {
1606                return log("Address has invalid conversion requests.")
1607            }
1608
1609            let timestamp = getCurrentBlock().timestamp
1610            let amount = self.accounts[address]?.payMOXDueMVConversionUpto(timestamp: timestamp)
1611            self.totalPaidDueMVConversion = self.totalPaidDueMVConversion + amount!
1612            if (self.paidDueMVConversion[timestamp] == nil) {
1613                self.paidDueMVConversion[timestamp] = 0.0
1614            }
1615            let time0000 = MoxyData.getTimestampTo0000(timestamp: getCurrentBlock().timestamp)
1616            self.paidDueMVConversion[timestamp] = self.paidDueMVConversion[timestamp]! + amount!
1617            self.accounts[address]?.updateTotalPaidDueMVConversion(amount: amount!)
1618        }
1619
1620        pub fun checkAndRemoveFinishedConversionTo(addresses: [Address]) {
1621            for address in addresses {
1622                if (self.accounts[address]?.haveFinishedConversions()!) {
1623                    self.mvToMOXConversionQueue.removeAccount(address: address)
1624                }
1625            }
1626        }
1627
1628        pub fun depositToPlayAndEarnVault(address: Address, vault: @FungibleToken.Vault) {
1629            self.accounts[address]?.playAndEarnRef!.borrow()!.deposit(from: <-vault)
1630        }
1631
1632        pub fun withdrawFromPlayAndEarnVault(address: Address, amount: UFix64) {
1633            let peVault = self.accounts[address]?.playAndEarnRef!.borrow()!
1634            let vault <- peVault.withdraw(amount: amount)
1635
1636            let recipient = getAccount(address)
1637            // Get a reference to the recipient's Receiver
1638            let receiverRef = recipient.getCapability(MoxyToken.moxyTokenReceiverPath)
1639                .borrow<&{FungibleToken.Receiver}>()
1640                ?? panic("Could not borrow receiver reference to the recipient's Vault")
1641
1642            receiverRef.deposit(from: <-vault)
1643        }
1644
1645        pub fun payFromPlayAndEarnVault(payee: Address, amount: UFix64, toAddress: Address) {
1646            let peVault = self.accounts[payee]?.playAndEarnRef!.borrow()!
1647            let vault <- peVault.withdraw(amount: amount)
1648
1649            // Get the recipient's public account object
1650            let recipient = getAccount(toAddress)
1651
1652            let receiverRef = recipient.getCapability(MoxyToken.moxyTokenReceiverPath)
1653                .borrow<&{FungibleToken.Receiver}>()
1654                ?? panic("Could not borrow receiver reference to the recipient's Vault")
1655
1656            receiverRef.deposit(from: <- vault)
1657        }
1658
1659        pub fun withdrawFundsFromPlayAndEarnVault(address: Address, amount: UFix64): @FungibleToken.Vault {
1660            let peVault = self.accounts[address]?.playAndEarnRef!.borrow()!
1661            let vault <- peVault.withdraw(amount: amount)
1662
1663            return <-vault
1664
1665        }
1666
1667        pub fun transferMOXY(fromVault: @FungibleToken.Vault, to: Address) {
1668            // Function to transfer MOX from one account to a recepient account
1669            // The process consists on obtainig the vault with the amount received
1670            // doing a withdraw from the origin account
1671            // Then is calculated the fee charged, and the amount deposited to
1672            // the receiver will be the original amount subtracting that fee.
1673            // Then the fee is stored 95% on Treasury account and 5% is converted
1674            // to PLAY in order to strength Proof of Play to all ecosystem.
1675            // Finally if the recipient is the Treasury Account, additionally the 10%
1676            // of the received funds will be converted to PLAY to strength 
1677            // Proof of Play.
1678            // All convertions from MOX to PLAY are done in a rate 2:1 
1679
1680            
1681            if (self.accounts[to] == nil && self.playAndEarnEventAccounts[to] == nil && self.moxyControlledAccounts[to] == nil) {
1682                panic ("Recipient account not found in Moxy Club.")
1683            }
1684
1685            // Get the recipient's public account object
1686            let recipient = getAccount(to)
1687            let feeRecipient = getAccount(self.treasuryAddress!)
1688
1689            // Get a reference to the recipient's Receiver
1690            let receiverRef = recipient.getCapability(MoxyToken.moxyTokenReceiverPath)
1691                .borrow<&{FungibleToken.Receiver}>()
1692                ?? panic("Could not borrow receiver reference to the recipient's Vault")
1693
1694            // Get a reference to the fee recipient's Receiver
1695            let feeReceiverRef = feeRecipient.getCapability(MoxyToken.moxyTokenReceiverPath)
1696                .borrow<&{FungibleToken.Receiver}>()
1697                ?? panic("Could not borrow receiver reference to the recipient's Vault")
1698
1699            // Calculate cutted amounts
1700            let feeAmount = self.getMOXYFeeAmount()
1701            var receiverAmount = fromVault.balance - feeAmount
1702            var convertToPLAY: UFix64 = 0.0
1703
1704            emit FeeCharged(address: to, amount: feeAmount)
1705
1706            // Receiver if treasury 10% goes to PLAY
1707            if (to == self.treasuryAddress) {
1708                convertToPLAY = receiverAmount * 0.1
1709                receiverAmount = receiverAmount - convertToPLAY
1710
1711                emit TreasuryRepurchase(amount: convertToPLAY)
1712            } 
1713
1714            let receiverVault: @FungibleToken.Vault <- fromVault.withdraw(amount: receiverAmount) 
1715            let feeReceiverVault: @FungibleToken.Vault <- fromVault.withdraw(amount: feeAmount + convertToPLAY)
1716
1717            // Deposit the withdrawn tokens in the recipient's receiver
1718            // If the recipient has pending Membership Fee to paid, the fee is collected
1719            if (self.accountHasMembershipFeePending(address: to)) {
1720                let vaultDeducted <- self.collectMembershipFee(address: to, vault: <-receiverVault)
1721                receiverRef.deposit(from: <- vaultDeducted)
1722            } else {
1723                receiverRef.deposit(from: <- receiverVault)
1724            }
1725
1726            // Fee Amount 95% to treasury and 5% to PLAY (2x1 ratio)
1727            let moxToPlayAmount = convertToPLAY + feeAmount * self.percentFeeToPLAY
1728            let moxFeeAmount = feeReceiverVault.balance - moxToPlayAmount
1729
1730            let feeReceiverMOXVault: @FungibleToken.Vault <- feeReceiverVault.withdraw(amount: moxFeeAmount)
1731            let moxToPLAYVault: @FungibleToken.Vault <- feeReceiverVault.withdraw(amount: moxToPlayAmount)
1732
1733            feeReceiverRef.deposit(from: <- feeReceiverMOXVault)
1734
1735            // Burn MOXY
1736            // Mint play 2:1 for treasuryAddress
1737            self.convertMOXYtoPLAY(vault: <-moxToPLAYVault, address: self.treasuryAddress!, relation: 2.0)
1738
1739            // Residual MOX handling. If there are differences due floating point precision
1740            feeReceiverRef.deposit(from: <- fromVault)
1741            feeReceiverRef.deposit(from: <- feeReceiverVault)
1742            
1743        }
1744
1745        pub fun accountHasMembershipFeePending(address: Address): Bool {
1746            if (self.accounts[address] == nil && self.playAndEarnEventAccounts[address] == nil && self.moxyControlledAccounts[address] == nil ) {
1747                panic ("Account not found in Moxy Club.")
1748            }
1749            if (self.playAndEarnEventAccounts[address] != nil || self.moxyControlledAccounts[address] != nil ) {
1750                return false
1751            }
1752            return self.accounts[address]?.hasMembershipFeePending()!
1753        }
1754
1755        access(contract) fun convertMOXYtoPLAY(vault: @FungibleToken.Vault, address: Address, relation: UFix64) {
1756            let playAmount = vault.balance / relation
1757
1758            if (playAmount > 0.0) {
1759                // Mint PLAY token
1760                let tokenAdmin: &PlayToken.Administrator = MoxyClub.account.borrow<&PlayToken.Administrator>(from: PlayToken.playTokenAdminStorage)
1761                    ?? panic("Signer is not the token admin")
1762
1763                let tokenReceiver: &{PlayToken.ReceiverInterface} = getAccount(address)
1764                    .getCapability(PlayToken.playTokenReceiverInterfacePath)
1765                    .borrow<&{PlayToken.ReceiverInterface}>()
1766                    ?? panic("Unable to borrow receiver reference")
1767
1768                
1769                let minter <- tokenAdmin.createNewMinter(allowedAmount: playAmount)
1770                
1771                let mintedVault <- minter.mintTokens(amount: playAmount)
1772                tokenReceiver.convertedFromMOXY(from: <-mintedVault)
1773                destroy minter
1774            }
1775
1776            // Burn MOX
1777            // Create a reference to the admin admin resource in storage
1778            let admin = MoxyClub.account.borrow<&MoxyToken.Administrator>(from: MoxyToken.moxyTokenAdminStorage)
1779                ?? panic("Could not borrow a reference to the admin resource")
1780
1781            let burner <- admin.createNewBurner()
1782            burner.burnTokens(from: <-vault)
1783            destroy burner
1784
1785        }
1786
1787        // Returns the MV to MOX requests by address
1788        pub fun getMVToMOXtRequests(address: Address): {UFix64: MVToMOXRequestInfo} {
1789            if (self.accounts[address] == nil) {
1790                panic("Address not found in MoxyClub")
1791            }
1792            return self.accounts[address]?.getMVToMOXtRequests()!
1793        }
1794
1795        pub fun isTGESet(): Bool {
1796            return self.getTGEDate() > 0.0
1797        }
1798        
1799        pub fun releaseIsNotStarted(): Bool {
1800            return !self.isReleaseStarted
1801        }
1802
1803        pub fun isTGEDateReached(): Bool {
1804            return self.getTGEDate() <= getCurrentBlock().timestamp
1805        }
1806
1807        pub fun areRoundsReadyToStartRelease(): Bool {
1808            let rounds = self.getRoundsCapability().borrow()!
1809            return  rounds.isReadyToStartRelease()
1810        }
1811
1812        pub fun haveAllRoundsStarted(): Bool {
1813            let rounds = self.getRoundsCapability().borrow()!
1814            return  rounds.haveAllRoundsStarted()
1815        }
1816        
1817        // Start release to a quantity of addresses. This is the starting point
1818        // The methods called from here will be not available to call independtly
1819        pub fun startReleaseTo(quantity: Int) {
1820             pre {
1821                self.releaseIsNotStarted() : "Cannot start allocation process: Release is already started."
1822                self.isTGESet() : "Cannot start allocation process: TGE Date is not set."
1823                self.isTGEDateReached() : "Cannot start allocation process: TGE date is not reached."
1824            }
1825
1826            let rounds = self.getRoundsCapability().borrow()!
1827            if (rounds.isQueueAtBegining()) {
1828                // Start round release is starting process. Emit event.
1829                let accountsToProcess = rounds.getAccountsToProcess()
1830                emit StartingRoundReleaseInitializeProcess(timestamp: (getCurrentBlock().timestamp),roundsToProcess: rounds.getRoundsLength(), accountsToProcess: accountsToProcess)
1831            }
1832
1833            for roundId in rounds.getRoundsNames() {
1834                if (!rounds.hasQueueFinished(roundId: roundId)) {
1835                    // Process unfinished round and exit
1836                    let run <- rounds.getQueueNextAddresses(roundId: roundId, quantity: quantity)
1837                    let addresses = run.getCurrentAddresses()
1838                    self.startReleaseRoundToAddress(roundId: roundId, addresses: addresses)
1839                    rounds.completeNextAddresses(roundId: roundId, run: <-run)
1840                    return
1841                }
1842            }
1843
1844            // Check if all rounds were processed
1845            if (rounds.initialAllocationFinished()) {
1846                self.isReleaseStarted = true
1847                let accountsToProcess = rounds.getAccountsToProcess()
1848                emit FinishedRoundReleaseInitializeProcess(timestamp: getCurrentBlock().timestamp, roundsProcessed: rounds.getRoundsLength(), accountsProcessed: accountsToProcess)
1849            }
1850        }
1851
1852        // Process start release to addresses from round id provided
1853        access(self) fun startReleaseRoundToAddress(roundId: String, addresses: [Address]) {
1854            for address in addresses {
1855                self.startReleaseRoundAddress(roundId: roundId, address: address)
1856            }
1857        }
1858
1859        access(self) fun startReleaseRoundAddress(roundId: String, address: Address) {
1860            let rounds = self.getRoundsCapability().borrow()!
1861            let amount = rounds.getAmountFor(roundId: roundId, address: address)
1862
1863            if (amount > 0.0) {
1864                // Mint $MOXY for the round
1865                let initialReleaseVault <- self.mintMOXYTokens(amount: amount)
1866                rounds.startReleaseRound(roundId: roundId, address: address, initialVault: <-initialReleaseVault)
1867            }
1868        }
1869
1870        pub fun assignMoxyControlledWalletsToRounds( 
1871                    publicIDOAddress: Address, teamAddress: Address, 
1872                    foundationAddress: Address, advisorsAddress: Address,
1873                    treasuryAddress: Address, ecosystemAddress: Address) {
1874            
1875            let roundsManager = self.getRoundsCapability().borrow()!
1876            roundsManager.fullAllocateTo(roundId: "public_ido", address: publicIDOAddress)
1877            roundsManager.fullAllocateTo(roundId: "team", address: teamAddress)
1878            roundsManager.fullAllocateTo(roundId: "moxy_foundation", address: foundationAddress)
1879            roundsManager.fullAllocateTo(roundId: "advisors", address: advisorsAddress)
1880            roundsManager.fullAllocateTo(roundId: "treasury", address: treasuryAddress)
1881            roundsManager.fullAllocateTo(roundId: "ecosystem", address: ecosystemAddress)
1882
1883            self.roundReleaseQueue.addAccount(address: publicIDOAddress)
1884            self.roundReleaseQueue.addAccount(address: teamAddress)
1885            self.roundReleaseQueue.addAccount(address: foundationAddress)
1886            self.roundReleaseQueue.addAccount(address: advisorsAddress)
1887            self.roundReleaseQueue.addAccount(address: treasuryAddress)
1888            self.roundReleaseQueue.addAccount(address: ecosystemAddress)
1889            
1890        }
1891
1892        pub fun areMoxyControlledWalletsAllocated(): Bool {
1893            let roundsManager = self.getRoundsCapability().borrow()!
1894
1895            return (
1896                    roundsManager.isReadyToStartReleaseTo(roundId: "public_ido") &&
1897                    roundsManager.isReadyToStartReleaseTo(roundId: "team") &&
1898                    roundsManager.isReadyToStartReleaseTo(roundId: "moxy_foundation") &&
1899                    roundsManager.isReadyToStartReleaseTo(roundId: "advisors") &&
1900                    roundsManager.isReadyToStartReleaseTo(roundId: "treasury") &&
1901                    roundsManager.isReadyToStartReleaseTo(roundId: "ecosystem")
1902                )
1903        }
1904
1905        pub fun purchaseFromPublicPresale(roundsRef: Capability<&MoxyReleaseRounds.Rounds>, address: Address, amount: UFix64) {
1906            let roundManager = roundsRef.borrow()!
1907
1908            roundManager.setAddress(roundId: "public_presale", address: address, amount: amount)
1909            self.roundReleaseQueue.addAccount(address: address)
1910
1911            if (self.isReleaseStarted) {
1912                // Mint $MOXY for the round
1913                let initialReleaseVault <- self.mintMOXYTokens(amount: amount)
1914                roundManager.allocateAfterTGE(roundId: "public_presale", vault: <-initialReleaseVault, address: address)
1915            }
1916        }
1917
1918        pub fun transferWithRoundSchedule(vault: @FungibleToken.Vault, roundsRef: Capability<&MoxyReleaseRounds.Rounds>, roundId: String, address: Address, startTime: UFix64) {
1919            let roundManager = roundsRef.borrow()!
1920
1921            roundManager.incorporateAddress(roundId: roundId, address: address, amount: vault.balance, startTime: startTime)
1922            self.roundReleaseQueue.addAccount(address: address)
1923
1924            roundManager.allocateOn(timestamp: startTime, roundId: roundId, vault: <-vault, address: address)
1925        }
1926
1927        pub fun getProcessRoundsRemainings(): Int {
1928            return self.roundReleaseQueue.getRemainings()
1929        }
1930
1931        pub fun getProcessRoundsAccountsQuantity(): Int {
1932            return self.roundReleaseQueue.getAccountsQuantity()
1933        }
1934
1935        pub fun getProcessRoundsStatus(): MoxyProcessQueue.CurrentRunStatus {
1936            return self.roundReleaseQueue.getCurrentRunStatus()
1937        }
1938
1939        pub fun setProcessRoundsRunSize(quantity: Int) {
1940            self.roundReleaseQueue.setRunSize(quantity: quantity)
1941        }
1942
1943        pub fun getProcessRoundsRunSize(): Int {
1944            return self.roundReleaseQueue.getRunSize()
1945        }
1946
1947        pub fun getProcessRoundsRemainingAddresses(): [Address] {
1948            return self.roundReleaseQueue.getRemainingAddresses()
1949        }
1950
1951        pub fun getProcessMVHoldingsRemainings(): Int {
1952            return self.mvHoldingsQueue.getRemainings()
1953        }
1954
1955        pub fun getProcessMVHoldingsAccountsQuantity(): Int {
1956            return self.mvHoldingsQueue.getAccountsQuantity()
1957        }
1958
1959        pub fun getProcessMVHoldingsStatus(): MoxyProcessQueue.CurrentRunStatus {
1960            return self.mvHoldingsQueue.getCurrentRunStatus()
1961        }
1962
1963        pub fun setProcessMVHoldingsRunSize(quantity: Int) {
1964            self.mvHoldingsQueue.setRunSize(quantity: quantity)
1965        }
1966
1967        pub fun getProcessMVHoldingsRunSize(): Int {
1968            return self.mvHoldingsQueue.getRunSize()
1969        }
1970
1971        pub fun getProcessMVHoldingsRemainingAddresses(): [Address] {
1972            return self.mvHoldingsQueue.getRemainingAddresses()
1973        }
1974
1975        pub fun getProcessProofOfPlayRemainings(): Int {
1976            return self.proofOfPlayQueue.getRemainings()
1977        }
1978
1979        pub fun getProcessProofOfPlayAccountsQuantity(): Int {
1980            return self.proofOfPlayQueue.getAccountsQuantity()
1981        }
1982
1983        pub fun getProcessProofOfPlayStatus(): MoxyProcessQueue.CurrentRunStatus {
1984            return self.proofOfPlayQueue.getCurrentRunStatus()
1985        }
1986
1987        pub fun setProcessProofOfPlayRunSize(quantity: Int) {
1988            self.proofOfPlayQueue.setRunSize(quantity: quantity)
1989        }
1990
1991        pub fun getProcessProofOfPlayRunSize(): Int {
1992            return self.proofOfPlayQueue.getRunSize()
1993        }
1994
1995        pub fun getProcessProofOfPlayRemainingAddresses(): [Address] {
1996            return self.proofOfPlayQueue.getRemainingAddresses()
1997        }
1998
1999        pub fun getMVToMOXConversionRemainings(): Int {
2000            return self.mvToMOXConversionQueue.getRemainings()
2001        }
2002
2003        pub fun getMVToMOXConversionAccountsQuantity(): Int {
2004            return self.mvToMOXConversionQueue.getAccountsQuantity()
2005        }
2006
2007        pub fun getMVToMOXConversionStatus(): MoxyProcessQueue.CurrentRunStatus {
2008            return self.mvToMOXConversionQueue.getCurrentRunStatus()
2009        }
2010
2011        pub fun setMVToMOXConversionRunSize(quantity: Int) {
2012            self.mvToMOXConversionQueue.setRunSize(quantity: quantity)
2013        }
2014        
2015        pub fun getMVToMOXConversionRunSize(): Int {
2016            return self.mvToMOXConversionQueue.getRunSize()
2017        }
2018
2019        pub fun getMVToMOXConversionRemainingAddresses(): [Address] {
2020            return self.mvToMOXConversionQueue.getRemainingAddresses()
2021        }
2022
2023        pub fun setIsGoodStandingOnOpenEvents(address: Address, value: Bool) {
2024            self.accounts[address]?.setIsGoodStandingOnOpenEvents(value: value)
2025        }
2026
2027        pub fun convertMOXYToMV(address: Address, vault: @FungibleToken.Vault) {
2028            // Converting MOXY to MV locks the MOXY
2029            if (!self.accounts[address]?.isGoodStandingOnOpenEvents()!) {
2030                panic ("Account should participate in next Opnen events to convert MOXY to MV")
2031            }
2032
2033            let account = getAccount(address)
2034            let amount = vault.balance
2035
2036            // Get a reference to the recipient's Receiver
2037            let userRef = account.getCapability(MoxyToken.moxyTokenLockedMVReceiverPath)
2038                .borrow<&{LockedMoxyToken.Receiver}>()
2039                ?? panic("Could not borrow receiver reference to the recipient's Vault")
2040
2041            // Deposit the withdrawn tokens in the recipient's receiver
2042            let vaultConverted <- vault as! @MoxyToken.Vault
2043            userRef.deposit(from: <- vaultConverted)
2044
2045            // Create an MV minter with the same amount of MOX locked
2046            let tokenAdmin = MoxyClub.account.borrow<&MoxyVaultToken.Administrator>(from: MoxyVaultToken.moxyVaultTokenAdminStorage)
2047                ?? panic("Signer is not the token admin")
2048            
2049            let minter <- tokenAdmin.createNewMinter(allowedAmount: amount)
2050            let mintedVault <- minter.mintTokens(amount: amount)
2051            // Get a reference to the recipient's Receiver
2052
2053            let userMVRef = account.getCapability(MoxyVaultToken.moxyVaultTokenReceiverTimestampPath)
2054                .borrow<&{MoxyVaultToken.ReceiverInterface}>()
2055                ?? panic("Could not borrow receiver reference to the recipient's Vault")
2056
2057            // Deposit the withdrawn tokens in the recipient's receiver
2058            userMVRef.depositDueConversion(from: <- mintedVault) 
2059
2060            destroy minter
2061        }
2062
2063        pub fun convertLockedMOXYToLockedMV(request: @LockedMoxyToken.ConversionRequest, address: Address) {
2064            pre {
2065                address == request.address : "Address does not match with the owner of the vault"
2066            }
2067
2068            if (!self.accounts[address]?.isGoodStandingOnOpenEvents()!) {
2069                panic ("Account should participate in next Opnen events to convert MOXY to MV")
2070            }
2071
2072            let account = getAccount(address)
2073            
2074            let fixedAmount = request.getFixedAmount()
2075
2076            // Get the reference of the LockedMVMOXY
2077            let userRef = account.getCapability(MoxyToken.moxyTokenLockedMVReceiverPath)
2078                    .borrow<&{LockedMoxyToken.Receiver}>()
2079                    ?? panic("Could not borrow receiver reference to the recipient's Vault")
2080            let vault <- request.withdraw()
2081            let amount = vault.balance
2082            userRef.deposit(from: <- vault)
2083
2084            // Mint locked MV to the user
2085            // Create an MV minter with the same amount of MOXY locked
2086            let tokenAdmin = MoxyClub.account.borrow<&MoxyVaultToken.Administrator>(from: MoxyVaultToken.moxyVaultTokenAdminStorage)
2087                ?? panic("Signer is not the token admin")
2088            
2089            let minter <- tokenAdmin.createNewMinter(allowedAmount: amount)
2090            let mintedVault <- minter.mintTokens(amount: amount)
2091
2092            // Get a reference to the recipient's Receiver
2093            let userMVRef = account.getCapability(MoxyVaultToken.moxyVaultTokenLockedReceiverPath)
2094                .borrow<&{LockedMoxyVaultToken.Receiver}>()
2095                ?? panic("Could not borrow receiver reference to the recipient's Vault")
2096
2097            // Deposit the withdrawn tokens in the recipient's receiver
2098//            userMVRef.deposit(from: <- mintedVault)
2099            let vaultConverted <- mintedVault.withdrawAmount(amount: amount - fixedAmount) as! @MoxyVaultToken.Vault
2100            userMVRef.depositFromSchedule(from: <- vaultConverted, schedule: request.getSchedule())
2101
2102            var i = 0
2103            let schedules = request.getFixedSchedules()
2104            while (i < schedules.length) {
2105                let fx = schedules[i]
2106                var am = mintedVault.balance
2107                if (fx.remaining < am) {
2108                    am = fx.remaining
2109                }
2110                let vaultConverted <- mintedVault.withdrawAmount(amount: am) as! @MoxyVaultToken.Vault
2111                userMVRef.depositFromFixedSchedule(from: <- vaultConverted, schedule: fx.schedule)
2112                i = i +1
2113            }
2114
2115            if (mintedVault.balance != 0.0) {
2116                panic("Error minted vault has not been fully allocated")
2117            } 
2118        
2119
2120            // Update MVHoldings timestamp if it is not initialized
2121            var lastMVHoldingsUpdatedTimestamp = self.accounts[address]?.lastMVHoldingsUpdatedTimestamp!
2122            if (lastMVHoldingsUpdatedTimestamp == 0.0) {
2123                self.accounts[address]?.setLastMVHoldingsUpdatedTimestamp(timestamp: getCurrentBlock().timestamp)
2124            }
2125
2126            destroy mintedVault
2127            destroy request
2128            destroy minter
2129        }
2130
2131        pub fun allocateDailyReleaseTo(roundsRef: Capability<&MoxyReleaseRounds.Rounds>, quantity: Int) {
2132            //It will run for a quantity of addresses depending on the current queue progress
2133            let run <- self.roundReleaseQueue.lockRunWith(quantity: quantity)
2134            if (run == nil) {
2135                emit NoAddressesToProcessForRoundReleaseAllocation(timestamp: getCurrentBlock().timestamp)
2136                destroy run
2137                return
2138            }
2139            let addresses = run?.getCurrentAddresses()!
2140            
2141            if (self.roundReleaseQueue.isAtBeginning()) {
2142                emit StartingDailyRoundReleaseAllocationProcess(timestamp: getCurrentBlock().timestamp, accountsToProcess: self.roundReleaseQueue.getAccountsQuantity())
2143            }
2144            self.allocateDailyReleaseToAddresses(roundsRef: roundsRef, addresses: addresses)
2145            self.roundReleaseQueue.completeNextAddresses(run: <-run!)
2146            if (self.roundReleaseQueue.hasFinished()) {
2147                emit FinishedDailyRoundReleaseAllocationProcess(timestamp: getCurrentBlock().timestamp, accountsProcessed: self.roundReleaseQueue.getAccountsQuantity())
2148            }
2149            emit BatchDailyRoundReleaseAllocationProcess(timestamp: getCurrentBlock().timestamp, requestedToProcess: quantity, accountsProcessed: addresses.length, totalAccounts: self.roundReleaseQueue.getAccountsQuantity())
2150        }
2151
2152        pub fun allocateDailyReleaseToAddresses(roundsRef: Capability<&MoxyReleaseRounds.Rounds>, addresses: [Address]) {
2153            for address in addresses {
2154                self.allocateDailyReleaseNowToAddress(roundsRef: roundsRef, address: address)
2155            }
2156        }
2157
2158        pub fun allocateDailyReleaseNowToAddress(roundsRef: Capability<&MoxyReleaseRounds.Rounds>, address: Address) {
2159//            let roundManager = roundsRef.borrow()!
2160            let roundManager= self.getRoundsCapability().borrow()!
2161
2162            if (!roundManager.hasRoundRelease(address: address)) {
2163                log("Address is not participating on round release process")
2164                return
2165            }
2166
2167            let membershipFeeReceiver = getAccount(address)
2168                            .getCapability(MoxyToken.moxyTokenReceiverPath)
2169                            .borrow<&{FungibleToken.Receiver}>()
2170                            ?? panic("Unable to borrow receiver reference")
2171
2172            let feeRemaining = self.getMembershipFeeMOXYRemainingFor(address: address)
2173
2174            let feeVault <- roundManager.allocateDailyReleaseNowToAddress(address: address, feeRemaining: feeRemaining)
2175            let vaultDeducted <- self.collectMembershipFee(address: address, vault: <-feeVault)
2176            membershipFeeReceiver.deposit(from: <- vaultDeducted)            
2177        }
2178
2179        pub fun getAccountAddress(start: Int, end: Int): [Address] {
2180            pre {
2181                start <= end : "Start parameter should be lower than end"
2182            }
2183            let total = self.accounts.length
2184            var e = end
2185            if (end >= total) {
2186                e = total
2187            }
2188            return self.accounts.keys.slice(from: start, upTo: e)
2189        }
2190
2191        pub fun getTotalAccounts(): Int {
2192            return self.accounts.length
2193        }
2194
2195        access(self) fun getRoundsCapability(): Capability<&MoxyReleaseRounds.Rounds> {
2196            return MoxyClub.account.getCapability<&MoxyReleaseRounds.Rounds>(MoxyReleaseRounds.moxyRoundsPrivate)
2197        }
2198
2199        pub fun getTGEDate(): UFix64 {
2200            let rounds = self.getRoundsCapability().borrow()!
2201            return rounds.tgeDate
2202        }
2203
2204        pub fun releaseStarted(): Bool {
2205            return self.isReleaseStarted
2206        }
2207
2208        init() {
2209            self.accounts <- {}
2210
2211            self.isReleaseStarted = false
2212  
2213            // Fee amount in MOX
2214            self.feeAmountInFLOW = 0.000001
2215            self.moxyToFLOWValue = 0.02076124 //Estimated FLOW to USD: 2.89, MOXY to USD: 0.06
2216            self.moxyToUSDValue = 0.06        //Public IDO value at TGE
2217
2218            // BURN on transaction fees: 95% to Moxy and its affiliates/partners, 
2219            // and 5% BURN to PLAY token to further strengthen Proof of Play
2220            self.percentFeeToPLAY = 0.05
2221            
2222            self.treasuryAddress = nil
2223            self.associationAddress = nil
2224
2225            self.totalEarnedFromMVHoldings = 0.0
2226            self.earnedFromMVHoldings = {}
2227            self.totalPaidDueDailyActivity = 0.0
2228            self.paidDueDailyActivity = {}
2229            
2230            self.totalPaidDueMVConversion = 0.0
2231            self.paidDueMVConversion = {}
2232
2233            // Maximum percentage for MV Holdings rewards for Locked MV
2234            // Initial value is 4%
2235            self.maximumPercentageLockedMV = 4.0
2236
2237            self.mvToMOXWithdrawalDays = 90
2238
2239            self.roundReleaseQueue <- MoxyProcessQueue.createNewQueue()
2240            self.mvHoldingsQueue <- MoxyProcessQueue.createNewQueue()
2241            self.proofOfPlayQueue <- MoxyProcessQueue.createNewQueue()
2242            self.mvToMOXConversionQueue <- MoxyProcessQueue.createNewQueue()
2243
2244            // Proof of Play Weigth
2245            self.popScoreWeight = 30.0
2246            self.popDailyScoreWeight = 70.0
2247            self.popPlayDonationWeight = 0.0
2248
2249            // Proof of play anual percenage
2250            self.proofOfPlayPercentage = 1.0
2251
2252            // Play and Earn event accounts dictionary
2253            self.playAndEarnEventAccounts <- {}
2254            
2255            // Moxy Controlled Accounts dictionary
2256            self.moxyControlledAccounts = {}
2257
2258            // Launch fee value is in MOXY
2259            self.launchFee = 10.0
2260
2261            //Masterclass prices in MOXY
2262            self.masterclassPrices = {}
2263
2264            //Games prices in MOXY
2265            self.gamesPrices = {}
2266
2267        }
2268
2269        destroy() {
2270            destroy self.accounts
2271            destroy self.playAndEarnEventAccounts
2272            destroy self.roundReleaseQueue
2273            destroy self.mvHoldingsQueue
2274            destroy self.proofOfPlayQueue
2275            destroy self.mvToMOXConversionQueue
2276        }
2277
2278    }
2279
2280    access(self) fun getMoxyEcosystemCapability(): &MoxyEcosystem {
2281        return self.account
2282            .getCapability(self.moxyEcosystemPrivate)
2283            .borrow<&MoxyClub.MoxyEcosystem>()!
2284    }
2285
2286    pub fun getMoxyEcosystemPublicCapability(): &MoxyEcosystem{MoxyEcosystemInfoInterface} {
2287        return self.account
2288                .getCapability(MoxyClub.moxyEcosystemInfoPublic)
2289                .borrow<&MoxyClub.MoxyEcosystem{MoxyEcosystemInfoInterface}>()!
2290    }
2291    
2292    pub resource interface MoxyEcosystemInfoInterface {
2293        pub fun isMoxyAccount(address: Address): Bool
2294        pub fun isPlayAndEarnEventAccount(address: Address): Bool
2295
2296        pub fun getTGEDate(): UFix64
2297        pub fun getTreasuryAddress(): Address?
2298        pub fun getAssociationAddress(): Address?
2299
2300        pub fun hasMembershipFeePendingFor(address: Address): Bool
2301        pub fun getMembershipFeeRemainingFor(address: Address): UFix64
2302        pub fun getMembershipFeeFor(address: Address): UFix64
2303        pub fun getMembershipFeeMOXYRemainingFor(address: Address): UFix64
2304        pub fun getMembershipFeeMOXYFor(address: Address): UFix64
2305        pub fun getMembershipFeeMOXY(): UFix64
2306
2307        pub fun getTotalEarnedFromMVHoldings(): UFix64
2308        pub fun getEarnedFromMVHoldingsForTime(timestamp: UFix64): UFix64
2309        pub fun getEarnedFromMVHoldingsForTimeRange(from: UFix64, to: UFix64): {UFix64:UFix64}
2310        pub fun getEarnedFromMVHoldingsFor(address: Address): {UFix64: UFix64}
2311        pub fun getEarnedFromMVHoldingsForAddressTime(address: Address, timestamp: UFix64): UFix64 
2312        pub fun getEarnedFromMVHoldingsForAddressTimeRange(address: Address, from: UFix64, to: UFix64): {UFix64:UFix64}
2313        pub fun getTotalEarnedFromMVHoldingsFor(address: Address): UFix64
2314
2315        pub fun getTotalPaidDueDailyActivity(): UFix64
2316        pub fun getPaidDueDailyActivityForTime(timestamp: UFix64): UFix64
2317        pub fun getPaidDueDailyActivityForTimeRange(from: UFix64, to: UFix64): {UFix64:UFix64}
2318        pub fun getPaidDueDailyActivityFor(address: Address): {UFix64: UFix64}
2319        pub fun getPaidDueDailyActivityForAddressTime(address: Address, timestamp: UFix64): UFix64
2320        pub fun getPaidDueDailyActivityForAddressTimeRange(address: Address, from: UFix64, to: UFix64): {UFix64:UFix64}
2321        pub fun getTotalPaidDueDailyActivityFor(address: Address): UFix64
2322        
2323        pub fun getTotalPaidDueMVConversion(): UFix64
2324        pub fun getTotalPaidDueMVConversionFor(address: Address): UFix64
2325        pub fun getPaidDueMVConversionForTime(timestamp: UFix64): UFix64
2326        pub fun getPaidDueMVConversionForTimeRange(from: UFix64, to: UFix64): {UFix64:UFix64}
2327        
2328        pub fun getMOXYFeeAmount(): UFix64
2329        pub fun getMOXYToFLOWValue(): UFix64
2330        pub fun getMOXYToUSDValue(): UFix64
2331
2332        pub fun areMoxyControlledWalletsAllocated(): Bool
2333        
2334        pub fun getProcessRoundsRemainings(): Int
2335        pub fun getProcessMVHoldingsRemainings(): Int 
2336        pub fun getProcessProofOfPlayRemainings(): Int 
2337        pub fun getMVToMOXConversionRemainings(): Int 
2338        pub fun getProcessRoundsAccountsQuantity(): Int
2339        pub fun getProcessMVHoldingsAccountsQuantity(): Int 
2340        pub fun getProcessProofOfPlayAccountsQuantity(): Int 
2341        pub fun getMVToMOXConversionAccountsQuantity(): Int 
2342        pub fun getProcessRoundsStatus(): MoxyProcessQueue.CurrentRunStatus
2343        pub fun getProcessMVHoldingsStatus(): MoxyProcessQueue.CurrentRunStatus
2344        pub fun getProcessProofOfPlayStatus(): MoxyProcessQueue.CurrentRunStatus
2345        pub fun getMVToMOXConversionStatus(): MoxyProcessQueue.CurrentRunStatus
2346        pub fun getProcessMVHoldingsRunSize(): Int
2347        pub fun getMVToMOXConversionRunSize(): Int
2348        pub fun getProcessRoundsRunSize(): Int 
2349        pub fun getProcessProofOfPlayRunSize(): Int 
2350        pub fun getProcessRoundsRemainingAddresses(): [Address]
2351        pub fun getProcessMVHoldingsRemainingAddresses(): [Address]
2352        pub fun getProcessProofOfPlayRemainingAddresses(): [Address]
2353        pub fun getMVToMOXConversionRemainingAddresses(): [Address]
2354        
2355        pub fun releaseStarted(): Bool
2356
2357        pub fun getLaunchFee(): UFix64
2358        pub fun getMasterclassPrice(classId: String): UFix64?
2359        pub fun getMasterclassPrices(): {String: UFix64}
2360
2361        pub fun getGamePrice(gameId: String): UFix64?
2362        pub fun getGamesPrices(): {String: UFix64}
2363        pub fun getProofOfPlayPercentage(): UFix64
2364
2365        pub fun isGoodStandingOnOpenEvents(address: Address): Bool
2366        pub fun getMVConverterStorageIdentifier(timestamp: UFix64): String
2367
2368        pub fun getAccountAddress(start: Int, end: Int): [Address]
2369        pub fun getTotalAccounts(): Int
2370    }
2371
2372    pub resource interface MVToMOXYRequestsInfoInterface {
2373        pub fun getMVToMOXtRequests(address: Address): {UFix64: MVToMOXRequestInfo}
2374    }
2375
2376    pub resource interface MoxyEcosystemOperations {
2377        pub fun convertLockedMOXYToLockedMV(request: @LockedMoxyToken.ConversionRequest, address: Address)
2378        pub fun convertMOXYToMV(address: Address, vault: @FungibleToken.Vault)
2379        pub fun payGamePrice(gameId: String, fromVault: @FungibleToken.Vault, address: Address)
2380        pub fun payLaunchFee(fromVault: @FungibleToken.Vault, address: Address)
2381        pub fun payMasterclassPrice(classId: String, fromVault: @FungibleToken.Vault, address: Address)
2382        pub fun depositToPlayAndEarnVault(address: Address, vault: @FungibleToken.Vault)
2383        pub fun setPlayAndEarnRefTo(address: Address, vaultRef: Capability<&FungibleToken.Vault>)
2384        pub fun setMVToMOXYConversionsRefTo(address: Address, conversionsRef: Capability<&{UFix64:MoxyClub.MVToMOXYConverter}>)
2385        pub fun transferMOXY(fromVault: @FungibleToken.Vault, to: Address)
2386        pub fun createMVToMOXYConverter(mvConverter: @MoxyVaultToken.MVConverter, conversionRequest: @LockedMoxyToken.ConversionRequest): @MVToMOXYConverter 
2387        pub fun registerMVToMOXConversion(address:Address, timestamp: UFix64, amount: UFix64)
2388        pub fun addMoxyAccount(address: Address)
2389    }
2390
2391    pub let moxyEcosystemStorage: StoragePath
2392    pub let moxyEcosystemPrivate: PrivatePath
2393    pub let moxyEcosystemInfoPublic: PublicPath
2394
2395    pub let mvToMOXYRequestsInfoPublic: PublicPath
2396
2397    pub fun version(): String {
2398        return "2.1.32"
2399    }
2400
2401    // Initialize contract
2402    init(){
2403        // Initial membership fee amount in USD, calculated with a value of 1 MOXY 0.06 USD
2404        self.membershipFee = 0.30
2405
2406        // Moxy Ecosystem initialization
2407        let moxyEcosystem <- create MoxyEcosystem()
2408
2409        self.moxyEcosystemStorage = /storage/moxyEcosystem
2410        self.moxyEcosystemPrivate = /private/moxyEcosystem
2411        self.moxyEcosystemInfoPublic = /public/moxyEcosystemInfo
2412
2413        self.mvToMOXYRequestsInfoPublic = /public/mvToMOXYRequestsInfoPublic
2414
2415        self.account.save(<-moxyEcosystem, to: self.moxyEcosystemStorage)
2416        self.account.link<&MoxyEcosystem>(self.moxyEcosystemPrivate, target: self.moxyEcosystemStorage)
2417
2418        self.account.link<&MoxyEcosystem{MVToMOXYRequestsInfoInterface}>(
2419            self.mvToMOXYRequestsInfoPublic,
2420            target: self.moxyEcosystemStorage
2421        )
2422
2423        self.account.link<&MoxyEcosystem{MoxyEcosystemInfoInterface}>(
2424            self.moxyEcosystemInfoPublic ,
2425            target: self.moxyEcosystemStorage
2426        )
2427
2428        self.account.link<&MoxyClub.MoxyEcosystem{MoxyClub.MoxyEcosystemOperations}>(
2429                /public/moxyEcosystemOperationsPublic, 
2430                target: MoxyClub.moxyEcosystemStorage)!
2431
2432        self.account.link<&MoxyReleaseRounds.Rounds{MoxyReleaseRounds.MoxyRoundsCreator}>(
2433                /public/moxyRoundsCreatorPublic, 
2434                target: MoxyReleaseRounds.moxyRoundsStorage)!
2435
2436    }
2437}
2438 
2439