@flowtyio/flow-contracts 0.1.5 → 0.1.7

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.
@@ -1,10 +1,8 @@
1
1
  import "FungibleToken"
2
- import "FungibleTokenMetadataViews"
3
2
  import "MetadataViews"
3
+ import "FungibleTokenMetadataViews"
4
4
 
5
-
6
- // THIS CONTRACT IS FOR TESTING PURPOSES ONLY!
7
- access(all) contract ExampleToken {
5
+ access(all) contract ExampleToken: FungibleToken {
8
6
 
9
7
  /// Total supply of ExampleTokens in existence
10
8
  access(all) var totalSupply: UFix64
@@ -66,32 +64,36 @@ access(all) contract ExampleToken {
66
64
  self.balance = balance
67
65
  }
68
66
 
69
- access(all) view fun getBalance(): UFix64 {
70
- return self.balance
71
- }
72
-
73
- access(all) view fun getDefaultStoragePath(): StoragePath? {
74
- return /storage/exampleTokenVault
67
+ access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
68
+ return amount <= self.balance
75
69
  }
76
70
 
77
- access(all) view fun getDefaultPublicPath(): PublicPath? {
78
- return /public/exampleTokenPublic
71
+ access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
72
+ return {self.getType(): true}
79
73
  }
80
74
 
81
- access(all) view fun getDefaultReceiverPath(): PublicPath? {
82
- return /public/exampleTokenPublic
75
+ access(all) view fun isSupportedVaultType(type: Type): Bool {
76
+ if (type == self.getType()) { return true } else { return false }
83
77
  }
84
78
 
85
- access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
86
- return self.balance >= amount
79
+ access(all) fun createEmptyVault(): @{FungibleToken.Vault} {
80
+ return <-create Vault(balance: 0.0)
87
81
  }
88
82
 
89
- /// Same as getViews above, but on a specific NFT instead of a contract
90
- access(all) view fun getViews(): [Type] {
83
+ /// Get all the Metadata Views implemented by ExampleToken
84
+ ///
85
+ /// @return An array of Types defining the implemented views. This value will be used by
86
+ /// developers to know which parameter to pass to the resolveView() method.
87
+ ///
88
+ access(all) view fun getViews(): [Type]{
91
89
  return ExampleToken.getContractViews(resourceType: nil)
92
90
  }
93
91
 
94
- /// Same as resolveView above, but on a specific NFT instead of a contract
92
+ /// Get a Metadata View from ExampleToken
93
+ ///
94
+ /// @param view: The Type of the desired view.
95
+ /// @return A structure representing the requested view.
96
+ ///
95
97
  access(all) fun resolveView(_ view: Type): AnyStruct? {
96
98
  return ExampleToken.resolveContractView(resourceType: nil, viewType: view)
97
99
  }
@@ -112,16 +114,6 @@ access(all) contract ExampleToken {
112
114
  return <-create Vault(balance: amount)
113
115
  }
114
116
 
115
- access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
116
- return {
117
- Type<@ExampleToken.Vault>(): true
118
- }
119
- }
120
-
121
- access(all) view fun isSupportedVaultType(type: Type): Bool {
122
- return type == Type<@ExampleToken.Vault>()
123
- }
124
-
125
117
  /// deposit
126
118
  ///
127
119
  /// Function that takes a Vault object as an argument and adds
@@ -139,8 +131,8 @@ access(all) contract ExampleToken {
139
131
  destroy vault
140
132
  }
141
133
 
142
- access(all) fun createEmptyVault(): @Vault {
143
- return <- ExampleToken.createEmptyVault()
134
+ access(contract) fun burnCallback() {
135
+ ExampleToken.totalSupply = ExampleToken.totalSupply - self.balance
144
136
  }
145
137
  }
146
138
 
@@ -151,7 +143,7 @@ access(all) contract ExampleToken {
151
143
  /// and store the returned Vault in their storage in order to allow their
152
144
  /// account to be able to receive deposits of this token type.
153
145
  ///
154
- access(all) fun createEmptyVault(): @Vault {
146
+ access(all) fun createEmptyVault(vaultType: Type): @Vault {
155
147
  return <-create Vault(balance: 0.0)
156
148
  }
157
149
 
@@ -227,6 +219,7 @@ access(all) contract ExampleToken {
227
219
  }
228
220
  }
229
221
 
222
+ /// Gets a list of the metadata views that this contract supports
230
223
  access(all) view fun getContractViews(resourceType: Type?): [Type] {
231
224
  return [Type<FungibleTokenMetadataViews.FTView>(),
232
225
  Type<FungibleTokenMetadataViews.FTDisplay>(),
@@ -234,6 +227,21 @@ access(all) contract ExampleToken {
234
227
  Type<FungibleTokenMetadataViews.TotalSupply>()]
235
228
  }
236
229
 
230
+ access(all) fun mintTokens(amount: UFix64): @{FungibleToken.Vault} {
231
+ let admin = self.account.storage.borrow<&Administrator>(from: /storage/exampleTokenAdmin)!
232
+ let minter <- admin.createNewMinter(allowedAmount: amount)
233
+
234
+ let tokens <- minter.mintTokens(amount: amount)
235
+ destroy minter
236
+
237
+ return <- tokens
238
+ }
239
+
240
+ /// Get a Metadata View from ExampleToken
241
+ ///
242
+ /// @param view: The Type of the desired view.
243
+ /// @return A structure representing the requested view.
244
+ ///
237
245
  access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
238
246
  switch viewType {
239
247
  case Type<FungibleTokenMetadataViews.FTView>():
@@ -244,15 +252,15 @@ access(all) contract ExampleToken {
244
252
  case Type<FungibleTokenMetadataViews.FTDisplay>():
245
253
  let media = MetadataViews.Media(
246
254
  file: MetadataViews.HTTPFile(
247
- url: "https://example.com"
255
+ url: "example.com"
248
256
  ),
249
257
  mediaType: "image/svg+xml"
250
258
  )
251
259
  let medias = MetadataViews.Medias([media])
252
260
  return FungibleTokenMetadataViews.FTDisplay(
253
- name: "Example Token",
261
+ name: "EXAMPLE Token",
254
262
  symbol: "EXAMPLE",
255
- description: "",
263
+ description: "This is an example token",
256
264
  externalURL: MetadataViews.ExternalURL("https://flow.com"),
257
265
  logos: medias,
258
266
  socials: {
@@ -286,11 +294,10 @@ access(all) contract ExampleToken {
286
294
  let vault <- create Vault(balance: self.totalSupply)
287
295
  self.account.storage.save(<-vault, to: /storage/exampleTokenVault)
288
296
 
289
- // Create a public capability to the stored Vault that only exposes
290
- // the `deposit` method through the `Receiver` interface
291
- //
292
- let publicCap = self.account.capabilities.storage.issue<&ExampleToken.Vault>(/storage/exampleTokenVault)
293
- self.account.capabilities.publish(publicCap, at: /public/exampleTokenPublic)
297
+ let cap = self.account.capabilities.storage.issue<&{FungibleToken.Vault}>(/storage/exampleTokenVault)
298
+ self.account.capabilities.publish(cap, at: /public/exampleTokenReceiver)
299
+ self.account.capabilities.publish(cap, at: /public/exampleTokenBalance)
300
+
294
301
 
295
302
  let admin <- create Administrator()
296
303
  self.account.storage.save(<-admin, to: /storage/exampleTokenAdmin)
@@ -1,6 +1,13 @@
1
1
  import "FlowToken"
2
2
  import "FungibleToken"
3
3
  import "FungibleTokenRouter"
4
+ import "HybridCustody"
5
+ import "MetadataViews"
6
+ import "ViewResolver"
7
+ import "AddressUtils"
8
+ import "CapabilityFactory"
9
+ import "CapabilityFilter"
10
+ import "FungibleTokenMetadataViews"
4
11
 
5
12
  access(all) contract ContractManager {
6
13
  access(all) let StoragePath: StoragePath
@@ -58,6 +65,7 @@ access(all) contract ContractManager {
58
65
  )
59
66
  }
60
67
 
68
+ self.configureHybridCustody(acct: acct)
61
69
  emit ManagerSaved(uuid: self.uuid, contractAddress: self.acct.address, ownerAddress: self.owner!.address)
62
70
  }
63
71
 
@@ -72,6 +80,9 @@ access(all) contract ContractManager {
72
80
 
73
81
  acct.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-tokens)
74
82
 
83
+ // setup a provider capability so that tokens are accessible via hybrid custody
84
+ acct.capabilities.storage.issue<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>(/storage/flowTokenVault)
85
+
75
86
  let router <- FungibleTokenRouter.createRouter(defaultAddress: defaultRouterAddress)
76
87
  acct.storage.save(<-router, to: FungibleTokenRouter.StoragePath)
77
88
 
@@ -84,6 +95,121 @@ access(all) contract ContractManager {
84
95
  self.data = {}
85
96
  self.resources <- {}
86
97
  }
98
+
99
+ access(self) fun configureHybridCustody(acct: auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account) {
100
+ if acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) == nil {
101
+ let ownedAccount <- HybridCustody.createOwnedAccount(acct: self.acct)
102
+ acct.storage.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath)
103
+ }
104
+
105
+ let owned = acct.storage.borrow<auth(HybridCustody.Owner) &HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath)
106
+ ?? panic("owned account not found")
107
+
108
+ let thumbnail = MetadataViews.HTTPFile(url: "https://avatars.flowty.io/6.x/thumbs/png?seed=".concat(self.acct.address.toString()))
109
+ let display = MetadataViews.Display(name: "Creator Hub", description: "Created by the Flowty Creator Hub", thumbnail: thumbnail)
110
+ owned.setDisplay(display)
111
+
112
+ if !acct.capabilities.get<&{HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountPublicPath).check() {
113
+ acct.capabilities.unpublish(HybridCustody.OwnedAccountPublicPath)
114
+ acct.capabilities.storage.issue<&{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath)
115
+ acct.capabilities.publish(
116
+ acct.capabilities.storage.issue<&{HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath),
117
+ at: HybridCustody.OwnedAccountPublicPath
118
+ )
119
+ }
120
+
121
+ // make sure that only the owner of this resource is a valid parent
122
+ let parents = owned.getParentAddresses()
123
+ let owner = self.owner!.address
124
+ var foundOwner = false
125
+ for parent in parents {
126
+ if parent == owner {
127
+ foundOwner = true
128
+ continue
129
+ }
130
+
131
+ // found a parent that should not be present
132
+ owned.removeParent(parent: parent)
133
+ }
134
+
135
+ if foundOwner {
136
+ return
137
+ }
138
+
139
+ // Flow maintains a set of pre-configured filter and factory resources that we will use:
140
+ // https://github.com/onflow/hybrid-custody?tab=readme-ov-file#hosted-capabilityfactory--capabilityfilter-implementations
141
+ var factoryAddress = ContractManager.account.address
142
+ var filterAddress = ContractManager.account.address
143
+ if let network = AddressUtils.getNetworkFromAddress(ContractManager.account.address) {
144
+ switch network {
145
+ case "TESTNET":
146
+ factoryAddress = Address(0x1b7fa5972fcb8af5)
147
+ filterAddress = Address(0xe2664be06bb0fe62)
148
+ break
149
+ case "MAINNET":
150
+ factoryAddress = Address(0x071d382668250606)
151
+ filterAddress = Address(0x78e93a79b05d0d7d)
152
+ break
153
+ }
154
+ }
155
+
156
+ owned.publishToParent(
157
+ parentAddress: owner,
158
+ factory: getAccount(factoryAddress!).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath),
159
+ filter: getAccount(filterAddress!).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath)
160
+ )
161
+ }
162
+
163
+ // Configure a given fungible token vault so that it can be received by this contract account
164
+ access(Manage) fun configureVault(vaultType: Type) {
165
+ pre {
166
+ vaultType.isSubtype(of: Type<@{FungibleToken.Vault}>()): "vault must be a fungible token"
167
+ }
168
+
169
+ let address = AddressUtils.parseAddress(vaultType)!
170
+ let name = vaultType.identifier.split(separator: ".")[2]
171
+
172
+ let ftContract = getAccount(address).contracts.borrow<&{FungibleToken}>(name: name)
173
+ ?? panic("vault contract does not implement FungibleToken")
174
+ let data = ftContract.resolveContractView(resourceType: vaultType, viewType: Type<FungibleTokenMetadataViews.FTVaultData>())! as! FungibleTokenMetadataViews.FTVaultData
175
+
176
+ let acct = self.acct.borrow()!
177
+ if acct.storage.type(at: data.storagePath) == nil {
178
+ acct.storage.save(<- ftContract.createEmptyVault(vaultType: vaultType), to: data.storagePath)
179
+ }
180
+
181
+ if !acct.capabilities.get<&{FungibleToken.Receiver}>(data.receiverPath).check() {
182
+ acct.capabilities.unpublish(data.receiverPath)
183
+ acct.capabilities.publish(
184
+ acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(data.storagePath),
185
+ at: data.receiverPath
186
+ )
187
+ }
188
+
189
+ if !acct.capabilities.get<&{FungibleToken.Receiver}>(data.metadataPath).check() {
190
+ acct.capabilities.unpublish(data.metadataPath)
191
+ acct.capabilities.publish(
192
+ acct.capabilities.storage.issue<&{FungibleToken.Vault}>(data.storagePath),
193
+ at: data.metadataPath
194
+ )
195
+ }
196
+
197
+ // is there a valid provider capability for this vault type?
198
+ var foundProvider = false
199
+ for controller in acct.capabilities.storage.getControllers(forPath: data.storagePath) {
200
+ if controller.borrowType.isSubtype(of: Type<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>()) {
201
+ foundProvider = true
202
+ break
203
+ }
204
+ }
205
+
206
+ if foundProvider {
207
+ return
208
+ }
209
+
210
+ // we did not find a provider, issue one so that its parent account is able to access it.
211
+ acct.capabilities.storage.issue<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>(data.storagePath)
212
+ }
87
213
  }
88
214
 
89
215
  access(all) fun createManager(tokens: @FlowToken.Vault, defaultRouterAddress: Address): @Manager {
package/flow.json CHANGED
@@ -359,7 +359,7 @@
359
359
  "source": "./contracts/flowty-drops/ContractManager.cdc",
360
360
  "aliases": {
361
361
  "testing": "0x0000000000000006",
362
- "testnet": "0x5d06d5357cdf8063",
362
+ "testnet": "0xea3e1b74e99dc035",
363
363
  "emulator": "0xf8d6e0586b0a20c7"
364
364
  }
365
365
  },
@@ -367,7 +367,7 @@
367
367
  "source": "./contracts/flowty-drops/initializers/ContractInitializer.cdc",
368
368
  "aliases": {
369
369
  "testing": "0x0000000000000006",
370
- "testnet": "0x5d06d5357cdf8063",
370
+ "testnet": "0xea3e1b74e99dc035",
371
371
  "emulator": "0xf8d6e0586b0a20c7"
372
372
  }
373
373
  },
@@ -375,7 +375,7 @@
375
375
  "source": "./contracts/flowty-drops/initializers/ContractBorrower.cdc",
376
376
  "aliases": {
377
377
  "testing": "0x0000000000000006",
378
- "testnet": "0x5d06d5357cdf8063",
378
+ "testnet": "0xea3e1b74e99dc035",
379
379
  "emulator": "0xf8d6e0586b0a20c7"
380
380
  }
381
381
  },
@@ -383,7 +383,7 @@
383
383
  "source": "./contracts/flowty-drops/initializers/OpenEditionInitializer.cdc",
384
384
  "aliases": {
385
385
  "testing": "0x0000000000000006",
386
- "testnet": "0x5d06d5357cdf8063",
386
+ "testnet": "0xea3e1b74e99dc035",
387
387
  "emulator": "0xf8d6e0586b0a20c7"
388
388
  }
389
389
  },
