Smart Contract

MoxyReleaseRounds

A.123cb47fe122f6e3.MoxyReleaseRounds

Deployed

1d ago
Feb 26, 2026, 09:44:38 PM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import LockedMoxyToken from 0x123cb47fe122f6e3
3import LockedMoxyVaultToken from 0x123cb47fe122f6e3
4import MoxyToken from 0x123cb47fe122f6e3
5import MoxyVaultToken from 0x123cb47fe122f6e3
6import LinearRelease from 0x123cb47fe122f6e3
7import MoxyProcessQueue from 0x123cb47fe122f6e3
8import MoxyData from 0x123cb47fe122f6e3
9 
10
11pub contract MoxyReleaseRounds {
12
13    pub event RoundAdded(name: String)
14
15    pub event AccountAddedToRound(round: String, address:Address, amount: UFix64)
16    pub event MOXYLockedTokensReleasedTo(round: String, address: Address, amount: UFix64)
17
18    // Unlock tokens events
19    pub event UnlockedMOXYTokenForLinearReleases(address: Address, amount: UFix64)
20    pub event UnlockedMVTokenForLinearReleases(address: Address, amount: UFix64)
21    
22    // Rounds user consent to participate on rounds releases
23    pub event AccountAlreadyAcceptedRoundParticipation(address: Address, timestamp: UFix64)
24    pub event AccountAcceptedRoundsParticipation(address: Address, timestamp: UFix64)
25
26    // Events for rounds allocation process
27    pub event StartingAllocationDailyReleaseRounds(timestamp: UFix64, accountsToProcess: Int)
28    pub event FinishingAllocationDailyReleaseRounds(timestamp: UFix64, accountsProcessed: Int)
29    pub event NoAmountToAllocateInRoundRelease(roundId: String, address: Address, timestamp: UFix64)
30    pub event RoundAllocationPerformed(roundId: String, address: Address, totalReleased: UFix64, timestamp: UFix64)
31
32    pub struct ParticipantRoundInfo {
33        pub let address: Address
34        pub let roundId: String
35        pub let amount: UFix64
36        pub let amountReleased: UFix64
37
38        init(address: Address, roundId: String, amount: UFix64, amountReleased: UFix64 ) {
39            self.address = address
40            self.roundId = roundId
41            self.amount = amount
42            self.amountReleased = amountReleased
43        }
44    }
45
46    pub struct RoundInfo {
47        pub let id: String
48        pub let type: String
49        pub let name: String
50        pub let initialRelease: UFix64
51        pub let lockTime: Int 
52        pub let months: Int
53        pub let tgeDate: UFix64
54        pub let endDate: UFix64
55        pub let totalIncorporated: UFix64
56        pub let totalAllocated: UFix64
57        pub let totalReleased: UFix64
58        pub let unlockPercentageAtTGE: UFix64
59        pub let unlockPercentageAfterLockTime: UFix64
60        pub let isReleaseStarted: Bool
61        pub let allocateBeforeTGE: Bool
62
63        init(id: String, type: String, name: String, initialRelease: UFix64, 
64            lockTime: Int, months: Int, days: Int,
65            tgeDate: UFix64, endDate: UFix64, totalIncorporated: UFix64, totalAllocated: UFix64, totalReleased: UFix64,
66            unlockPercentageAtTGE: UFix64, unlockPercentageAfterLockTime: UFix64,
67            isReleaseStarted: Bool, allocateBeforeTGE: Bool) {
68            
69            self.id = id
70            self.type = type
71            self.name = name
72            self.initialRelease = initialRelease
73            self.lockTime = lockTime
74            self.months = months
75            self.tgeDate = tgeDate
76            self.endDate = endDate
77            self.totalIncorporated = totalIncorporated
78            self.totalAllocated = totalAllocated
79            self.totalReleased = totalReleased
80            self.unlockPercentageAtTGE = unlockPercentageAtTGE
81            self.unlockPercentageAfterLockTime = unlockPercentageAfterLockTime
82            self.isReleaseStarted = isReleaseStarted
83            self.allocateBeforeTGE = allocateBeforeTGE
84        }
85    }
86
87
88    pub struct RoundReleaseInfo {
89        pub var amount: UFix64
90
91        // amountReleased is the total amount that is released in this round
92        pub var amountReleased: UFix64
93
94        // date of the last release performed
95        pub var lastReleaseDate: UFix64
96
97        init(amount: UFix64, amountReleased: UFix64, lastReleaseDate: UFix64) {
98            self.amount = amount
99            self.amountReleased = amountReleased
100            self.lastReleaseDate = lastReleaseDate
101        }
102
103    }
104
105    pub resource RoundRelease {
106        // amount is the total amount that the user will receive in this round
107        pub var amount: UFix64
108
109        pub var linearReleases: [LinearRelease.LinearSchedule]
110
111        // amountReleased is the total amount that is released in this round
112        pub var amountReleased: UFix64
113
114        // date of the last release performed
115        pub var lastReleaseDate: UFix64
116
117        pub var isAmountAtTGEPaid: Bool
118        pub var isAmountAfterLockPaid: Bool
119
120        pub fun getRoundReleaseInfo(): RoundReleaseInfo {
121            var i = 0
122            while (i < self.linearReleases.length) {
123                i = i + 1
124            }
125            return RoundReleaseInfo(amount: self.amount, amountReleased: self.amountReleased, lastReleaseDate: self.lastReleaseDate)
126        }
127
128        pub fun getAllocationRemaining(): UFix64 {
129            return self.amount - self.amountReleased
130        }
131
132        pub fun getTotalToAllocateNow(): UFix64 {
133            var total = 0.0
134            var i = 0
135            while (i < self.linearReleases.length) {
136                total = total + self.linearReleases[i].getTotalToUnlock()
137                i = i + 1
138            }
139            return total
140        }
141
142        pub fun payLinearReleases(): UFix64 {
143            var total = 0.0
144            var i = 0
145            while (i < self.linearReleases.length) {
146                total = total + self.linearReleases[i].getTotalToUnlock()
147                self.linearReleases[i].updateLastReleaseDate()
148                i = i + 1
149            }
150            self.amountReleased = self.amountReleased + total
151            self.lastReleaseDate = getCurrentBlock().timestamp
152            return total
153        }
154
155        pub fun addLinearRelease(linearRelease: LinearRelease.LinearSchedule) {
156            self.linearReleases.append(linearRelease)
157        }
158
159        pub fun getAmount(): UFix64 {
160            return self.amount
161        }
162
163        pub fun increaseAmount(amount: UFix64) {
164            self.amount = self.amount + amount
165        }
166
167        pub fun mergeWith(amount: UFix64, linearRelease: LinearRelease.LinearSchedule) {
168            self.amount = self.amount + amount
169            self.addLinearRelease(linearRelease: linearRelease)
170        }
171
172        pub fun setStartDate(timestamp: UFix64) {
173            self.lastReleaseDate = timestamp
174            var i = 0
175            while (i < self.linearReleases.length) {
176                self.linearReleases[i].setStartDate(timestamp: timestamp)
177               i = i + 1
178            }
179
180        }
181
182        pub fun getAmountAtTGEToPay(): UFix64 {
183            var total = 0.0
184            var i = 0
185            while (i < self.linearReleases.length) {
186                total = total + self.linearReleases[i].getAmountAtTGEToPay()
187                i = i + 1
188            }
189            return total
190        }
191
192        pub fun getAmountAtTGE(): UFix64 {
193            var total = 0.0
194            var i = 0
195            while (i < self.linearReleases.length) {
196                total = total + self.linearReleases[i].getAmountAtTGE()
197                i = i + 1 
198            }
199            return total
200        }
201
202        pub fun getAmountAtTGEFor(amount: UFix64): UFix64 {
203            var total = 0.0
204            var i = 0
205            while (i < self.linearReleases.length && total == 0.0) {
206                if (self.linearReleases[i].totalAmount == amount) {
207                    total = total + self.linearReleases[i].getAmountAtTGE()
208                }
209                i = i + 1 
210            }
211            return total
212        }
213
214        pub fun getAmountAfterUnlockToPay(): UFix64 {
215            var total = 0.0
216            var i = 0
217            while (i < self.linearReleases.length) {
218                total = total + self.linearReleases[i].getAmountAfterUnlockToPay()
219                i = i + 1
220            }
221            return total
222        }
223
224        pub fun getAmountAfterUnlock(): UFix64 {
225            var total = 0.0
226            var i = 0
227            while (i < self.linearReleases.length) {
228                total = total + self.linearReleases[i].getAmountAfterUnlock()
229                i = i + 1
230            }
231            return total
232        }
233
234        pub fun getAmountAfterUnlockFor(amount: UFix64): UFix64 {
235            var total = 0.0
236            var i = 0
237            while (i < self.linearReleases.length && total == 0.0) {
238                if (self.linearReleases[i].totalAmount == amount) {
239                    total = total + self.linearReleases[i].getAmountAfterUnlock()
240                }
241                i = i + 1
242            }
243            return total
244        }
245
246        pub fun getDailyAmountToPay(): UFix64 {
247            var total = 0.0
248            var i = 0
249            while (i < self.linearReleases.length) {
250                total = total + self.linearReleases[i].getDailyAmountToPay()
251                i = i + 1
252            }
253            return total
254        }
255
256        pub fun getDailyAmount(): UFix64 {
257            var total = 0.0
258            var i = 0
259            while (i < self.linearReleases.length) {
260                total = total + self.linearReleases[i].getDailyAmount()
261                i = i + 1
262            }
263            return total
264        }
265
266        pub fun getTotalDailyAmount(): UFix64 {
267            var total = 0.0
268            var i = 0
269            while (i < self.linearReleases.length) {
270                total = total + self.linearReleases[i].getTotalDailyAmount()
271                i = i + 1
272            }
273            return total
274        }
275
276        pub fun getTotalDailyAmountFor(amount: UFix64): UFix64 {
277            var total = 0.0
278            var i = 0
279            while (i < self.linearReleases.length && total == 0.0) {
280                if (self.linearReleases[i].totalAmount == amount) {
281                    total = total + self.linearReleases[i].getTotalDailyAmount()
282                }
283                i = i + 1
284            }
285            return total
286        }
287
288        pub fun getFirstLinearRelease(): LinearRelease.LinearSchedule {
289            return self.linearReleases.removeFirst()
290        }
291
292        init(amount: UFix64, linearRelease: LinearRelease.LinearSchedule) {
293            self.amount = amount
294            self.lastReleaseDate = linearRelease.tgeDate
295            self.linearReleases = [linearRelease]
296            self.amountReleased = 0.0
297            self.isAmountAtTGEPaid = false
298            self.isAmountAfterLockPaid = false
299        }
300
301    }
302
303    pub resource RoundReleases: RoundReleasesInfo {
304        access(contract) let releases: @{String: RoundRelease}
305        pub let lockedMOXYVault: Capability<&LockedMoxyToken.LockedVault>
306        pub let lockedMVVault: Capability<&LockedMoxyVaultToken.LockedVault>
307
308        pub fun setAddress(roundId: String, roundRelease: @RoundRelease) {
309            if (self.releases[roundId] == nil) {
310                // Add to round
311                self.releases[roundId] <-! roundRelease
312            } else {
313                // Update round adding the round release info increasing
314                // amount for an address that already has a release round
315                let amount = roundRelease.amount
316                let linearRelease = roundRelease.getFirstLinearRelease()  
317
318                let release <- self.releases.remove(key: roundId)!
319                release.mergeWith(amount: amount, linearRelease: linearRelease)
320                
321                let old <- self.releases[roundId] <- release
322                destroy old
323                destroy roundRelease
324            }
325        }
326
327        pub fun setStartDate(timestamp: UFix64) {
328            for roundId in self.releases.keys {
329                self.releases[roundId]?.setStartDate(timestamp: timestamp)!
330            }
331        }
332        
333        pub fun payLinearRelease(roundId: String): UFix64 {
334            let amount = self.releases[roundId]?.payLinearReleases()!
335
336            return amount
337        }
338        
339        // RoundReleases
340        pub fun allocateDailyReleaseToNow(feeRemaining: UFix64): @FungibleToken.Vault {
341            // Unlock MOXY tokens
342            let lockedMOXYVault = self.lockedMOXYVault.borrow()!
343            let moxyVault <- lockedMOXYVault.withdrawUnlocked()
344            
345            let address = lockedMOXYVault.owner!.address
346
347            let recipient = getAccount(address)
348            let moxyVaultRef = recipient.getCapability(MoxyToken.moxyTokenReceiverPath)
349                .borrow<&{FungibleToken.Receiver}>()
350                ?? panic("Could not borrow receiver reference to the recipient's Vault")
351            let moxyAmount = moxyVault.balance
352            var feeToDeduct = feeRemaining
353            if (feeToDeduct > moxyAmount) {
354                feeToDeduct = moxyAmount
355            }
356            let vaultFee <- moxyVault.withdraw(amount: feeToDeduct)
357
358            moxyVaultRef.deposit(from: <-moxyVault)
359            emit UnlockedMOXYTokenForLinearReleases(address: address, amount: moxyAmount)
360
361            // Unlock MV tokens
362            let lockedMVVault = self.lockedMVVault.borrow()!
363            let mvVault <- lockedMVVault.withdrawUnlocked()
364            
365            let mvVaultRef = recipient.getCapability(MoxyVaultToken.moxyVaultTokenReceiverTimestampPath)
366                .borrow<&{MoxyVaultToken.ReceiverInterface}>()
367                ?? panic("Could not borrow receiver reference to the recipient's Vault")
368            let mvAmount = mvVault.balance
369
370            mvVaultRef.depositAmount(from: <-mvVault)
371            emit UnlockedMOXYTokenForLinearReleases(address: address, amount: mvAmount)
372
373            return <-vaultFee
374        }
375
376        pub fun getRoundReleaseInfo(roundId: String): RoundReleaseInfo {
377            return self.releases[roundId]?.getRoundReleaseInfo()!
378        }
379
380        pub fun getAmountFor(roundId: String ): UFix64 {
381            return self.releases[roundId]?.amount!
382        }
383
384        pub fun getAmountReleasedFor(roundId: String ): UFix64 {
385            return self.releases[roundId]?.amountReleased!
386        }
387
388        pub fun getTotalToAllocateNowFor(roundId: String ): UFix64 {
389            return self.releases[roundId]?.getTotalToAllocateNow()!
390        }
391
392        pub fun getAmountsDictFor(roundId: String, amount: UFix64 ): {String: UFix64} {
393            let amounts = {
394                            "atTGE": self.releases[roundId]?.getAmountAtTGEFor(amount: amount)!,
395                            "afterUnlock": self.releases[roundId]?.getAmountAfterUnlockFor(amount: amount)!,
396                            "daily": self.releases[roundId]?.getTotalDailyAmountFor(amount: amount)!
397                         }
398            return amounts
399        }
400
401        init(lockedMOXYVault: Capability<&LockedMoxyToken.LockedVault>, lockedMVVault: Capability<&LockedMoxyVaultToken.LockedVault>) {
402            self.releases <- {}
403            self.lockedMOXYVault = lockedMOXYVault
404            self.lockedMVVault = lockedMVVault
405        }
406
407        destroy() {
408            destroy self.releases
409        }
410    }
411
412    pub resource Round {
413        pub let id: String
414        pub let type: String
415        pub let name: String
416        pub let initialRelease: UFix64
417        pub let lockTime: Int 
418        pub let months: Int
419        pub var tgeDate: UFix64
420        access(self) var accounts: {Address: Capability<&RoundReleases>}
421        pub var totalAllocated: UFix64
422        pub var totalIncorporated: UFix64
423        pub var totalReleased: UFix64
424        pub var unlockPercentageAtTGE: UFix64
425        pub var unlockPercentageAfterLockTime: UFix64
426        pub var isReleaseStarted: Bool
427        pub let allocateBeforeTGE: Bool
428        pub var allocationQueue: @MoxyProcessQueue.Queue
429
430        pub fun getRoundInfo(): RoundInfo {
431            return RoundInfo(id: self.id, type: self.type, name: self.name,
432                        initialRelease: self.initialRelease, 
433                        lockTime: self.lockTime, months: self.months, 
434                        days: Int(self.getDays()), tgeDate: self.tgeDate, endDate: self.getEndDate(),
435                        totalIncorporated: self.totalIncorporated,
436                        totalAllocated: self.totalAllocated, 
437                        totalReleased: self.totalReleased, 
438                        unlockPercentageAtTGE: self.unlockPercentageAtTGE, 
439                        unlockPercentageAfterLockTime: self.unlockPercentageAfterLockTime,
440                        isReleaseStarted: self.isReleaseStarted, 
441                        allocateBeforeTGE: self.allocateBeforeTGE)
442        }
443
444        pub fun getRoundReleaseInfo(_ address: Address): RoundReleaseInfo? {
445            if (self.accounts[address] == nil) {
446               return nil
447            }
448            return self.accounts[address]!.borrow()!.getRoundReleaseInfo(roundId: self.id)
449        }
450
451        pub fun getAmountFor(address: Address): UFix64 {
452            if (self.accounts[address] == nil) {
453                log("(amount) Address not found: ".concat(address.toString()).concat(" round: ").concat(self.id))
454                return 0.0
455            }
456            return self.accounts[address]!.borrow()!.getAmountFor(roundId: self.id)
457        }
458
459        pub fun getAmountReleasedFor(address: Address): UFix64 {
460            if (self.accounts[address] == nil) {
461                log("(amount released) Address not found: ".concat(address.toString()))
462                return 0.0
463            }
464            return self.accounts[address]!.borrow()!.getAmountReleasedFor(roundId: self.id)
465        }
466
467        pub fun getTotalToAllocateNowFor(address: Address): UFix64 {
468            return self.accounts[address]!.borrow()!.getTotalToAllocateNowFor(roundId: self.id)
469        }
470
471        pub fun getAmountsDictFor(address: Address, amount: UFix64): {String: UFix64} {
472            return self.accounts[address]!.borrow()!.getAmountsDictFor(roundId: self.id, amount: amount)
473        }
474
475        pub fun getAccounts(): {Address: ParticipantRoundInfo} {
476            let accounts: {Address: ParticipantRoundInfo} = {}
477  
478            for address in self.accounts.keys {
479                let amount =  self.getAmountFor(address: address)
480                let amountReleased =  self.getAmountReleasedFor(address: address)
481                accounts[address] = ParticipantRoundInfo(address: address, roundId: self.id, amount: amount, amountReleased: amountReleased)
482            }
483            return accounts;
484        }
485
486        pub fun getRoundAddresses(): [Address] {
487            return self.accounts.keys
488        }
489
490        pub fun getAllocationRemaining(): UFix64 {
491            return (self.initialRelease + self.totalIncorporated) - self.totalAllocated
492        }
493
494        pub fun isReadyToStartRelease(): Bool {
495            if (self.allocateBeforeTGE == false) {
496                // Can start because is not required full allocation before TGE (i.e presale round)
497                return true
498            }
499            return self.getAllocationRemaining() <= 0.0
500        }
501
502        pub fun isRoundStarted(): Bool {
503            return self.isReleaseStarted
504        }
505
506        pub fun canAllocateAfterTGE(): Bool {
507            return !self.allocateBeforeTGE
508        }
509
510        pub fun isInitialAllocationFinished(): Bool {
511            return self.hasQueueFinished() || 
512                    (self.canAllocateAfterTGE() && self.allocationQueue.isEmptyQueue())
513        }
514
515        //Round.setAddress
516        access(contract) fun setAddress(address: Address, amount: UFix64, releasesRef: Capability<&RoundReleases>) {
517            self.setAddressOn(address: address, amount: amount, releasesRef: releasesRef, timestamp: self.tgeDate)
518        }
519
520        access(contract) fun setAddressOn(address: Address, amount: UFix64, releasesRef: Capability<&RoundReleases>, timestamp: UFix64) {
521            // Adding reference to address
522            if (self.accounts[address] == nil) {
523                self.accounts[address] = releasesRef
524            }
525
526            let roundReleases = self.accounts[address]!.borrow()!
527            let linearRelease = self.generateScheduleForDate(timestamp: timestamp, amount: amount)
528            let roundRelease <- create RoundRelease(amount: amount, linearRelease: linearRelease)
529            
530            roundReleases.setAddress(roundId: self.id, roundRelease: <-roundRelease)
531            self.allocationQueue.addAccount(address: address)
532            
533            self.totalAllocated = self.totalAllocated + amount
534        }
535
536        //Round.incorporateAddress
537        access(contract) fun incorporateAddress(address: Address, amount: UFix64, releasesRef: Capability<&RoundReleases>, startTime: UFix64){
538            let time0000 = MoxyData.getTimestampTo0000(timestamp: startTime)
539            self.setAddressOn(address: address, amount: amount, releasesRef: releasesRef, timestamp: time0000)
540            self.totalIncorporated = self.totalIncorporated + amount
541        }
542
543        pub fun isAddressInRound(address: Address): Bool { 
544            return self.accounts.containsKey(address)
545        }
546
547        // Round
548        pub fun allocateDailyReleaseToNow(address: Address) {
549            // Get the amount from the last release to a given date
550            let now = getCurrentBlock().timestamp
551            let amountToAllocate = self.getTotalToAllocateNowTo(address: address)
552            
553            if (amountToAllocate <= 0.0) {
554                if (self.isInLockedPeriod()) {
555                    log("Round: ".concat(self.id).concat(" is in lock period."))
556                } else {
557                    log("Warning - No amount to allocate on Round: ".concat(self.id).concat(" Posible cause process already run."))
558                }
559                emit NoAmountToAllocateInRoundRelease(roundId: self.id, address: address, timestamp: now)
560                return
561            }
562            let totalToRelease = self.accounts[address]!.borrow()!.payLinearRelease(roundId: self.id)
563            
564            self.totalReleased = self.totalReleased + totalToRelease
565            emit RoundAllocationPerformed(roundId: self.id, address: address, totalReleased: totalToRelease, timestamp: now)
566        }
567
568        pub fun getReleaseRatioFor(address: Address): UFix64 {
569            if (self.totalAllocated <= 0.0) {
570                panic("Round does not have allocations yet. Release ratio could not be calculated")
571            }
572            
573            let amount = self.getAmountFor(address: address)
574            return amount / self.totalAllocated
575        }
576
577        pub fun getTotalToAllocateNowTo(address: Address): UFix64 {
578            return self.getTotalToAllocateNowFor(address: address)
579        }
580
581        pub fun getDailyAllocationsFrom(from: UFix64, to: UFix64): [UFix64] {
582            let from0000 = MoxyData.getTimestampTo0000(timestamp: from)
583            let to0000 = MoxyData.getTimestampTo0000(timestamp: to)
584            let days = self.getDaysFromTo(from: from0000, to: to0000)
585            let amount = self.dailyAllocationAmount()
586            
587            return [from0000, to0000, UFix64(days), amount, self.tgeDate, self.getEndDate()]
588        }
589
590        pub fun getDailyAllocationsFromToAddress(address: Address, from: UFix64, to: UFix64): [UFix64]? {
591            let allocationInfo = self.getDailyAllocationsFrom(from: from, to: to)
592            let amount =  self.getAmountFor(address: address)
593            let amountReleased =  self.getAmountReleasedFor(address: address)
594            
595            allocationInfo.append(amount)
596            allocationInfo.append(amountReleased)
597            return allocationInfo
598        }
599
600        pub fun getDaysFromTo(from: UFix64, to: UFix64): UInt64 {
601            let from0000 = MoxyData.getTimestampTo0000(timestamp: from)
602            let to0000 = MoxyData.getTimestampTo0000(timestamp: to)
603
604            return UInt64((to0000 - from0000) / 86400.0)
605        }
606
607        pub fun startReleaseAddress(address: Address, initialVault: @FungibleToken.Vault) {
608            pre {
609                !self.isReleaseStarted : "Release is already started."
610            }
611
612            var residualReceiver: &{LockedMoxyToken.Receiver}? = nil
613            var unlockDate = 0.0
614            let days = self.getDays()
615
616            let amountsDict = self.getAmountsDictFor(address: address, amount: initialVault.balance)!
617            let amount = initialVault.balance
618            
619            var am01 = amountsDict["atTGE"]!
620            var am02 = amountsDict["afterUnlock"]!
621            var am03 = amountsDict["daily"]!
622
623            let amountAtTGEVault <- initialVault.withdraw(amount: am01) as! @MoxyToken.Vault
624            let amountAfterUnlockVault <- initialVault.withdraw(amount: am02) as! @MoxyToken.Vault
625            let amountVault <- initialVault.withdraw(amount: am03) as! @MoxyToken.Vault
626            // Deposit residual amount if any
627            amountVault.deposit(from: <-initialVault)
628
629            // Get the locked recipient Vault
630            let recipient = getAccount(address)
631            let receiverRef = recipient.getCapability(MoxyToken.moxyTokenLockedReceiverPath)
632                    .borrow<&{LockedMoxyToken.Receiver}>()
633                    ?? panic("Could not borrow receiver reference to the recipient's Vault (moxyTokenLockedReceiverPath)")
634
635            // Deposit the withdrawn tokens in the recipient's receiver
636            // The tokens will be locked upto the days defined on round with
637            // starting point at TGE Date.
638
639            receiverRef.depositFor(from: <-amountAtTGEVault, time: self.tgeDate)
640            receiverRef.depositFor(from: <-amountAfterUnlockVault, time: self.getUnlockDate())
641            
642            // Generate linear schedule to send to Locked Token
643            //let schedule <- self.generateScheduleFor(amount: amountVault.balance)
644            let schedule = self.generateScheduleFor(amount: amount)
645            
646            receiverRef.depositFromFixedSchedule(from: <-amountVault, schedule: schedule)
647            emit MOXYLockedTokensReleasedTo(round: self.id, address: address, amount: amount )
648        }
649
650        pub fun allocateAfterTGE(vault: @FungibleToken.Vault, address: Address) {
651            self.allocateOn(timestamp: self.tgeDate, vault: <-vault, address: address)
652        }
653
654        pub fun allocateOn(timestamp: UFix64, vault: @FungibleToken.Vault, address: Address) {
655            let amountsDict = self.getAmountsDictFor(address: address, amount: vault.balance)!
656            let amount = vault.balance
657            let startTime = MoxyData.getTimestampTo0000(timestamp: timestamp)
658
659            var am01 = amountsDict["atTGE"]!
660            var am02 = amountsDict["afterUnlock"]!
661            var am03 = amountsDict["daily"]!
662
663            let amountAtTGEVault <- vault.withdraw(amount: am01) as! @MoxyToken.Vault
664
665            let amountAfterUnlockVault <- vault.withdraw(amount: am02) as! @MoxyToken.Vault
666
667            let amountVault <- vault.withdraw(amount: am03) as! @MoxyToken.Vault
668
669            // Get recipient reference to assign release schedule
670            let recipient = getAccount(address)
671            let receiverRef = recipient.getCapability(MoxyToken.moxyTokenLockedReceiverPath)
672                    .borrow<&{LockedMoxyToken.Receiver}>()
673                    ?? panic("Could not borrow receiver reference to the recipient's Vault (moxyTokenLockedReceiverPath)")
674
675            // Deposit the withdrawn tokens in the recipient's receiver
676            // The tokens will be locked upto the days defined on round with
677            // starting point at TGE Date.
678            receiverRef.depositFor(from: <-amountAtTGEVault, time: startTime)
679
680            let unlockDate = self.getUnlockDateStartingOn(timestamp: startTime)
681            receiverRef.depositFor(from: <-amountAfterUnlockVault, time: unlockDate)
682
683            let schedule = self.generateScheduleForDate(timestamp: startTime, amount: amount)
684
685            let vaultConverted <- vault  as! @MoxyToken.Vault
686
687            receiverRef.depositFromFixedSchedule(from: <-amountVault, schedule: schedule)
688            receiverRef.depositFor(from: <- vaultConverted, time: unlockDate)
689
690            emit MOXYLockedTokensReleasedTo(round: self.id, address: address, amount: amount )
691        }
692
693        pub fun getUnlockDate(): UFix64 {
694            return self.getUnlockDateStartingOn(timestamp: self.tgeDate)
695        }
696
697        pub fun getUnlockDateStartingOn(timestamp: UFix64): UFix64 {
698            return timestamp + (UFix64(self.lockTime) * 86400.0)
699        }
700        
701        // Generate a dictionary with the release schedule
702        // for an specified amount
703        pub fun generateScheduleFor(amount: UFix64): LinearRelease.LinearSchedule {
704            return self.generateScheduleForDate(timestamp: self.tgeDate, amount: amount)
705        }
706
707        pub fun generateScheduleForDate(timestamp: UFix64, amount: UFix64): LinearRelease.LinearSchedule {
708
709            let unlockAtTGEAmount = amount * (self.unlockPercentageAtTGE / 100.0)
710            let unlockAfterLockTimeAmount = amount * (self.unlockPercentageAfterLockTime / 100.0)
711            let totalToRelease = amount - (unlockAtTGEAmount + unlockAfterLockTimeAmount)
712            let days = Int(self.getDays())
713            let dailyAmount = totalToRelease / UFix64(days)
714            let unlockDate = self.getUnlockDateStartingOn(timestamp: timestamp)
715 
716            let newLinearRelease = LinearRelease.createLinearSchedule(tgeDate: timestamp, totalAmount: amount, initialAmount: unlockAtTGEAmount, unlockDate: unlockDate, unlockAmount: unlockAfterLockTimeAmount, days: days, dailyAmount: dailyAmount)
717            return newLinearRelease
718        }
719
720
721        pub fun getDaysFrom(months: Int): UFix64 {
722            // Dictionary represents the months in the left
723            // and the days in the right
724            let dictionary: {Int:Int} = {
725                24: 730,
726                20: 605,
727                16: 485,
728                12: 365,
729                10: 300,
730                6: 180,
731                3: 90,
732                1: 30,
733                0: 1
734            }
735            if (dictionary[months] == nil) {
736                return UFix64(months * 30)
737            }
738            return UFix64(dictionary[months]!)
739        }
740
741        pub fun getDays(): UFix64 {
742            return self.getDaysFrom(months: self.months)
743        }
744
745        pub fun getEndDate(): UFix64 {
746            if (!self.isTGESet()) {
747                return 0.0
748            }
749            return self.getUnlockDate() + (self.getDays() * 86400.0)
750        }
751
752        pub fun isTGESet(): Bool {
753            return self.tgeDate > 0.0
754        }
755
756        pub fun isReleaseProcesStarted(): Bool {
757            return self.totalReleased != 0.0
758        }
759
760        pub fun getLockedTokenTime(): UFix64 {
761            return UFix64(self.lockTime) * 86400.0
762        }
763
764        pub fun isInLockedPeriod(): Bool {
765            return getCurrentBlock().timestamp < (self.tgeDate + UFix64(self.lockTime) * 86400.0)  
766        }
767
768        pub fun dailyAllocationAmount():UFix64 {
769            if (self.months == 0) {
770                return self.initialRelease
771            }
772
773            let partial = self.initialRelease * ((self.unlockPercentageAtTGE + self.unlockPercentageAfterLockTime) / 100.0)
774            let total = (self.initialRelease - partial) / self.getDays()
775            return total
776        }
777
778        pub fun removeAddress(address: Address){
779            if (self.isReleaseProcesStarted()) {
780                return
781            }
782            let amount =  self.getAmountFor(address: address)
783            self.totalAllocated = self.totalAllocated - amount
784            self.accounts.remove(key: address)
785        }
786
787        pub fun setStartDate(timestamp: UFix64) {
788            self.tgeDate = timestamp
789
790            for address in self.accounts.keys {
791                self.accounts[address]!.borrow()!.setStartDate(timestamp: timestamp)
792
793            }
794        }
795
796        // Returns the number of accounts that this round will process
797        pub fun getAccountsToProcess(): Int {
798            return self.accounts.length
799        }
800
801        pub fun isQueueAtBegining(): Bool {
802            return self.allocationQueue.isAtBeginning()
803        }
804
805        pub fun hasQueueFinished(): Bool {
806            return self.allocationQueue.isEmptyQueue() || self.allocationQueue.hasFinished()
807        }
808
809        pub fun getQueueNextAddresses(quantity: Int): @MoxyProcessQueue.Run {
810            let run <- self.allocationQueue.lockRunWith(quantity: quantity)
811            if (run == nil) {
812                panic("Wait for queues to be released")
813            }
814            return <-run!
815        }
816
817        pub fun completeNextAddresses(run: @MoxyProcessQueue.Run) {
818            self.allocationQueue.completeNextAddresses(run: <-run)
819        }
820
821        pub fun removeAccount(address: Address) {
822            if (self.accounts[address] == nil ) {
823                return log("Can't remove. Address is not a rounds participant.")
824            }
825            let amount =  self.getAmountFor(address: address)
826            self.totalAllocated = self.totalAllocated - amount
827            self.accounts.remove(key: address)
828        }
829
830        init(id: String, type: String, name: String, initialRelease: UFix64, lockTime: Int, months: Int, unlockPercentageAtTGE: UFix64, unlockPercentageAfterLockTime: UFix64, allocateBeforeTGE: Bool) {
831            pre {
832                unlockPercentageAtTGE + unlockPercentageAfterLockTime <= 100.0: "Unlock percentage could not be greater than 100%"
833            }
834
835            self.id = id
836            self.type = type
837            self.name = name
838            self.initialRelease = initialRelease
839            self.lockTime = lockTime
840            self.months = months
841            self.accounts = {}
842            self.totalIncorporated = 0.0
843            self.totalAllocated = 0.0
844            self.tgeDate = 0.0
845            self.totalReleased = 0.0
846            self.unlockPercentageAtTGE = unlockPercentageAtTGE
847            self.unlockPercentageAfterLockTime = unlockPercentageAfterLockTime
848            self.isReleaseStarted = false
849            self.allocateBeforeTGE =  allocateBeforeTGE
850            self.allocationQueue <- MoxyProcessQueue.createNewQueue()
851        }
852
853        destroy() {
854            destroy self.allocationQueue
855        }
856    }
857
858    pub resource Rounds: MoxyRoundsInfo, MoxyRoundsCreator {
859        access(contract) let rounds: @{String: Round}
860        access(self) let releases: {Address:Capability<&RoundReleases>}
861        pub var tgeDate: UFix64
862
863        pub fun setTGEDate(timestamp: UFix64) {
864            self.tgeDate = MoxyData.getTimestampTo0000(timestamp: timestamp)
865        }
866
867        // Check if allocatin is complete on all release rounds
868        pub fun isReadyToStartRelease(): Bool {
869            for roundId in self.rounds.keys {
870                let isReady = self.rounds[roundId]?.isReadyToStartRelease()!
871                if (!isReady) {
872                    return false
873                }
874            }
875            return true
876        }
877
878        pub fun getAccountsToProcess(): Int {
879            var quantity = 0
880            for roundId in self.rounds.keys {
881                quantity = quantity + self.rounds[roundId]?.getAccountsToProcess()!
882            }
883            return quantity
884        }
885
886        pub fun completeNextAddresses(roundId: String, run: @MoxyProcessQueue.Run) {
887            let round: @MoxyReleaseRounds.Round <-! self.rounds.remove(key: roundId)!
888            round.completeNextAddresses(run: <-run)
889            self.rounds[roundId] <-! round
890        }
891
892        pub fun allocateAfterTGE(roundId: String, vault: @FungibleToken.Vault, address: Address) {
893            let round <- self.rounds.remove(key: roundId)!
894            round.allocateAfterTGE(vault: <-vault, address: address)
895            let old <- self.rounds[roundId] <- round
896            destroy old
897        }
898
899        pub fun allocateOn(timestamp: UFix64, roundId: String, vault: @FungibleToken.Vault, address: Address) {
900            let round <- self.rounds.remove(key: roundId)!
901            let time0000 = MoxyData.getTimestampTo0000(timestamp: timestamp)
902            round.allocateOn(timestamp: time0000, vault: <-vault, address: address)
903            let old <- self.rounds[roundId] <- round
904            destroy old
905        }
906
907        pub fun getAmountFor(roundId: String, address: Address): UFix64 {
908            return self.rounds[roundId]?.getAmountFor(address: address)!
909        }
910
911        pub fun isReadyToStartReleaseTo(roundId: String): Bool {
912            return self.rounds[roundId]?.isReadyToStartRelease()!
913        }
914
915        pub fun haveAllRoundsStarted(): Bool {
916            for roundId in self.rounds.keys {
917                let isStarted = self.rounds[roundId]?.isReleaseStarted!
918                if (!isStarted) {
919                    return false
920                }
921            }
922            return true
923        }
924
925        pub fun isQueueAtBegining():Bool {
926            for roundId in self.rounds.keys {
927                let isAtBegining = self.rounds[roundId]?.isQueueAtBegining()!
928                if (!isAtBegining) {
929                    return false
930                }
931            }
932            return true
933        }
934
935        pub fun haveAllQueuesFinished():Bool {
936            for roundId in self.rounds.keys {
937                let isFinished = self.rounds[roundId]?.hasQueueFinished()!
938                if (!isFinished) {
939                    return false
940                }
941            }
942            return true
943        }
944
945        pub fun initialAllocationFinished():Bool {
946            // Check if all queues are finished but not in the cases
947            // that the releasee can start after TGE 
948            for roundId in self.rounds.keys {
949                let isFinished = self.rounds[roundId]?.isInitialAllocationFinished()!
950                if (!isFinished) {
951                    return false
952                }
953            }
954            return true
955        }
956
957        pub fun getRoundsNames(): [String] {
958            return self.rounds.keys
959        }
960
961        pub fun hasQueueFinished(roundId: String): Bool {
962            return self.rounds[roundId]?.hasQueueFinished()!
963        }
964
965        pub fun getQueueNextAddresses(roundId: String, quantity: Int): @MoxyProcessQueue.Run {
966            return <- self.rounds[roundId]?.getQueueNextAddresses(quantity: quantity)!
967        }
968
969        pub fun getRoundsLength(): Int {
970            return self.rounds.length
971        }
972
973        pub fun setStartDate(timestamp: UFix64) {
974            for roundId in self.rounds.keys {
975                self.rounds[roundId]?.setStartDate(timestamp: timestamp)!
976            }
977        }
978
979        pub fun getAddresses(): [Address] {
980            return self.releases.keys
981        }
982
983        pub fun getRoundAddresses(roundId: String): [Address] {
984            return self.rounds[roundId]?.getRoundAddresses()!
985        }
986
987        pub fun addRound(_ id: String, type: String, name: String, initialRelease: UFix64, lockTime: Int, months: Int, unlockPercentageAtTGE: UFix64, unlockPercentageAfterLockTime: UFix64, allocateBeforeTGE: Bool) {
988            let round <- create Round(id: id, type: type, name: name, initialRelease: initialRelease, lockTime: lockTime, months: months, unlockPercentageAtTGE: unlockPercentageAtTGE, unlockPercentageAfterLockTime: unlockPercentageAfterLockTime, allocateBeforeTGE: allocateBeforeTGE)
989            round.setStartDate(timestamp: self.tgeDate)
990            let old <- self.rounds[id] <- round
991            destroy old
992            emit MoxyReleaseRounds.RoundAdded(name: name)
993        }
994
995        pub fun createRoundReleases(lockedMOXYVault: Capability<&LockedMoxyToken.LockedVault>, lockedMVVault: Capability<&LockedMoxyVaultToken.LockedVault>): @RoundReleases {
996            return <- create RoundReleases(lockedMOXYVault: lockedMOXYVault, lockedMVVault: lockedMVVault)
997        }
998
999        pub fun acceptRounds(address: Address, releasesRef: Capability<&MoxyReleaseRounds.RoundReleases>) {
1000            pre {
1001                address == releasesRef.borrow()!.lockedMOXYVault.address : "Address does not match with Vault address"
1002                address == releasesRef.borrow()!.lockedMVVault.address : "Address does not match with Vault address"
1003            }
1004            
1005            // User consents to partipate in rounds releases
1006            if (self.releases[address] != nil ) {
1007                log("Address already accepted the Rounds participation.")
1008                emit AccountAlreadyAcceptedRoundParticipation(address: address, timestamp: getCurrentBlock().timestamp)
1009                return
1010            }
1011
1012            // Capability added to releases collection for user future 
1013            // rounds participations
1014            self.releases[address] = releasesRef
1015
1016            emit AccountAcceptedRoundsParticipation(address: address, timestamp: getCurrentBlock().timestamp)
1017        }
1018
1019        pub fun removeFromRounds(address: Address){
1020            if (self.releases[address] == nil ) {
1021                return log("Can't remove. Address is not a rounds participant.")
1022            }
1023            self.releases.remove(key: address)
1024            for roundId in self.rounds.keys {
1025                self.rounds[roundId]?.removeAccount(address: address)
1026            }
1027        }
1028
1029        pub fun addressHasAcceptedRounds(address: Address): Bool {
1030            return self.releases[address] != nil
1031        }
1032
1033        pub fun fullAllocateTo(roundId: String, address: Address) {
1034            let amount = self.rounds[roundId]?.getAllocationRemaining()!
1035            self.setAddress(roundId: roundId, address: address, amount: amount)
1036        }
1037
1038        //Rounds.setAddress
1039        pub fun setAddress(roundId: String, address: Address, amount: UFix64) {
1040            if (self.releases[address] == nil) {
1041                panic("Required accept consent from address: ".concat(address.toString()))
1042            }
1043
1044            // Make a new resource to store the roundID in the key and
1045            // a structure with the RoundRelease (with amount and amount released) in value
1046            let alreadyStarted = false
1047            let exceedsAllocation = self.rounds[roundId]?.getAllocationRemaining()! < amount
1048            if (alreadyStarted || exceedsAllocation) {
1049                var message = ""
1050                if (alreadyStarted) {
1051                    message = roundId.concat(" - Cannot allocate to round: process already started")
1052                } else {
1053                    message = roundId.concat(" - Amount exceeds initial Allocation. Max to allocate is ".concat(self.rounds[roundId]?.getAllocationRemaining()!.toString()))
1054                } 
1055                panic(message)
1056            } else {
1057                // Sets the address
1058                let releasesRef = self.releases[address]!
1059                self.rounds[roundId]?.setAddress(address: address, amount: amount, releasesRef: releasesRef)!
1060            }
1061
1062        }
1063
1064        pub fun incorporateAddress(roundId: String, address: Address, amount: UFix64, startTime: UFix64) {
1065            if (self.releases[address] == nil) {
1066                panic("Required accept consent from address: ".concat(address.toString()))
1067            }
1068
1069            let releasesRef = self.releases[address]!
1070            self.rounds[roundId]?.incorporateAddress(address: address, amount: amount, releasesRef: releasesRef, startTime: startTime)!
1071        }
1072
1073
1074        // Rounds
1075        pub fun startReleaseRound(roundId: String, address: Address, initialVault: @FungibleToken.Vault) {
1076            let round <- self.rounds.remove(key: roundId)!
1077            let amount = round.totalAllocated
1078            round.startReleaseAddress(address: address, initialVault: <-initialVault)
1079            let old <- self.rounds[roundId] <- round
1080            destroy old
1081        }
1082
1083        pub fun hasRoundRelease(address: Address): Bool {
1084            return self.releases[address] != nil
1085        }
1086
1087        //Rounds
1088        pub fun allocateDailyReleaseNowToAddress(address: Address, feeRemaining: UFix64): @FungibleToken.Vault {
1089            let roundReleases = self.releases[address]!.borrow()!
1090            let feeVault <- roundReleases.allocateDailyReleaseToNow(feeRemaining: feeRemaining)
1091            
1092            for roundId in roundReleases.releases.keys {
1093                if (self.rounds[roundId]?.isAddressInRound(address: address)!) {
1094                    self.rounds[roundId]?.allocateDailyReleaseToNow(address: address)!
1095                }
1096
1097            }
1098            return <-feeVault
1099        }
1100
1101        pub fun removeAddress(roundId: String, address: Address){
1102            self.rounds[roundId]?.removeAddress(address: address)
1103        }
1104
1105        pub fun getAllocationRemaining(_ id: String):UFix64? {
1106            return self.rounds[id]?.getAllocationRemaining()
1107        }
1108        pub fun getDailyAllocationsFrom(roundId: String, from: UFix64, to: UFix64): [UFix64]? {
1109            return self.rounds[roundId]?.getDailyAllocationsFrom(from: from, to: to)
1110        }
1111
1112        pub fun getDailyAllocationsFromToAddress(roundId: String, address: Address, from: UFix64, to: UFix64): [UFix64]?? {
1113            return self.rounds[roundId]?.getDailyAllocationsFromToAddress(address: address, from: from, to: to)
1114        }
1115
1116        pub fun getAmountReleasedFor(roundId: String, address: Address): UFix64 {
1117            return self.rounds[roundId]?.getAmountReleasedFor(address: address)!
1118        }
1119
1120        pub fun getRoundReleaseInfo(_ id: String,  address: Address): RoundReleaseInfo? {
1121            let roundInfo = self.rounds[id]?.getRoundReleaseInfo(address)!
1122            return roundInfo
1123        }
1124
1125        pub fun getAccounts(_ id: String): {Address: ParticipantRoundInfo}? {
1126            return self.rounds[id]?.getAccounts();
1127        }
1128
1129        pub fun getRoundsForAddress(address: Address): {String: ParticipantRoundInfo} {
1130            let rounds: {String: ParticipantRoundInfo} = {}
1131            for roundId in self.rounds.keys {
1132                let amount =  self.getAmountFor(roundId: roundId, address: address)
1133                if (amount > 0.0) {
1134                    let amountReleased =  self.getAmountReleasedFor(roundId: roundId, address: address)
1135                    rounds[roundId] = ParticipantRoundInfo(address: address, roundId: roundId, amount: amount, amountReleased: amountReleased)
1136                }
1137            }
1138            return rounds
1139        }
1140
1141        pub fun getRoundInfo(roundId: String): RoundInfo {
1142            return self.rounds[roundId]?.getRoundInfo()!
1143        }
1144
1145        init() {
1146            self.rounds <- {}
1147            self.releases = {}
1148            
1149            //  set to JUL 10th, 2023 - GMT+0000
1150            self.tgeDate = 1688947200.0
1151        }
1152
1153        destroy() {
1154            destroy self.rounds
1155        }
1156    }
1157
1158    access(self) fun getRoundsCapability(): &Rounds {
1159        return self.account
1160            .getCapability(self.moxyRoundsPrivate)
1161            .borrow<&MoxyReleaseRounds.Rounds>()!
1162    }
1163
1164    pub fun getRounds(): [String] {
1165        let roundsManager = self.getRoundsCapability()
1166        return roundsManager.rounds.keys
1167    }
1168
1169    pub resource interface RoundReleasesInfo {
1170
1171    }
1172
1173    pub resource interface MoxyRoundsInfo {
1174        pub fun getRoundsForAddress(address: Address): {String: ParticipantRoundInfo} 
1175        pub fun getAllocationRemaining(_ id: String): UFix64? 
1176        pub fun getDailyAllocationsFrom(roundId: String, from: UFix64, to: UFix64): [UFix64]?
1177        pub fun getDailyAllocationsFromToAddress(roundId: String, address: Address, from: UFix64, to: UFix64): [UFix64]??
1178        pub fun getAccounts(_ id: String): {Address: ParticipantRoundInfo}? 
1179        pub fun addressHasAcceptedRounds(address: Address): Bool
1180        pub fun getAddresses(): [Address]
1181        pub fun getRoundAddresses(roundId: String): [Address]
1182        pub fun getRoundReleaseInfo(_ id: String,  address: Address): RoundReleaseInfo?
1183        pub fun haveAllQueuesFinished():Bool
1184        pub fun initialAllocationFinished():Bool
1185        pub fun getRoundInfo(roundId: String): RoundInfo
1186    }
1187
1188    pub resource interface MoxyRoundsCreator {
1189        pub fun createRoundReleases(lockedMOXYVault: Capability<&LockedMoxyToken.LockedVault>, lockedMVVault: Capability<&LockedMoxyVaultToken.LockedVault>): @RoundReleases
1190        pub fun acceptRounds(address: Address, releasesRef: Capability<&MoxyReleaseRounds.RoundReleases>)
1191    }
1192
1193    pub let moxyRoundsStorage: StoragePath
1194    pub let moxyRoundsPrivate: PrivatePath
1195    pub let moxyRoundsInfoPublic: PublicPath
1196
1197    pub let roundReleasesStorage: StoragePath
1198    pub let roundReleasesPrivate: PrivatePath
1199    pub let roundReleasesInfoPublic: PublicPath
1200
1201    // Initialize contract
1202    init(){
1203        
1204        // Moxy Rounds initialization
1205        let moxyRounds <- create Rounds()
1206
1207        moxyRounds.addRound("seed", type: "Token Sale", name: "Seed", initialRelease: 45000000.0, lockTime: 0, months: 24, unlockPercentageAtTGE: 0.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: true)
1208        moxyRounds.addRound("private_1", type: "Token Sale", name: "Private 1", initialRelease: 75000000.0, lockTime: 0, months: 20, unlockPercentageAtTGE: 0.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: true)
1209        moxyRounds.addRound("private_2", type: "Token Sale", name: "Private 2", initialRelease: 113500000.0, lockTime: 0, months: 16, unlockPercentageAtTGE: 0.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: true)
1210        moxyRounds.addRound("public_presale", type: "Token Sale", name: "Public Whitelist", initialRelease: 18000000.0, lockTime: 0, months: 3, unlockPercentageAtTGE: 50.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: false)
1211        moxyRounds.addRound("public_ido", type: "Token Sale", name: "Public IDO", initialRelease: 11000000.0, lockTime: 0, months: 0, unlockPercentageAtTGE: 100.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: true)
1212
1213        moxyRounds.addRound("team", type: "Token Allocation", name: "Team", initialRelease: 225000000.0, lockTime: 365, months: 24, unlockPercentageAtTGE: 0.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: true)
1214        moxyRounds.addRound("moxy_foundation", type: "Token Allocation", name: "Moxy Foundation", initialRelease: 375000000.0, lockTime: 180, months: 24, unlockPercentageAtTGE: 15.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: true)
1215        moxyRounds.addRound("advisors", type: "Token Allocation", name: "Advisors", initialRelease: 75000000.0, lockTime: 180, months: 24, unlockPercentageAtTGE: 0.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: true)
1216        moxyRounds.addRound("treasury", type: "Token Allocation", name: "Treasury", initialRelease: 150000000.0, lockTime: 90, months: 24, unlockPercentageAtTGE: 0.0, unlockPercentageAfterLockTime: 25.0, allocateBeforeTGE: true)
1217        moxyRounds.addRound("ecosystem", type: "Token Allocation", name: "Ecosystem", initialRelease: 412500000.0, lockTime: 180, months: 24, unlockPercentageAtTGE: 0.0, unlockPercentageAfterLockTime: 0.0, allocateBeforeTGE: true)
1218
1219        // Storage of Rounds
1220        self.moxyRoundsStorage = /storage/moxyRounds
1221        self.moxyRoundsPrivate = /private/moxyRounds
1222        self.moxyRoundsInfoPublic = /public/moxyRoundsInfoPublic
1223
1224        self.account.save(<-moxyRounds, to: self.moxyRoundsStorage)
1225        self.account.link<&MoxyReleaseRounds.Rounds>(self.moxyRoundsPrivate, target: self.moxyRoundsStorage)
1226        self.account.link<&MoxyReleaseRounds.Rounds{MoxyRoundsInfo}>(
1227                self.moxyRoundsInfoPublic, 
1228                target: self.moxyRoundsStorage)!
1229
1230        // Storage of RoundRelease on user account
1231        self.roundReleasesStorage = /storage/roundReleaseStorage
1232        self.roundReleasesPrivate = /private/roundReleasePrivate
1233        self.roundReleasesInfoPublic = /public/roundReleaseInfoPublic
1234    }
1235}
1236 
1237