@flowtyio/flow-contracts 0.1.0-beta.8 → 0.1.0

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