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

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.
@@ -0,0 +1,44 @@
1
+ /// Burner is a contract that can facilitate the destruction of any resource on flow.
2
+ ///
3
+ /// Contributors
4
+ /// - Austin Kline - https://twitter.com/austin_flowty
5
+ /// - Deniz Edincik - https://twitter.com/bluesign
6
+ /// - Bastian Müller - https://twitter.com/turbolent
7
+ access(all) contract Burner {
8
+ /// When Crescendo (Cadence 1.0) is released, custom destructors will be removed from cadece.
9
+ /// Burnable is an interface meant to replace this lost feature, allowing anyone to add a callback
10
+ /// method to ensure they do not destroy something which is not meant to be,
11
+ /// or to add logic based on destruction such as tracking the supply of a FT Collection
12
+ ///
13
+ /// NOTE: The only way to see benefit from this interface
14
+ /// is to always use the burn method in this contract. Anyone who owns a resource can always elect **not**
15
+ /// to destroy a resource this way
16
+ access(all) resource interface Burnable {
17
+ access(contract) fun burnCallback()
18
+ }
19
+
20
+ /// burn is a global method which will destroy any resource it is given.
21
+ /// If the provided resource implements the Burnable interface,
22
+ /// it will call the burnCallback method and then destroy afterwards.
23
+ access(all) fun burn(_ r: @AnyResource) {
24
+ if let s <- r as? @{Burnable} {
25
+ s.burnCallback()
26
+ destroy s
27
+ } else if let arr <- r as? @[AnyResource] {
28
+ while arr.length > 0 {
29
+ let item <- arr.removeFirst()
30
+ self.burn(<-item)
31
+ }
32
+ destroy arr
33
+ } else if let dict <- r as? @{HashableStruct: AnyResource} {
34
+ let keys = dict.keys
35
+ while keys.length > 0 {
36
+ let item <- dict.remove(key: keys.removeFirst())!
37
+ self.burn(<-item)
38
+ }
39
+ destroy dict
40
+ } else {
41
+ destroy r
42
+ }
43
+ }
44
+ }
@@ -1,17 +1,17 @@
1
1
  /*
2
2
  * The FlowStorageFees smart contract
3
3
  *
4
- * An account's storage capacity determines up to how much storage on chain it can use.
4
+ * An account's storage capacity determines up to how much storage on chain it can use.
5
5
  * A storage capacity is calculated by multiplying the amount of reserved flow with `StorageFee.storageMegaBytesPerReservedFLOW`
6
6
  * The minimum amount of flow tokens reserved for storage capacity is `FlowStorageFees.minimumStorageReservation` this is paid during account creation, by the creator.
7
- *
8
- * At the end of all transactions, any account that had any value changed in their storage
7
+ *
8
+ * At the end of all transactions, any account that had any value changed in their storage
9
9
  * has their storage capacity checked against their storage used and their main flow token vault against the minimum reservation.
10
10
  * If any account fails this check the transaction wil fail.
11
- *
12
- * An account moving/deleting its `FlowToken.Vault` resource will result
11
+ *
12
+ * An account moving/deleting its `FlowToken.Vault` resource will result
13
13
  * in the transaction failing because the account will have no storage capacity.
14
- *
14
+ *
15
15
  */
16
16
 
17
17
  import "FungibleToken"
