diff --git a/packages/core-sdk/package.json b/packages/core-sdk/package.json index 8761542a..2151ab1b 100644 --- a/packages/core-sdk/package.json +++ b/packages/core-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@story-protocol/core-sdk", - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "description": "Story Protocol Core SDK", "main": "dist/story-protocol-core-sdk.cjs.js", "module": "dist/story-protocol-core-sdk.esm.js", diff --git a/packages/core-sdk/src/index.ts b/packages/core-sdk/src/index.ts index 4047a68e..9c51cd78 100644 --- a/packages/core-sdk/src/index.ts +++ b/packages/core-sdk/src/index.ts @@ -6,6 +6,8 @@ export { PermissionClient } from "./resources/permission"; export { LicenseClient } from "./resources/license"; export { DisputeClient } from "./resources/dispute"; export { NftClient } from "./resources/nftClient"; +export { IPAccountClient } from "./resources/ipAccount"; +export { RoyaltyClient } from "./resources/royalty"; export type { StoryConfig, SupportedChainIds } from "./types/config"; export type { TypedData } from "./types/common"; @@ -31,9 +33,11 @@ export type { RegisterCommercialRemixPILRequest, RegisterPILResponse, AttachLicenseTermsRequest, + AttachLicenseTermsResponse, LicenseTermsIdResponse, MintLicenseTokensRequest, MintLicenseTokensResponse, + LicenseTermsId, } from "./types/resources/license"; export { PIL_TYPE } from "./types/resources/license"; @@ -46,7 +50,8 @@ export type { SnapshotResponse, ClaimableRevenueRequest, ClaimableRevenueResponse, - RoyaltyVaultAddress, + ClaimRevenueRequest, + ClaimRevenueResponse, } from "./types/resources/royalty"; export type { @@ -86,3 +91,6 @@ export type { PiLicenseTemplateGetLicenseTermsResponse, IpAccountImplStateResponse, } from "./abi/generated"; + +export { getPermissionSignature } from "./utils/sign"; +export type { PermissionSignatureRequest } from "./types/common"; diff --git a/packages/core-sdk/src/resources/ipAsset.ts b/packages/core-sdk/src/resources/ipAsset.ts index 49a5b82a..12eff524 100644 --- a/packages/core-sdk/src/resources/ipAsset.ts +++ b/packages/core-sdk/src/resources/ipAsset.ts @@ -1,4 +1,4 @@ -import { Hex, PublicClient, zeroAddress, Address, LocalAccount, zeroHash } from "viem"; +import { Hex, PublicClient, zeroAddress, Address, zeroHash, WalletClient } from "viem"; import { chain, getAddress } from "../utils/utils"; import { SupportedChainIds } from "../types/config"; @@ -119,7 +119,7 @@ export class IPAssetClient { ipId: ipIdAddress, deadline: calculatedDeadline, nonce: 1, - account: this.wallet.account as LocalAccount, + wallet: this.wallet as WalletClient, chainId: chain[this.chainId], permissions: [ { @@ -385,7 +385,7 @@ export class IPAssetClient { ipId: ipIdAddress, deadline: calculatedDeadline, nonce: 2, - account: this.wallet.account as LocalAccount, + wallet: this.wallet as WalletClient, chainId: chain[this.chainId], permissions: [ { @@ -434,7 +434,7 @@ export class IPAssetClient { ipId: ipIdAddress, deadline: calculatedDeadline, nonce: 1, - account: this.wallet.account as LocalAccount, + wallet: this.wallet as WalletClient, chainId: chain[this.chainId], permissions: [ { @@ -518,7 +518,7 @@ export class IPAssetClient { ipId: ipIdAddress, deadline: calculatedDeadline, nonce: 2, - account: this.wallet.account as LocalAccount, + wallet: this.wallet as WalletClient, chainId: chain[this.chainId], permissions: [ { @@ -576,7 +576,7 @@ export class IPAssetClient { ipId: ipIdAddress, deadline: calculatedDeadline, nonce: 1, - account: this.wallet.account as LocalAccount, + wallet: this.wallet as WalletClient, chainId: chain[this.chainId], permissions: [ { diff --git a/packages/core-sdk/src/resources/license.ts b/packages/core-sdk/src/resources/license.ts index f167f2a3..ee35db2e 100644 --- a/packages/core-sdk/src/resources/license.ts +++ b/packages/core-sdk/src/resources/license.ts @@ -22,6 +22,8 @@ import { MintLicenseTokensRequest, MintLicenseTokensResponse, PIL_TYPE, + AttachLicenseTermsResponse, + LicenseTermsId, } from "../types/resources/license"; import { handleError } from "../utils/errors"; import { getLicenseTermByType } from "../utils/getLicenseTermsByType"; @@ -157,7 +159,9 @@ export class LicenseClient { * @param request.txOptions [Optional] The transaction options. * @returns A Promise that resolves to an object containing the transaction hash. */ - public async attachLicenseTerms(request: AttachLicenseTermsRequest) { + public async attachLicenseTerms( + request: AttachLicenseTermsRequest, + ): Promise { try { request.licenseTermsId = BigInt(request.licenseTermsId); const isRegistered = await this.ipAssetRegistryClient.isRegistered({ @@ -193,7 +197,7 @@ export class LicenseClient { }); if (request.txOptions?.waitForTransaction) { await this.rpcClient.waitForTransactionReceipt({ hash: txHash }); - return { txHash: txHash }; + return { txHash: txHash, success: true }; } else { return { txHash: txHash }; } @@ -289,7 +293,7 @@ export class LicenseClient { * @returns A Promise that resolves to an object containing the PILTerms associate with the given ID. */ public async getLicenseTerms( - selectedLicenseTermsId: string | number | bigint, + selectedLicenseTermsId: LicenseTermsId, ): Promise { try { return await this.piLicenseTemplateReadOnlyClient.getLicenseTerms({ diff --git a/packages/core-sdk/src/resources/nftClient.ts b/packages/core-sdk/src/resources/nftClient.ts index 41b78bba..8e39e083 100644 --- a/packages/core-sdk/src/resources/nftClient.ts +++ b/packages/core-sdk/src/resources/nftClient.ts @@ -32,10 +32,9 @@ export class NftClient { * @returns A Promise that resolves to a CreateNFTCollectionResponse containing the transaction hash and collection address. * @emits CollectionCreated (nftContract); */ - public async createNFTCollection< - TReq extends CreateNFTCollectionRequest, - TRes = CreateNFTCollectionResponse, - >(request: TReq): Promise { + public async createNFTCollection( + request: CreateNFTCollectionRequest, + ): Promise { try { if ( request.mintFee !== undefined && @@ -61,9 +60,9 @@ export class NftClient { return { txHash: txHash, nftContract: targetLogs[0].nftContract, - } as TRes; + }; } - return { txHash: txHash } as TRes; + return { txHash: txHash }; } catch (error) { handleError(error, "Failed to create a SPG NFT collection"); } diff --git a/packages/core-sdk/src/resources/permission.ts b/packages/core-sdk/src/resources/permission.ts index e67de329..011e2c62 100644 --- a/packages/core-sdk/src/resources/permission.ts +++ b/packages/core-sdk/src/resources/permission.ts @@ -1,4 +1,4 @@ -import { PublicClient, encodeFunctionData, Address, LocalAccount, toFunctionSelector } from "viem"; +import { PublicClient, encodeFunctionData, Address, toFunctionSelector, WalletClient } from "viem"; import { handleError } from "../utils/errors"; import { @@ -129,7 +129,7 @@ export class PermissionClient { }, ], chainId: chain[this.chainId], - account: this.wallet.account as LocalAccount, + wallet: this.wallet as WalletClient, }); const txHash = await ipAccountClient.executeWithSig({ to: getAddress(this.accessControllerClient.address, "accessControllerClientAddress"), @@ -265,7 +265,7 @@ export class PermissionClient { nonce, permissions, chainId: chain[this.chainId], - account: this.wallet.account as LocalAccount, + wallet: this.wallet as WalletClient, permissionFunc: "setBatchPermissions", }); const txHash = await ipAccountClient.executeWithSig({ diff --git a/packages/core-sdk/src/resources/royalty.ts b/packages/core-sdk/src/resources/royalty.ts index 7f4e5f10..52cbb66c 100644 --- a/packages/core-sdk/src/resources/royalty.ts +++ b/packages/core-sdk/src/resources/royalty.ts @@ -1,4 +1,4 @@ -import { Hex, PublicClient, encodeFunctionData } from "viem"; +import { Address, Hex, PublicClient, encodeFunctionData } from "viem"; import { handleError } from "../utils/errors"; import { @@ -8,7 +8,6 @@ import { CollectRoyaltyTokensResponse, PayRoyaltyOnBehalfRequest, PayRoyaltyOnBehalfResponse, - RoyaltyVaultAddress, SnapshotRequest, SnapshotResponse, ClaimRevenueRequest, @@ -255,7 +254,7 @@ export class RoyaltyClient { * @param royaltyVaultIpId the id of the royalty vault. * @returns A Promise that resolves to an object containing the royalty vault address. */ - public async getRoyaltyVaultAddress(royaltyVaultIpId: Hex): Promise { + public async getRoyaltyVaultAddress(royaltyVaultIpId: Hex): Promise
{ const isRoyaltyVaultIpIdRegistered = await this.ipAssetRegistryClient.isRegistered({ id: getAddress(royaltyVaultIpId, "royaltyVaultIpId"), }); diff --git a/packages/core-sdk/src/types/common.ts b/packages/core-sdk/src/types/common.ts index 2a978ac9..a517766c 100644 --- a/packages/core-sdk/src/types/common.ts +++ b/packages/core-sdk/src/types/common.ts @@ -1,4 +1,18 @@ +import { Address, WalletClient } from "viem"; + +import { SetPermissionsRequest } from "./resources/permission"; + export type TypedData = { interface: string; // i.e. "(address,uint256)" data: unknown[]; }; + +export type PermissionSignatureRequest = { + ipId: Address; + nonce: number | bigint; + deadline: bigint; + wallet: WalletClient; + chainId: bigint; + permissions: Omit[]; + permissionFunc?: "setPermission" | "setBatchPermissions"; +}; diff --git a/packages/core-sdk/src/types/resources/license.ts b/packages/core-sdk/src/types/resources/license.ts index fc036791..9d8a2f0c 100644 --- a/packages/core-sdk/src/types/resources/license.ts +++ b/packages/core-sdk/src/types/resources/license.ts @@ -49,7 +49,7 @@ export type RegisterCommercialUsePILRequest = { }; export type RegisterCommercialRemixPILRequest = { - mintingFee: string; + mintingFee: string | number | bigint; commercialRevShare: number; currency: Address; txOptions?: TxOptions; @@ -62,6 +62,11 @@ export type AttachLicenseTermsRequest = { txOptions?: TxOptions; }; +export type AttachLicenseTermsResponse = { + txHash: string; + success?: boolean; +}; + export type MintLicenseTokensRequest = { licensorIpId: Address; licenseTermsId: string | number | bigint; @@ -81,3 +86,5 @@ export enum PIL_TYPE { COMMERCIAL_USE, COMMERCIAL_REMIX, } + +export type LicenseTermsId = string | number | bigint; diff --git a/packages/core-sdk/src/types/resources/nftClient.ts b/packages/core-sdk/src/types/resources/nftClient.ts index 02cd8898..3d536498 100644 --- a/packages/core-sdk/src/types/resources/nftClient.ts +++ b/packages/core-sdk/src/types/resources/nftClient.ts @@ -12,7 +12,4 @@ export type CreateNFTCollectionRequest = { txOptions?: TxOptions; }; -export type CreateNFTCollectionResponse = - TReq["txOptions"] extends { waitForTransaction: true } - ? { txHash: string; nftContract: Hex } - : { txHash: string; nftContract?: Hex }; +export type CreateNFTCollectionResponse = { txHash: string; nftContract?: Hex }; diff --git a/packages/core-sdk/src/types/resources/royalty.ts b/packages/core-sdk/src/types/resources/royalty.ts index 248347f5..fa7ce7f6 100644 --- a/packages/core-sdk/src/types/resources/royalty.ts +++ b/packages/core-sdk/src/types/resources/royalty.ts @@ -83,5 +83,3 @@ export type SnapshotResponse = { txHash: string; snapshotId?: bigint; }; - -export type RoyaltyVaultAddress = Address; diff --git a/packages/core-sdk/src/utils/sign.ts b/packages/core-sdk/src/utils/sign.ts index f97ed0b2..3763c682 100644 --- a/packages/core-sdk/src/utils/sign.ts +++ b/packages/core-sdk/src/utils/sign.ts @@ -1,30 +1,29 @@ -import { - Address, - ContractFunctionName, - Hex, - LocalAccount, - PrivateKeyAccount, - encodeFunctionData, - toFunctionSelector, -} from "viem"; +import { Hex, encodeFunctionData, toFunctionSelector } from "viem"; import { accessControllerAbi, accessControllerAddress } from "../abi/generated"; import { getAddress } from "./utils"; -import { SetPermissionsRequest } from "../types/resources/permission"; import { defaultFunctionSelector } from "../constants/common"; +import { PermissionSignatureRequest } from "../types/common"; -export const getPermissionSignature = async (params: { - ipId: Address; - nonce: number | bigint; - deadline: bigint; - account: LocalAccount | PrivateKeyAccount; - chainId: bigint; - permissions: Omit[]; - permissionFunc?: ContractFunctionName; -}): Promise => { - const { ipId, deadline, nonce, account, chainId, permissions, permissionFunc } = params; - if (!account.signTypedData) { - throw new Error("The account does not support signTypedData, Please use a local account."); +/** + * Get the signature for setting permissions. + * @param param - The parameter object containing necessary data to get the signature. + * @param param.ipId - The IP ID. + * @param param.deadline - The deadline. + * @param param.nonce - The nonce. + * @param param.wallet - The wallet client. + * @param param.chainId - The chain ID. + * @param param.permissions - The permissions. + * @param param.permissionFunc - The permission function,default function is setPermission. + * @returns A Promise that resolves to the signature. + */ +export const getPermissionSignature = async (param: PermissionSignatureRequest): Promise => { + const { ipId, deadline, nonce, wallet, chainId, permissions, permissionFunc } = param; + if (!wallet.signTypedData) { + throw new Error("The wallet client does not support signTypedData, please try again."); + } + if (!wallet.account) { + throw new Error("The wallet client does not have an account, please try again."); } const permissionFunction = permissionFunc ? permissionFunc : "setPermission"; const data = encodeFunctionData({ @@ -49,7 +48,8 @@ export const getPermissionSignature = async (params: { })), ], }); - return await account.signTypedData({ + return await wallet.signTypedData({ + account: wallet.account, domain: { name: "Story Protocol IP Account", version: "1", diff --git a/packages/core-sdk/test/integration/ipAccount.test.ts b/packages/core-sdk/test/integration/ipAccount.test.ts index 50ae1090..590e4c9c 100644 --- a/packages/core-sdk/test/integration/ipAccount.test.ts +++ b/packages/core-sdk/test/integration/ipAccount.test.ts @@ -1,7 +1,13 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { AccessPermission, StoryClient } from "../../src"; -import { MockERC721, getStoryClientInSepolia, getTokenId, sepoliaChainId } from "./utils/util"; +import { AccessPermission, StoryClient, getPermissionSignature } from "../../src"; +import { + MockERC721, + getStoryClientInSepolia, + getTokenId, + sepoliaChainId, + walletClient, +} from "./utils/util"; import { Hex, encodeFunctionData, getAddress, toFunctionSelector } from "viem"; import { accessControllerAbi, @@ -9,7 +15,8 @@ import { coreMetadataModuleAddress, } from "../../src/abi/generated"; import { privateKeyToAccount } from "viem/accounts"; -import { getDeadline, getPermissionSignature } from "../../src/utils/sign"; +import { getDeadline } from "../../src/utils/sign"; + chai.use(chaiAsPromised); const expect = chai.expect; const coreMetadataModule = coreMetadataModuleAddress[sepoliaChainId]; @@ -60,6 +67,7 @@ describe("Ip Account functions", () => { const deadline = getDeadline(60000n); const signature = await getPermissionSignature({ ipId, + wallet: walletClient, permissions: [ { ipId: ipId, @@ -70,7 +78,7 @@ describe("Ip Account functions", () => { }, ], nonce: expectedState, - account, + chainId: BigInt(sepoliaChainId), deadline: deadline, }); diff --git a/packages/core-sdk/test/integration/ipAsset.test.ts b/packages/core-sdk/test/integration/ipAsset.test.ts index af1daf12..3593557a 100644 --- a/packages/core-sdk/test/integration/ipAsset.test.ts +++ b/packages/core-sdk/test/integration/ipAsset.test.ts @@ -133,13 +133,12 @@ describe("IP Asset Functions ", () => { name: "test-collection", symbol: "TEST", maxSupply: 100, - mintCost: 0n, txOptions: { waitForTransaction: true, }, }); expect(txData.nftContract).to.be.a("string").and.not.empty; - nftContract = txData.nftContract; + nftContract = txData.nftContract!; }); describe("should not throw error when mint and register ip and attach pil terms", async () => { diff --git a/packages/core-sdk/test/unit/resources/ipAsset.test.ts b/packages/core-sdk/test/unit/resources/ipAsset.test.ts index e0802bae..50985403 100644 --- a/packages/core-sdk/test/unit/resources/ipAsset.test.ts +++ b/packages/core-sdk/test/unit/resources/ipAsset.test.ts @@ -18,10 +18,10 @@ describe("Test IpAssetClient", () => { walletMock = createMock(); const accountMock = createMock(); walletMock.account = accountMock; - walletMock.account.signTypedData = sinon + ipAssetClient = new IPAssetClient(rpcMock, walletMock, "sepolia"); + walletMock.signTypedData = sinon .stub() .resolves("0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"); - ipAssetClient = new IPAssetClient(rpcMock, walletMock, "sepolia"); (ipAssetClient.spgClient as any).address = "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"; (ipAssetClient.accessControllerClient as any).address = "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"; @@ -72,7 +72,7 @@ describe("Test IpAssetClient", () => { } }); - it("should throw account error when register given account is not local account ", async () => { + it("should throw account error when register given wallet have no signTypedData ", async () => { const walletMock = createMock(); walletMock.account = createMock(); ipAssetClient = new IPAssetClient(rpcMock, walletMock, "sepolia"); @@ -95,7 +95,7 @@ describe("Test IpAssetClient", () => { }); } catch (err) { expect((err as Error).message).equal( - "Failed to register IP: The account does not support signTypedData, Please use a local account.", + "Failed to register IP: The wallet client does not support signTypedData, please try again.", ); } }); diff --git a/packages/core-sdk/test/unit/resources/nftClient.test.ts b/packages/core-sdk/test/unit/resources/nftClient.test.ts index 269b6cd8..2c9809e2 100644 --- a/packages/core-sdk/test/unit/resources/nftClient.test.ts +++ b/packages/core-sdk/test/unit/resources/nftClient.test.ts @@ -102,8 +102,8 @@ describe("Test NftClient", () => { const result = await nftClient.createNFTCollection({ ...reqBody, maxSupply: undefined, - mintCost: undefined, - mintToken: undefined, + mintFee: undefined, + mintFeeToken: undefined, }); expect(result.txHash).to.equal(mock.txHash); }); diff --git a/packages/core-sdk/test/unit/resources/permission.test.ts b/packages/core-sdk/test/unit/resources/permission.test.ts index f50f26b5..130ff20b 100644 --- a/packages/core-sdk/test/unit/resources/permission.test.ts +++ b/packages/core-sdk/test/unit/resources/permission.test.ts @@ -17,6 +17,9 @@ describe("Test Permission", () => { walletMock = createMock(); const accountMock = createMock(); walletMock.account = accountMock; + walletMock.signTypedData = sinon + .stub() + .resolves("0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"); permissionClient = new PermissionClient(rpcMock, walletMock, "sepolia"); IpAccountImplClient.prototype.state = sinon.stub().resolves(1n); walletMock.account.signTypedData = sinon @@ -185,8 +188,8 @@ describe("Test Permission", () => { } }); - it("should account error when call createSetPermissionSignature given account is not instance of local account", async () => { - walletMock.account = createMock(); + it("should account error when call createSetPermissionSignature given wallet has no signTypedData method", async () => { + walletMock = createMock(); permissionClient = new PermissionClient(rpcMock, walletMock, "11155111"); sinon.stub(permissionClient.ipAssetRegistryClient, "isRegistered").resolves(true); @@ -200,7 +203,7 @@ describe("Test Permission", () => { }); } catch (error) { expect((error as Error).message).to.equal( - "Failed to create set permission signature: The account does not support signTypedData, Please use a local account.", + "Failed to create set permission signature: The wallet client does not support signTypedData, please try again.", ); } }); diff --git a/packages/core-sdk/test/unit/utils/sign.test.ts b/packages/core-sdk/test/unit/utils/sign.test.ts index 40d70258..0dc5c2a2 100644 --- a/packages/core-sdk/test/unit/utils/sign.test.ts +++ b/packages/core-sdk/test/unit/utils/sign.test.ts @@ -1,37 +1,59 @@ import { expect } from "chai"; import { getDeadline, getPermissionSignature } from "../../../src/utils/sign"; -import { Hex, LocalAccount, zeroAddress } from "viem"; +import { Hex, WalletClient, createWalletClient, http, zeroAddress } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { sepoliaChainId } from "../../integration/utils/util"; import sinon from "sinon"; +import { chainStringToViemChain } from "../../../src/utils/utils"; describe("Sign", () => { describe("Get Permission Signature", () => { - it("should throw sign error when call getPermissionSignature given account does not support signTypedData", async () => { + it("should throw sign error when call getPermissionSignature given wallet does not support signTypedData", async () => { try { await getPermissionSignature({ ipId: zeroAddress, nonce: 1, deadline: 1000n, permissions: [], - account: {} as LocalAccount, + wallet: {} as WalletClient, chainId: BigInt(sepoliaChainId), }); } catch (e) { expect((e as Error).message).to.equal( - "The account does not support signTypedData, Please use a local account.", + "The wallet client does not support signTypedData, please try again.", + ); + } + }); + + it("should throw sign error when call getPermissionSignature given wallet does not have an account", async () => { + try { + await getPermissionSignature({ + ipId: zeroAddress, + nonce: 1, + deadline: 1000n, + permissions: [], + wallet: { signTypedData: () => Promise.resolve("") } as unknown as WalletClient, + chainId: BigInt(sepoliaChainId), + }); + } catch (e) { + expect((e as Error).message).to.equal( + "The wallet client does not have an account, please try again.", ); } }); it("should return signature when call getPermissionSignature given account support signTypedData", async () => { - const account = privateKeyToAccount(process.env.SEPOLIA_WALLET_PRIVATE_KEY as Hex); + const walletClient = createWalletClient({ + chain: chainStringToViemChain("sepolia"), + transport: http(), + account: privateKeyToAccount(process.env.SEPOLIA_WALLET_PRIVATE_KEY as Hex), + }); const result = await getPermissionSignature({ ipId: zeroAddress, nonce: 1, deadline: 1000n, permissions: [{ ipId: zeroAddress, signer: zeroAddress, to: zeroAddress, permission: 0 }], - account, + wallet: walletClient, chainId: BigInt(sepoliaChainId), }); expect(result).is.a("string").and.not.empty; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb27f2c2..87e4a676 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9451,6 +9451,7 @@ packages: /rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true dependencies: glob: 7.2.3