@flowtyio/flow-contracts 0.1.0-beta.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/contracts/FungibleTokenSwitchboard.cdc +360 -0
- package/contracts/MetadataViews.cdc +79 -6
- package/contracts/NonFungibleToken.cdc +17 -10
- package/contracts/TokenForwarding.cdc +19 -11
- package/contracts/capability-cache/CapabilityCache.cdc +97 -0
- package/contracts/dapper/TopShot.cdc +323 -259
- package/contracts/dapper/TopShotLocking.cdc +41 -15
- package/contracts/dapper/offers/DapperOffersV2.cdc +46 -43
- package/contracts/dapper/offers/OffersV2.cdc +40 -56
- package/contracts/dapper/offers/Resolver.cdc +20 -13
- package/contracts/emerald-city/FLOAT.cdc +259 -254
- package/contracts/example/ExampleNFT.cdc +2 -2
- package/contracts/find/FindViews.cdc +357 -353
- package/contracts/flow-utils/ScopedFTProviders.cdc +5 -2
- package/contracts/flow-utils/ScopedNFTProviders.cdc +6 -2
- package/contracts/flowty-drops/ContractManager.cdc +73 -0
- package/contracts/flowty-drops/DropFactory.cdc +75 -0
- package/contracts/flowty-drops/DropTypes.cdc +282 -0
- package/contracts/flowty-drops/FlowtyActiveCheckers.cdc +113 -0
- package/contracts/flowty-drops/FlowtyAddressVerifiers.cdc +64 -0
- package/contracts/flowty-drops/FlowtyDrops.cdc +461 -0
- package/contracts/flowty-drops/FlowtyPricers.cdc +48 -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 +57 -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 +140 -0
- package/contracts/flowty-drops/nft/OpenEditionNFT.cdc +42 -0
- package/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +54 -0
- package/contracts/flowty-drops/nft/UniversalCollection.cdc +29 -0
- package/contracts/fungible-token-router/FungibleTokenRouter.cdc +103 -0
- package/contracts/hybrid-custody/CapabilityDelegator.cdc +28 -26
- package/contracts/hybrid-custody/CapabilityFactory.cdc +20 -18
- package/contracts/hybrid-custody/CapabilityFilter.cdc +41 -24
- package/contracts/hybrid-custody/HybridCustody.cdc +303 -242
- package/contracts/hybrid-custody/factories/FTAllFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTBalanceFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTProviderFactory.cdc +17 -5
- package/contracts/hybrid-custody/factories/FTReceiverBalanceFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTReceiverFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTVaultFactory.cdc +46 -0
- package/contracts/hybrid-custody/factories/NFTCollectionFactory.cdc +45 -0
- package/contracts/hybrid-custody/factories/NFTCollectionPublicFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/NFTProviderAndCollectionFactory.cdc +22 -0
- package/contracts/hybrid-custody/factories/NFTProviderFactory.cdc +16 -4
- package/contracts/lost-and-found/LostAndFound.cdc +21 -17
- package/flow.json +181 -7
- package/package.json +1 -1
- package/contracts/hybrid-custody/factories/NFTProviderAndCollectionPublicFactory.cdc +0 -10
|
@@ -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,140 @@
|
|
|
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
|
+
access(all) let data: {String: AnyStruct}
|
|
13
|
+
|
|
14
|
+
access(all) fun getDisplay(): MetadataViews.NFTCollectionDisplay {
|
|
15
|
+
return self.collectionDisplay
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
init(collectionDisplay: MetadataViews.NFTCollectionDisplay) {
|
|
19
|
+
self.collectionDisplay = collectionDisplay
|
|
20
|
+
|
|
21
|
+
self.data = {}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
access(all) struct Metadata {
|
|
26
|
+
// these are used to create the display metadata view so that we can concatenate
|
|
27
|
+
// the id onto it.
|
|
28
|
+
access(all) let name: String
|
|
29
|
+
access(all) let description: String
|
|
30
|
+
access(all) let thumbnail: {MetadataViews.File}
|
|
31
|
+
|
|
32
|
+
access(all) let traits: MetadataViews.Traits?
|
|
33
|
+
access(all) let editions: MetadataViews.Editions?
|
|
34
|
+
access(all) let externalURL: MetadataViews.ExternalURL?
|
|
35
|
+
access(all) let royalties: MetadataViews.Royalties?
|
|
36
|
+
|
|
37
|
+
access(all) let data: {String: AnyStruct}
|
|
38
|
+
|
|
39
|
+
init(
|
|
40
|
+
name: String,
|
|
41
|
+
description: String,
|
|
42
|
+
thumbnail: {MetadataViews.File},
|
|
43
|
+
traits: MetadataViews.Traits?,
|
|
44
|
+
editions: MetadataViews.Editions?,
|
|
45
|
+
externalURL: MetadataViews.ExternalURL?,
|
|
46
|
+
royalties: MetadataViews.Royalties?,
|
|
47
|
+
data: {String: AnyStruct}
|
|
48
|
+
) {
|
|
49
|
+
self.name = name
|
|
50
|
+
self.description = description
|
|
51
|
+
self.thumbnail = thumbnail
|
|
52
|
+
|
|
53
|
+
self.traits = traits
|
|
54
|
+
self.editions = editions
|
|
55
|
+
self.externalURL = externalURL
|
|
56
|
+
self.royalties = royalties
|
|
57
|
+
|
|
58
|
+
self.data = {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
access(all) resource Container {
|
|
63
|
+
access(all) var collectionInfo: CollectionInfo
|
|
64
|
+
access(all) let metadata: {UInt64: Metadata}
|
|
65
|
+
access(all) var frozen: Bool
|
|
66
|
+
|
|
67
|
+
access(all) let data: {String: AnyStruct}
|
|
68
|
+
access(all) let resources: @{String: AnyResource}
|
|
69
|
+
|
|
70
|
+
access(all) fun borrowMetadata(id: UInt64): &Metadata? {
|
|
71
|
+
return &self.metadata[id]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
access(Owner) fun addMetadata(id: UInt64, data: Metadata) {
|
|
75
|
+
pre {
|
|
76
|
+
self.metadata[id] == nil: "id already has metadata assigned"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
self.metadata[id] = data
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
access(Owner) fun freeze() {
|
|
83
|
+
self.frozen = true
|
|
84
|
+
emit MetadataFrozen(uuid: self.uuid, owner: self.owner?.address)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
init(collectionInfo: CollectionInfo) {
|
|
88
|
+
self.collectionInfo = collectionInfo
|
|
89
|
+
self.metadata = {}
|
|
90
|
+
self.frozen = false
|
|
91
|
+
|
|
92
|
+
self.data = {}
|
|
93
|
+
self.resources <- {}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
access(all) struct InitializedCaps {
|
|
98
|
+
access(all) let pubCap: Capability<&Container>
|
|
99
|
+
access(all) let ownerCap: Capability<auth(Owner) &Container>
|
|
100
|
+
|
|
101
|
+
access(all) let data: {String: AnyStruct}
|
|
102
|
+
|
|
103
|
+
init(pubCap: Capability<&Container>, ownerCap: Capability<auth(Owner) &Container>) {
|
|
104
|
+
self.pubCap = pubCap
|
|
105
|
+
self.ownerCap = ownerCap
|
|
106
|
+
|
|
107
|
+
self.data = {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
access(all) fun createContainer(collectionInfo: CollectionInfo): @Container {
|
|
112
|
+
return <- create Container(collectionInfo: collectionInfo)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
access(all) fun initialize(acct: auth(Storage, Capabilities) &Account, collectionInfo: CollectionInfo, nftType: Type): InitializedCaps {
|
|
116
|
+
let storagePath = self.getCollectionStoragePath(type: nftType)
|
|
117
|
+
let container <- self.createContainer(collectionInfo: collectionInfo)
|
|
118
|
+
acct.storage.save(<-container, to: storagePath)
|
|
119
|
+
let pubCap = acct.capabilities.storage.issue<&Container>(storagePath)
|
|
120
|
+
let ownerCap = acct.capabilities.storage.issue<auth(Owner) &Container>(storagePath)
|
|
121
|
+
return InitializedCaps(pubCap: pubCap, ownerCap: ownerCap)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
access(all) struct UriFile: MetadataViews.File {
|
|
125
|
+
access(self) let url: String
|
|
126
|
+
|
|
127
|
+
access(all) view fun uri(): String {
|
|
128
|
+
return self.url
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
init(_ url: String) {
|
|
132
|
+
self.url = url
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
access(all) fun getCollectionStoragePath(type: Type): StoragePath {
|
|
137
|
+
let segments = type.identifier.split(separator: ".")
|
|
138
|
+
return StoragePath(identifier: "NFTMetadataContainer_".concat(segments[2]).concat("_").concat(segments[1]))!
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
params["type"] = Type<@NFT>()
|
|
39
|
+
|
|
40
|
+
self.MetadataCap = ContractBorrower.borrowInitializer(typeIdentifier: initializeIdentifier).initialize(contractAcct: self.account, params: params).pubCap
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
"ViewResolver"
|
|
16
|
+
]).concat("\n\n")
|
|
17
|
+
.concat("access(all) contract ").concat(name).concat(": BaseCollection, ViewResolver {\n")
|
|
18
|
+
.concat(" access(all) var MetadataCap: Capability<&NFTMetadata.Container>\n")
|
|
19
|
+
.concat(" access(all) var totalSupply: UInt64\n")
|
|
20
|
+
.concat("\n\n")
|
|
21
|
+
.concat(" access(all) resource NFT: BaseNFT.NFT {\n")
|
|
22
|
+
.concat(" access(all) let id: UInt64\n")
|
|
23
|
+
.concat(" access(all) let metadataID: UInt64\n")
|
|
24
|
+
.concat("\n\n")
|
|
25
|
+
.concat(" init() {\n")
|
|
26
|
+
.concat(" ").concat(name).concat(".totalSupply = ").concat(name).concat(".totalSupply + 1\n")
|
|
27
|
+
.concat(" self.id = ").concat(name).concat(".totalSupply\n")
|
|
28
|
+
.concat(" self.metadataID = 0\n")
|
|
29
|
+
.concat(" }\n")
|
|
30
|
+
.concat(" }\n")
|
|
31
|
+
.concat(" access(all) resource NFTMinter: FlowtyDrops.Minter {\n")
|
|
32
|
+
.concat(" access(contract) fun createNextNFT(): @{NonFungibleToken.NFT} {\n")
|
|
33
|
+
.concat(" return <- create NFT()\n")
|
|
34
|
+
.concat(" }\n")
|
|
35
|
+
.concat(" }\n")
|
|
36
|
+
.concat("\n")
|
|
37
|
+
.concat(" access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {\n")
|
|
38
|
+
.concat(" return <- UniversalCollection.createCollection(nftType: Type<@NFT>())\n")
|
|
39
|
+
.concat(" }\n")
|
|
40
|
+
.concat("\n")
|
|
41
|
+
.concat(" init(params: {String: AnyStruct}, initializeIdentifier: String) {\n")
|
|
42
|
+
.concat(" self.totalSupply = 0\n")
|
|
43
|
+
.concat(" let minter <- create NFTMinter()\n")
|
|
44
|
+
.concat(" self.account.storage.save(<-minter, to: FlowtyDrops.getMinterStoragePath(type: self.getType()))\n")
|
|
45
|
+
.concat(" params[\"minterController\"] = self.account.capabilities.storage.issue<&{FlowtyDrops.Minter}>(FlowtyDrops.getMinterStoragePath(type: self.getType()))\n")
|
|
46
|
+
.concat(" params[\"type\"] = Type<@NFT>()\n")
|
|
47
|
+
.concat("\n\n")
|
|
48
|
+
.concat(" self.MetadataCap = ContractBorrower.borrowInitializer(typeIdentifier: initializeIdentifier).initialize(contractAcct: self.account, params: params).pubCap\n")
|
|
49
|
+
.concat(" }\n")
|
|
50
|
+
.concat("}\n")
|
|
51
|
+
|
|
52
|
+
acct.contracts.add(name: name, code: code.utf8, params, initializeIdentifier)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
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) let data: {String: AnyStruct}
|
|
11
|
+
access(all) let resources: @{String: AnyResource}
|
|
12
|
+
|
|
13
|
+
access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
|
|
14
|
+
return <- create Collection(nftType: self.nftType)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
init (nftType: Type) {
|
|
18
|
+
self.ownedNFTs <- {}
|
|
19
|
+
self.nftType = nftType
|
|
20
|
+
|
|
21
|
+
self.data = {}
|
|
22
|
+
self.resources <- {}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
access(all) fun createCollection(nftType: Type): @Collection {
|
|
27
|
+
return <- create Collection(nftType: nftType)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/*
|
|
2
|
+
FungibleTokenRouter forwards tokens from one account to another using
|
|
3
|
+
FungibleToken metadata views. If a token is not configured to be received,
|
|
4
|
+
any deposits will panic like they would a deposit that it attempt to a
|
|
5
|
+
non-existent receiver
|
|
6
|
+
|
|
7
|
+
https://github.com/Flowtyio/fungible-token-router
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import "FungibleToken"
|
|
11
|
+
import "FungibleTokenMetadataViews"
|
|
12
|
+
import "FlowToken"
|
|
13
|
+
|
|
14
|
+
access(all) contract FungibleTokenRouter {
|
|
15
|
+
access(all) let StoragePath: StoragePath
|
|
16
|
+
access(all) let PublicPath: PublicPath
|
|
17
|
+
|
|
18
|
+
access(all) entitlement Owner
|
|
19
|
+
|
|
20
|
+
access(all) event RouterCreated(uuid: UInt64, defaultAddress: Address)
|
|
21
|
+
access(all) event OverrideAdded(uuid: UInt64, owner: Address?, overrideAddress: Address, tokenType: String)
|
|
22
|
+
access(all) event OverrideRemoved(uuid: UInt64, owner: Address?, overrideAddress: Address?, tokenType: String)
|
|
23
|
+
access(all) event TokensRouted(tokenType: String, amount: UFix64, to: Address)
|
|
24
|
+
|
|
25
|
+
access(all) resource Router: FungibleToken.Receiver {
|
|
26
|
+
// a default address that is used for any token type that is not overridden
|
|
27
|
+
access(all) var defaultAddress: Address
|
|
28
|
+
|
|
29
|
+
// token type identifier -> destination address
|
|
30
|
+
access(all) var addressOverrides: {String: Address}
|
|
31
|
+
|
|
32
|
+
access(Owner) fun setDefaultAddress(_ addr: Address) {
|
|
33
|
+
self.defaultAddress = addr
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
access(Owner) fun addOverride(type: Type, addr: Address) {
|
|
37
|
+
emit OverrideAdded(uuid: self.uuid, owner: self.owner?.address, overrideAddress: addr, tokenType: type.identifier)
|
|
38
|
+
self.addressOverrides[type.identifier] = addr
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
access(Owner) fun removeOverride(type: Type): Address? {
|
|
42
|
+
let removedAddr = self.addressOverrides.remove(key: type.identifier)
|
|
43
|
+
emit OverrideRemoved(uuid: self.uuid, owner: self.owner?.address, overrideAddress: removedAddr, tokenType: type.identifier)
|
|
44
|
+
return removedAddr
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
access(all) fun deposit(from: @{FungibleToken.Vault}) {
|
|
48
|
+
let tokenType = from.getType()
|
|
49
|
+
let destination = self.addressOverrides[tokenType.identifier] ?? self.defaultAddress
|
|
50
|
+
|
|
51
|
+
var vaultDataOpt: FungibleTokenMetadataViews.FTVaultData? = nil
|
|
52
|
+
|
|
53
|
+
if tokenType == Type<@FlowToken.Vault>() {
|
|
54
|
+
vaultDataOpt = FungibleTokenMetadataViews.FTVaultData(
|
|
55
|
+
storagePath: /storage/flowTokenVault,
|
|
56
|
+
receiverPath: /public/flowTokenReceiver,
|
|
57
|
+
metadataPath: /public/flowTokenReceiver,
|
|
58
|
+
receiverLinkedType: Type<&FlowToken.Vault>(),
|
|
59
|
+
metadataLinkedType: Type<&FlowToken.Vault>(),
|
|
60
|
+
createEmptyVaultFunction: fun(): @{FungibleToken.Vault} {
|
|
61
|
+
return <- FlowToken.createEmptyVault(vaultType: tokenType)
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
} else if let md = from.resolveView(Type<FungibleTokenMetadataViews.FTVaultData>()) {
|
|
65
|
+
vaultDataOpt = md as! FungibleTokenMetadataViews.FTVaultData
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let vaultData = vaultDataOpt ?? panic("vault data could not be retrieved for type ".concat(tokenType.identifier))
|
|
69
|
+
let receiver = getAccount(destination).capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath)
|
|
70
|
+
assert(receiver.check(), message: "no receiver found at path: ".concat(vaultData.receiverPath.toString()))
|
|
71
|
+
|
|
72
|
+
emit TokensRouted(tokenType: tokenType.identifier, amount: from.balance, to: destination)
|
|
73
|
+
receiver.borrow()!.deposit(from: <-from)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
|
|
77
|
+
// theoretically any token is supported, it depends on the defaultAddress
|
|
78
|
+
return {}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
access(all) view fun isSupportedVaultType(type: Type): Bool {
|
|
82
|
+
// theoretically any token is supported, it depends on the defaultAddress
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
init(defaultAddress: Address) {
|
|
87
|
+
self.defaultAddress = defaultAddress
|
|
88
|
+
self.addressOverrides = {}
|
|
89
|
+
|
|
90
|
+
emit RouterCreated(uuid: self.uuid, defaultAddress: defaultAddress)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
access(all) fun createRouter(defaultAddress: Address): @Router {
|
|
95
|
+
return <- create Router(defaultAddress: defaultAddress)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
init() {
|
|
99
|
+
let identifier = "FungibleTokenRouter_".concat(self.account.address.toString())
|
|
100
|
+
self.StoragePath = StoragePath(identifier: identifier)!
|
|
101
|
+
self.PublicPath = PublicPath(identifier: identifier)!
|
|
102
|
+
}
|
|
103
|
+
}
|