Smart Contract
DynamicNFT
A.bb12a6da563a5e8e.DynamicNFT
1/*
2*
3* This is an example of how to implement Dynamic NFTs on Flow.
4* A Dynamic NFT is one that can be changed after minting. In
5* this contract, a NFT's metadata can be changed by an Administrator.
6*
7*/
8
9import NonFungibleToken from 0x1d7e57aa55817448
10import MetadataViews from 0x1d7e57aa55817448
11import TraderflowScores from 0xbb12a6da563a5e8e
12import ViewResolver from 0x1d7e57aa55817448
13
14access(all) contract DynamicNFT: NonFungibleToken {
15
16 access(all) var totalSupply: UInt64
17
18 access(all) event ContractInitialized()
19 access(all) event Withdraw(id: UInt64, from: Address?)
20 access(all) event Deposit(id: UInt64, to: Address?)
21 access(all) event Minted(id: UInt64, by: Address, name: String, description: String, thumbnail: String)
22
23 access(all) let CollectionStoragePath: StoragePath
24 access(all) let CollectionPublicPath: PublicPath
25 access(all) let MinterStoragePath: StoragePath
26
27 access(all) struct NFTMetadata {
28 access(all) let name: String
29 access(all) let description: String
30 access(all) var thumbnail: String
31 access(self) let metadata: TraderflowScores.TradeMetadata
32
33 init(
34 name: String,
35 description: String,
36 thumbnail: String,
37 metadata: TraderflowScores.TradeMetadata
38 ) {
39 self.name = name
40 self.description = description
41 self.thumbnail = thumbnail
42 self.metadata = metadata
43 }
44
45 access(all) fun updateThumbnail(ipfs: String) {
46 self.thumbnail = ipfs
47 }
48
49 }
50
51 access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
52 access(all) let id: UInt64
53 access(all) let sequence: UInt64
54 access(all) var metadata: NFTMetadata
55 access(self) let trades: TraderflowScores.TradeScores
56
57 access(all) view fun getViews(): [Type] {
58 return [
59 Type<MetadataViews.Display>()
60 ]
61 }
62
63 access(all) fun resolveView(_ view: Type): AnyStruct? {
64 let template: NFTMetadata = self.getMetadata()
65 switch view {
66 case Type<MetadataViews.Display>():
67 return MetadataViews.Display(
68 name: template.name,
69 description: template.description,
70 thumbnail: MetadataViews.HTTPFile(
71 url: template.thumbnail
72 )
73 )
74 }
75 return nil
76 }
77
78 access(all) view fun getTrades(): TraderflowScores.TradeScores {
79 return self.trades
80 }
81
82 access(all) fun getMetadata(): NFTMetadata {
83 return NFTMetadata(name: self.metadata.name, description: self.metadata.description, thumbnail: self.metadata.thumbnail, metadata: self.trades.metadata())
84 }
85
86 access(contract) fun borrowTradesRef(): &TraderflowScores.TradeScores {
87 return &self.trades
88 }
89
90 access(contract) fun updateArtwork(ipfs: String) {
91 self.metadata.updateThumbnail(ipfs: ipfs)
92 }
93
94 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
95 return <- DynamicNFT.createEmptyCollection(nftType: Type<@DynamicNFT.NFT>())
96 }
97
98 init(_name: String, _description: String, _thumbnail: String) {
99 self.id = self.uuid
100 self.sequence = DynamicNFT.totalSupply
101 self.trades = TraderflowScores.TradeScores()
102 self.metadata = NFTMetadata(name: _name, description: _description, thumbnail: _thumbnail, metadata: self.trades.metadata())
103 DynamicNFT.totalSupply = DynamicNFT.totalSupply + 1
104 }
105 }
106
107 access(all) resource interface CollectionPublic {
108 access(all) fun deposit(token: @{NonFungibleToken.NFT})
109 access(all) fun getIDs(): [UInt64]
110 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
111 access(all) fun borrowAuthNFT(id: UInt64): &DynamicNFT.NFT? {
112 post {
113 (result == nil) || (result?.id == id):
114 "Cannot borrow DynamicNFT reference: the ID of the returned reference is incorrect"
115 }
116 }
117 }
118
119 access(all) resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection, CollectionPublic {
120 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
121
122 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
123 let token: @{NonFungibleToken.NFT} <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
124 emit Withdraw(id: token.id, from: self.owner?.address)
125 return <- token
126 }
127
128 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
129 let token: @DynamicNFT.NFT <- token as! @DynamicNFT.NFT
130 emit Deposit(id: token.id, to: self.owner?.address)
131 self.ownedNFTs[token.id] <-! token
132 }
133
134 access(all) view fun getIDs(): [UInt64] {
135 return self.ownedNFTs.keys
136 }
137
138 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
139 return (&self.ownedNFTs[id])
140 }
141
142 access(all) fun borrowAuthNFT(id: UInt64): &DynamicNFT.NFT? {
143 if self.ownedNFTs[id] != nil {
144 let ref: &{NonFungibleToken.NFT}? = (&self.ownedNFTs[id])
145 return ref as! &DynamicNFT.NFT
146 }
147 return nil
148 }
149
150 access(all) view fun getLength(): Int {
151 return self.ownedNFTs.length
152 }
153
154 access(all) fun forEachID(_ f: fun (UInt64): Bool): Void {}
155
156 /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
157 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
158 let supportedTypes: {Type: Bool} = {}
159 supportedTypes[Type<@DynamicNFT.NFT>()] = true
160 return supportedTypes
161 }
162
163 /// Returns whether or not the given type is accepted by the collection
164 /// A collection that can accept any type should just return true by default
165 access(all) view fun isSupportedNFTType(type: Type): Bool {
166 return type == Type<@DynamicNFT.NFT>()
167 }
168
169 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
170 return <- DynamicNFT.createEmptyCollection(nftType: Type<@DynamicNFT.NFT>())
171 }
172
173 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
174 pre {
175 self.ownedNFTs[id] != nil : "NFT not in collection"
176 }
177 let nft: &{NonFungibleToken.NFT} = (&self.ownedNFTs[id])!
178 let dynamicNFT: &DynamicNFT.NFT = nft as! &DynamicNFT.NFT
179 return dynamicNFT
180 }
181
182 init () {
183 self.ownedNFTs <- {}
184 }
185 }
186
187 access(all) event rebuildNFT(id: UInt64, owner: Address, metadata: TraderflowScores.TradeMetadataRebuild)
188
189 access(all) resource Administrator {
190
191 access(all) fun mintNFT(
192 recipient: &Collection,
193 name: String,
194 description: String,
195 thumbnail: String
196 ) {
197 let nft: @DynamicNFT.NFT <- create NFT(_name: name, _description: description, _thumbnail: thumbnail)
198 emit Minted(id: nft.id, by: self.owner!.address, name: name, description: description, thumbnail: thumbnail)
199 recipient.deposit(token: <- nft)
200 }
201
202 access(all) fun pushTrade(
203 id: UInt64,
204 currentOwner: Address,
205 trade: TraderflowScores.Trade
206 ) {
207 let ownerCollection: &DynamicNFT.Collection = getAccount(currentOwner).capabilities.get<&Collection>(DynamicNFT.CollectionPublicPath)
208 .borrow()
209 ?? panic("This person does not have a DynamicNFT Collection set up properly.")
210 let nftRef: &DynamicNFT.NFT = ownerCollection.borrowAuthNFT(id: id) ?? panic("This account does not own an NFT with this id.")
211 let tradeRef: &TraderflowScores.TradeScores = nftRef.borrowTradesRef()
212 let update: TraderflowScores.TradeMetadataRebuild = tradeRef.pushTrade(_trade: trade)
213 emit rebuildNFT(id:id, owner:currentOwner, metadata:update)
214 }
215
216 access(all) fun pushEquity(
217 id: UInt64,
218 currentOwner: Address,
219 equity: UFix64
220 ) {
221 let ownerCollection: &DynamicNFT.Collection = getAccount(currentOwner).capabilities.get<&Collection>(DynamicNFT.CollectionPublicPath)
222 .borrow()
223 ?? panic("This person does not have a DynamicNFT Collection set up properly.")
224 let nftRef: &DynamicNFT.NFT = ownerCollection.borrowAuthNFT(id: id) ?? panic("This account does not own an NFT with this id.")
225 let tradeRef: &TraderflowScores.TradeScores = nftRef.borrowTradesRef()
226 let update: TraderflowScores.TradeMetadataRebuild = tradeRef.pushEquity(_equity: equity)
227 emit rebuildNFT(id:id, owner:currentOwner, metadata:update)
228 }
229
230 access(all) fun updateArtwork(
231 id: UInt64,
232 currentOwner: Address,
233 ipfs: String
234 ) {
235 let ownerCollection: &DynamicNFT.Collection = getAccount(currentOwner).capabilities.get<&Collection>(DynamicNFT.CollectionPublicPath)
236 .borrow()
237 ?? panic("This person does not have a DynamicNFT Collection set up properly.")
238 let nftRef: &DynamicNFT.NFT = ownerCollection.borrowAuthNFT(id: id) ?? panic("This account does not own an NFT with this id.")
239
240 nftRef.updateArtwork(ipfs: ipfs)
241 }
242 }
243
244 access(all) view fun getContractViews(resourceType: Type?): [Type] {
245 return [
246 Type<MetadataViews.NFTCollectionData>()
247 ]
248 }
249
250 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
251 return nil
252 }
253
254 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
255 return <- create Collection()
256 }
257
258 init() {
259 self.totalSupply = 0
260
261 self.CollectionStoragePath = /storage/DynamicNFTCollection
262 self.CollectionPublicPath = /public/DynamicNFTCollection
263 self.MinterStoragePath = /storage/DynamicNFTMinter
264
265 self.account.storage.save(<- create Administrator(), to: self.MinterStoragePath)
266
267 emit ContractInitialized()
268 }
269}
270