DeploySEALED

▓▓▓◇□╱^█&&*▪▒▒◇?■~□░○╳&▪█~░◆&░■▓◆**?█◆@◆◆╳□&▫▫╲●▒&%◆^□▫▪~%@╲▒$●▓

Transaction ID

Timestamp

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

Block Height

143,326,096

Computation

0

Execution Fee

0.01398 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
Mneme
1codeString
// MADE BY: Noah_Overflow // This contract is for Mneme, a proof of support platform // on Flow. // Mneme (Μνήμη) is one of the original three Muses in pre-Homeric Greek mythology. // Before the more famous Nine Muses were standardized by Hesiod (daughters of Zeus and Mnemosyne) // there were three older Muses: // Melete – Muse of Practice/Contemplation // Mneme – Muse of Memory // Aoide – Muse of Song // Mneme herself was seen as the preserver of knowledge and inspiration, responsible for the ability of poets, orators // and artists to recall what came before and give it form. She represents the thread that links art to time and culture // Literally, the memory of humanity encoded in creative work. import NonFungibleToken from 0x1d7e57aa55817448 import FungibleToken from 0xf233dcee88fe0abe import FlowToken from 0x1654653399040a61 import ViewResolver from 0x1d7e57aa55817448 import MetadataViews from 0x1d7e57aa55817448 import Pistis from 0x67c649092019e032 import RandomConsumer from 0x45caec600164c9e6 import Xorshift128plus from 0x45caec600164c9e6 import Burner from 0xf233dcee88fe0abe import FlowTransactionScheduler from 0xe467b9dd11fa00df // import "CrossVMMetadataViews" // import "EVM" access(all) contract Mneme: NonFungibleToken { // ----------------------------------------------------------------------- // Mneme contract-level fields. // These contain actual values that are stored in the smart contract. // ----------------------------------------------------------------------- // Dictionary to hold general collection information access(self) let collectionInfo: {String: AnyStruct} access(self) var artistOriginals: {Address: [Int64]} access(self) var artistEditions: {Address: [Int64]} access(self) var totalOriginals: UInt64 access(self) var totalEditions: UInt64 access(self) var totalCertificates: UInt64 access(all) let address: Address /// The RandomConsumer.Consumer resource used to request & fulfill randomness access(self) let consumer: @RandomConsumer.Consumer // ----------------------------------------------------------------------- // Mneme account paths // ----------------------------------------------------------------------- access(all) let CollectionStoragePath: StoragePath access(all) let CollectionPublicPath: PublicPath /// Path where the minter should be stored /// The standard paths for the collection are stored in the collection resource type access(all) let ArtDropStoragePath: StoragePath access(all) let ArtDropPublicPath: PublicPath access(all) let AdministratorStoragePath: StoragePath access(all) let ArtistStoragePath: StoragePath access(all) let HandlerStoragePath: StoragePath // ----------------------------------------------------------------------- // Mneme Entitlements // ----------------------------------------------------------------------- access(all) entitlement Admin access(all) entitlement AddArtist access(all) entitlement Editions /// Event to show when an NFT is minted access(all) event Minted( type: String, id: UInt64, uuid: UInt64, name: String, description: String ) access(all) event OriginalCreated(artistAddress: Address, originalId: UInt64) access(all) event EditionCreated(artistAddress: Address, editionId: UInt64) access(all) event MultipliersUpdated(artistAddress: Address, editionId: UInt64) access(all) event RNGReceiptPopped(id: UInt64, multiplier: UFix64) // ----------------------------------------------------------------------- // Mneme contract-level Composite Type definitions // ----------------------------------------------------------------------- // These are just *definitions* for Types that this contract // and other accounts can use. These definitions do not contain // actual stored values, but an instance (or object) of one of these Types // can be created by this contract that contains stored values. // ----------------------------------------------------------------------- access(all) resource Original: Pistis.Pool { access(all) let id: UInt64 access(all) var name: String access(all) var description: String access(all) var thumbnail: String access(all) let artistAddress: Address access(all) var editions: {UInt64: UInt64} access(all) var totalMinted: Int64 access(all) var price: UFix64 access(all) var metadata: {String: String} access(all) var extra: {String: AnyStruct} // Pistis fields access(all) var vaultsDict: @{Type: {FungibleToken.Vault}} access(all) var vaultReceiverPath: {Type: PublicPath} init ( id: UInt64, name: String, description: String, thumbnail: String, artistAddress: Address, price: UFix64, metadata: {String: String}, ) { self.id = id self.name = name self.description = description self.thumbnail = thumbnail self.artistAddress = artistAddress self.editions = {} self.totalMinted = 0 self.price = price self.vaultsDict <- {} self.vaultReceiverPath = {} self.metadata = metadata self.extra = {} } access(all) fun addEdition(editionId: UInt64) { self.editions[editionId] = 0 } access(all) fun increaseTotalMinted() { self.totalMinted = self.totalMinted + 1 } // helper funct to return metadata without ref access(all) fun getMetadata(): {String: String} { return self.metadata } } // Edition resource represents the Art's metadata // and its rewards rules // Attachment access(all) resource Edition { access(all) let originalId: UInt64 access(all) let id: UInt64 access(all) var name: String access(all) var price: UFix64 access(all) var type: String access(all) var story: String access(all) var dimensions: {String: String} access(all) var reprintLimit: Int64 access(all) let artistAddress: Address access(all) var totalMinted: Int64 access(all) var profitSplit: {Address: UFix64} // the address and the percentage of the profit // ArtDrop, Original Painting, Charity, CopyrightsHolder/Reseller, communityPool. (Reseller Address is dynamic) // the total of the percentages must be 100% // distribute after claim/point of no return. access(all) var multipliers: [UFix64] // Map of multiplier to the id of the CertificateNFT access(all) var usedMultipliers: {UFix64: UInt64} access(all) let rngReceipts: @{UInt64: Receipt} access(all) let revealReceipts: @{UInt64: FlowTransactionScheduler.ScheduledTransaction} init( originalId: UInt64, id: UInt64, name: String, price: UFix64, type: String, story: String, dimensions: {String: String}, reprintLimit: Int64, artistAddress: Address, multipliers: [UFix64], profitSplit: {Address: UFix64}) { self.originalId = originalId self.name = name self.price = price self.id = id self.type = type self.story = story self.dimensions = dimensions self.reprintLimit = reprintLimit self.artistAddress = artistAddress self.multipliers = multipliers self.usedMultipliers = {} self.totalMinted = 0 self.rngReceipts <- {} self.revealReceipts <- {} self.profitSplit = profitSplit // The number of multipliers should be equal to // the reprint limit if multipliers.length != Int(reprintLimit) { panic("The number of multipliers should be equal to the reprint limit") } } // helper funct to return dimension without ref access(all) fun getDimensions(): {String: String} { return self.dimensions } access(Editions) fun editEdition( name: String?, price: UFix64?, type: String?, story: String?, dimensions: {String: String}?, reprintLimit: Int64?) { pre { self.totalMinted == 0: "This edition has already been minted" } self.name = name ?? self.name self.price = price ?? self.price self.type = type ?? self.type self.story = story ?? self.story self.dimensions = dimensions ?? self.dimensions self.reprintLimit = reprintLimit ?? self.reprintLimit } // update multipliers access(Editions) fun updateMultipliers(multipliers: [UFix64]) { // The number of multipliers should be equal to // the reprint limit if multipliers.length != Int(self.reprintLimit) { panic("The number of multipliers should be equal to the reprint limit") } self.multipliers = multipliers } /// mintNFT mints a new NFT with a new ID /// and returns it to the calling context access(Editions) fun mintCertificateNFT(thumbnail: String) { // Get the original path let originalStorageIdentifier = "\(Mneme.account.address)_ArtDrop_Original_\(self.artistAddress)_\(self.originalId)" let originalStoragePath = StoragePath(identifier: originalStorageIdentifier)! let originalRef = Mneme.account.storage.borrow< &Mneme.Original>(from: originalStoragePath)! if originalRef == nil { panic("Original not found") } // Check if the edition has a reprint limit // If it does, check if the total minted count has reached the reprint limit if self.reprintLimit != 0 { if self.totalMinted >= self.reprintLimit { panic("This edition has reached the reprint limit") } } let metadata: {String: AnyStruct} = {} let currentBlock = getCurrentBlock() metadata["mintedBlock"] = currentBlock.height metadata["mintedTime"] = currentBlock.timestamp // this piece of metadata will be used to show embedding rarity into a trait metadata["foo"] = "bar" // [2x. 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x] // increase the total minted count for this Edition self.totalMinted = self.totalMinted + 1 // increase the total certificates count Mneme.totalCertificates = Mneme.totalCertificates + 1 // request randomness let request <- Mneme.consumer.requestRandomness() let receipt <- create Receipt(id: Mneme.totalCertificates, request: <-request) // Safe receipt linked to this Edition resource self.rngReceipts[Mneme.totalCertificates] <-! receipt // create a new NFT var newNFT <- create CertificateNFT( id: Mneme.totalCertificates, serial: UInt64(self.totalMinted), name: self.name, description: self.story, thumbnail: thumbnail, metadata: metadata, artistAddress: self.artistAddress ) // Schedule a transaction to reveal the Receipt let future = getCurrentBlock().timestamp + 5.0 let priority = FlowTransactionScheduler.Priority.Medium let executionEffort: UInt64 = 1000 let estimate = FlowTransactionScheduler.estimate( data: "", timestamp: future, priority: priority, executionEffort: executionEffort ) assert( estimate.timestamp != nil || priority == FlowTransactionScheduler.Priority.Medium, message: estimate.error ?? "estimation failed" ) if Mneme.account.storage.borrow<&AnyResource>(from: Mneme.HandlerStoragePath) == nil { let handler <- create Handler() Mneme.account.storage.save(<-handler, to: Mneme.HandlerStoragePath) } // Withdraw FLOW fees from this contract's account vault let vaultRef = Mneme.account.storage .borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) ?? panic("missing FlowToken vault on contract account") let fees <- vaultRef.withdraw(amount: estimate.flowFee ?? 0.0) as! @FlowToken.Vault let handlerCap = Mneme.account.capabilities.storage .issue<auth(FlowTransactionScheduler.Execute) &{FlowTransactionScheduler.TransactionHandler}>(Mneme.HandlerStoragePath) let revealReceipt <- FlowTransactionScheduler.schedule( handlerCap: handlerCap, data: "", timestamp: future, priority: priority, executionEffort: executionEffort, fees: <-fees ) // deposit fees to stake self.revealReceipts[Mneme.totalCertificates] <-! revealReceipt // Increase total minted count for the Original originalRef.increaseTotalMinted() // emit the Minted event emit Minted(type: newNFT.getType().identifier, id: newNFT.id, uuid: newNFT.uuid, name: newNFT.name, description: newNFT.description ) // send the new NFT to the artist's collection let artistAccount = getAccount(self.artistAddress) let artistCollection = artistAccount.capabilities.borrow<&{NonFungibleToken.Receiver}>(Mneme.CollectionPublicPath)! artistCollection.deposit(token: <- newNFT) } // Pop the rng receipt access(Editions) fun popRNGReceipt(id: UInt64) { pre { self.rngReceipts[id] != nil: "RNG receipt not found" } // get the receipt let receipt <- self.rngReceipts.remove(key: id)! // get a random number within the reprint limit // of this edition let randomNumber = Mneme._randomWithinRange(request: <- receipt.popRequest(), max: Int(self.reprintLimit)) // use this random number to get the multiplier from the multipliers array let multiplier = self.multipliers[randomNumber] // assign the multiplier to the id of the receipt/NFT self.usedMultipliers[multiplier] = receipt.id // return the used receipt self.rngReceipts[id] <-! receipt emit RNGReceiptPopped(id: id, multiplier: multiplier) } } /// We choose the name NFT here, but this type can have any name now /// because the interface does not require it to have a specific name any more access(all) resource CertificateNFT: NonFungibleToken.NFT, Pistis.Pool { access(all) let id: UInt64 access(all) let serial: UInt64 access(all) let artistAddress: Address access(all) var vaultsDict: @{Type: {FungibleToken.Vault}} access(all) var vaultReceiverPath: {Type: PublicPath} access(all) let name: String access(all) let description: String access(all) let thumbnail: String access(all) let royalties: MetadataViews.Royalty access(all) let metadata: {String: AnyStruct} init( id: UInt64, serial: UInt64, name: String, description: String, thumbnail: String, metadata: {String: AnyStruct}, artistAddress: Address ) { self.id = id self.serial = serial self.artistAddress = artistAddress self.name = name self.description = description self.thumbnail = thumbnail self.royalties = MetadataViews.Royalty( receiver: getAccount(Mneme.account.address).capabilities.get<&FlowToken.Vault>(/public/flowTokenReceiver), cut: 0.5, description: "The deployer gets 5% of every secondary sale." ) self.metadata = metadata self.vaultsDict <- {} self.vaultReceiverPath = {} } view access(all) fun getTraits(): {String: AnyStruct} { let metadata: {String: AnyStruct} = {"name": self.name} metadata["editionID"] = self.id metadata["editionName"] = self.name metadata["editionDescription"] = self.description metadata["editionThumbnail"] = self.thumbnail metadata["editionMetadata"] = self.metadata metadata["editionArtistAddress"] = self.artistAddress return metadata } /// createEmptyCollection creates an empty Collection /// and returns it to the caller so that they can own NFTs /// @{NonFungibleToken.Collection} access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <-Mneme.createEmptyCollection(nftType: Type<@Mneme.CertificateNFT>()) } access(all) view fun getViews(): [Type] { return [ Type<MetadataViews.Display>(), Type<MetadataViews.Royalties>(), Type<MetadataViews.Editions>(), Type<MetadataViews.ExternalURL>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Serial>(), Type<MetadataViews.Traits>(), Type<MetadataViews.EVMBridgedMetadata>() ] } access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type<MetadataViews.Display>(): return MetadataViews.Display( name: self.name, description: self.description, thumbnail: MetadataViews.HTTPFile( url: self.thumbnail ) ) case Type<MetadataViews.Editions>(): // There is no max number of NFTs that can be minted from this contract // so the max edition field value is set to nil let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil) let editionList: [MetadataViews.Edition] = [editionInfo] return MetadataViews.Editions( editionList ) case Type<MetadataViews.Serial>(): return MetadataViews.Serial( self.serial ) case Type<MetadataViews.Royalties>(): return MetadataViews.Royalties( [self.royalties] ) case Type<MetadataViews.ExternalURL>(): return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString())) case Type<MetadataViews.NFTCollectionData>(): return Mneme.resolveContractView(resourceType: Type<@Mneme.CertificateNFT>(), viewType: Type<MetadataViews.NFTCollectionData>()) case Type<MetadataViews.NFTCollectionDisplay>(): return Mneme.resolveContractView(resourceType: Type<@Mneme.CertificateNFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>()) case Type<MetadataViews.Traits>(): // exclude mintedTime and foo to show other uses of Traits let excludedTraits = self.getTraits() return MetadataViews.dictToTraits(dict: self.getTraits(), excludedNames: []) case Type<MetadataViews.EVMBridgedMetadata>(): // Implementing this view gives the project control over how the bridged NFT is represented as an // ERC721 when bridged to EVM on Flow via the public infrastructure bridge. // NOTE: If your NFT is a cross-VM NFT, meaning you control both your Cadence & EVM contracts and // registered your custom association with the VM bridge, it's recommended you use the // CrossVMMetadata.EVMBytesMetadata view to define and pass metadata as EVMBytes into your // EVM contract at the time of bridging into EVM. For more information about cross-VM NFTs, // see FLIP-318: https://github.com/onflow/flips/issues/318 // Get the contract-level name and symbol values let contractLevel = Mneme.resolveContractView( resourceType: nil, viewType: Type<MetadataViews.EVMBridgedMetadata>() ) as! MetadataViews.EVMBridgedMetadata? if let contractMetadata = contractLevel { // Compose the token-level URI based on a base URI and the token ID, pointing to a JSON file. This // would be a file you've uploaded and are hosting somewhere - in this case HTTP, but this could be // IPFS, S3, a data URL containing the JSON directly, etc. let baseURI = "https://example-nft.onflow.org/token-metadata/" let uriValue = self.id.toString().concat(".json") return MetadataViews.EVMBridgedMetadata( name: contractMetadata.name, symbol: contractMetadata.symbol, uri: MetadataViews.URI( baseURI: baseURI, // defining baseURI results in a concatenation of baseURI and value value: self.id.toString().concat(".json") ) ) } else { return nil } /* case Type<CrossVMMetadataViews.EVMPointer>(): // This view is intended for NFT projects with corresponding NFT implementations in both Cadence and // EVM. Resolving EVMPointer indicates the associated EVM implementation. Fully validating the // cross-VM association would involve inspecting the associated EVM contract and ensuring that // contract also points to the resolved Cadence type and contract address. For more information // about cross-VM NFTs, see FLIP-318: https://github.com/onflow/flips/issues/318 return Mneme.resolveContractView(resourceType: self.getType(), viewType: view) case Type<CrossVMMetadataViews.EVMBytesMetadata>(): // This view is intended for Cadence-native NFTs with corresponding ERC721 implementations. By // resolving, you're able to pass arbitrary metadata into your EVM contract whenever an NFT is // bridged which can be useful for Cadence NFTs with dynamic metadata values. // See FLIP-318 for more information about cross-VM NFTs: https://github.com/onflow/flips/issues/318 // Here we encoded the EVMBridgedMetadata URI and encode the string as EVM bytes, but you could pass any // Cadence values that can be abi encoded and decode them in your EVM contract as you wish. Within // your EVM contract, you can abi decode the bytes and update metadata in your ERC721 contract as // you see fit. let bridgedMetadata = (self.resolveView(Type<MetadataViews.EVMBridgedMetadata>()) as! MetadataViews.EVMBridgedMetadata?)! let uri = bridgedMetadata.uri.uri() let encodedURI = EVM.encodeABI([uri]) let evmBytes = EVM.EVMBytes(value: encodedURI) return CrossVMMetadataViews.EVMBytesMetadata(bytes: evmBytes) */ } return nil } } access(all) resource Collection: NonFungibleToken.Collection, Pistis.Loyalty { /// dictionary of NFT conforming tokens /// NFT is a resource type with an `UInt64` ID field access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} access(all) var loyaltyPoints: {Address: UFix64} access(all) var supportedArtists: {Address: [Int64]} init () { self.ownedNFTs <- {} self.loyaltyPoints = {} self.loyaltyPoints[Mneme.account.address] = 0.0 self.supportedArtists = {} } // Function to calculate loyalty points access(all) view fun calculateLoyaltyPoints(artistAddress: Address): Int { // get the array of editions let editions = self.supportedArtists[artistAddress]! // loyalty points is the of editions multiplied by 10 let loyaltyPoints: Int = editions.length * 10 return loyaltyPoints } access(all) fun addVault(vaultType: Type, vault: @{FungibleToken.Vault}, id: UInt64, vaultReceiverPath: PublicPath) { let nft <- self.ownedNFTs.remove(key: id) as! @Mneme.CertificateNFT nft.addVault(vaultType: vaultType, vault: <- vault, vaultReceiverPath: vaultReceiverPath) self.ownedNFTs[id] <-! nft } access(all) fun depositToVault(id: UInt64, vaultType: Type, vaultDeposit: @{FungibleToken.Vault}) { let nft <- self.ownedNFTs.remove(key: id) as! @Mneme.CertificateNFT nft.depositToVault(vaultType: vaultType, vaultDeposit: <- vaultDeposit) self.ownedNFTs[id] <-! nft } // Withdraw from a vault access(all) fun withdrawFromVault(id: UInt64, vaultType: Type) { let nft <- self.ownedNFTs.remove(key: id) as! @Mneme.CertificateNFT let newVault <- nft.withdrawFromVault(id: id, vaultType: vaultType) let account = getAccount(self.owner!.address) let vault <- newVault.remove(key: newVault.keys[0])! let receiverRef = account.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)! receiverRef.deposit(from: <- vault.withdraw(amount: vault.balance)) destroy newVault destroy vault self.ownedNFTs[id] <-! nft } /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts access(all) view fun getSupportedNFTTypes(): {Type: Bool} { let supportedTypes: {Type: Bool} = {} supportedTypes[Type<@Mneme.CertificateNFT>()] = true return supportedTypes } /// Returns whether or not the given type is accepted by the collection /// A collection that can accept any type should just return true by default access(all) view fun isSupportedNFTType(type: Type): Bool { return type == Type<@Mneme.CertificateNFT>() } /// withdraw removes an NFT from the collection and moves it to the caller access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Mneme.Collection.withdraw: Could not withdraw an NFT with ID " .concat(withdrawID.toString()) .concat(". Check the submitted ID to make sure it is one that this collection owns.")) // Remove loyalty points from the collector self.substractLoyalty(address: self.owner?.address!, loyaltyPoints: 10.0) return <-token } /// deposit takes a NFT and adds it to the collections dictionary /// and adds the ID to the id array access(all) fun deposit(token: @{NonFungibleToken.NFT}) { let token <- token as! @Mneme.CertificateNFT let id = token.id let artistAddress = token.artistAddress // add the new token to the dictionary which removes the old one let oldToken <- self.ownedNFTs[token.id] <- token if self.supportedArtists[artistAddress] == nil { self.supportedArtists[artistAddress] = [] } self.supportedArtists[artistAddress]!.append(Int64(id)) let loyaltyPoints = self.calculateLoyaltyPoints(artistAddress: artistAddress) // Based on NFT's edition and other factors, add loyalty points to the collector self.addLoyalty(address: self.owner?.address!, loyaltyPoints: UFix64(loyaltyPoints)) destroy oldToken // This code is for testing purposes only // Do not add to your contract unless you have a specific // reason to want to emit the NFTUpdated event somewhere // in your contract let authTokenRef = (&self.ownedNFTs[id] as auth(NonFungibleToken.Update) &{NonFungibleToken.NFT}?)! //authTokenRef.updateTransferDate(date: getCurrentBlock().timestamp) Mneme.emitNFTUpdated(authTokenRef) } /// getIDs returns an array of the IDs that are in the collection access(all) view fun getIDs(): [UInt64] { return self.ownedNFTs.keys } /// Gets the amount of NFTs stored in the collection access(all) view fun getLength(): Int { return self.ownedNFTs.length } access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { return &self.ownedNFTs[id] } /// Borrow the view resolver for the specified NFT ID access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? { if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? { return nft as &{ViewResolver.Resolver} } return nil } /// createEmptyCollection creates an empty Collection of the same type /// and returns it to the caller /// @return A an empty collection of the same type access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <-Mneme.createEmptyCollection(nftType: Type<@Mneme.CertificateNFT>()) } } // ----------------------------------------------------------------------- // ArtDrop Scheduled Transaction Handler // ----------------------------------------------------------------------- // Struct for the scheduled transaction data access(all) struct revealData { access(all) let id: UInt64 access(all) let artistAddress: Address access(all) let editionId: UInt64 init(id: UInt64, artistAddress: Address, editionId: UInt64) { self.id = id self.artistAddress = artistAddress self.editionId = editionId } } /// Handler resource that implements the Scheduled Transaction interface access(all) resource Handler: FlowTransactionScheduler.TransactionHandler { access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) { // Extract reveal data from transaction data let revealData = data as! revealData? ?? panic("revealData data is required") // Get a ref to the Edition resource // using the artistAddress and editionId let storageIdentifier = "ArtDrop_Edition_\(revealData.artistAddress)_\(revealData.editionId)" let storagePath = StoragePath(identifier: storageIdentifier)! let editionRef = Mneme.account.storage.borrow<auth(Mneme.Editions) &Mneme.Edition>(from: storagePath)! if editionRef == nil { panic("Edition not found") } // get the rng receipts for the edition // with this the revealData.id let rngReceipt = editionRef.popRNGReceipt(id: revealData.id) } } // ----------------------------------------------------------------------- // ArtDrop Receipt Resource // ----------------------------------------------------------------------- /// The Receipt resource is used to store the associated randomness request. By listing the /// RandomConsumer.RequestWrapper conformance, this resource inherits all the default implementations of the /// interface. This is why the Receipt resource has access to the getRequestBlock() and popRequest() functions /// without explicitly defining them. /// access(all) resource Receipt : RandomConsumer.RequestWrapper { /// The associated randomness request which contains the block height at which the request was made // The setID of the intended pack access(all) let id: UInt64 /// and whether the request has been fulfilled. access(all) var request: @RandomConsumer.Request? init(id: UInt64, request: @RandomConsumer.Request) { self.id = id self.request <- request } } /// Returns a random number between 0 and 1 using the RandomConsumer.Consumer resource contained in the contract. /// For the purposes of this contract, a simple modulo operation could have been used though this is not the case /// for all ranges. Using the Consumer.fulfillRandomInRange function ensures that we can get a random number /// within any range without a risk of bias. /// access(self) fun _randomWithinRange(request: @RandomConsumer.Request, max: Int): UInt64 { return self.consumer.fulfillRandomInRange(request: <-request, min: 0, max: UInt64(max)) } // ----------------------------------------------------------------------- // Mneme public functions // ----------------------------------------------------------------------- // Get all the Artists and their Editions access(all) view fun getAllArtists(): {Address: [Int64]} { return self.artistEditions } // Struct for Original metadata access(all) struct OriginalMetadata { access(all) let name: String access(all) let description: String access(all) let thumbnail: String access(all) let metadata: {String: String} init(name: String, description: String, thumbnail: String, metadata: {String: String}) { self.name = name self.description = description self.thumbnail = thumbnail self.metadata = metadata } } // Get all the Originals for an artist access(all) fun getOriginalMetadata(artistAddress: Address, originalId: UInt64): OriginalMetadata? { pre { self.artistOriginals[artistAddress] != nil: "This artist does not exist" } let storageIdentifier = "\(Mneme.address)_ArtDrop_Original_\(artistAddress)_\(originalId)" let publicPath = PublicPath(identifier: storageIdentifier)! let originalRef = Mneme.account.capabilities.borrow<&Mneme.Original>(publicPath)! let originalMetadata = originalRef.getMetadata() let metadata = OriginalMetadata(name: originalRef.name, description: originalRef.description, thumbnail: originalRef.thumbnail, metadata: originalMetadata) return metadata } // Struct for Edition metadata access(all) struct EditionMetadata { access(all) let originalId: UInt64 access(all) let id: UInt64 access(all) var name: String access(all) var price: UFix64 access(all) var type: String access(all) var story: String access(all) var dimensions: {String: String} access(all) var reprintLimit: Int64 access(all) let artistAddress: Address access(all) var totalMinted: Int64 init(originalId: UInt64, id: UInt64, name: String, price: UFix64, type: String, story: String, dimensions: {String: String}, reprintLimit: Int64, artistAddress: Address, totalMinted: Int64) { self.originalId = originalId self.id = id self.name = name self.price = price self.type = type self.story = story self.dimensions = dimensions self.reprintLimit = reprintLimit self.artistAddress = artistAddress self.totalMinted = totalMinted } } // Get an Edition's metadata // parameters: artistAddress: Address, editionId: UInt64 access(all) fun getEditionMetadata(artistAddress: Address, editionId: UInt64): EditionMetadata? { pre { self.artistEditions[artistAddress] != nil: "This artist does not exist" } // Check if editionId exists in the array let editionsArray = self.artistEditions[artistAddress]! var editionExists = false for edition in editionsArray { if edition == Int64(editionId) { editionExists = true break } } if !editionExists { return nil } let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Edition_\(artistAddress)_\(editionId)" let storagePath = StoragePath(identifier: storageIdentifier)! let editionRef = Mneme.account.storage.borrow<&Mneme.Edition>(from: storagePath)! let dimensions = editionRef.getDimensions() 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) return metadata } /// createEmptyCollection creates an empty Collection for the specified NFT type /// and returns it to the caller so that they can own NFTs access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} { return <- create Collection() } /// Function that returns all the Metadata Views implemented by a Non Fungible Token /// /// @return An array of Types defining the implemented views. This value will be used by /// developers to know which parameter to pass to the resolveView() method. /// access(all) view fun getContractViews(resourceType: Type?): [Type] { return [ Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.EVMBridgedMetadata>() ] } /// Function that resolves a metadata view for this contract. /// /// @param view: The Type of the desired view. /// @return A structure representing the requested view. /// access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { switch viewType { case Type<MetadataViews.NFTCollectionData>(): let collectionData = MetadataViews.NFTCollectionData( storagePath: self.CollectionStoragePath, publicPath: self.CollectionPublicPath, publicCollection: Type<&Mneme.Collection>(), publicLinkedType: Type<&Mneme.Collection>(), createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} { return <-Mneme.createEmptyCollection(nftType: Type<@Mneme.CertificateNFT>()) }) ) return collectionData case Type<MetadataViews.NFTCollectionDisplay>(): let media = MetadataViews.Media( file: MetadataViews.HTTPFile( url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg" ), mediaType: "image/svg+xml" ) return MetadataViews.NFTCollectionDisplay( name: "The Example Collection", description: "This collection is used as an example to help you develop your next Flow NFT.", externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"), squareImage: media, bannerImage: media, socials: { "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") } ) case Type<MetadataViews.EVMBridgedMetadata>(): // Implementing this view gives the project control over how the bridged NFT is represented as an ERC721 // when bridged to EVM on Flow via the public infrastructure bridge. // Compose the contract-level URI. In this case, the contract metadata is located on some HTTP host, // but it could be IPFS, S3, a data URL containing the JSON directly, etc. return MetadataViews.EVMBridgedMetadata( name: "Mneme", symbol: "XMPL", uri: MetadataViews.URI( baseURI: nil, // setting baseURI as nil sets the given value as the uri field value value: "https://example-nft.onflow.org/contract-metadata.json" ) ) /* case Type<CrossVMMetadataViews.EVMPointer>(): // This view is intended for NFT projects with corresponding NFT implementations in both Cadence and // EVM. Resolving EVMPointer indicates the associated EVM implementation. Fully validating the // cross-VM association would involve inspecting the associated EVM contract and ensuring that contract // also points to the resolved Cadence type and contract address. For more information about cross-VM // NFTs, see FLIP-318: https://github.com/onflow/flips/issues/318 // Assigning a dummy EVM address and deserializing. Implementations would want to declare the actual // EVM address corresponding to their corresponding ERC721. If using a proxy in your EVM contracts, this // address should be your proxy's address. let evmContractAddress = EVM.addressFromString( "0x1234565789012345657890123456578901234565" ) // Since this NFT is distributed in Cadence, it's declared as Cadence-native let nativeVM = CrossVMMetadataViews.VM.Cadence return CrossVMMetadataViews.EVMPointer( cadenceType: Type<@Mneme.CertificateNFT>(), cadenceContractAddress: self.account.address, evmContractAddress: evmContractAddress, nativeVM: nativeVM ) */ } return nil } // Administrator resource access(all) resource Administrator { // Function to create a new Original resource access(all) fun createOriginal( name: String, description: String, thumbnail: String, artistAddress: Address, price: UFix64, metadata: {String: String} ) { // increase the total originals count Mneme.totalOriginals = Mneme.totalOriginals + 1 // make this path dynamic based on contract's address let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Original_\(artistAddress)_\(Mneme.totalOriginals)" let storagePath = StoragePath(identifier: storageIdentifier)! let publicPath = PublicPath(identifier: storageIdentifier)! // create a new original resource let newOriginal <- create Original(id: Mneme.totalOriginals, name: name, description: description, thumbnail: thumbnail, artistAddress: artistAddress, price: price, metadata: metadata) // save the new original to storage Mneme.account.storage.save(<-newOriginal, to: storagePath) // create a public capability for the original let originalCap: Capability<&Mneme.Original> = Mneme.account.capabilities.storage.issue<&Mneme.Original>(storagePath) Mneme.account.capabilities.publish(originalCap, at: publicPath) // add the new original to the artist's originals // add a new key to the artist's originals dictionary if Mneme.artistOriginals[artistAddress] == nil { Mneme.artistOriginals[artistAddress] = [] } Mneme.artistOriginals[artistAddress]!.append(Int64(Mneme.totalOriginals)) // emit OriginalCreated event emit OriginalCreated(artistAddress: artistAddress, originalId: Mneme.totalOriginals) } // Function to create a new Edition resource access(all) fun createEdition( originalId: UInt64, name: String, price: UFix64, type: String, story: String, dimensions: {String: String}, reprintLimit: Int64, artistAddress: Address, multipliers: [UFix64], profitSplit: {Address: UFix64}) { if Mneme.artistEditions[artistAddress] == nil { Mneme.artistEditions[artistAddress] = [] } // increase the total editions count Mneme.totalEditions = Mneme.totalEditions + 1 // make this path dynamic based on contract's address let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Edition_\(artistAddress)_\(Mneme.totalEditions)" let storagePath = StoragePath(identifier: storageIdentifier)! let publicPath = PublicPath(identifier: storageIdentifier)! // create a new edition resource 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) // add the new edition to the original's editions // get the original path let originalStorageIdentifier = "\(Mneme.account.address)_ArtDrop_Original_\(artistAddress)_\(originalId)" let originalStoragePath = StoragePath(identifier: originalStorageIdentifier)! let originalRef = Mneme.account.storage.borrow< &Mneme.Original>(from: originalStoragePath)! if originalRef == nil { panic("Original not found") } originalRef.addEdition(editionId: newEdition.id) // save the new edition to storage Mneme.account.storage.save(<- newEdition, to: storagePath) // create a public capability for the edition let editionCap: Capability<&Mneme.Edition> = Mneme.account.capabilities.storage.issue<&Mneme.Edition>(storagePath) //////// // DO WE NEED THIS PUBLIC CAP? // //////// // Mneme.account.capabilities.publish(editionCap, at: publicPath) // add the new edition to the artist's editions Mneme.artistEditions[artistAddress]!.append(Int64(Mneme.totalEditions)) // return <- newEdition // publish an authorized capability to the // stored edition resource to the artist let artistCap: Capability<auth(Mneme.Editions) &Mneme.Edition> = Mneme.account.capabilities.storage.issue<auth(Editions) &Mneme.Edition>(storagePath) Mneme.account.inbox.publish(artistCap, name: storageIdentifier, recipient: artistAddress) // Emit an event to the artist emit EditionCreated(artistAddress: artistAddress, editionId: Mneme.totalEditions) } // Function to update an edition's multipliers access(all) fun updateMultipliers( artistAddress: Address, editionId: UInt64, multipliers: [UFix64]) { let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Edition_\(artistAddress)_\(Mneme.totalEditions)" let editionRef = Mneme.account.storage.borrow<auth(Editions) &Mneme.Edition>(from: StoragePath(identifier: storageIdentifier)!)! editionRef.updateMultipliers(multipliers: multipliers) emit MultipliersUpdated(artistAddress: artistAddress, editionId: editionId) } // Function to mint a new certificate NFT access(all) fun mintCertificateNFT( artistAddress: Address, editionId: UInt64, thumbnail: String ) { let storageIdentifier = "\(Mneme.account.address)_ArtDrop_Edition_\(artistAddress)_\(editionId)" let editionRef = Mneme.account.storage.borrow<auth(Editions) &Mneme.Edition>(from: StoragePath(identifier: storageIdentifier)!)! if editionRef == nil { panic("Edition not found") } editionRef.mintCertificateNFT(thumbnail: thumbnail) // return <- newCertificateNFT } } init() { self.collectionInfo = {} self.artistOriginals = {} self.artistEditions = {} self.totalOriginals = 0 self.totalCertificates = 0 self.totalEditions = 0 let identifier = "Mneme_\(self.account.address))" // Create a RandomConsumer.Consumer resource self.consumer <-RandomConsumer.createConsumer() self.address = self.account.address // Set the named paths self.ArtDropStoragePath = StoragePath(identifier: identifier)! self.ArtDropPublicPath = PublicPath(identifier: identifier)! self.AdministratorStoragePath = StoragePath(identifier: "\(identifier)_Administrator")! self.CollectionStoragePath = StoragePath(identifier: identifier)! self.CollectionPublicPath = PublicPath(identifier: identifier)! self.ArtistStoragePath = StoragePath(identifier: "\(identifier)_Artist")! self.HandlerStoragePath = StoragePath(identifier: "\(identifier)_Handler")! // Create a Collection resource and save it to storage let collection <- create Collection() self.account.storage.save(<-collection, to: self.CollectionStoragePath) // create a public capability for the collection let collectionCap = self.account.capabilities.storage.issue<&Mneme.Collection>(self.CollectionStoragePath) self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath) // Create an Administrator resource and save it to storage let administrator <- create Administrator() self.account.storage.save(<-administrator, to: self.AdministratorStoragePath) // Create a Handler resource and save it to storage let handler <- create Handler() self.account.storage.save(<-handler, to: self.HandlerStoragePath) } }

Cadence Script

1transaction(name: String, code: String ) {
2		prepare(signer: auth(AddContract) &Account) {
3			signer.contracts.add(name: name, code: code.utf8 )
4		}
5	}