Skip to content

Instantly share code, notes, and snippets.

@bpolania
Last active September 16, 2024 22:59
Show Gist options
  • Save bpolania/0d95264fb34f9023756da3349b7a92c2 to your computer and use it in GitHub Desktop.
Save bpolania/0d95264fb34f9023756da3349b7a92c2 to your computer and use it in GitHub Desktop.

Story Protocol SDK Documentation

Overview

The Story Protocol SDK is a TypeScript library that provides an interface to interact with the Story Protocol smart contracts. It includes classes for managing NFTs, handling various workflows, and dealing with errors specific to the protocol.

Installation

npm install story-protocol-sdk

Main Components

SPGNFT

The SPGNFT class provides methods to interact with the SPGNFT (Story Protocol Generative NFT) contract.

Usage

import { SPGNFT, ethers } from 'story-protocol-sdk';

const provider = new ethers.providers.JsonRpcProvider('YOUR_RPC_URL');
const signer = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
const spgnft = new SPGNFT('SPGNFT_CONTRACT_ADDRESS', signer);

// Example: Minting an NFT
try {
  const tx = await spgnft.mint('RECIPIENT_ADDRESS', 'NFT_METADATA_URI');
  console.log('NFT minted:', tx.hash);
} catch (error) {
  if (error instanceof SPGNFTError.MintingDenied) {
    console.error('Minting denied. You may not have the minter role.');
  }
  // Handle other errors...
}

Workflows

The SDK provides classes for different workflows in the Story Protocol:

  • RegistrationWorkflows
  • LicenseAttachmentWorkflows
  • GroupingWorkflows
  • DerivativeWorkflows (not fully implemented yet)

Usage Example: RegistrationWorkflows

import { RegistrationWorkflows, WorkflowStructs, ethers } from 'story-protocol-sdk';

const registrationWorkflows = new RegistrationWorkflows('REGISTRATION_WORKFLOWS_ADDRESS', signer);

// Example: Creating a new collection
const collectionAddress = await registrationWorkflows.createCollection(
  'My Collection',
  'MC',
  1000,
  ethers.utils.parseEther('0.1'),
  'MINT_FEE_TOKEN_ADDRESS',
  'MINT_FEE_RECIPIENT_ADDRESS',
  await signer.getAddress(),
  true,
  true
);

console.log('New collection created at:', collectionAddress);

// Example: Minting and registering an IP
const ipMetadata: WorkflowStructs.IPMetadata = {
  ipMetadataURI: 'https://example.com/ip-metadata',
  ipMetadataHash: ethers.utils.keccak256(ethers.utils.toUtf8Bytes('IP Metadata')),
  nftMetadataURI: 'https://example.com/nft-metadata',
  nftMetadataHash: ethers.utils.keccak256(ethers.utils.toUtf8Bytes('NFT Metadata'))
};

const { ipId, tokenId } = await registrationWorkflows.mintAndRegisterIp(
  collectionAddress,
  await signer.getAddress(),
  ipMetadata
);

console.log('Minted and registered IP:', { ipId, tokenId });

Error Handling

The SDK provides custom error classes for different types of errors that may occur during interactions with the Story Protocol contracts. These include:

  • SPGError
  • RegistrationWorkflowsError
  • LicenseAttachmentWorkflowsError
  • DerivativeWorkflowsError
  • GroupingWorkflowsError
  • SPGNFTError

You can catch and handle these errors to provide more specific error messages or take appropriate actions in your application.

Types and Interfaces

The SDK includes several TypeScript interfaces and types to help with type checking and autocompletion:

  • SPGNFTConfig
  • WorkflowConfig
  • WorkflowStructs.IPMetadata
  • WorkflowStructs.SignatureData
  • WorkflowStructs.MakeDerivative
  • PILTerms

Constants

  • ADMIN_ROLE: The default admin role (0x1)
  • MINTER_ROLE: The default minter role (0x1)
