Smart Contract

SolarpupsNFT

A.a8d493db1bb4df56.SolarpupsNFT

Deployed

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