Welcome Zstake Service
...
Story Protocol
Ecosystem Travel Guide
How to use Stability AI in Story
let's say you generate an image using stability ai without adding a proper license to your image, others could use it freely in this tutorial, you will learn how to add a license to your stability ai generated image so that if others want to use it, they must properly license it from you 🧾 references and sites https //docs story foundation/docs/register stability images https //docs story foundation/docs/register stability images https //docs story foundation/docs/deployed smart contracts https //docs story foundation/docs/deployed smart contracts https //platform stability ai/ https //platform stability ai/ https //app pinata cloud/ https //app pinata cloud/ 🛠️ prerequisites before starting 1\ install node js ⌨️ curl fssl https //deb nodesource com/setup 20 x | sudo bash sudo apt get install y nodejs node v 📍result terminal 2\ install the dependencies ⌨️ mkdir story si && cd story si npm install @story protocol/core sdk pinata viem axios sharp form data 📍result terminal 3\ config env the private key is an evm wallet and the wallet must hold at least one ip token pinata stability api is available for free on their website ⌨️ nano env ⌨️ wallet private key= pinata jwt= pinata gateway= stability api key= rpc provider url=https //rpc odyssey storyrpc io royalty policy lap address=0x28b4f70ffe5ba7a26aef979226f77eb57fb9fdb6 susd address=0xc0f6e387ac0b324ec18eacf22ee7271207dce3d5 📍result terminal 🖼️ how to register nft in story 1\ generating stability api images ⌨️ nano si image js ⌨️ import dotenv from 'dotenv'; dotenv config(); import { pinatasdk } from 'pinata' const pinata = new pinatasdk({ pinatajwt process env pinata jwt, pinatagateway process env pinata gateway }) // find or create a group to store public images export async function getorcreatepublicgroup() { const response = await pinata groups list() const groups = response groups // access the groups array // check if groups is an array if (array isarray(groups)) { let group = groups find(g => g name === 'public stability images') // if the group doesn't exist, create it if (!group) { group = await pinata groups create({ name 'public stability images', ispublic true, }) } return group id } else { throw new error('the format of groups is not an array ') } } // upload our image to ipfs by putting it in a public group export async function uploadblobtoipfs(blob, filename, groupid) { const file = new file(\[blob], filename, { type 'image/png' }) // start the upload const response = await pinata upload file(file) group(groupid) const { cid } = await response return cid } // example usage const blob = new blob(\['your image data here'], { type 'image/png' }) const filename = 'compressed image png' const groupid = await getorcreatepublicgroup() uploadblobtoipfs(blob, filename, groupid) then((cid) => { console log(`image uploaded to ipfs with cid ${cid}`) }) catch((error) => { console error('error uploading image ', error) }) 📍result terminal 2\ store image in ipfs ⌨️ nano ipfs js ⌨️ import dotenv from 'dotenv'; dotenv config(); import { pinatasdk } from 'pinata' const pinata = new pinatasdk({ pinatajwt process env pinata jwt, pinatagateway process env pinata gateway }) // find or create a group to store public images export async function getorcreatepublicgroup() { const response = await pinata groups list() const groups = response groups // access the groups array // check if groups is an array if (array isarray(groups)) { let group = groups find(g => g name === 'public stability images') // if the group doesn't exist, create it if (!group) { group = await pinata groups create({ name 'public stability images', ispublic true, }) } return group id } else { throw new error('the format of groups is not an array ') } } // upload our image to ipfs by putting it in a public group export async function uploadblobtoipfs(blob, filename, groupid) { const file = new file(\[blob], filename, { type 'image/png' }) // start the upload const response = await pinata upload file(file) group(groupid) const { cid } = await response return cid } // example usage const blob = new blob(\['your image data here'], { type 'image/png' }) const filename = 'compressed image png' const groupid = await getorcreatepublicgroup() uploadblobtoipfs(blob, filename, groupid) then((cid) => { console log(`image uploaded to ipfs with cid ${cid}`) }) catch((error) => { console error('error uploading image ', error) }) ⌨️ node ipfs js 📍result terminal 📍result https //app pinata cloud/ https //app pinata cloud/ 3\ set up your ip nft ( metadata, license terms, e t c ) ⌨️ nano story nft mjs ⌨️ import dotenv from 'dotenv'; dotenv config(); import pkg from '@story protocol/core sdk'; const { storyclient, ipmetadata, licenseterms } = pkg; import { http } from "viem"; import { privatekeytoaccount } from "viem/accounts"; import { uploadjsontoipfs } from " /utils/uploadtoipfs js"; import { createhash } from "crypto"; // set environment variables const privatekey = `0x${process env wallet private key}`; const account = privatekeytoaccount(privatekey); const config = { account account, transport http(process env rpc provider url), chainid "odyssey", }; const client = new storyclient(config); // check available methods in the client object console log(client); // function to register an image as ip async function registerimageasip(cid, title, description) { const ipmetadata = { title title, description description, iptype 'image', attributes \[ { key 'model', value 'stability', }, { key 'service', value 'stable image core' }, { key 'prompt', value 'lighthouse on a cliff overlooking the ocean', }, ], creators \[ { name 'zstake', contributionpercent 100, address account address, }, ], }; const nftmetadata = { name 'nft representing ownership of our image', description 'this nft represents ownership of the image generated by stability', image process env pinata gateway + '/files/' + cid, attributes \[ { key 'model', value 'stability', }, { key 'service', value 'stable image core' }, { key 'prompt', value 'lighthouse on a cliff overlooking the ocean', }, ] }; try { // upload ip metadata to ipfs const ipipfshash = await uploadjsontoipfs(ipmetadata); const iphash = createhash("sha256") update(json stringify(ipmetadata)) digest("hex"); // upload nft metadata to ipfs const nftipfshash = await uploadjsontoipfs(nftmetadata); const nfthash = createhash("sha256") update(json stringify(nftmetadata)) digest("hex"); console log(`ip metadata ipfs hash ${ipipfshash}`); console log(`ip metadata sha 256 hash ${iphash}`); console log(`nft metadata ipfs hash ${nftipfshash}`); console log(`nft metadata sha 256 hash ${nfthash}`); // after confirming the exact method name, use it // const response = await client \<methodname>(ipmetadata); // console log(`image successfully registered id ${response id}`); } catch (error) { console error('error registering image as ip ', error); } } // set and register license terms const royaltypolicylapaddress = '0x28b4f70ffe5ba7a26aef979226f77eb57fb9fdb6'; // insert royaltypolicylap address const susdaddress = '0xc0f6e387ac0b324ec18eacf22ee7271207dce3d5'; // insert susd address const commercialremixterms = { transferable true, royaltypolicy '0x28b4f70ffe5ba7a26aef979226f77eb57fb9fdb6', defaultmintingfee bigint(10), expiration bigint(0), commercialuse true, commercialattribution true, // copyright notice required commercializerchecker '0x0000000000000000000000000000000000000000', commercializercheckerdata '0x0000000000000000000000000000000000000000', commercialrevshare 50, // claim 50% of derivative revenue commercialrevceiling bigint(0), derivativesallowed true, derivativesattribution true, derivativesapproval false, derivativesreciprocal true, derivativerevceiling bigint(0), currency susdaddress, uri '', } // function to register license terms async function registerlicenseterms() { try { // after confirming the exact method name, use it // const response = await client \<methodname>(commercialremixterms); // console log('license terms registered successfully ', response); } catch (error) { console error('error registering license terms ', error); } } // example usage const cid = 'your cid'; const title = 'your titile'; const description = ' '; registerimageasip(cid, title, description); registerlicenseterms(); ⌨️ node story nft mjs 📍result terminal 📍result https //app pinata cloud/ https //app pinata cloud/ ipfs meta upload(data json) 4\ create nft collection ⌨️ nano create nft collection mjs ⌨️ import dotenv from 'dotenv'; dotenv config(); import pkg from '@story protocol/core sdk'; const { storyclient } = pkg; import { http } from 'viem'; import { privatekeytoaccount } from 'viem/accounts'; const privatekey = `0x${process env wallet private key}`; const account = privatekeytoaccount(privatekey); const config = { account account, transport http(process env rpc provider url), chainid 'odyssey', }; const client = new storyclient(config); const createnftcollection = async () => { const newcollection = await client nftclient createnftcollection({ name 'zstake nfts', symbol 'znft', ispublicminting true, mintopen true, mintfeerecipient '0x0000000000000000000000000000000000000000', // zeroaddress contracturi '', txoptions { waitfortransaction true }, }); console log( `new spg nft collection created at transaction hash ${newcollection txhash}`, `spg nft contract address ${newcollection spgnftcontract}` ); }; // call the function createnftcollection(); ⌨️ node create nft collection mjs 📍result terminal 📍result odyssey testnet scan 5\ install the dependencies spg nft contract address= runs node create nft collection mjs and uses the output address ⌨️ nano env ⌨️ spg nft contract address= = \<npm install> 6\ nft mint and register ip ⌨️ nano mint and register ip asset mjs ⌨️ import dotenv from 'dotenv'; dotenv config(); import pkg from '@story protocol/core sdk'; const { storyclient } = pkg; import { http } from 'viem'; import { privatekeytoaccount } from 'viem/accounts'; import { createhash } from 'crypto'; const privatekey = `0x${process env wallet private key}`; const account = privatekeytoaccount(privatekey); const config = { account account, transport http(process env rpc provider url), chainid 'odyssey', }; const client = new storyclient(config); const ipipfshash = 'bafkreibhof7bofpx5ya2qmvzipslrirhel7egb2cvw4abr4tlhxqjybcoy'; const iphash = createhash('sha256') update('ip metadata') digest('hex'); const nftipfshash = 'bafkreiaiohxpcdpz4xrcxubsoakxclruwg4px2tzj2ybscwve3se2kxho4'; const nfthash = createhash('sha256') update('nft metadata') digest('hex'); const royaltypolicylapaddress = process env royalty policy lap address; const susdaddress = process env susd address; const commercialremixterms = { transferable true, royaltypolicy royaltypolicylapaddress, defaultmintingfee bigint(10), expiration bigint(0), commercialuse true, commercialattribution true, commercializerchecker '0x0000000000000000000000000000000000000000', commercializercheckerdata '0x0000000000000000000000000000000000000000', commercialrevshare 50, commercialrevceiling bigint(0), derivativesallowed true, derivativesattribution true, derivativesapproval false, derivativesreciprocal true, derivativerevceiling bigint(0), currency susdaddress, uri '', }; const registeripasset = async () => { const response = await client ipasset mintandregisteripassetwithpilterms({ spgnftcontract process env spg nft contract address, terms \[commercialremixterms], ipmetadata { ipmetadatauri process env pinata gateway + '/files/' + ipipfshash, ipmetadatahash `0x${iphash}`, nftmetadatauri process env pinata gateway + '/files/' + 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}` ); }; // call the function registeripasset(); ⌨️ node mint and register ip asset mjs 📍result terminal 📍result odyssey testnet scan 💡 zstake thoughts you all probably know about the collaboration with stability ai we are all excited about this news, as story protocol is working on a real collaboration partnership the announcement was made, but in just one day, the story protocol team has proven this through the official doc, showing how ip works when we actually implemented it, all the functions were already in place on chain and working through the api was smooth ip will protect the survival of all creators in the future