@flowtyio/flow-contracts 0.1.0-beta.3 → 0.1.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +1 -1
  2. package/contracts/Burner.cdc +44 -0
  3. package/contracts/FlowStorageFees.cdc +15 -15
  4. package/contracts/FlowToken.cdc +29 -78
  5. package/contracts/FungibleToken.cdc +80 -53
  6. package/contracts/FungibleTokenMetadataViews.cdc +13 -25
  7. package/contracts/FungibleTokenSwitchboard.cdc +360 -0
  8. package/contracts/MetadataViews.cdc +107 -50
  9. package/contracts/NonFungibleToken.cdc +110 -60
  10. package/contracts/TokenForwarding.cdc +19 -11
  11. package/contracts/ViewResolver.cdc +20 -16
  12. package/contracts/capability-cache/CapabilityCache.cdc +97 -0
  13. package/contracts/dapper/DapperUtilityCoin.cdc +106 -39
  14. package/contracts/dapper/FlowUtilityToken.cdc +107 -40
  15. package/contracts/dapper/TopShot.cdc +323 -259
  16. package/contracts/dapper/TopShotLocking.cdc +41 -15
  17. package/contracts/dapper/offers/DapperOffersV2.cdc +46 -43
  18. package/contracts/dapper/offers/OffersV2.cdc +40 -56
  19. package/contracts/dapper/offers/Resolver.cdc +20 -13
  20. package/contracts/emerald-city/FLOAT.cdc +259 -254
  21. package/contracts/example/ExampleNFT.cdc +18 -21
  22. package/contracts/example/ExampleToken.cdc +68 -1
  23. package/contracts/find/FindViews.cdc +357 -353
  24. package/contracts/flow-utils/AddressUtils.cdc +20 -23
  25. package/contracts/flow-utils/ArrayUtils.cdc +10 -11
  26. package/contracts/flow-utils/ScopedFTProviders.cdc +27 -19
  27. package/contracts/flow-utils/ScopedNFTProviders.cdc +31 -26
  28. package/contracts/flow-utils/StringUtils.cdc +24 -37
  29. package/contracts/flowty-drops/ContractManager.cdc +47 -0
  30. package/contracts/flowty-drops/DropFactory.cdc +75 -0
  31. package/contracts/flowty-drops/DropTypes.cdc +278 -0
  32. package/contracts/flowty-drops/FlowtyAddressVerifiers.cdc +64 -0
  33. package/contracts/flowty-drops/FlowtyDrops.cdc +431 -0
  34. package/contracts/flowty-drops/FlowtyPricers.cdc +48 -0
  35. package/contracts/flowty-drops/FlowtySwitchers.cdc +113 -0
  36. package/contracts/flowty-drops/initializers/ContractBorrower.cdc +14 -0
  37. package/contracts/flowty-drops/initializers/ContractInitializer.cdc +7 -0
  38. package/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc +58 -0
  39. package/contracts/flowty-drops/nft/BaseCollection.cdc +97 -0
  40. package/contracts/flowty-drops/nft/BaseNFT.cdc +107 -0
  41. package/contracts/flowty-drops/nft/ContractFactory.cdc +13 -0
  42. package/contracts/flowty-drops/nft/ContractFactoryTemplate.cdc +48 -0
  43. package/contracts/flowty-drops/nft/NFTMetadata.cdc +119 -0
  44. package/contracts/flowty-drops/nft/OpenEditionNFT.cdc +41 -0
  45. package/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +52 -0
  46. package/contracts/flowty-drops/nft/UniversalCollection.cdc +23 -0
  47. package/contracts/fungible-token-router/FungibleTokenRouter.cdc +105 -0
  48. package/contracts/hybrid-custody/CapabilityDelegator.cdc +28 -26
  49. package/contracts/hybrid-custody/CapabilityFactory.cdc +20 -18
  50. package/contracts/hybrid-custody/CapabilityFilter.cdc +41 -24
  51. package/contracts/hybrid-custody/HybridCustody.cdc +303 -242
  52. package/contracts/hybrid-custody/factories/FTAllFactory.cdc +16 -4
  53. package/contracts/hybrid-custody/factories/FTBalanceFactory.cdc +16 -4
  54. package/contracts/hybrid-custody/factories/FTProviderFactory.cdc +17 -5
  55. package/contracts/hybrid-custody/factories/FTReceiverBalanceFactory.cdc +16 -4
  56. package/contracts/hybrid-custody/factories/FTReceiverFactory.cdc +16 -4
  57. package/contracts/hybrid-custody/factories/FTVaultFactory.cdc +46 -0
  58. package/contracts/hybrid-custody/factories/NFTCollectionFactory.cdc +45 -0
  59. package/contracts/hybrid-custody/factories/NFTCollectionPublicFactory.cdc +16 -4
  60. package/contracts/hybrid-custody/factories/NFTProviderAndCollectionFactory.cdc +22 -0
  61. package/contracts/hybrid-custody/factories/NFTProviderFactory.cdc +16 -4
  62. package/contracts/lost-and-found/LostAndFound.cdc +29 -25
  63. package/contracts/nft-catalog/NFTCatalog.cdc +60 -64
  64. package/contracts/nft-catalog/NFTCatalogAdmin.cdc +28 -27
  65. package/flow.json +189 -5
  66. package/package.json +1 -1
  67. package/contracts/hybrid-custody/factories/NFTProviderAndCollectionPublicFactory.cdc +0 -10