@@ -391,7 +391,7 @@
391
391
  "source": "./contracts/flowty-drops/nft/BaseCollection.cdc",
392
392
  "aliases": {
393
393
  "testing": "0x0000000000000006",
394
- "testnet": "0x5d06d5357cdf8063",
394
+ "testnet": "0xea3e1b74e99dc035",
395
395
  "emulator": "0xf8d6e0586b0a20c7"
396
396
  }
397
397
  },
@@ -399,7 +399,7 @@
399
399
  "source": "./contracts/flowty-drops/nft/ContractFactoryTemplate.cdc",
400
400
  "aliases": {
401
401
  "testing": "0x0000000000000006",
402
- "testnet": "0x5d06d5357cdf8063",
402
+ "testnet": "0xea3e1b74e99dc035",
403
403
  "emulator": "0xf8d6e0586b0a20c7"
404
404
  }
405
405
  },
@@ -407,7 +407,7 @@
407
407
  "source": "./contracts/flowty-drops/nft/ContractFactory.cdc",
408
408
  "aliases": {
409
409
  "testing": "0x0000000000000006",
410
- "testnet": "0x5d06d5357cdf8063",
410
+ "testnet": "0xea3e1b74e99dc035",
411
411
  "emulator": "0xf8d6e0586b0a20c7"
412
412
  }