// story-protocol-sdk.ts
import { ethers } from 'ethers';
// Types
export interface SPGNFTConfig {
address: string;
provider: ethers.providers.Provider;
}
export interface WorkflowConfig {
groupingWorkflowsAddress: string;
licenseAttachmentWorkflowsAddress: string;
registrationWorkflowsAddress: string;
derivativeWorkflowsAddress: string;
provider: ethers.providers.Provider;
}
export namespace WorkflowStructs {
export interface IPMetadata {
ipMetadataURI: string;
ipMetadataHash: string;
nftMetadataURI: string;
nftMetadataHash: string;
}
export interface SignatureData {
signer: string;
deadline: ethers.BigNumber;
signature: string;
}
export interface MakeDerivative {
parentIpIds: string[];
licenseTemplate: string;
licenseTermsIds: number[];
royaltyContext: string;
}
}
export interface PILTerms {
// Define the structure of PILTerms based on the smart contract
[key: string]: any;
}
export const ADMIN_ROLE = ethers.utils.hexZeroPad('0x01', 32);
export const MINTER_ROLE = ethers.utils.hexZeroPad('0x01', 32);
// Errors
export class StoryProtocolError extends Error {
constructor(message: string) {
super(message);
this.name = 'StoryProtocolError';
}
}
export class SPGError extends StoryProtocolError {
static CallerNotMinterRole = class extends SPGError {
constructor() {
super('Caller does not have the minter role');
}
};
}
export class RegistrationWorkflowsError extends StoryProtocolError {
static ZeroAddressParam = class extends RegistrationWorkflowsError {
constructor() {
super('Zero address provided as a param to the RegistrationWorkflows');
}
};
}
export class LicenseAttachmentWorkflowsError extends StoryProtocolError {
static ZeroAddressParam = class extends LicenseAttachmentWorkflowsError {
constructor() {
super('Zero address provided as a param to the LicenseAttachmentWorkflows');
}
};
}
export class DerivativeWorkflowsError extends StoryProtocolError {
static ZeroAddressParam = class extends DerivativeWorkflowsError {
constructor() {
super('Zero address provided as a param to the DerivativeWorkflows');
}
};
static EmptyLicenseTokens = class extends DerivativeWorkflowsError {
constructor() {
super('License token list is empty');
}
};
static CallerAndNotTokenOwner = class extends DerivativeWorkflowsError {
constructor(tokenId: string, caller: string, actualTokenOwner: string) {
super(`Caller ${caller} is not the owner of the license token ${tokenId}. Actual owner: ${actualTokenOwner}`);
}
};
}
export class GroupingWorkflowsError extends StoryProtocolError {
static ZeroAddressParam = class extends GroupingWorkflowsError {
constructor() {
super('Zero address provided as a param to the GroupingWorkflows');
}
};
}
export class SPGNFTError extends StoryProtocolError {
static ZeroAddressParam = class extends SPGNFTError {
constructor() {
super('Zero address provided as a param');
}
};
static ZeroMaxSupply = class extends SPGNFTError {
constructor() {
super('Zero max supply provided');
}
};
static MaxSupplyReached = class extends SPGNFTError {
constructor() {
super('Max mint supply reached');
}
};
static MintingDenied = class extends SPGNFTError {
constructor() {
super('Minting is denied. Public minting is false and caller does not have the minter role');
}
};
static CallerNotFeeRecipient = class extends SPGNFTError {
constructor() {
super('Caller is not the fee recipient');
}
};
static MintingClosed = class extends SPGNFTError {
constructor() {
super('Minting is closed');
}
};
static CallerNotPeripheryContract = class extends SPGNFTError {
constructor() {
super('Caller is not one of the periphery contracts');
}
};
}
// SPGNFT
export class SPGNFT {
private contract: ethers.Contract;
constructor(address: string, signer: ethers.Signer) {
this.contract = new ethers.Contract(address, [
'function initialize(string,string,uint32,uint256,address,address,address,bool,bool)',
'function totalSupply() view returns (uint256)',
'function mintFee() view returns (uint256)',
'function mintFeeToken() view returns (address)',
'function mintFeeRecipient() view returns (address)',
'function mintOpen() view returns (bool)',
'function publicMinting() view returns (bool)',
'function setMintFee(uint256)',
'function setMintFeeToken(address)',
'function setMintFeeRecipient(address)',
'function setMintOpen(bool)',
'function setPublicMinting(bool)',
'function mint(address,string) returns (uint256)',
'function mintByPeriphery(address,address,string) returns (uint256)',
'function withdrawToken(address)'
], signer);
}
async initialize(
name: string,
symbol: string,
maxSupply: number,
mintFee: ethers.BigNumber,
mintFeeToken: string,
mintFeeRecipient: string,
owner: string,
mintOpen: boolean,
isPublicMinting: boolean
): Promise<void> {
return this.contract.initialize(name, symbol, maxSupply, mintFee, mintFeeToken, mintFeeRecipient, owner, mintOpen, isPublicMinting);
}
async totalSupply(): Promise<number> {
return this.contract.totalSupply();
}
async mintFee(): Promise<ethers.BigNumber> {
return this.contract.mintFee();
}
async mintFeeToken(): Promise<string> {
return this.contract.mintFeeToken();
}
async mintFeeRecipient(): Promise<string> {
return this.contract.mintFeeRecipient();
}
async mintOpen(): Promise<boolean> {
return this.contract.mintOpen();
}
async publicMinting(): Promise<boolean> {
return this.contract.publicMinting();
}
async setMintFee(fee: ethers.BigNumber): Promise<void> {
return this.contract.setMintFee(fee);
}
async setMintFeeToken(token: string): Promise<void> {
return this.contract.setMintFeeToken(token);
}
async setMintFeeRecipient(newFeeRecipient: string): Promise<void> {
try {
return await this.contract.setMintFeeRecipient(newFeeRecipient);
} catch (error) {
if (error.message.includes('SPGNFT__CallerNotFeeRecipient')) {
throw new SPGNFTError.CallerNotFeeRecipient();
}
throw error;
}
}
async setMintOpen(mintOpen: boolean): Promise<void> {
return this.contract.setMintOpen(mintOpen);
}
async setPublicMinting(isPublicMinting: boolean): Promise<void> {
return this.contract.setPublicMinting(isPublicMinting);
}
async mint(to: string, nftMetadataURI: string): Promise<ethers.ContractTransaction> {
try {
return await this.contract.mint(to, nftMetadataURI);
} catch (error) {
if (error.message.includes('SPGNFT__MintingDenied')) {
throw new SPGNFTError.MintingDenied();
}
if (error.message.includes('SPGNFT__MintingClosed')) {
throw new SPGNFTError.MintingClosed();
}
if (error.message.includes('SPGNFT__MaxSupplyReached')) {
throw new SPGNFTError.MaxSupplyReached();
}
throw error;
}
}
async mintByPeriphery(to: string, payer: string, nftMetadataURI: string): Promise<ethers.ContractTransaction> {
return this.contract.mintByPeriphery(to, payer, nftMetadataURI);
}
async withdrawToken(token: string): Promise<void> {
return this.contract.withdrawToken(token);
}
}
// Base Workflow
export abstract class BaseWorkflow {
protected contract: ethers.Contract;
constructor(address: string, signer: ethers.Signer) {
this.contract = new ethers.Contract(address, [], signer);
}
}
// Registration Workflows
export class RegistrationWorkflows extends BaseWorkflow {
constructor(address: string, signer: ethers.Signer) {
super(address, signer);
this.contract = new ethers.Contract(address, [
'function createCollection(string,string,uint32,uint256,address,address,address,bool,bool) returns (address)',
'function mintAndRegisterIp(address,address,tuple(string,bytes32,string,bytes32)) returns (address,uint256)',
'function registerIp(address,uint256,tuple(string,bytes32,string,bytes32),tuple(address,uint256,bytes)) returns (address)'
], signer);
}
async createCollection(
name: string,
symbol: string,
maxSupply: number,
mintFee: ethers.BigNumber,
mintFeeToken: string,
mintFeeRecipient: string,
owner: string,
mintOpen: boolean,
isPublicMinting: boolean
): Promise<string> {
return this.contract.createCollection(
name,
symbol,
maxSupply,
mintFee,
mintFeeToken,
mintFeeRecipient,
owner,
mintOpen,
isPublicMinting
);
}
async mintAndRegisterIp(
spgNftContract: string,
recipient: string,
ipMetadata: WorkflowStructs.IPMetadata
): Promise<{ ipId: string; tokenId: number }> {
const result = await this.contract.mintAndRegisterIp(spgNftContract, recipient, ipMetadata);
return { ipId: result[0], tokenId: result[1].toNumber() };
}
async registerIp(
nftContract: string,
tokenId: number,
ipMetadata: WorkflowStructs.IPMetadata,
sigMetadata: WorkflowStructs.SignatureData
): Promise<string> {
return this.contract.registerIp(nftContract, tokenId, ipMetadata, sigMetadata);
}
}
// License Attachment Workflows
export class LicenseAttachmentWorkflows extends BaseWorkflow {
constructor(address: string, signer: ethers.Signer) {
super(address, signer);
this.contract = new ethers.Contract(address, [
'function registerPILTermsAndAttach(address,tuple) returns (uint256)',
'function mintAndRegisterIpAndAttachPILTerms(address,address,tuple(string,bytes32,string,bytes32),tuple) returns (address,uint256,uint256)',
'function registerIpAndAttachPILTerms(address,uint256,tuple(string,bytes32,string,bytes32),tuple,tuple(address,uint256,bytes),tuple(address,uint256,bytes)) returns (address,uint256)'
], signer);
}
async registerPILTermsAndAttach(ipId: string, terms: PILTerms): Promise<number> {
const result = await this.contract.registerPILTermsAndAttach(ipId, terms);
return result.toNumber();
}
async mintAndRegisterIpAndAttachPILTerms(
spgNftContract: string,
recipient: string,
ipMetadata: WorkflowStructs.IPMetadata,
terms: PILTerms
): Promise<{ ipId: string; tokenId: number; licenseTermsId: number }> {
const result = await this.contract.mintAndRegisterIpAndAttachPILTerms(spgNftContract, recipient, ipMetadata, terms);
return { ipId: result[0], tokenId: result[1].toNumber(), licenseTermsId: result[2].toNumber() };
}
async registerIpAndAttachPILTerms(
nftContract: string,
tokenId: number,
ipMetadata: WorkflowStructs.IPMetadata,
terms: PILTerms,
sigMetadata: WorkflowStructs.SignatureData,
sigAttach: WorkflowStructs.SignatureData
): Promise<{ ipId: string; licenseTermsId: number }> {
const result = await this.contract.registerIpAndAttachPILTerms(nftContract, tokenId, ipMetadata, terms, sigMetadata, sigAttach);
return { ipId: result[0], licenseTermsId: result[1].toNumber() };
}
}
// Grouping Workflows
export class GroupingWorkflows extends BaseWorkflow {
constructor(address: string, signer: ethers.Signer) {
super(address, signer);
this.contract = new ethers.Contract(address, [
'function mintAndRegisterIpAndAttachLicenseAndAddToGroup(address,address,address,address,uint256,tuple(string,bytes32,string,bytes32),tuple(address,uint256,bytes)) returns (address,uint256)',
'function registerIpAndAttachLicenseAndAddToGroup(address,uint256,address,address,uint256,tuple(string,bytes32,string,bytes32),tuple(address,uint256,bytes),tuple(address,uint256,bytes)) returns (address)',
'function registerGroupAndAttachLicenseAndAddIps(address,address[],address,uint256) returns (address)'
], signer);
}
async mintAndRegisterIpAndAttachLicenseAndAddToGroup(
spgNftContract: string,
groupId: string,
recipient: string,
licenseTemplate: string,
licenseTermsId: number,
ipMetadata: WorkflowStructs.IPMetadata,
sigAddToGroup: WorkflowStructs.SignatureData
): Promise<{ ipId: string; tokenId: number }> {
const result = await this.contract.mintAndRegisterIpAndAttachLicenseAndAddToGroup(
spgNftContract,
groupId,
recipient,
licenseTemplate,
licenseTermsId,
ipMetadata,
sigAddToGroup
);
return { ipId: result[0], tokenId: result[1].toNumber() };
}
async registerIpAndAttachLicenseAndAddToGroup(
nftContract: string,
tokenId: number,
groupId: string,
licenseTemplate: string,
licenseTermsId: number,
ipMetadata: WorkflowStructs.IPMetadata,
sigMetadataAndAttach: WorkflowStructs.SignatureData,
sigAddToGroup: WorkflowStructs.SignatureData
): Promise<string> {
return this.contract.registerIpAndAttachLicenseAndAddToGroup(
nftContract,
tokenId,
groupId,
licenseTemplate,
licenseTermsId,
ipMetadata,
sigMetadataAndAttach,
sigAddToGroup
);
}
async registerGroupAndAttachLicenseAndAddIps(
groupPool: string,
ipIds: string[],
licenseTemplate: string,
licenseTermsId: number
): Promise<string> {
return this.contract.registerGroupAndAttachLicenseAndAddIps(
groupPool,
ipIds,
licenseTemplate,
licenseTermsId
);
}
}
// Derivative Workflows
export class DerivativeWorkflows extends BaseWorkflow {
constructor(address: string, signer: ethers.Signer) {
super(address, signer);
this.contract = new ethers.Contract(address, [
'function mintAndRegisterIpAndMakeDerivative(address,tuple(address[],address,uint256[],bytes),tuple(string,bytes32,string,bytes32),address) returns (address,uint256)',
'function registerIpAndMakeDerivative(address,uint256,tuple(address[],address,uint256[],bytes),tuple(string,bytes32,string,bytes32),tuple(address,uint256,bytes),tuple(address,uint256,bytes)) returns (address)',
'function mintAndRegisterIpAndMakeDerivativeWithLicenseTokens(address,uint256[],bytes,tuple(string,bytes32,string,bytes32),address) returns (address,uint256)',
'function registerIpAndMakeDerivativeWithLicenseTokens(address,uint256,uint256[],bytes,tuple(string,bytes32,string,bytes32),tuple(address,uint256,bytes),tuple(address,uint256,bytes)) returns (address)'
], signer);
}
async mintAndRegisterIpAndMakeDerivative(
spgNftContract: string,
derivData: WorkflowStructs.MakeDerivative,
ipMetadata: WorkflowStructs.IPMetadata,
recipient: string
): Promise<{ ipId: string; tokenId: number }> {
try {
const result = await this.contract.mintAndRegisterIpAndMakeDerivative(
spgNftContract,
derivData,
ipMetadata,
recipient
);
return { ipId: result[0], tokenId: result[1].toNumber() };
} catch (error) {
if (error.message.includes('DerivativeWorkflows__ZeroAddressParam')) {
throw new DerivativeWorkflowsError.ZeroAddressParam();
}
throw error;
}
}
async registerIpAndMakeDerivative(
nftContract: string,
tokenId: number,
derivData: WorkflowStructs.MakeDerivative,
ipMetadata: WorkflowStructs.IPMetadata,
sigMetadata: WorkflowStructs.SignatureData,
sigRegister: WorkflowStructs.SignatureData
): Promise<string> {
try {
return await this.contract.registerIpAndMakeDerivative(
nftContract,
tokenId,
derivData,
ipMetadata,
sigMetadata,
sigRegister
);
} catch (error) {
if (error.message.includes('DerivativeWorkflows__ZeroAddressParam')) {
throw new DerivativeWorkflowsError.ZeroAddressParam();
}
throw error;
}
}
async mintAndRegisterIpAndMakeDerivativeWithLicenseTokens(
spgNftContract: string,
licenseTokenIds: number[],
royaltyContext: string,
ipMetadata: WorkflowStructs.IPMetadata,
recipient: string
): Promise<{ ipId: string; tokenId: number }> {
try {
const result = await this.contract.mintAndRegisterIpAndMakeDerivativeWithLicenseTokens(
spgNftContract,
licenseTokenIds,
royaltyContext,
ipMetadata,
recipient
);
return { ipId: result[0], tokenId: result[1].toNumber() };
} catch (error) {
if (error.message.includes('DerivativeWorkflows__EmptyLicenseTokens')) {
throw new DerivativeWorkflowsError.EmptyLicenseTokens();
}
if (error.message.includes('DerivativeWorkflows__CallerAndNotTokenOwner')) {
const match = error.message.match(/(\d+), (0x[a-fA-F0-9]{40}), (0x[a-fA-F0-9]{40})/);
if (match) {
throw new DerivativeWorkflowsError.CallerAndNotTokenOwner(match[1], match[2], match[3]);
}
}
throw error;
}
}
async registerIpAndMakeDerivativeWithLicenseTokens(
nftContract: string,
tokenId: number,
licenseTokenIds: number[],
royaltyContext: string,
ipMetadata: WorkflowStructs.IPMetadata,
sigMetadata: WorkflowStructs.SignatureData,
sigRegister: WorkflowStructs.SignatureData
): Promise<string> {
try {
return await this.contract.registerIpAndMakeDerivativeWithLicenseTokens(
nftContract,
tokenId,
licenseTokenIds,
royaltyContext,
ipMetadata,
sigMetadata,
sigRegister
);
} catch (error) {
if (error.message.includes('DerivativeWorkflows__EmptyLicenseTokens')) {
throw new DerivativeWorkflowsError.EmptyLicenseTokens();
}
if (error.message.includes('DerivativeWorkflows__CallerAndNotTokenOwner')) {
const match = error.message.match(/(\d+), (0x[a-fA-F0-9]{40}), (0x[a-fA-F0-9]{40})/);
if (match) {
throw new DerivativeWorkflowsError.CallerAndNotTokenOwner(match[1], match[2], match[3]);
}
}
throw error;
}
}
}
// Helper functions
export class PermissionHelper {
static async setPermissionForModule(
ipId: string,
module: string,
accessController: string,
selector: string,
sigData: WorkflowStructs.SignatureData,
signer: ethers.Signer
): Promise<ethers.ContractTransaction> {
const ipAccount = new ethers.Contract(ipId, ['function executeWithSig(address,uint256,bytes,address,uint256,bytes)'], signer);
return ipAccount.executeWithSig(
accessController,
0,
ethers.utils.defaultAbiCoder.encode(
['address', 'address', 'address', 'bytes4', 'uint8'],
[ipId, await signer.getAddress(), module, selector, 1] // 1 represents AccessPermission.ALLOW
),
sigData.signer,
sigData.deadline,
sigData.signature
);
}
static async setBatchPermissionForModules(
ipId: string,
accessController: string,
modules: string[],
selectors: string[],
sigData: WorkflowStructs.SignatureData,
signer: ethers.Signer
): Promise<ethers.ContractTransaction> {
const ipAccount = new ethers.Contract(ipId, ['function executeWithSig(address,uint256,bytes,address,uint256,bytes)'], signer);
const permissionList = modules.map((module, index) => ({
ipAccount: ipId,
signer: await signer.getAddress(),
to: module,
func: selectors[index],
permission: 1 // AccessPermission.ALLOW
}));
return ipAccount.executeWithSig(
accessController,
0,
ethers.utils.defaultAbiCoder.encode(['tuple(address,address,address,bytes4,uint8)[]'], [permissionList]),
sigData.signer,
sigData.deadline,
sigData.signature
);
}
}
export class MetadataHelper {
static async setMetadataWithSig(
ipId: string,
coreMetadataModule: string,
accessController: string,
ipMetadata: WorkflowStructs.IPMetadata,
sigData: WorkflowStructs.SignatureData,
signer: ethers.Signer
): Promise<ethers.ContractTransaction> {
if (sigData.signer !== ethers.constants.AddressZero && !sigData.deadline.isZero() && sigData.signature !== '0x') {
await PermissionHelper.setPermissionForModule(
ipId,
coreMetadataModule,
accessController,
'0x12345678', // Replace with actual selector for setAll
sigData,
signer
);
}
return this.setMetadata(ipId, coreMetadataModule, ipMetadata, signer);
}
static async setMetadata(
ipId: string,
coreMetadataModule: string,
ipMetadata: WorkflowStructs.IPMetadata,
signer: ethers.Signer
): Promise<ethers.ContractTransaction> {
const metadataModuleContract = new ethers.Contract(
coreMetadataModule,
['function setAll(address,string,bytes32,bytes32)'],
signer
);
return metadataModuleContract.setAll(
ipId,
ipMetadata.ipMetadataURI,
ipMetadata.ipMetadataHash,
ipMetadata.nftMetadataHash
);
}
}
export class LicensingHelper {
static async registerPILTermsAndAttach(
ipId: string,
pilTemplate: string,
licensingModule: string,
licenseRegistry: string,
terms: PILTerms,
signer: ethers.Signer
): Promise<{ licenseTermsId: number; tx: ethers.ContractTransaction }> {
const pilTemplateContract = new ethers.Contract(
pilTemplate,
['function registerLicenseTerms((uint256,string,string,uint256,uint256,bytes32,bytes32))'],
signer
);
const licenseTermsId = await pilTemplateContract.callStatic.registerLicenseTerms(terms);
const tx = await pilTemplateContract.registerLicenseTerms(terms);
await this.attachLicenseTerms(ipId, licensingModule, licenseRegistry, pilTemplate, licenseTermsId, signer);
return { licenseTermsId, tx };
}
static async attachLicenseTerms(
ipId: string,
licensingModule: string,
licenseRegistry: string,
licenseTemplate: string,
licenseTermsId: number,
signer: ethers.Signer
): Promise<ethers.ContractTransaction | undefined> {
const licenseRegistryContract = new ethers.Contract(
licenseRegistry,
['function hasIpAttachedLicenseTerms(address,address,uint256)'],
signer
);
const isAttached = await licenseRegistryContract.hasIpAttachedLicenseTerms(ipId, licenseTemplate, licenseTermsId);
if (!isAttached) {
const licensingModuleContract = new ethers.Contract(
licensingModule,
['function attachLicenseTerms(address,address,uint256)'],
signer
);
return licensingModuleContract.attachLicenseTerms(ipId, licenseTemplate, licenseTermsId);
}
}
}
// Export all components
export {
SPGNFT,
RegistrationWorkflows,
LicenseAttachmentWorkflows,
GroupingWorkflows,
DerivativeWorkflows,
PermissionHelper,
MetadataHelper,
LicensingHelper
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment