@flowtyio/flow-contracts 0.1.0-beta.4 → 0.1.0-beta.6
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/contracts/example/ExampleNFT.cdc +17 -18
- package/contracts/example/ExampleToken.cdc +68 -1
- package/contracts/flow-utils/AddressUtils.cdc +19 -22
- package/contracts/flow-utils/ArrayUtils.cdc +100 -49
- package/contracts/flow-utils/ScopedNFTProviders.cdc +28 -28
- package/contracts/flow-utils/StringUtils.cdc +23 -36
- package/contracts/nft-catalog/NFTCatalog.cdc +60 -64
- package/contracts/nft-catalog/NFTCatalogAdmin.cdc +28 -27
- package/package.json +1 -1
|
@@ -143,6 +143,10 @@ access(all) contract ExampleNFT: ViewResolver {
|
|
|
143
143
|
}
|
|
144
144
|
return nil
|
|
145
145
|
}
|
|
146
|
+
|
|
147
|
+
access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
|
|
148
|
+
return <- ExampleNFT.createEmptyCollection()
|
|
149
|
+
}
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
access(all) resource interface ExampleNFTCollectionPublic: NonFungibleToken.Collection {
|
|
@@ -185,6 +189,14 @@ access(all) contract ExampleNFT: ViewResolver {
|
|
|
185
189
|
}
|
|
186
190
|
}
|
|
187
191
|
|
|
192
|
+
access(all) view fun getIDs(): [UInt64] {
|
|
193
|
+
return self.ownedNFTs.keys
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
|
|
197
|
+
return &self.ownedNFTs[id]
|
|
198
|
+
}
|
|
199
|
+
|
|
188
200
|
access(all) view fun isSupportedNFTType(type: Type): Bool {
|
|
189
201
|
return type == Type<@ExampleNFT.NFT>()
|
|
190
202
|
}
|
|
@@ -194,10 +206,10 @@ access(all) contract ExampleNFT: ViewResolver {
|
|
|
194
206
|
}
|
|
195
207
|
|
|
196
208
|
// withdraw removes an NFT from the collection and moves it to the caller
|
|
197
|
-
access(NonFungibleToken.
|
|
209
|
+
access(NonFungibleToken.Withdraw | NonFungibleToken.Owner) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
|
|
198
210
|
let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
|
|
199
211
|
|
|
200
|
-
emit Withdraw(id: token.
|
|
212
|
+
emit Withdraw(id: token.id, from: self.owner?.address)
|
|
201
213
|
|
|
202
214
|
return <-token
|
|
203
215
|
}
|
|
@@ -216,17 +228,6 @@ access(all) contract ExampleNFT: ViewResolver {
|
|
|
216
228
|
|
|
217
229
|
destroy oldToken
|
|
218
230
|
}
|
|
219
|
-
|
|
220
|
-
// getIDs returns an array of the IDs that are in the collection
|
|
221
|
-
access(all) view fun getIDs(): [UInt64] {
|
|
222
|
-
return self.ownedNFTs.keys
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// borrowNFT gets a reference to an NFT in the collection
|
|
226
|
-
// so that the caller can read its metadata and call its methods
|
|
227
|
-
access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
|
|
228
|
-
return &self.ownedNFTs[id]
|
|
229
|
-
}
|
|
230
231
|
|
|
231
232
|
access(all) fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
|
|
232
233
|
if self.ownedNFTs[id] != nil {
|
|
@@ -343,16 +344,14 @@ access(all) contract ExampleNFT: ViewResolver {
|
|
|
343
344
|
/// @param view: The Type of the desired view.
|
|
344
345
|
/// @return A structure representing the requested view.
|
|
345
346
|
///
|
|
346
|
-
access(all)
|
|
347
|
-
switch
|
|
347
|
+
access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
|
|
348
|
+
switch viewType {
|
|
348
349
|
case Type<MetadataViews.NFTCollectionData>():
|
|
349
350
|
return MetadataViews.NFTCollectionData(
|
|
350
351
|
storagePath: ExampleNFT.CollectionStoragePath,
|
|
351
352
|
publicPath: ExampleNFT.CollectionPublicPath,
|
|
352
|
-
providerPath: /private/exampleNFTCollection,
|
|
353
353
|
publicCollection: Type<&ExampleNFT.Collection>(),
|
|
354
354
|
publicLinkedType: Type<&ExampleNFT.Collection>(),
|
|
355
|
-
providerLinkedType: Type<auth(NonFungibleToken.Withdrawable) &ExampleNFT.Collection>(),
|
|
356
355
|
createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
|
|
357
356
|
return <-ExampleNFT.createEmptyCollection()
|
|
358
357
|
})
|
|
@@ -383,7 +382,7 @@ access(all) contract ExampleNFT: ViewResolver {
|
|
|
383
382
|
/// @return An array of Types defining the implemented views. This value will be used by
|
|
384
383
|
/// developers to know which parameter to pass to the resolveView() method.
|
|
385
384
|
///
|
|
386
|
-
access(all) view fun
|
|
385
|
+
access(all) view fun getContractViews(resourceType: Type?): [Type] {
|
|
387
386
|
return [
|
|
388
387
|
Type<MetadataViews.NFTCollectionData>(),
|
|
389
388
|
Type<MetadataViews.NFTCollectionDisplay>()
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import "FungibleToken"
|
|
2
|
+
import "FungibleTokenMetadataViews"
|
|
3
|
+
import "MetadataViews"
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
// THIS CONTRACT IS FOR TESTING PURPOSES ONLY!
|
|
@@ -80,6 +82,20 @@ access(all) contract ExampleToken {
|
|
|
80
82
|
return /public/exampleTokenPublic
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
|
|
86
|
+
return self.balance >= amount
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Same as getViews above, but on a specific NFT instead of a contract
|
|
90
|
+
access(all) view fun getViews(): [Type] {
|
|
91
|
+
return ExampleToken.getContractViews(resourceType: nil)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Same as resolveView above, but on a specific NFT instead of a contract
|
|
95
|
+
access(all) fun resolveView(_ view: Type): AnyStruct? {
|
|
96
|
+
return ExampleToken.resolveContractView(resourceType: nil, viewType: view)
|
|
97
|
+
}
|
|
98
|
+
|
|
83
99
|
/// withdraw
|
|
84
100
|
///
|
|
85
101
|
/// Function that takes an amount as an argument
|
|
@@ -90,7 +106,7 @@ access(all) contract ExampleToken {
|
|
|
90
106
|
/// created Vault to the context that called so it can be deposited
|
|
91
107
|
/// elsewhere.
|
|
92
108
|
///
|
|
93
|
-
access(FungibleToken.
|
|
109
|
+
access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
|
|
94
110
|
self.balance = self.balance - amount
|
|
95
111
|
emit TokensWithdrawn(amount: amount, from: self.owner?.address)
|
|
96
112
|
return <-create Vault(balance: amount)
|
|
@@ -211,6 +227,57 @@ access(all) contract ExampleToken {
|
|
|
211
227
|
}
|
|
212
228
|
}
|
|
213
229
|
|
|
230
|
+
access(all) view fun getContractViews(resourceType: Type?): [Type] {
|
|
231
|
+
return [Type<FungibleTokenMetadataViews.FTView>(),
|
|
232
|
+
Type<FungibleTokenMetadataViews.FTDisplay>(),
|
|
233
|
+
Type<FungibleTokenMetadataViews.FTVaultData>(),
|
|
234
|
+
Type<FungibleTokenMetadataViews.TotalSupply>()]
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
|
|
238
|
+
switch viewType {
|
|
239
|
+
case Type<FungibleTokenMetadataViews.FTView>():
|
|
240
|
+
return FungibleTokenMetadataViews.FTView(
|
|
241
|
+
ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
|
|
242
|
+
ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
|
|
243
|
+
)
|
|
244
|
+
case Type<FungibleTokenMetadataViews.FTDisplay>():
|
|
245
|
+
let media = MetadataViews.Media(
|
|
246
|
+
file: MetadataViews.HTTPFile(
|
|
247
|
+
url: "https://example.com"
|
|
248
|
+
),
|
|
249
|
+
mediaType: "image/svg+xml"
|
|
250
|
+
)
|
|
251
|
+
let medias = MetadataViews.Medias([media])
|
|
252
|
+
return FungibleTokenMetadataViews.FTDisplay(
|
|
253
|
+
name: "Example Token",
|
|
254
|
+
symbol: "EXAMPLE",
|
|
255
|
+
description: "",
|
|
256
|
+
externalURL: MetadataViews.ExternalURL("https://flow.com"),
|
|
257
|
+
logos: medias,
|
|
258
|
+
socials: {
|
|
259
|
+
"twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
case Type<FungibleTokenMetadataViews.FTVaultData>():
|
|
263
|
+
let vaultRef = ExampleToken.account.storage.borrow<auth(FungibleToken.Withdraw) &ExampleToken.Vault>(from: /storage/exampleTokenVault)
|
|
264
|
+
?? panic("Could not borrow reference to the contract's Vault!")
|
|
265
|
+
return FungibleTokenMetadataViews.FTVaultData(
|
|
266
|
+
storagePath: /storage/exampleTokenVault,
|
|
267
|
+
receiverPath: /public/exampleTokenReceiver,
|
|
268
|
+
metadataPath: /public/exampleTokenBalance,
|
|
269
|
+
receiverLinkedType: Type<&{FungibleToken.Receiver, FungibleToken.Vault}>(),
|
|
270
|
+
metadataLinkedType: Type<&{FungibleToken.Balance, FungibleToken.Vault}>(),
|
|
271
|
+
createEmptyVaultFunction: (fun (): @{FungibleToken.Vault} {
|
|
272
|
+
return <-vaultRef.createEmptyVault()
|
|
273
|
+
})
|
|
274
|
+
)
|
|
275
|
+
case Type<FungibleTokenMetadataViews.TotalSupply>():
|
|
276
|
+
return FungibleTokenMetadataViews.TotalSupply(totalSupply: ExampleToken.totalSupply)
|
|
277
|
+
}
|
|
278
|
+
return nil
|
|
279
|
+
}
|
|
280
|
+
|
|
214
281
|
init() {
|
|
215
282
|
self.totalSupply = 1000.0
|
|
216
283
|
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
pub contract AddressUtils {
|
|
4
|
-
|
|
5
|
-
pub fun withoutPrefix(_ input: String): String {
|
|
1
|
+
access(all) contract AddressUtils {
|
|
2
|
+
access(all) fun withoutPrefix(_ input: String): String {
|
|
6
3
|
var address = input
|
|
7
4
|
|
|
8
5
|
// get rid of 0x
|
|
@@ -17,7 +14,7 @@ pub contract AddressUtils {
|
|
|
17
14
|
return address
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
access(all) fun parseUInt64(_ input: AnyStruct): UInt64? {
|
|
21
18
|
var stringValue = ""
|
|
22
19
|
|
|
23
20
|
if let string = input as? String {
|
|
@@ -25,7 +22,7 @@ pub contract AddressUtils {
|
|
|
25
22
|
} else if let address = input as? Address {
|
|
26
23
|
stringValue = address.toString()
|
|
27
24
|
} else if let type = input as? Type {
|
|
28
|
-
let parts =
|
|
25
|
+
let parts = type.identifier.split(separator: ".")
|
|
29
26
|
if parts.length == 1 {
|
|
30
27
|
return nil
|
|
31
28
|
}
|
|
@@ -45,14 +42,14 @@ pub contract AddressUtils {
|
|
|
45
42
|
return r
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
access(all) fun parseAddress(_ input: AnyStruct): Address? {
|
|
49
46
|
if let parsed = self.parseUInt64(input) {
|
|
50
47
|
return Address(parsed)
|
|
51
48
|
}
|
|
52
49
|
return nil
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
|
|
52
|
+
access(all) fun isValidAddress(_ input: AnyStruct, forNetwork: String): Bool {
|
|
56
53
|
let address = self.parseUInt64(input)
|
|
57
54
|
if address == nil {
|
|
58
55
|
return false
|
|
@@ -61,18 +58,19 @@ pub contract AddressUtils {
|
|
|
61
58
|
let codeWords: {String: UInt64} = {
|
|
62
59
|
"MAINNET" : 0,
|
|
63
60
|
"TESTNET" : 0x6834ba37b3980209,
|
|
61
|
+
"CRESCENDO" : 0x6834ba37b3980209,
|
|
64
62
|
"EMULATOR": 0x1cb159857af02018
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
let parityCheckMatrixColumns: [UInt64] = [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
0x00001, 0x00002, 0x00004, 0x00008, 0x00010, 0x00020, 0x00040, 0x00080,
|
|
67
|
+
0x00100, 0x00200, 0x00400, 0x00800, 0x01000, 0x02000, 0x04000, 0x08000,
|
|
68
|
+
0x10000, 0x20000, 0x40000, 0x7328d, 0x6689a, 0x6112f, 0x6084b, 0x433fd,
|
|
69
|
+
0x42aab, 0x41951, 0x233ce, 0x22a81, 0x21948, 0x1ef60, 0x1deca, 0x1c639,
|
|
70
|
+
0x1bdd8, 0x1a535, 0x194ac, 0x18c46, 0x1632b, 0x1529b, 0x14a43, 0x13184,
|
|
71
|
+
0x12942, 0x118c1, 0x0f812, 0x0e027, 0x0d00e, 0x0c83c, 0x0b01d, 0x0a831,
|
|
72
|
+
0x0982b, 0x07034, 0x0682a, 0x05819, 0x03807, 0x007d2, 0x00727, 0x0068e,
|
|
73
|
+
0x0067c, 0x0059d, 0x004eb, 0x003b4, 0x0036a, 0x002d9, 0x001c7, 0x0003f
|
|
76
74
|
]
|
|
77
75
|
|
|
78
76
|
var parity: UInt64 = 0
|
|
@@ -93,8 +91,8 @@ pub contract AddressUtils {
|
|
|
93
91
|
return parity == 0 && codeWord == 0
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
for network in ["MAINNET", "TESTNET", "EMULATOR"]{
|
|
94
|
+
access(all) fun getNetworkFromAddress(_ input: AnyStruct): String? {
|
|
95
|
+
for network in ["MAINNET", "TESTNET", "EMULATOR"] {
|
|
98
96
|
if self.isValidAddress(input, forNetwork: network){
|
|
99
97
|
return network
|
|
100
98
|
}
|
|
@@ -102,8 +100,7 @@ pub contract AddressUtils {
|
|
|
102
100
|
return nil
|
|
103
101
|
}
|
|
104
102
|
|
|
105
|
-
|
|
106
|
-
return
|
|
103
|
+
access(all) fun currentNetwork(): String {
|
|
104
|
+
return self.getNetworkFromAddress(self.account.address) ?? panic("unknown network!")
|
|
107
105
|
}
|
|
108
|
-
|
|
109
106
|
}
|
|
@@ -1,69 +1,120 @@
|
|
|
1
|
-
|
|
1
|
+
import "FungibleToken"
|
|
2
|
+
import "StringUtils"
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
// ScopedFTProviders
|
|
5
|
+
//
|
|
6
|
+
// TO AVOID RISK, PLEASE DEPLOY YOUR OWN VERSION OF THIS CONTRACT SO THAT
|
|
7
|
+
// MALICIOUS UPDATES ARE NOT POSSIBLE
|
|
8
|
+
//
|
|
9
|
+
// ScopedProviders are meant to solve the issue of unbounded access FungibleToken vaults
|
|
10
|
+
// when a provider is called for.
|
|
11
|
+
access(all) contract ScopedFTProviders {
|
|
12
|
+
access(all) struct interface FTFilter {
|
|
13
|
+
access(all) fun canWithdrawAmount(_ amount: UFix64): Bool
|
|
14
|
+
access(FungibleToken.Withdraw) fun markAmountWithdrawn(_ amount: UFix64)
|
|
15
|
+
access(all) fun getDetails(): {String: AnyStruct}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
access(all) struct AllowanceFilter: FTFilter {
|
|
19
|
+
access(self) let allowance: UFix64
|
|
20
|
+
access(self) var allowanceUsed: UFix64
|
|
4
21
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
f(current)
|
|
9
|
-
current = current + 1
|
|
22
|
+
init(_ allowance: UFix64) {
|
|
23
|
+
self.allowance = allowance
|
|
24
|
+
self.allowanceUsed = 0.0
|
|
10
25
|
}
|
|
11
|
-
}
|
|
12
26
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
res.append(i)
|
|
17
|
-
})
|
|
18
|
-
return res
|
|
19
|
-
}
|
|
27
|
+
access(all) fun canWithdrawAmount(_ amount: UFix64): Bool {
|
|
28
|
+
return amount + self.allowanceUsed <= self.allowance
|
|
29
|
+
}
|
|
20
30
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
var i: Int = array.length - 1
|
|
24
|
-
while i >= 0 {
|
|
25
|
-
res.append(array[i])
|
|
26
|
-
i = i - 1
|
|
31
|
+
access(FungibleToken.Withdraw) fun markAmountWithdrawn(_ amount: UFix64) {
|
|
32
|
+
self.allowanceUsed = self.allowanceUsed + amount
|
|
27
33
|
}
|
|
28
|
-
return res
|
|
29
|
-
}
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
access(all) fun getDetails(): {String: AnyStruct} {
|
|
36
|
+
return {
|
|
37
|
+
"allowance": self.allowance,
|
|
38
|
+
"allowanceUsed": self.allowanceUsed
|
|
39
|
+
}
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
// ScopedFTProvider
|
|
44
|
+
//
|
|
45
|
+
// A ScopedFTProvider is a wrapped FungibleTokenProvider with
|
|
46
|
+
// filters that can be defined by anyone using the ScopedFTProvider.
|
|
47
|
+
access(all) resource ScopedFTProvider: FungibleToken.Provider {
|
|
48
|
+
access(self) let provider: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>
|
|
49
|
+
access(self) var filters: [{FTFilter}]
|
|
50
|
+
|
|
51
|
+
// block timestamp that this provider can no longer be used after
|
|
52
|
+
access(self) let expiration: UFix64?
|
|
53
|
+
|
|
54
|
+
access(all) init(provider: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>, filters: [{FTFilter}], expiration: UFix64?) {
|
|
55
|
+
self.provider = provider
|
|
56
|
+
self.filters = filters
|
|
57
|
+
self.expiration = expiration
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
access(all) fun check(): Bool {
|
|
61
|
+
return self.provider.check()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
access(all) view fun isExpired(): Bool {
|
|
65
|
+
if let expiration = self.expiration {
|
|
66
|
+
return getCurrentBlock().timestamp >= expiration
|
|
41
67
|
}
|
|
68
|
+
return false
|
|
42
69
|
}
|
|
43
|
-
}
|
|
44
70
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
71
|
+
access(all) fun canWithdraw(_ amount: UFix64): Bool {
|
|
72
|
+
if self.isExpired() {
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for filter in self.filters {
|
|
77
|
+
if !filter.canWithdrawAmount(amount) {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return true
|
|
49
83
|
}
|
|
50
|
-
return res
|
|
51
|
-
}
|
|
52
84
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
|
|
86
|
+
pre {
|
|
87
|
+
!self.isExpired(): "provider has expired"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
var i = 0
|
|
91
|
+
while i < self.filters.length {
|
|
92
|
+
if !self.filters[i].canWithdrawAmount(amount) {
|
|
93
|
+
panic(StringUtils.join(["cannot withdraw tokens. filter of type", self.filters[i].getType().identifier, "failed."], " "))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
self.filters[i].markAmountWithdrawn(amount)
|
|
97
|
+
i = i + 1
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return <-self.provider.borrow()!.withdraw(amount: amount)
|
|
57
101
|
}
|
|
58
|
-
return res
|
|
59
|
-
}
|
|
60
102
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
103
|
+
access(all) fun getDetails(): [{String: AnyStruct}] {
|
|
104
|
+
let details: [{String: AnyStruct}] = []
|
|
105
|
+
for filter in self.filters {
|
|
106
|
+
details.append(filter.getDetails())
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return details
|
|
65
110
|
}
|
|
66
|
-
return res
|
|
67
111
|
}
|
|
68
112
|
|
|
69
|
-
|
|
113
|
+
access(all) fun createScopedFTProvider(
|
|
114
|
+
provider: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>,
|
|
115
|
+
filters: [{FTFilter}],
|
|
116
|
+
expiration: UFix64?
|
|
117
|
+
): @ScopedFTProvider {
|
|
118
|
+
return <- create ScopedFTProvider(provider: provider, filters: filters, expiration: expiration)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -11,14 +11,14 @@ import "StringUtils"
|
|
|
11
11
|
//
|
|
12
12
|
// By using a scoped provider, only a subset of assets can be taken if the provider leaks
|
|
13
13
|
// instead of the entire nft collection.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
access(all) contract ScopedNFTProviders {
|
|
15
|
+
access(all) struct interface NFTFilter {
|
|
16
|
+
access(all) fun canWithdraw(_ nft: &{NonFungibleToken.NFT}): Bool
|
|
17
|
+
access(NonFungibleToken.Withdraw) fun markWithdrawn(_ nft: &{NonFungibleToken.NFT})
|
|
18
|
+
access(all) fun getDetails(): {String: AnyStruct}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
access(all) struct NFTIDFilter: NFTFilter {
|
|
22
22
|
// the ids that are allowed to be withdrawn.
|
|
23
23
|
// If ids[num] is false, the id cannot be withdrawn anymore
|
|
24
24
|
access(self) let ids: {UInt64: Bool}
|
|
@@ -31,22 +31,22 @@ pub contract ScopedNFTProviders {
|
|
|
31
31
|
self.ids = d
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
return self.ids[nft.
|
|
34
|
+
access(all) fun canWithdraw(_ nft: &{NonFungibleToken.NFT}): Bool {
|
|
35
|
+
return self.ids[nft.getID()] != nil && self.ids[nft.getID()] == true
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
self.ids[nft.
|
|
38
|
+
access(NonFungibleToken.Withdraw) fun markWithdrawn(_ nft: &{NonFungibleToken.NFT}) {
|
|
39
|
+
self.ids[nft.getID()] = false
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
access(all) fun getDetails(): {String: AnyStruct} {
|
|
43
43
|
return {
|
|
44
44
|
"ids": self.ids
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
access(all) struct UUIDFilter: NFTFilter {
|
|
50
50
|
// the ids that are allowed to be withdrawn.
|
|
51
51
|
// If ids[num] is false, the id cannot be withdrawn anymore
|
|
52
52
|
access(self) let uuids: {UInt64: Bool}
|
|
@@ -59,15 +59,15 @@ pub contract ScopedNFTProviders {
|
|
|
59
59
|
self.uuids = d
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
access(all) fun canWithdraw(_ nft: &{NonFungibleToken.NFT}): Bool {
|
|
63
63
|
return self.uuids[nft.uuid] != nil && self.uuids[nft.uuid]! == true
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
access(NonFungibleToken.Withdraw) fun markWithdrawn(_ nft: &{NonFungibleToken.NFT}) {
|
|
67
67
|
self.uuids[nft.uuid] = false
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
access(all) fun getDetails(): {String: AnyStruct} {
|
|
71
71
|
return {
|
|
72
72
|
"uuids": self.uuids
|
|
73
73
|
}
|
|
@@ -77,39 +77,39 @@ pub contract ScopedNFTProviders {
|
|
|
77
77
|
// ScopedNFTProvider
|
|
78
78
|
//
|
|
79
79
|
// Wrapper around an NFT Provider that is restricted to specific ids.
|
|
80
|
-
|
|
81
|
-
access(self) let provider: Capability
|
|
80
|
+
access(all) resource ScopedNFTProvider: NonFungibleToken.Provider {
|
|
81
|
+
access(self) let provider: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>
|
|
82
82
|
access(self) let filters: [{NFTFilter}]
|
|
83
83
|
|
|
84
84
|
// block timestamp that this provider can no longer be used after
|
|
85
85
|
access(self) let expiration: UFix64?
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
access(all) view fun isExpired(): Bool {
|
|
88
88
|
if let expiration = self.expiration {
|
|
89
89
|
return getCurrentBlock().timestamp >= expiration
|
|
90
90
|
}
|
|
91
91
|
return false
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
access(all) init(provider: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>, filters: [{NFTFilter}], expiration: UFix64?) {
|
|
95
95
|
self.provider = provider
|
|
96
96
|
self.expiration = expiration
|
|
97
97
|
self.filters = filters
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
access(all) fun canWithdraw(_ id: UInt64): Bool {
|
|
101
101
|
if self.isExpired() {
|
|
102
102
|
return false
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
let nft = self.provider.borrow()!.borrowNFT(id
|
|
105
|
+
let nft = self.provider.borrow()!.borrowNFT(id)
|
|
106
106
|
if nft == nil {
|
|
107
107
|
return false
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
var i = 0
|
|
111
111
|
while i < self.filters.length {
|
|
112
|
-
if !self.filters[i].canWithdraw(nft) {
|
|
112
|
+
if !self.filters[i].canWithdraw(nft!) {
|
|
113
113
|
return false
|
|
114
114
|
}
|
|
115
115
|
i = i + 1
|
|
@@ -117,17 +117,17 @@ pub contract ScopedNFTProviders {
|
|
|
117
117
|
return true
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
access(all) fun check(): Bool {
|
|
121
121
|
return self.provider.check()
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
|
|
125
125
|
pre {
|
|
126
126
|
!self.isExpired(): "provider has expired"
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
let nft <- self.provider.borrow()!.withdraw(withdrawID: withdrawID)
|
|
130
|
-
let ref = &nft as &NonFungibleToken.NFT
|
|
130
|
+
let ref = &nft as &{NonFungibleToken.NFT}
|
|
131
131
|
|
|
132
132
|
var i = 0
|
|
133
133
|
while i < self.filters.length {
|
|
@@ -142,7 +142,7 @@ pub contract ScopedNFTProviders {
|
|
|
142
142
|
return <-nft
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
access(all) fun getDetails(): [{String: AnyStruct}] {
|
|
146
146
|
let details: [{String: AnyStruct}] = []
|
|
147
147
|
for f in self.filters {
|
|
148
148
|
details.append(f.getDetails())
|
|
@@ -152,8 +152,8 @@ pub contract ScopedNFTProviders {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
provider: Capability
|
|
155
|
+
access(all) fun createScopedNFTProvider(
|
|
156
|
+
provider: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>,
|
|
157
157
|
filters: [{NFTFilter}],
|
|
158
158
|
expiration: UFix64?
|
|
159
159
|
): @ScopedNFTProvider {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import "ArrayUtils"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
access(all) contract StringUtils {
|
|
4
|
+
|
|
5
|
+
access(all) fun format(_ s: String, _ args: {String:String}): String{
|
|
6
|
+
var formatted = s
|
|
7
|
+
for key in args.keys{
|
|
8
|
+
formatted = StringUtils.replaceAll(formatted, "{".concat(key).concat("}"), args[key]!)
|
|
9
|
+
}
|
|
10
|
+
return formatted
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
access(all) fun explode(_ s: String): [String]{
|
|
14
14
|
var chars : [String] = []
|
|
15
15
|
for i in ArrayUtils.range(0, s.length){
|
|
16
16
|
chars.append(s[i].toString())
|
|
@@ -18,7 +18,7 @@ pub contract StringUtils {
|
|
|
18
18
|
return chars
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
access(all) fun trimLeft(_ s: String): String{
|
|
22
22
|
for i in ArrayUtils.range(0, s.length){
|
|
23
23
|
if s[i] != " "{
|
|
24
24
|
return s.slice(from: i, upTo: s.length)
|
|
@@ -27,23 +27,23 @@ pub contract StringUtils {
|
|
|
27
27
|
return ""
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
access(all) fun trim(_ s: String): String{
|
|
31
31
|
return self.trimLeft(s)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
return
|
|
34
|
+
access(all) fun replaceAll(_ s: String, _ search: String, _ replace: String): String{
|
|
35
|
+
return s.replaceAll(of: search, with: replace)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
access(all) fun hasPrefix(_ s: String, _ prefix: String) : Bool{
|
|
39
39
|
return s.length >= prefix.length && s.slice(from:0, upTo: prefix.length)==prefix
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
access(all) fun hasSuffix(_ s: String, _ suffix: String) : Bool{
|
|
43
43
|
return s.length >= suffix.length && s.slice(from:s.length-suffix.length, upTo: s.length)==suffix
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
access(all) fun index(_ s : String, _ substr : String, _ startIndex: Int): Int?{
|
|
47
47
|
for i in ArrayUtils.range(startIndex,s.length-substr.length+1){
|
|
48
48
|
if s[i]==substr[0] && s.slice(from:i, upTo:i+substr.length) == substr{
|
|
49
49
|
return i
|
|
@@ -52,7 +52,7 @@ pub contract StringUtils {
|
|
|
52
52
|
return nil
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
access(all) fun count(_ s: String, _ substr: String): Int{
|
|
56
56
|
var pos = [self.index(s, substr, 0)]
|
|
57
57
|
while pos[0]!=nil {
|
|
58
58
|
pos.insert(at:0, self.index(s, substr, pos[0]!+pos.length*substr.length+1))
|
|
@@ -60,38 +60,25 @@ pub contract StringUtils {
|
|
|
60
60
|
return pos.length-1
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
access(all) fun contains(_ s: String, _ substr: String): Bool {
|
|
64
64
|
if let index = self.index(s, substr, 0) {
|
|
65
65
|
return true
|
|
66
66
|
}
|
|
67
67
|
return false
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
access(all) fun substringUntil(_ s: String, _ until: String, _ startIndex: Int): String{
|
|
71
71
|
if let index = self.index( s, until, startIndex){
|
|
72
72
|
return s.slice(from:startIndex, upTo: index)
|
|
73
73
|
}
|
|
74
74
|
return s.slice(from:startIndex, upTo:s.length)
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
var p = 0
|
|
80
|
-
while p<=s.length{
|
|
81
|
-
var preDelimiter = self.substringUntil(s, delimiter, p)
|
|
82
|
-
segments.append(preDelimiter)
|
|
83
|
-
p = p + preDelimiter.length + delimiter.length
|
|
84
|
-
}
|
|
85
|
-
return segments
|
|
77
|
+
access(all) fun split(_ s: String, _ delimiter: String): [String] {
|
|
78
|
+
return s.split(separator: delimiter)
|
|
86
79
|
}
|
|
87
80
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
for s in strs {
|
|
91
|
-
joinedStr = joinedStr.concat(s).concat(separator)
|
|
92
|
-
}
|
|
93
|
-
return joinedStr.slice(from: 0, upTo: joinedStr.length - separator.length)
|
|
81
|
+
access(all) fun join(_ strs: [String], _ separator: String): String {
|
|
82
|
+
return String.join(strs, separator: separator)
|
|
94
83
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
84
|
}
|
|
@@ -10,10 +10,10 @@ import "MetadataViews"
|
|
|
10
10
|
// To make an addition to the catalog you can propose an NFT and provide its metadata.
|
|
11
11
|
// An Admin can approve a proposal which would add the NFT to the catalog
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
access(all) contract NFTCatalog {
|
|
14
14
|
// EntryAdded
|
|
15
15
|
// An NFT collection has been added to the catalog
|
|
16
|
-
|
|
16
|
+
access(all) event EntryAdded(
|
|
17
17
|
collectionIdentifier : String,
|
|
18
18
|
contractName : String,
|
|
19
19
|
contractAddress : Address,
|
|
@@ -30,7 +30,7 @@ pub contract NFTCatalog {
|
|
|
30
30
|
|
|
31
31
|
// EntryUpdated
|
|
32
32
|
// An NFT Collection has been updated in the catalog
|
|
33
|
-
|
|
33
|
+
access(all) event EntryUpdated(
|
|
34
34
|
collectionIdentifier : String,
|
|
35
35
|
contractName : String,
|
|
36
36
|
contractAddress : Address,
|
|
@@ -47,23 +47,23 @@ pub contract NFTCatalog {
|
|
|
47
47
|
|
|
48
48
|
// EntryRemoved
|
|
49
49
|
// An NFT Collection has been removed from the catalog
|
|
50
|
-
|
|
50
|
+
access(all) event EntryRemoved(collectionIdentifier : String, nftType: Type)
|
|
51
51
|
|
|
52
52
|
// ProposalEntryAdded
|
|
53
53
|
// A new proposal to make an addtion to the catalog has been made
|
|
54
|
-
|
|
54
|
+
access(all) event ProposalEntryAdded(proposalID : UInt64, collectionIdentifier : String, message: String, status: String, proposer : Address)
|
|
55
55
|
|
|
56
56
|
// ProposalEntryUpdated
|
|
57
57
|
// A proposal has been updated
|
|
58
|
-
|
|
58
|
+
access(all) event ProposalEntryUpdated(proposalID : UInt64, collectionIdentifier : String, message: String, status: String, proposer : Address)
|
|
59
59
|
|
|
60
60
|
// ProposalEntryRemoved
|
|
61
61
|
// A proposal has been removed from storage
|
|
62
|
-
|
|
62
|
+
access(all) event ProposalEntryRemoved(proposalID : UInt64)
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
access(all) let ProposalManagerStoragePath: StoragePath
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
access(all) let ProposalManagerPublicPath: PublicPath
|
|
67
67
|
|
|
68
68
|
access(self) let catalog: {String : NFTCatalog.NFTCatalogMetadata} // { collectionIdentifier -> Metadata }
|
|
69
69
|
access(self) let catalogTypeData: {String : {String : Bool}} // Additional view to go from { NFT Type Identifier -> {Collection Identifier : Bool } }
|
|
@@ -75,17 +75,17 @@ pub contract NFTCatalog {
|
|
|
75
75
|
// NFTCatalogProposalManager
|
|
76
76
|
// Used to authenticate proposals made to the catalog
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
access(all) resource interface NFTCatalogProposalManagerPublic {
|
|
79
|
+
access(all) fun getCurrentProposalEntry(): String?
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
access(all) resource NFTCatalogProposalManager : NFTCatalogProposalManagerPublic {
|
|
82
82
|
access(self) var currentProposalEntry: String?
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
access(all) fun getCurrentProposalEntry(): String? {
|
|
85
85
|
return self.currentProposalEntry
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
access(all) fun setCurrentProposalEntry(identifier: String?) {
|
|
89
89
|
self.currentProposalEntry = identifier
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -95,19 +95,19 @@ pub contract NFTCatalog {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
access(all) resource Snapshot {
|
|
99
|
+
access(all) var catalogSnapshot: {String : NFTCatalogMetadata}
|
|
100
|
+
access(all) var shouldUseSnapshot: Bool
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
access(all) fun setPartialSnapshot(_ snapshotKey: String, _ snapshotEntry: NFTCatalogMetadata) {
|
|
103
103
|
self.catalogSnapshot[snapshotKey] = snapshotEntry
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
access(all) fun setShouldUseSnapshot(_ shouldUseSnapshot: Bool) {
|
|
107
107
|
self.shouldUseSnapshot = shouldUseSnapshot
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
access(all) fun getCatalogSnapshot(): {String : NFTCatalogMetadata} {
|
|
111
111
|
return self.catalogSnapshot
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -117,7 +117,7 @@ pub contract NFTCatalog {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
access(all) fun createEmptySnapshot(): @Snapshot {
|
|
121
121
|
return <- create Snapshot()
|
|
122
122
|
}
|
|
123
123
|
|
|
@@ -125,13 +125,13 @@ pub contract NFTCatalog {
|
|
|
125
125
|
// Represents information about an NFT collection resource
|
|
126
126
|
// Note: Not suing the struct from Metadata standard due to
|
|
127
127
|
// inability to store functions
|
|
128
|
-
|
|
128
|
+
access(all) struct NFTCollectionData {
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
access(all) let storagePath : StoragePath
|
|
131
|
+
access(all) let publicPath : PublicPath
|
|
132
|
+
access(all) let privatePath: PrivatePath
|
|
133
|
+
access(all) let publicLinkedType: Type
|
|
134
|
+
access(all) let privateLinkedType: Type
|
|
135
135
|
|
|
136
136
|
init(
|
|
137
137
|
storagePath : StoragePath,
|
|
@@ -150,12 +150,12 @@ pub contract NFTCatalog {
|
|
|
150
150
|
|
|
151
151
|
// NFTCatalogMetadata
|
|
152
152
|
// Represents data about an NFT
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
access(all) struct NFTCatalogMetadata {
|
|
154
|
+
access(all) let contractName : String
|
|
155
|
+
access(all) let contractAddress : Address
|
|
156
|
+
access(all) let nftType: Type
|
|
157
|
+
access(all) let collectionData: NFTCollectionData
|
|
158
|
+
access(all) let collectionDisplay: MetadataViews.NFTCollectionDisplay
|
|
159
159
|
|
|
160
160
|
init (contractName : String, contractAddress : Address, nftType: Type, collectionData : NFTCollectionData, collectionDisplay : MetadataViews.NFTCollectionDisplay) {
|
|
161
161
|
self.contractName = contractName
|
|
@@ -169,13 +169,13 @@ pub contract NFTCatalog {
|
|
|
169
169
|
// NFTCatalogProposal
|
|
170
170
|
// Represents a proposal to the catalog
|
|
171
171
|
// Includes data about an NFT
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
172
|
+
access(all) struct NFTCatalogProposal {
|
|
173
|
+
access(all) let collectionIdentifier : String
|
|
174
|
+
access(all) let metadata : NFTCatalogMetadata
|
|
175
|
+
access(all) let message : String
|
|
176
|
+
access(all) let status : String
|
|
177
|
+
access(all) let proposer : Address
|
|
178
|
+
access(all) let createdTime : UFix64
|
|
179
179
|
|
|
180
180
|
init(collectionIdentifier : String, metadata : NFTCatalogMetadata, message : String, status : String, proposer : Address) {
|
|
181
181
|
self.collectionIdentifier = collectionIdentifier
|
|
@@ -192,8 +192,8 @@ pub contract NFTCatalog {
|
|
|
192
192
|
If obtaining all elements from the catalog is essential, please
|
|
193
193
|
use the getCatalogKeys and forEachCatalogKey methods instead.
|
|
194
194
|
*/
|
|
195
|
-
|
|
196
|
-
let snapshot = self.account.borrow<&NFTCatalog.Snapshot>(from: /storage/CatalogSnapshot)
|
|
195
|
+
access(all) fun getCatalog() : {String : NFTCatalogMetadata} {
|
|
196
|
+
let snapshot = self.account.storage.borrow<&NFTCatalog.Snapshot>(from: /storage/CatalogSnapshot)
|
|
197
197
|
if snapshot != nil {
|
|
198
198
|
let snapshot = snapshot!
|
|
199
199
|
if snapshot.shouldUseSnapshot {
|
|
@@ -206,23 +206,23 @@ pub contract NFTCatalog {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
access(all) fun getCatalogKeys(): [String] {
|
|
210
210
|
return self.catalog.keys
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
access(all) fun forEachCatalogKey(_ function: fun (String): Bool) {
|
|
214
214
|
self.catalog.forEachKey(function)
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
access(all) view fun getCatalogEntry(collectionIdentifier : String) : NFTCatalogMetadata? {
|
|
218
218
|
return self.catalog[collectionIdentifier]
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
access(all) fun getCollectionsForType(nftTypeIdentifier: String) : {String : Bool}? {
|
|
222
222
|
return self.catalogTypeData[nftTypeIdentifier]
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
access(all) fun getCatalogTypeData() : {String : {String : Bool}} {
|
|
226
226
|
return self.catalogTypeData
|
|
227
227
|
}
|
|
228
228
|
|
|
@@ -231,12 +231,10 @@ pub contract NFTCatalog {
|
|
|
231
231
|
// @param metadata: The Metadata for the NFT collection that will be stored in the catalog
|
|
232
232
|
// @param message: A message to the catalog owners
|
|
233
233
|
// @param proposer: Who is making the proposition(the address needs to be verified)
|
|
234
|
-
|
|
235
|
-
let
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
let proposerManagerRef = proposerManagerCap.borrow()!
|
|
234
|
+
access(all) fun proposeNFTMetadata(collectionIdentifier : String, metadata : NFTCatalogMetadata, message : String, proposer : Address) : UInt64 {
|
|
235
|
+
let proposerManagerRef = getAccount(proposer).capabilities.borrow<&NFTCatalogProposalManager>(
|
|
236
|
+
NFTCatalog.ProposalManagerPublicPath
|
|
237
|
+
) ?? panic("Proposer needs to set up a manager")
|
|
240
238
|
|
|
241
239
|
assert(proposerManagerRef.getCurrentProposalEntry()! == collectionIdentifier, message: "Expected proposal entry does not match entry for the proposer")
|
|
242
240
|
|
|
@@ -250,41 +248,39 @@ pub contract NFTCatalog {
|
|
|
250
248
|
|
|
251
249
|
// Withdraw a proposal from the catalog
|
|
252
250
|
// @param proposalID: The ID of proposal you want to withdraw
|
|
253
|
-
|
|
251
|
+
access(all) fun withdrawNFTProposal(proposalID : UInt64) {
|
|
254
252
|
pre {
|
|
255
253
|
self.catalogProposals[proposalID] != nil : "Invalid Proposal ID"
|
|
256
254
|
}
|
|
257
255
|
let proposal = self.catalogProposals[proposalID]!
|
|
258
256
|
let proposer = proposal.proposer
|
|
259
257
|
|
|
260
|
-
let
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
let proposerManagerRef = proposerManagerCap.borrow()!
|
|
258
|
+
let proposerManagerRef = getAccount(proposer).capabilities.borrow<&NFTCatalogProposalManager>(
|
|
259
|
+
NFTCatalog.ProposalManagerPublicPath
|
|
260
|
+
) ?? panic("Proposer needs to set up a manager")
|
|
265
261
|
|
|
266
262
|
assert(proposerManagerRef.getCurrentProposalEntry()! == proposal.collectionIdentifier, message: "Expected proposal entry does not match entry for the proposer")
|
|
267
263
|
|
|
268
264
|
self.removeCatalogProposal(proposalID : proposalID)
|
|
269
265
|
}
|
|
270
266
|
|
|
271
|
-
|
|
267
|
+
access(all) fun getCatalogProposals() : {UInt64 : NFTCatalogProposal} {
|
|
272
268
|
return self.catalogProposals
|
|
273
269
|
}
|
|
274
270
|
|
|
275
|
-
|
|
271
|
+
access(all) view fun getCatalogProposalEntry(proposalID : UInt64) : NFTCatalogProposal? {
|
|
276
272
|
return self.catalogProposals[proposalID]
|
|
277
273
|
}
|
|
278
274
|
|
|
279
|
-
|
|
275
|
+
access(all) fun getCatalogProposalKeys() : [UInt64] {
|
|
280
276
|
return self.catalogProposals.keys
|
|
281
277
|
}
|
|
282
278
|
|
|
283
|
-
|
|
279
|
+
access(all) fun forEachCatalogProposalKey(_ function: fun (UInt64): Bool) {
|
|
284
280
|
self.catalogProposals.forEachKey(function)
|
|
285
281
|
}
|
|
286
282
|
|
|
287
|
-
|
|
283
|
+
access(all) fun createNFTCatalogProposalManager(): @NFTCatalogProposalManager {
|
|
288
284
|
return <-create NFTCatalogProposalManager()
|
|
289
285
|
}
|
|
290
286
|
|
|
@@ -6,11 +6,13 @@ import "NFTCatalog"
|
|
|
6
6
|
// a proxy resource to receive a capability that lets you make changes to the NFT Catalog
|
|
7
7
|
// and manage proposals
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
access(all) contract NFTCatalogAdmin {
|
|
10
|
+
|
|
11
|
+
access(all) entitlement CatalogActions
|
|
10
12
|
|
|
11
13
|
// AddProposalAccepted
|
|
12
14
|
// Emitted when a proposal to add a new catalog item has been approved by an admin
|
|
13
|
-
|
|
15
|
+
access(all) event AddProposalAccepted(
|
|
14
16
|
proposer: Address,
|
|
15
17
|
collectionIdentifier : String,
|
|
16
18
|
contractName : String,
|
|
@@ -20,7 +22,7 @@ pub contract NFTCatalogAdmin {
|
|
|
20
22
|
|
|
21
23
|
// UpdateProposalAccepted
|
|
22
24
|
// Emitted when a proposal to update a catalog item has been approved by an admin
|
|
23
|
-
|
|
25
|
+
access(all) event UpdateProposalAccepted(
|
|
24
26
|
proposer: Address,
|
|
25
27
|
collectionIdentifier : String,
|
|
26
28
|
contractName : String,
|
|
@@ -30,7 +32,7 @@ pub contract NFTCatalogAdmin {
|
|
|
30
32
|
|
|
31
33
|
// ProposalRejected
|
|
32
34
|
// Emitted when a proposal to add or update a catalog item has been rejected.
|
|
33
|
-
|
|
35
|
+
access(all) event ProposalRejected(
|
|
34
36
|
proposer: Address,
|
|
35
37
|
collectionIdentifier : String,
|
|
36
38
|
contractName : String,
|
|
@@ -38,33 +40,33 @@ pub contract NFTCatalogAdmin {
|
|
|
38
40
|
displayName : String
|
|
39
41
|
)
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
access(all) let AdminPrivatePath: PrivatePath
|
|
44
|
+
access(all) let AdminStoragePath: StoragePath
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
access(all) let AdminProxyPublicPath: PublicPath
|
|
47
|
+
access(all) let AdminProxyStoragePath: StoragePath
|
|
46
48
|
|
|
47
49
|
// Admin
|
|
48
50
|
// Admin resource to manage NFT Catalog
|
|
49
|
-
|
|
51
|
+
access(all) resource Admin {
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
access(CatalogActions) fun addCatalogEntry(collectionIdentifier: String, metadata : NFTCatalog.NFTCatalogMetadata) {
|
|
52
54
|
NFTCatalog.addCatalogEntry(collectionIdentifier: collectionIdentifier, metadata : metadata)
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
access(CatalogActions) fun updateCatalogEntry(collectionIdentifier : String , metadata : NFTCatalog.NFTCatalogMetadata) {
|
|
56
58
|
NFTCatalog.updateCatalogEntry(collectionIdentifier: collectionIdentifier, metadata : metadata)
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
access(CatalogActions) fun removeCatalogEntry(collectionIdentifier : String) {
|
|
60
62
|
NFTCatalog.removeCatalogEntry(collectionIdentifier : collectionIdentifier)
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
access(CatalogActions) fun removeCatalogEntryUnsafe(collectionIdentifier : String, nftTypeIdentifier: String) {
|
|
64
66
|
NFTCatalog.removeCatalogEntryUnsafe(collectionIdentifier : collectionIdentifier, nftTypeIdentifier: nftTypeIdentifier)
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
access(CatalogActions) fun approveCatalogProposal(proposalID : UInt64) {
|
|
68
70
|
pre {
|
|
69
71
|
NFTCatalog.getCatalogProposalEntry(proposalID : proposalID) != nil : "Invalid Proposal ID"
|
|
70
72
|
NFTCatalog.getCatalogProposalEntry(proposalID : proposalID)!.status == "IN_REVIEW" : "Invalid Proposal"
|
|
@@ -94,7 +96,7 @@ pub contract NFTCatalogAdmin {
|
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
access(CatalogActions) fun rejectCatalogProposal(proposalID : UInt64) {
|
|
98
100
|
pre {
|
|
99
101
|
NFTCatalog.getCatalogProposalEntry(proposalID : proposalID) != nil : "Invalid Proposal ID"
|
|
100
102
|
NFTCatalog.getCatalogProposalEntry(proposalID : proposalID)!.status == "IN_REVIEW" : "Invalid Proposal"
|
|
@@ -111,7 +113,7 @@ pub contract NFTCatalogAdmin {
|
|
|
111
113
|
)
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
|
|
116
|
+
access(CatalogActions) fun removeCatalogProposal(proposalID : UInt64) {
|
|
115
117
|
pre {
|
|
116
118
|
NFTCatalog.getCatalogProposalEntry(proposalID : proposalID) != nil : "Invalid Proposal ID"
|
|
117
119
|
}
|
|
@@ -125,16 +127,16 @@ pub contract NFTCatalogAdmin {
|
|
|
125
127
|
// AdminProxy
|
|
126
128
|
// A proxy resource that can store
|
|
127
129
|
// a capability to admin controls
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
access(all) resource interface IAdminProxy {
|
|
131
|
+
access(all) fun addCapability(capability : Capability<auth(CatalogActions) &Admin>)
|
|
132
|
+
access(all) fun hasCapability() : Bool
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
|
|
135
|
+
access(all) resource AdminProxy : IAdminProxy {
|
|
134
136
|
|
|
135
|
-
access(self) var capability : Capability
|
|
137
|
+
access(self) var capability : Capability<auth(CatalogActions) &Admin>?
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
access(all) fun addCapability(capability : Capability<auth(CatalogActions) &Admin>) {
|
|
138
140
|
pre {
|
|
139
141
|
capability.check() : "Invalid Admin Capability"
|
|
140
142
|
self.capability == nil : "Admin Proxy already set"
|
|
@@ -142,11 +144,11 @@ pub contract NFTCatalogAdmin {
|
|
|
142
144
|
self.capability = capability
|
|
143
145
|
}
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
access(all) fun getCapability() : Capability<auth(CatalogActions) &Admin>? {
|
|
146
148
|
return self.capability
|
|
147
149
|
}
|
|
148
150
|
|
|
149
|
-
|
|
151
|
+
access(all) fun hasCapability() : Bool {
|
|
150
152
|
return self.capability != nil
|
|
151
153
|
}
|
|
152
154
|
|
|
@@ -156,7 +158,7 @@ pub contract NFTCatalogAdmin {
|
|
|
156
158
|
|
|
157
159
|
}
|
|
158
160
|
|
|
159
|
-
|
|
161
|
+
access(all) fun createAdminProxy() : @AdminProxy {
|
|
160
162
|
return <- create AdminProxy()
|
|
161
163
|
}
|
|
162
164
|
|
|
@@ -169,7 +171,6 @@ pub contract NFTCatalogAdmin {
|
|
|
169
171
|
|
|
170
172
|
let admin <- create Admin()
|
|
171
173
|
|
|
172
|
-
self.account.save(<-admin, to: self.AdminStoragePath)
|
|
173
|
-
self.account.link<&Admin>(self.AdminPrivatePath, target: self.AdminStoragePath)
|
|
174
|
+
self.account.storage.save(<-admin, to: self.AdminStoragePath)
|
|
174
175
|
}
|
|
175
176
|
}
|