Smart Contract
ArenaBoyz
A.3a3a4f7d9eec9073.ArenaBoyz
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import MetadataViews from 0x1d7e57aa55817448
4
5pub contract ArenaBoyz: NonFungibleToken {
6
7 pub var totalSupply: UInt64
8 pub var customSupply: UInt64
9 pub var genesisSupply: UInt64
10 pub var commonSupply: UInt64
11
12 pub var totalBurned: UInt64
13 pub var customBurned: UInt64
14 pub var genesisBurned: UInt64
15 pub var commonBurned: UInt64
16
17 pub var metadataUrl: String
18
19 pub event ContractInitialized()
20 pub event Withdraw(id: UInt64, from: Address?)
21 pub event Deposit(id: UInt64, to: Address?)
22 pub event Mint(id: UInt64, type: UInt8)
23 pub event Burn(id: UInt64, type: UInt8)
24 pub event MinterCreated()
25
26 pub let CollectionStoragePath: StoragePath
27 pub let CollectionPublicPath: PublicPath
28 pub let AdminStoragePath: StoragePath
29 pub let MinterStoragePath: StoragePath
30
31 pub enum NFTType: UInt8 {
32 pub case custom
33 pub case genesis
34 pub case common
35 }
36
37 pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
38
39 pub let id: UInt64
40 pub let type: NFTType
41
42 init(
43 id: UInt64,
44 type: NFTType,
45 ) {
46 self.id = id
47 self.type = type
48 }
49
50 destroy () {
51 ArenaBoyz.totalBurned = ArenaBoyz.totalBurned + UInt64(1)
52 switch self.type {
53 case NFTType.custom:
54 ArenaBoyz.customBurned = ArenaBoyz.customBurned + UInt64(1)
55 emit Burn(id: self.id, type: UInt8(0))
56 case NFTType.genesis:
57 ArenaBoyz.genesisBurned = ArenaBoyz.genesisBurned + UInt64(1)
58 emit Burn(id: self.id, type: UInt8(1))
59
60 case NFTType.common:
61 ArenaBoyz.commonBurned = ArenaBoyz.commonBurned + UInt64(1)
62 emit Burn(id: self.id, type: UInt8(2))
63
64 }
65
66 }
67
68 pub fun getViews(): [Type] {
69 return [
70 Type<MetadataViews.ExternalURL>(),
71 Type<MetadataViews.Display>(),
72 Type<MetadataViews.Royalties>(),
73 Type<MetadataViews.NFTCollectionData>(),
74 Type<MetadataViews.NFTCollectionDisplay>()
75 ]
76 }
77
78 pub fun resolveView(_ view: Type): AnyStruct? {
79 switch view {
80 case Type<MetadataViews.ExternalURL>():
81 return MetadataViews.ExternalURL(
82 url: ArenaBoyz.metadataUrl.concat("heroes/").concat(self.id.toString())
83 )
84 case Type<MetadataViews.Display>():
85 return MetadataViews.Display(
86 name: ("ArenaBoyz #").concat(self.id.toString()),
87 description: "A Superhero capable of doing battle in the ArenaBoyz Game!",
88 thumbnail: MetadataViews.HTTPFile(
89 url: ArenaBoyz.metadataUrl.concat("heroes/i/").concat(self.id.toString()).concat(".png")
90 )
91 )
92 case Type<MetadataViews.Royalties>():
93 let royalties : [MetadataViews.Royalty] = []
94 royalties.append(MetadataViews.Royalty(recepient: ArenaBoyz.account.getCapability<&{FungibleToken.Receiver}>(MetadataViews.getRoyaltyReceiverPublicPath()), cut: UFix64(0.10), description: "Crypthulhu royalties"))
95 return MetadataViews.Royalties(cutInfos: royalties)
96
97 case Type<MetadataViews.NFTCollectionData>():
98 return MetadataViews.NFTCollectionData(
99 storagePath: ArenaBoyz.CollectionStoragePath,
100 publicPath: ArenaBoyz.CollectionPublicPath,
101 providerPath: /private/abCollection,
102 publicCollection: Type<&ArenaBoyz.Collection{ArenaBoyz.CollectionPublic}>(),
103 publicLinkedType: Type<&ArenaBoyz.Collection{ArenaBoyz.CollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(),
104 providerLinkedType: Type<&ArenaBoyz.Collection{ArenaBoyz.CollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(),
105 createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
106 return <-ArenaBoyz.createEmptyCollection()
107 })
108 )
109 case Type<MetadataViews.NFTCollectionDisplay>():
110 return MetadataViews.NFTCollectionDisplay(
111 name: "ArenaBoyz",
112 description: "ArenaBoyz is a ...",
113 externalURL: MetadataViews.ExternalURL("https://"),
114 squareImage: MetadataViews.Media(
115 file: MetadataViews.HTTPFile(url: ArenaBoyz.metadataUrl.concat("heroes/collection_image.png")),
116 mediaType: "image/png"
117 ),
118 bannerImage: MetadataViews.Media(
119 file: MetadataViews.HTTPFile(url: ArenaBoyz.metadataUrl.concat("heroes/collection_banner.png")),
120 mediaType: "image/png"
121 ),
122 socials: {
123 "discord": MetadataViews.ExternalURL("https://"),
124 "twitter": MetadataViews.ExternalURL("https://")
125 }
126 )
127
128 }
129
130 return nil
131 }
132 }
133
134 pub resource interface CollectionPublic {
135 pub fun deposit(token: @NonFungibleToken.NFT)
136 pub fun getIDs(): [UInt64]
137 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
138 pub fun borrowArenaBoyz(id: UInt64): &ArenaBoyz.NFT? {
139 post {
140 (result == nil) || (result?.id == id):
141 "Cannot borrow ArenaBoyz reference: the ID of the returned reference is incorrect"
142 }
143 }
144 }
145
146 pub resource Collection: CollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
147 // dictionary of NFT conforming tokens
148 // NFT is a resource type with an `UInt64` ID field
149 pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
150
151 init () {
152 self.ownedNFTs <- {}
153 }
154
155 // withdraw removes an NFT from the collection and moves it to the caller
156 pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
157
158 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
159
160 emit Withdraw(id: token.id, from: self.owner?.address)
161
162 return <-token
163 }
164
165 // deposit takes a NFT and adds it to the collections dictionary
166 // and adds the ID to the id array
167 pub fun deposit(token: @NonFungibleToken.NFT) {
168 let token <- token as! @ArenaBoyz.NFT
169
170 let id: UInt64 = token.id
171
172 // add the new token to the dictionary which removes the old one
173 let oldToken <- self.ownedNFTs[id] <- token
174
175 emit Deposit(id: id, to: self.owner?.address)
176
177 destroy oldToken
178 }
179
180 // getIDs returns an array of the IDs that are in the collection
181 pub fun getIDs(): [UInt64] {
182 return self.ownedNFTs.keys
183 }
184
185 // borrowNFT gets a reference to an NFT in the collection
186 // so that the caller can read its metadata and call its methods
187 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
188 return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
189 }
190
191 pub fun borrowArenaBoyz(id: UInt64): &ArenaBoyz.NFT? {
192 if self.ownedNFTs[id] != nil {
193 // Create an authorized reference to allow downcasting
194 let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
195 return ref as! &ArenaBoyz.NFT
196 }
197
198 return nil
199 }
200
201 pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
202 let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
203 let abNft = nft as! &ArenaBoyz.NFT
204 return abNft as &AnyResource{MetadataViews.Resolver}
205 }
206
207 destroy() {
208 destroy self.ownedNFTs
209 }
210 }
211
212 // public function that anyone can call to create a new empty collection
213 pub fun createEmptyCollection(): @NonFungibleToken.Collection {
214 return <- create Collection()
215 }
216
217 // Resource that an admin or something similar would own to be
218 // able to mint new NFTs
219 //
220 pub resource NFTMinter {
221 // range if possible
222 pub fun getNextCustomID(): UInt64 {
223 var nextId = ArenaBoyz.customSupply + UInt64(1)
224 return (nextId <= 1000) ? nextId : self.getNextCommonID()
225 }
226
227 // Determine the next available ID for genesis NFTs and use the reserved
228 // range if possible
229 pub fun getNextGenesisID(): UInt64 {
230 var nextId = UInt64(1000) + ArenaBoyz.genesisSupply + UInt64(1)
231 return (nextId <= 11000) ? nextId : panic("Cannot mint more than 10000 genesis NFTs")
232 }
233
234 // Determine the next available ID for the rest of NFTs and take into
235 // account the custom NFTs that have been minted outside of the reserved
236 // range
237 pub fun getNextCommonID(): UInt64 {
238 var customIdOverflow = Int256(ArenaBoyz.customSupply) - Int256(1000)
239 customIdOverflow = customIdOverflow > 0 ? customIdOverflow : 0
240 return 11000 + ArenaBoyz.commonSupply + UInt64(customIdOverflow) + UInt64(1)
241 }
242
243 pub fun mintCustomNFT(
244 recipient: &Collection{NonFungibleToken.CollectionPublic},
245 ) {
246 var nextId = self.getNextCustomID()
247
248 // Update supply counters
249 ArenaBoyz.customSupply = ArenaBoyz.customSupply + UInt64(1)
250 ArenaBoyz.totalSupply = ArenaBoyz.totalSupply + UInt64(1)
251
252 self.mint(
253 recipient: recipient,
254 id: nextId,
255 type: ArenaBoyz.NFTType.custom
256 )
257 }
258
259 pub fun mintGenesisNFT(
260 recipient: &Collection{NonFungibleToken.CollectionPublic},
261 ) {
262 // Determine the next available ID
263 var nextId = self.getNextGenesisID()
264
265 // Update supply counters
266 ArenaBoyz.genesisSupply = ArenaBoyz.genesisSupply + UInt64(1)
267 ArenaBoyz.totalSupply = ArenaBoyz.totalSupply + UInt64(1)
268
269 self.mint(
270 recipient: recipient,
271 id: nextId,
272 type: ArenaBoyz.NFTType.genesis
273 )
274 }
275
276 pub fun mintNFT(
277 recipient: &Collection{NonFungibleToken.CollectionPublic},
278 ) {
279 // Determine the next available ID
280 var nextId = self.getNextCommonID()
281
282 // Update supply counters
283 ArenaBoyz.commonSupply = ArenaBoyz.commonSupply + UInt64(1)
284 ArenaBoyz.totalSupply = ArenaBoyz.totalSupply + UInt64(1)
285
286 self.mint(
287 recipient: recipient,
288 id: nextId,
289 type: ArenaBoyz.NFTType.common
290 )
291 }
292
293
294
295 priv fun mint(
296 recipient: &Collection{NonFungibleToken.CollectionPublic},
297 id: UInt64,
298 type: ArenaBoyz.NFTType,
299 ) {
300 // create a new NFT
301 var newNFT <- create NFT(id: id, type: type)
302 switch newNFT.type {
303 case NFTType.custom:
304 emit Mint(id: id, type: UInt8(0))
305 case NFTType.genesis:
306 emit Mint(id: id, type: UInt8(1))
307 case NFTType.common:
308 emit Mint(id: id, type: UInt8(2))
309 }
310 // deposit it in the recipient's account using their reference
311 recipient.deposit(token: <-newNFT)
312 }
313 }
314
315 pub resource Admin {
316
317
318 pub fun setMetadataUrl(url: String) {
319 ArenaBoyz.metadataUrl = url
320 }
321
322 pub fun createNFTMinter(): @NFTMinter {
323 emit MinterCreated()
324 return <-create NFTMinter()
325 }
326 }
327
328 init() {
329 // Initialize supply counters
330 self.totalSupply = 0
331 self.customSupply = 0
332 self.genesisSupply = 0
333 self.commonSupply = 0
334
335 // Initialize burned counters
336 self.totalBurned = 0
337 self.customBurned = 0
338 self.genesisBurned = 0
339 self.commonBurned = 0
340
341 self.metadataUrl = "https://"
342
343 // Set the named paths
344 self.CollectionStoragePath = /storage/abCollection
345 self.CollectionPublicPath = /public/abCollection
346 self.AdminStoragePath = /storage/abAdmin
347 self.MinterStoragePath = /storage/abMinter
348
349 // Create a Collection resource and save it to storage
350 let collection <- create Collection()
351 self.account.save(<-collection, to: self.CollectionStoragePath)
352
353 // create a public capability for the collection
354 self.account.link<&ArenaBoyz.Collection{NonFungibleToken.CollectionPublic, CollectionPublic}>(
355 self.CollectionPublicPath,
356 target: self.CollectionStoragePath
357 )
358
359 let admin <- create Admin()
360 let minter <- admin.createNFTMinter()
361 self.account.save(<-admin, to: self.AdminStoragePath)
362 self.account.save(<-minter, to: self.MinterStoragePath)
363
364 emit ContractInitialized()
365 }
366}
367