Building Dynamic NFTs on Tezos
Jelly Woman
Posted on November 30, 2022
Over the past year and a half, Non-fungible tokens (NFT) have gained a lot of traction, but the next generation of NFTs will be dynamic. Dynamic NFTs give us an opportunity to explore a new level of interpretability for NFTs and other tokens, as it allows us to alter the metadata of our NFT. The ‘dynamic-ness’ of the NFT is often dependent on some online or IRL event such as a piece of art or character updating based on weather data, real-life sports scores, or even simple user activities. In this article we will be looking at a simple example of a dynamic NFT, combining trading cards and their attributes being updated based on simulated sports data.
Tezos and NFTs
Creating and managing NFTs on Tezos is both sustainable and practical, due to the low energy consumption and low fees of the Tezos blockchain. Plus, the large ecosystem that exists on Tezos and the developers, artists and collectors make it a great fit for NFTs. Most Tezos NFTs can be found at a Tezos (hosted/compatible) NFT Marketplace such as Objkt, Kalamint and Rarible.
Start by deploying an NFT/smart contract on Tezos
Let’s use SmartPy to build the NFT smart contract in the example below.
A good place to get started is the Online SmartPy IDE. It runs directly in your browser and lets you build your SmartPy contracts and test them.
We’ll be building the NFT using the FA2 standard on Tezos.
Here is a link (click here) to the full smart contract you can use to build the NFT. We’ll walk you through some of the important parts of the smart contract below.
We start by importing smartpy into our project.
import smartpy as sp
In the below code, we’re defining the structure for our Token Metadata. This metadata helps us add media and attributes to our NFT.
class Token_meta_data:
def __init__(self, config):
self.config = config
def get_type(self):
return sp.TRecord(token_id = sp.TNat, token_info = sp.TMap(sp.TString, sp.TBytes))
def set_type_and_layout(self, expr):
sp.set_type(expr, self.get_type())
The following code lets us interact with the NFT smart contract to output the token metadata URI. Marketplaces and wallets will use this data to render the NFT correctly.
class FA2_token_metadata(FA2_core):
def set_token_metadata_view(self):
def token_metadata(self, tok):
"""
Return the token-metadata URI for the given token.
For a reference implementation, dynamic-views seem to be the
most flexible choice.
"""
sp.set_type(tok, sp.TNat)
sp.result(self.data.token_metadata[tok])
self.token_metadata = sp.offchain_view(pure = True, doc = "Get Token Metadata")(token_metadata)
The rest of the code in the smart contract linked above is boilerplate to enable interactions with the NFT. Some examples are mentioned below:
def count_tokens(self):
def does_token_exist(self, tok):
def all_tokens(self):
def total_supply(self, tok):
def is_operator(self, query):
Now let’s take a look at how we can deploy our Tezos smart contract. Before we can deploy anything, let's first get some Testnet ꜩ from the Tezos Faucet. Once you complete the CAPTCHA verification on the Faucet page, you will be given a faucet key. Keep this file in a secure location. It contains the secret key and mnemonic. This is used for deployment of the smart contract we have created.
Open your Temple wallet and import the account we got from the faucet.
Now let's deploy the actual contract. Hit run in the SmartPy online IDE. It will run your contract and output some simulations on the adjacent panel. Over there you will find the option to Deploy Michelson Contract:
Follow the subsequent steps and you’ll be able to finish deploying the contract.
Now we will learn about Revise and how it empowers you and your NFTs.
Introduction to Revise
Revise is a platform that’ll let you build dynamic NFTs. NFTs today are fairly static. Once you buy it they don’t change. What you see is what you get. Dynamic NFTs are able to react to the outside world, to data, to user interactions, etc. They are capable of changing and evolving over time.
Let’s use Revise to build your own football team NFT collections, then add NFTs too it and allow them to change dynamically.
Create a Repo
Let’s clone this GitHub repo to get started. The repo is an empty Javascript project with revise-sdk added to the package manager. Once you cd
into the project folder, you can run npm install
to install all the dependencies. Let’s create an index.js
file. We’ll write all our code in this file, we’ll use node index.js
to run the program.
Copy and paste the following code into the index.js
file.
const { Revise } = require("revise-sdk");
const AUTH_TOKEN = "...PASTE YOUR AUTH TOKEN HERE...";
const revise = new Revise({auth: AUTH_TOKEN});
async function run() {
// write your code here
}
run()
In the first couple of lines, we’re importing the Revise-sdk and adding our auth token (more on how to get this below). In the third line, we’re instantiating the revise
object. We can now use this object to call all the functions needed to complete the exercise. Revise functions are mostly async, so we create a run
function that is async and can call the revise functions using the await
keyword. This will make our code more readable and maintainable.
💡 You can generate the Auth token from app.revise.network. For more details refer to the next section of this article.
Generate API Key (Auth Token)
Before we continue writing the rest of the code let’s go grab an auth key. Visit Revise and click on “get started”. Once you make an account and log in you’ll see the “generate API key” link in the header. Click on it, you should now be able to generate the API key (auth token). Copy the key that is shown on the screen and keep it safe. This key is not stored on Revise’s servers for security reasons. If you misplace the key it’s permanently lost. You can always come back and generate a new key.
Let’s replace the AUTH_TOKEN
in the above code with the key we just generated. Now we’re ready to start writing out Dynamic player NFT.
Add a Collection
To start with let’s make a collection called “My Dynamic Football Team”. This collection can have multiple NFTs. Each NFT is a player trading card. In order to make the collection, we’ll call the addCollection
function. We’ll have to pass it two parameters, the name for the collection and the URI for the collection. The collectionURI
is a unique project name that can be used to generate links for the NFT (why we need these links is described later). We have to make sure that the URI is unique and a single word. For e.g. “myfootballteam" is a valid Collection URI “my football team” is not. Add the following code to your index.js
inside the run
function.
const collection = await revise.addCollection({name: "My Dynamic Football Team", uri: "..CREATE YOUR UNIQUE URI"})
// Collection Name : Use any name you want for your collection (this gets shown in the marketplace))
// Collection_URI : Use a unique name (no spaces or special characters)
// this will generate a unique link for your collection
// for e.g. if you choose "mydynamicplayer12345"
// your baseURI wil be "mydynamicplayer12345.revise.link"
Add an NFT
Now let’s add our first NFT player. We’ll use the addNFT
function to create our player. Add the following code to your run
function below the addCollection
function call.
const nft = await revise.addNFT({
image: 'https://revise-testing.fra1.cdn.digitaloceanspaces.com/players/bronze.png',
name: 'Kylian Mbappe',
tokenId: '1',
description: 'Forward with exceptional abilities. Legend to have on your team'
}, [
{team: "PSG"}, {position: "Forward"}, {level: "Bronze"}, {offense: "75"}, {defense: "45"}, {stamina: "76"}, {skill: "71"}
], collection.id)
console.log(nft)
We’re passing parameters for the image of our player, the name, description, tokenId
and some properties. We’re also passing the collectionId
we received after creating our collection for the earlier code snippet. tokenId
is the unique ID of your NFT on the blockchain. This helps the blockchain and marketplaces distinguish between the NFTs part of the same collection. Two NFTs shouldn’t have the same tokenId
. We can add any amount of custom data to our NFTs via its attributes. As you can see in the above snippet we’ve added “team”, “position“, offense”, “defense”, “stamina” etc. You can add any number of attributes here.
Run
The final state of our index.js
file should look like this now.
const { Revise } = require("revise-sdk");
const AUTH_TOKEN = "...PASTE YOUR AUTH TOKEN HERE...";
const revise = new Revise({auth: AUTH_TOKEN});
async function run() {
const collection = await revise.addCollection({name: "My Dynamic Football Team", uri: "..CREATE YOUR UNIQUE URI"})
const nft = await revise.addNFT({
image: 'https://revise-testing.fra1.cdn.digitaloceanspaces.com/players/bronze.png',
name: 'Kylian Mbappe',
tokenId: '1',
description: 'Forward with exceptional abilities. Legend to have on your team'
}, [
{team: "PSG"}, {position: "Forward"}, {level: "Bronze"}, {offense: "75"}, {defense: "45"}, {stamina: "76"}, {skill: "71"}
], collection.id)
console.log(nft)
}
run()
Let’s run this file to create a collection and add our first NFT. Run node index.js
in your terminal. The expected output is an id
as shown below.
The command you ran earlier should output the id for the NFT we just created. The ID is the unique identifier for this NFT, we’ll be using this nftId
to fetch and makes updates to our player. Let’s store this ID carefully.
💡 Note: the tokenId
and nftId
are different. tokenId
is used by marketplaces and wallets to identify your NFT. nftId
is used by Revise to identify and control the NFT.
Visit your Revise dashboard to see your NFT player in action.
Click on the “View” button to see your NFT and it should show up with the Bronze image.
Adding dynamic capabilities to your NFT
Now that we’ve created our first NFT player, let’s make it dynamic by changing its moods.
Create a new file called levels.js
. Paste the following code in the file.
const { Revise } = require("revise-sdk");
const AUTH_TOKEN = "...AUTH_TOKEN...";
const revise = new Revise({auth: AUTH_TOKEN});
const API = async function() {
const options = [
{level: 'Silver', offense: '82', defense: '51', stamina: '82', skill: '79', image: "https://revise-testing.fra1.cdn.digitaloceanspaces.com/players/silver.png"},
{level: 'Gold', offense: '98', defense: '64', stamina: '92', skill: '87', image: "https://revise-testing.fra1.cdn.digitaloceanspaces.com/players/gold.png"}
]
const randomIndex = Math.floor(Math.random() * 2)
return options[randomIndex];
}
async function run() {
revise.every('2m').listenTo(API).start(async (data) => {
const player = await revise.fetchNFT("0d6cee67-d3c1-41d5-bd57-ebbe4d34ebd3")//Change the NFT Id to yours
revise.nft(player)
.setProperty("level", data.level)
.setProperty("offense", data.offense)
.setProperty("defense", data.defense)
.setProperty("stamina", data.stamina)
.setProperty("skill", data.skill)
.setImage(data.image)
.save()
console.log(`${player.name}'s is now at level ${data.level}`)
})
}
run()
In the above code, we’re setting up automation which will fetch a level every 2 mins and update our player attributes and image. Execute the above file by running node levels.js
. The program will execute automatically and evolve our NFT player in the background. You should be able to see the player change its level to Silver or Gold and subsequent attributes and all the old versions (revisions) of the NFT as well.
Congrats on creating your dynamic football collection and NFT!
If you want to learn more or explore opportunities creating with Revise, please schedule a call with us here or join our Discord or message us on Twitter.
Posted on November 30, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.