How to Add Metadata to an IP Asset with the SDK
Learn how to add IP & NFT metadata to an IP Asset using the Typescript SDK.
See the Completed Code
To see a completed, working example of how to add metadata to your IP Asset, please see the metadata tutorial.
Note: The completed code follows step 5b. If you want to follow step 5a instead, you can very easily swap out the code.
Video Walkthrough
If you want to check out a video walkthrough of this tutorial, go here.
In this tutorial, you will learn how to register your IP on Story using the TypeScript SDK.
The Explanation
Let's say you have some off-chain IP (ex. a book, a character, a drawing, etc). In order to register that IP on Story, you first need to mint an NFT to represent that IP, and then register that NFT on Story, turning it into an 🧩 IP Asset. As you can probably tell, this is two transactions: Mint NFT ▶️ Register NFT
To make things easier, we provide a function that combines these two operations called mintAndRegisterIpAssetWithPilTerms
, which you will use below. This function not only mints an NFT and registers it, but also allows you to set License Terms on the IP and set metadata on the NFT & the IP such that it displays properly on things like our explorer.
0. Before you Start
There are a few steps you have to complete before you can start the tutorial.
- Add your Story Network Testnet wallet's private key to
.env
file:
WALLET_PRIVATE_KEY=<YOUR_WALLET_PRIVATE_KEY>
- Go to Pinata and create a new API key. Add the JWT to your
.env
file:
PINATA_JWT=<YOUR_PINATA_JWT>
- Add your preferred RPC URL to your
.env
file. You can just use the public default one we provide:
RPC_PROVIDER_URL=https://testnet.storyrpc.io
- Install the dependencies:
npm install @story-protocol/core-sdk @pinata/sdk viem
1. Set up your Story Config
- Associated docs: TypeScript SDK Setup
import { StoryClient, StoryConfig } from '@story-protocol/core-sdk'
import { http } from 'viem'
import { privateKeyToAccount, Address, Account } from 'viem/accounts'
const privateKey: Address = `0x${process.env.WALLET_PRIVATE_KEY}`
const account: Account = privateKeyToAccount(privateKey)
const config: StoryConfig = {
account: account,
transport: http(process.env.RPC_PROVIDER_URL),
chainId: 'iliad',
}
const client = StoryClient.newClient(config)
2. Set up your IP Metadata
View the IPA Metadata Standard and construct your metadata for your IP. You can use the generateIpMetadata
function to properly format your metadata and ensure it is of the correct type, as shown below:
import { IpMetadata } from '@story-protocol/core-sdk'
const ipMetadata: IpMetadata = client.ipAsset.generateIpMetadata({
title: 'My IP Asset',
description: 'This is a test IP asset',
watermarkImg: 'https://picsum.photos/200',
attributes: [
{
key: 'Rarity',
value: 'Legendary',
},
],
})
3. Set up your NFT Metadata
The NFT Metadata follows the ERC-721 Metadata Standard.
const nftMetadata = {
name: 'Test NFT',
description: 'This is a test NFT',
image: 'https://picsum.photos/200',
}
4. Upload your IP and NFT Metadata to IPFS
In a separate file, create a function to upload your IP & NFT Metadata objects to IPFS:
const pinataSDK = require('@pinata/sdk')
export async function uploadJSONToIPFS(jsonMetadata): Promise<string> {
const pinata = new pinataSDK({ pinataJWTKey: process.env.PINATA_JWT })
const { IpfsHash } = await pinata.pinJSONToIPFS(jsonMetadata)
return IpfsHash
}
You can then use that function to upload your metadata, as shown below:
import { uploadJSONToIPFS } from './utils/uploadToIpfs'
import { createHash } from 'crypto'
const ipIpfsHash = await uploadJSONToIPFS(ipMetadata)
const ipHash = createHash('sha256').update(JSON.stringify(ipMetadata)).digest('hex')
const nftIpfsHash = await uploadJSONToIPFS(nftMetadata)
const nftHash = createHash('sha256').update(JSON.stringify(nftMetadata)).digest('hex')
5. Register the NFT as an IP Asset
There are two ways to do this step. Either:
5a. You already have an NFT minted
In your .env
file, add the contract address and token ID of where your NFT was minted from:
NFT_CONTRACT_ADDRESS=<NFT_CONTRACT_ADDRESS>
TOKEN_ID=<TOKEN_ID>
Now that we have an NFT contract address set in our .env
file, the code below will register your NFT as an 🧩 IP Asset, set License Terms on the IP, and then set both NFT & IP metadata.
- Associated Docs: Register an Existing NFT as an IP Asset
import { PIL_TYPE, RegisterIpResponse } from '@story-protocol/core-sdk'
import { Address } from 'viem'
const response: RegisterIpResponse = await client.ipAsset.register({
nftContract: process.env.NFT_CONTRACT_ADDRESS as Address,
tokenId: process.env.TOKEN_ID as number,
ipMetadata: {
ipMetadataURI: `https://ipfs.io/ipfs/${ipIpfsHash}`,
ipMetadataHash: `0x${ipHash}`,
nftMetadataURI: `https://ipfs.io/ipfs/${nftIpfsHash}`,
nftMetadataHash: `0x${nftHash}`,
},
txOptions: { waitForTransaction: true },
})
console.log(`Root IPA created at transaction hash ${response.txHash}, IPA ID: ${response.ipId}`)
console.log(`View on the explorer: https://explorer.story.foundation/ipa/${response.ipId}`)
5b. You want to mint an NFT + register in the same step
First, in a separate script, you must create a new SPG NFT collection. You can do this with the SDK (view a working example here):
Why do we have to do this?
In order to use the
mintAndRegisterIpAssetWithPilTerms
function below, we'll have to deploy an SPG NFT collection.Instead of doing this, you could technically write your own contract that implements ISPGNFT. But an easy way to create a collection that implements
ISPGNFT
is just to call thecreateCollection
function in the SPG contract using the SDK, as shown below.
import { StoryClient, StoryConfig } from '@story-protocol/core-sdk'
import { http } from 'viem
const privateKey: Address = `0x${process.env.WALLET_PRIVATE_KEY}`
const account: Account = privateKeyToAccount(privateKey)
const config: StoryConfig = {
account: account,
transport: http(process.env.RPC_PROVIDER_URL),
chainId: 'iliad',
}
const client = StoryClient.newClient(config)
const newCollection = await client.nftClient.createNFTCollection({
name: 'Test NFT',
symbol: 'TEST',
txOptions: { waitForTransaction: true },
})
console.log(
`New SPG NFT collection created at transaction hash ${newCollection.txHash}`,
`NFT contract address: ${newCollection.nftContract}`
)
Look at the console output, and copy the NFT contract address. Add that value as NFT_CONTRACT_ADDRESS
to your .env
file:
NFT_CONTRACT_ADDRESS=<NFT_CONTRACT_ADDRESS>
Note
You only have to do the above step once. Once you have your NFT contract address, you can register any amount of IPs and will not have to do this again.
Now that we have an NFT contract address set in our .env
file, the code below will mint an NFT, register it as an 🧩 IP Asset, set License Terms on the IP, and then set both NFT & IP metadata.
- Associated Docs: Mint, Register, and Attach Terms
import { PIL_TYPE, CreateIpAssetWithPilTermsResponse } from '@story-protocol/core-sdk'
import { Address } from 'viem'
const response: CreateIpAssetWithPilTermsResponse = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms({
nftContract: process.env.NFT_CONTRACT_ADDRESS as Address,
pilType: PIL_TYPE.NON_COMMERCIAL_REMIX,
ipMetadata: {
ipMetadataURI: `https://ipfs.io/ipfs/${ipIpfsHash}`,
ipMetadataHash: `0x${ipHash}`,
nftMetadataURI: `https://ipfs.io/ipfs/${nftIpfsHash}`,
nftMetadataHash: `0x${nftHash}`,
},
txOptions: { waitForTransaction: true },
})
console.log(`Root IPA created at transaction hash ${response.txHash}, IPA ID: ${response.ipId}`)
console.log(`View on the explorer: https://explorer.story.foundation/ipa/${response.ipId}`)
6. Done!
See the Completed Code
To see a completed, working example of how to add metadata to your IP Asset, please see the metadata tutorial.
Note: The completed code follows step 5b. If you want to follow step 5a instead, you can very easily swap out the code.
Updated 12 days ago