413
413
  },
@@ -415,7 +415,7 @@
415
415
  "source": "./contracts/flowty-drops/nft/OpenEditionTemplate.cdc",
416
416
  "aliases": {
417
417
  "testing": "0x0000000000000006",
418
- "testnet": "0x5d06d5357cdf8063",
418
+ "testnet": "0xea3e1b74e99dc035",
419
419
  "emulator": "0xf8d6e0586b0a20c7"
420
420
  }
421
421
  },
@@ -423,7 +423,7 @@
423
423
  "source": "./contracts/flowty-drops/nft/UniversalCollection.cdc",
424
424
  "aliases": {
425
425
  "testing": "0x0000000000000006",
426
- "testnet": "0x5d06d5357cdf8063",
426
+ "testnet": "0xea3e1b74e99dc035",
427
427
  "emulator": "0xf8d6e0586b0a20c7"
428
428
  }
429
429
  },
@@ -431,7 +431,7 @@
431
431
  "source": "./contracts/flowty-drops/nft/BaseNFT.cdc",
432
432
  "aliases": {
433
433
  "testing": "0x0000000000000006",
434
- "testnet": "0x5d06d5357cdf8063",
434
+ "testnet": "0xea3e1b74e99dc035",
435
435
  "emulator": "0xf8d6e0586b0a20c7"
436
436
  }
