Cadence Cookbook

Contribute

Implementing Series for NFTs

Implementing Series for NFTs

14 Oct 2022

Contributed by Flow Blockchain

Intermediate

This cadence code will help you being to understand how to implement series and sets into your NFT project.

Smart Contract Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 // the below is a series structure that lays out how a series is to be created // // Variable size dictionary of SeriesData structs access(self) var seriesData: {UInt32: SeriesData} // Variable size dictionary of Series resources access(self) var series: @{UInt32: Series} .... pub struct SeriesData { // Unique ID for the Series pub let seriesId: UInt32 // Dictionary of metadata key value pairs access(self) var metadata: {String: String} init( seriesId: UInt32, metadata: {String: String}) { self.seriesId = seriesId self.metadata = metadata emit SeriesCreated(seriesId: self.seriesId) } pub fun getMetadata(): {String: String} { return self.metadata } } .... // // // Resource that allows an admin to mint new NFTs // pub resource Series { // Unique ID for the Series pub let seriesId: UInt32 // Array of NFTSets that belong to this Series access(self) var setIds: [UInt32] // Series sealed state pub var seriesSealedState: Bool; // Set sealed state access(self) var setSealedState: {UInt32: Bool}; // Current number of editions minted per Set access(self) var numberEditionsMintedPerSet: {UInt32: UInt32} init( seriesId: UInt32, metadata: {String: String}) { self.seriesId = seriesId self.seriesSealedState = false self.numberEditionsMintedPerSet = {} self.setIds = [] self.setSealedState = {} SetAndSeries.seriesData[seriesId] = SeriesData( seriesId: seriesId, metadata: metadata ) } pub fun addNftSet( setId: UInt32, maxEditions: UInt32, ipfsMetadataHashes: {UInt32: String}, metadata: {String: String}) { pre { self.setIds.contains(setId) == false: "The Set has already been added to the Series." } // Create the new Set struct var newNFTSet = NFTSetData( setId: setId, seriesId: self.seriesId, maxEditions: maxEditions, ipfsMetadataHashes: ipfsMetadataHashes, metadata: metadata ) // Add the NFTSet to the array of Sets self.setIds.append(setId) // Initialize the NFT edition count to zero self.numberEditionsMintedPerSet[setId] = 0 // Store it in the sets mapping field SetAndSeries.setData[setId] = newNFTSet emit SetCreated(seriesId: self.seriesId, setId: setId) } ..... // mintSetAndSeries // Mints a new NFT with a new ID // and deposits it in the recipients collection using their collection reference // pub fun mintSetAndSeriesNFT( recipient: &{NonFungibleToken.CollectionPublic}, tokenId: UInt64, setId: UInt32) { pre { self.numberEditionsMintedPerSet[setId] != nil: "The Set does not exist." self.numberEditionsMintedPerSet[setId]! < SetAndSeries.getSetMaxEditions(setId: setId)!: "Set has reached maximum NFT edition capacity." } // Gets the number of editions that have been minted so far in // this set let editionNum: UInt32 = self.numberEditionsMintedPerSet[setId]! + (1 as UInt32) // deposit it in the recipient's account using their reference recipient.deposit(token: <-create SetAndSeries.NFT( tokenId: tokenId, setId: setId, editionNum: editionNum )) // Increment the count of global NFTs SetAndSeries.totalSupply = SetAndSeries.totalSupply + (1 as UInt64) // Update the count of Editions minted in the set self.numberEditionsMintedPerSet[setId] = editionNum } .... // Admin is a special authorization resource that // allows the owner to perform important NFT // functions // pub resource Admin { pub fun addSeries(seriesId: UInt32, metadata: {String: String}) { pre { SetAndSeries.series[seriesId] == nil: "Cannot add Series: The Series already exists" } // Create the new Series var newSeries <- create Series( seriesId: seriesId, metadata: metadata ) // Add the new Series resource to the Series dictionary in the contract SetAndSeries.series[seriesId] <-! newSeries } pub fun borrowSeries(seriesId: UInt32): &Series { pre { SetAndSeries.series[seriesId] != nil: "Cannot borrow Series: The Series does not exist" } // Get a reference to the Series and return it return &SetAndSeries.series[seriesId] as &Series } pub fun createNewAdmin(): @Admin { return <-create Admin() } }

When creating an NFT that implements Sets and Series, you need to define structures that lay out how the sets and series are to be laid out. In this case we will go through series first.

Here we create first two variable size dictionaries. One contains a dictionary that holds all of our SeriesData structs and the scond is a dictionary of all of our Series resources. This helps us manage and make sure we aren't duplicating Series.

Afterwards we layout the structure of how we want our SeriesData to look. In this case we have a unique ID and some metadata, but you can add whatever type of variables you would like, key to note is to have a unique ID so that you are managing series appropriately.

Once the struct is created, we then create our Series resource that lists all the functions we are able to do when we have a series resource in our account. In this instance we're able to create NFT sets which will then lead to us being able to mint NFTS into sets of a series.

In order to be able to mint NFTS we first have to create a series resource in our account. In this instance we have an admin resource that is store in the deployer of the smart contracts account.

Here we have a function that allows us to addSeries and when that happens we are then able to create sets and mint NFTS.

We also include the ability to borrow a series so that we can access the functions in the series and do what we need to there.

Transaction Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import SetAndSeries from 0x01 transaction { let adminCheck: &SetAndSeries.Admin prepare(acct: AuthAccount) { self.adminCheck = acct.borrow<&SetAndSeries.Admin>(from: SetAndSeries.AdminStoragePath) ?? panic("could not borrow admin reference") } execute { self.adminCheck.addSeries(seriesId: 1, metadata: {"Series": "1"}) log("series added") } }

Here we take the admin resource from the AuthAccount and make sure that it contains one.

Once we are positive it contains the admin resource in its account, we then call the addSeries function in order to add a Series resource to use later.


ProgressNFT Fundamentals

Up Next: Creating a Set in Series

54%


Related Recipes

14 Oct 2022
Mint NFT
Beginner
14 Oct 2022
Collection for Holding NFTs
Beginner
14 Oct 2022
NFT with Metadata
Beginner