@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 +1 -1
- package/contracts/capability-cache/CapabilityCache.cdc +97 -0
- package/contracts/flowty-drops/ContractManager.cdc +47 -0
- package/contracts/flowty-drops/DropFactory.cdc +75 -0
- package/contracts/flowty-drops/DropTypes.cdc +278 -0
- package/contracts/flowty-drops/FlowtyAddressVerifiers.cdc +64 -0
- package/contracts/flowty-drops/FlowtyDrops.cdc +431 -0
- package/contracts/flowty-drops/FlowtyPricers.cdc +48 -0
- package/contracts/flowty-drops/FlowtySwitchers.cdc +113 -0
- package/contracts/flowty-drops/initializers/ContractBorrower.cdc +14 -0
- package/contracts/flowty-drops/initializers/ContractInitializer.cdc +7 -0
- package/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc +58 -0
- package/contracts/flowty-drops/nft/BaseCollection.cdc +97 -0
- package/contracts/flowty-drops/nft/BaseNFT.cdc +107 -0
- package/contracts/flowty-drops/nft/ContractFactory.cdc +13 -0
- package/contracts/flowty-drops/nft/ContractFactoryTemplate.cdc +48 -0
- package/contracts/flowty-drops/nft/NFTMetadata.cdc +119 -0
- package/contracts/flowty-drops/nft/OpenEditionNFT.cdc +41 -0
- package/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +52 -0
- package/contracts/flowty-drops/nft/UniversalCollection.cdc +23 -0
- package/flow.json +148 -4
- package/package.json +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import "NonFungibleToken"
|
|
2
|
+
import "ViewResolver"
|
|
3
|
+
import "MetadataViews"
|
|
4
|
+
import "NFTMetadata"
|
|
5
|
+
import "FlowtyDrops"
|
|
6
|
+
import "AddressUtils"
|
|
7
|
+
import "StringUtils"
|
|
8
|
+
|
|
9
|
+
access(all) contract interface BaseCollection: ViewResolver {
|
|
10
|
+
access(all) var MetadataCap: Capability<&NFTMetadata.Container>
|
|
11
|
+
access(all) var totalSupply: UInt64
|
|
12
|
+
|
|
13
|
+
access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}
|
|
14
|
+
|
|
15
|
+
// The base collection is an interface that attmepts to take more boilerplate
|
|
16
|
+
// off of NFT-standard compliant definitions.
|
|
17
|
+
access(all) resource interface Collection: NonFungibleToken.Collection {
|
|
18
|
+
access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
|
|
19
|
+
access(all) var nftType: Type
|
|
20
|
+
|
|
21
|
+
access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
|
|
22
|
+
pre {
|
|
23
|
+
token.getType() == self.nftType: "unexpected nft type being deposited"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
destroy self.ownedNFTs.insert(key: token.uuid, <-token)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
|
|
30
|
+
return &self.ownedNFTs[id]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
|
|
34
|
+
return {
|
|
35
|
+
self.nftType: true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
access(all) view fun isSupportedNFTType(type: Type): Bool {
|
|
40
|
+
return type == self.nftType
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
|
|
44
|
+
return <- self.ownedNFTs.remove(key: withdrawID)!
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
access(all) view fun getContractViews(resourceType: Type?): [Type] {
|
|
49
|
+
return [
|
|
50
|
+
Type<MetadataViews.NFTCollectionData>(),
|
|
51
|
+
Type<MetadataViews.NFTCollectionDisplay>()
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
|
|
56
|
+
if resourceType == nil {
|
|
57
|
+
return nil
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let rt = resourceType!
|
|
61
|
+
let segments = rt.identifier.split(separator: ".")
|
|
62
|
+
let pathIdentifier = StringUtils.join([segments[2], segments[1]], "_")
|
|
63
|
+
|
|
64
|
+
let addr = AddressUtils.parseAddress(rt)!
|
|
65
|
+
let acct = getAccount(addr)
|
|
66
|
+
|
|
67
|
+
switch viewType {
|
|
68
|
+
case Type<MetadataViews.NFTCollectionData>():
|
|
69
|
+
let segments = rt.identifier.split(separator: ".")
|
|
70
|
+
let pathIdentifier = StringUtils.join([segments[2], segments[1]], "_")
|
|
71
|
+
|
|
72
|
+
return MetadataViews.NFTCollectionData(
|
|
73
|
+
storagePath: StoragePath(identifier: pathIdentifier)!,
|
|
74
|
+
publicPath: PublicPath(identifier: pathIdentifier)!,
|
|
75
|
+
publicCollection: Type<&{NonFungibleToken.Collection}>(),
|
|
76
|
+
publicLinkedType: Type<&{NonFungibleToken.Collection}>(),
|
|
77
|
+
createEmptyCollectionFunction: fun(): @{NonFungibleToken.Collection} {
|
|
78
|
+
let addr = AddressUtils.parseAddress(rt)!
|
|
79
|
+
let c = getAccount(addr).contracts.borrow<&{BaseCollection}>(name: segments[2])!
|
|
80
|
+
return <- c.createEmptyCollection(nftType: rt)
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
case Type<MetadataViews.NFTCollectionDisplay>():
|
|
84
|
+
let c = getAccount(addr).contracts.borrow<&{BaseCollection}>(name: segments[2])!
|
|
85
|
+
let md = c.MetadataCap.borrow()
|
|
86
|
+
if md == nil {
|
|
87
|
+
return nil
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return md!.collectionInfo.collectionDisplay
|
|
91
|
+
case Type<FlowtyDrops.DropResolver>():
|
|
92
|
+
return FlowtyDrops.DropResolver(cap: acct.capabilities.get<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerPublicPath))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return nil
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import "NonFungibleToken"
|
|
2
|
+
import "StringUtils"
|
|
3
|
+
import "AddressUtils"
|
|
4
|
+
import "ViewResolver"
|
|
5
|
+
import "MetadataViews"
|
|
6
|
+
import "BaseCollection"
|
|
7
|
+
import "FlowtyDrops"
|
|
8
|
+
import "NFTMetadata"
|
|
9
|
+
import "UniversalCollection"
|
|
10
|
+
|
|
11
|
+
// A few primary challenges that have come up in thinking about how to define base-level interfaces
|
|
12
|
+
// for collections and NFTs:
|
|
13
|
+
//
|
|
14
|
+
// - How do we resolve contract-level interfaces?
|
|
15
|
+
// - How do we track total supply/serial numbers for NFTs?
|
|
16
|
+
// - How do we store traits and medias?
|
|
17
|
+
//
|
|
18
|
+
// For some of these, mainly contract-level interfaces, we might be able to simply consolidate
|
|
19
|
+
// all of these into one contract interface and require that collection display (banner, thumbnail, name, description, etc.)
|
|
20
|
+
// be stored at the top-level of the contract so that they can be easily referenced later. This could make things easier in that we can
|
|
21
|
+
// make a base definition for anyone to use, but since it isn't a concrete definition, anyone can later override the pre-generated
|
|
22
|
+
// pieces to and modify the code to their liking. This could achieve the best of both worlds where there is minimal work to get something
|
|
23
|
+
// off the ground, but doesn't close the door to customization in the future. This could come at the cost of duplicated resource definitions,
|
|
24
|
+
// or could have the risk of circular imports depending on how we resolve certain pieces of information about a collection.
|
|
25
|
+
access(all) contract interface BaseNFT: ViewResolver {
|
|
26
|
+
access(all) resource interface NFT: NonFungibleToken.NFT {
|
|
27
|
+
// This is the id entry that corresponds to an NFTs NFTMetadata.Container entry.
|
|
28
|
+
// Some NFTs might share the same data, so we want to permit reusing storage where possible
|
|
29
|
+
access(all) metadataID: UInt64
|
|
30
|
+
|
|
31
|
+
access(all) view fun getViews(): [Type] {
|
|
32
|
+
return [
|
|
33
|
+
Type<MetadataViews.Display>(),
|
|
34
|
+
Type<MetadataViews.Serial>(),
|
|
35
|
+
Type<MetadataViews.Traits>(),
|
|
36
|
+
Type<MetadataViews.Editions>(),
|
|
37
|
+
Type<MetadataViews.ExternalURL>(),
|
|
38
|
+
Type<MetadataViews.NFTCollectionData>(),
|
|
39
|
+
Type<MetadataViews.NFTCollectionDisplay>()
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
access(all) fun resolveView(_ view: Type): AnyStruct? {
|
|
44
|
+
if view == Type<MetadataViews.Serial>() {
|
|
45
|
+
return self.id
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let rt = self.getType()
|
|
49
|
+
let segments = rt.identifier.split(separator: ".")
|
|
50
|
+
let addr = AddressUtils.parseAddress(rt)!
|
|
51
|
+
let tmp = getAccount(addr).contracts.borrow<&{BaseCollection}>(name: segments[2])
|
|
52
|
+
if tmp == nil {
|
|
53
|
+
return nil
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let c = tmp!
|
|
57
|
+
let tmpMd = c.MetadataCap.borrow()
|
|
58
|
+
if tmpMd == nil {
|
|
59
|
+
return nil
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let md = tmpMd!
|
|
63
|
+
switch view {
|
|
64
|
+
case Type<MetadataViews.NFTCollectionData>():
|
|
65
|
+
let pathIdentifier = StringUtils.join([segments[2], segments[1]], "_")
|
|
66
|
+
return MetadataViews.NFTCollectionData(
|
|
67
|
+
storagePath: StoragePath(identifier: pathIdentifier)!,
|
|
68
|
+
publicPath: PublicPath(identifier: pathIdentifier)!,
|
|
69
|
+
publicCollection: Type<&{NonFungibleToken.Collection}>(),
|
|
70
|
+
publicLinkedType: Type<&{NonFungibleToken.Collection}>(),
|
|
71
|
+
createEmptyCollectionFunction: fun(): @{NonFungibleToken.Collection} {
|
|
72
|
+
let addr = AddressUtils.parseAddress(rt)!
|
|
73
|
+
let c = getAccount(addr).contracts.borrow<&{BaseCollection}>(name: segments[2])!
|
|
74
|
+
return <- c.createEmptyCollection(nftType: rt)
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
case Type<MetadataViews.NFTCollectionDisplay>():
|
|
78
|
+
return md.collectionInfo.collectionDisplay
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if let entry = md.borrowMetadata(id: self.metadataID) {
|
|
82
|
+
switch view {
|
|
83
|
+
case Type<MetadataViews.Traits>():
|
|
84
|
+
return entry.traits
|
|
85
|
+
case Type<MetadataViews.Editions>():
|
|
86
|
+
return entry.editions
|
|
87
|
+
case Type<MetadataViews.Display>():
|
|
88
|
+
let num = (entry.editions?.infoList?.length ?? 0) > 0 ? entry.editions!.infoList[0].number : self.id
|
|
89
|
+
|
|
90
|
+
return MetadataViews.Display(
|
|
91
|
+
name: entry.name.concat(" #").concat(num.toString()),
|
|
92
|
+
description: entry.description,
|
|
93
|
+
thumbnail: NFTMetadata.UriFile(entry.thumbnail.uri())
|
|
94
|
+
)
|
|
95
|
+
case Type<MetadataViews.ExternalURL>():
|
|
96
|
+
return entry.externalURL
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return nil
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
|
|
104
|
+
return <- UniversalCollection.createCollection(nftType: self.getType())
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "ContractFactoryTemplate"
|
|
2
|
+
import "AddressUtils"
|
|
3
|
+
|
|
4
|
+
access(all) contract ContractFactory {
|
|
5
|
+
access(all) fun createContract(templateType: Type, acct: auth(Contracts) &Account, name: String, params: {String: AnyStruct}, initializeIdentifier: String) {
|
|
6
|
+
let templateAddr = AddressUtils.parseAddress(templateType)!
|
|
7
|
+
let contractName = templateType.identifier.split(separator: ".")[2]
|
|
8
|
+
let templateContract = getAccount(templateAddr).contracts.borrow<&{ContractFactoryTemplate}>(name: contractName)
|
|
9
|
+
?? panic("provided type is not a ContractTemplateFactory")
|
|
10
|
+
|
|
11
|
+
templateContract.createContract(acct: acct, name: name, params: params, initializeIdentifier: initializeIdentifier)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import "NonFungibleToken"
|
|
2
|
+
import "MetadataViews"
|
|
3
|
+
import "ViewResolver"
|
|
4
|
+
|
|
5
|
+
import "FlowtyDrops"
|
|
6
|
+
import "BaseNFT"
|
|
7
|
+
import "BaseCollection"
|
|
8
|
+
import "NFTMetadata"
|
|
9
|
+
import "UniversalCollection"
|
|
10
|
+
import "ContractBorrower"
|
|
11
|
+
|
|
12
|
+
import "AddressUtils"
|
|
13
|
+
|
|
14
|
+
access(all) contract interface ContractFactoryTemplate {
|
|
15
|
+
access(all) fun createContract(acct: auth(Contracts) &Account, name: String, params: {String: AnyStruct}, initializeIdentifier: String)
|
|
16
|
+
|
|
17
|
+
access(all) fun getContractAddresses(): {String: Address} {
|
|
18
|
+
let d: {String: Address} = {
|
|
19
|
+
"NonFungibleToken": AddressUtils.parseAddress(Type<&{NonFungibleToken}>())!,
|
|
20
|
+
"MetadataViews": AddressUtils.parseAddress(Type<&MetadataViews>())!,
|
|
21
|
+
"ViewResolver": AddressUtils.parseAddress(Type<&{ViewResolver}>())!,
|
|
22
|
+
"FlowtyDrops": AddressUtils.parseAddress(Type<&FlowtyDrops>())!,
|
|
23
|
+
"BaseNFT": AddressUtils.parseAddress(Type<&{BaseNFT}>())!,
|
|
24
|
+
"BaseCollection": AddressUtils.parseAddress(Type<&{BaseCollection}>())!,
|
|
25
|
+
"NFTMetadata": AddressUtils.parseAddress(Type<&NFTMetadata>())!,
|
|
26
|
+
"UniversalCollection": AddressUtils.parseAddress(Type<&UniversalCollection>())!,
|
|
27
|
+
"BaseCollection": AddressUtils.parseAddress(Type<&{BaseCollection}>())!,
|
|
28
|
+
"AddressUtils": AddressUtils.parseAddress(Type<&AddressUtils>())!,
|
|
29
|
+
"ContractBorrower": AddressUtils.parseAddress(Type<ContractBorrower>())!
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return d
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
access(all) fun importLine(name: String, addr: Address): String {
|
|
36
|
+
return "import ".concat(name).concat(" from ").concat(addr.toString()).concat("\n")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
access(all) fun generateImports(names: [String]): String {
|
|
40
|
+
let addresses = self.getContractAddresses()
|
|
41
|
+
var imports = ""
|
|
42
|
+
for n in names {
|
|
43
|
+
imports = imports.concat(self.importLine(name: n, addr: addresses[n] ?? panic("missing contract import address: ".concat(n))))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return imports
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import "NonFungibleToken"
|
|
2
|
+
import "MetadataViews"
|
|
3
|
+
|
|
4
|
+
access(all) contract NFTMetadata {
|
|
5
|
+
access(all) entitlement Owner
|
|
6
|
+
|
|
7
|
+
access(all) event MetadataFrozen(uuid: UInt64, owner: Address?)
|
|
8
|
+
|
|
9
|
+
access(all) struct CollectionInfo {
|
|
10
|
+
access(all) var collectionDisplay: MetadataViews.NFTCollectionDisplay
|
|
11
|
+
|
|
12
|
+
init(collectionDisplay: MetadataViews.NFTCollectionDisplay) {
|
|
13
|
+
self.collectionDisplay = collectionDisplay
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
access(all) struct Metadata {
|
|
18
|
+
// these are used to create the display metadata view so that we can concatenate
|
|
19
|
+
// the id onto it.
|
|
20
|
+
access(all) let name: String
|
|
21
|
+
access(all) let description: String
|
|
22
|
+
access(all) let thumbnail: {MetadataViews.File}
|
|
23
|
+
|
|
24
|
+
access(all) let traits: MetadataViews.Traits?
|
|
25
|
+
access(all) let editions: MetadataViews.Editions?
|
|
26
|
+
access(all) let externalURL: MetadataViews.ExternalURL?
|
|
27
|
+
|
|
28
|
+
access(all) let data: {String: AnyStruct} // general-purpose data bucket
|
|
29
|
+
|
|
30
|
+
init(
|
|
31
|
+
name: String,
|
|
32
|
+
description: String,
|
|
33
|
+
thumbnail: {MetadataViews.File},
|
|
34
|
+
traits: MetadataViews.Traits?,
|
|
35
|
+
editions: MetadataViews.Editions?,
|
|
36
|
+
externalURL: MetadataViews.ExternalURL?,
|
|
37
|
+
data: {String: AnyStruct}
|
|
38
|
+
) {
|
|
39
|
+
self.name = name
|
|
40
|
+
self.description = description
|
|
41
|
+
self.thumbnail = thumbnail
|
|
42
|
+
|
|
43
|
+
self.traits = traits
|
|
44
|
+
self.editions = editions
|
|
45
|
+
self.externalURL = externalURL
|
|
46
|
+
|
|
47
|
+
self.data = {}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
access(all) resource Container {
|
|
52
|
+
access(all) var collectionInfo: CollectionInfo
|
|
53
|
+
access(all) let metadata: {UInt64: Metadata}
|
|
54
|
+
access(all) var frozen: Bool
|
|
55
|
+
|
|
56
|
+
access(all) fun borrowMetadata(id: UInt64): &Metadata? {
|
|
57
|
+
return &self.metadata[id]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
access(Owner) fun addMetadata(id: UInt64, data: Metadata) {
|
|
61
|
+
pre {
|
|
62
|
+
self.metadata[id] == nil: "id already has metadata assigned"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
self.metadata[id] = data
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
access(Owner) fun freeze() {
|
|
69
|
+
self.frozen = true
|
|
70
|
+
emit MetadataFrozen(uuid: self.uuid, owner: self.owner?.address)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
init(collectionInfo: CollectionInfo) {
|
|
74
|
+
self.collectionInfo = collectionInfo
|
|
75
|
+
self.metadata = {}
|
|
76
|
+
self.frozen = false
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
access(all) struct InitializedCaps {
|
|
81
|
+
access(all) let pubCap: Capability<&Container>
|
|
82
|
+
access(all) let ownerCap: Capability<auth(Owner) &Container>
|
|
83
|
+
|
|
84
|
+
init(pubCap: Capability<&Container>, ownerCap: Capability<auth(Owner) &Container>) {
|
|
85
|
+
self.pubCap = pubCap
|
|
86
|
+
self.ownerCap = ownerCap
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
access(all) fun createContainer(collectionInfo: CollectionInfo): @Container {
|
|
91
|
+
return <- create Container(collectionInfo: collectionInfo)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
access(all) fun initialize(acct: auth(Storage, Capabilities) &Account, collectionInfo: CollectionInfo, collectionType: Type): InitializedCaps {
|
|
95
|
+
let storagePath = self.getCollectionStoragePath(type: collectionType)
|
|
96
|
+
let container <- self.createContainer(collectionInfo: collectionInfo)
|
|
97
|
+
acct.storage.save(<-container, to: storagePath)
|
|
98
|
+
let pubCap = acct.capabilities.storage.issue<&Container>(storagePath)
|
|
99
|
+
let ownerCap = acct.capabilities.storage.issue<auth(Owner) &Container>(storagePath)
|
|
100
|
+
return InitializedCaps(pubCap: pubCap, ownerCap: ownerCap)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
access(all) struct UriFile: MetadataViews.File {
|
|
104
|
+
access(self) let url: String
|
|
105
|
+
|
|
106
|
+
access(all) view fun uri(): String {
|
|
107
|
+
return self.url
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
init(_ url: String) {
|
|
111
|
+
self.url = url
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
access(all) fun getCollectionStoragePath(type: Type): StoragePath {
|
|
116
|
+
let segments = type.identifier.split(separator: ".")
|
|
117
|
+
return StoragePath(identifier: "NFTMetadataContainer_".concat(segments[2]).concat("_").concat(segments[1]))!
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import "NonFungibleToken"
|
|
2
|
+
import "FlowtyDrops"
|
|
3
|
+
import "BaseNFT"
|
|
4
|
+
import "NFTMetadata"
|
|
5
|
+
import "UniversalCollection"
|
|
6
|
+
import "ContractBorrower"
|
|
7
|
+
import "BaseCollection"
|
|
8
|
+
|
|
9
|
+
access(all) contract OpenEditionNFT: BaseCollection {
|
|
10
|
+
access(all) var MetadataCap: Capability<&NFTMetadata.Container>
|
|
11
|
+
access(all) var totalSupply: UInt64
|
|
12
|
+
|
|
13
|
+
access(all) resource NFT: BaseNFT.NFT {
|
|
14
|
+
access(all) let id: UInt64
|
|
15
|
+
access(all) let metadataID: UInt64
|
|
16
|
+
|
|
17
|
+
init() {
|
|
18
|
+
OpenEditionNFT.totalSupply = OpenEditionNFT.totalSupply + 1
|
|
19
|
+
self.id = OpenEditionNFT.totalSupply
|
|
20
|
+
self.metadataID = 0
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
access(all) resource NFTMinter: FlowtyDrops.Minter {
|
|
25
|
+
access(contract) fun createNextNFT(): @{NonFungibleToken.NFT} {
|
|
26
|
+
return <- create NFT()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
|
|
31
|
+
return <- UniversalCollection.createCollection(nftType: Type<@NFT>())
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
init(params: {String: AnyStruct}, initializeIdentifier: String) {
|
|
35
|
+
self.totalSupply = 0
|
|
36
|
+
self.account.storage.save(<- create NFTMinter(), to: FlowtyDrops.getMinterStoragePath(type: self.getType()))
|
|
37
|
+
params["minterController"] = self.account.capabilities.storage.issue<&{FlowtyDrops.Minter}>(FlowtyDrops.getMinterStoragePath(type: self.getType()))
|
|
38
|
+
|
|
39
|
+
self.MetadataCap = ContractBorrower.borrowInitializer(typeIdentifier: initializeIdentifier).initialize(contractAcct: self.account, params: params).pubCap
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import "ContractFactoryTemplate"
|
|
2
|
+
import "MetadataViews"
|
|
3
|
+
import "NFTMetadata"
|
|
4
|
+
|
|
5
|
+
access(all) contract OpenEditionTemplate: ContractFactoryTemplate {
|
|
6
|
+
access(all) fun createContract(acct: auth(Contracts) &Account, name: String, params: {String: AnyStruct}, initializeIdentifier: String) {
|
|
7
|
+
let code = self.generateImports(names: [
|
|
8
|
+
"NonFungibleToken",
|
|
9
|
+
"FlowtyDrops",
|
|
10
|
+
"BaseNFT",
|
|
11
|
+
"NFTMetadata",
|
|
12
|
+
"UniversalCollection",
|
|
13
|
+
"ContractBorrower",
|
|
14
|
+
"BaseCollection"
|
|
15
|
+
]).concat("\n\n")
|
|
16
|
+
.concat("access(all) contract ").concat(name).concat(": BaseCollection {\n")
|
|
17
|
+
.concat(" access(all) var MetadataCap: Capability<&NFTMetadata.Container>\n")
|
|
18
|
+
.concat(" access(all) var totalSupply: UInt64\n")
|
|
19
|
+
.concat("\n\n")
|
|
20
|
+
.concat(" access(all) resource NFT: BaseNFT.NFT {\n")
|
|
21
|
+
.concat(" access(all) let id: UInt64\n")
|
|
22
|
+
.concat(" access(all) let metadataID: UInt64\n")
|
|
23
|
+
.concat("\n\n")
|
|
24
|
+
.concat(" init() {\n")
|
|
25
|
+
.concat(" ").concat(name).concat(".totalSupply = ").concat(name).concat(".totalSupply + 1\n")
|
|
26
|
+
.concat(" self.id = ").concat(name).concat(".totalSupply\n")
|
|
27
|
+
.concat(" self.metadataID = 0\n")
|
|
28
|
+
.concat(" }\n")
|
|
29
|
+
.concat(" }\n")
|
|
30
|
+
.concat(" access(all) resource NFTMinter: FlowtyDrops.Minter {\n")
|
|
31
|
+
.concat(" access(contract) fun createNextNFT(): @{NonFungibleToken.NFT} {\n")
|
|
32
|
+
.concat(" return <- create NFT()\n")
|
|
33
|
+
.concat(" }\n")
|
|
34
|
+
.concat(" }\n")
|
|
35
|
+
.concat("\n")
|
|
36
|
+
.concat(" access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {\n")
|
|
37
|
+
.concat(" return <- UniversalCollection.createCollection(nftType: Type<@NFT>())\n")
|
|
38
|
+
.concat(" }\n")
|
|
39
|
+
.concat("\n")
|
|
40
|
+
.concat(" init(params: {String: AnyStruct}, initializeIdentifier: String) {\n")
|
|
41
|
+
.concat(" self.totalSupply = 0\n")
|
|
42
|
+
.concat(" let minter <- create NFTMinter()\n")
|
|
43
|
+
.concat(" self.account.storage.save(<-minter, to: FlowtyDrops.getMinterStoragePath(type: self.getType()))\n")
|
|
44
|
+
.concat(" params[\"minterController\"] = self.account.capabilities.storage.issue<&{FlowtyDrops.Minter}>(FlowtyDrops.getMinterStoragePath(type: self.getType()))\n")
|
|
45
|
+
.concat("\n\n")
|
|
46
|
+
.concat(" self.MetadataCap = ContractBorrower.borrowInitializer(typeIdentifier: initializeIdentifier).initialize(contractAcct: self.account, params: params).pubCap\n")
|
|
47
|
+
.concat(" }\n")
|
|
48
|
+
.concat("}\n")
|
|
49
|
+
|
|
50
|
+
acct.contracts.add(name: name, code: code.utf8, params, initializeIdentifier)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "NonFungibleToken"
|
|
2
|
+
import "MetadataViews"
|
|
3
|
+
import "BaseCollection"
|
|
4
|
+
|
|
5
|
+
access(all) contract UniversalCollection {
|
|
6
|
+
access(all) resource Collection: BaseCollection.Collection {
|
|
7
|
+
access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
|
|
8
|
+
access(all) var nftType: Type
|
|
9
|
+
|
|
10
|
+
access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
|
|
11
|
+
return <- create Collection(nftType: self.nftType)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
init (nftType: Type) {
|
|
15
|
+
self.ownedNFTs <- {}
|
|
16
|
+
self.nftType = nftType
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
access(all) fun createCollection(nftType: Type): @Collection {
|
|
21
|
+
return <- create Collection(nftType: nftType)
|
|
22
|
+
}
|
|
23
|
+
}
|