437
437
  },
@@ -439,7 +439,7 @@
439
439
  "source": "./contracts/flowty-drops/nft/NFTMetadata.cdc",
440
440
  "aliases": {
441
441
  "testing": "0x0000000000000006",
442
- "testnet": "0x5d06d5357cdf8063",
442
+ "testnet": "0xea3e1b74e99dc035",
443
443
  "emulator": "0xf8d6e0586b0a20c7"
444
444
  }
445
445
  },
@@ -447,7 +447,7 @@
447
447
  "source": "./contracts/flowty-drops/FlowtyDrops.cdc",
448
448
  "aliases": {
449
449
  "testing": "0x0000000000000006",
450
- "testnet": "0x5d06d5357cdf8063",
450
+ "testnet": "0xea3e1b74e99dc035",
451
451
  "emulator": "0xf8d6e0586b0a20c7"
452
452
  }
453
453
  },
@@ -455,7 +455,7 @@
455
455
  "source": "./contracts/flowty-drops/DropFactory.cdc",
456
456
  "aliases": {
457
457
  "testing": "0x0000000000000006",
458
- "testnet": "0x5d06d5357cdf8063",
458
+ "testnet": "0xea3e1b74e99dc035",
459
459
  "emulator": "0xf8d6e0586b0a20c7"
460
460
  }
