Smart Contract
MoxyClub
A.123cb47fe122f6e3.MoxyClub
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