@@ -26,8 +26,8 @@ access(all) contract FlowStorageFees {
26
26
  access(all) event MinimumStorageReservationChanged(_ minimumStorageReservation: UFix64)
27
27
 
28
28
  // Defines how much storage capacity every account has per reserved Flow token.
29
- // definition is written per unit of flow instead of the inverse,
30
- // so there is no loss of precision calculating storage from flow,
29
+ // definition is written per unit of flow instead of the inverse,
30
+ // so there is no loss of precision calculating storage from flow,
31
31
  // but there is loss of precision when calculating flow per storage.
32
32
  access(all) var storageMegaBytesPerReservedFLOW: UFix64
33
33
 
@@ -68,7 +68,7 @@ access(all) contract FlowStorageFees {
68
68
  let acct = getAccount(accountAddress)
69
69
 
70
70
  if let balanceRef = acct.capabilities.borrow<&FlowToken.Vault>(/public/flowTokenBalance) {
71
- balance = balanceRef.getBalance()
71
+ balance = balanceRef.balance
72
72
  }
73
73
 
74
74
  return self.accountBalanceToAccountStorageCapacity(balance)
@@ -86,7 +86,7 @@ access(all) contract FlowStorageFees {
86
86
 
87
87
  // getAccountsCapacityForTransactionStorageCheck returns the storage capacity of a batch of accounts
88
88
  // This is used to check if a transaction will fail because of any account being over the storage capacity
89
- // The payer is an exception as its storage capacity is derived from its balance minus the maximum possible transaction fees
89
+ // The payer is an exception as its storage capacity is derived from its balance minus the maximum possible transaction fees
90
90
  // (transaction fees if the execution effort is at the execution efort limit, a.k.a.: computation limit, a.k.a.: gas limit)
91
91
  access(all) fun getAccountsCapacityForTransactionStorageCheck(accountAddresses: [Address], payer: Address, maxTxFees: UFix64): [UFix64] {
92
92
  let capacities: [UFix64] = []
@@ -97,13 +97,13 @@ access(all) contract FlowStorageFees {
97
97
  if let balanceRef = acct.capabilities.borrow<&FlowToken.Vault>(/public/flowTokenBalance) {
98
98
  if accountAddress == payer {
99
99
  // if the account is the payer, deduct the maximum possible transaction fees from the balance
100
- balance = balanceRef.getBalance().saturatingSubtract(maxTxFees)
100
+ balance = balanceRef.balance.saturatingSubtract(maxTxFees)
101
101
  } else {
102
- balance = balanceRef.getBalance()
102
+ balance = balanceRef.balance
103
103
  }
104
104
  }
105
105
 
106
- capacities.append(self.accountBalanceToAccountStorageCapacity(balance))
106
+ capacities.append(self.accountBalanceToAccountStorageCapacity(balance))
107
107
  }
108
108
  return capacities
109
109
  }
@@ -117,7 +117,7 @@ access(all) contract FlowStorageFees {
117
117
  return 0.0
118
118
  }
119
119
 
120
- // return balance multiplied with megabytes per flow
120
+ // return balance multiplied with megabytes per flow
121
121
  return self.flowToStorageCapacity(balance)
122
122
  }
123
123
 
@@ -157,7 +157,7 @@ access(all) contract FlowStorageFees {
157
157
  var balance = 0.0
158
158
 
159
159
  if let balanceRef = acct.capabilities.borrow<&FlowToken.Vault>(/public/flowTokenBalance) {
160
- balance = balanceRef.getBalance()
160
+ balance = balanceRef.balance
161
161
  }
162
162
 
163
163
  // get how much should be reserved for storage
@@ -1,16 +1,13 @@
1
1
  import "FungibleToken"
2
2
  import "MetadataViews"
3
3
  import "FungibleTokenMetadataViews"
4
- import "ViewResolver"
4
+ import "Burner"
5
5
 
6
- access(all) contract FlowToken: ViewResolver {
6
+ access(all) contract FlowToken: FungibleToken {
7
7
 
8
8
  // Total supply of Flow tokens in existence
9
9
  access(all) var totalSupply: UFix64
10
10
 
11
- // Event that is emitted when the contract is created
12
- access(all) event TokensInitialized(initialSupply: UFix64)
13
-
14
11
  // Event that is emitted when tokens are withdrawn from a Vault
15
12
  access(all) event TokensWithdrawn(amount: UFix64, from: Address?)
16
13
 
@@ -20,9 +17,6 @@ access(all) contract FlowToken: ViewResolver {
20
17
  // Event that is emitted when new tokens are minted
21
18
  access(all) event TokensMinted(amount: UFix64)
22
19
 
23
- // Event that is emitted when tokens are destroyed
24
- access(all) event TokensBurned(amount: UFix64)
25
-
26
20
  // Event that is emitted when a new minter resource is created
27
21
  access(all) event MinterCreated(allowedAmount: UFix64)
28
22
 
@@ -41,20 +35,24 @@ access(all) contract FlowToken: ViewResolver {
41
35
  // out of thin air. A special Minter resource needs to be defined to mint
42
36
  // new tokens.
43
37
  //
44
- access(all) resource Vault: FungibleToken.Vault, FungibleToken.Provider, FungibleToken.Receiver, ViewResolver.Resolver {
38
+ access(all) resource Vault: FungibleToken.Vault {
45
39
 
46
40
  // holds the balance of a users tokens
47
41
  access(all) var balance: UFix64
48
42
 
49
- access(all) view fun getBalance(): UFix64 {
50
- return self.balance
51
- }
52
-
53
43
  // initialize the balance at resource creation time
54
44
  init(balance: UFix64) {
55
45
  self.balance = balance
56
46
  }
57
47
 
48
+ /// Called when a fungible token is burned via the `Burner.burn()` method
49
+ access(contract) fun burnCallback() {
50
+ if self.balance > 0.0 {
51
+ FlowToken.totalSupply = FlowToken.totalSupply - self.balance
52
+ }
53
+ self.balance = 0.0
54
+ }
55
+
58
56
  /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
59
57
  access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
60
58
  return {self.getType(): true}
@@ -64,14 +62,9 @@ access(all) contract FlowToken: ViewResolver {
64
62
  if (type == self.getType()) { return true } else { return false }
65
63
  }
66
64
 
67
- /// Returns the storage path where the vault should typically be stored
68
- access(all) view fun getDefaultStoragePath(): StoragePath? {
69
- return /storage/flowTokenVault
70
- }
71
-
72
- /// Returns the public path where this vault should have a public capability
73
- access(all) view fun getDefaultPublicPath(): PublicPath? {
74
- return /public/flowTokenReceiver
65
+ /// Asks if the amount can be withdrawn from this vault
66
+ access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
67
+ return amount <= self.balance
75
68
  }
76
69
 
77
70
  // withdraw
@@ -83,7 +76,7 @@ access(all) contract FlowToken: ViewResolver {
83
76
  // created Vault to the context that called so it can be deposited
84
77
  // elsewhere.
85
78
  //
86
- access(FungibleToken.Withdrawable) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
79
+ access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
87
80
  self.balance = self.balance - amount
88
81
  emit TokensWithdrawn(amount: amount, from: self.owner?.address)
89
82
  return <-create Vault(balance: amount)
@@ -110,7 +103,7 @@ access(all) contract FlowToken: ViewResolver {
110
103
  /// developers to know which parameter to pass to the resolveView() method.
111
104
  ///
112
105
  access(all) view fun getViews(): [Type]{
113
- return FlowToken.getViews()
106
+ return FlowToken.getContractViews(resourceType: nil)
114
107
  }
115
108
 
116
109
  /// Get a Metadata View from FlowToken
@@ -119,7 +112,7 @@ access(all) contract FlowToken: ViewResolver {
119
112
  /// @return A structure representing the requested view.
120
113
  ///
121
114
  access(all) fun resolveView(_ view: Type): AnyStruct? {
122
- return FlowToken.resolveView(view)
115
+ return FlowToken.resolveContractView(resourceType: nil, viewType: view)
123
116
  }
124
117
 
125
118
  access(all) fun createEmptyVault(): @{FungibleToken.Vault} {
@@ -134,11 +127,12 @@ access(all) contract FlowToken: ViewResolver {
134
127
  // and store the returned Vault in their storage in order to allow their
135
128
  // account to be able to receive deposits of this token type.
136
129
  //
137
- access(all) fun createEmptyVault(): @FlowToken.Vault {
130
+ access(all) fun createEmptyVault(vaultType: Type): @FlowToken.Vault {
138
131
  return <-create Vault(balance: 0.0)
139
132
  }
140
133
 
141
- access(all) view fun getViews(): [Type] {
134
+ /// Gets a list of the metadata views that this contract supports
135
+ access(all) view fun getContractViews(resourceType: Type?): [Type] {
142
136
  return [Type<FungibleTokenMetadataViews.FTView>(),
143
137
  Type<FungibleTokenMetadataViews.FTDisplay>(),
144
138
  Type<FungibleTokenMetadataViews.FTVaultData>(),
@@ -150,12 +144,12 @@ access(all) contract FlowToken: ViewResolver {
150
144
  /// @param view: The Type of the desired view.
151
145
  /// @return A structure representing the requested view.
152
146
  ///
153
- access(all) fun resolveView(_ view: Type): AnyStruct? {
154
- switch view {
147
+ access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
148
+ switch viewType {
155
149
  case Type<FungibleTokenMetadataViews.FTView>():
156
150
  return FungibleTokenMetadataViews.FTView(
157
- ftDisplay: self.resolveView(Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
158
- ftVaultData: self.resolveView(Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
151
+ ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
152
+ ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
159
153
  )
160
154
  case Type<FungibleTokenMetadataViews.FTDisplay>():
161
155
  let media = MetadataViews.Media(
@@ -176,16 +170,16 @@ access(all) contract FlowToken: ViewResolver {
176
170
  }
177
171
  )
178
172
  case Type<FungibleTokenMetadataViews.FTVaultData>():
173
+ let vaultRef = FlowToken.account.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
174
+ ?? panic("Could not borrow reference to the contract's Vault!")
179
175
  return FungibleTokenMetadataViews.FTVaultData(
180
176
  storagePath: /storage/flowTokenVault,
181
177
  receiverPath: /public/flowTokenReceiver,
182
178
  metadataPath: /public/flowTokenBalance,
183
- providerPath: /private/flowTokenVault,
184
- receiverLinkedType: Type<&FlowToken.Vault>(),
185
- metadataLinkedType: Type<&FlowToken.Vault>(),
186
- providerLinkedType: Type<&FlowToken.Vault>(),
179
+ receiverLinkedType: Type<&{FungibleToken.Receiver, FungibleToken.Vault}>(),
180
+ metadataLinkedType: Type<&{FungibleToken.Balance, FungibleToken.Vault}>(),
187
181
  createEmptyVaultFunction: (fun (): @{FungibleToken.Vault} {
188
- return <-FlowToken.createEmptyVault()
182
+ return <-vaultRef.createEmptyVault()
189
183
  })
190
184
  )
191
185
  case Type<FungibleTokenMetadataViews.TotalSupply>():
@@ -203,15 +197,6 @@ access(all) contract FlowToken: ViewResolver {
203
197
  emit MinterCreated(allowedAmount: allowedAmount)
204
198
  return <-create Minter(allowedAmount: allowedAmount)
205
199
  }
206
-
207
- // createNewBurner
208
- //
209
- // Function that creates and returns a new burner resource
210
- //
211
- access(all) fun createNewBurner(): @Burner {
212
- emit BurnerCreated()
213
- return <-create Burner()
214
- }
215
200
  }
216
201
 
217
202
  // Minter
@@ -244,34 +229,6 @@ access(all) contract FlowToken: ViewResolver {
244
229
  }
245
230
  }
246
231
 
247
- // Burner
248
- //
249
- // Resource object that token admin accounts can hold to burn tokens.
250
- //
251
- access(all) resource Burner {
252
-
253
- // burnTokens
254
- //
255
- // Function that destroys a Vault instance, effectively burning the tokens.
256
- //
257
- // Note: the burned tokens are automatically subtracted from the
258
- // total supply in the Vault destructor.
259
- //
260
- access(all) fun burnTokens(from: @FlowToken.Vault) {
261
- FlowToken.burnTokens(from: <-from)
262
- }
263
- }
264
-
265
- access(all) fun burnTokens(from: @FlowToken.Vault) {
266
- let vault <- from as! @FlowToken.Vault
267
- let amount = vault.balance
268
- destroy vault
269
- if amount > 0.0 {
270
- FlowToken.totalSupply = FlowToken.totalSupply - amount
271
- emit TokensBurned(amount: amount)
272
- }
273
- }
274
-
275
232
  /// Gets the Flow Logo XML URI from storage
276
233
  access(all) fun getLogoURI(): String {
277
234
  return FlowToken.account.storage.copy<String>(from: /storage/flowTokenLogoURI) ?? ""
@@ -284,9 +241,6 @@ access(all) contract FlowToken: ViewResolver {
284
241
  //
285
242
  let vault <- create Vault(balance: self.totalSupply)
286
243
 
287
- // Example of how to resolve a metadata view for a Vault
288
- let ftView = vault.resolveView(Type<FungibleTokenMetadataViews.FTView>())
289
-
290
244
  adminAccount.storage.save(<-vault, to: /storage/flowTokenVault)
291
245
 
292
246
  // Create a public capability to the stored Vault that only exposes
@@ -304,8 +258,5 @@ access(all) contract FlowToken: ViewResolver {
304
258
  let admin <- create Administrator()
305
259
  adminAccount.storage.save(<-admin, to: /storage/flowTokenAdmin)
306
260
 
307
- // Emit an event that shows that the contract was initialized
308
- emit TokensInitialized(initialSupply: self.totalSupply)
309
-
310
261
  }
311
262
  }
@@ -32,6 +32,7 @@ to the Provider interface.
32
32
  */
33
33
 
34
34
  import "ViewResolver"
35
+ import "Burner"
35
36
 
36
37
  /// FungibleToken
37
38
  ///
@@ -40,16 +41,28 @@ import "ViewResolver"
40
41
  /// utility methods that many projects will still want to have on their contracts,
41
42
  /// but they are by no means required. all that is required is that the token
42
43
  /// implements the `Vault` interface
43
- access(all) contract FungibleToken {
44
+ access(all) contract interface FungibleToken: ViewResolver {
44
45
 
45
46
  // An entitlement for allowing the withdrawal of tokens from a Vault
46
- access(all) entitlement Withdrawable
47
+ access(all) entitlement Withdraw
47
48
 
48
49
  /// The event that is emitted when tokens are withdrawn from a Vault
49
- access(all) event Withdraw(amount: UFix64, from: Address?, type: String)
50
+ access(all) event Withdrawn(type: String, amount: UFix64, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64)
50
51
 
51
52
  /// The event that is emitted when tokens are deposited to a Vault
52
- access(all) event Deposit(amount: UFix64, to: Address?, type: String)
53
+ access(all) event Deposited(type: String, amount: UFix64, to: Address?, toUUID: UInt64, depositedUUID: UInt64)
54
+
55
+ /// Event that is emitted when the global burn method is called with a non-zero balance
56
+ access(all) event Burned(type: String, amount: UFix64, fromUUID: UInt64)
57
+
58
+ /// Balance
59
+ ///
60
+ /// The interface that provides standard functions\
61
+ /// for getting balance information
62
+ ///
63
+ access(all) resource interface Balance {
64
+ access(all) var balance: UFix64
65
+ }
53
66
 
54
67
  /// Provider
55
68
  ///
@@ -62,27 +75,29 @@ access(all) contract FungibleToken {
62
75
  ///
63
76
  access(all) resource interface Provider {
64
77
 
65
- /// withdraw subtracts tokens from the owner's Vault
78
+ /// Function to ask a provider if a specific amount of tokens
79
+ /// is available to be withdrawn
80
+ /// This could be useful to avoid panicing when calling withdraw
81
+ /// when the balance is unknown
82
+ /// Additionally, if the provider is pulling from multiple vaults
83
+ /// it only needs to check some of the vaults until the desired amount
84
+ /// is reached, potentially helping with performance.
85
+ ///
86
+ access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool
87
+
88
+ /// withdraw subtracts tokens from the implementing resource
66
89
  /// and returns a Vault with the removed tokens.
67
90
  ///
68
- /// The function's access level is public, but this is not a problem
69
- /// because only the owner storing the resource in their account
70
- /// can initially call this function.
71
- ///
72
- /// The owner may grant other accounts access by creating a private
73
- /// capability that allows specific other users to access
74
- /// the provider resource through a reference.
75
- ///
76
- /// The owner may also grant all accounts access by creating a public
77
- /// capability that allows all users to access the provider
78
- /// resource through a reference.
91
+ /// The function's access level is `access(Withdraw)`
92
+ /// So in order to access it, one would either need the object itself
93
+ /// or an entitled reference with `Withdraw`.
79
94
  ///
80
- access(Withdrawable) fun withdraw(amount: UFix64): @{Vault} {
95
+ access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
81
96
  post {
82
97
  // `result` refers to the return value
83
- result.getBalance() == amount:
98
+ result.balance == amount:
84
99
  "Withdrawal amount must be the same as the balance of the withdrawn Vault"
85
- emit Withdraw(amount: amount, from: self.owner?.address, type: self.getType().identifier)
100
+ emit Withdrawn(type: self.getType().identifier, amount: amount, from: self.owner?.address, fromUUID: self.uuid, withdrawnUUID: result.uuid)
86
101
  }
87
102
  }
88
103
  }
@@ -104,15 +119,11 @@ access(all) contract FungibleToken {
104
119
  access(all) fun deposit(from: @{Vault})
105
120
 
106
121
  /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
107
- access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
108
- pre { true: "dummy" }
109
- }
122
+ access(all) view fun getSupportedVaultTypes(): {Type: Bool}
110
123
 
111
124
  /// Returns whether or not the given type is accepted by the Receiver
112
125
  /// A vault that can accept any type should just return true by default
113
- access(all) view fun isSupportedVaultType(type: Type): Bool {
114
- pre { true: "dummy" }
115
- }
126
+ access(all) view fun isSupportedVaultType(type: Type): Bool
116
127
  }
117
128
 
118
129
  /// Vault
@@ -120,17 +131,35 @@ access(all) contract FungibleToken {
120
131
  /// Ideally, this interface would also conform to Receiver, Balance, Transferor, Provider, and Resolver
121
132
  /// but that is not supported yet
122
133
  ///
123
- access(all) resource interface Vault: Receiver, Provider, ViewResolver.Resolver {
124
-
125
- //access(all) event ResourceDestroyed(balance: UFix64 = self.getBalance())
126
-
127
- /// Get the balance of the vault
128
- access(all) view fun getBalance(): UFix64
134
+ access(all) resource interface Vault: Receiver, Provider, Balance, ViewResolver.Resolver, Burner.Burnable {
135
+
136
+ /// Field that tracks the balance of a vault
137
+ access(all) var balance: UFix64
138
+
139
+ /// Called when a fungible token is burned via the `Burner.burn()` method
140
+ /// Implementations can do any bookkeeping or emit any events
141
+ /// that should be emitted when a vault is destroyed.
142
+ /// Many implementations will want to update the token's total supply
143
+ /// to reflect that the tokens have been burned and removed from the supply.
144
+ /// Implementations also need to set the balance to zero before the end of the function
145
+ /// This is to prevent vault owners from spamming fake Burned events.
146
+ access(contract) fun burnCallback() {
147
+ pre {
148
+ emit Burned(type: self.getType().identifier, amount: self.balance, fromUUID: self.uuid)
149
+ }
150
+ post {
151
+ self.balance == 0.0: "The balance must be set to zero during the burnCallback method so that it cannot be spammed"
152
+ }
153
+ self.balance = 0.0
154
+ }
129
155
 
130
156
  /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
157
+ /// The default implementation is included here because vaults are expected
158
+ /// to only accepted their own type, so they have no need to provide an implementation
159
+ /// for this function
131
160
  access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
132
161
  // Below check is implemented to make sure that run-time type would
133
- // only get returned when the parent resource conforms with `FungibleToken.Vault`.
162
+ // only get returned when the parent resource conforms with `FungibleToken.Vault`.
134
163
  if self.getType().isSubtype(of: Type<@{FungibleToken.Vault}>()) {
135
164
  return {self.getType(): true}
136
165
  } else {
@@ -140,36 +169,25 @@ access(all) contract FungibleToken {
140
169
  }
141
170
  }
142
171
 
172
+ /// Checks if the given type is supported by this Vault
143
173
  access(all) view fun isSupportedVaultType(type: Type): Bool {
144
174
  return self.getSupportedVaultTypes()[type] ?? false
145
175
  }
146
176
 
147
- /// Returns the storage path where the vault should typically be stored
148
- access(all) view fun getDefaultStoragePath(): StoragePath?
149
-
150
- /// Returns the public path where this vault should have a public capability
151
- access(all) view fun getDefaultPublicPath(): PublicPath?
152
-
153
- /// Returns the public path where this vault's Receiver should have a public capability
154
- /// Publishing a Receiver Capability at a different path enables alternate Receiver implementations to be used
155
- /// in the same canonical namespace as the underlying Vault.
156
- access(all) view fun getDefaultReceiverPath(): PublicPath? {
157
- return nil
158
- }
159
-
160
177
  /// withdraw subtracts `amount` from the Vault's balance
161
178
  /// and returns a new Vault with the subtracted balance
162
179
  ///
163
- access(Withdrawable) fun withdraw(amount: UFix64): @{Vault} {
180
+ access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
164
181
  pre {
165
- self.getBalance() >= amount:
182
+ self.balance >= amount:
166
183
  "Amount withdrawn must be less than or equal than the balance of the Vault"
167
184
  }
168
185
  post {
186
+ result.getType() == self.getType(): "Must return the same vault type as self"
169
187
  // use the special function `before` to get the value of the `balance` field
170
188
  // at the beginning of the function execution
171
189
  //
172
- self.getBalance() == before(self.getBalance()) - amount:
190
+ self.balance == before(self.balance) - amount:
173
191
  "New Vault balance must be the difference of the previous balance and the withdrawn Vault balance"
174
192
  }
175
193
  }
@@ -180,12 +198,12 @@ access(all) contract FungibleToken {
180
198
  // Assert that the concrete type of the deposited vault is the same
181
199
  // as the vault that is accepting the deposit
182
200
  pre {
183
- from.isInstance(self.getType()):
201
+ from.isInstance(self.getType()):
184
202
  "Cannot deposit an incompatible token type"
185
- emit Deposit(amount: from.getBalance(), to: self.owner?.address, type: from.getType().identifier)
203
+ emit Deposited(type: from.getType().identifier, amount: from.balance, to: self.owner?.address, toUUID: self.uuid, depositedUUID: from.uuid)
186
204
  }
187
205
  post {
188
- self.getBalance() == before(self.getBalance()) + before(from.getBalance()):
206
+ self.balance == before(self.balance) + before(from.balance):
189
207
  "New Vault balance must be the sum of the previous balance and the deposited Vault"
190
208
  }
191
209
  }
@@ -194,8 +212,17 @@ access(all) contract FungibleToken {
194
212
  ///
195
213
  access(all) fun createEmptyVault(): @{Vault} {
196
214
  post {
197
- result.getBalance() == 0.0: "The newly created Vault must have zero balance"
215
+ result.balance == 0.0: "The newly created Vault must have zero balance"
198
216
  }
199
217
  }
200
218
  }
201
- }
219
+
220
+ /// createEmptyVault allows any user to create a new Vault that has a zero balance
221
+ ///
222
+ access(all) fun createEmptyVault(vaultType: Type): @{FungibleToken.Vault} {
223
+ post {
224
+ result.getType() == vaultType: "The returned vault does not match the desired type"
225
+ result.balance == 0.0: "The newly created Vault must have zero balance"
226
+ }
227
+ }
228
+ }
@@ -4,21 +4,21 @@ import "ViewResolver"
4
4
 
5
5
  /// This contract implements the metadata standard proposed
6
6
  /// in FLIP-1087.
7
- ///
7
+ ///
8
8
  /// Ref: https://github.com/onflow/flips/blob/main/application/20220811-fungible-tokens-metadata.md
9
- ///
9
+ ///
10
10
  /// Structs and resources can implement one or more
11
11
  /// metadata types, called views. Each view type represents
12
12
  /// a different kind of metadata.
13
13
  ///
14
14
  access(all) contract FungibleTokenMetadataViews {
15
15
 
16
- /// FTView wraps FTDisplay and FTVaultData, and is used to give a complete
17
- /// picture of a Fungible Token. Most Fungible Token contracts should
16
+ /// FTView wraps FTDisplay and FTVaultData, and is used to give a complete
17
+ /// picture of a Fungible Token. Most Fungible Token contracts should
18
18
  /// implement this view.
19
19
  ///
20
20
  access(all) struct FTView {
21
- access(all) let ftDisplay: FTDisplay?
21
+ access(all) let ftDisplay: FTDisplay?
22
22
  access(all) let ftVaultData: FTVaultData?
23
23
  view init(
24
24
  ftDisplay: FTDisplay?,
@@ -45,8 +45,8 @@ access(all) contract FungibleTokenMetadataViews {
45
45
  )
46
46
  }
47
47
 
48
- /// View to expose the information needed to showcase this FT.
49
- /// This can be used by applications to give an overview and
48
+ /// View to expose the information needed to showcase this FT.
49
+ /// This can be used by applications to give an overview and
50
50
  /// graphics of the FT.
51
51
  ///
52
52
  access(all) struct FTDisplay {
@@ -94,7 +94,7 @@ access(all) contract FungibleTokenMetadataViews {
94
94
  }
95
95
 
96
96
  /// Helper to get FTDisplay in a way that will return a typed optional.
97
- ///
97
+ ///
98
98
  /// @param viewResolver: A reference to the resolver resource
99
99
  /// @return An optional FTDisplay struct
100
100
  ///
@@ -108,7 +108,7 @@ access(all) contract FungibleTokenMetadataViews {
108
108
  }
109
109
 
110
110
  /// View to expose the information needed store and interact with a FT vault.
111
- /// This can be used by applications to setup a FT vault with proper
111
+ /// This can be used by applications to setup a FT vault with proper
112
112
  /// storage and public capabilities.
113
113
  ///
114
114
  access(all) struct FTVaultData {
@@ -121,22 +121,14 @@ access(all) contract FungibleTokenMetadataViews {
121
121
  /// Public path which must be linked to expose the balance and resolver public capabilities.
122
122
  access(all) let metadataPath: PublicPath
123
123
 
124
- /// Private path which should be linked to expose the provider capability to withdraw funds
125
- /// from the vault.
126
- access(all) let providerPath: PrivatePath
127
-
128
- /// Type that should be linked at the `receiverPath`. This is a restricted type requiring
124
+ /// Type that should be linked at the `receiverPath`. This is a restricted type requiring
129
125
  /// the `FungibleToken.Receiver` interface.
130
126
  access(all) let receiverLinkedType: Type
131
127
 
132
- /// Type that should be linked at the `receiverPath`. This is a restricted type requiring
128
+ /// Type that should be linked at the `receiverPath`. This is a restricted type requiring
133
129
  /// the `ViewResolver.Resolver` interfaces.
134
130
  access(all) let metadataLinkedType: Type
135
131
 
136
- /// Type that should be linked at the aforementioned private path. This
137
- /// is normally a restricted type with at a minimum the `FungibleToken.Provider` interface.
138
- access(all) let providerLinkedType: Type
139
-
140
132
  /// Function that allows creation of an empty FT vault that is intended
141
133
  /// to store the funds.
142
134
  access(all) let createEmptyVault: fun(): @{FungibleToken.Vault}
@@ -145,24 +137,19 @@ access(all) contract FungibleTokenMetadataViews {
145
137
  storagePath: StoragePath,
146
138
  receiverPath: PublicPath,
147
139
  metadataPath: PublicPath,
148
- providerPath: PrivatePath,
149
140
  receiverLinkedType: Type,
150
141
  metadataLinkedType: Type,
151
- providerLinkedType: Type,
152
142
  createEmptyVaultFunction: fun(): @{FungibleToken.Vault}
153
143
  ) {
154
144
  pre {
155
145
  receiverLinkedType.isSubtype(of: Type<&{FungibleToken.Receiver}>()): "Receiver public type must include FungibleToken.Receiver."
156
- metadataLinkedType.isSubtype(of: Type<&{ViewResolver.Resolver}>()): "Metadata public type must include ViewResolver.Resolver interfaces."
157
- providerLinkedType.isSubtype(of: Type<&{FungibleToken.Provider}>()): "Provider type must include FungibleToken.Provider interface."
146
+ metadataLinkedType.isSubtype(of: Type<&{FungibleToken.Vault}>()): "Metadata linked type must be a fungible token vault"
158
147
  }
159
148
  self.storagePath = storagePath
160
149
  self.receiverPath = receiverPath
161
150
  self.metadataPath = metadataPath
162
- self.providerPath = providerPath
163
151
  self.receiverLinkedType = receiverLinkedType
164
152
  self.metadataLinkedType = metadataLinkedType
165
- self.providerLinkedType = providerLinkedType
166
153
  self.createEmptyVault = createEmptyVaultFunction
167
154
  }
168
155
  }
@@ -190,3 +177,4 @@ access(all) contract FungibleTokenMetadataViews {
190
177
  }
191
178
  }
192
179
  }
180
+
@@ -134,7 +134,7 @@ access(all) contract MetadataViews {
134
134
  ///
135
135
  access(all) let file: {File}
136
136
 
137
- /// media-type comes on the form of type/subtype as described here
137
+ /// media-type comes on the form of type/subtype as described here
138
138
  /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
139
139
  ///
140
140
  access(all) let mediaType: String
@@ -197,7 +197,7 @@ access(all) contract MetadataViews {
197
197
  }
198
198
 
199
199
  /// View to expose a URL to this item on an external site.
200
- /// This can be used by applications like .find and Blocto to direct users
200
+ /// This can be used by applications like .find and Blocto to direct users
201
201
  /// to the original link for an NFT or a project page that describes the NFT collection.
202
202
  /// eg https://www.my-nft-project.com/overview-of-nft-collection
203
203
  ///
@@ -223,27 +223,27 @@ access(all) contract MetadataViews {
223
223
  return nil
224
224
  }
225
225
 
226
- /// View that defines the composable royalty standard that gives marketplaces a
226
+ /// View that defines the composable royalty standard that gives marketplaces a
227
227
  /// unified interface to support NFT royalties.
228
228
  ///
229
229
  access(all) struct Royalty {
230
230
 
231
231
  /// Generic FungibleToken Receiver for the beneficiary of the royalty
232
232
  /// Can get the concrete type of the receiver with receiver.getType()
233
- /// Recommendation - Users should create a new link for a FlowToken
234
- /// receiver for this using `getRoyaltyReceiverPublicPath()`, and not
235
- /// use the default FlowToken receiver. This will allow users to update
233
+ /// Recommendation - Users should create a new link for a FlowToken
234
+ /// receiver for this using `getRoyaltyReceiverPublicPath()`, and not
235
+ /// use the default FlowToken receiver. This will allow users to update
236
236
  /// the capability in the future to use a more generic capability
237
237
  access(all) let receiver: Capability<&{FungibleToken.Receiver}>
238
238
 
239
- /// Multiplier used to calculate the amount of sale value transferred to
240
- /// royalty receiver. Note - It should be between 0.0 and 1.0
241
- /// Ex - If the sale value is x and multiplier is 0.56 then the royalty
239
+ /// Multiplier used to calculate the amount of sale value transferred to
240
+ /// royalty receiver. Note - It should be between 0.0 and 1.0
241
+ /// Ex - If the sale value is x and multiplier is 0.56 then the royalty
242
242
  /// value would be 0.56 * x.
243
243
  /// Generally percentage get represented in terms of basis points
244
- /// in solidity based smart contracts while cadence offers `UFix64`
245
- /// that already supports the basis points use case because its
246
- /// operations are entirely deterministic integer operations and support
244
+ /// in solidity based smart contracts while cadence offers `UFix64`
245
+ /// that already supports the basis points use case because its
246
+ /// operations are entirely deterministic integer operations and support
247
247
  /// up to 8 points of precision.
248
248
  access(all) let cut: UFix64
249
249
 
@@ -263,7 +263,7 @@ access(all) contract MetadataViews {
263
263
  }
264
264
 
265
265
  /// Wrapper view for multiple Royalty views.
266
- /// Marketplaces can query this `Royalties` struct from NFTs
266
+ /// Marketplaces can query this `Royalties` struct from NFTs
267
267
  /// and are expected to pay royalties based on these specifications.
268
268
  ///
269
269
  access(all) struct Royalties {
@@ -353,9 +353,9 @@ access(all) contract MetadataViews {
353
353
  view init(_ traits: [Trait]) {
354
354
  self.traits = traits
355
355
  }
356
-
356
+
357
357
  /// Adds a single Trait to the Traits view
358
- ///
358
+ ///
359
359
  /// @param Trait: The trait struct to be added
360
360
  ///
361
361
  access(all) fun addTrait(_ t: Trait) {
@@ -377,8 +377,8 @@ access(all) contract MetadataViews {
377
377
  return nil
378
378
  }
379
379
 
380
- /// Helper function to easily convert a dictionary to traits. For NFT
381
- /// collections that do not need either of the optional values of a Trait,
380
+ /// Helper function to easily convert a dictionary to traits. For NFT
381
+ /// collections that do not need either of the optional values of a Trait,
382
382
  /// this method should suffice to give them an array of valid traits.
383
383
  ///
384
384
  /// @param dict: The dictionary to be converted to Traits
@@ -494,7 +494,7 @@ access(all) contract MetadataViews {
494
494
  }
495
495
 
496
496
  /// View to expose rarity information for a single rarity
497
- /// Note that a rarity needs to have either score or description but it can
497
+ /// Note that a rarity needs to have either score or description but it can
498
498
  /// have both
499
499
  ///
500
500
  access(all) struct Rarity {
@@ -534,8 +534,8 @@ access(all) contract MetadataViews {
534
534
  return nil
535
535
  }
536
536
 
537
- /// NFTView wraps all Core views along `id` and `uuid` fields, and is used
538
- /// to give a complete picture of an NFT. Most NFTs should implement this
537
+ /// NFTView wraps all Core views along `id` and `uuid` fields, and is used
538
+ /// to give a complete picture of an NFT. Most NFTs should implement this
539
539
  /// view.
540
540
  ///
541
541
  access(all) struct NFTView {
@@ -569,7 +569,7 @@ access(all) contract MetadataViews {
569
569
  }
570
570
  }
571
571
 
572
- /// Helper to get an NFT view
572
+ /// Helper to get an NFT view
573
573
  ///
574
574
  /// @param id: The NFT id
575
575
  /// @param viewResolver: A reference to the resolver resource
@@ -594,7 +594,7 @@ access(all) contract MetadataViews {
594
594
  }
595
595
 
596
596
  /// View to expose the information needed store and retrieve an NFT.
597
- /// This can be used by applications to setup a NFT collection with proper
597
+ /// This can be used by applications to setup a NFT collection with proper
598
598
  /// storage and public capabilities.
599
599
  ///
600
600
  access(all) struct NFTCollectionData {
@@ -605,24 +605,13 @@ access(all) contract MetadataViews {
605
605
  /// including standard NFT interfaces and metadataviews interfaces
606
606
  access(all) let publicPath: PublicPath
607
607
 
608
- /// Private path which should be linked to expose the provider
609
- /// capability to withdraw NFTs from the collection holding NFTs
610
- access(all) let providerPath: PrivatePath
611
-
612
- /// Public collection type that is expected to provide sufficient read-only access to standard
613
- /// functions (deposit + getIDs + borrowNFT). For new
614
- /// collections, this may be set to be equal to the type specified in `publicLinkedType`.
608
+ /// The concrete type of the collection that is exposed to the public
609
+ /// now that entitlements exist, it no longer needs to be restricted to a specific interface
615
610
  access(all) let publicCollection: Type
616
611
 
617
- /// Type that should be linked at the aforementioned public path. This is normally a
618
- /// restricted type with many interfaces. Notably the
619
- /// `NFT.Receiver`, and `ViewResolver.ResolverCollection` interfaces are required.
612
+ /// Type that should be linked at the aforementioned public path
620
613
  access(all) let publicLinkedType: Type
621
614
 
622
- /// Type that should be linked at the aforementioned private path. This is normally
623
- /// a restricted type with at a minimum the `NFT.Provider` interface
624
- access(all) let providerLinkedType: Type
625
-
626
615
  /// Function that allows creation of an empty NFT collection that is intended to store
627
616
  /// this NFT.
628
617
  access(all) let createEmptyCollection: fun(): @{NonFungibleToken.Collection}
@@ -630,22 +619,17 @@ access(all) contract MetadataViews {
630
619
  view init(
631
620
  storagePath: StoragePath,
632
621
  publicPath: PublicPath,
633
- providerPath: PrivatePath,
634
622
  publicCollection: Type,
635
623
  publicLinkedType: Type,
636
- providerLinkedType: Type,
637
624
  createEmptyCollectionFunction: fun(): @{NonFungibleToken.Collection}
638
625
  ) {
639
626
  pre {
640
- publicLinkedType.isSubtype(of: Type<&{NonFungibleToken.Receiver, ViewResolver.ResolverCollection}>()): "Public type must include NonFungibleToken.Receiver and ViewResolver.ResolverCollection interfaces."
641
- providerLinkedType.isSubtype(of: Type<&{NonFungibleToken.Provider, ViewResolver.ResolverCollection}>()): "Provider type must include NonFungibleToken.Provider and ViewResolver.ResolverCollection interface."
627
+ publicLinkedType.isSubtype(of: Type<&{NonFungibleToken.Collection}>()): "Public type must be a subtype of NonFungibleToken.Collection interface."
642
628
  }
643
629
  self.storagePath=storagePath
644
630
  self.publicPath=publicPath
645
- self.providerPath = providerPath
646
631
  self.publicCollection=publicCollection
647
632
  self.publicLinkedType=publicLinkedType
648
- self.providerLinkedType = providerLinkedType
649
633
  self.createEmptyCollection=createEmptyCollectionFunction
650
634
  }
651
635
  }
@@ -665,7 +649,7 @@ access(all) contract MetadataViews {
665
649
  }
666
650
 
667
651
  /// View to expose the information needed to showcase this NFT's
668
- /// collection. This can be used by applications to give an overview and
652
+ /// collection. This can be used by applications to give an overview and
669
653
  /// graphics of the NFT collection this NFT belongs to.
670
654
  ///
671
655
  access(all) struct NFTCollectionDisplay {
@@ -705,7 +689,7 @@ access(all) contract MetadataViews {
705
689
  }
706
690
  }
707
691
 
708
- /// Helper to get NFTCollectionDisplay in a way that will return a typed
692
+ /// Helper to get NFTCollectionDisplay in a way that will return a typed
709
693
  /// Optional
710
694
  ///
711
695
  /// @param viewResolver: A reference to the resolver resource
@@ -2,20 +2,17 @@
2
2
 
3
3
  ## The Flow Non-Fungible Token standard
4
4
 
5
- ## `NonFungibleToken` contract interface
5
+ ## `NonFungibleToken` contract
6
6
 
7
7
  The interface that all Non-Fungible Token contracts should conform to.
8
- If a user wants to deploy a new NFT contract, their contract would need
9
- to implement the NonFungibleToken interface.
8
+ If a user wants to deploy a new NFT contract, their contract should implement
9
+ The types defined here
10
10
 
11
- Their contract must follow all the rules and naming
12
- that the interface specifies.
13
-
14
- ## `NFT` resource
11
+ ## `NFT` resource interface
15
12
 
16
13
  The core resource type that represents an NFT in the smart contract.
17
14
 
18
- ## `Collection` Resource
15
+ ## `Collection` Resource interface
19
16
 
20
17
  The resource that stores a user's NFT collection.
21
18
  It includes a few functions to allow the owner to easily
@@ -26,10 +23,8 @@ move tokens in and out of the collection.
26
23
  These interfaces declare functions with some pre and post conditions
27
24
  that require the Collection to follow certain naming and behavior standards.
28
25
 
29
- They are separate because it gives the user the ability to share a reference
30
- to their Collection that only exposes the fields and functions in one or more
31
- of the interfaces. It also gives users the ability to make custom resources
32
- that implement these interfaces to do various things with the tokens.
26
+ They are separate because it gives developers the ability to define functions
27
+ that can use any type that implements these interfaces
33
28
 
34
29
  By using resources and interfaces, users of NFT smart contracts can send
35
30
  and receive tokens peer-to-peer, without having to interact with a central ledger
@@ -43,16 +38,19 @@ Collection to complete the transfer.
43
38
 
44
39
  import "ViewResolver"
45
40
 
46
- /// The main NFT contract interface. Other NFT contracts will
47
- /// import and implement this interface
41
+ /// The main NFT contract. Other NFT contracts will
42
+ /// import and implement the interfaces defined in this contract
48
43
  ///
49
- access(all) contract NonFungibleToken {
44
+ access(all) contract interface NonFungibleToken: ViewResolver {
50
45
 
51
46
  /// An entitlement for allowing the withdrawal of tokens from a Vault
52
- access(all) entitlement Withdrawable
47
+ access(all) entitlement Withdraw
53
48
 
54
49
  /// An entitlement for allowing updates and update events for an NFT
55
- access(all) entitlement Updatable
50
+ access(all) entitlement Update
51
+
52
+ /// entitlement for owner that grants Withdraw and Update
53
+ access(all) entitlement Owner
56
54
 
57
55
  /// Event that contracts should emit when the metadata of an NFT is updated
58
56
  /// It can only be emitted by calling the `emitNFTUpdated` function
@@ -60,45 +58,69 @@ access(all) contract NonFungibleToken {
60
58
  /// The entitlement prevents spammers from calling this from other users' collections
61
59
  /// because only code within a collection or that has special entitled access
62
60
  /// to the collections methods will be able to get the entitled reference
63
- ///
61
+ ///
64
62
  /// The event makes it so that third-party indexers can monitor the events
65
63
  /// and query the updated metadata from the owners' collections.
66
64
  ///
67
- access(all) event Updated(id: UInt64, uuid: UInt64, owner: Address?, type: String)
68
- access(all) view fun emitNFTUpdated(_ nftRef: auth(Updatable) &{NonFungibleToken.NFT})
65
+ access(all) event Updated(type: String, id: UInt64, uuid: UInt64, owner: Address?)
66
+ access(contract) view fun emitNFTUpdated(_ nftRef: auth(Update | Owner) &{NonFungibleToken.NFT})
69
67
  {
70
- emit Updated(id: nftRef.getID(), uuid: nftRef.uuid, owner: nftRef.owner?.address, type: nftRef.getType().identifier)
68
+ emit Updated(type: nftRef.getType().identifier, id: nftRef.id, uuid: nftRef.uuid, owner: nftRef.owner?.address)
71
69
  }
72
70
 
73
71
 
74
72
  /// Event that is emitted when a token is withdrawn,
75
- /// indicating the owner of the collection that it was withdrawn from.
73
+ /// indicating the type, id, uuid, the owner of the collection that it was withdrawn from,
74
+ /// and the UUID of the resource it was withdrawn from, usually a collection.
76
75
  ///
77
76
  /// If the collection is not in an account's storage, `from` will be `nil`.
78
77
  ///
79
- access(all) event Withdraw(id: UInt64, uuid: UInt64, from: Address?, type: String)
78
+ access(all) event Withdrawn(type: String, id: UInt64, uuid: UInt64, from: Address?, providerUUID: UInt64)
80
79
 
81
80
  /// Event that emitted when a token is deposited to a collection.
81
+ /// Indicates the type, id, uuid, the owner of the collection that it was deposited to,
82
+ /// and the UUID of the collection it was deposited to
82
83
  ///
83
- /// It indicates the owner of the collection that it was deposited to.
84
+ /// If the collection is not in an account's storage, `from`, will be `nil`.
84
85
  ///
85
- access(all) event Deposit(id: UInt64, uuid: UInt64, to: Address?, type: String)
86
+ access(all) event Deposited(type: String, id: UInt64, uuid: UInt64, to: Address?, collectionUUID: UInt64)
87
+
88
+ /// Included for backwards-compatibility
89
+ access(all) resource interface INFT: NFT {}
86
90
 
87
91
  /// Interface that the NFTs must conform to
88
92
  ///
89
93
  access(all) resource interface NFT: ViewResolver.Resolver {
90
- /// The unique ID that each NFT has
91
- access(all) view fun getID(): UInt64
92
94
 
93
- // access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid, type: self.getType().identifier)
95
+ /// unique ID for the NFT
96
+ access(all) let id: UInt64
97
+
98
+ /// Event that is emitted automatically every time a resource is destroyed
99
+ /// The type information is included in the metadata event so it is not needed as an argument
100
+ access(all) event ResourceDestroyed(id: UInt64 = self.id, uuid: UInt64 = self.uuid)
101
+
102
+ /// createEmptyCollection creates an empty Collection that is able to store the NFT
103
+ /// and returns it to the caller so that they can own NFTs
104
+ /// @return A an empty collection that can store this NFT
105
+ access(all) fun createEmptyCollection(): @{Collection} {
106
+ post {
107
+ result.getLength() == 0: "The created collection must be empty!"
108
+ }
109
+ }
110
+
111
+ /// Gets all the NFTs that this NFT directly owns
112
+ /// @return A dictionary of all subNFTS keyed by type
113
+ access(all) view fun getAvailableSubNFTS(): {Type: UInt64} {
114
+ return {}
115
+ }
94
116
 
95
117
  /// Get a reference to an NFT that this NFT owns
96
118
  /// Both arguments are optional to allow the NFT to choose
97
119
  /// how it returns sub NFTs depending on what arguments are provided
98
- /// For example, if `type` has a value, but `id` doesn't, the NFT
120
+ /// For example, if `type` has a value, but `id` doesn't, the NFT
99
121
  /// can choose which NFT of that type to return if there is a "default"
100
122
  /// If both are `nil`, then NFTs that only store a single NFT can just return
101
- /// that. This helps callers who aren't sure what they are looking for
123
+ /// that. This helps callers who aren't sure what they are looking for
102
124
  ///
103
125
  /// @param type: The Type of the desired NFT
104
126
  /// @param id: The id of the NFT to borrow
@@ -109,7 +131,7 @@ access(all) contract NonFungibleToken {
109
131
  }
110
132
  }
111
133
 
112
- /// Interface to mediate withdraws from the Collection
134
+ /// Interface to mediate withdrawals from a resource, usually a Collection
113
135
  ///
114
136
  access(all) resource interface Provider {
115
137
 
@@ -118,10 +140,11 @@ access(all) contract NonFungibleToken {
118
140
 
119
141
  /// withdraw removes an NFT from the collection and moves it to the caller
120
142
  /// It does not specify whether the ID is UUID or not
121
- access(Withdrawable) fun withdraw(withdrawID: UInt64): @{NFT} {
143
+ /// @param withdrawID: The id of the NFT to withdraw from the collection
144
+ access(Withdraw | Owner) fun withdraw(withdrawID: UInt64): @{NFT} {
122
145
  post {
123
- result.getID() == withdrawID: "The ID of the withdrawn token must be the same as the requested ID"
124
- emit Withdraw(id: result.getID(), uuid: result.uuid, from: self.owner?.address, type: result.getType().identifier)
146
+ result.id == withdrawID: "The ID of the withdrawn token must be the same as the requested ID"
147
+ emit Withdrawn(type: result.getType().identifier, id: result.id, uuid: result.uuid, from: self.owner?.address, providerUUID: self.uuid)
125
148
  }
126
149
  }
127
150
  }
@@ -131,61 +154,81 @@ access(all) contract NonFungibleToken {
131
154
  access(all) resource interface Receiver {
132
155
 
133
156
  /// deposit takes an NFT as an argument and adds it to the Collection
134
- ///
157
+ /// @param token: The NFT to deposit
135
158
  access(all) fun deposit(token: @{NFT})
136
159
 
137
160
  /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
161
+ /// @return A dictionary of types mapped to booleans indicating if this
162
+ /// reciever supports it
138
163
  access(all) view fun getSupportedNFTTypes(): {Type: Bool}
139
164
 
140
165
  /// Returns whether or not the given type is accepted by the collection
141
166
  /// A collection that can accept any type should just return true by default
167
+ /// @param type: An NFT type
168
+ /// @return A boolean indicating if this receiver can recieve the desired NFT type
142
169
  access(all) view fun isSupportedNFTType(type: Type): Bool
143
170
  }
144
171
 
172
+ /// Kept for backwards-compatibility reasons
173
+ access(all) resource interface CollectionPublic {
174
+ access(all) fun deposit(token: @{NFT})
175
+ access(all) view fun getLength(): Int
176
+ access(all) view fun getIDs(): [UInt64]
177
+ access(all) view fun borrowNFT(_ id: UInt64): &{NFT}?
178
+ }
179
+
145
180
  /// Requirement for the concrete resource type
146
181
  /// to be declared in the implementing contract
147
182
  ///
148
- access(all) resource interface Collection: Provider, Receiver, ViewResolver.ResolverCollection {
149
-
150
- /// Return the default storage path for the collection
151
- access(all) view fun getDefaultStoragePath(): StoragePath?
183
+ access(all) resource interface Collection: Provider, Receiver, CollectionPublic, ViewResolver.ResolverCollection {
152
184
 
153
- /// Return the default public path for the collection
154
- access(all) view fun getDefaultPublicPath(): PublicPath?
155
-
156
- /// Normally we would require that the collection specify
157
- /// a specific dictionary to store the NFTs, but this isn't necessary any more
158
- /// as long as all the other functions are there
159
-
160
- /// createEmptyCollection creates an empty Collection
161
- /// and returns it to the caller so that they can own NFTs
162
- access(all) fun createEmptyCollection(): @{Collection} {
163
- post {
164
- result.getLength() == 0: "The created collection must be empty!"
165
- }
166
- }
167
-
168
- /// deposit takes a NFT and adds it to the collections dictionary
169
- /// and adds the ID to the id array
185
+ /// deposit takes a NFT as an argument and stores it in the collection
186
+ /// @param token: The NFT to deposit into the collection
170
187
  access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
171
188
  pre {
172
189
  // We emit the deposit event in the `Collection` interface
173
190
  // because the `Collection` interface is almost always the final destination
174
191
  // of tokens and deposit emissions from custom receivers could be confusing
175
192
  // and hard to reconcile to event listeners
176
- emit Deposit(id: token.getID(), uuid: token.uuid, to: self.owner?.address, type: token.getType().identifier)
193
+ emit Deposited(type: token.getType().identifier, id: token.id, uuid: token.uuid, to: self.owner?.address, collectionUUID: self.uuid)
177
194
  }
178
195
  }
179
196
 
180
197
  /// Gets the amount of NFTs stored in the collection
198
+ /// @return An integer indicating the size of the collection
181
199
  access(all) view fun getLength(): Int
182
200
 
201
+ /// Borrows a reference to an NFT stored in the collection
202
+ /// If the NFT with the specified ID is not in the collection,
203
+ /// the function should return `nil` and not panic.
204
+ ///
205
+ /// @param id: The desired nft id in the collection to return a referece for.
206
+ /// @return An optional reference to the NFT
183
207
  access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
184
208
  post {
185
- (result == nil) || (result?.getID() == id):
209
+ (result == nil) || (result?.id == id):
186
210
  "Cannot borrow NFT reference: The ID of the returned reference does not match the ID that was specified"
187
211
  }
188
- return nil
212
+ }
213
+
214
+ /// createEmptyCollection creates an empty Collection of the same type
215
+ /// and returns it to the caller
216
+ /// @return A an empty collection of the same type
217
+ access(all) fun createEmptyCollection(): @{Collection} {
218
+ post {
219
+ result.getType() == self.getType(): "The created collection does not have the same type as this collection"
220
+ result.getLength() == 0: "The created collection must be empty!"
221
+ }
222
+ }
223
+ }
224
+
225
+ /// createEmptyCollection creates an empty Collection for the specified NFT type
226
+ /// and returns it to the caller so that they can own NFTs
227
+ /// @param nftType: The desired nft type to return a collection for.
228
+ /// @return An array of NFT Types that the implementing contract defines.
229
+ access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
230
+ post {
231
+ result.getIDs().length == 0: "The created collection must be empty!"
189
232
  }
190
233
  }
191
234
  }
@@ -1,43 +1,47 @@
1
- // Taken from the NFT Metadata standard, this contract exposes an interface to let
1
+ // Taken from the NFT Metadata standard, this contract exposes an interface to let
2
2
  // anyone borrow a contract and resolve views on it.
3
3
  //
4
4
  // This will allow you to obtain information about a contract without necessarily knowing anything about it.
5
5
  // All you need is its address and name and you're good to go!
6
6
  access(all) contract interface ViewResolver {
7
7
 
8
- /// Function that returns all the Metadata Views implemented by the resolving contract
8
+ /// Function that returns all the Metadata Views implemented by the resolving contract.
9
+ /// Some contracts may have multiple resource types that support metadata views
10
+ /// so there there is an optional parameter for specify which resource type the caller
11
+ /// is looking for views for.
12
+ /// Some contract-level views may be type-agnostic. In that case, the contract
13
+ /// should return the same views regardless of what type is passed in.
9
14
  ///
15
+ /// @param resourceType: An optional resource type to return views for
10
16
  /// @return An array of Types defining the implemented views. This value will be used by
11
17
  /// developers to know which parameter to pass to the resolveView() method.
12
18
  ///
13
- access(all) view fun getViews(): [Type] {
14
- return []
15
- }
19
+ access(all) view fun getContractViews(resourceType: Type?): [Type]
16
20
 
17
21
  /// Function that resolves a metadata view for this token.
22
+ /// Some contracts may have multiple resource types that support metadata views
23
+ /// so there there is an optional parameter for specify which resource type the caller
24
+ /// is looking for views for.
25
+ /// Some contract-level views may be type-agnostic. In that case, the contract
26
+ /// should return the same views regardless of what type is passed in.
18
27
  ///
28
+ /// @param resourceType: An optional resource type to return views for
19
29
  /// @param view: The Type of the desired view.
20
30
  /// @return A structure representing the requested view.
21
31
  ///
22
- access(all) fun resolveView(_ view: Type): AnyStruct? {
23
- return nil
24
- }
32
+ access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct?
25
33
 
26
- /// Provides access to a set of metadata views. A struct or
27
- /// resource (e.g. an NFT) can implement this interface to provide access to
34
+ /// Provides access to a set of metadata views. A struct or
35
+ /// resource (e.g. an NFT) can implement this interface to provide access to
28
36
  /// the views that it supports.
29
37
  ///
30
38
  access(all) resource interface Resolver {
31
39
 
32
40
  /// Same as getViews above, but on a specific NFT instead of a contract
33
- access(all) view fun getViews(): [Type] {
34
- return []
35
- }
41
+ access(all) view fun getViews(): [Type]
36
42
 
37
43
  /// Same as resolveView above, but on a specific NFT instead of a contract
38
- access(all) fun resolveView(_ view: Type): AnyStruct? {
39
- return nil
40
- }
44
+ access(all) fun resolveView(_ view: Type): AnyStruct?
41
45
  }
42
46
 
43
47
  /// A group of view resolvers indexed by ID.
@@ -110,10 +110,8 @@ access(all) contract ExampleNFT: ViewResolver {
110
110
  return MetadataViews.NFTCollectionData(
111
111
  storagePath: ExampleNFT.CollectionStoragePath,
112
112
  publicPath: ExampleNFT.CollectionPublicPath,
113
- providerPath: /private/exampleNFTCollection,
114
113
  publicCollection: Type<&ExampleNFT.Collection>(),
115
114
  publicLinkedType: Type<&ExampleNFT.Collection>(),
116
- providerLinkedType: Type<auth(NonFungibleToken.Withdrawable) &ExampleNFT.Collection>(),
117
115
  createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
118
116
  return <-ExampleNFT.createEmptyCollection()
119
117
  })
package/flow.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "networks": {
3
3
  "emulator": "127.0.0.1:3569",
4
+ "testing": "127.0.0.1:3569",
4
5
  "mainnet": "access.mainnet.nodes.onflow.org:9000",
5
6
  "sandboxnet": "access.sandboxnet.nodes.onflow.org:9000",
6
7
  "testnet": "access.devnet.nodes.onflow.org:9000"
@@ -28,6 +29,14 @@
28
29
  "mainnet": "0x1d7e57aa55817448"
29
30
  }
30
31
  },
32
+ "Burner": {
33
+ "source": "./contracts/Burner.cdc",
34
+ "aliases": {
35
+ "emulator": "0xf8d6e0586b0a20c7",
36
+ "testnet": "0x631e88ae7f1d7c20",
37
+ "mainnet": "0x1d7e57aa55817448"
38
+ }
39
+ },
31
40
  "MetadataViews": {
32
41
  "source": "./contracts/MetadataViews.cdc",
33
42
  "aliases": {
@@ -342,6 +351,7 @@
342
351
  "deployments": {
343
352
  "emulator": {
344
353
  "emulator-account": [
354
+ "Burner",
345
355
  "NonFungibleToken",
346
356
  "MetadataViews",
347
357
  "ViewResolver",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowtyio/flow-contracts",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0-beta.4",
4
4
  "main": "index.json",
5
5
  "description": "An NPM package for common flow contracts",
6
6
  "author": "flowtyio",