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