@flowtyio/flow-contracts 0.0.18 → 0.1.0-beta.2

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.
@@ -4,11 +4,11 @@
4
4
 
5
5
  ## `NonFungibleToken` contract interface
6
6
 
7
- The interface that all Non-Fungible Token contracts could conform to.
7
+ The interface that all Non-Fungible Token contracts should conform to.
8
8
  If a user wants to deploy a new NFT contract, their contract would need
9
9
  to implement the NonFungibleToken interface.
10
10
 
11
- Their contract would have to follow all the rules and naming
11
+ Their contract must follow all the rules and naming
12
12
  that the interface specifies.
13
13
 
14
14
  ## `NFT` resource
@@ -41,162 +41,151 @@ Collection to complete the transfer.
41
41
 
42
42
  */
43
43
 
44
+ import "ViewResolver"
45
+
44
46
  /// The main NFT contract interface. Other NFT contracts will
45
47
  /// import and implement this interface
46
48
  ///
47
- pub contract interface NonFungibleToken {
49
+ access(all) contract NonFungibleToken {
50
+
51
+ /// An entitlement for allowing the withdrawal of tokens from a Vault
52
+ access(all) entitlement Withdrawable
48
53
 
49
- /// The total number of tokens of this type in existence
50
- pub var totalSupply: UInt64
54
+ /// An entitlement for allowing updates and update events for an NFT
55
+ access(all) entitlement Updatable
51
56
 
52
- /// Event that emitted when the NFT contract is initialized
57
+ /// Event that contracts should emit when the metadata of an NFT is updated
58
+ /// It can only be emitted by calling the `emitNFTUpdated` function
59
+ /// with an `Updatable` entitled reference to the NFT that was updated
60
+ /// The entitlement prevents spammers from calling this from other users' collections
61
+ /// because only code within a collection or that has special entitled access
62
+ /// to the collections methods will be able to get the entitled reference
63
+ ///
64
+ /// The event makes it so that third-party indexers can monitor the events
65
+ /// and query the updated metadata from the owners' collections.
53
66
  ///
54
- pub event ContractInitialized()
67
+ access(all) event Updated(id: UInt64, uuid: UInt64, owner: Address?, type: String)
68
+ access(all) view fun emitNFTUpdated(_ nftRef: auth(Updatable) &{NonFungibleToken.NFT})
69
+ {
70
+ emit Updated(id: nftRef.getID(), uuid: nftRef.uuid, owner: nftRef.owner?.address, type: nftRef.getType().identifier)
71
+ }
72
+
55
73
 
56
74
  /// Event that is emitted when a token is withdrawn,
57
75
  /// indicating the owner of the collection that it was withdrawn from.
58
76
  ///
59
77
  /// If the collection is not in an account's storage, `from` will be `nil`.
60
78
  ///
61
- pub event Withdraw(id: UInt64, from: Address?)
79
+ access(all) event Withdraw(id: UInt64, uuid: UInt64, from: Address?, type: String)
62
80
 
63
81
  /// Event that emitted when a token is deposited to a collection.
64
82
  ///
65
83
  /// It indicates the owner of the collection that it was deposited to.
66
84
  ///
67
- pub event Deposit(id: UInt64, to: Address?)
85
+ access(all) event Deposit(id: UInt64, uuid: UInt64, to: Address?, type: String)
68
86
 
69
- /// Interface that the NFTs have to conform to
70
- /// The metadata views methods are included here temporarily
71
- /// because enforcing the metadata interfaces in the standard
72
- /// would break many contracts in an upgrade. Those breaking changes
73
- /// are being saved for the stable cadence milestone
87
+ /// Interface that the NFTs must conform to
74
88
  ///
75
- pub resource interface INFT {
89
+ access(all) resource interface NFT: ViewResolver.Resolver {
76
90
  /// The unique ID that each NFT has
77
- pub let id: UInt64
91
+ access(all) view fun getID(): UInt64
78
92
 
79
- /// Function that returns all the Metadata Views implemented by a Non Fungible Token
80
- ///
81
- /// @return An array of Types defining the implemented views. This value will be used by
82
- /// developers to know which parameter to pass to the resolveView() method.
83
- ///
84
- pub fun getViews(): [Type] {
85
- return []
86
- }
93
+ // access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid, type: self.getType().identifier)
87
94
 
88
- /// Function that resolves a metadata view for this token.
95
+ /// Get a reference to an NFT that this NFT owns
96
+ /// Both arguments are optional to allow the NFT to choose
97
+ /// 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
99
+ /// can choose which NFT of that type to return if there is a "default"
100
+ /// 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
89
102
  ///
90
- /// @param view: The Type of the desired view.
91
- /// @return A structure representing the requested view.
103
+ /// @param type: The Type of the desired NFT
104
+ /// @param id: The id of the NFT to borrow
92
105
  ///
93
- pub fun resolveView(_ view: Type): AnyStruct? {
106
+ /// @return A structure representing the requested view.
107
+ access(all) fun getSubNFT(type: Type, id: UInt64) : &{NonFungibleToken.NFT}? {
94
108
  return nil
95
109
  }
96
110
  }
97
111
 
98
- /// Requirement that all conforming NFT smart contracts have
99
- /// to define a resource called NFT that conforms to INFT
100
- ///
101
- pub resource NFT: INFT {
102
- pub let id: UInt64
103
- }
104
-
105
112
  /// Interface to mediate withdraws from the Collection
106
113
  ///
107
- pub resource interface Provider {
108
- /// Removes an NFT from the resource implementing it and moves it to the caller
109
- ///
110
- /// @param withdrawID: The ID of the NFT that will be removed
111
- /// @return The NFT resource removed from the implementing resource
112
- ///
113
- pub fun withdraw(withdrawID: UInt64): @NFT {
114
+ access(all) resource interface Provider {
115
+
116
+ // We emit withdraw events from the provider interface because conficting withdraw
117
+ // events aren't as confusing to event listeners as conflicting deposit events
118
+
119
+ /// withdraw removes an NFT from the collection and moves it to the caller
120
+ /// It does not specify whether the ID is UUID or not
121
+ access(Withdrawable) fun withdraw(withdrawID: UInt64): @{NFT} {
114
122
  post {
115
- result.id == withdrawID: "The ID of the withdrawn token must be the same as the requested ID"
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)
116
125
  }
117
126
  }
118
127
  }
119
128
 
120
129
  /// Interface to mediate deposits to the Collection
121
130
  ///
122
- pub resource interface Receiver {
131
+ access(all) resource interface Receiver {
123
132
 
124
- /// Adds an NFT to the resource implementing it
133
+ /// deposit takes an NFT as an argument and adds it to the Collection
125
134
  ///
126
- /// @param token: The NFT resource that will be deposited
127
- ///
128
- pub fun deposit(token: @NFT)
129
- }
135
+ access(all) fun deposit(token: @{NFT})
130
136
 
131
- /// Interface that an account would commonly
132
- /// publish for their collection
133
- ///
134
- pub resource interface CollectionPublic {
135
- pub fun deposit(token: @NFT)
136
- pub fun getIDs(): [UInt64]
137
- pub fun borrowNFT(id: UInt64): &NFT
138
- /// Safe way to borrow a reference to an NFT that does not panic
139
- ///
140
- /// @param id: The ID of the NFT that want to be borrowed
141
- /// @return An optional reference to the desired NFT, will be nil if the passed id does not exist
142
- ///
143
- pub fun borrowNFTSafe(id: UInt64): &NFT? {
144
- post {
145
- result == nil || result!.id == id: "The returned reference's ID does not match the requested ID"
146
- }
147
- return nil
148
- }
137
+ /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
138
+ access(all) view fun getSupportedNFTTypes(): {Type: Bool}
139
+
140
+ /// Returns whether or not the given type is accepted by the collection
141
+ /// A collection that can accept any type should just return true by default
142
+ access(all) view fun isSupportedNFTType(type: Type): Bool
149
143
  }
150
144
 
151
145
  /// Requirement for the concrete resource type
152
146
  /// to be declared in the implementing contract
153
147
  ///
154
- pub resource Collection: Provider, Receiver, CollectionPublic {
148
+ access(all) resource interface Collection: Provider, Receiver, ViewResolver.ResolverCollection {
155
149
 
156
- /// Dictionary to hold the NFTs in the Collection
157
- pub var ownedNFTs: @{UInt64: NFT}
150
+ /// Return the default storage path for the collection
151
+ access(all) view fun getDefaultStoragePath(): StoragePath?
158
152
 
159
- /// Removes an NFT from the collection and moves it to the caller
160
- ///
161
- /// @param withdrawID: The ID of the NFT that will be withdrawn
162
- /// @return The resource containing the desired NFT
163
- ///
164
- pub fun withdraw(withdrawID: UInt64): @NFT
153
+ /// Return the default public path for the collection
154
+ access(all) view fun getDefaultPublicPath(): PublicPath?
165
155
 
166
- /// Takes a NFT and adds it to the collections dictionary
167
- /// and adds the ID to the ID array
168
- ///
169
- /// @param token: An NFT resource
170
- ///
171
- pub fun deposit(token: @NFT)
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
172
159
 
173
- /// Returns an array of the IDs that are in the collection
174
- ///
175
- /// @return An array containing all the IDs on the collection
176
- ///
177
- pub fun getIDs(): [UInt64]
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
+ }
178
167
 
179
- /// Returns a borrowed reference to an NFT in the collection
180
- /// so that the caller can read data and call methods from it
181
- ///
182
- /// @param id: The ID of the NFT that want to be borrowed
183
- /// @return A reference to the NFT
184
- ///
185
- pub fun borrowNFT(id: UInt64): &NFT {
168
+ /// deposit takes a NFT and adds it to the collections dictionary
169
+ /// and adds the ID to the id array
170
+ access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
186
171
  pre {
187
- self.ownedNFTs[id] != nil: "NFT does not exist in the collection!"
172
+ // We emit the deposit event in the `Collection` interface
173
+ // because the `Collection` interface is almost always the final destination
174
+ // of tokens and deposit emissions from custom receivers could be confusing
175
+ // and hard to reconcile to event listeners
176
+ emit Deposit(id: token.getID(), uuid: token.uuid, to: self.owner?.address, type: token.getType().identifier)
188
177
  }
189
178
  }
190
- }
191
179
 
192
- /// Creates an empty Collection and returns it to the caller so that they can own NFTs
193
- ///
194
- /// @return A new Collection resource
195
- ///
196
- pub fun createEmptyCollection(): @Collection {
197
- post {
198
- result.getIDs().length == 0: "The created collection must be empty!"
180
+ /// Gets the amount of NFTs stored in the collection
181
+ access(all) view fun getLength(): Int
182
+
183
+ access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
184
+ post {
185
+ (result == nil) || (result?.getID() == id):
186
+ "Cannot borrow NFT reference: The ID of the returned reference does not match the ID that was specified"
187
+ }
188
+ return nil
199
189
  }
200
190
  }
201
191
  }
202
-
@@ -1,15 +1,16 @@
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
- pub contract interface ViewResolver {
6
+ access(all) contract interface ViewResolver {
7
+
7
8
  /// Function that returns all the Metadata Views implemented by the resolving contract
8
9
  ///
9
10
  /// @return An array of Types defining the implemented views. This value will be used by
10
11
  /// developers to know which parameter to pass to the resolveView() method.
11
12
  ///
12
- pub fun getViews(): [Type] {
13
+ access(all) view fun getViews(): [Type] {
13
14
  return []
14
15
  }
15
16
 
@@ -18,8 +19,36 @@ pub contract interface ViewResolver {
18
19
  /// @param view: The Type of the desired view.
19
20
  /// @return A structure representing the requested view.
20
21
  ///
21
- pub fun resolveView(_ view: Type): AnyStruct? {
22
+ access(all) fun resolveView(_ view: Type): AnyStruct? {
22
23
  return nil
23
24
  }
25
+
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
28
+ /// the views that it supports.
29
+ ///
30
+ access(all) resource interface Resolver {
31
+
32
+ /// Same as getViews above, but on a specific NFT instead of a contract
33
+ access(all) view fun getViews(): [Type] {
34
+ return []
35
+ }
36
+
37
+ /// 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
+ }
41
+ }
42
+
43
+ /// A group of view resolvers indexed by ID.
44
+ ///
45
+ access(all) resource interface ResolverCollection {
46
+ access(all) view fun borrowViewResolver(id: UInt64): &{Resolver}? {
47
+ return nil
48
+ }
49
+
50
+ access(all) view fun getIDs(): [UInt64] {
51
+ return []
52
+ }
53
+ }
24
54
  }
25
-
@@ -11,49 +11,41 @@ import "FlowToken"
11
11
 
12
12
  Consumers of this contract would then need to pop the resource out of the DepositEstimate resource to get it back
13
13
  */
14
- pub contract FeeEstimator {
15
- pub resource DepositEstimate {
16
- pub var item: @AnyResource?
17
- pub var storageFee: UFix64
14
+ access(all) contract FeeEstimator {
15
+ access(all) resource DepositEstimate {
16
+ access(all) var item: @AnyResource?
17
+ access(all) var storageFee: UFix64
18
18
 
19
19
  init(item: @AnyResource, storageFee: UFix64) {
20
20
  self.item <- item
21
21
  self.storageFee = storageFee
22
22
  }
23
23
 
24
- pub fun withdraw(): @AnyResource {
25
- let resource <- self.item <- nil
26
- return <-resource!
27
- }
28
-
29
- destroy() {
30
- pre {
31
- self.item == nil: "cannot destroy with non-null item"
32
- }
33
-
34
- destroy self.item
24
+ access(all) fun withdraw(): @AnyResource {
25
+ let r <- self.item <- nil
26
+ return <-r!
35
27
  }
36
28
  }
37
29
 
38
- pub fun hasStorageCapacity(_ addr: Address, _ storageFee: UFix64): Bool {
30
+ access(all) fun hasStorageCapacity(_ addr: Address, _ storageFee: UFix64): Bool {
39
31
  return FlowStorageFees.defaultTokenAvailableBalance(addr) > storageFee
40
32
  }
41
33
 
42
- pub fun estimateDeposit(
34
+ access(all) fun estimateDeposit(
43
35
  item: @AnyResource,
44
36
  ): @DepositEstimate {
45
- let storageUsedBefore = FeeEstimator.account.storageUsed
46
- FeeEstimator.account.save(<-item, to: /storage/temp)
47
- let storageUsedAfter = FeeEstimator.account.storageUsed
37
+ let storageUsedBefore = FeeEstimator.account.storage.used
38
+ FeeEstimator.account.storage.save(<-item, to: /storage/temp)
39
+ let storageUsedAfter = FeeEstimator.account.storage.used
48
40
 
49
41
  let storageDiff = storageUsedAfter - storageUsedBefore
50
42
  let storageFee = FeeEstimator.storageUsedToFlowAmount(storageDiff)
51
- let loadedItem <- FeeEstimator.account.load<@AnyResource>(from: /storage/temp)!
43
+ let loadedItem <- FeeEstimator.account.storage.load<@AnyResource>(from: /storage/temp)!
52
44
  let estimate <- create DepositEstimate(item: <-loadedItem, storageFee: storageFee)
53
45
  return <- estimate
54
46
  }
55
47
 
56
- pub fun storageUsedToFlowAmount(_ storageUsed: UInt64): UFix64 {
48
+ access(all) fun storageUsedToFlowAmount(_ storageUsed: UInt64): UFix64 {
57
49
  let storageMB = FlowStorageFees.convertUInt64StorageBytesToUFix64Megabytes(storageUsed)
58
50
  return FlowStorageFees.storageCapacityToFlow(storageMB)
59
51
  }