@flowtyio/flow-contracts 0.1.0-beta.3 → 0.1.0-beta.30

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.
Files changed (67) hide show
  1. package/README.md +1 -1
  2. package/contracts/Burner.cdc +44 -0
  3. package/contracts/FlowStorageFees.cdc +15 -15
  4. package/contracts/FlowToken.cdc +29 -78
  5. package/contracts/FungibleToken.cdc +80 -53
  6. package/contracts/FungibleTokenMetadataViews.cdc +13 -25
  7. package/contracts/FungibleTokenSwitchboard.cdc +360 -0
  8. package/contracts/MetadataViews.cdc +107 -50
  9. package/contracts/NonFungibleToken.cdc +110 -60
  10. package/contracts/TokenForwarding.cdc +19 -11
  11. package/contracts/ViewResolver.cdc +20 -16
  12. package/contracts/capability-cache/CapabilityCache.cdc +97 -0
  13. package/contracts/dapper/DapperUtilityCoin.cdc +106 -39
  14. package/contracts/dapper/FlowUtilityToken.cdc +107 -40
  15. package/contracts/dapper/TopShot.cdc +323 -259
  16. package/contracts/dapper/TopShotLocking.cdc +41 -15
  17. package/contracts/dapper/offers/DapperOffersV2.cdc +46 -43
  18. package/contracts/dapper/offers/OffersV2.cdc +40 -56
  19. package/contracts/dapper/offers/Resolver.cdc +20 -13
  20. package/contracts/emerald-city/FLOAT.cdc +259 -254
  21. package/contracts/example/ExampleNFT.cdc +18 -21
  22. package/contracts/example/ExampleToken.cdc +68 -1
  23. package/contracts/find/FindViews.cdc +357 -353
  24. package/contracts/flow-utils/AddressUtils.cdc +20 -23
  25. package/contracts/flow-utils/ArrayUtils.cdc +10 -11
  26. package/contracts/flow-utils/ScopedFTProviders.cdc +27 -19
  27. package/contracts/flow-utils/ScopedNFTProviders.cdc +31 -26
  28. package/contracts/flow-utils/StringUtils.cdc +24 -37
  29. package/contracts/flowty-drops/ContractManager.cdc +47 -0
  30. package/contracts/flowty-drops/DropFactory.cdc +75 -0
  31. package/contracts/flowty-drops/DropTypes.cdc +278 -0
  32. package/contracts/flowty-drops/FlowtyAddressVerifiers.cdc +64 -0
  33. package/contracts/flowty-drops/FlowtyDrops.cdc +431 -0
  34. package/contracts/flowty-drops/FlowtyPricers.cdc +48 -0
  35. package/contracts/flowty-drops/FlowtySwitchers.cdc +113 -0
  36. package/contracts/flowty-drops/initializers/ContractBorrower.cdc +14 -0
  37. package/contracts/flowty-drops/initializers/ContractInitializer.cdc +7 -0
  38. package/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc +58 -0
  39. package/contracts/flowty-drops/nft/BaseCollection.cdc +97 -0
  40. package/contracts/flowty-drops/nft/BaseNFT.cdc +107 -0
  41. package/contracts/flowty-drops/nft/ContractFactory.cdc +13 -0
  42. package/contracts/flowty-drops/nft/ContractFactoryTemplate.cdc +48 -0
  43. package/contracts/flowty-drops/nft/NFTMetadata.cdc +119 -0
  44. package/contracts/flowty-drops/nft/OpenEditionNFT.cdc +41 -0
  45. package/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +52 -0
  46. package/contracts/flowty-drops/nft/UniversalCollection.cdc +23 -0
  47. package/contracts/fungible-token-router/FungibleTokenRouter.cdc +105 -0
  48. package/contracts/hybrid-custody/CapabilityDelegator.cdc +28 -26
  49. package/contracts/hybrid-custody/CapabilityFactory.cdc +20 -18
  50. package/contracts/hybrid-custody/CapabilityFilter.cdc +41 -24
  51. package/contracts/hybrid-custody/HybridCustody.cdc +303 -242
  52. package/contracts/hybrid-custody/factories/FTAllFactory.cdc +16 -4
  53. package/contracts/hybrid-custody/factories/FTBalanceFactory.cdc +16 -4
  54. package/contracts/hybrid-custody/factories/FTProviderFactory.cdc +17 -5
  55. package/contracts/hybrid-custody/factories/FTReceiverBalanceFactory.cdc +16 -4
  56. package/contracts/hybrid-custody/factories/FTReceiverFactory.cdc +16 -4
  57. package/contracts/hybrid-custody/factories/FTVaultFactory.cdc +46 -0
  58. package/contracts/hybrid-custody/factories/NFTCollectionFactory.cdc +45 -0
  59. package/contracts/hybrid-custody/factories/NFTCollectionPublicFactory.cdc +16 -4
  60. package/contracts/hybrid-custody/factories/NFTProviderAndCollectionFactory.cdc +22 -0
  61. package/contracts/hybrid-custody/factories/NFTProviderFactory.cdc +16 -4
  62. package/contracts/lost-and-found/LostAndFound.cdc +29 -25
  63. package/contracts/nft-catalog/NFTCatalog.cdc +60 -64
  64. package/contracts/nft-catalog/NFTCatalogAdmin.cdc +28 -27
  65. package/flow.json +189 -5
  66. package/package.json +1 -1
  67. package/contracts/hybrid-custody/factories/NFTProviderAndCollectionPublicFactory.cdc +0 -10
@@ -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
+ }
@@ -0,0 +1,105 @@
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")
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
+ panic("Could not find FungibleTokenMetadataViews.FTVaultData on depositing tokens")
76
+ }
77
+
78
+ access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
79
+ // theoretically any token is supported, it depends on the defaultAddress
80
+ return {}
81
+ }
82
+
83
+ access(all) view fun isSupportedVaultType(type: Type): Bool {
84
+ // theoretically any token is supported, it depends on the defaultAddress
85
+ return true
86
+ }
87
+
88
+ init(defaultAddress: Address) {
89
+ self.defaultAddress = defaultAddress
90
+ self.addressOverrides = {}
91
+
92
+ emit RouterCreated(uuid: self.uuid, defaultAddress: defaultAddress)
93
+ }
94
+ }
95
+
96
+ access(all) fun createRouter(defaultAddress: Address): @Router {
97
+ return <- create Router(defaultAddress: defaultAddress)
98
+ }
99
+
100
+ init() {
101
+ let identifier = "FungibleTokenRouter_".concat(self.account.address.toString())
102
+ self.StoragePath = StoragePath(identifier: identifier)!
103
+ self.PublicPath = PublicPath(identifier: identifier)!
104
+ }
105
+ }