---
# System prepended metadata

title: Stoa NFT Documentation

---

# Stoa NFT Documentation

---

## Overview

Stoa's NFT system uses a system of seasons - periodically, users of STOA will be introduced to new possible rewards for continued meditation. Such a period is called a season, and within each period every available NFT changes.

This is implemented with a two-contract system: 
1) The NFTSeasonFactory
2) The NFTSeasonInstance

Which this guide will cover after an introduction of the access control system in use: Ownership

---
### Ownership Access Control

Both contracts use an Ownership model of access control - this means that there are checks in place around critical functions that restrict who may call those functions. 

Almost all of the NFTSeasonFactory contract functions are restricted to being called by the contract's current owner - the exceptions are generally `read` class functions that simply return contract data. 

The same is true for the NFTSeasonInstance contract with the major exception that the Mint functions are public - necessary for the operation of the app. 

Ownership is an access control concept where there is one privileged account - the owner - who is the only account capable of interacting with the restricted functions. 

This is a standard security practice that ensures there is no unexpected state updates. 

#### Transfer Ownership

There is one additional note about ownership - Ownable contracts always contain a `transferOwnership` function that allows the current owner to transfer ownership to a new account. 

---
### NFTSeasonFactory

The Season Factory is the contract that the administrators of STOA will interact with the most. 

The Season Factory tracks how many seasons exist, what their addresses and ID numbers are (using an auto-incrementing integer), and which season is to be used as the current season (plus it's associated address and index.)

It also has a function for creating a new season - the UI for this function is described later in this document - that takes a string which is expected to be an IPFS URI to a directory of metadata. 
It then creates a new season at the end of the list of seasons, setting the IPFS URI to be the NFTBaseuri for the new season, and adds it's associated index and address to the various lookup tables that track that information. 
It also updates the current season to be that newly created index and address. 

---

### NFTSeason Instance

The NFTSeason Instance is the implementation of the previously described Season functionality. 

NFTSeason Instances enable lookups of token data based on that token's ID, lookup which tokens a user has been assigned, and allows users to mint new tokens if certain conditions have been met as described in the STOA design document. 

Under the hood, NFTSeason Instances are clone-able proxies - there is one instance of the contract that describes all logic, then myriad and essentially unlimited instances that defer to that one instance for all logic and maintain their own unique state. This reduces the gas price of instantiating new seasons. 

**Note:** Discovering **all** of the NFT's a person may hold requires iterating through each NFTSeason Instance and discovering which tokens are assigned to them at that address, and adding the results of that to a list displayed on the front end. In essence, each season will appear to be a different collection. 

Furthermore, the NFTSeason Instance is something of a misnomer - they are not NFTs, but limited access fungible tokens. A person may only mint one of each instance, and in the case of the generative token images, only one from each set. This strangulation on supply prevents massive inflation, but allows apples-to-applies comparisons between user's collections as a single token can belong to unlimited users. 

---

### UI & JSON

#### UI

The UI for interacting with the contracts is very simple in the app - extra functions will be available via etherscan, if needed, but the most frequently used interactions are available directly in the app. 

One function is the `update current season` function - it allows the owner to change the `current` season registered with the NFTSeasonFactory, which changes the NFT's available to be minted via the app. 


The other function is the `create new season` function - this function takes the url of the IPFS folder that contains all the metadata for the new season's 'NFTs'. 

#### JSON

The way that NFTs declare their metadata (image, description, composite parts, other details) is via a `tokenURI` read function on the NFTSeasonInstance contract. 

The tokenURI function takes an `index`, which is the NFT's 'id' expressed as an integer, and returns the `baseURI` string concatenated with the `index` and a `.json`. This essentailly provides a link directly to a file named `index.json` inside of a folder with a bunch of other similar files, ie the NFT collection. 

The standard method for providing this url and making sure that the NFT data is clear and readable by everyone is to 
1) Create a folder of all NFT images
2) Name all images their respective `index.jpg` or `index.png` or whichever
3) Upload that folder to IPFS, note the location of this folder
4) Create a new folder of all NFT metadata
5) Name all files `index.json`
6) In each file, ensure each file follows the opensea metadata standard (https://docs.opensea.io/docs/metadata-standards#metadata-structure)
7) Use the folder from step 3 to describe the image in each json file
8) Upload the folder of JSONs to IPFS, note the location of this folder
9) Paste that folder location into the `create new season` function in the Admin Panel

---

### Upgrading

The NFTSeason Factory and NFTSeason Instance are both upgradeable - this allows STOA to implement changes to the structure and logic of the implementation smart contracts without requiring all users (Opensea et al.) to update their records of the addresses performing such logic for STOA. 

Upgrading works by copying and renaming the previous instance of the implementation - ie the last one that was uploaded - then having the **original deployer** or **registered owner** of the **proxy contracts** deploy the new instances **at the proxy address.** More details here: https://docs.openzeppelin.com/contracts/4.x/upgradeable#openzeppelin::upgrades.adoc