Smart Contract

KrikeyAINFT

A.a8d493db1bb4df56.KrikeyAINFT

Deployed

2h ago
Mar 01, 2026, 02:22:31 PM UTC

Dependents

0 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2import FungibleToken from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4
5/**
6 * This contract defines the structure and behaviour of Solarpups NFT assets.
7 * By using the KrikeyAINFT contract, assets can be registered in the AssetRegistry
8 * so that NFTs, belonging to that asset can be minted. Assets and NFT tokens can
9 * also be locked by this contract.
10 */
11pub contract KrikeyAINFT: NonFungibleToken {
12
13    pub let KrikeyAINFTPublicPath:   PublicPath
14    pub let KrikeyAINFTPrivatePath:  PrivatePath
15    pub let KrikeyAINFTStoragePath:  StoragePath
16    pub let CollectionStoragePath: StoragePath
17    pub let AssetRegistryStoragePath: StoragePath
18    pub let MinterFactoryStoragePath: StoragePath
19
20    pub event ContractInitialized()
21    pub event Withdraw(id: UInt64, from: Address?)
22    pub event Deposit(id: UInt64, to: Address?)
23    pub event MintAsset(id: UInt64, assetId: String)
24    pub event BurnAsset(id: UInt64, assetId: String)
25    pub event CollectionDeleted(from: Address?)
26
27    pub var totalSupply:  UInt64
28    access(self) let assets:       {String: Asset}
29
30    // Common interface for the NFT data.
31    pub resource interface TokenDataAware {
32        pub let data: TokenData
33    }
34
35    /**
36     * This resource represents a specific Solarpups NFT which can be
37     * minted and transferred. Each NFT belongs to an asset id and has
38     * an edition information. In addition to that each NFT can have other
39     * NFTs which makes it composable.
40     */
41    pub resource NFT: NonFungibleToken.INFT, TokenDataAware, MetadataViews.Resolver {
42        pub let id: UInt64
43        pub let data: TokenData
44        access(self) let items: @{String:{TokenDataAware, NonFungibleToken.INFT}}
45
46        init(id: UInt64, data: TokenData, items: @{String:{TokenDataAware, NonFungibleToken.INFT}}) {
47            self.id = id
48            self.data = data
49            self.items <- items
50        }
51
52        destroy() {
53          emit BurnAsset(id: self.id, assetId: self.data.assetId)
54          destroy self.items
55        }
56
57        pub fun getViews(): [Type] {
58            return [
59                Type<MetadataViews.Display>(),
60                Type<MetadataViews.Royalties>(),
61                Type<MetadataViews.Editions>(),
62                Type<MetadataViews.ExternalURL>(),
63                Type<MetadataViews.NFTCollectionData>(),
64                Type<MetadataViews.NFTCollectionDisplay>(),
65                Type<MetadataViews.Serial>()
66            ]
67        }
68
69        pub fun resolveView(_ view: Type): AnyStruct? {
70            let asset = KrikeyAINFT.getAsset(assetId: self.data.assetId)
71            let url = "https://cdn.krikeyapp.com/nft_web/nft_images/".concat(self.data.assetId).concat(".png")
72            switch view {
73                case Type<MetadataViews.Display>():
74                    return MetadataViews.Display(
75                        name: "Solarpups NFT",
76                        description: "The world's most adorable and sensitive pup.",
77                        thumbnail: MetadataViews.HTTPFile(
78                            url: url,
79                        )
80                    )
81                case Type<MetadataViews.Editions>():
82                    // There is no max number of NFTs that can be minted from this contract
83                    // so the max edition field value is set to nil
84                    let editionInfo = MetadataViews.Edition(name: "Solarpups NFT Edition", number: self.data.edition as! UInt64, max: nil)
85                    let editionList: [MetadataViews.Edition] = [editionInfo]
86                    return MetadataViews.Editions(
87                        editionList
88                    )
89                case Type<MetadataViews.Serial>():
90                    return MetadataViews.Serial(
91                        self.id
92                    )
93                case Type<MetadataViews.Royalties>():
94                    let RECEIVER_PATH = /public/flowTokenReceiver
95                    // Address Hardcoded for testing
96                    var royaltyReceiver = getAccount(0xff338e9d95c0bb8c).getCapability<&{FungibleToken.Receiver}>(RECEIVER_PATH)
97                    let royalty = MetadataViews.Royalty(receiver: royaltyReceiver, cut: asset!.royalty, description: "Solarpups Krikey Creator Royalty")
98                    return MetadataViews.Royalties(
99                        [royalty]
100                    )
101                case Type<MetadataViews.ExternalURL>():
102                    return MetadataViews.ExternalURL(url)
103                case Type<MetadataViews.NFTCollectionData>():
104                    return MetadataViews.NFTCollectionData(
105                        storagePath: KrikeyAINFT.KrikeyAINFTStoragePath,
106                        publicPath: KrikeyAINFT.KrikeyAINFTPublicPath,
107                        providerPath: KrikeyAINFT.KrikeyAINFTPrivatePath,
108                        publicCollection: Type<&KrikeyAINFT.Collection{KrikeyAINFT.CollectionPublic}>(),
109                        publicLinkedType: Type<&KrikeyAINFT.Collection{KrikeyAINFT.CollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(),
110                        providerLinkedType: Type<&KrikeyAINFT.Collection{KrikeyAINFT.CollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(),
111                        createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
112                            return <-KrikeyAINFT.createEmptyCollection()
113                        })
114                    )
115                case Type<MetadataViews.NFTCollectionDisplay>():
116                    let media= MetadataViews.Media(
117                        file: MetadataViews.HTTPFile(
118                            url: "https://cdn.krikeyapp.com/web/assets/img/solar-pups/logo.png"
119                        ),
120                        mediaType: "image/png"
121                    )
122                    return MetadataViews.NFTCollectionDisplay(
123                        name: "The Krikey Solarpups Collection",
124                        description: "The world's most adorable and sensitive pups.",
125                        externalURL: MetadataViews.ExternalURL("https://www.solarpups.com/marketplace"),
126                        squareImage: media,
127                        bannerImage: media,
128                        socials: {
129                            "discord": MetadataViews.ExternalURL("https://discord.com/invite/krikey"),
130                            "facebook": MetadataViews.ExternalURL("https://www.facebook.com/krikeyappAR/"),
131                            "twitter": MetadataViews.ExternalURL("https://twitter.com/SolarPupsNFTs"),
132                            "instagram": MetadataViews.ExternalURL("https://www.instagram.com/krikeyapp/?hl=en"),
133                            "youtube": MetadataViews.ExternalURL("https://www.youtube.com/channel/UCdTV4cmkQwWgaZ89ITMO-bg")
134                        }
135                    )
136            }
137            return nil
138        }
139    }
140
141    /**
142     * The data of a NFT token. The asset id references to the asset in the
143     * asset registry which holds all the information the NFT is about.
144     */
145    pub struct TokenData {
146      pub let assetId: String
147      pub let edition: UInt16
148
149      init(assetId: String, edition: UInt16) {
150        self.assetId = assetId
151        self.edition = edition
152      }
153    }
154
155    /**
156     * This resource is used to register an asset in order to mint NFT tokens of it.
157     * The asset registry manages the supply of the asset and is also able to lock it.
158     */
159    pub resource AssetRegistry {
160
161      pub fun store(asset: Asset) {
162          pre { KrikeyAINFT.assets[asset.assetId] == nil: "asset id already registered" }
163
164          KrikeyAINFT.assets[asset.assetId] = asset
165      }
166
167      access(contract) fun setMaxSupply(assetId: String) {
168        pre { KrikeyAINFT.assets[assetId] != nil: "asset not found" }
169        KrikeyAINFT.assets[assetId]!.setMaxSupply()
170      }
171
172    }
173
174    /**
175     * This structure defines all the information an asset has. The content
176     * attribute is a IPFS link to a data structure which contains all
177     * the data the NFT asset is about.
178     *
179     */
180    pub struct Asset {
181        pub let assetId: String
182        pub let creators: {Address:UFix64}
183        pub var content: String
184        pub let royalty: UFix64
185        pub let supply: Supply
186
187        access(contract) fun setMaxSupply() {
188            self.supply.setMax(supply: 1)
189        }
190
191        access(contract) fun setCurSupply(supply: UInt16) {
192            self.supply.setCur(supply: supply)
193        }
194
195        init(creators: {Address:UFix64}, assetId: String, content: String) {
196            pre {
197                creators.length > 0: "no address found"
198            }
199
200            var sum:UFix64 = 0.0
201            for value in creators.values {
202                sum = sum + value
203            }
204            assert(sum == 1.0, message: "invalid creator shares")
205
206            self.creators = creators
207            self.assetId  = assetId
208            self.content  = content
209            self.royalty  = 0.05
210            self.supply   = Supply(max: 1)
211        }
212    }
213
214    /**
215     * This structure defines all information about the asset supply.
216     */
217    pub struct Supply {
218        pub var max: UInt16
219        pub var cur: UInt16
220
221        access(contract) fun setMax(supply: UInt16) {
222            pre {
223                supply <= self.max: "supply must be lower or equal than current max supply"
224                supply >= self.cur: "supply must be greater or equal than current supply"
225            }
226            self.max = supply
227        }
228
229        access(contract) fun setCur(supply: UInt16) {
230            pre {
231                supply <= self.max: "max supply limit reached"
232                supply > self.cur: "supply must be greater than current supply"
233            }
234            self.cur = supply
235        }
236
237        init(max: UInt16) {
238            self.max = max
239            self.cur = 0
240        }
241    }
242
243    /**
244     * This resource is used by an account to collect Solarpups NFTs.
245     */
246    pub resource Collection: CollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
247        pub var ownedNFTs:   @{UInt64: NonFungibleToken.NFT}
248        pub var ownedAssets: {String: {UInt16:UInt64}}
249
250        init () {
251            self.ownedNFTs <- {}
252            self.ownedAssets = {}
253        }
254
255        pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
256            let token <- (self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")) as! @KrikeyAINFT.NFT
257            self.ownedAssets[token.data.assetId]?.remove(key: token.data.edition)
258            if (self.ownedAssets[token.data.assetId]?.length == 0) {
259                self.ownedAssets.remove(key: token.data.assetId)
260            }
261
262            if (self.owner?.address != nil) {
263                emit Withdraw(id: token.id, from: self.owner?.address!)
264            }
265            return <-token
266        }
267
268        pub fun batchWithdraw(ids: [UInt64]): @NonFungibleToken.Collection {
269            var batchCollection <- create Collection()
270            for id in ids {
271                batchCollection.deposit(token: <-self.withdraw(withdrawID: id))
272            }
273            return <-batchCollection
274        }
275
276        pub fun deposit(token: @NonFungibleToken.NFT) {
277            let token <- token as! @KrikeyAINFT.NFT
278            let id: UInt64 = token.id
279
280            if (self.ownedAssets[token.data.assetId] == nil) {
281                self.ownedAssets[token.data.assetId] = {}
282            }
283            self.ownedAssets[token.data.assetId]!.insert(key: token.data.edition, token.id)
284
285            let oldToken <- self.ownedNFTs[id] <- token
286            if (self.owner?.address != nil) {
287                emit Deposit(id: id, to: self.owner?.address!)
288            }
289            destroy oldToken
290        }
291
292        pub fun batchDeposit(tokens: @NonFungibleToken.Collection) {
293            for key in tokens.getIDs() {
294                self.deposit(token: <-tokens.withdraw(withdrawID: key))
295            }
296            destroy tokens
297        }
298
299        pub fun getIDs(): [UInt64] {
300            return self.ownedNFTs.keys
301        }
302
303        pub fun getAssetIDs(): [String] {
304            return self.ownedAssets.keys
305        }
306
307        pub fun getTokenIDs(assetId: String): [UInt64] {
308            return (self.ownedAssets[assetId] ?? {}).values
309        }
310
311        pub fun getEditions(assetId: String): {UInt16:UInt64} {
312            return self.ownedAssets[assetId] ?? {}
313        }
314
315        pub fun getOwnedAssets(): {String: {UInt16:UInt64}} {
316            return self.ownedAssets
317        }
318
319        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
320            pre {
321                self.ownedNFTs[id] != nil: "this NFT is nil"
322            }
323            let ref = &self.ownedNFTs[id] as &NonFungibleToken.NFT?
324            return ref! as! &NonFungibleToken.NFT
325        }
326
327        pub fun borrowKrikeyAINFT(id: UInt64): &KrikeyAINFT.NFT {
328            pre {
329                self.ownedNFTs[id] != nil: "this NFT is nil"
330            }
331            let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
332            let solarpupsNFT = nft as! &KrikeyAINFT.NFT
333            return solarpupsNFT as &KrikeyAINFT.NFT
334        }
335
336        pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
337            let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
338            let solarpupsNFT = nft as! &KrikeyAINFT.NFT
339            return solarpupsNFT as &AnyResource{MetadataViews.Resolver}
340        }
341
342        destroy() {
343            destroy self.ownedNFTs
344            self.ownedAssets = {}
345            if (self.owner?.address != nil) {
346                emit CollectionDeleted(from: self.owner?.address!)
347            }
348        }
349    }
350
351    pub fun createEmptyCollection(): @NonFungibleToken.Collection {
352        return <- create Collection()
353    }
354
355    // This is the interface that users can cast their KrikeyAINFT Collection as
356    // to allow others to deposit KrikeyAINFTs into their Collection. It also allows for reading
357    // the details of KrikeyAINFTs in the Collection.
358    pub resource interface CollectionPublic {
359        pub fun getIDs(): [UInt64]
360        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
361        pub fun getAssetIDs(): [String]
362        pub fun deposit(token: @NonFungibleToken.NFT)
363        pub fun batchDeposit(tokens: @NonFungibleToken.Collection)
364        pub fun getTokenIDs(assetId: String): [UInt64]
365        pub fun getEditions(assetId: String): {UInt16:UInt64}
366        pub fun getOwnedAssets(): {String: {UInt16:UInt64}}
367        pub fun borrowKrikeyAINFT(id: UInt64): &NonFungibleToken.NFT? {
368            post {
369                (result == nil) || (result?.id == id):
370                    "Cannot borrow KrikeyAINFT reference: the ID of the returned reference is incorrect"
371            }
372        }
373        pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver}
374    }
375
376    pub resource MinterFactory {
377        pub fun createMinter(): @Minter {
378            return <- create Minter()
379        }
380    }
381
382    // This resource is used to mint Solarpups NFTs.
383	pub resource Minter {
384
385		pub fun mint(assetId: String): @NonFungibleToken.Collection {
386            pre {
387                KrikeyAINFT.assets[assetId] != nil: "asset not found"
388            }
389
390            let collection <- create Collection()
391            let supply = KrikeyAINFT.assets[assetId]!.supply
392
393            supply.setCur(supply: supply.cur + (1 as UInt16))
394
395            let data = TokenData(assetId: assetId, edition: supply.cur)
396            let token <- create NFT(id: KrikeyAINFT.totalSupply, data: data, items: <- {})
397                    collection.deposit(token: <- token)
398
399            KrikeyAINFT.totalSupply = KrikeyAINFT.totalSupply + (1 as UInt64)
400            emit MintAsset(id: KrikeyAINFT.totalSupply, assetId: assetId)
401            KrikeyAINFT.assets[assetId]!.setCurSupply(supply: supply.cur)
402            return <- collection
403		}
404	}
405
406	access(account) fun getAsset(assetId: String): &KrikeyAINFT.Asset? {
407	    pre { self.assets[assetId] != nil: "asset not found" }
408	    return &self.assets[assetId] as &KrikeyAINFT.Asset?
409	}
410
411	pub fun getAssetIds(): [String] {
412	    return self.assets.keys
413	}
414
415	init() {
416        self.totalSupply  = 0
417        self.assets       = {}
418
419        self.KrikeyAINFTPublicPath     = /public/KrikeyAINFTsProd03
420        self.KrikeyAINFTPrivatePath    = /private/KrikeyAINFTsProd03
421        self.KrikeyAINFTStoragePath    = /storage/KrikeyAINFTsProd03
422        self.CollectionStoragePath      = /storage/KrikeyAINFTsProd03
423        self.AssetRegistryStoragePath   = /storage/SolarpupsAssetRegistryProd03
424        self.MinterFactoryStoragePath   = /storage/SolarpupsMinterFactoryProd03
425
426        self.account.save(<- create AssetRegistry(), to: self.AssetRegistryStoragePath)
427        self.account.save(<- create MinterFactory(), to: self.MinterFactoryStoragePath)
428        self.account.save(<- create Collection(),    to: self.KrikeyAINFTStoragePath)
429
430        // create a public capability for the collection
431        self.account.link<&KrikeyAINFT.Collection{NonFungibleToken.CollectionPublic, KrikeyAINFT.CollectionPublic, MetadataViews.ResolverCollection}>(
432            self.KrikeyAINFTPublicPath,
433            target: self.KrikeyAINFTStoragePath
434        )
435
436        emit ContractInitialized()
437	}
438
439}
440