@flowtyio/flow-contracts 0.1.0-beta.24 → 0.1.0-beta.26

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.
package/README.md CHANGED
@@ -49,7 +49,7 @@ Currently, the list includes:
49
49
  | FTProviderFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
50
50
  | FTReceiverFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
51
51
  | NFTCollectionPublicFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
52
- | NFTProviderAndCollectionPublicFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
52
+ | NFTProviderAndCollectionFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
53
53
  | NFTProviderFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
54
54
  | NFTCatalog | 0x49a7cda3a1eecc29 | 0x324c34e1c517e4db |
55
55
  | NFTCatalogAdmin | 0x49a7cda3a1eecc29 | 0x324c34e1c517e4db |
@@ -0,0 +1,97 @@
1
+ /*
2
+ https://github.com/Flowtyio/capability-cache
3
+
4
+ CapabilityCache helps manage capabilities which are issued but are not in public paths.
5
+ Rather than looping through all capabilities under a storage path and finding one that
6
+ matches the Capability type you want, the cache can be used to retrieve them
7
+ */
8
+ access(all) contract CapabilityCache {
9
+
10
+ access(all) let basePathIdentifier: String
11
+
12
+ access(all) event CapabilityAdded(owner: Address?, cacheUuid: UInt64, namespace: String, resourceType: Type, capabilityType: Type, capabilityID: UInt64)
13
+ access(all) event CapabilityRemoved(owner: Address?, cacheUuid: UInt64, namespace: String, resourceType: Type, capabilityType: Type, capabilityID: UInt64)
14
+
15
+ // Add to a namespace
16
+ access(all) entitlement Add
17
+
18
+ // Remove from a namespace
19
+ access(all) entitlement Delete
20
+
21
+ // Retrieve a cap from the namespace
22
+ access(all) entitlement Get
23
+
24
+ // Resource that manages capabilities for a provided namespace. Only one capability is permitted per type.
25
+ access(all) resource Cache {
26
+ // A dictionary of resourceType -> CapabilityType -> Capability
27
+ // For example, one might store a Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}> for the @TopShot.NFT resource.
28
+ // Note that the resource type is not necessarily the type that the borrowed capability is an instance of. This is because some resource definitions
29
+ // might be reused.
30
+ access(self) let caps: {Type: {Type: Capability}}
31
+
32
+ // who is this capability cache maintained by? e.g. flowty, dapper, find?
33
+ access(all) let namespace: String
34
+
35
+ // Remove a capability, if it exists,
36
+ access(Delete) fun removeCapabilityByType(resourceType: Type, capabilityType: Type): Capability? {
37
+ if let ref = &self.caps[resourceType] as auth(Mutate) &{Type: Capability}? {
38
+ let cap = ref.remove(key: capabilityType)
39
+ if cap != nil {
40
+ emit CapabilityRemoved(owner: self.owner?.address, cacheUuid: self.uuid, namespace: self.namespace, resourceType: resourceType, capabilityType: capabilityType, capabilityID: cap!.id)
41
+ }
42
+ }
43
+
44
+ return nil
45
+ }
46
+
47
+ // Adds a capability to the cache. If there is already an entry for the given type,
48
+ // it will be returned
49
+ access(Add) fun addCapability(resourceType: Type, cap: Capability): Capability? {
50
+ pre {
51
+ cap.id != 0: "cannot add a capability with id 0"
52
+ }
53
+
54
+ let capType = cap.getType()
55
+ emit CapabilityAdded(owner: self.owner?.address, cacheUuid: self.uuid, namespace: self.namespace, resourceType: resourceType, capabilityType: capType, capabilityID: cap.id)
56
+ if let ref = &self.caps[resourceType] as auth(Mutate) &{Type: Capability}? {
57
+ return ref.insert(key: capType, cap)
58
+ }
59
+
60
+ self.caps[resourceType] = {
61
+ capType: cap
62
+ }
63
+
64
+ return nil
65
+ }
66
+
67
+ // Retrieve a capability key'd by a given type.
68
+ access(Get) fun getCapabilityByType(resourceType: Type, capabilityType: Type): Capability? {
69
+ if let tmp = self.caps[resourceType] {
70
+ return tmp[capabilityType]
71
+ }
72
+
73
+ return nil
74
+ }
75
+
76
+ init(namespace: String) {
77
+ self.caps = {}
78
+
79
+ self.namespace = namespace
80
+ }
81
+ }
82
+
83
+ // There is no uniform storage path for the Capability Cache. Instead, each platform which issues capabilities
84
+ // should manage their own cache, and can generate the storage path to store it in with this helper method
85
+ access(all) fun getPathForCache(_ namespace: String): StoragePath {
86
+ return StoragePath(identifier: self.basePathIdentifier.concat(namespace))
87
+ ?? panic("invalid namespace value")
88
+ }
89
+
90
+ access(all) fun createCache(namespace: String): @Cache {
91
+ return <- create Cache(namespace: namespace)
92
+ }
93
+
94
+ init() {
95
+ self.basePathIdentifier = "CapabilityCache_".concat(self.account.address.toString()).concat("_")
96
+ }
97
+ }
@@ -0,0 +1,47 @@
1
+ import "FlowToken"
2
+ import "FungibleToken"
3
+
4
+ access(all) contract ContractManager {
5
+ access(all) let StoragePath: StoragePath
6
+ access(all) let PublicPath: PublicPath
7
+
8
+ access(all) entitlement Manage
9
+
10
+ access(all) resource Manager {
11
+ access(self) let acct: Capability<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>
12
+
13
+ access(Manage) fun borrowContractAccount(): auth(Contracts) &Account {
14
+ return self.acct.borrow()!
15
+ }
16
+
17
+ access(all) fun addFlowTokensToAccount(_ tokens: @FlowToken.Vault) {
18
+ self.acct.borrow()!.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-tokens)
19
+ }
20
+
21
+ access(all) fun getAccount(): &Account {
22
+ return getAccount(self.acct.address)
23
+ }
24
+
25
+ init(tokens: @FlowToken.Vault) {
26
+ pre {
27
+ tokens.balance >= 0.001: "minimum balance of 0.001 required for initialization"
28
+ }
29
+
30
+ let acct = Account(payer: ContractManager.account)
31
+ self.acct = acct.capabilities.account.issue<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>()
32
+ assert(self.acct.check(), message: "failed to setup account capability")
33
+
34
+ acct.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-tokens)
35
+ }
36
+ }
37
+
38
+ access(all) fun createManager(tokens: @FlowToken.Vault): @Manager {
39
+ return <- create Manager(tokens: <- tokens)
40
+ }
41
+
42
+ init() {
43
+ let identifier = "ContractManager_".concat(self.account.address.toString())
44
+ self.StoragePath = StoragePath(identifier: identifier)!
45
+ self.PublicPath = PublicPath(identifier: identifier)!
46
+ }
47
+ }
@@ -0,0 +1,75 @@
1
+ import "FungibleToken"
2
+ import "MetadataViews"
3
+
4
+ import "FlowtyDrops"
5
+ import "FlowtySwitchers"
6
+ import "FlowtyAddressVerifiers"
7
+ import "FlowtyPricers"
8
+
9
+ /*
10
+ The DropFactory is a contract that helps create common types of drops
11
+ */
12
+ access(all) contract DropFactory {
13
+ access(all) fun createEndlessOpenEditionDrop(
14
+ price: UFix64,
15
+ paymentTokenType: Type,
16
+ dropDisplay: MetadataViews.Display,
17
+ minterCap: Capability<&{FlowtyDrops.Minter}>,
18
+ nftTypeIdentifier: String
19
+ ): @FlowtyDrops.Drop {
20
+ pre {
21
+ paymentTokenType.isSubtype(of: Type<@{FungibleToken.Vault}>()): "paymentTokenType must be a FungibleToken"
22
+ }
23
+
24
+ // This drop is always on and never ends.
25
+ let switcher = FlowtySwitchers.AlwaysOn()
26
+
27
+ // All addresses are allowed to participate
28
+ let addressVerifier = FlowtyAddressVerifiers.AllowAll(maxPerMint: 10)
29
+
30
+ // The cost of each mint is the same, and only permits one token type as payment
31
+ let pricer = FlowtyPricers.FlatPrice(price: price, paymentTokenType: paymentTokenType)
32
+
33
+ let phaseDetails = FlowtyDrops.PhaseDetails(switcher: switcher, display: nil, pricer: pricer, addressVerifier: addressVerifier)
34
+ let phase <- FlowtyDrops.createPhase(details: phaseDetails)
35
+
36
+
37
+ let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
38
+ let dropDetails = FlowtyDrops.DropDetails(display: dropDisplay, medias: nil, commissionRate: 0.05, nftType: nftTypeIdentifier)
39
+ let drop <- FlowtyDrops.createDrop(details: dropDetails, minterCap: minterCap, phases: <- [<-phase])
40
+
41
+ return <- drop
42
+ }
43
+
44
+ access(all) fun createTimeBasedOpenEditionDrop(
45
+ price: UFix64,
46
+ paymentTokenType: Type,
47
+ dropDisplay: MetadataViews.Display,
48
+ minterCap: Capability<&{FlowtyDrops.Minter}>,
49
+ startUnix: UInt64?,
50
+ endUnix: UInt64?,
51
+ nftTypeIdentifier: String
52
+ ): @FlowtyDrops.Drop {
53
+ pre {
54
+ paymentTokenType.isSubtype(of: Type<@{FungibleToken.Vault}>()): "paymentTokenType must be a FungibleToken"
55
+ }
56
+
57
+ // This switcher turns on at a set unix timestamp (or is on by default if nil), and ends at the specified end date if provided
58
+ let switcher = FlowtySwitchers.TimestampSwitch(start: startUnix, end: endUnix)
59
+
60
+ // All addresses are allowed to participate
61
+ let addressVerifier = FlowtyAddressVerifiers.AllowAll(maxPerMint: 10)
62
+
63
+ // The cost of each mint is the same, and only permits one token type as payment
64
+ let pricer = FlowtyPricers.FlatPrice(price: price, paymentTokenType: paymentTokenType)
65
+
66
+ let phaseDetails = FlowtyDrops.PhaseDetails(switcher: switcher, display: nil, pricer: pricer, addressVerifier: addressVerifier)
67
+ let phase <- FlowtyDrops.createPhase(details: phaseDetails)
68
+
69
+ let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
70
+ let dropDetails = FlowtyDrops.DropDetails(display: dropDisplay, medias: nil, commissionRate: 0.05, nftType: nftTypeIdentifier)
71
+ let drop <- FlowtyDrops.createDrop(details: dropDetails, minterCap: minterCap, phases: <- [<-phase])
72
+
73
+ return <- drop
74
+ }
75
+ }
@@ -0,0 +1,278 @@
1
+ import "FlowtyDrops"
2
+ import "MetadataViews"
3
+ import "ViewResolver"
4
+ import "AddressUtils"
5
+
6
+ access(all) contract DropTypes {
7
+ access(all) struct Display {
8
+ access(all) let name: String
9
+ access(all) let description: String
10
+ access(all) let url: String
11
+
12
+ init(_ display: MetadataViews.Display) {
13
+ self.name = display.name
14
+ self.description = display.description
15
+ self.url = display.thumbnail.uri()
16
+ }
17
+ }
18
+
19
+ access(all) struct Media {
20
+ access(all) let url: String
21
+ access(all) let mediaType: String
22
+
23
+ init(_ media: MetadataViews.Media) {
24
+ self.url = media.file.uri()
25
+ self.mediaType = media.mediaType
26
+ }
27
+ }
28
+
29
+ access(all) struct DropSummary {
30
+ access(all) let id: UInt64
31
+ access(all) let display: Display
32
+ access(all) let medias: [Media]
33
+ access(all) let totalMinted: Int
34
+ access(all) let minterCount: Int
35
+ access(all) let commissionRate: UFix64
36
+ access(all) let nftType: String
37
+
38
+ access(all) let address: Address?
39
+ access(all) let mintedByAddress: Int?
40
+
41
+ access(all) let phases: [PhaseSummary]
42
+
43
+ access(all) let blockTimestamp: UInt64
44
+ access(all) let blockHeight: UInt64
45
+
46
+ init(
47
+ id: UInt64,
48
+ display: MetadataViews.Display,
49
+ medias: MetadataViews.Medias?,
50
+ totalMinted: Int,
51
+ minterCount: Int,
52
+ mintedByAddress: Int?,
53
+ commissionRate: UFix64,
54
+ nftType: Type,
55
+ address: Address?,
56
+ phases: [PhaseSummary]
57
+ ) {
58
+ self.id = id
59
+ self.display = Display(display)
60
+
61
+ self.medias = []
62
+ for m in medias?.items ?? [] {
63
+ self.medias.append(Media(m))
64
+ }
65
+
66
+
67
+ self.totalMinted = totalMinted
68
+ self.commissionRate = commissionRate
69
+ self.minterCount = minterCount
70
+ self.mintedByAddress = mintedByAddress
71
+ self.nftType = nftType.identifier
72
+ self.address = address
73
+ self.phases = phases
74
+
75
+ let b = getCurrentBlock()
76
+ self.blockHeight = b.height
77
+ self.blockTimestamp = UInt64(b.timestamp)
78
+ }
79
+ }
80
+
81
+ access(all) struct Quote {
82
+ access(all) let price: UFix64
83
+ access(all) let quantity: Int
84
+ access(all) let paymentIdentifier: String
85
+ access(all) let minter: Address?
86
+
87
+ init(price: UFix64, quantity: Int, paymentIdentifier: String, minter: Address?) {
88
+ self.price = price
89
+ self.quantity = quantity
90
+ self.paymentIdentifier = paymentIdentifier
91
+ self.minter = minter
92
+ }
93
+ }
94
+
95
+ access(all) struct PhaseSummary {
96
+ access(all) let id: UInt64
97
+ access(all) let index: Int
98
+
99
+ access(all) let switcherType: String
100
+ access(all) let pricerType: String
101
+ access(all) let addressVerifierType: String
102
+
103
+ access(all) let hasStarted: Bool
104
+ access(all) let hasEnded: Bool
105
+ access(all) let start: UInt64?
106
+ access(all) let end: UInt64?
107
+
108
+ access(all) let paymentTypes: [String]
109
+
110
+ access(all) let address: Address?
111
+ access(all) let remainingForAddress: Int?
112
+
113
+ access(all) let quote: Quote?
114
+
115
+ init(
116
+ index: Int,
117
+ phase: &{FlowtyDrops.PhasePublic},
118
+ address: Address?,
119
+ totalMinted: Int?,
120
+ minter: Address?,
121
+ quantity: Int?,
122
+ paymentIdentifier: String?
123
+ ) {
124
+ self.index = index
125
+ self.id = phase.uuid
126
+
127
+ let d = phase.getDetails()
128
+ self.switcherType = d.switcher.getType().identifier
129
+ self.pricerType = d.pricer.getType().identifier
130
+ self.addressVerifierType = d.addressVerifier.getType().identifier
131
+
132
+ self.hasStarted = d.switcher.hasStarted()
133
+ self.hasEnded = d.switcher.hasEnded()
134
+ self.start = d.switcher.getStart()
135
+ self.end = d.switcher.getEnd()
136
+
137
+ self.paymentTypes = []
138
+ for pt in d.pricer.getPaymentTypes() {
139
+ self.paymentTypes.append(pt.identifier)
140
+ }
141
+
142
+ if let addr = address {
143
+ self.address = address
144
+ self.remainingForAddress = d.addressVerifier.remainingForAddress(addr: addr, totalMinted: totalMinted ?? 0)
145
+ } else {
146
+ self.address = nil
147
+ self.remainingForAddress = nil
148
+ }
149
+
150
+ if paymentIdentifier != nil && quantity != nil {
151
+ let price = d.pricer.getPrice(num: quantity!, paymentTokenType: CompositeType(paymentIdentifier!)!, minter: minter)
152
+
153
+ self.quote = Quote(price: price, quantity: quantity!, paymentIdentifier: paymentIdentifier!, minter: minter)
154
+ } else {
155
+ self.quote = nil
156
+ }
157
+ }
158
+ }
159
+
160
+ access(all) fun getDropSummary(nftTypeIdentifier: String, dropID: UInt64, minter: Address?, quantity: Int?, paymentIdentifier: String?): DropSummary? {
161
+ let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
162
+ let segments = nftTypeIdentifier.split(separator: ".")
163
+ let contractAddress = AddressUtils.parseAddress(nftType)!
164
+ let contractName = segments[2]
165
+
166
+ let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName)
167
+ if resolver == nil {
168
+ return nil
169
+ }
170
+
171
+ let dropResolver = resolver!.resolveContractView(resourceType: nftType, viewType: Type<FlowtyDrops.DropResolver>()) as! FlowtyDrops.DropResolver?
172
+ if dropResolver == nil {
173
+ return nil
174
+ }
175
+
176
+ let container = dropResolver!.borrowContainer()
177
+ if container == nil {
178
+ return nil
179
+ }
180
+
181
+ let drop = container!.borrowDropPublic(id: dropID)
182
+ if drop == nil {
183
+ return nil
184
+ }
185
+
186
+ let dropDetails = drop!.getDetails()
187
+
188
+ let phaseSummaries: [PhaseSummary] = []
189
+ for index, phase in drop!.borrowAllPhases() {
190
+ let summary = PhaseSummary(
191
+ index: index,
192
+ phase: phase,
193
+ address: minter,
194
+ totalMinted: minter != nil ? dropDetails.minters[minter!] : nil,
195
+ minter: minter,
196
+ quantity: quantity,
197
+ paymentIdentifier: paymentIdentifier
198
+ )
199
+ phaseSummaries.append(summary)
200
+ }
201
+
202
+ let dropSummary = DropSummary(
203
+ id: drop!.uuid,
204
+ display: dropDetails.display,
205
+ medias: dropDetails.medias,
206
+ totalMinted: dropDetails.totalMinted,
207
+ minterCount: dropDetails.minters.keys.length,
208
+ mintedByAddress: minter != nil ? dropDetails.minters[minter!] : nil,
209
+ commissionRate: dropDetails.commissionRate,
210
+ nftType: CompositeType(dropDetails.nftType)!,
211
+ address: minter,
212
+ phases: phaseSummaries
213
+ )
214
+
215
+ return dropSummary
216
+ }
217
+
218
+ access(all) fun getAllDropSummaries(nftTypeIdentifier: String, minter: Address?, quantity: Int?, paymentIdentifier: String?): [DropSummary] {
219
+ let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
220
+ let segments = nftTypeIdentifier.split(separator: ".")
221
+ let contractAddress = AddressUtils.parseAddress(nftType)!
222
+ let contractName = segments[2]
223
+
224
+ let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName)
225
+ if resolver == nil {
226
+ return []
227
+ }
228
+
229
+ let dropResolver = resolver!.resolveContractView(resourceType: nftType, viewType: Type<FlowtyDrops.DropResolver>()) as! FlowtyDrops.DropResolver?
230
+ if dropResolver == nil {
231
+ return []
232
+ }
233
+
234
+ let container = dropResolver!.borrowContainer()
235
+ if container == nil {
236
+ return []
237
+ }
238
+
239
+ let summaries: [DropSummary] = []
240
+ for id in container!.getIDs() {
241
+ let drop = container!.borrowDropPublic(id: id)
242
+ if drop == nil {
243
+ continue
244
+ }
245
+
246
+ let dropDetails = drop!.getDetails()
247
+
248
+ let phaseSummaries: [PhaseSummary] = []
249
+ for index, phase in drop!.borrowAllPhases() {
250
+ let summary = PhaseSummary(
251
+ index: index,
252
+ phase: phase,
253
+ address: minter,
254
+ totalMinted: minter != nil ? dropDetails.minters[minter!] : nil,
255
+ minter: minter,
256
+ quantity: quantity,
257
+ paymentIdentifier: paymentIdentifier
258
+ )
259
+ phaseSummaries.append(summary)
260
+ }
261
+
262
+ summaries.append(DropSummary(
263
+ id: drop!.uuid,
264
+ display: dropDetails.display,
265
+ medias: dropDetails.medias,
266
+ totalMinted: dropDetails.totalMinted,
267
+ minterCount: dropDetails.minters.keys.length,
268
+ mintedByAddress: minter != nil ? dropDetails.minters[minter!] : nil,
269
+ commissionRate: dropDetails.commissionRate,
270
+ nftType: CompositeType(dropDetails.nftType)!,
271
+ address: minter,
272
+ phases: phaseSummaries
273
+ ))
274
+ }
275
+
276
+ return summaries
277
+ }
278
+ }
@@ -0,0 +1,64 @@
1
+ import "FlowtyDrops"
2
+
3
+ /*
4
+ This contract contains implementations of the FlowtyDrops.AddressVerifier struct interface
5
+ */
6
+ access(all) contract FlowtyAddressVerifiers {
7
+ /*
8
+ The AllowAll AddressVerifier allows any address to mint without any verification
9
+ */
10
+ access(all) struct AllowAll: FlowtyDrops.AddressVerifier {
11
+ access(all) var maxPerMint: Int
12
+
13
+ access(all) view fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool {
14
+ return num <= self.maxPerMint
15
+ }
16
+
17
+ access(Mutate) fun setMaxPerMint(_ value: Int) {
18
+ self.maxPerMint = value
19
+ }
20
+
21
+ init(maxPerMint: Int) {
22
+ pre {
23
+ maxPerMint > 0: "maxPerMint must be greater than 0"
24
+ }
25
+
26
+ self.maxPerMint = maxPerMint
27
+ }
28
+ }
29
+
30
+ /*
31
+ The AllowList Verifier only lets a configured set of addresses participate in a drop phase. The number
32
+ of mints per address is specified to allow more granular control of what each address is permitted to do.
33
+ */
34
+ access(all) struct AllowList: FlowtyDrops.AddressVerifier {
35
+ access(self) let allowedAddresses: {Address: Int}
36
+
37
+ access(all) view fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool {
38
+ if let allowedMints = self.allowedAddresses[addr] {
39
+ return allowedMints >= num + totalMinted
40
+ }
41
+
42
+ return false
43
+ }
44
+
45
+ access(all) view fun remainingForAddress(addr: Address, totalMinted: Int): Int? {
46
+ if let allowedMints = self.allowedAddresses[addr] {
47
+ return allowedMints - totalMinted
48
+ }
49
+ return nil
50
+ }
51
+
52
+ access(Mutate) fun setAddress(addr: Address, value: Int) {
53
+ self.allowedAddresses[addr] = value
54
+ }
55
+
56
+ access(Mutate) fun removeAddress(addr: Address) {
57
+ self.allowedAddresses.remove(key: addr)
58
+ }
59
+
60
+ init(allowedAddresses: {Address: Int}) {
61
+ self.allowedAddresses = allowedAddresses
62
+ }
63
+ }
64
+ }