From a013af71d2179fa78c26b9e7aac13c81456af63e Mon Sep 17 00:00:00 2001 From: bonnie Date: Fri, 12 Apr 2024 11:17:22 +0800 Subject: [PATCH] Add ipAsset module methods including registerDerivative and registerDerivativeWithLicenseTokens --- packages/core-sdk/src/index.ts | 9 +- packages/core-sdk/src/resources/ipAsset.ts | 124 ++++- packages/core-sdk/src/resources/license.ts | 17 +- .../core-sdk/src/types/resources/ipAsset.ts | 23 + .../core-sdk/src/types/resources/license.ts | 4 +- .../storyTestNet/ipAsset.storyTest.test.ts | 152 ++++-- .../storyTestNet/license.storyTest.test.ts | 10 +- .../storyTestNet/royalty.storyTest.test.ts | 458 +++++++++--------- 8 files changed, 472 insertions(+), 325 deletions(-) diff --git a/packages/core-sdk/src/index.ts b/packages/core-sdk/src/index.ts index ff45c0e8..612edaa6 100644 --- a/packages/core-sdk/src/index.ts +++ b/packages/core-sdk/src/index.ts @@ -9,7 +9,14 @@ export { DisputeClient } from "./resources/dispute"; export type { StoryConfig } from "./types/config"; export type { Hex, TypedData } from "./types/common"; -export type { RegisterIpResponse, RegisterRequest } from "./types/resources/ipAsset"; +export type { + RegisterIpResponse, + RegisterRequest, + RegisterDerivativeResponse, + RegisterDerivativeRequest, + RegisterDerivativeWithLicenseTokensRequest, + RegisterDerivativeWithLicenseTokensResponse, +} from "./types/resources/ipAsset"; export type { MintLicenseTokensResponse, diff --git a/packages/core-sdk/src/resources/ipAsset.ts b/packages/core-sdk/src/resources/ipAsset.ts index 0a27eaa1..915d0b1e 100644 --- a/packages/core-sdk/src/resources/ipAsset.ts +++ b/packages/core-sdk/src/resources/ipAsset.ts @@ -1,37 +1,38 @@ -import { Hex, PublicClient, WalletClient, getAddress } from "viem"; +import { Hex, PublicClient, WalletClient, getAddress, zeroAddress } from "viem"; -import { chain, parseToBigInt, waitTxAndFilterLog } from "../utils/utils"; -import { getIPAssetRegistryConfig } from "../abi/config"; +import { chain, parseToBigInt, waitTx, waitTxAndFilterLog } from "../utils/utils"; +import { + getIPAssetRegistryConfig, + getLicenseTemplateConfig, + getLicensingModuleConfig, +} from "../abi/config"; import { SupportedChainIds } from "../types/config"; import { handleError } from "../utils/errors"; -import { RegisterIpResponse, RegisterRequest } from "../types/resources/ipAsset"; +import { + RegisterDerivativeRequest, + RegisterDerivativeResponse, + RegisterDerivativeWithLicenseTokensRequest, + RegisterDerivativeWithLicenseTokensResponse, + RegisterIpResponse, + RegisterRequest, +} from "../types/resources/ipAsset"; export class IPAssetClient { private readonly wallet: WalletClient; private readonly rpcClient: PublicClient; private readonly chainId: SupportedChainIds; public ipAssetRegistryConfig; + public licenseModuleConfig; + public licenseTemplateConfig; constructor(rpcClient: PublicClient, wallet: WalletClient, chainId: SupportedChainIds) { this.wallet = wallet; this.rpcClient = rpcClient; this.chainId = chainId; this.ipAssetRegistryConfig = getIPAssetRegistryConfig(chainId); + this.licenseModuleConfig = getLicensingModuleConfig(chainId); + this.licenseTemplateConfig = getLicenseTemplateConfig(chainId); } - private async isNFTRegistered(tokenAddress: Hex, tokenId: bigint): Promise { - const ipId = await this.rpcClient.readContract({ - ...this.ipAssetRegistryConfig, - functionName: "ipId", - args: [parseToBigInt(chain[this.chainId]), tokenAddress, tokenId], - }); - const isRegistered = await this.rpcClient.readContract({ - ...this.ipAssetRegistryConfig, - functionName: "isRegistered", - args: [ipId], - }); - return isRegistered ? ipId : "0x"; - } - /** * Registers an NFT as IP, creating a corresponding IP record. * @param request The request object that contains all data needed to register IP. @@ -68,4 +69,91 @@ export class IPAssetClient { handleError(error, "Failed to register IP"); } } + + /** + * Registers a derivative directly with parent IP's license terms, without needing license tokens, + * and attaches the license terms of the parent IPs to the derivative IP. + * The license terms must be attached to the parent IP before calling this function. + * All IPs attached default license terms by default. + * The derivative IP owner must be the caller or an authorized operator. + * @param request The request object that contains all data needed to register derivative IP. + * @param request.childIpId The derivative IP ID. + * @param request.parentIpIds The parent IP IDs. + * @param request.licenseTermsIds The IDs of the license terms that the parent IP supports. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash. + */ + public async registerDerivative( + request: RegisterDerivativeRequest, + ): Promise { + try { + const { request: call } = await this.rpcClient.simulateContract({ + ...this.licenseModuleConfig, + functionName: "registerDerivative", + args: [ + request.childIpId, + request.parentIpIds, + request.licenseTermsIds.map((id) => BigInt(id)), + request.licenseTemplate || this.licenseTemplateConfig.address, + zeroAddress, + ], + account: this.wallet.account, + }); + const txHash = await this.wallet.writeContract(call); + if (request.txOptions?.waitForTransaction) { + await waitTx(this.rpcClient, txHash); + return { txHash }; + } else { + return { txHash }; + } + } catch (error) { + handleError(error, "Failed to register derivative"); + } + } + + /** + * Registers a derivative with license tokens. + * the derivative IP is registered with license tokens minted from the parent IP's license terms. + * the license terms of the parent IPs issued with license tokens are attached to the derivative IP. + * the caller must be the derivative IP owner or an authorized operator. + * @param request The request object that contains all data needed to register derivative license tokens. + * @param request.childIpId The derivative IP ID. + * @param request.licenseTokenIds The IDs of the license tokens. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash. + */ + public async registerDerivativeWithLicenseTokens( + request: RegisterDerivativeWithLicenseTokensRequest, + ): Promise { + try { + const { request: call } = await this.rpcClient.simulateContract({ + ...this.licenseModuleConfig, + functionName: "registerDerivativeWithLicenseTokens", + args: [request.childIpId, request.licenseTokenIds.map((id) => BigInt(id)), zeroAddress], + account: this.wallet.account, + }); + const txHash = await this.wallet.writeContract(call); + if (request.txOptions?.waitForTransaction) { + await waitTx(this.rpcClient, txHash); + return { txHash: txHash }; + } else { + return { txHash: txHash }; + } + } catch (error) { + handleError(error, "Failed to register derivative with license tokens"); + } + } + private async isNFTRegistered(tokenAddress: Hex, tokenId: bigint): Promise { + const ipId = await this.rpcClient.readContract({ + ...this.ipAssetRegistryConfig, + functionName: "ipId", + args: [parseToBigInt(chain[this.chainId]), tokenAddress, tokenId], + }); + const isRegistered = await this.rpcClient.readContract({ + ...this.ipAssetRegistryConfig, + functionName: "isRegistered", + args: [ipId], + }); + return isRegistered ? ipId : "0x"; + } } diff --git a/packages/core-sdk/src/resources/license.ts b/packages/core-sdk/src/resources/license.ts index c0e987ca..4879609e 100644 --- a/packages/core-sdk/src/resources/license.ts +++ b/packages/core-sdk/src/resources/license.ts @@ -88,7 +88,7 @@ export class LicenseClient { }; const licenseTermsId = await this.getLicenseTermsId(licenseTerms); if (licenseTermsId !== 0) { - return { licenseId: licenseTermsId.toString() }; + return { licenseTermsId: licenseTermsId.toString() }; } const { request: call } = await this.rpcClient.simulateContract({ ...this.licenseTemplateConfig, @@ -103,7 +103,7 @@ export class LicenseClient { ...this.licenseTemplateConfig, eventName: "LicenseTermsRegistered", }); - return { txHash: txHash, licenseId: targetLogs[0].args.licenseTermsId.toString() }; + return { txHash: txHash, licenseTermsId: targetLogs[0].args.licenseTermsId.toString() }; } else { return { txHash: txHash }; } @@ -146,7 +146,7 @@ export class LicenseClient { }; const licenseTermsId = await this.getLicenseTermsId(licenseTerms); if (licenseTermsId !== 0) { - return { licenseId: licenseTermsId.toString() }; + return { licenseTermsId: licenseTermsId.toString() }; } const { request: call } = await this.rpcClient.simulateContract({ @@ -162,7 +162,7 @@ export class LicenseClient { ...this.licenseTemplateConfig, eventName: "LicenseTermsRegistered", }); - return { txHash: txHash, licenseId: targetLogs[0].args.licenseTermsId.toString() }; + return { txHash: txHash, licenseTermsId: targetLogs[0].args.licenseTermsId.toString() }; } else { return { txHash: txHash }; } @@ -205,7 +205,7 @@ export class LicenseClient { }; const licenseTermsId = await this.getLicenseTermsId(licenseTerms); if (licenseTermsId !== 0) { - return { licenseId: licenseTermsId.toString() }; + return { licenseTermsId: licenseTermsId.toString() }; } const { request: call } = await this.rpcClient.simulateContract({ ...this.licenseTemplateConfig, @@ -220,7 +220,7 @@ export class LicenseClient { ...this.licenseTemplateConfig, eventName: "LicenseTermsRegistered", }); - return { txHash: txHash, licenseId: targetLogs[0].args.licenseTermsId.toString() }; + return { txHash: txHash, licenseTermsId: targetLogs[0].args.licenseTermsId.toString() }; } else { return { txHash: txHash }; } @@ -230,10 +230,9 @@ export class LicenseClient { } /** - * Attaches license terms to an IP, and the function must be called by the IP owner or an authorized operator. + * Attaches license terms to an IP. * @param request The request object that contains all data needed to attach license terms. - @param request.ipId The IP ID. - @param request.tokenAddress The address of the NFT. + @param request.ipId The address of the IP to which the license terms are attached. @param request.licenseTemplate The address of the license template. @param request.licenseTermsId The ID of the license terms. * @param request.txOptions [Optional] The transaction options. diff --git a/packages/core-sdk/src/types/resources/ipAsset.ts b/packages/core-sdk/src/types/resources/ipAsset.ts index 1f8aa71b..cf78abd7 100644 --- a/packages/core-sdk/src/types/resources/ipAsset.ts +++ b/packages/core-sdk/src/types/resources/ipAsset.ts @@ -12,3 +12,26 @@ export type RegisterRequest = { tokenId: string; txOptions?: TxOptions; }; + +export type RegisterDerivativeWithLicenseTokensRequest = { + childIpId: Hex; + licenseTokenIds: string[]; + txOptions?: TxOptions; +}; + +export type RegisterDerivativeWithLicenseTokensResponse = { + txHash: string; +}; + +export type RegisterDerivativeRequest = { + childIpId: Hex; + parentIpIds: Hex[]; + licenseTermsIds: string[]; + licenseTemplate?: Hex; + txOptions?: TxOptions; +}; + +export type RegisterDerivativeResponse = { + txHash?: string; + childIpId?: Hex; +}; diff --git a/packages/core-sdk/src/types/resources/license.ts b/packages/core-sdk/src/types/resources/license.ts index b78d7fbe..120ec505 100644 --- a/packages/core-sdk/src/types/resources/license.ts +++ b/packages/core-sdk/src/types/resources/license.ts @@ -37,7 +37,7 @@ export type LicenseTerms = { export type LicenseTermsIdResponse = number; export type RegisterLicenseTermsResponse = { - licenseId?: string; + licenseTermsId?: string; txHash?: string; }; @@ -56,8 +56,8 @@ export type RegisterCommercialRemixPILRequest = { export type AttachLicenseTermsRequest = { ipId: Hex; - licenseTemplate?: Hex; licenseTermsId: string; + licenseTemplate?: Hex; txOptions?: TxOptions; }; diff --git a/packages/core-sdk/test/integration/storyTestNet/ipAsset.storyTest.test.ts b/packages/core-sdk/test/integration/storyTestNet/ipAsset.storyTest.test.ts index 907d0a21..98cf60c6 100644 --- a/packages/core-sdk/test/integration/storyTestNet/ipAsset.storyTest.test.ts +++ b/packages/core-sdk/test/integration/storyTestNet/ipAsset.storyTest.test.ts @@ -14,6 +14,11 @@ import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); const expect = chai.expect; + +const parentIpId = "0xca2def24ec4A50633a922245F84518504aaAE562"; +const noCommercialLicenseTermsId = "6"; +let startTokenId = 120; +let ipId: Hex; describe("IP Asset Functions in storyTestnet", () => { let client: StoryClient; before(function () { @@ -28,70 +33,115 @@ describe("IP Asset Functions in storyTestnet", () => { client.license.licenseRegistryConfig = getLicenseRegistryConfig("1513"); client.license.licensingModuleConfig = getLicensingModuleConfig("1513"); }); - - describe("Create IP Asset", async function () { - let tokenId: string = "1"; - before(async () => { - //TODO: wait for MockERC721 redeploy - // const baseConfig = { - // chain: chainStringToViemChain("storyTestnet"), - // transport: http(process.env.STORY_TEST_NET_RPC_PROVIDER_URL), - // } as const; - // const publicClient = createPublicClient(baseConfig); - // const walletClient = createWalletClient({ - // ...baseConfig, - // account: privateKeyToAccount(process.env.STORY_TEST_NET_WALLET_PRIVATE_KEY as Hex), - // }); - // const { request } = await publicClient.simulateContract({ - // abi: [ - // { - // inputs: [ - // { internalType: "address", name: "to", type: "address" }, - // { - // internalType: "uint256", - // name: "tokenId", - // type: "uint256", - // }, - // ], - // name: "mintId", - // outputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], - // stateMutability: "nonpayable", - // type: "function", - // }, - // ], - // address: storyTestnetAddress.MockERC721, - // functionName: "mintId", - // args: [process.env.STORY_TEST_NET_TEST_WALLET_ADDRESS as Hex, BigInt(1051)], - // }); - // const hash = await walletClient.writeContract(request); - // const { logs } = await publicClient.waitForTransactionReceipt({ - // hash, - // }); - // if (logs[0].topics[3]) { - // tokenId = parseInt(logs[0].topics[3], 16).toString(); - // } + const getTokenId = async (tokenId: number): Promise => { + const baseConfig = { + chain: chainStringToViemChain("storyTestnet"), + transport: http(process.env.STORY_TEST_NET_RPC_PROVIDER_URL), + } as const; + const publicClient = createPublicClient(baseConfig); + const walletClient = createWalletClient({ + ...baseConfig, + account: privateKeyToAccount(process.env.STORY_TEST_NET_WALLET_PRIVATE_KEY as Hex), }); - + const { request } = await publicClient.simulateContract({ + abi: [ + { + inputs: [ + { internalType: "address", name: "to", type: "address" }, + { + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "mintId", + outputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + ], + address: storyTestnetAddress.MockERC721, + functionName: "mintId", + args: [process.env.STORY_TEST_NET_TEST_WALLET_ADDRESS as Hex, BigInt(tokenId)], + account: walletClient.account, + }); + const hash = await walletClient.writeContract(request); + const { logs } = await publicClient.waitForTransactionReceipt({ + hash, + }); + if (logs[0].topics[3]) { + return parseInt(logs[0].topics[3], 16).toString(); + } + }; + describe("Create IP Asset", async function () { it("should not throw error when registering a IP Asset", async () => { - expect(tokenId).to.be.a("string"); - expect(tokenId).not.empty; - + const tokenId = await getTokenId(startTokenId++); const waitForTransaction: boolean = true; const response = await expect( client.ipAsset.register({ tokenContract: storyTestnetAddress.MockERC721, - tokenId: tokenId, + tokenId: tokenId!, txOptions: { waitForTransaction: waitForTransaction, }, }), ).to.not.be.rejected; - // expect(response.txHash).to.be.a("string"); - // expect(response.txHash).not.empty; if (waitForTransaction) { - expect(response.ipId).to.be.a("string"); - expect(response.ipId).not.empty; + expect(response.ipId).to.be.a("string").and.not.empty; + ipId = response.ipId; } }); + + it("should not throw error when registering derivative", async () => { + console.log("ipId:", ipId); + await client.license.attachLicenseTerms({ + ipId: parentIpId, + licenseTermsId: noCommercialLicenseTermsId, + txOptions: { + waitForTransaction: true, + }, + }); + const response = await expect( + client.ipAsset.registerDerivative({ + childIpId: ipId, + parentIpIds: [parentIpId], + licenseTermsIds: [noCommercialLicenseTermsId], + txOptions: { + waitForTransaction: true, + }, + }), + ).to.not.be.rejected; + expect(response.txHash).to.be.a("string").and.not.empty; + }); + + it("should not throw error when registering derivative with license tokens", async () => { + const tokenId = await getTokenId(startTokenId++); + const ipId = ( + await client.ipAsset.register({ + tokenContract: storyTestnetAddress.MockERC721, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + const mintLicenseTokensResult = await client.license.mintLicenseTokens({ + licenseTermsId: noCommercialLicenseTermsId, + licensorIpId: parentIpId, + txOptions: { + waitForTransaction: true, + }, + }); + const response = await expect( + client.ipAsset.registerDerivativeWithLicenseTokens({ + childIpId: ipId, + licenseTokenIds: [mintLicenseTokensResult.licenseTokenId!], + txOptions: { + waitForTransaction: true, + }, + }), + ).to.not.be.rejected; + expect(response.txHash).to.be.a("string").not.empty; + }); }); }); diff --git a/packages/core-sdk/test/integration/storyTestNet/license.storyTest.test.ts b/packages/core-sdk/test/integration/storyTestNet/license.storyTest.test.ts index edcfd651..48422b61 100644 --- a/packages/core-sdk/test/integration/storyTestNet/license.storyTest.test.ts +++ b/packages/core-sdk/test/integration/storyTestNet/license.storyTest.test.ts @@ -15,7 +15,7 @@ import { storyTestnetAddress } from "../../env"; chai.use(chaiAsPromised); const expect = chai.expect; -describe("License Functions in storyTestnet", () => { +describe.skip("License Functions in storyTestnet", () => { let client: StoryClient; before(function () { const config: StoryConfig = { @@ -40,7 +40,7 @@ describe("License Functions in storyTestnet", () => { }, }); // expect(result.txHash).to.a("string").and.not.empty; - expect(result.licenseId).to.be.a("string").and.not.empty; + expect(result.licenseTermsId).to.be.a("string").and.not.empty; }); it("should not throw error when registering license with commercial use", async function () { @@ -52,7 +52,7 @@ describe("License Functions in storyTestnet", () => { }, }); // expect(result.txHash).to.be.a("string").and.not.empty; - expect(result.licenseId).to.be.a("string").not.empty; + expect(result.licenseTermsId).to.be.a("string").not.empty; }); it("should not throw error when registering license with commercial Remix use", async function () { @@ -65,7 +65,7 @@ describe("License Functions in storyTestnet", () => { }, }); // expect(result.txHash).to.be.a("string").and.not.empty; - expect(result.licenseId).to.be.a("string").and.not.empty; + expect(result.licenseTermsId).to.be.a("string").and.not.empty; }); }); @@ -86,7 +86,7 @@ describe("License Functions in storyTestnet", () => { waitForTransaction: true, }, }); - licenseId = registerLicenseResult.licenseId!; + licenseId = registerLicenseResult.licenseTermsId!; }); it("should not throw error when attach License Terms", async function () { diff --git a/packages/core-sdk/test/integration/storyTestNet/royalty.storyTest.test.ts b/packages/core-sdk/test/integration/storyTestNet/royalty.storyTest.test.ts index 39fae201..4ef58157 100644 --- a/packages/core-sdk/test/integration/storyTestNet/royalty.storyTest.test.ts +++ b/packages/core-sdk/test/integration/storyTestNet/royalty.storyTest.test.ts @@ -1,245 +1,225 @@ -// import chai from "chai"; -// import { StoryClient, StoryConfig } from "../../../src"; -// import { -// Hex, -// http, -// Account, -// createPublicClient, -// createWalletClient, -// PublicClient, -// WalletClient, -// Abi, -// } from "viem"; -// import { privateKeyToAccount } from "viem/accounts"; -// import { -// IPAccountABI, -// getPILicenseTemplateConfig, -// getIPAssetRegistryConfig, -// getLicenseRegistryConfig, -// getLicensingModuleConfig, -// getRoyaltyPolicyLAPConfig, -// getRoyaltyVaultImplConfig, -// } from "../../config"; -// import chaiAsPromised from "chai-as-promised"; -// import { storyTestnetAddress } from "../../env"; -// import { chainStringToViemChain, waitTx } from "../../../src/utils/utils"; -// chai.use(chaiAsPromised); -// const expect = chai.expect; +import chai from "chai"; +import { StoryClient, StoryConfig } from "../../../src"; +import { + Hex, + http, + Account, + createPublicClient, + createWalletClient, + PublicClient, + WalletClient, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { + getIPAssetRegistryConfig, + getLicenseRegistryConfig, + getLicensingModuleConfig, + getRoyaltyPolicyLAPConfig, + getRoyaltyVaultImplConfig, +} from "../../config"; +import chaiAsPromised from "chai-as-promised"; +import { storyTestnetAddress } from "../../env"; +import { chainStringToViemChain, waitTx } from "../../../src/utils/utils"; +chai.use(chaiAsPromised); +const expect = chai.expect; -// describe.skip("Test royalty Functions", () => { -// let client: StoryClient; -// let senderAddress: string; -// let publicClient: PublicClient; -// let walletClient: WalletClient; -// before(function () { -// const config: StoryConfig = { -// chainId: "storyTestnet", -// transport: http(process.env.STORY_TEST_NET_RPC_PROVIDER_URL), -// account: privateKeyToAccount(process.env.STORY_TEST_NET_WALLET_PRIVATE_KEY as Hex), -// }; -// const configAccount: Account = config.account as Account; -// senderAddress = configAccount.address; -// client = StoryClient.newClient(config); -// client.policy.ipAccountABI = IPAccountABI; -// client.policy.getPILicenseTemplateConfig = getPILicenseTemplateConfig("storyTestnet"); -// client.ipAsset.ipAssetRegistryConfig = getIPAssetRegistryConfig("1513"); -// client.license.licenseRegistryConfig = getLicenseRegistryConfig("1513"); -// client.license.licensingModuleConfig = getLicensingModuleConfig("1513"); -// client.royalty.royaltyPolicyLAPConfig = getRoyaltyPolicyLAPConfig("1513"); -// client.royalty.royaltyVaultImplConfig = getRoyaltyVaultImplConfig("1513"); -// const baseConfig = { -// chain: chainStringToViemChain("storyTestnet"), -// transport: http(process.env.STORY_TEST_NET_RPC_PROVIDER_URL), -// } as const; -// publicClient = createPublicClient(baseConfig); -// walletClient = createWalletClient({ -// ...baseConfig, -// account: privateKeyToAccount(process.env.STORY_TEST_NET_WALLET_PRIVATE_KEY as Hex), -// }); -// }); -// describe("Royalty in storyTestNet", async function () { -// let ipId1: Hex; -// let ipId2: Hex; -// let tokenId = 44; -// const getIpId = async (): Promise => { -// tokenId++; +describe.skip("Test royalty Functions", () => { + let client: StoryClient; + let senderAddress: string; + let publicClient: PublicClient; + let walletClient: WalletClient; + before(function () { + const config: StoryConfig = { + chainId: "storyTestnet", + transport: http(process.env.STORY_TEST_NET_RPC_PROVIDER_URL), + account: privateKeyToAccount(process.env.STORY_TEST_NET_WALLET_PRIVATE_KEY as Hex), + }; + const configAccount: Account = config.account as Account; + senderAddress = configAccount.address; + client = StoryClient.newClient(config); + client.ipAsset.ipAssetRegistryConfig = getIPAssetRegistryConfig("1513"); + client.license.licenseRegistryConfig = getLicenseRegistryConfig("1513"); + client.license.licensingModuleConfig = getLicensingModuleConfig("1513"); + client.royalty.royaltyPolicyLAPConfig = getRoyaltyPolicyLAPConfig("1513"); + client.royalty.royaltyVaultImplConfig = getRoyaltyVaultImplConfig("1513"); + const baseConfig = { + chain: chainStringToViemChain("storyTestnet"), + transport: http(process.env.STORY_TEST_NET_RPC_PROVIDER_URL), + } as const; + publicClient = createPublicClient(baseConfig); + walletClient = createWalletClient({ + ...baseConfig, + account: privateKeyToAccount(process.env.STORY_TEST_NET_WALLET_PRIVATE_KEY as Hex), + }); + }); + describe("Royalty in storyTestNet", async function () { + let ipId1: Hex; + let ipId2: Hex; + let tokenId = 44; + const getIpId = async (): Promise => { + tokenId++; -// const { request } = await publicClient.simulateContract({ -// abi: [ -// { -// inputs: [ -// { internalType: "address", name: "to", type: "address" }, -// { -// internalType: "uint256", -// name: "tokenId", -// type: "uint256", -// }, -// ], -// name: "mintId", -// outputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], -// stateMutability: "nonpayable", -// type: "function", -// }, -// ], -// address: storyTestnetAddress.MockERC721, -// functionName: "mintId", -// args: [process.env.STORY_TEST_NET_TEST_WALLET_ADDRESS as Hex, BigInt(tokenId)], -// account: walletClient.account, -// }); -// const hash = await walletClient.writeContract(request); -// const { logs } = await publicClient.waitForTransactionReceipt({ -// hash, -// }); -// const response = await client.ipAsset.register({ -// tokenContract: storyTestnetAddress.MockERC721, -// tokenId: parseInt(logs[0].topics[3]!, 16).toString(), -// txOptions: { -// waitForTransaction: true, -// }, -// }); -// return response.ipId! as Hex; -// }; -// const getCommercialPolicyId = async (): Promise => { -// const response = await client.policy.registerPILCommercialUsePolicy({ -// commercialRevShare: 100, -// territories: [], -// distributionChannels: [], -// contentRestrictions: [], -// txOptions: { -// waitForTransaction: true, -// }, -// }); -// return response.policyId!; -// }; + const { request } = await publicClient.simulateContract({ + abi: [ + { + inputs: [ + { internalType: "address", name: "to", type: "address" }, + { + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "mintId", + outputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + ], + address: storyTestnetAddress.MockERC721, + functionName: "mintId", + args: [process.env.STORY_TEST_NET_TEST_WALLET_ADDRESS as Hex, BigInt(tokenId)], + account: walletClient.account, + }); + const hash = await walletClient.writeContract(request); + const { logs } = await publicClient.waitForTransactionReceipt({ + hash, + }); + const response = await client.ipAsset.register({ + tokenContract: storyTestnetAddress.MockERC721, + tokenId: parseInt(logs[0].topics[3]!, 16).toString(), + txOptions: { + waitForTransaction: true, + }, + }); + return response.ipId! as Hex; + }; + const getCommercialPolicyId = async (): Promise => { + const response = await client.license.registerCommercialUsePIL({ + mintingFee: "1", + currency: storyTestnetAddress.MockERC20, + txOptions: { + waitForTransaction: true, + }, + }); + return response.licenseTermsId!; + }; -// const addPolicyToIp = async (ipId: Hex, policyId: string) => { -// await client.policy.addPolicyToIp({ -// ipId, -// policyId, -// txOptions: { -// waitForTransaction: true, -// }, -// }); -// }; + const attachLicenseTerms = async (ipId: Hex, licenseTermsId: string) => { + await client.license.attachLicenseTerms({ + ipId, + licenseTermsId: licenseTermsId, + txOptions: { + waitForTransaction: true, + }, + }); + }; -// const mintLicense = async (ipId: Hex, policyId: string) => { -// const mintLicenseResponse = await client.license.mintLicense({ -// policyId, -// licensorIpId: ipId, -// mintAmount: 1, -// receiverAddress: process.env.STORY_TEST_NET_TEST_WALLET_ADDRESS as Hex, -// txOptions: { -// waitForTransaction: true, -// }, -// }); -// return mintLicenseResponse.licenseId; -// }; + const registerDerivative = async (ipId: Hex, parentIpId: Hex, licenseTermsIds: string) => { + const result = await client.ipAsset.registerDerivative({ + childIpId: ipId, + parentIpIds: [parentIpId], + licenseTermsIds: [licenseTermsIds], + txOptions: { + waitForTransaction: false, + }, + }); + }; + before(async () => { + ipId1 = await getIpId(); + ipId2 = await getIpId(); + const licenseTermsId = await getCommercialPolicyId(); + await attachLicenseTerms(ipId1, licenseTermsId); + await registerDerivative(ipId2, ipId1, licenseTermsId); + }); -// const linkIpToParents = async (licenseIds: string[], childIpId: Hex) => { -// await client.license.linkIpToParent({ -// licenseIds: licenseIds, -// childIpId: childIpId, -// txOptions: { -// waitForTransaction: true, -// }, -// }); -// }; -// before(async () => { -// ipId1 = await getIpId(); -// ipId2 = await getIpId(); -// const commercialPolicyId = await getCommercialPolicyId(); -// await addPolicyToIp(ipId1, commercialPolicyId); -// const licenseForIpId1 = await mintLicense(ipId1, commercialPolicyId); -// await linkIpToParents([licenseForIpId1!], ipId2); -// }); + it("should not throw error when pay royalty on behalf", async () => { + //1. approve the spender + const abi = [ + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ]; + const { request: call } = await publicClient.simulateContract({ + abi: abi, + address: storyTestnetAddress.MockERC20, + functionName: "approve", + args: [client.royalty.royaltyPolicyLAPConfig.address, BigInt(100)], + account: walletClient.account, + }); + const approveHash = await walletClient.writeContract(call); + await waitTx(publicClient, approveHash); + //2. mint the token + const { request } = await publicClient.simulateContract({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "mint", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + ], + address: storyTestnetAddress.MockERC20, + functionName: "mint", + account: walletClient.account, + args: [process.env.STORY_TEST_NET_TEST_WALLET_ADDRESS! as Hex, BigInt(100)], + }); + const mintHash = await walletClient.writeContract(request); + await waitTx(publicClient, mintHash); + const response = await client.royalty.payRoyaltyOnBehalf({ + receiverIpId: ipId1, + payerIpId: ipId2, + token: storyTestnetAddress.MockERC20, + amount: BigInt(10), + txOptions: { + waitForTransaction: true, + }, + }); + expect(response.txHash).to.be.a("string").not.empty; + }); -// it("should not throw error when pay royalty on behalf", async () => { -// //1. approve the spender -// const abi = [ -// { -// inputs: [ -// { -// internalType: "address", -// name: "spender", -// type: "address", -// }, -// { -// internalType: "uint256", -// name: "value", -// type: "uint256", -// }, -// ], -// name: "approve", -// outputs: [ -// { -// internalType: "bool", -// name: "", -// type: "bool", -// }, -// ], -// stateMutability: "nonpayable", -// type: "function", -// }, -// ]; -// const { request: call } = await publicClient.simulateContract({ -// abi: abi, -// address: storyTestnetAddress.MockERC20, -// functionName: "approve", -// args: [client.royalty.royaltyPolicyLAPConfig.address, BigInt(100)], -// account: walletClient.account, -// }); -// const approveHash = await walletClient.writeContract(call); -// await waitTx(publicClient, approveHash); -// //2. mint the token -// const { request } = await publicClient.simulateContract({ -// abi: [ -// { -// inputs: [ -// { -// internalType: "address", -// name: "to", -// type: "address", -// }, -// { -// internalType: "uint256", -// name: "amount", -// type: "uint256", -// }, -// ], -// name: "mint", -// outputs: [], -// stateMutability: "nonpayable", -// type: "function", -// }, -// ], -// address: storyTestnetAddress.MockERC20, -// functionName: "mint", -// account: walletClient.account, -// args: [process.env.STORY_TEST_NET_TEST_WALLET_ADDRESS! as Hex, BigInt(100)], -// }); -// const mintHash = await walletClient.writeContract(request); -// await waitTx(publicClient, mintHash); -// const response = await client.royalty.payRoyaltyOnBehalf({ -// receiverIpId: ipId1, -// payerIpId: ipId2, -// token: storyTestnetAddress.MockERC20, -// amount: BigInt(10), -// txOptions: { -// waitForTransaction: true, -// }, -// }); -// expect(response.txHash).to.be.a("string").not.empty; -// }); - -// it("should not throw error when collect royalty tokens", async () => { -// const response = await client.royalty.collectRoyaltyTokens({ -// ancestorIpId: ipId1, -// derivativeId: ipId2, -// txOptions: { -// waitForTransaction: true, -// }, -// }); -// expect(response.txHash).to.be.a("string").not.empty; -// }); -// }); -// }); + it("should not throw error when collect royalty tokens", async () => { + const response = await client.royalty.collectRoyaltyTokens({ + ancestorIpId: ipId1, + derivativeId: ipId2, + txOptions: { + waitForTransaction: true, + }, + }); + expect(response.txHash).to.be.a("string").not.empty; + }); + }); +});