Smart Contract

DynamicNFT

A.bb12a6da563a5e8e.DynamicNFT

Deployed

3d ago
Feb 25, 2026, 03:54:31 AM UTC

Dependents

3 imports
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