@@ -0,0 +1,431 @@
1
+ import "NonFungibleToken"
2
+ import "FungibleToken"
3
+ import "MetadataViews"
4
+ import "AddressUtils"
5
+
6
+ access(all) contract FlowtyDrops {
7
+ access(all) let ContainerStoragePath: StoragePath
8
+ access(all) let ContainerPublicPath: PublicPath
9
+
10
+ access(all) event DropAdded(address: Address, id: UInt64, name: String, description: String, imageUrl: String, start: UInt64?, end: UInt64?)
11
+ access(all) event Minted(address: Address, dropID: UInt64, phaseID: UInt64, nftID: UInt64, nftType: String)
12
+ access(all) event PhaseAdded(dropID: UInt64, dropAddress: Address, id: UInt64, index: Int, switcherType: String, pricerType: String, addressVerifierType: String)
13
+ access(all) event PhaseRemoved(dropID: UInt64, dropAddress: Address, id: UInt64)
14
+
15
+ access(all) entitlement Owner
16
+ access(all) entitlement EditPhase
17
+
18
+ // Interface to expose all the components necessary to participate in a drop
19
+ // and to ask questions about a drop.
20
+ access(all) resource interface DropPublic {
21
+ access(all) fun borrowPhasePublic(index: Int): &{PhasePublic}
22
+ access(all) fun borrowActivePhases(): [&{PhasePublic}]
23
+ access(all) fun borrowAllPhases(): [&{PhasePublic}]
24
+ access(all) fun mint(
25
+ payment: @{FungibleToken.Vault},
26
+ amount: Int,
27
+ phaseIndex: Int,
28
+ expectedType: Type,
29
+ receiverCap: Capability<&{NonFungibleToken.CollectionPublic}>,
30
+ commissionReceiver: Capability<&{FungibleToken.Receiver}>,
31
+ data: {String: AnyStruct}
32
+ ): @{FungibleToken.Vault}
33
+ access(all) fun getDetails(): DropDetails
34
+ }
35
+
36
+ access(all) resource Drop: DropPublic {
37
+ access(all) event ResourceDestroyed(
38
+ uuid: UInt64 = self.uuid,
39
+ minterAddress: Address = self.minterCap.address,
40
+ nftType: String = self.details.nftType,
41
+ totalMinted: Int = self.details.totalMinted
42
+ )
43
+
44
+ // phases represent the stages of a drop. For example, a drop might have an allowlist and a public mint phase.
45
+ access(self) let phases: @[Phase]
46
+ // the details of a drop. This includes things like display information and total number of mints
47
+ access(self) let details: DropDetails
48
+ access(self) let minterCap: Capability<&{Minter}>
49
+
50
+ access(all) fun mint(
51
+ payment: @{FungibleToken.Vault},
52
+ amount: Int,
53
+ phaseIndex: Int,
54
+ expectedType: Type,
55
+ receiverCap: Capability<&{NonFungibleToken.CollectionPublic}>,
56
+ commissionReceiver: Capability<&{FungibleToken.Receiver}>,
57
+ data: {String: AnyStruct}
58
+ ): @{FungibleToken.Vault} {
59
+ pre {
60
+ expectedType.isSubtype(of: Type<@{NonFungibleToken.NFT}>()): "expected type must be an NFT"
61
+ expectedType.identifier == self.details.nftType: "expected type does not match drop details type"
62
+ self.phases.length > phaseIndex: "phase index is too high"
63
+ receiverCap.check(): "receiver capability is not valid"
64
+ }
65
+
66
+ // validate the payment vault amount and type
67
+ let phase: &Phase = &self.phases[phaseIndex]
68
+ assert(
69
+ phase.details.addressVerifier.canMint(addr: receiverCap.address, num: amount, totalMinted: self.details.minters[receiverCap.address] ?? 0, data: {}),
70
+ message: "receiver address has exceeded their mint capacity"
71
+ )
72
+
73
+ let paymentAmount = phase.details.pricer.getPrice(num: amount, paymentTokenType: payment.getType(), minter: receiverCap.address)
74
+ let withdrawn <- payment.withdraw(amount: paymentAmount) // make sure that we have a fresh vault resource
75
+
76
+ // take commission
77
+ let commission <- withdrawn.withdraw(amount: self.details.commissionRate * withdrawn.balance)
78
+ commissionReceiver.borrow()!.deposit(from: <-commission)
79
+
80
+ assert(phase.details.pricer.getPrice(num: amount, paymentTokenType: withdrawn.getType(), minter: receiverCap.address) * (1.0 - self.details.commissionRate) == withdrawn.balance, message: "incorrect payment amount")
81
+ assert(phase.details.pricer.getPaymentTypes().contains(withdrawn.getType()), message: "unsupported payment type")
82
+
83
+ // mint the nfts
84
+ let minter = self.minterCap.borrow() ?? panic("minter capability could not be borrowed")
85
+ let mintedNFTs <- minter.mint(payment: <-withdrawn, amount: amount, phase: phase, data: data)
86
+ assert(phase.details.switcher.hasStarted() && !phase.details.switcher.hasEnded(), message: "phase is not active")
87
+ assert(mintedNFTs.length == amount, message: "incorrect number of items returned")
88
+
89
+ // distribute to receiver
90
+ let receiver = receiverCap.borrow() ?? panic("could not borrow receiver capability")
91
+ self.details.addMinted(num: mintedNFTs.length, addr: receiverCap.address)
92
+
93
+ while mintedNFTs.length > 0 {
94
+ let nft <- mintedNFTs.removeFirst()
95
+
96
+ let nftType = nft.getType()
97
+ emit Minted(address: receiverCap.address, dropID: self.uuid, phaseID: phase.uuid, nftID: nft.id, nftType: nftType.identifier)
98
+
99
+ // validate that every nft is the right type
100
+ assert(nftType == expectedType, message: "unexpected nft type was minted")
101
+
102
+ receiver.deposit(token: <-nft)
103
+ }
104
+
105
+ // cleanup
106
+ destroy mintedNFTs
107
+
108
+ // return excess payment
109
+ return <- payment
110
+ }
111
+
112
+ access(Owner) fun borrowPhase(index: Int): auth(EditPhase) &Phase {
113
+ return &self.phases[index]
114
+ }
115
+
116
+
117
+ access(all) fun borrowPhasePublic(index: Int): &{PhasePublic} {
118
+ return &self.phases[index]
119
+ }
120
+
121
+ access(all) fun borrowActivePhases(): [&{PhasePublic}] {
122
+ let arr: [&{PhasePublic}] = []
123
+ var count = 0
124
+ while count < self.phases.length {
125
+ let ref = self.borrowPhasePublic(index: count)
126
+ let switcher = ref.getDetails().switcher
127
+ if switcher.hasStarted() && !switcher.hasEnded() {
128
+ arr.append(ref)
129
+ }
130
+
131
+ count = count + 1
132
+ }
133
+
134
+ return arr
135
+ }
136
+
137
+ access(all) fun borrowAllPhases(): [&{PhasePublic}] {
138
+ let arr: [&{PhasePublic}] = []
139
+ var index = 0
140
+ while index < self.phases.length {
141
+ let ref = self.borrowPhasePublic(index: index)
142
+ arr.append(ref)
143
+ index = index + 1
144
+ }
145
+
146
+ return arr
147
+ }
148
+
149
+ access(Owner) fun addPhase(_ phase: @Phase) {
150
+ emit PhaseAdded(
151
+ dropID: self.uuid,
152
+ dropAddress: self.owner!.address,
153
+ id: phase.uuid,
154
+ index: self.phases.length,
155
+ switcherType: phase.details.switcher.getType().identifier,
156
+ pricerType: phase.details.pricer.getType().identifier,
157
+ addressVerifierType: phase.details.addressVerifier.getType().identifier
158
+ )
159
+ self.phases.append(<-phase)
160
+ }
161
+
162
+ access(Owner) fun removePhase(index: Int): @Phase {
163
+ pre {
164
+ self.phases.length > index: "index is greater than length of phases"
165
+ }
166
+
167
+ let phase <- self.phases.remove(at: index)
168
+ emit PhaseRemoved(dropID: self.uuid, dropAddress: self.owner!.address, id: phase.uuid)
169
+
170
+ return <- phase
171
+ }
172
+
173
+ access(all) fun getDetails(): DropDetails {
174
+ return self.details
175
+ }
176
+
177
+ init(details: DropDetails, minterCap: Capability<&{Minter}>, phases: @[Phase]) {
178
+ pre {
179
+ minterCap.check(): "minter capability is not valid"
180
+ }
181
+
182
+ self.phases <- phases
183
+ self.details = details
184
+ self.minterCap = minterCap
185
+ }
186
+ }
187
+
188
+ access(all) struct DropDetails {
189
+ access(all) let display: MetadataViews.Display
190
+ access(all) let medias: MetadataViews.Medias?
191
+ access(all) var totalMinted: Int
192
+ access(all) var minters: {Address: Int}
193
+ access(all) let commissionRate: UFix64
194
+ access(all) let nftType: String
195
+
196
+ access(contract) fun addMinted(num: Int, addr: Address) {
197
+ self.totalMinted = self.totalMinted + num
198
+ if self.minters[addr] == nil {
199
+ self.minters[addr] = 0
200
+ }
201
+
202
+ self.minters[addr] = self.minters[addr]! + num
203
+ }
204
+
205
+ init(display: MetadataViews.Display, medias: MetadataViews.Medias?, commissionRate: UFix64, nftType: String) {
206
+ self.display = display
207
+ self.medias = medias
208
+ self.totalMinted = 0
209
+ self.commissionRate = commissionRate
210
+ self.minters = {}
211
+ self.nftType = nftType
212
+ }
213
+ }
214
+
215
+ // A switcher represents a phase being on or off, and holds information
216
+ // about whether a phase has started or not.
217
+ access(all) struct interface Switcher {
218
+ // Signal that a phase has started. If the phase has not ended, it means that this switcher's phase
219
+ // is active
220
+ access(all) view fun hasStarted(): Bool
221
+ // Signal that a phase has ended. If a switcher has ended, minting will not work. That could mean
222
+ // the drop is over, or it could mean another phase has begun.
223
+ access(all) view fun hasEnded(): Bool
224
+
225
+ access(all) view fun getStart(): UInt64?
226
+ access(all) view fun getEnd(): UInt64?
227
+ }
228
+
229
+ // A phase represents a stage of a drop. Some drops will only have one
230
+ // phase, while others could have many. For example, a drop with an allow list
231
+ // and a public mint would likely have two phases.
232
+ access(all) resource Phase: PhasePublic {
233
+ access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid)
234
+
235
+ access(all) let details: PhaseDetails
236
+
237
+ // returns whether this phase of a drop has started.
238
+ access(all) fun isActive(): Bool {
239
+ return self.details.switcher.hasStarted() && !self.details.switcher.hasEnded()
240
+ }
241
+
242
+ access(all) fun getDetails(): PhaseDetails {
243
+ return self.details
244
+ }
245
+
246
+ access(EditPhase) fun borrowSwitchAuth(): auth(Mutate) &{Switcher} {
247
+ return &self.details.switcher
248
+ }
249
+
250
+ access(EditPhase) fun borrowPricerAuth(): auth(Mutate) &{Pricer} {
251
+ return &self.details.pricer
252
+ }
253
+
254
+ access(EditPhase) fun borrowAddressVerifierAuth(): auth(Mutate) &{AddressVerifier} {
255
+ return &self.details.addressVerifier
256
+ }
257
+
258
+ init(details: PhaseDetails) {
259
+ self.details = details
260
+ }
261
+ }
262
+
263
+ access(all) resource interface PhasePublic {
264
+ // What does a phase need to be able to answer/manage?
265
+ // - What are the details of the phase being interactive with?
266
+ // - How many items are left in the current phase?
267
+ // - Can Address x mint on a phase?
268
+ // - What is the cost to mint for the phase I am interested in (for address x)?
269
+ access(all) fun getDetails(): PhaseDetails
270
+ access(all) fun isActive(): Bool
271
+ }
272
+
273
+ access(all) struct PhaseDetails {
274
+ // handles whether a phase is on or not
275
+ access(all) let switcher: {Switcher}
276
+
277
+ // display information about a phase
278
+ access(all) let display: MetadataViews.Display?
279
+
280
+ // handles the pricing of a phase
281
+ access(all) let pricer: {Pricer}
282
+
283
+ // verifies whether an address is able to mint
284
+ access(all) let addressVerifier: {AddressVerifier}
285
+
286
+ // placecholder data dictionary to allow new fields to be accessed
287
+ access(all) let data: {String: AnyStruct}
288
+
289
+ init(switcher: {Switcher}, display: MetadataViews.Display?, pricer: {Pricer}, addressVerifier: {AddressVerifier}) {
290
+ self.switcher = switcher
291
+ self.display = display
292
+ self.pricer = pricer
293
+ self.addressVerifier = addressVerifier
294
+
295
+ self.data = {}
296
+ }
297
+ }
298
+
299
+ access(all) struct interface AddressVerifier {
300
+ access(all) fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool {
301
+ return true
302
+ }
303
+
304
+ access(all) fun remainingForAddress(addr: Address, totalMinted: Int): Int? {
305
+ return nil
306
+ }
307
+ }
308
+
309
+ access(all) struct interface Pricer {
310
+ access(all) fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64
311
+ access(all) fun getPaymentTypes(): [Type]
312
+ }
313
+
314
+ access(all) resource interface Minter {
315
+ access(contract) fun mint(payment: @{FungibleToken.Vault}, amount: Int, phase: &FlowtyDrops.Phase, data: {String: AnyStruct}): @[{NonFungibleToken.NFT}] {
316
+ let resourceAddress = AddressUtils.parseAddress(self.getType())!
317
+ let receiver = getAccount(resourceAddress).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver).borrow()
318
+ ?? panic("invalid flow token receiver")
319
+ receiver.deposit(from: <-payment)
320
+
321
+ let nfts: @[{NonFungibleToken.NFT}] <- []
322
+
323
+ var count = 0
324
+ while count < amount {
325
+ count = count + 1
326
+ nfts.append(<- self.createNextNFT())
327
+ }
328
+
329
+ return <- nfts
330
+ }
331
+
332
+ access(contract) fun createNextNFT(): @{NonFungibleToken.NFT}
333
+ }
334
+
335
+ access(all) struct DropResolver {
336
+ access(self) let cap: Capability<&{ContainerPublic}>
337
+
338
+ access(all) fun borrowContainer(): &{ContainerPublic}? {
339
+ return self.cap.borrow()
340
+ }
341
+
342
+ init(cap: Capability<&{ContainerPublic}>) {
343
+ pre {
344
+ cap.check(): "container capability is not valid"
345
+ }
346
+
347
+ self.cap = cap
348
+ }
349
+ }
350
+
351
+ access(all) resource interface ContainerPublic {
352
+ access(all) fun borrowDropPublic(id: UInt64): &{DropPublic}?
353
+ access(all) fun getIDs(): [UInt64]
354
+ }
355
+
356
+ // Contains drops.
357
+ access(all) resource Container: ContainerPublic {
358
+ access(self) let drops: @{UInt64: Drop}
359
+
360
+ access(Owner) fun addDrop(_ drop: @Drop) {
361
+ let details = drop.getDetails()
362
+
363
+ let phases = drop.borrowAllPhases()
364
+ assert(phases.length > 0, message: "drops must have at least one phase to be added to a container")
365
+
366
+ let firstPhaseDetails = phases[0].getDetails()
367
+
368
+ emit DropAdded(
369
+ address: self.owner!.address,
370
+ id: drop.uuid,
371
+ name: details.display.name,
372
+ description: details.display.description,
373
+ imageUrl: details.display.thumbnail.uri(),
374
+ start: firstPhaseDetails.switcher.getStart(),
375
+ end: firstPhaseDetails.switcher.getEnd()
376
+ )
377
+ destroy self.drops.insert(key: drop.uuid, <-drop)
378
+ }
379
+
380
+ access(Owner) fun removeDrop(id: UInt64): @Drop {
381
+ pre {
382
+ self.drops.containsKey(id): "drop was not found"
383
+ }
384
+
385
+ return <- self.drops.remove(key: id)!
386
+ }
387
+
388
+ access(Owner) fun borrowDrop(id: UInt64): auth(Owner) &Drop? {
389
+ return &self.drops[id]
390
+ }
391
+
392
+ access(all) fun borrowDropPublic(id: UInt64): &{DropPublic}? {
393
+ return &self.drops[id]
394
+ }
395
+
396
+ access(all) fun getIDs(): [UInt64] {
397
+ return self.drops.keys
398
+ }
399
+
400
+ init() {
401
+ self.drops <- {}
402
+ }
403
+ }
404
+
405
+ access(all) fun createPhase(details: PhaseDetails): @Phase {
406
+ return <- create Phase(details: details)
407
+ }
408
+
409
+ access(all) fun createDrop(details: DropDetails, minterCap: Capability<&{Minter}>, phases: @[Phase]): @Drop {
410
+ return <- create Drop(details: details, minterCap: minterCap, phases: <- phases)
411
+ }
412
+
413
+ access(all) fun createContainer(): @Container {
414
+ return <- create Container()
415
+ }
416
+
417
+ access(all) fun getMinterStoragePath(type: Type): StoragePath {
418
+ let segments = type.identifier.split(separator: ".")
419
+ let identifier = "FlowtyDrops_Minter_".concat(segments[1]).concat(segments[2])
420
+ return StoragePath(identifier: identifier)!
421
+ }
422
+
423
+ init() {
424
+ let identifier = "FlowtyDrops_".concat(self.account.address.toString())
425
+ let containerIdentifier = identifier.concat("_Container")
426
+ let minterIdentifier = identifier.concat("_Minter")
427
+
428
+ self.ContainerStoragePath = StoragePath(identifier: containerIdentifier)!
429
+ self.ContainerPublicPath = PublicPath(identifier: containerIdentifier)!
430
+ }
431
+ }
@@ -0,0 +1,48 @@
1
+ import "FlowtyDrops"
2
+ import "FlowToken"
3
+
4
+ /*
5
+ This contract contains implementations of the FlowtyDrops.Pricer interface.
6
+ You can use these, or any custom implementation for the phases of your drop.
7
+ */
8
+ access(all) contract FlowtyPricers {
9
+
10
+ /*
11
+ The FlatPrice Pricer implementation has a set price and token type. Every mint is the same cost regardless of
12
+ the number minter, or what address is minting
13
+ */
14
+ access(all) struct FlatPrice: FlowtyDrops.Pricer {
15
+ access(all) var price: UFix64
16
+ access(all) let paymentTokenType: String
17
+
18
+ access(all) view fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64 {
19
+ return self.price * UFix64(num)
20
+ }
21
+
22
+ access(all) view fun getPaymentTypes(): [Type] {
23
+ return [CompositeType(self.paymentTokenType)!]
24
+ }
25
+
26
+ access(Mutate) fun setPrice(price: UFix64) {
27
+ self.price = price
28
+ }
29
+
30
+ init(price: UFix64, paymentTokenType: Type) {
31
+ self.price = price
32
+ self.paymentTokenType = paymentTokenType.identifier
33
+ }
34
+ }
35
+
36
+ /*
37
+ The Free Pricer can be used for a free mint, it has no price and always marks its payment type as @FlowToken.Vault
38
+ */
39
+ access(all) struct Free: FlowtyDrops.Pricer {
40
+ access(all) fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64 {
41
+ return 0.0
42
+ }
43
+
44
+ access(all) fun getPaymentTypes(): [Type] {
45
+ return [Type<@FlowToken.Vault>()]
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,113 @@
1
+ import "FlowtyDrops"
2
+
3
+ /*
4
+ This contract contains implementations for the FlowtyDrops.Switcher struct interface.
5
+ You can use these implementations, or your own, for switches when configuring a drop
6
+ */
7
+ access(all) contract FlowtySwitchers {
8
+ /*
9
+ The AlwaysOn Switcher is always on and never ends.
10
+ */
11
+ access(all) struct AlwaysOn: FlowtyDrops.Switcher {
12
+ access(all) view fun hasStarted(): Bool {
13
+ return true
14
+ }
15
+
16
+ access(all) view fun hasEnded(): Bool {
17
+ return false
18
+ }
19
+
20
+ access(all) view fun getStart(): UInt64? {
21
+ return nil
22
+ }
23
+
24
+ access(all) view fun getEnd(): UInt64? {
25
+ return nil
26
+ }
27
+ }
28
+
29
+ /*
30
+ The manual switcher is used to explicitly toggle a drop.
31
+ This version of switcher allows a creator to turn on or off a drop at will
32
+ */
33
+ access(all) struct ManualSwitch: FlowtyDrops.Switcher {
34
+ access(self) var started: Bool
35
+ access(self) var ended: Bool
36
+
37
+ access(all) view fun hasStarted(): Bool {
38
+ return self.started
39
+ }
40
+
41
+ access(all) view fun hasEnded(): Bool {
42
+ return self.ended
43
+ }
44
+
45
+ access(all) view fun getStart(): UInt64? {
46
+ return nil
47
+ }
48
+
49
+ access(all) view fun getEnd(): UInt64? {
50
+ return nil
51
+ }
52
+
53
+ access(Mutate) fun setStarted(_ b: Bool) {
54
+ self.started = b
55
+ }
56
+
57
+ access(Mutate) fun setEnded(_ b: Bool) {
58
+ self.ended = b
59
+ }
60
+
61
+ init() {
62
+ self.started = false
63
+ self.ended = false
64
+ }
65
+ }
66
+
67
+ /*
68
+ TimestampSwitch uses block timestamps to determine if a phase or drop is live or not.
69
+ A timestamp switcher has a start and an end time.
70
+ */
71
+ access(all) struct TimestampSwitch: FlowtyDrops.Switcher {
72
+ access(all) var start: UInt64?
73
+ access(all) var end: UInt64?
74
+
75
+
76
+ access(all) view fun hasStarted(): Bool {
77
+ return self.start == nil || UInt64(getCurrentBlock().timestamp) >= self.start!
78
+ }
79
+
80
+ access(all) view fun hasEnded(): Bool {
81
+ if self.end == nil {
82
+ return false
83
+ }
84
+
85
+ return UInt64(getCurrentBlock().timestamp) > self.end!
86
+ }
87
+
88
+ access(all) view fun getStart(): UInt64? {
89
+ return self.start
90
+ }
91
+
92
+ access(all) view fun getEnd(): UInt64? {
93
+ return self.end
94
+ }
95
+
96
+ access(Mutate) fun setStart(start: UInt64?) {
97
+ self.start = start
98
+ }
99
+
100
+ access(Mutate) fun setEnd(end: UInt64?) {
101
+ self.end = end
102
+ }
103
+
104
+ init(start: UInt64?, end: UInt64?) {
105
+ pre {
106
+ start == nil || end == nil || start! < end!: "start must be less than end"
107
+ }
108
+
109
+ self.start = start
110
+ self.end = end
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,14 @@
1
+ import "FlowtyDrops"
2
+ import "NFTMetadata"
3
+ import "AddressUtils"
4
+ import "ContractInitializer"
5
+
6
+ access(all) contract ContractBorrower {
7
+ access(all) fun borrowInitializer(typeIdentifier: String): &{ContractInitializer} {
8
+ let type = CompositeType(typeIdentifier) ?? panic("invalid type identifier")
9
+ let addr = AddressUtils.parseAddress(type)!
10
+
11
+ let contractName = type.identifier.split(separator: ".")[2]
12
+ return getAccount(addr).contracts.borrow<&{ContractInitializer}>(name: contractName)!
13
+ }
14
+ }
@@ -0,0 +1,7 @@
1
+ import "FlowtyDrops"
2
+ import "NFTMetadata"
3
+ import "AddressUtils"
4
+
5
+ access(all) contract interface ContractInitializer {
6
+ access(all) fun initialize(contractAcct: auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account, params: {String: AnyStruct}): NFTMetadata.InitializedCaps
7
+ }
@@ -0,0 +1,58 @@
1
+ import "ContractInitializer"
2
+ import "NFTMetadata"
3
+ import "FlowtyDrops"
4
+
5
+ access(all) contract OpenEditionInitializer: ContractInitializer {
6
+ access(all) fun initialize(contractAcct: auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account, params: {String: AnyStruct}): NFTMetadata.InitializedCaps {
7
+ pre {
8
+ params["data"] != nil: "missing param data"
9
+ params["data"]!.getType() == Type<NFTMetadata.Metadata>(): "data param must be of type NFTMetadata.Metadata"
10
+ params["collectionInfo"] != nil: "missing param collectionInfo"
11
+ params["collectionInfo"]!.getType() == Type<NFTMetadata.CollectionInfo>(): "collectionInfo param must be of type NFTMetadata.CollectionInfo"
12
+ }
13
+
14
+ let data = params["data"]! as! NFTMetadata.Metadata
15
+ let collectionInfo = params["collectionInfo"]! as! NFTMetadata.CollectionInfo
16
+
17
+ let acct: auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account = Account(payer: contractAcct)
18
+ let cap = acct.capabilities.account.issue<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>()
19
+
20
+ let t = self.getType()
21
+ let contractName = t.identifier.split(separator: ".")[2]
22
+
23
+ self.account.storage.save(cap, to: StoragePath(identifier: "metadataAuthAccount_".concat(contractName))!)
24
+
25
+ // do we have information to setup a drop as well?
26
+ if params.containsKey("dropDetails") && params.containsKey("phaseDetails") && params.containsKey("minterController") {
27
+ // extract expected keys
28
+ let minterCap = params["minterController"]! as! Capability<&{FlowtyDrops.Minter}>
29
+ let dropDetails = params["dropDetails"]! as! FlowtyDrops.DropDetails
30
+ let phaseDetails = params["phaseDetails"]! as! [FlowtyDrops.PhaseDetails]
31
+
32
+ assert(minterCap.check(), message: "invalid minter capability")
33
+
34
+
35
+ let phases: @[FlowtyDrops.Phase] <- []
36
+ for p in phaseDetails {
37
+ phases.append(<- FlowtyDrops.createPhase(details: p))
38
+ }
39
+
40
+ let drop <- FlowtyDrops.createDrop(details: dropDetails, minterCap: minterCap, phases: <- phases)
41
+ if acct.storage.borrow<&AnyResource>(from: FlowtyDrops.ContainerStoragePath) == nil {
42
+ acct.storage.save(<- FlowtyDrops.createContainer(), to: FlowtyDrops.ContainerStoragePath)
43
+
44
+ acct.capabilities.unpublish(FlowtyDrops.ContainerPublicPath)
45
+ acct.capabilities.publish(
46
+ acct.capabilities.storage.issue<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerStoragePath),
47
+ at: FlowtyDrops.ContainerPublicPath
48
+ )
49
+ }
50
+
51
+ let container = acct.storage.borrow<auth(FlowtyDrops.Owner) &FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath)
52
+ ?? panic("drops container not found")
53
+ container.addDrop(<- drop)
54
+ }
55
+
56
+ return NFTMetadata.initialize(acct: acct, collectionInfo: collectionInfo, collectionType: self.getType())
57
+ }
58
+ }