@flowtyio/flow-contracts 0.1.4 → 0.1.5

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.
@@ -35,7 +35,7 @@ access(all) contract DropFactory {
35
35
 
36
36
 
37
37
  let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
38
- let dropDetails = FlowtyDrops.DropDetails(display: dropDisplay, medias: nil, commissionRate: 0.05, nftType: nftTypeIdentifier)
38
+ let dropDetails = FlowtyDrops.DropDetails(display: dropDisplay, medias: nil, commissionRate: 0.05, nftType: nftTypeIdentifier, paymentTokenTypes: {paymentTokenType.identifier: true})
39
39
  let drop <- FlowtyDrops.createDrop(details: dropDetails, minterCap: minterCap, phases: <- [<-phase])
40
40
 
41
41
  return <- drop
@@ -67,7 +67,7 @@ access(all) contract DropFactory {
67
67
  let phase <- FlowtyDrops.createPhase(details: phaseDetails)
68
68
 
69
69
  let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
70
- let dropDetails = FlowtyDrops.DropDetails(display: dropDisplay, medias: nil, commissionRate: 0.05, nftType: nftTypeIdentifier)
70
+ let dropDetails = FlowtyDrops.DropDetails(display: dropDisplay, medias: nil, commissionRate: 0.05, nftType: nftTypeIdentifier, paymentTokenTypes: {paymentTokenType.identifier: true})
71
71
  let drop <- FlowtyDrops.createDrop(details: dropDetails, minterCap: minterCap, phases: <- [<-phase])
72
72
 
73
73
  return <- drop
@@ -36,6 +36,7 @@ access(all) contract DropTypes {
36
36
  access(all) let commissionRate: UFix64
37
37
  access(all) let nftType: String
38
38
  access(all) let creator: Address?
39
+ access(all) let paymentTokenTypes: {String: Bool}
39
40
 
40
41
  access(all) let address: Address?
41
42
  access(all) let mintedByAddress: Int?
@@ -58,7 +59,8 @@ access(all) contract DropTypes {
58
59
  address: Address?,
59
60
  phases: [PhaseSummary],
60
61
  royaltyRate: UFix64,
61
- creator: Address?
62
+ creator: Address?,
63
+ paymentTokenTypes: {String: Bool}
62
64
  ) {
63
65
  self.id = id
64
66
  self.display = Display(display)
@@ -82,6 +84,7 @@ access(all) contract DropTypes {
82
84
  self.blockHeight = b.height
83
85
  self.blockTimestamp = UInt64(b.timestamp)
84
86
  self.royaltyRate = royaltyRate
87
+ self.paymentTokenTypes = paymentTokenTypes
85
88
  }
86
89
  }
87
90
 
@@ -118,7 +121,7 @@ access(all) contract DropTypes {
118
121
  access(all) let remainingForAddress: Int?
119
122
  access(all) let maxPerMint: Int?
120
123
 
121
- access(all) let quote: Quote?
124
+ access(all) let quotes: {String: Quote}
122
125
 
123
126
  init(
124
127
  index: Int,
@@ -127,7 +130,7 @@ access(all) contract DropTypes {
127
130
  totalMinted: Int?,
128
131
  minter: Address?,
129
132
  quantity: Int?,
130
- paymentIdentifier: String?
133
+ paymentIdentifiers: [String]
131
134
  ) {
132
135
  self.index = index
133
136
  self.id = phase.uuid
@@ -155,19 +158,17 @@ access(all) contract DropTypes {
155
158
  self.remainingForAddress = nil
156
159
  }
157
160
 
158
- self.maxPerMint = d.addressVerifier.getMaxPerMint(addr: self.address, totalMinted: totalMinted ?? 0, data: {} as {String: AnyStruct})
161
+ self.maxPerMint = d.addressVerifier.getMaxPerMint(addr: self.address, totalMinted: totalMinted ?? 0, data: {})
162
+ self.quotes = {}
159
163
 
160
- if paymentIdentifier != nil && quantity != nil {
161
- let price = d.pricer.getPrice(num: quantity!, paymentTokenType: CompositeType(paymentIdentifier!)!, minter: minter)
162
-
163
- self.quote = Quote(price: price, quantity: quantity!, paymentIdentifier: paymentIdentifier!, minter: minter)
164
- } else {
165
- self.quote = nil
164
+ for paymentIdentifier in paymentIdentifiers {
165
+ let price = d.pricer.getPrice(num: quantity!, paymentTokenType: CompositeType(paymentIdentifier)!, minter: minter)
166
+ self.quotes[paymentIdentifier] = Quote(price: price, quantity: quantity!, paymentIdentifier: paymentIdentifier, minter: minter)
166
167
  }
167
168
  }
168
169
  }
169
170
 
170
- access(all) fun getDropSummary(nftTypeIdentifier: String, dropID: UInt64, minter: Address?, quantity: Int?, paymentIdentifier: String?): DropSummary? {
171
+ access(all) fun getDropSummary(nftTypeIdentifier: String, dropID: UInt64, minter: Address?, quantity: Int?, paymentIdentifiers: [String]): DropSummary? {
171
172
  let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
172
173
  let segments = nftTypeIdentifier.split(separator: ".")
173
174
  let contractAddress = AddressUtils.parseAddress(nftType)!
@@ -206,7 +207,7 @@ access(all) contract DropTypes {
206
207
  totalMinted: minter != nil ? dropDetails.minters[minter!] : nil,
207
208
  minter: minter,
208
209
  quantity: quantity,
209
- paymentIdentifier: paymentIdentifier
210
+ paymentIdentifiers: paymentIdentifiers.length > 0 ? paymentIdentifiers : dropDetails.paymentTokenTypes.keys
210
211
  )
211
212
  phaseSummaries.append(summary)
212
213
  }
@@ -231,13 +232,14 @@ access(all) contract DropTypes {
231
232
  address: minter,
232
233
  phases: phaseSummaries,
233
234
  royaltyRate: royaltyRate,
234
- creator: creator
235
+ creator: creator,
236
+ paymentTokenTypes: dropDetails.paymentTokenTypes
235
237
  )
236
238
 
237
239
  return dropSummary
238
240
  }
239
241
 
240
- access(all) fun getAllDropSummaries(nftTypeIdentifier: String, minter: Address?, quantity: Int?, paymentIdentifier: String?): [DropSummary] {
242
+ access(all) fun getAllDropSummaries(nftTypeIdentifier: String, minter: Address?, quantity: Int?, paymentIdentifiers: [String]): [DropSummary] {
241
243
  let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
242
244
  let segments = nftTypeIdentifier.split(separator: ".")
243
245
  let contractAddress = AddressUtils.parseAddress(nftType)!
@@ -278,7 +280,7 @@ access(all) contract DropTypes {
278
280
  totalMinted: minter != nil ? dropDetails.minters[minter!] : nil,
279
281
  minter: minter,
280
282
  quantity: quantity,
281
- paymentIdentifier: paymentIdentifier
283
+ paymentIdentifiers: paymentIdentifiers.length > 0 ? paymentIdentifiers : dropDetails.paymentTokenTypes.keys
282
284
  )
283
285
  phaseSummaries.append(summary)
284
286
  }
@@ -307,7 +309,8 @@ access(all) contract DropTypes {
307
309
  address: minter,
308
310
  phases: phaseSummaries,
309
311
  royaltyRate: royaltyRate,
310
- creator: creator
312
+ creator: creator,
313
+ paymentTokenTypes: dropDetails.paymentTokenTypes
311
314
  ))
312
315
  }
313
316
 
@@ -5,12 +5,19 @@ import "AddressUtils"
5
5
  import "FungibleTokenMetadataViews"
6
6
  import "FungibleTokenRouter"
7
7
 
8
+ // FlowtyDrops is a contract to help collections manage their primary sale needs on flow.
9
+ // Multiple drops can be made for a single contract (like how TopShot has had lots of pack drops),
10
+ // and can be split into phases to represent different behaviors over the course of a drop
8
11
  access(all) contract FlowtyDrops {
12
+ // The total number of nfts minted by this contract
13
+ access(all) var TotalMinted: UInt64
14
+
9
15
  access(all) let ContainerStoragePath: StoragePath
10
16
  access(all) let ContainerPublicPath: PublicPath
11
17
 
12
18
  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)
19
+ access(all) event DropRemoved(address: Address, id: UInt64)
20
+ access(all) event Minted(address: Address, dropID: UInt64, phaseID: UInt64, nftID: UInt64, nftType: String, totalMinted: UInt64)
14
21
  access(all) event PhaseAdded(dropID: UInt64, dropAddress: Address, id: UInt64, index: Int, activeCheckerType: String, pricerType: String, addressVerifierType: String)
15
22
  access(all) event PhaseRemoved(dropID: UInt64, dropAddress: Address, id: UInt64)
16
23
 
@@ -20,9 +27,9 @@ access(all) contract FlowtyDrops {
20
27
  // Interface to expose all the components necessary to participate in a drop
21
28
  // and to ask questions about a drop.
22
29
  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}]
30
+ access(all) view fun borrowPhasePublic(index: Int): &{PhasePublic}
31
+ access(all) view fun borrowActivePhases(): [&{PhasePublic}]
32
+ access(all) view fun borrowAllPhases(): [&{PhasePublic}]
26
33
  access(all) fun mint(
27
34
  payment: @{FungibleToken.Vault},
28
35
  amount: Int,
@@ -31,8 +38,18 @@ access(all) contract FlowtyDrops {
31
38
  receiverCap: Capability<&{NonFungibleToken.CollectionPublic}>,
32
39
  commissionReceiver: Capability<&{FungibleToken.Receiver}>?,
33
40
  data: {String: AnyStruct}
34
- ): @{FungibleToken.Vault}
35
- access(all) fun getDetails(): DropDetails
41
+ ): @{FungibleToken.Vault} {
42
+ pre {
43
+ self.getDetails().paymentTokenTypes[payment.getType().identifier] == true: "unsupported payment token type"
44
+ receiverCap.check(): "unvalid nft receiver capability"
45
+ commissionReceiver == nil || commissionReceiver!.check(): "commission receiver must be nil or a valid capability"
46
+ self.getType() == Type<@Drop>(): "unsupported type implementing DropPublic"
47
+ expectedType.isSubtype(of: Type<@{NonFungibleToken.NFT}>()): "expected type must be an NFT"
48
+ expectedType.identifier == self.getDetails().nftType: "expected type does not match drop details type"
49
+ receiverCap.check(): "receiver capability is not valid"
50
+ }
51
+ }
52
+ access(all) view fun getDetails(): DropDetails
36
53
  }
37
54
 
38
55
  // A phase represents a stage of a drop. Some drops will only have one
@@ -47,23 +64,23 @@ access(all) contract FlowtyDrops {
47
64
  access(all) let resources: @{String: AnyResource}
48
65
 
49
66
  // returns whether this phase of a drop has started.
50
- access(all) fun isActive(): Bool {
67
+ access(all) view fun isActive(): Bool {
51
68
  return self.details.activeChecker.hasStarted() && !self.details.activeChecker.hasEnded()
52
69
  }
53
70
 
54
- access(all) fun getDetails(): PhaseDetails {
71
+ access(all) view fun getDetails(): PhaseDetails {
55
72
  return self.details
56
73
  }
57
74
 
58
- access(EditPhase) fun borrowActiveCheckerAuth(): auth(Mutate) &{ActiveChecker} {
75
+ access(EditPhase) view fun borrowActiveCheckerAuth(): auth(Mutate) &{ActiveChecker} {
59
76
  return &self.details.activeChecker
60
77
  }
61
78
 
62
- access(EditPhase) fun borrowPricerAuth(): auth(Mutate) &{Pricer} {
79
+ access(EditPhase) view fun borrowPricerAuth(): auth(Mutate) &{Pricer} {
63
80
  return &self.details.pricer
64
81
  }
65
82
 
66
- access(EditPhase) fun borrowAddressVerifierAuth(): auth(Mutate) &{AddressVerifier} {
83
+ access(EditPhase) view fun borrowAddressVerifierAuth(): auth(Mutate) &{AddressVerifier} {
67
84
  return &self.details.addressVerifier
68
85
  }
69
86
 
@@ -75,6 +92,8 @@ access(all) contract FlowtyDrops {
75
92
  }
76
93
  }
77
94
 
95
+ // The primary resource of this contract. A drop has some top-level details, and some phase-specific details which are encapsulated
96
+ // by each phase.
78
97
  access(all) resource Drop: DropPublic {
79
98
  access(all) event ResourceDestroyed(
80
99
  uuid: UInt64 = self.uuid,
@@ -87,8 +106,11 @@ access(all) contract FlowtyDrops {
87
106
  access(self) let phases: @[Phase]
88
107
  // the details of a drop. This includes things like display information and total number of mints
89
108
  access(self) let details: DropDetails
109
+ // capability to mint nfts with. Regardless of where a drop is hosted, the minter itself is what is responsible for creating nfts
110
+ // and is used by the drop's mint method.
90
111
  access(self) let minterCap: Capability<&{Minter}>
91
112
 
113
+ // general-purpose property bags which are needed to ensure extensibility of this resource
92
114
  access(all) let data: {String: AnyStruct}
93
115
  access(all) let resources: @{String: AnyResource}
94
116
 
@@ -102,10 +124,7 @@ access(all) contract FlowtyDrops {
102
124
  data: {String: AnyStruct}
103
125
  ): @{FungibleToken.Vault} {
104
126
  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
127
  self.phases.length > phaseIndex: "phase index is too high"
108
- receiverCap.check(): "receiver capability is not valid"
109
128
  }
110
129
 
111
130
  // validate the payment vault amount and type
@@ -116,6 +135,7 @@ access(all) contract FlowtyDrops {
116
135
  )
117
136
 
118
137
  let paymentAmount = phase.details.pricer.getPrice(num: amount, paymentTokenType: payment.getType(), minter: receiverCap.address)
138
+ assert(payment.balance >= paymentAmount, message: "payment balance is lower than payment amount")
119
139
  let withdrawn <- payment.withdraw(amount: paymentAmount) // make sure that we have a fresh vault resource
120
140
 
121
141
  // take commission
@@ -124,13 +144,14 @@ access(all) contract FlowtyDrops {
124
144
  commissionReceiver!.borrow()!.deposit(from: <-commission)
125
145
  }
126
146
 
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")
147
+ // The balance of the payment sent to the creator is equal to the paymentAmount - fees
148
+ assert(paymentAmount * (1.0 - self.details.commissionRate) == withdrawn.balance, message: "incorrect payment amount")
128
149
  assert(phase.details.pricer.getPaymentTypes().contains(withdrawn.getType()), message: "unsupported payment type")
150
+ assert(phase.details.activeChecker.hasStarted() && !phase.details.activeChecker.hasEnded(), message: "phase is not active")
129
151
 
130
152
  // mint the nfts
131
153
  let minter = self.minterCap.borrow() ?? panic("minter capability could not be borrowed")
132
154
  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
155
  assert(mintedNFTs.length == amount, message: "incorrect number of items returned")
135
156
 
136
157
  // distribute to receiver
@@ -141,7 +162,8 @@ access(all) contract FlowtyDrops {
141
162
  let nft <- mintedNFTs.removeFirst()
142
163
 
143
164
  let nftType = nft.getType()
144
- emit Minted(address: receiverCap.address, dropID: self.uuid, phaseID: phase.uuid, nftID: nft.id, nftType: nftType.identifier)
165
+ FlowtyDrops.TotalMinted = FlowtyDrops.TotalMinted + 1
166
+ emit Minted(address: receiverCap.address, dropID: self.uuid, phaseID: phase.uuid, nftID: nft.id, nftType: nftType.identifier, totalMinted: FlowtyDrops.TotalMinted)
145
167
 
146
168
  // validate that every nft is the right type
147
169
  assert(nftType == expectedType, message: "unexpected nft type was minted")
@@ -156,23 +178,23 @@ access(all) contract FlowtyDrops {
156
178
  return <- payment
157
179
  }
158
180
 
159
- access(Owner) fun borrowPhase(index: Int): auth(EditPhase) &Phase {
181
+ access(Owner) view fun borrowPhase(index: Int): auth(EditPhase) &Phase {
160
182
  return &self.phases[index]
161
183
  }
162
184
 
163
185
 
164
- access(all) fun borrowPhasePublic(index: Int): &{PhasePublic} {
186
+ access(all) view fun borrowPhasePublic(index: Int): &{PhasePublic} {
165
187
  return &self.phases[index]
166
188
  }
167
189
 
168
- access(all) fun borrowActivePhases(): [&{PhasePublic}] {
169
- let arr: [&{PhasePublic}] = []
190
+ access(all) view fun borrowActivePhases(): [&{PhasePublic}] {
191
+ var arr: [&{PhasePublic}] = []
170
192
  var count = 0
171
193
  while count < self.phases.length {
172
194
  let ref = self.borrowPhasePublic(index: count)
173
195
  let activeChecker = ref.getDetails().activeChecker
174
196
  if activeChecker.hasStarted() && !activeChecker.hasEnded() {
175
- arr.append(ref)
197
+ arr = arr.concat([ref])
176
198
  }
177
199
 
178
200
  count = count + 1
@@ -181,12 +203,12 @@ access(all) contract FlowtyDrops {
181
203
  return arr
182
204
  }
183
205
 
184
- access(all) fun borrowAllPhases(): [&{PhasePublic}] {
185
- let arr: [&{PhasePublic}] = []
206
+ access(all) view fun borrowAllPhases(): [&{PhasePublic}] {
207
+ var arr: [&{PhasePublic}] = []
186
208
  var index = 0
187
209
  while index < self.phases.length {
188
210
  let ref = self.borrowPhasePublic(index: index)
189
- arr.append(ref)
211
+ arr = arr.concat([ref])
190
212
  index = index + 1
191
213
  }
192
214
 
@@ -217,7 +239,7 @@ access(all) contract FlowtyDrops {
217
239
  return <- phase
218
240
  }
219
241
 
220
- access(all) fun getDetails(): DropDetails {
242
+ access(all) view fun getDetails(): DropDetails {
221
243
  return self.details
222
244
  }
223
245
 
@@ -242,6 +264,7 @@ access(all) contract FlowtyDrops {
242
264
  access(all) var minters: {Address: Int}
243
265
  access(all) let commissionRate: UFix64
244
266
  access(all) let nftType: String
267
+ access(all) let paymentTokenTypes: {String: Bool}
245
268
 
246
269
  access(all) let data: {String: AnyStruct}
247
270
 
@@ -254,7 +277,7 @@ access(all) contract FlowtyDrops {
254
277
  self.minters[addr] = self.minters[addr]! + num
255
278
  }
256
279
 
257
- init(display: MetadataViews.Display, medias: MetadataViews.Medias?, commissionRate: UFix64, nftType: String) {
280
+ init(display: MetadataViews.Display, medias: MetadataViews.Medias?, commissionRate: UFix64, nftType: String, paymentTokenTypes: {String: Bool}) {
258
281
  pre {
259
282
  nftType != "": "nftType should be a composite type identifier"
260
283
  }
@@ -265,6 +288,8 @@ access(all) contract FlowtyDrops {
265
288
  self.commissionRate = commissionRate
266
289
  self.minters = {}
267
290
  self.nftType = nftType
291
+ self.paymentTokenTypes = paymentTokenTypes
292
+
268
293
  self.data = {}
269
294
  }
270
295
  }
@@ -289,8 +314,8 @@ access(all) contract FlowtyDrops {
289
314
  // - How many items are left in the current phase?
290
315
  // - Can Address x mint on a phase?
291
316
  // - 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
317
+ access(all) view fun getDetails(): PhaseDetails
318
+ access(all) view fun isActive(): Bool
294
319
  }
295
320
 
296
321
  access(all) struct PhaseDetails {
@@ -319,6 +344,7 @@ access(all) contract FlowtyDrops {
319
344
  }
320
345
  }
321
346
 
347
+ // The AddressVerifier interface is responsible for determining whether an address is permitted to mint or not
322
348
  access(all) struct interface AddressVerifier {
323
349
  access(all) fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool {
324
350
  return true
@@ -333,12 +359,15 @@ access(all) contract FlowtyDrops {
333
359
  }
334
360
  }
335
361
 
362
+ // The pricer interface is responsible for the cost of a mint. It can vary by phase
336
363
  access(all) struct interface Pricer {
337
364
  access(all) fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64
338
365
  access(all) fun getPaymentTypes(): [Type]
339
366
  }
340
367
 
341
368
  access(all) resource interface Minter {
369
+ // mint is only able to be called either by this contract (FlowtyDrops) or the implementing contract.
370
+ // In its default implementation, it is assumed that the receiver capability for payment is the FungibleTokenRouter
342
371
  access(contract) fun mint(payment: @{FungibleToken.Vault}, amount: Int, phase: &FlowtyDrops.Phase, data: {String: AnyStruct}): @[{NonFungibleToken.NFT}] {
343
372
  let resourceAddress = AddressUtils.parseAddress(self.getType())!
344
373
  let receiver = getAccount(resourceAddress).capabilities.get<&{FungibleToken.Receiver}>(FungibleTokenRouter.PublicPath).borrow()
@@ -356,9 +385,11 @@ access(all) contract FlowtyDrops {
356
385
  return <- nfts
357
386
  }
358
387
 
388
+ // required so that the minter interface has a way to create NFTs on its implementing resource
359
389
  access(contract) fun createNextNFT(): @{NonFungibleToken.NFT}
360
390
  }
361
391
 
392
+ // Struct to wrap obtaining a Drop container. Intended for use with the ViewResolver contract interface
362
393
  access(all) struct DropResolver {
363
394
  access(self) let cap: Capability<&{ContainerPublic}>
364
395
 
@@ -380,7 +411,7 @@ access(all) contract FlowtyDrops {
380
411
  access(all) fun getIDs(): [UInt64]
381
412
  }
382
413
 
383
- // Contains drops.
414
+ // Container holds drops so that one address can host more than one drop at once
384
415
  access(all) resource Container: ContainerPublic {
385
416
  access(self) let drops: @{UInt64: Drop}
386
417
 
@@ -413,6 +444,7 @@ access(all) contract FlowtyDrops {
413
444
  self.drops.containsKey(id): "drop was not found"
414
445
  }
415
446
 
447
+ emit DropRemoved(address: self.owner!.address, id: id)
416
448
  return <- self.drops.remove(key: id)!
417
449
  }
418
450
 
@@ -461,5 +493,7 @@ access(all) contract FlowtyDrops {
461
493
 
462
494
  self.ContainerStoragePath = StoragePath(identifier: containerIdentifier)!
463
495
  self.ContainerPublicPath = PublicPath(identifier: containerIdentifier)!
496
+
497
+ self.TotalMinted = 0
464
498
  }
465
499
  }
@@ -40,7 +40,14 @@ access(all) contract interface BaseNFT: ViewResolver {
40
40
  ]
41
41
  }
42
42
 
43
+ // In case the implementor of `NFT` wants to override resolved views,
44
+ // the actual logic to perform view resolution is done in another method,
45
+ // with this one calling directly into it.
43
46
  access(all) fun resolveView(_ view: Type): AnyStruct? {
47
+ return self._resolveView(view)
48
+ }
49
+
50
+ access(all) fun _resolveView(_ view: Type): AnyStruct? {
44
51
  if view == Type<MetadataViews.Serial>() {
45
52
  return MetadataViews.Serial(self.id)
46
53
  }
@@ -94,6 +94,7 @@ access(all) contract NFTMetadata {
94
94
  access(Owner) fun addMetadata(id: UInt64, data: Metadata) {
95
95
  pre {
96
96
  self.metadata[id] == nil: "id already has metadata assigned"
97
+ !self.frozen: "metadata is frozen and cannot be updated"
97
98
  }
98
99
 
99
100
  self.metadata[id] = data
package/flow.json CHANGED
@@ -359,7 +359,7 @@
359
359
  "source": "./contracts/flowty-drops/ContractManager.cdc",
360
360
  "aliases": {
361
361
  "testing": "0x0000000000000006",
362
- "testnet": "0x155bce6ac93f3e00",
362
+ "testnet": "0x5d06d5357cdf8063",
363
363
  "emulator": "0xf8d6e0586b0a20c7"
364
364
  }
365
365
  },
@@ -367,7 +367,7 @@
367
367
  "source": "./contracts/flowty-drops/initializers/ContractInitializer.cdc",
368
368
  "aliases": {
369
369
  "testing": "0x0000000000000006",
370
- "testnet": "0x155bce6ac93f3e00",
370
+ "testnet": "0x5d06d5357cdf8063",
371
371
  "emulator": "0xf8d6e0586b0a20c7"
372
372
  }
373
373
  },
@@ -375,7 +375,7 @@
375
375
  "source": "./contracts/flowty-drops/initializers/ContractBorrower.cdc",
376
376
  "aliases": {
377
377
  "testing": "0x0000000000000006",
378
- "testnet": "0x155bce6ac93f3e00",
378
+ "testnet": "0x5d06d5357cdf8063",
379
379
  "emulator": "0xf8d6e0586b0a20c7"
380
380
  }
381
381
  },
@@ -383,7 +383,7 @@
383
383
  "source": "./contracts/flowty-drops/initializers/OpenEditionInitializer.cdc",
384
384
  "aliases": {
385
385
  "testing": "0x0000000000000006",
386
- "testnet": "0x155bce6ac93f3e00",
386
+ "testnet": "0x5d06d5357cdf8063",
387
387
  "emulator": "0xf8d6e0586b0a20c7"
388
388
  }
389
389
  },
@@ -391,7 +391,7 @@
391
391
  "source": "./contracts/flowty-drops/nft/BaseCollection.cdc",
392
392
  "aliases": {
393
393
  "testing": "0x0000000000000006",
394
- "testnet": "0x155bce6ac93f3e00",
394
+ "testnet": "0x5d06d5357cdf8063",
395
395
  "emulator": "0xf8d6e0586b0a20c7"
396
396
  }
397
397
  },
@@ -399,7 +399,7 @@
399
399
  "source": "./contracts/flowty-drops/nft/ContractFactoryTemplate.cdc",
400
400
  "aliases": {
401
401
  "testing": "0x0000000000000006",
402
- "testnet": "0x155bce6ac93f3e00",
402
+ "testnet": "0x5d06d5357cdf8063",
403
403
  "emulator": "0xf8d6e0586b0a20c7"
404
404
  }
405
405
  },
@@ -407,7 +407,7 @@
407
407
  "source": "./contracts/flowty-drops/nft/ContractFactory.cdc",
408
408
  "aliases": {
409
409
  "testing": "0x0000000000000006",
410
- "testnet": "0x155bce6ac93f3e00",
410
+ "testnet": "0x5d06d5357cdf8063",
411
411
  "emulator": "0xf8d6e0586b0a20c7"
412
412
  }
413
413
  },
@@ -415,7 +415,7 @@
415
415
  "source": "./contracts/flowty-drops/nft/OpenEditionTemplate.cdc",
416
416
  "aliases": {
417
417
  "testing": "0x0000000000000006",
418
- "testnet": "0x155bce6ac93f3e00",
418
+ "testnet": "0x5d06d5357cdf8063",
419
419
  "emulator": "0xf8d6e0586b0a20c7"
420
420
  }
421
421
  },
@@ -423,7 +423,7 @@
423
423
  "source": "./contracts/flowty-drops/nft/UniversalCollection.cdc",
424
424
  "aliases": {
425
425
  "testing": "0x0000000000000006",
426
- "testnet": "0x155bce6ac93f3e00",
426
+ "testnet": "0x5d06d5357cdf8063",
427
427
  "emulator": "0xf8d6e0586b0a20c7"
428
428
  }
429
429
  },
@@ -431,7 +431,7 @@
431
431
  "source": "./contracts/flowty-drops/nft/BaseNFT.cdc",
432
432
  "aliases": {
433
433
  "testing": "0x0000000000000006",
434
- "testnet": "0x155bce6ac93f3e00",
434
+ "testnet": "0x5d06d5357cdf8063",
435
435
  "emulator": "0xf8d6e0586b0a20c7"
436
436
  }
437
437
  },
@@ -439,7 +439,7 @@
439
439
  "source": "./contracts/flowty-drops/nft/NFTMetadata.cdc",
440
440
  "aliases": {
441
441
  "testing": "0x0000000000000006",
442
- "testnet": "0x155bce6ac93f3e00",
442
+ "testnet": "0x5d06d5357cdf8063",
443
443
  "emulator": "0xf8d6e0586b0a20c7"
444
444
  }
445
445
  },
@@ -447,7 +447,7 @@
447
447
  "source": "./contracts/flowty-drops/FlowtyDrops.cdc",
448
448
  "aliases": {
449
449
  "testing": "0x0000000000000006",
450
- "testnet": "0x155bce6ac93f3e00",
450
+ "testnet": "0x5d06d5357cdf8063",
451
451
  "emulator": "0xf8d6e0586b0a20c7"
452
452
  }
453
453
  },
@@ -455,7 +455,7 @@
455
455
  "source": "./contracts/flowty-drops/DropFactory.cdc",
456
456
  "aliases": {
457
457
  "testing": "0x0000000000000006",
458
- "testnet": "0x155bce6ac93f3e00",
458
+ "testnet": "0x5d06d5357cdf8063",
459
459
  "emulator": "0xf8d6e0586b0a20c7"
460
460
  }
461
461
  },
@@ -463,7 +463,7 @@
463
463
  "source": "./contracts/flowty-drops/FlowtyActiveCheckers.cdc",
464
464
  "aliases": {
465
465
  "testing": "0x0000000000000006",
466
- "testnet": "0x155bce6ac93f3e00",
466
+ "testnet": "0x5d06d5357cdf8063",
467
467
  "emulator": "0xf8d6e0586b0a20c7"
468
468
  }
469
469
  },
@@ -471,7 +471,7 @@
471
471
  "source": "./contracts/flowty-drops/FlowtyPricers.cdc",
472
472
  "aliases": {
473
473
  "testing": "0x0000000000000006",
474
- "testnet": "0x155bce6ac93f3e00",
474
+ "testnet": "0x5d06d5357cdf8063",
475
475
  "emulator": "0xf8d6e0586b0a20c7"
476
476
  }
477
477
  },
@@ -479,7 +479,7 @@
479
479
  "source": "./contracts/flowty-drops/FlowtyAddressVerifiers.cdc",
480
480
  "aliases": {
481
481
  "testing": "0x0000000000000006",
482
- "testnet": "0x155bce6ac93f3e00",
482
+ "testnet": "0x5d06d5357cdf8063",
483
483
  "emulator": "0xf8d6e0586b0a20c7"
484
484
  }
485
485
  },
@@ -487,7 +487,7 @@
487
487
  "source": "./contracts/flowty-drops/DropTypes.cdc",
488
488
  "aliases": {
489
489
  "testing": "0x0000000000000006",
490
- "testnet": "0x53ff16309e318ae2",
490
+ "testnet": "0xa8d83f18fd8ba80a",
491
491
  "emulator": "0xf8d6e0586b0a20c7"
492
492
  }
493
493
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowtyio/flow-contracts",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "main": "index.json",
5
5
  "description": "An NPM package for common flow contracts",
6
6
  "author": "flowtyio",