@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.
- package/contracts/Burner.cdc +44 -0
- package/contracts/FlowStorageFees.cdc +15 -15
- package/contracts/FlowToken.cdc +29 -78
- package/contracts/FungibleToken.cdc +80 -53
- package/contracts/FungibleTokenMetadataViews.cdc +13 -25
- package/contracts/MetadataViews.cdc +28 -44
- package/contracts/NonFungibleToken.cdc +102 -59
- package/contracts/ViewResolver.cdc +20 -16
- package/contracts/example/ExampleNFT.cdc +0 -2
- package/flow.json +10 -0
- package/package.json +1 -1
|
@@ -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.
|
|
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.
|
|
100
|
+
balance = balanceRef.balance.saturatingSubtract(maxTxFees)
|
|
101
101
|
} else {
|
|
102
|
-
balance = balanceRef.
|
|
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.
|
|
160
|
+
balance = balanceRef.balance
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// get how much should be reserved for storage
|
package/contracts/FlowToken.cdc
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import "FungibleToken"
|
|
2
2
|
import "MetadataViews"
|
|
3
3
|
import "FungibleTokenMetadataViews"
|
|
4
|
-
import "
|
|
4
|
+
import "Burner"
|
|
5
5
|
|
|
6
|
-
access(all) contract FlowToken:
|
|
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
|
|
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
|
-
///
|
|
68
|
-
access(all) view fun
|
|
69
|
-
return
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
154
|
-
switch
|
|
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.
|
|
158
|
-
ftVaultData: self.
|
|
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
|
-
|
|
184
|
-
|
|
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 <-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
///
|
|
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
|
|
69
|
-
///
|
|
70
|
-
///
|
|
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(
|
|
95
|
+
access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
|
|
81
96
|
post {
|
|
82
97
|
// `result` refers to the return value
|
|
83
|
-
result.
|
|
98
|
+
result.balance == amount:
|
|
84
99
|
"Withdrawal amount must be the same as the balance of the withdrawn Vault"
|
|
85
|
-
emit
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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(
|
|
180
|
+
access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
|
|
164
181
|
pre {
|
|
165
|
-
self.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
///
|
|
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<&{
|
|
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
|
-
///
|
|
609
|
-
///
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
9
|
-
|
|
8
|
+
If a user wants to deploy a new NFT contract, their contract should implement
|
|
9
|
+
The types defined here
|
|
10
10
|
|
|
11
|
-
|
|
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
|
|
30
|
-
|
|
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
|
|
47
|
-
/// import and implement this
|
|
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
|
|
47
|
+
access(all) entitlement Withdraw
|
|
53
48
|
|
|
54
49
|
/// An entitlement for allowing updates and update events for an NFT
|
|
55
|
-
access(all) entitlement
|
|
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
|
|
68
|
-
access(
|
|
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(
|
|
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
|
|
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
|
-
///
|
|
84
|
+
/// If the collection is not in an account's storage, `from`, will be `nil`.
|
|
84
85
|
///
|
|
85
|
-
access(all) event
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
124
|
-
emit
|
|
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
|
-
///
|
|
154
|
-
|
|
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
|
|
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?.
|
|
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
|
-
|
|
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
|
|
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
|
|
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",
|