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.
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.
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>
- 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 in Step 5, we'll have to have 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'
import { account, RPCProviderUrl } from './utils'
const privateKey: Address = `0x${process.env.WALLET_PRIVATE_KEY}`
const account: Account = privateKeyToAccount(privateKey)
const config: StoryConfig = {
account: account,
transport: http('https://testnet.storyrpc.io'),
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>
1. Set up your Story Config
- Associated docs: TypeScript SDK Setup
import { StoryClient, StoryConfig } from '@story-protocol/core-sdk'
import { http } from 'viem'
import { account } from './utils/utils'
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('https://testnet.storyrpc.io'),
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:
import { IpMetadata } from '@story-protocol/core-sdk'
const pinataSDK = require('@pinata/sdk')
export async function uploadJSONToIPFS(ipMetadata: IpMetadata): Promise<string> {
const pinata = new pinataSDK({ pinataJWTKey: process.env.PINATA_JWT })
const { IpfsHash } = await pinata.pinJSONToIPFS(ipMetadata)
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
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 } from '@story-protocol/core-sdk'
const registeredIpAssetResponse = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms({
nftContract: process.env.NFT_CONTRACT_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 ${registeredIpAssetResponse.txHash}, IPA ID: ${registeredIpAssetResponse.ipId}`)
console.log(`View on the explorer: https://explorer.story.foundation/ipa/${registeredIpAssetResponse.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.
Updated 2 days ago