@flowtyio/flow-contracts 0.1.0-beta.9 → 0.1.1

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 (64) hide show
  1. package/README.md +1 -1
  2. package/contracts/FungibleTokenSwitchboard.cdc +360 -0
  3. package/contracts/MetadataViews.cdc +79 -6
  4. package/contracts/NonFungibleToken.cdc +17 -10
  5. package/contracts/capability-cache/CapabilityCache.cdc +97 -0
  6. package/contracts/dapper/TopShot.cdc +323 -259
  7. package/contracts/dapper/TopShotLocking.cdc +41 -15
  8. package/contracts/dapper/offers/DapperOffersV2.cdc +46 -43
  9. package/contracts/dapper/offers/OffersV2.cdc +40 -56
  10. package/contracts/dapper/offers/Resolver.cdc +20 -13
  11. package/contracts/emerald-city/FLOAT.cdc +259 -254
  12. package/contracts/evm/CrossVMNFT.cdc +50 -0
  13. package/contracts/evm/EVM.cdc +851 -0
  14. package/contracts/evm/FlowEVMBridgeConfig.cdc +454 -0
  15. package/contracts/evm/FlowEVMBridgeHandlerInterfaces.cdc +163 -0
  16. package/contracts/evm/FlowEVMBridgeHandlers.cdc +230 -0
  17. package/contracts/evm/FlowEVMBridgeUtils.cdc +1303 -0
  18. package/contracts/evm/IBridgePermissions.cdc +19 -0
  19. package/contracts/evm/ICrossVM.cdc +12 -0
  20. package/contracts/evm/ICrossVMAsset.cdc +19 -0
  21. package/contracts/evm/Serialize.cdc +141 -0
  22. package/contracts/evm/SerializeMetadata.cdc +221 -0
  23. package/contracts/example/ExampleNFT.cdc +2 -2
  24. package/contracts/find/FindViews.cdc +357 -353
  25. package/contracts/flow-utils/ScopedFTProviders.cdc +5 -2
  26. package/contracts/flow-utils/ScopedNFTProviders.cdc +6 -2
  27. package/contracts/flowty-drops/ContractManager.cdc +73 -0
  28. package/contracts/flowty-drops/DropFactory.cdc +75 -0
  29. package/contracts/flowty-drops/DropTypes.cdc +282 -0
  30. package/contracts/flowty-drops/FlowtyActiveCheckers.cdc +113 -0
  31. package/contracts/flowty-drops/FlowtyAddressVerifiers.cdc +64 -0
  32. package/contracts/flowty-drops/FlowtyDrops.cdc +461 -0
  33. package/contracts/flowty-drops/FlowtyPricers.cdc +48 -0
  34. package/contracts/flowty-drops/initializers/ContractBorrower.cdc +14 -0
  35. package/contracts/flowty-drops/initializers/ContractInitializer.cdc +7 -0
  36. package/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc +57 -0
  37. package/contracts/flowty-drops/nft/BaseCollection.cdc +97 -0
  38. package/contracts/flowty-drops/nft/BaseNFT.cdc +107 -0
  39. package/contracts/flowty-drops/nft/ContractFactory.cdc +13 -0
  40. package/contracts/flowty-drops/nft/ContractFactoryTemplate.cdc +48 -0
  41. package/contracts/flowty-drops/nft/NFTMetadata.cdc +140 -0
  42. package/contracts/flowty-drops/nft/OpenEditionNFT.cdc +42 -0
  43. package/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +54 -0
  44. package/contracts/flowty-drops/nft/UniversalCollection.cdc +29 -0
  45. package/contracts/fungible-token-router/FungibleTokenRouter.cdc +103 -0
  46. package/contracts/hybrid-custody/CapabilityDelegator.cdc +28 -26
  47. package/contracts/hybrid-custody/CapabilityFactory.cdc +20 -18
  48. package/contracts/hybrid-custody/CapabilityFilter.cdc +41 -24
  49. package/contracts/hybrid-custody/HybridCustody.cdc +303 -242
  50. package/contracts/hybrid-custody/factories/FTAllFactory.cdc +16 -4
  51. package/contracts/hybrid-custody/factories/FTBalanceFactory.cdc +16 -4
  52. package/contracts/hybrid-custody/factories/FTProviderFactory.cdc +17 -5
  53. package/contracts/hybrid-custody/factories/FTReceiverBalanceFactory.cdc +16 -4
  54. package/contracts/hybrid-custody/factories/FTReceiverFactory.cdc +16 -4
  55. package/contracts/hybrid-custody/factories/FTVaultFactory.cdc +46 -0
  56. package/contracts/hybrid-custody/factories/NFTCollectionFactory.cdc +45 -0
  57. package/contracts/hybrid-custody/factories/NFTCollectionPublicFactory.cdc +16 -4
  58. package/contracts/hybrid-custody/factories/NFTProviderAndCollectionFactory.cdc +22 -0
  59. package/contracts/hybrid-custody/factories/NFTProviderFactory.cdc +16 -4
  60. package/contracts/lost-and-found/LostAndFound.cdc +21 -17
  61. package/contracts/tokens/USDCFlow.cdc +232 -0
  62. package/flow.json +278 -7
  63. package/package.json +1 -1
  64. 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
+ }