461
461
  },
@@ -463,7 +463,7 @@
463
463
  "source": "./contracts/flowty-drops/FlowtyActiveCheckers.cdc",
464
464
  "aliases": {
465
465
  "testing": "0x0000000000000006",
466
- "testnet": "0x5d06d5357cdf8063",
466
+ "testnet": "0xea3e1b74e99dc035",
467
467
  "emulator": "0xf8d6e0586b0a20c7"
468
468
  }
469
469
  },
@@ -471,7 +471,7 @@
471
471
  "source": "./contracts/flowty-drops/FlowtyPricers.cdc",
472
472
  "aliases": {
473
473
  "testing": "0x0000000000000006",
474
- "testnet": "0x5d06d5357cdf8063",
474
+ "testnet": "0xea3e1b74e99dc035",
475
475
  "emulator": "0xf8d6e0586b0a20c7"
476
476
  }
477
477
  },
@@ -479,7 +479,7 @@
479
479
  "source": "./contracts/flowty-drops/FlowtyAddressVerifiers.cdc",
480
480
  "aliases": {
481
481
  "testing": "0x0000000000000006",
482
- "testnet": "0x5d06d5357cdf8063",
482
+ "testnet": "0xea3e1b74e99dc035",
483
483
  "emulator": "0xf8d6e0586b0a20c7"
484
484
  }
485
485
  },
@@ -487,7 +487,7 @@
487
487
  "source": "./contracts/flowty-drops/DropTypes.cdc",
488
488
  "aliases": {
489
489
  "testing": "0x0000000000000006",
490
- "testnet": "0xa8d83f18fd8ba80a",
490
+ "testnet": "0xfc6a7e477099ca54",
491
491
  "emulator": "0xf8d6e0586b0a20c7"
492
492
  }
493
493
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowtyio/flow-contracts",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "main": "index.json",
5
5
  "description": "An NPM package for common flow contracts",
6
6
  "author": "flowtyio",