Smart Contract
Sharks
A.1c7d5d603d4010e4.Sharks
1// Description: Smart Contract for Sharks Virtual Commemorative Tickets
2// SPDX-License-Identifier: UNLICENSED
3
4import NonFungibleToken from 0x1d7e57aa55817448
5import MetadataViews from 0x1d7e57aa55817448
6import ViewResolver from 0x1d7e57aa55817448
7
8access(all) contract Sharks : NonFungibleToken {
9
10 access(all) var totalSupply: UInt64
11
12 access(all) event ContractInitialized()
13 access(all) event Withdraw(id: UInt64, from: Address?)
14 access(all) event Deposit(id: UInt64, to: Address?)
15
16 access(all) let CollectionStoragePath: StoragePath
17 access(all) let CollectionPublicPath: PublicPath
18 access(all) let MinterStoragePath: StoragePath
19
20 // Correct type from NonFungibleToken.INFT to NonFungibleToken.NFT
21 access(all) resource NFT : NonFungibleToken.NFT {
22 access(all) let id: UInt64
23 access(all) var link: String
24 access(all) var batch: UInt32
25 access(all) var sequence: UInt16
26 access(all) var limit: UInt16
27
28 init(
29 initID: UInt64,
30 initlink: String,
31 initbatch: UInt32,
32 initsequence: UInt16,
33 initlimit: UInt16
34 ) {
35 self.id = initID
36 self.link = initlink
37 self.batch = initbatch
38 self.sequence = initsequence
39 self.limit = initlimit
40 }
41
42 /// createEmptyCollection creates an empty Collection
43 /// and returns it to the caller so that they can own NFTs
44 /// @{NonFungibleToken.Collection}
45 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
46 return <-Sharks.createEmptyCollection(nftType: Type<@Sharks.NFT>())
47 }
48
49 access(all) view fun getViews(): [Type] {
50 return [
51 Type<MetadataViews.NFTCollectionData>()
52 ]
53 }
54
55 access(all) fun resolveView(_ view: Type): AnyStruct? {
56 switch view {
57 case Type<MetadataViews.NFTCollectionData>():
58 return Sharks.resolveContractView(resourceType: Type<@Sharks.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
59 }
60 return nil
61 }
62 }
63
64 // Ensure all type specifications conform to interface requirements
65 access(all) resource interface SharksCollectionPublic {
66 // Deprecated, only here for backwards compatibility
67 }
68
69 access(all) resource Collection : NonFungibleToken.Collection, SharksCollectionPublic {
70 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
71
72 init() {
73 self.ownedNFTs <- {}
74 }
75
76 /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
77 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
78 let supportedTypes: {Type: Bool} = {}
79 supportedTypes[Type<@Sharks.NFT>()] = true
80 return supportedTypes
81 }
82
83 /// Returns whether or not the given type is accepted by the collection
84 /// A collection that can accept any type should just return true by default
85 access(all) view fun isSupportedNFTType(type: Type): Bool {
86 return type == Type<@Sharks.NFT>()
87 }
88
89 /// withdraw removes an NFT from the collection and moves it to the caller
90 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
91 let token <- self.ownedNFTs.remove(key: withdrawID)
92 ?? panic("Could not withdraw an NFT with the provided ID from the collection")
93
94 return <-token
95 }
96
97 /// deposit takes a NFT and adds it to the collections dictionary
98 /// and adds the ID to the id array
99 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
100 let token <- token as! @Sharks.NFT
101 let id = token.id
102
103 // add the new token to the dictionary which removes the old one
104 let oldToken <- self.ownedNFTs[token.id] <- token
105
106 destroy oldToken
107
108 // This code is for testing purposes only
109 // Do not add to your contract unless you have a specific
110 // reason to want to emit the NFTUpdated event somewhere
111 // in your contract
112 let authTokenRef = (&self.ownedNFTs[id] as auth(NonFungibleToken.Update) &{NonFungibleToken.NFT}?)!
113 //authTokenRef.updateTransferDate(date: getCurrentBlock().timestamp)
114 Sharks.emitNFTUpdated(authTokenRef)
115 }
116
117 /// getIDs returns an array of the IDs that are in the collection
118 access(all) view fun getIDs(): [UInt64] {
119 return self.ownedNFTs.keys
120 }
121
122 /// Gets the amount of NFTs stored in the collection
123 access(all) view fun getLength(): Int {
124 return self.ownedNFTs.length
125 }
126
127 /// Borrow the view resolver for the specified NFT ID
128 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
129 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
130 return nft as &{ViewResolver.Resolver}
131 }
132 return nil
133 }
134
135 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
136 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
137 }
138
139 /// createEmptyCollection creates an empty Collection of the same type
140 /// and returns it to the caller
141 /// @return A an empty collection of the same type
142 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
143 return <- Sharks.createEmptyCollection(nftType: Type<@Sharks.NFT>())
144 }
145 }
146
147 /// createEmptyCollection creates an empty Collection for the specified NFT type
148 /// and returns it to the caller so that they can own NFTs
149 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
150 return <- create Collection()
151 }
152
153 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
154 ///
155 /// @return An array of Types defining the implemented views. This value will be used by
156 /// developers to know which parameter to pass to the resolveView() method.
157 ///
158 access(all) view fun getContractViews(resourceType: Type?): [Type] {
159 return [
160 Type<MetadataViews.NFTCollectionData>(),
161 Type<MetadataViews.NFTCollectionDisplay>(),
162 Type<MetadataViews.EVMBridgedMetadata>()
163 ]
164 }
165
166 /// Function that resolves a metadata view for this contract.
167 ///
168 /// @param view: The Type of the desired view.
169 /// @return A structure representing the requested view.
170 ///
171 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
172 switch viewType {
173 case Type<MetadataViews.NFTCollectionData>():
174 let collectionData = MetadataViews.NFTCollectionData(
175 storagePath: self.CollectionStoragePath,
176 publicPath: self.CollectionPublicPath,
177 publicCollection: Type<&Sharks.Collection>(),
178 publicLinkedType: Type<&Sharks.Collection>(),
179 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
180 return <-Sharks.createEmptyCollection(nftType: Type<@Sharks.NFT>())
181 })
182 )
183 return collectionData
184 case Type<MetadataViews.NFTCollectionDisplay>():
185 let media = MetadataViews.Media(
186 file: MetadataViews.HTTPFile(
187 url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
188 ),
189 mediaType: "image/svg+xml"
190 )
191 return MetadataViews.NFTCollectionDisplay(
192 name: "Sharks Collection",
193 description: "This collection represents Sharks Virtual Commemorative Tickets.",
194 externalURL: MetadataViews.ExternalURL("https://sharks-nft.onflow.org"),
195 squareImage: media,
196 bannerImage: media,
197 socials: {
198 "twitter": MetadataViews.ExternalURL("https://twitter.com/sharks")
199 }
200 )
201 case Type<MetadataViews.EVMBridgedMetadata>():
202 // Implementing this view gives the project control over how the bridged NFT is represented as an ERC721
203 // when bridged to EVM on Flow via the public infrastructure bridge.
204
205 // Compose the contract-level URI. In this case, the contract metadata is located on some HTTP host,
206 // but it could be IPFS, S3, a data URL containing the JSON directly, etc.
207 return MetadataViews.EVMBridgedMetadata(
208 name: "Sharks",
209 symbol: "SHRK",
210 uri: MetadataViews.URI(
211 baseURI: nil, // setting baseURI as nil sets the given value as the uri field value
212 value: "https://sharks-nft.onflow.org/contract-metadata.json"
213 )
214 )
215 }
216 return nil
217 }
218
219 /// Resource that an admin or something similar would own to be
220 /// able to mint new NFTs
221 ///
222 access(all) resource NFTMinter {
223
224 /// mintNFT mints a new NFT with a new ID
225 /// and returns it to the calling context
226 access(all) fun mintNFT(
227 glink: String,
228 gbatch: UInt32,
229 glimit: UInt16,
230 gsequence: UInt16
231 ): @Sharks.NFT {
232
233 let tokenID = (UInt64(gbatch) << 32) | (UInt64(glimit) << 16) | UInt64(gsequence)
234
235 let metadata: {String: AnyStruct} = {}
236 let currentBlock = getCurrentBlock()
237 metadata["mintedBlock"] = currentBlock.height
238 metadata["mintedTime"] = currentBlock.timestamp
239
240 // this piece of metadata will be used to show embedding rarity into a trait
241 metadata["foo"] = "bar"
242
243 // create a new NFT
244 var newNFT <- create NFT(
245 initID: tokenID,
246 initlink: glink,
247 initbatch: gbatch,
248 initsequence: gsequence,
249 initlimit: glimit
250 )
251 Sharks.totalSupply = Sharks.totalSupply + 1
252 return <-newNFT
253 }
254 }
255
256 init() {
257 self.CollectionStoragePath = /storage/SharksCollection
258 self.CollectionPublicPath = /public/SharksCollection
259 self.MinterStoragePath = /storage/SharksMinter
260 self.totalSupply = 0
261
262 let collection <- create Collection()
263 self.account.storage.save(<-collection, to: self.CollectionStoragePath)
264
265 let collectionCap = self.account.capabilities.storage.issue<&Sharks.Collection>(self.CollectionStoragePath)
266 self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
267
268 let minter <- create NFTMinter()
269 self.account.storage.save(<-minter, to: self.MinterStoragePath)
270 }
271}
272