Smart Contract

Mneme

A.67c649092019e032.Mneme

Valid From

143,326,096

Deployed

3d ago
Feb 25, 2026, 02:37:57 AM UTC

Dependents

0 imports
1// MADE BY: Noah_Overflow
2
3// This contract is for Mneme, a proof of support platform
4// on Flow. 
5
6// Mneme (Μνήμη) is one of the original three Muses in pre-Homeric Greek mythology.
7// Before the more famous Nine Muses were standardized by Hesiod (daughters of Zeus and Mnemosyne)
8// there were three older Muses:
9
10// Melete – Muse of Practice/Contemplation
11// Mneme – Muse of Memory
12// Aoide – Muse of Song
13
14// Mneme herself was seen as the preserver of knowledge and inspiration, responsible for the ability of poets, orators 
15// and artists to recall what came before and give it form. She represents the thread that links art to time and culture 
16// Literally, the memory of humanity encoded in creative work.
17
18import NonFungibleToken from 0x1d7e57aa55817448
19import FungibleToken from 0xf233dcee88fe0abe
20import FlowToken from 0x1654653399040a61 
21import ViewResolver from 0x1d7e57aa55817448
22import MetadataViews from 0x1d7e57aa55817448
23import Pistis from 0x67c649092019e032
24import RandomConsumer from 0x45caec600164c9e6
25import Xorshift128plus from 0x45caec600164c9e6
26import Burner from 0xf233dcee88fe0abe
27import FlowTransactionScheduler from 0xe467b9dd11fa00df
28// import "CrossVMMetadataViews"
29// import "EVM"
30
31access(all) 
32contract Mneme: NonFungibleToken {
33    // -----------------------------------------------------------------------
34    // Mneme contract-level fields.
35    // These contain actual values that are stored in the smart contract.
36    // -----------------------------------------------------------------------
37    // Dictionary to hold general collection information
38    access(self) let collectionInfo: {String: AnyStruct}  
39    access(self) var artistOriginals: {Address: [Int64]}
40    access(self) var artistEditions: {Address: [Int64]}
41    access(self) var totalOriginals: UInt64
42    access(self) var totalEditions: UInt64
43    access(self) var totalCertificates: UInt64
44    access(all) let address: Address
45    /// The RandomConsumer.Consumer resource used to request & fulfill randomness
46    access(self) let consumer: @RandomConsumer.Consumer
47    // -----------------------------------------------------------------------
48    // Mneme account paths
49    // -----------------------------------------------------------------------
50	access(all) let CollectionStoragePath: StoragePath
51	access(all) let CollectionPublicPath: PublicPath
52    /// Path where the minter should be stored
53    /// The standard paths for the collection are stored in the collection resource type
54    access(all) let ArtDropStoragePath: StoragePath
55    access(all) let ArtDropPublicPath: PublicPath
56    access(all) let AdministratorStoragePath: StoragePath
57    access(all) let ArtistStoragePath: StoragePath
58    access(all) let HandlerStoragePath: StoragePath
59    // -----------------------------------------------------------------------
60
61    // Mneme Entitlements
62    // ----------------------------------------------------------------------- 
63    access(all) entitlement Admin
64    access(all) entitlement AddArtist
65    access(all) entitlement Editions
66
67    /// Event to show when an NFT is minted
68    access(all) event Minted(
69        type: String,
70        id: UInt64,
71        uuid: UInt64,
72        name: String,
73        description: String
74    )
75    access(all) event OriginalCreated(artistAddress: Address, originalId: UInt64)
76    access(all) event EditionCreated(artistAddress: Address, editionId: UInt64)
77    access(all) event MultipliersUpdated(artistAddress: Address, editionId: UInt64)
78    access(all) event RNGReceiptPopped(id: UInt64, multiplier: UFix64)
79    // -----------------------------------------------------------------------
80    // Mneme contract-level Composite Type definitions
81    // -----------------------------------------------------------------------
82    // These are just *definitions* for Types that this contract
83    // and other accounts can use. These definitions do not contain
84    // actual stored values, but an instance (or object) of one of these Types
85    // can be created by this contract that contains stored values.
86    // -----------------------------------------------------------------------
87    access(all) resource Original: Pistis.Pool {
88        access(all) let id: UInt64
89        access(all) var name: String
90        access(all) var description: String
91        access(all) var thumbnail: String
92        access(all) let artistAddress: Address
93        access(all) var editions: {UInt64: UInt64}
94        access(all) var totalMinted: Int64
95        access(all) var price: UFix64
96        access(all) var metadata: {String: String}
97        access(all) var extra: {String: AnyStruct}
98        // Pistis fields
99        access(all) var vaultsDict: @{Type: {FungibleToken.Vault}}
100        access(all) var vaultReceiverPath: {Type: PublicPath}
101
102        init (
103            id: UInt64,
104            name: String,
105            description: String,
106            thumbnail: String,
107            artistAddress: Address,
108            price: UFix64,
109            metadata: {String: String},
110        ) {
111            self.id = id
112            self.name = name
113            self.description = description
114            self.thumbnail = thumbnail
115            self.artistAddress = artistAddress
116            self.editions = {}
117            self.totalMinted = 0
118            self.price = price
119            self.vaultsDict <- {}
120            self.vaultReceiverPath = {}
121            self.metadata = metadata
122            self.extra = {}
123        }
124
125        access(all) fun addEdition(editionId: UInt64) {
126            self.editions[editionId] = 0
127        } 
128        access(all) fun increaseTotalMinted() {
129            self.totalMinted = self.totalMinted + 1
130        }
131        // helper funct to return metadata without ref
132        access(all) fun getMetadata(): {String: String} {
133            return self.metadata
134        }
135    } 
136    // Edition resource represents the Art's metadata 
137    // and its rewards rules
138    // Attachment
139
140    access(all) resource Edition {
141        access(all) let originalId: UInt64
142        access(all) let id: UInt64
143        access(all) var name: String
144        access(all) var price: UFix64
145        access(all) var type: String
146        access(all) var story: String
147        access(all) var dimensions: {String: String}
148        access(all) var reprintLimit: Int64
149        access(all) let artistAddress: Address
150        access(all) var totalMinted: Int64
151        access(all) var profitSplit: {Address: UFix64} // the address and the percentage of the profit
152                                                       // ArtDrop, Original Painting, Charity, CopyrightsHolder/Reseller, communityPool. (Reseller Address is dynamic) 
153                                                       // the total of the percentages must be 100%
154                                                       // distribute after claim/point of no return.
155
156        access(all) var multipliers: [UFix64]
157        // Map of multiplier to the id of the CertificateNFT
158        access(all) var usedMultipliers: {UFix64: UInt64}
159        access(all) let rngReceipts: @{UInt64: Receipt}
160        access(all) let revealReceipts: @{UInt64: FlowTransactionScheduler.ScheduledTransaction}
161
162        init(
163            originalId: UInt64,
164            id: UInt64,
165            name: String,
166            price: UFix64,
167            type: String,
168            story: String,
169            dimensions: {String: String},
170            reprintLimit: Int64,
171            artistAddress: Address,
172            multipliers: [UFix64],
173            profitSplit: {Address: UFix64}) {
174
175            self.originalId = originalId
176            self.name = name
177            self.price = price
178            self.id = id
179            self.type = type
180            self.story = story
181            self.dimensions = dimensions
182            self.reprintLimit = reprintLimit
183            self.artistAddress = artistAddress
184            self.multipliers = multipliers
185            self.usedMultipliers = {}
186            self.totalMinted = 0
187            self.rngReceipts <- {}
188            self.revealReceipts <- {}
189            self.profitSplit = profitSplit
190
191            // The number of multipliers should be equal to
192            // the reprint limit
193            if multipliers.length != Int(reprintLimit) {
194                panic("The number of multipliers should be equal to the reprint limit")
195            }
196
197        }
198        // helper funct to return dimension without ref
199        access(all) fun getDimensions(): {String: String} {
200            return self.dimensions
201        }
202
203        access(Editions) fun editEdition(
204            name: String?,
205            price: UFix64?,
206            type: String?,
207            story: String?,
208            dimensions: {String: String}?,
209            reprintLimit: Int64?) {
210                pre {
211                    self.totalMinted == 0: "This edition has already been minted"
212                }
213            self.name = name ?? self.name
214            self.price = price ?? self.price
215            self.type = type ?? self.type
216            self.story = story ?? self.story
217            self.dimensions = dimensions ?? self.dimensions
218            self.reprintLimit = reprintLimit ?? self.reprintLimit
219        }
220        // update multipliers
221        access(Editions) fun updateMultipliers(multipliers: [UFix64]) {
222            // The number of multipliers should be equal to
223            // the reprint limit
224            if multipliers.length != Int(self.reprintLimit) {
225                panic("The number of multipliers should be equal to the reprint limit")
226            }
227            self.multipliers = multipliers
228        }
229        /// mintNFT mints a new NFT with a new ID
230        /// and returns it to the calling context
231        access(Editions) 
232        fun mintCertificateNFT(thumbnail: String) {
233            // Get the original path
234            let originalStorageIdentifier = "\(Mneme.account.address)_ArtDrop_Original_\(self.artistAddress)_\(self.originalId)"
235            let originalStoragePath = StoragePath(identifier: originalStorageIdentifier)!
236            let originalRef = Mneme.account.storage.borrow< &Mneme.Original>(from: originalStoragePath)!
237            if originalRef == nil {
238                panic("Original not found")
239            }
240            // Check if the edition has a reprint limit
241            // If it does, check if the total minted count has reached the reprint limit
242            if self.reprintLimit != 0 {
243                if self.totalMinted >= self.reprintLimit {
244                    panic("This edition has reached the reprint limit")
245                }
246            }            
247            let metadata: {String: AnyStruct} = {}
248            let currentBlock = getCurrentBlock()
249            metadata["mintedBlock"] = currentBlock.height
250            metadata["mintedTime"] = currentBlock.timestamp
251            // this piece of metadata will be used to show embedding rarity into a trait
252            metadata["foo"] = "bar"
253            // [2x. 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x]
254
255            // increase the total minted count for this Edition
256            self.totalMinted = self.totalMinted + 1
257            // increase the total certificates count
258            Mneme.totalCertificates = Mneme.totalCertificates + 1
259            // request randomness
260            let request <- Mneme.consumer.requestRandomness()
261            let receipt <- create Receipt(id: Mneme.totalCertificates, request: <-request)
262            // Safe receipt linked to this Edition resource
263            self.rngReceipts[Mneme.totalCertificates] <-! receipt
264
265            // create a new NFT
266            var newNFT <- create CertificateNFT(
267                id: Mneme.totalCertificates,
268                serial: UInt64(self.totalMinted),
269                name: self.name,
270                description: self.story,
271                thumbnail: thumbnail,
272                metadata: metadata,
273                artistAddress: self.artistAddress
274            )
275            // Schedule a transaction to reveal the Receipt
276            let future = getCurrentBlock().timestamp + 5.0
277            let priority = FlowTransactionScheduler.Priority.Medium
278            let executionEffort: UInt64 = 1000
279            let estimate = FlowTransactionScheduler.estimate(
280                data: "",
281                timestamp: future,
282                priority: priority,
283                executionEffort: executionEffort
284            )
285            assert(
286                estimate.timestamp != nil || priority == FlowTransactionScheduler.Priority.Medium,
287                message: estimate.error ?? "estimation failed"
288            )
289            if Mneme.account.storage.borrow<&AnyResource>(from: Mneme.HandlerStoragePath) == nil {
290                let handler <- create Handler()
291                Mneme.account.storage.save(<-handler, to: Mneme.HandlerStoragePath)
292            }
293            // Withdraw FLOW fees from this contract's account vault
294            let vaultRef = Mneme.account.storage
295                .borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
296                ?? panic("missing FlowToken vault on contract account")
297            let fees <- vaultRef.withdraw(amount: estimate.flowFee ?? 0.0) as! @FlowToken.Vault
298            let handlerCap = Mneme.account.capabilities.storage
299                .issue<auth(FlowTransactionScheduler.Execute) &{FlowTransactionScheduler.TransactionHandler}>(Mneme.HandlerStoragePath)
300
301            let revealReceipt <- FlowTransactionScheduler.schedule(
302                handlerCap: handlerCap,
303                data: "",
304                timestamp: future,
305                priority: priority,
306                executionEffort: executionEffort,
307                fees: <-fees
308            )
309            // deposit fees to stake
310       
311            self.revealReceipts[Mneme.totalCertificates] <-! revealReceipt
312            // Increase total minted count for the Original
313            originalRef.increaseTotalMinted()
314            // emit the Minted event
315            emit Minted(type: newNFT.getType().identifier,
316                        id: newNFT.id,
317                        uuid: newNFT.uuid,
318                        name: newNFT.name,
319                        description: newNFT.description
320                        )
321            // send the new NFT to the artist's collection
322            let artistAccount = getAccount(self.artistAddress)
323            let artistCollection = artistAccount.capabilities.borrow<&{NonFungibleToken.Receiver}>(Mneme.CollectionPublicPath)!
324            artistCollection.deposit(token: <- newNFT)
325        }
326        // Pop the rng receipt
327        access(Editions) fun popRNGReceipt(id: UInt64) {
328            pre {
329                self.rngReceipts[id] != nil: "RNG receipt not found"
330            }
331            // get the receipt
332            let receipt <- self.rngReceipts.remove(key: id)!
333            // get a random number within the reprint limit
334            // of this edition
335            let randomNumber = Mneme._randomWithinRange(request: <- receipt.popRequest(), max: Int(self.reprintLimit))
336            // use this random number to get the multiplier from the multipliers array
337            let multiplier = self.multipliers[randomNumber]
338            // assign the multiplier to the id of the receipt/NFT
339            self.usedMultipliers[multiplier] = receipt.id
340            // return the used receipt
341            self.rngReceipts[id] <-! receipt
342
343            emit RNGReceiptPopped(id: id, multiplier: multiplier)
344        }
345    }
346    
347
348    /// We choose the name NFT here, but this type can have any name now
349    /// because the interface does not require it to have a specific name any more
350    access(all) resource CertificateNFT: NonFungibleToken.NFT, Pistis.Pool {
351        access(all) let id: UInt64
352        access(all) let serial: UInt64
353        access(all) let artistAddress: Address
354        access(all) var vaultsDict: @{Type: {FungibleToken.Vault}}
355        access(all) var vaultReceiverPath: {Type: PublicPath}
356        access(all) let name: String
357        access(all) let description: String
358        access(all) let thumbnail: String 
359        access(all) let royalties: MetadataViews.Royalty
360        access(all) let metadata: {String: AnyStruct}
361        
362        init(
363            id: UInt64,
364            serial: UInt64,
365            name: String,
366            description: String,
367            thumbnail: String,
368            metadata: {String: AnyStruct},
369            artistAddress: Address
370        ) {
371            self.id = id
372            self.serial = serial
373            self.artistAddress = artistAddress
374            self.name = name
375            self.description = description
376            self.thumbnail = thumbnail
377            self.royalties = MetadataViews.Royalty(
378                    receiver: getAccount(Mneme.account.address).capabilities.get<&FlowToken.Vault>(/public/flowTokenReceiver),
379                    cut: 0.5,
380                    description: "The deployer gets 5% of every secondary sale."
381                )
382            self.metadata = metadata
383            self.vaultsDict <- {}
384            self.vaultReceiverPath = {}
385        }
386
387        view access(all)
388        fun getTraits(): {String: AnyStruct} {
389            let metadata: {String: AnyStruct} = {"name": self.name}
390            metadata["editionID"] = self.id
391            metadata["editionName"] = self.name
392            metadata["editionDescription"] = self.description
393            metadata["editionThumbnail"] = self.thumbnail
394            metadata["editionMetadata"] = self.metadata
395            metadata["editionArtistAddress"] = self.artistAddress
396            return metadata
397        }
398        /// createEmptyCollection creates an empty Collection
399        /// and returns it to the caller so that they can own NFTs
400        /// @{NonFungibleToken.Collection}
401        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
402            return <-Mneme.createEmptyCollection(nftType: Type<@Mneme.CertificateNFT>())
403
404            
405        }
406
407        access(all) view fun getViews(): [Type] {
408            return [
409                Type<MetadataViews.Display>(),
410                Type<MetadataViews.Royalties>(),
411                Type<MetadataViews.Editions>(),
412                Type<MetadataViews.ExternalURL>(),
413                Type<MetadataViews.NFTCollectionData>(),
414                Type<MetadataViews.NFTCollectionDisplay>(),
415                Type<MetadataViews.Serial>(),
416                Type<MetadataViews.Traits>(),
417                Type<MetadataViews.EVMBridgedMetadata>()
418            ]
419        }
420
421        access(all) fun resolveView(_ view: Type): AnyStruct? {
422            switch view {
423                case Type<MetadataViews.Display>():
424                    return MetadataViews.Display(
425                        name: self.name,
426                        description: self.description,
427                        thumbnail: MetadataViews.HTTPFile(
428                            url: self.thumbnail
429                        )
430                    )
431                case Type<MetadataViews.Editions>():
432                    // There is no max number of NFTs that can be minted from this contract
433                    // so the max edition field value is set to nil
434                    let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil)
435                    let editionList: [MetadataViews.Edition] = [editionInfo]
436                    return MetadataViews.Editions(
437                        editionList
438                    )
439                case Type<MetadataViews.Serial>():
440                    return MetadataViews.Serial(
441                        self.serial
442                    )
443                case Type<MetadataViews.Royalties>():
444                    return MetadataViews.Royalties(
445                        [self.royalties]
446                    )
447                case Type<MetadataViews.ExternalURL>():
448                    return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString()))
449                case Type<MetadataViews.NFTCollectionData>():
450                    return Mneme.resolveContractView(resourceType: Type<@Mneme.CertificateNFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
451                case Type<MetadataViews.NFTCollectionDisplay>():
452                    return Mneme.resolveContractView(resourceType: Type<@Mneme.CertificateNFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>())
453                case Type<MetadataViews.Traits>():
454                    // exclude mintedTime and foo to show other uses of Traits
455                    let excludedTraits = self.getTraits() 
456                    return MetadataViews.dictToTraits(dict: self.getTraits(), excludedNames: [])
457                case Type<MetadataViews.EVMBridgedMetadata>():
458                    // Implementing this view gives the project control over how the bridged NFT is represented as an
459                    // ERC721 when bridged to EVM on Flow via the public infrastructure bridge.
460                    // NOTE: If your NFT is a cross-VM NFT, meaning you control both your Cadence & EVM contracts and
461                    //      registered your custom association with the VM bridge, it's recommended you use the 
462                    //      CrossVMMetadata.EVMBytesMetadata view to define and pass metadata as EVMBytes into your
463                    //      EVM contract at the time of bridging into EVM. For more information about cross-VM NFTs,
464                    //      see FLIP-318: https://github.com/onflow/flips/issues/318
465
466                    // Get the contract-level name and symbol values
467                    let contractLevel = Mneme.resolveContractView(
468                            resourceType: nil,
469                            viewType: Type<MetadataViews.EVMBridgedMetadata>()
470                        ) as! MetadataViews.EVMBridgedMetadata?
471
472                    if let contractMetadata = contractLevel {
473                        // Compose the token-level URI based on a base URI and the token ID, pointing to a JSON file. This
474                        // would be a file you've uploaded and are hosting somewhere - in this case HTTP, but this could be
475                        // IPFS, S3, a data URL containing the JSON directly, etc.
476                        let baseURI = "https://example-nft.onflow.org/token-metadata/"
477                        let uriValue = self.id.toString().concat(".json")
478
479                        return MetadataViews.EVMBridgedMetadata(
480                            name: contractMetadata.name,
481                            symbol: contractMetadata.symbol,
482                            uri: MetadataViews.URI(
483                                baseURI: baseURI, // defining baseURI results in a concatenation of baseURI and value
484                                value: self.id.toString().concat(".json")
485                            )
486                        )
487                    } else {
488                        return nil
489                    }
490/*                 case Type<CrossVMMetadataViews.EVMPointer>():
491                    // This view is intended for NFT projects with corresponding NFT implementations in both Cadence and
492                    // EVM. Resolving EVMPointer indicates the associated EVM implementation. Fully validating the
493                    // cross-VM association would involve inspecting the associated EVM contract and ensuring that
494                    // contract also points to the resolved Cadence type and contract address. For more information
495                    // about cross-VM NFTs, see FLIP-318: https://github.com/onflow/flips/issues/318
496
497                    return Mneme.resolveContractView(resourceType: self.getType(), viewType: view)
498                case Type<CrossVMMetadataViews.EVMBytesMetadata>():
499                    // This view is intended for Cadence-native NFTs with corresponding ERC721 implementations. By
500                    // resolving, you're able to pass arbitrary metadata into your EVM contract whenever an NFT is
501                    // bridged which can be useful for Cadence NFTs with dynamic metadata values.
502                    // See FLIP-318 for more information about cross-VM NFTs: https://github.com/onflow/flips/issues/318
503
504                    // Here we encoded the EVMBridgedMetadata URI and encode the string as EVM bytes, but you could pass any
505                    // Cadence values that can be abi encoded and decode them in your EVM contract as you wish. Within
506                    // your EVM contract, you can abi decode the bytes and update metadata in your ERC721 contract as
507                    // you see fit.
508                    let bridgedMetadata = (self.resolveView(Type<MetadataViews.EVMBridgedMetadata>()) as! MetadataViews.EVMBridgedMetadata?)!
509                    let uri = bridgedMetadata.uri.uri()
510                    let encodedURI = EVM.encodeABI([uri])
511                    let evmBytes = EVM.EVMBytes(value: encodedURI)
512                    return CrossVMMetadataViews.EVMBytesMetadata(bytes: evmBytes) */
513            }
514            return nil
515        }
516    }
517
518    access(all) resource Collection: NonFungibleToken.Collection, Pistis.Loyalty {
519        /// dictionary of NFT conforming tokens
520        /// NFT is a resource type with an `UInt64` ID field
521        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
522        access(all) var loyaltyPoints: {Address: UFix64}
523        access(all) var supportedArtists: {Address: [Int64]}
524
525        init () {
526            self.ownedNFTs <- {}
527            self.loyaltyPoints = {}
528            self.loyaltyPoints[Mneme.account.address] = 0.0
529            self.supportedArtists = {}
530        }
531
532        // Function to calculate loyalty points
533        access(all) view fun calculateLoyaltyPoints(artistAddress: Address): Int {
534        // get the array of editions
535           let editions = self.supportedArtists[artistAddress]!
536           // loyalty points is the of editions multiplied by 10
537           let loyaltyPoints: Int = editions.length * 10
538
539
540           return loyaltyPoints
541        }
542
543        access(all) fun addVault(vaultType: Type, vault: @{FungibleToken.Vault}, id: UInt64, vaultReceiverPath: PublicPath) {
544            let nft <- self.ownedNFTs.remove(key: id) as! @Mneme.CertificateNFT
545            nft.addVault(vaultType: vaultType, vault: <- vault, vaultReceiverPath: vaultReceiverPath)
546            self.ownedNFTs[id] <-! nft 
547        } 
548
549        access(all) fun depositToVault(id: UInt64, vaultType: Type, vaultDeposit: @{FungibleToken.Vault}) {
550            let nft <- self.ownedNFTs.remove(key: id) as! @Mneme.CertificateNFT
551            nft.depositToVault(vaultType: vaultType, vaultDeposit: <- vaultDeposit)
552            self.ownedNFTs[id] <-! nft 
553        } 
554
555        // Withdraw from a vault
556        access(all) fun withdrawFromVault(id: UInt64, vaultType: Type) {
557            let nft <- self.ownedNFTs.remove(key: id) as! @Mneme.CertificateNFT
558            let newVault <- nft.withdrawFromVault(id: id, vaultType: vaultType)
559            let account = getAccount(self.owner!.address)
560            let vault <- newVault.remove(key: newVault.keys[0])!
561            let receiverRef = account.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)!
562            receiverRef.deposit(from: <- vault.withdraw(amount: vault.balance))
563            destroy newVault
564            destroy vault
565            self.ownedNFTs[id] <-! nft 
566        }
567
568        /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
569        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
570            let supportedTypes: {Type: Bool} = {}
571            supportedTypes[Type<@Mneme.CertificateNFT>()] = true
572            return supportedTypes
573        }
574
575        /// Returns whether or not the given type is accepted by the collection
576        /// A collection that can accept any type should just return true by default
577        access(all) view fun isSupportedNFTType(type: Type): Bool {
578            return type == Type<@Mneme.CertificateNFT>()
579        }
580
581        /// withdraw removes an NFT from the collection and moves it to the caller
582        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
583            let token <- self.ownedNFTs.remove(key: withdrawID)
584                ?? panic("Mneme.Collection.withdraw: Could not withdraw an NFT with ID "
585                        .concat(withdrawID.toString())
586                        .concat(". Check the submitted ID to make sure it is one that this collection owns."))
587
588            // Remove loyalty points from the collector
589            self.substractLoyalty(address: self.owner?.address!, loyaltyPoints: 10.0)
590            
591
592            return <-token
593        }
594
595        /// deposit takes a NFT and adds it to the collections dictionary
596        /// and adds the ID to the id array
597        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
598            let token <- token as! @Mneme.CertificateNFT
599            let id = token.id
600            let artistAddress = token.artistAddress
601
602            // add the new token to the dictionary which removes the old one
603            let oldToken <- self.ownedNFTs[token.id] <- token
604            if self.supportedArtists[artistAddress] == nil {
605                self.supportedArtists[artistAddress] = []
606            }
607            self.supportedArtists[artistAddress]!.append(Int64(id))
608
609            let loyaltyPoints = self.calculateLoyaltyPoints(artistAddress: artistAddress)
610            // Based on NFT's edition and other factors, add loyalty points to the collector
611            self.addLoyalty(address: self.owner?.address!, loyaltyPoints: UFix64(loyaltyPoints))
612            destroy oldToken
613
614            // This code is for testing purposes only
615            // Do not add to your contract unless you have a specific
616            // reason to want to emit the NFTUpdated event somewhere
617            // in your contract
618            let authTokenRef = (&self.ownedNFTs[id] as auth(NonFungibleToken.Update) &{NonFungibleToken.NFT}?)!
619            //authTokenRef.updateTransferDate(date: getCurrentBlock().timestamp)
620            Mneme.emitNFTUpdated(authTokenRef)
621        }
622
623        /// getIDs returns an array of the IDs that are in the collection
624        access(all) view fun getIDs(): [UInt64] {
625            return self.ownedNFTs.keys
626        }
627
628        /// Gets the amount of NFTs stored in the collection
629        access(all) view fun getLength(): Int {
630            return self.ownedNFTs.length
631        }
632
633        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
634            return &self.ownedNFTs[id]
635        }
636
637        /// Borrow the view resolver for the specified NFT ID
638        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
639            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
640                return nft as &{ViewResolver.Resolver}
641            }
642            return nil
643        }
644
645        /// createEmptyCollection creates an empty Collection of the same type
646        /// and returns it to the caller
647        /// @return A an empty collection of the same type
648        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
649            return <-Mneme.createEmptyCollection(nftType: Type<@Mneme.CertificateNFT>())
650        }
651    }
652    // -----------------------------------------------------------------------
653    // ArtDrop Scheduled Transaction Handler 
654    // -----------------------------------------------------------------------
655    // Struct for the scheduled transaction data
656    access(all) struct revealData {
657        access(all) let id: UInt64
658        access(all) let artistAddress: Address
659        access(all) let editionId: UInt64 
660
661        init(id: UInt64, artistAddress: Address, editionId: UInt64) {
662            self.id = id
663            self.artistAddress = artistAddress
664            self.editionId = editionId
665        }
666    }
667    /// Handler resource that implements the Scheduled Transaction interface
668    access(all) resource Handler: FlowTransactionScheduler.TransactionHandler {
669
670
671        access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) {
672            // Extract reveal data from transaction data
673            let revealData = data as! revealData? ?? panic("revealData data is required")
674            // Get a ref to the Edition resource 
675            // using the artistAddress and editionId
676            let storageIdentifier = "ArtDrop_Edition_\(revealData.artistAddress)_\(revealData.editionId)"
677            let storagePath = StoragePath(identifier: storageIdentifier)!
678            let editionRef = Mneme.account.storage.borrow<auth(Mneme.Editions) &Mneme.Edition>(from: storagePath)!
679            if editionRef == nil {
680                panic("Edition not found")
681            }
682            // get the rng receipts for the edition
683            // with this the revealData.id
684            let rngReceipt = editionRef.popRNGReceipt(id: revealData.id)
685
686
687
688        }
689    }
690    // -----------------------------------------------------------------------
691    // ArtDrop Receipt Resource
692    // -----------------------------------------------------------------------
693    /// The Receipt resource is used to store the associated randomness request. By listing the
694    /// RandomConsumer.RequestWrapper conformance, this resource inherits all the default implementations of the
695    /// interface. This is why the Receipt resource has access to the getRequestBlock() and popRequest() functions
696    /// without explicitly defining them.
697    ///
698    access(all) resource Receipt : RandomConsumer.RequestWrapper {
699        /// The associated randomness request which contains the block height at which the request was made
700        // The setID of the intended pack
701        access(all) let id: UInt64
702
703        /// and whether the request has been fulfilled.
704        access(all) var request: @RandomConsumer.Request?
705
706        init(id: UInt64, request: @RandomConsumer.Request) {
707            self.id = id
708            self.request <- request
709        }
710    }
711    /// Returns a random number between 0 and 1 using the RandomConsumer.Consumer resource contained in the contract.
712    /// For the purposes of this contract, a simple modulo operation could have been used though this is not the case
713    /// for all ranges. Using the Consumer.fulfillRandomInRange function ensures that we can get a random number
714    /// within any range without a risk of bias.
715    ///
716    access(self) 
717    fun _randomWithinRange(request: @RandomConsumer.Request, max: Int): UInt64 {
718        return self.consumer.fulfillRandomInRange(request: <-request, min: 0, max: UInt64(max))
719    }
720    // -----------------------------------------------------------------------
721    // Mneme public functions
722    // -----------------------------------------------------------------------
723    // Get all the Artists and their Editions
724    access(all) view fun getAllArtists(): {Address: [Int64]} {
725        return self.artistEditions
726    }
727    // Struct for Original metadata
728    access(all) struct OriginalMetadata {
729        access(all) let name: String
730        access(all) let description: String
731        access(all) let thumbnail: String
732        access(all) let metadata: {String: String}
733        init(name: String, description: String, thumbnail: String, metadata: {String: String}) {
734            self.name = name
735            self.description = description
736            self.thumbnail = thumbnail
737            self.metadata = metadata
738        }   
739    }
740    // Get all the Originals for an artist
741    access(all)  fun getOriginalMetadata(artistAddress: Address, originalId: UInt64): OriginalMetadata? {
742        pre {
743            self.artistOriginals[artistAddress] != nil: "This artist does not exist"
744        }
745        let storageIdentifier = "\(Mneme.address)_ArtDrop_Original_\(artistAddress)_\(originalId)"
746        let publicPath = PublicPath(identifier: storageIdentifier)!
747
748        let originalRef = Mneme.account.capabilities.borrow<&Mneme.Original>(publicPath)!
749        let originalMetadata = originalRef.getMetadata()
750        let metadata = OriginalMetadata(name: originalRef.name, description: originalRef.description, thumbnail: originalRef.thumbnail, metadata: originalMetadata)
751        return metadata
752    }
753    // Struct for Edition metadata
754    access(all) struct EditionMetadata {
755        access(all) let originalId: UInt64
756        access(all) let id: UInt64
757        access(all) var name: String
758        access(all) var price: UFix64
759        access(all) var type: String
760        access(all) var story: String
761        access(all) var dimensions: {String: String}
762        access(all) var reprintLimit: Int64
763        access(all) let artistAddress: Address
764        access(all) var totalMinted: Int64
765        init(originalId: UInt64, id: UInt64, name: String, price: UFix64, type: String, story: String, dimensions: {String: String}, reprintLimit: Int64, artistAddress: Address, totalMinted: Int64) {
766            self.originalId = originalId
767            self.id = id
768            self.name = name
769            self.price = price
770            self.type = type
771            self.story = story
772            self.dimensions = dimensions
773            self.reprintLimit = reprintLimit
774            self.artistAddress = artistAddress
775            self.totalMinted = totalMinted
776        }
777    }
778    // Get an Edition's metadata
779    // parameters: artistAddress: Address, editionId: UInt64
780    access(all)  fun getEditionMetadata(artistAddress: Address, editionId: UInt64): EditionMetadata? {
781        pre {
782            self.artistEditions[artistAddress] != nil: "This artist does not exist"
783        }
784        // Check if editionId exists in the array
785        let editionsArray = self.artistEditions[artistAddress]!
786        var editionExists = false
787        for edition in editionsArray {
788            if edition == Int64(editionId) {
789                editionExists = true
790                break
791            }
792        }
793        if !editionExists {
794            return nil
795        }
796        let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Edition_\(artistAddress)_\(editionId)"
797        let storagePath = StoragePath(identifier: storageIdentifier)!
798
799        let editionRef = Mneme.account.storage.borrow<&Mneme.Edition>(from: storagePath)!
800        let dimensions = editionRef.getDimensions()
801        let metadata = EditionMetadata(originalId: editionRef.originalId, id: editionRef.id, name: editionRef.name, price: editionRef.price, type: editionRef.type, story: editionRef.story, dimensions: dimensions, reprintLimit: editionRef.reprintLimit, artistAddress: editionRef.artistAddress, totalMinted: editionRef.totalMinted)
802        return metadata
803    }
804    /// createEmptyCollection creates an empty Collection for the specified NFT type
805    /// and returns it to the caller so that they can own NFTs
806    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
807        return <- create Collection()
808    }
809
810    /// Function that returns all the Metadata Views implemented by a Non Fungible Token
811    ///
812    /// @return An array of Types defining the implemented views. This value will be used by
813    ///         developers to know which parameter to pass to the resolveView() method.
814    ///
815    access(all) view fun getContractViews(resourceType: Type?): [Type] {
816        return [
817            Type<MetadataViews.NFTCollectionData>(),
818            Type<MetadataViews.NFTCollectionDisplay>(),
819            Type<MetadataViews.EVMBridgedMetadata>()
820        ]
821    }
822
823    /// Function that resolves a metadata view for this contract.
824    ///
825    /// @param view: The Type of the desired view.
826    /// @return A structure representing the requested view.
827    ///
828    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
829        switch viewType {
830            case Type<MetadataViews.NFTCollectionData>():
831                let collectionData = MetadataViews.NFTCollectionData(
832                    storagePath: self.CollectionStoragePath,
833                    publicPath: self.CollectionPublicPath,
834                    publicCollection: Type<&Mneme.Collection>(),
835                    publicLinkedType: Type<&Mneme.Collection>(),
836                    createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
837                        return <-Mneme.createEmptyCollection(nftType: Type<@Mneme.CertificateNFT>())
838                    })
839                )
840                return collectionData
841            case Type<MetadataViews.NFTCollectionDisplay>():
842                let media = MetadataViews.Media(
843                    file: MetadataViews.HTTPFile(
844                        url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
845                    ),
846                    mediaType: "image/svg+xml"
847                )
848                return MetadataViews.NFTCollectionDisplay(
849                    name: "The Example Collection",
850                    description: "This collection is used as an example to help you develop your next Flow NFT.",
851                    externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
852                    squareImage: media,
853                    bannerImage: media,
854                    socials: {
855                        "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
856                    }
857                )
858            case Type<MetadataViews.EVMBridgedMetadata>():
859                // Implementing this view gives the project control over how the bridged NFT is represented as an ERC721
860                // when bridged to EVM on Flow via the public infrastructure bridge.
861
862                // Compose the contract-level URI. In this case, the contract metadata is located on some HTTP host,
863                // but it could be IPFS, S3, a data URL containing the JSON directly, etc.
864                return MetadataViews.EVMBridgedMetadata(
865                    name: "Mneme",
866                    symbol: "XMPL",
867                    uri: MetadataViews.URI(
868                        baseURI: nil, // setting baseURI as nil sets the given value as the uri field value
869                        value: "https://example-nft.onflow.org/contract-metadata.json"
870                    )
871                )
872/*             case Type<CrossVMMetadataViews.EVMPointer>():
873                // This view is intended for NFT projects with corresponding NFT implementations in both Cadence and
874                // EVM. Resolving EVMPointer indicates the associated EVM implementation. Fully validating the
875                // cross-VM association would involve inspecting the associated EVM contract and ensuring that contract
876                // also points to the resolved Cadence type and contract address. For more information about cross-VM
877                // NFTs, see FLIP-318: https://github.com/onflow/flips/issues/318
878
879                // Assigning a dummy EVM address and deserializing. Implementations would want to declare the actual
880                // EVM address corresponding to their corresponding ERC721. If using a proxy in your EVM contracts, this
881                // address should be your proxy's address.
882                let evmContractAddress = EVM.addressFromString(
883                        "0x1234565789012345657890123456578901234565"
884                    )
885                // Since this NFT is distributed in Cadence, it's declared as Cadence-native
886                let nativeVM = CrossVMMetadataViews.VM.Cadence
887                return CrossVMMetadataViews.EVMPointer(
888                    cadenceType: Type<@Mneme.CertificateNFT>(),
889                    cadenceContractAddress: self.account.address,
890                    evmContractAddress: evmContractAddress,
891                    nativeVM: nativeVM
892                ) */
893        }
894        return nil
895    }
896
897    // Administrator resource
898    access(all) resource Administrator {
899        // Function to create a new Original resource
900        access(all) fun createOriginal(
901            name: String,
902            description: String,
903            thumbnail: String,
904            artistAddress: Address,
905            price: UFix64,
906            metadata: {String: String}
907        ) {
908            // increase the total originals count
909            Mneme.totalOriginals = Mneme.totalOriginals + 1
910            // make this path dynamic based on contract's address
911            let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Original_\(artistAddress)_\(Mneme.totalOriginals)"
912            let storagePath = StoragePath(identifier: storageIdentifier)!
913            let publicPath = PublicPath(identifier: storageIdentifier)!
914
915            // create a new original resource
916            let newOriginal <- create Original(id: Mneme.totalOriginals, name: name, description: description, thumbnail: thumbnail, artistAddress: artistAddress, price: price, metadata: metadata)
917            // save the new original to storage
918            Mneme.account.storage.save(<-newOriginal, to: storagePath)
919            // create a public capability for the original
920            let originalCap: Capability<&Mneme.Original> = Mneme.account.capabilities.storage.issue<&Mneme.Original>(storagePath)
921            Mneme.account.capabilities.publish(originalCap, at: publicPath)
922            // add the new original to the artist's originals
923            // add a new key to the artist's originals dictionary
924            if Mneme.artistOriginals[artistAddress] == nil {
925                Mneme.artistOriginals[artistAddress] = []
926            }
927            Mneme.artistOriginals[artistAddress]!.append(Int64(Mneme.totalOriginals))
928            // emit OriginalCreated event
929            emit OriginalCreated(artistAddress: artistAddress, originalId: Mneme.totalOriginals)
930        }
931        // Function to create a new Edition resource
932        access(all) fun createEdition(
933            originalId: UInt64,
934            name: String,
935            price: UFix64,
936            type: String,
937            story: String,
938            dimensions: {String: String},
939            reprintLimit: Int64,
940            artistAddress: Address,
941            multipliers: [UFix64],
942            profitSplit: {Address: UFix64}) {
943            if Mneme.artistEditions[artistAddress] == nil {
944                Mneme.artistEditions[artistAddress] = []
945            }
946            // increase the total editions count
947            Mneme.totalEditions = Mneme.totalEditions + 1
948            // make this path dynamic based on contract's address
949            let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Edition_\(artistAddress)_\(Mneme.totalEditions)"
950            let storagePath = StoragePath(identifier: storageIdentifier)!
951            let publicPath = PublicPath(identifier: storageIdentifier)!
952
953            // create a new edition resource
954            let newEdition <- create Edition(originalId: originalId, id: Mneme.totalEditions, name: name, price: price, type: type, story: story, dimensions: dimensions, reprintLimit: reprintLimit, artistAddress: artistAddress, multipliers: multipliers, profitSplit: profitSplit)
955            // add the new edition to the original's editions
956            // get the original path
957            let originalStorageIdentifier = "\(Mneme.account.address)_ArtDrop_Original_\(artistAddress)_\(originalId)"
958            let originalStoragePath = StoragePath(identifier: originalStorageIdentifier)!
959            let originalRef = Mneme.account.storage.borrow< &Mneme.Original>(from: originalStoragePath)!
960            if originalRef == nil {
961                panic("Original not found")
962            }
963            originalRef.addEdition(editionId: newEdition.id)
964
965            // save the new edition to storage
966            Mneme.account.storage.save(<- newEdition, to: storagePath)
967            // create a public capability for the edition
968            let editionCap: Capability<&Mneme.Edition> = Mneme.account.capabilities.storage.issue<&Mneme.Edition>(storagePath)
969            ////////
970            // DO WE NEED THIS PUBLIC CAP? //
971            ////////
972            // Mneme.account.capabilities.publish(editionCap, at: publicPath)
973            // add the new edition to the artist's editions
974            Mneme.artistEditions[artistAddress]!.append(Int64(Mneme.totalEditions))
975            // return <- newEdition
976
977            // publish an authorized capability to the 
978            // stored edition resource to the artist
979            let artistCap: Capability<auth(Mneme.Editions) &Mneme.Edition> = Mneme.account.capabilities.storage.issue<auth(Editions)  &Mneme.Edition>(storagePath)
980            Mneme.account.inbox.publish(artistCap, name: storageIdentifier, recipient: artistAddress)
981
982            // Emit an event to the artist
983            emit EditionCreated(artistAddress: artistAddress, editionId: Mneme.totalEditions)
984
985        }
986        // Function to update an edition's multipliers
987        access(all) fun updateMultipliers(
988            artistAddress: Address,
989            editionId: UInt64,
990            multipliers: [UFix64]) {
991            let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Edition_\(artistAddress)_\(Mneme.totalEditions)"
992            let editionRef = Mneme.account.storage.borrow<auth(Editions) &Mneme.Edition>(from: StoragePath(identifier: storageIdentifier)!)!
993            editionRef.updateMultipliers(multipliers: multipliers)
994            emit MultipliersUpdated(artistAddress: artistAddress, editionId: editionId)
995        }
996
997        // Function to mint a new certificate NFT
998        access(all) fun mintCertificateNFT(
999            artistAddress: Address,
1000            editionId: UInt64,
1001            thumbnail: String
1002        ) { 
1003            let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Edition_\(artistAddress)_\(editionId)"
1004            let editionRef = Mneme.account.storage.borrow<auth(Editions) &Mneme.Edition>(from: StoragePath(identifier: storageIdentifier)!)!
1005            if editionRef == nil {
1006                panic("Edition not found")
1007            }
1008            editionRef.mintCertificateNFT(thumbnail: thumbnail)
1009            // return <- newCertificateNFT  
1010        }
1011
1012    }
1013
1014
1015    init() {
1016        self.collectionInfo = {}
1017        self.artistOriginals = {}
1018        self.artistEditions = {}
1019        self.totalOriginals = 0
1020        self.totalCertificates = 0
1021        self.totalEditions = 0
1022        let identifier = "Mneme_\(self.account.address))"
1023        // Create a RandomConsumer.Consumer resource
1024        self.consumer <-RandomConsumer.createConsumer()
1025        self.address = self.account.address
1026        // Set the named paths
1027        self.ArtDropStoragePath = StoragePath(identifier: identifier)!
1028        self.ArtDropPublicPath = PublicPath(identifier: identifier)!
1029        self.AdministratorStoragePath = StoragePath(identifier: "\(identifier)_Administrator")!
1030        self.CollectionStoragePath = StoragePath(identifier: identifier)!
1031        self.CollectionPublicPath = PublicPath(identifier: identifier)!
1032        self.ArtistStoragePath = StoragePath(identifier: "\(identifier)_Artist")!
1033        self.HandlerStoragePath = StoragePath(identifier: "\(identifier)_Handler")!
1034
1035        // Create a Collection resource and save it to storage
1036        let collection <- create Collection()
1037        self.account.storage.save(<-collection, to: self.CollectionStoragePath)
1038        // create a public capability for the collection
1039        let collectionCap = self.account.capabilities.storage.issue<&Mneme.Collection>(self.CollectionStoragePath)
1040        self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
1041        // Create an Administrator resource and save it to storage
1042        let administrator <- create Administrator()
1043        self.account.storage.save(<-administrator, to: self.AdministratorStoragePath)
1044        // Create a Handler resource and save it to storage
1045        let handler <- create Handler()
1046        self.account.storage.save(<-handler, to: self.HandlerStoragePath)
1047    }
1048}