Smart Contract

Cryptoys

A.ca63ce22f0d6bdba.Cryptoys

Deployed

1w ago
Feb 18, 2026, 08:59:29 AM UTC

Dependents

8 imports
1import ViewResolver from 0x1d7e57aa55817448
2import NonFungibleToken from 0x1d7e57aa55817448
3import MetadataViews from 0x1d7e57aa55817448
4import ICryptoys from 0xca63ce22f0d6bdba
5import CryptoysMetadataView from 0xca63ce22f0d6bdba
6
7// Cryptoys
8// The jam.
9//
10access(all)
11contract Cryptoys: NonFungibleToken, ICryptoys{ 
12	// Events
13	//
14	access(all)
15	event ContractInitialized()
16	
17	access(all)
18	event Withdraw(id: UInt64, from: Address?)
19	
20	access(all)
21	event Deposit(id: UInt64, to: Address?)
22	
23	access(all)
24	event AddedToBucket(owner: Address, key: String, id: UInt64, uuid: UInt64)
25	
26	access(all)
27	event WithdrawnFromBucket(owner: Address, key: String, id: UInt64, uuid: UInt64)
28	
29	access(all)
30	event Minted(id: UInt64, uuid: UInt64, metadata:{ String: String}, royalties: [Royalty])
31	
32	access(all)
33	event RoyaltyUpserted(name: String, royalty: Royalty)
34	
35	access(all)
36	event DisplayUpdated(id: UInt64, image: String, video: String)
37	
38	// Named Paths
39	//
40	access(all)
41	let CollectionStoragePath: StoragePath
42	
43	access(all)
44	let CollectionPublicPath: PublicPath
45	
46	access(all)
47	let AdminStoragePath: StoragePath
48	
49	// totalSupply
50	// The total number of Cryptoys that have been minted
51	//
52	access(all)
53	var totalSupply: UInt64
54	
55    access(all) view fun getContractViews(resourceType: Type?): [Type] {
56        return [   ]
57    }
58
59    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
60        return nil
61    }
62    
63	// NFT
64	// A Cryptoy as an NFT
65	//
66	access(all)
67	resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver, ICryptoys.INFT{ 
68		// The token's ID
69		access(all)
70		let id: UInt64
71		
72		access(account)
73		let metadata:{ String: String}
74		
75		access(account)
76		let royalties: [String]
77		
78		access(account)
79		let bucket: @{String:{ UInt64:{ ICryptoys.INFT}}}
80		
81		init(id: UInt64, metadata:{ String: String}, royalties: [String]?){ 
82			pre{ 
83				metadata.length != 0:
84					"Cryptoy failed to initialize: metadata cannot be empty"
85			}
86			self.id = id
87			self.metadata = metadata
88			self.royalties = royalties ?? []
89			self.bucket <-{} 
90		}
91		
92		access(all)
93		fun getMetadata():{ String: String}{ 
94			return self.metadata
95		}
96		
97		access(all)
98		fun getDisplay(): Display{ 
99			var image = ""
100			var video = ""
101			if Cryptoys.display[self.id] != nil{ 
102				image = (Cryptoys.display[self.id]!).image
103				video = (Cryptoys.display[self.id]!).video
104			}
105			if image == ""{ 
106				image = self.metadata["image"] ?? ""
107			}
108			if video == ""{ 
109				video = self.metadata["video"] ?? ""
110			}
111			return Cryptoys.Display(image: image, video: video)
112		}
113		
114		access(all)
115		fun getRoyalties(): [Royalty]{ 
116			var nftRoyalties: [Royalty] = []
117			for royalty in self.royalties{ 
118				var nftRoyalty: Royalty? = Cryptoys.royalties[royalty]
119				if nftRoyalty != nil{ 
120					nftRoyalties.append(nftRoyalty!)
121				}
122			}
123			return nftRoyalties
124		}
125		
126		access(account)
127		fun withdrawBucketItem(_ key: String, _ itemUuid: UInt64): @{ICryptoys.INFT} { 
128			let resources = self.borrowBucketResourcesByKey(key) ?? panic("withdrawBucketItem() failed to find resources by key: ".concat(key))
129			let nft <- resources.remove(key: itemUuid) ?? panic("withdrawBucketItem() failed to find NFT in bucket with id: ".concat(itemUuid.toString()))
130			emit WithdrawnFromBucket(owner: (self.owner!).address, key: key, id: self.id, uuid: itemUuid)
131			return <-nft
132		}
133		
134		access(all)
135		fun addToBucket(_ key: String, _ nft: @{ICryptoys.INFT}){ 
136            let nftUuid: UInt64 = nft.uuid
137
138            let resources =  self.borrowBucketResourcesByKey(key)
139            if resources == nil {
140                let bucket = self.borrowBucket()
141                bucket[key] <-! {nftUuid: <- nft}
142            } else {
143                let exitingBucket = resources!
144                exitingBucket[nftUuid] <-! nft
145            }
146
147            emit AddedToBucket(owner: self.owner!.address, key: key, id: self.id, uuid: nftUuid)
148		}
149		
150		access(all)
151        fun borrowBucketResourcesByKey(_ key: String): auth(Insert, Remove) &{UInt64: {ICryptoys.INFT}}? {
152            return &self.bucket[key] 
153        }
154		
155		access(all)
156		fun borrowBucket(): auth(Insert, Remove) &{String: {UInt64: {ICryptoys.INFT}}} {
157			return &self.bucket
158		}
159		
160		access(all)
161		fun getBucketKeys(): [String]{ 
162			return *self.borrowBucket().keys
163		}
164		
165		access(all)
166		fun getBucketResourceIdsByKey(_ key: String): [UInt64]{ 
167			let resources = self.borrowBucketResourcesByKey(key) ?? panic("getBucketResourceIdsByKey() failed to find resources by key: ".concat(key))
168			return *resources.keys
169		}
170		
171		access(all)
172		fun borrowBucketItem(_ key: String, _ itemUuid: UInt64):  &{ICryptoys.INFT} {
173			let resources = self.borrowBucketResourcesByKey(key) ?? panic("borrowBucketItem() failed to find resources by key: ".concat(key))
174			let bucketItem = resources[itemUuid] ?? panic("borrowBucketItem() failed to borrow resource with id: ".concat(itemUuid.toString()))
175			return bucketItem as! &Cryptoys.NFT
176		}
177
178        access(all) 
179        view fun getViews(): [Type] {
180            return [Type<MetadataViews.Display>(), Type<CryptoysMetadataView.Cryptoy>()]
181        }
182		
183		access(all)
184		fun resolveView(_ view: Type): AnyStruct?{ 
185			var display = self.getDisplay()
186			switch view{ 
187				case Type<MetadataViews.Display>():
188					return MetadataViews.Display(name: self.metadata["type"] ?? "", description: self.metadata["description"] ?? "", thumbnail: MetadataViews.HTTPFile(url: display.image))
189				case Type<CryptoysMetadataView.Cryptoy>():
190					return CryptoysMetadataView.Cryptoy(name: self.metadata["type"], description: self.metadata["description"], image: display.image, coreImage: self.metadata["coreImage"], video: display.video, platformId: self.metadata["platformId"], category: self.metadata["category"], type: self.metadata["type"], skin: self.metadata["skin"], tier: self.metadata["tier"], rarity: self.metadata["rarity"], edition: self.metadata["edition"], series: self.metadata["series"], legionId: self.metadata["legionId"], creator: self.metadata["creator"], packaging: self.metadata["packaging"], termsUrl: self.metadata["termsUrl"])  
191			}
192			return nil
193		}
194		
195		access(all)
196		fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
197			return <-create Collection()
198		}
199	}
200	
201	// Collection
202	// A collection of Cryptoy NFTs owned by an account
203	//
204	access(all)
205	resource Collection: ICryptoys.CryptoysCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection{ 
206		// dictionary of NFT conforming tokens
207		// NFT is a resource type with an `UInt64` ID field
208		//
209		access(all)
210		var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
211		
212		// withdraw
213		// Removes an NFT from the collection and moves it to the caller
214		//
215		access(NonFungibleToken.Withdraw)
216		fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{ 
217			let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("withdraw() failed: missing NFT with id: ".concat(withdrawID.toString()))
218			emit Withdraw(id: token.id, from: self.owner?.address)
219			return <-token
220		}
221		
222		// deposit
223		// Takes a NFT and adds it to the collections dictionary
224		// and adds the ID to the id array
225		//
226		access(all)
227		fun deposit(token: @{NonFungibleToken.NFT}){ 
228			let token <- token as! @Cryptoys.NFT
229			let id: UInt64 = token.id
230			
231			// add the new token to the dictionary which removes the old one
232			let oldToken <- self.ownedNFTs[id] <- token
233			emit Deposit(id: id, to: self.owner?.address)
234			destroy oldToken
235		}
236		
237		// getIDs
238		// Returns an array of the IDs that are in the collection
239		//
240		access(all)
241		view fun getIDs(): [UInt64]{ 
242			return self.ownedNFTs.keys
243		}
244		
245		// borrowNFT
246		// Gets a reference to an NFT in the collection
247		// so that the caller can read its metadata and call its methods
248		//
249		access(all)
250		view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{ 
251			return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
252		}
253		
254		access(all)
255		fun borrowCryptoy(id: UInt64): &Cryptoys.NFT{ 
256			if self.ownedNFTs[id] != nil{ 
257				let ref = (&self.ownedNFTs[id] as auth(Remove) &{NonFungibleToken.NFT}?)!
258				return ref as! &Cryptoys.NFT
259			} else{ 
260				return panic("borrowCryptoy() failed: cryptoy not found with id: ".concat(id.toString()))
261			}
262		}
263		
264		access(all)
265		fun borrowBucketItem(_ id: UInt64, _ key: String, _ itemUuid: UInt64): &{ICryptoys.INFT}{ 
266			if self.ownedNFTs[id] == nil{ 
267				return panic("borrowBucketItem() failed: parent cryptoy not found with id: ".concat(id.toString()))
268			}
269			let ref = (&self.ownedNFTs[id] as auth(Remove) &{NonFungibleToken.NFT}?)!
270			let cryptoyRef = ref as! &Cryptoys.NFT
271			return cryptoyRef.borrowBucketItem(key, itemUuid)
272		}
273		
274		access(all)
275		view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{ 
276			let nft = (&self.ownedNFTs[id] as auth(Remove) &{NonFungibleToken.NFT}?)!
277			let cryptoyNFT = nft as! &Cryptoys.NFT
278			return cryptoyNFT
279		}
280		
281		access(all)
282		fun getRoyalties(id: UInt64): [Royalty]{ 
283			let ref = (&self.ownedNFTs[id] as auth(Remove) &{NonFungibleToken.NFT}?)!
284			return (ref as! &NFT).getRoyalties()
285		}
286		
287		access(NonFungibleToken.Withdraw)
288		fun withdrawBucketItem(parentId: UInt64, key: String, itemUuid: UInt64): @{ICryptoys.INFT}{ 
289			if self.ownedNFTs[parentId] == nil{ 
290				panic("withdrawBucketItem() failed: parent cryptoy not found with id: ".concat(parentId.toString()))
291			}
292			let nftRef = (&self.ownedNFTs[parentId] as auth(Remove) &{NonFungibleToken.NFT}?)!
293			let cryptoyRef = nftRef as! &Cryptoys.NFT
294			return <-cryptoyRef.withdrawBucketItem(key, itemUuid)
295		}
296		
297		access(all)
298		view fun getSupportedNFTTypes():{ Type: Bool}{ 
299			panic("implement me")
300		}
301		
302		access(all)
303		view fun isSupportedNFTType(type: Type): Bool{ 
304			panic("implement me")
305		}
306		
307		access(all)
308		fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
309			return <-create Collection()
310		}
311		
312		// destructor
313		// initializer
314		//
315		init(){ 
316			self.ownedNFTs <-{} 
317		}
318	}
319	
320	access(all)
321	struct Royalty{ 
322		access(all)
323		let name: String
324		
325		access(all)
326		let address: Address
327		
328		access(all)
329		let fee: UFix64
330		
331		init(name: String, address: Address, fee: UFix64){ 
332			self.name = name
333			self.address = address
334			self.fee = fee
335		}
336	}
337	
338	access(all)
339	struct Display{ 
340		access(all)
341		let image: String
342		
343		access(all)
344		let video: String
345		
346		init(image: String, video: String){ 
347			self.image = image
348			self.video = video
349		}
350	}
351	
352	// royalties
353	// royalties for each INFT
354	//
355	access(account)
356	let royalties:{ String: Royalty}
357	
358	// display for each composed NFTs
359	//
360	access(account)
361	let display:{ UInt64: Display}
362	
363	// createEmptyCollection
364	// public function that anyone can call to create a new empty collection
365	//
366	access(all)
367	fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{ 
368		return <-create Collection()
369	}
370	
371	// Admin
372	// Resource that an admin or something similar would own to be
373	// able to mint new NFTs and update royalties
374	//
375	access(all)
376	resource Admin{ 
377		
378		// mintNFT
379		// Mints a new NFT with a new ID
380		// and deposit it in the recipients collection using their collection reference
381		//
382		access(all)
383		fun mintNFT(recipient: Capability<&{NonFungibleToken.CollectionPublic}>, metadata:{ String: String}, royaltyNames: [String]?): UInt64{ 
384			var nftRoyalties: [Royalty] = []
385			if royaltyNames != nil{ 
386				for royalty in royaltyNames!{ 
387					var nftRoyalty = Cryptoys.royalties[royalty]
388					if nftRoyalty == nil{ 
389						panic("mintNFT() failed: royalty not found: ".concat(royalty))
390					}
391					nftRoyalties.append(nftRoyalty!)
392				}
393			}
394			let nftId = Cryptoys.totalSupply
395			
396			// deposit it in the recipient's account using their reference
397			var nft <- create Cryptoys.NFT(id: nftId, metadata: metadata, royalties: royaltyNames)
398			emit Minted(id: nft.id, uuid: nft.uuid, metadata: nft.getMetadata(), royalties: nftRoyalties)
399			Cryptoys.totalSupply = Cryptoys.totalSupply + 1
400			(recipient.borrow()!).deposit(token: <-nft)
401			return nftId
402		}
403		
404		access(all)
405		fun upsertRoyalty(royalty: Royalty){ 
406			Cryptoys.royalties[royalty.name] = royalty
407			emit RoyaltyUpserted(name: royalty.name, royalty: royalty)
408		}
409		
410		access(all)
411		fun updateDisplay(cryptoy: &Cryptoys.NFT, display: Display?){ 
412			if display != nil{ 
413				Cryptoys.display.insert(key: cryptoy.id, display!)
414			} else{ 
415				Cryptoys.display.remove(key: cryptoy.id)
416			}
417			var display = cryptoy.getDisplay()
418			emit DisplayUpdated(id: cryptoy.id, image: display.image, video: display.video)
419		}
420		
421		access(all)
422		fun getRoyalties():{ String: Royalty}{ 
423			return Cryptoys.royalties
424		}
425	}
426	
427	// initializer
428	//
429	init(){ 
430		// Set our named paths
431		self.AdminStoragePath = /storage/cryptoysAdmin
432		self.CollectionStoragePath = /storage/cryptoysCollection
433		self.CollectionPublicPath = /public/cryptoysCollection
434        
435		// Initialize the total supply
436		self.totalSupply = 0
437		self.royalties ={} 
438		self.display ={} 
439		
440		// store an empty NFT Collection in account storage
441		self.account.storage.save(<-self.createEmptyCollection(nftType: Type<@Collection>()), to: self.CollectionStoragePath)
442		
443		// publish a reference to the Collection in storage
444		var capability_1 = self.account.capabilities.storage.issue<&{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, ICryptoys.CryptoysCollectionPublic}>(self.CollectionStoragePath)
445		self.account.capabilities.publish(capability_1, at: self.CollectionPublicPath)
446		
447		// Create a admin resource and save it to storage
448		let admin <- create Admin()
449		self.account.storage.save(<-admin, to: self.AdminStoragePath)
450		emit ContractInitialized()
451	}
452}
453