From 289661e42eaad39c88f7fecc8b358b3bba27f299 Mon Sep 17 00:00:00 2001 From: Bonnie57 <146059114+bonnie57@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:07:18 +0800 Subject: [PATCH 1/3] Refactor the related unit tests (#213) * Modify calling signTypedData by wallet client * Upload pnpm-lock.yaml * Remove useless type * Delete StoryAPI and related tests * Remove utils useless function * Refactor client unit test * Refactor dispute unit tests * Refactor ipAccount tests * Refactor nftClient tests * Add address validation for signature * Export PermissionSignatureResponse type * bump up package version to 1.0.0-rc.15 --- packages/core-sdk/package.json | 2 +- packages/core-sdk/src/clients/storyAPI.ts | 28 -- packages/core-sdk/src/index.ts | 9 +- packages/core-sdk/src/resources/dispute.ts | 6 +- packages/core-sdk/src/resources/ipAccount.ts | 4 +- packages/core-sdk/src/types/common.ts | 10 +- .../core-sdk/src/types/resources/dispute.ts | 23 -- packages/core-sdk/src/utils/sign.ts | 26 +- packages/core-sdk/src/utils/utils.ts | 66 ----- packages/core-sdk/test/unit/client.test.ts | 59 +--- .../test/unit/clients/storyAPI.test.ts | 82 ----- .../test/unit/resources/dispute.test.ts | 280 ++++++------------ .../test/unit/resources/ipAccount.test.ts | 181 ++--------- .../test/unit/resources/nftClient.test.ts | 106 +++---- .../test/unit/resources/permission.test.ts | 2 +- .../core-sdk/test/unit/utils/utils.test.ts | 103 ------- 16 files changed, 190 insertions(+), 797 deletions(-) delete mode 100644 packages/core-sdk/src/clients/storyAPI.ts delete mode 100644 packages/core-sdk/test/unit/clients/storyAPI.test.ts diff --git a/packages/core-sdk/package.json b/packages/core-sdk/package.json index 2151ab1b..08a0bbea 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.14", + "version": "1.0.0-rc.15", "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/clients/storyAPI.ts b/packages/core-sdk/src/clients/storyAPI.ts deleted file mode 100644 index d1ca4470..00000000 --- a/packages/core-sdk/src/clients/storyAPI.ts +++ /dev/null @@ -1,28 +0,0 @@ -import axios, { AxiosInstance } from "axios"; - -import { RoyaltyPolicy, RoyaltyPolicyApiResponse } from "../types/resources/royalty"; -import { License, LicenseApiResponse } from "../types/resources/license"; - -export class StoryAPIClient { - private readonly httpClient: AxiosInstance; - - constructor() { - this.httpClient = axios.create({ - baseURL: "https://api.storyprotocol.net", - timeout: 60000, - headers: { - "x-api-key": "U3RvcnlQcm90b2NvbFRlc3RBUElLRVk=", - }, - }); - } - - public async getRoyaltyPolicy(ipId: string): Promise { - const royaltyPolicyResp = await this.httpClient.get(`/api/v1/royalties/policies/${ipId}`); - return (royaltyPolicyResp.data as RoyaltyPolicyApiResponse).data; - } - - public async getLicense(licenseId: string): Promise { - const licenseResp = await this.httpClient.get(`/api/v1/licenses/${licenseId}`); - return (licenseResp.data as LicenseApiResponse).data; - } -} diff --git a/packages/core-sdk/src/index.ts b/packages/core-sdk/src/index.ts index 9c51cd78..e32f9257 100644 --- a/packages/core-sdk/src/index.ts +++ b/packages/core-sdk/src/index.ts @@ -10,11 +10,10 @@ export { IPAccountClient } from "./resources/ipAccount"; export { RoyaltyClient } from "./resources/royalty"; export type { StoryConfig, SupportedChainIds } from "./types/config"; -export type { TypedData } from "./types/common"; export type { - RegisterIpResponse, RegisterRequest, + RegisterIpResponse, RegisterDerivativeResponse, RegisterDerivativeRequest, RegisterDerivativeWithLicenseTokensRequest, @@ -34,7 +33,6 @@ export type { RegisterPILResponse, AttachLicenseTermsRequest, AttachLicenseTermsResponse, - LicenseTermsIdResponse, MintLicenseTokensRequest, MintLicenseTokensResponse, LicenseTermsId, @@ -64,11 +62,8 @@ export type { } from "./types/resources/permission"; export { AccessPermission } from "./types/resources/permission"; export type { - Dispute, RaiseDisputeRequest, RaiseDisputeResponse, - SetDisputeJudgementRequest, - SetDisputeJudgementResponse, CancelDisputeRequest, CancelDisputeResponse, ResolveDisputeRequest, @@ -93,4 +88,4 @@ export type { } from "./abi/generated"; export { getPermissionSignature } from "./utils/sign"; -export type { PermissionSignatureRequest } from "./types/common"; +export type { PermissionSignatureRequest, PermissionSignatureResponse } from "./types/common"; diff --git a/packages/core-sdk/src/resources/dispute.ts b/packages/core-sdk/src/resources/dispute.ts index 2cfd30fe..4ff41e9d 100644 --- a/packages/core-sdk/src/resources/dispute.ts +++ b/packages/core-sdk/src/resources/dispute.ts @@ -10,6 +10,7 @@ import { ResolveDisputeResponse, } from "../types/resources/dispute"; import { DisputeModuleClient, SimpleWalletClient } from "../abi/generated"; +import { getAddress } from "../utils/utils"; export class DisputeClient { private readonly rpcClient: PublicClient; @@ -39,7 +40,7 @@ export class DisputeClient { public async raiseDispute(request: RaiseDisputeRequest): Promise { try { const txHash = await this.disputeModuleClient.raiseDispute({ - targetIpId: request.targetIpId, + targetIpId: getAddress(request.targetIpId, "request.targetIpId"), linkToDisputeEvidence: request.linkToDisputeEvidence, targetTag: stringToHex(request.targetTag, { size: 32 }), data: request.calldata || "0x", @@ -104,14 +105,13 @@ export class DisputeClient { disputeId: BigInt(request.disputeId), data: request.data, }); - if (request.txOptions?.waitForTransaction) { await this.rpcClient.waitForTransactionReceipt({ hash: txHash }); } return { txHash: txHash }; } catch (error) { - handleError(error, "Failed to cancel dispute"); + handleError(error, "Failed to resolve dispute"); } } } diff --git a/packages/core-sdk/src/resources/ipAccount.ts b/packages/core-sdk/src/resources/ipAccount.ts index 4fce31c2..5822e775 100644 --- a/packages/core-sdk/src/resources/ipAccount.ts +++ b/packages/core-sdk/src/resources/ipAccount.ts @@ -77,10 +77,10 @@ export class IPAccountClient { ); const txHash = await ipAccountClient.executeWithSig({ - to: request.to, + to: getAddress(request.to, "request.to"), value: BigInt(0), data: request.data, - signer: request.signer, + signer: getAddress(request.signer, "request.signer"), deadline: BigInt(request.deadline), signature: request.signature, }); diff --git a/packages/core-sdk/src/types/common.ts b/packages/core-sdk/src/types/common.ts index a517766c..6ba78e4f 100644 --- a/packages/core-sdk/src/types/common.ts +++ b/packages/core-sdk/src/types/common.ts @@ -1,4 +1,4 @@ -import { Address, WalletClient } from "viem"; +import { Address, Hex, WalletClient } from "viem"; import { SetPermissionsRequest } from "./resources/permission"; @@ -9,10 +9,12 @@ export type TypedData = { export type PermissionSignatureRequest = { ipId: Address; - nonce: number | bigint; - deadline: bigint; + nonce: string | number | bigint; + deadline: string | number | bigint; wallet: WalletClient; - chainId: bigint; + chainId: string | number | bigint; permissions: Omit[]; permissionFunc?: "setPermission" | "setBatchPermissions"; }; + +export type PermissionSignatureResponse = Hex; diff --git a/packages/core-sdk/src/types/resources/dispute.ts b/packages/core-sdk/src/types/resources/dispute.ts index caaf7883..a650b561 100644 --- a/packages/core-sdk/src/types/resources/dispute.ts +++ b/packages/core-sdk/src/types/resources/dispute.ts @@ -2,15 +2,6 @@ import { Address } from "viem"; import { TxOptions } from "../options"; -export type Dispute = { - targetIpId: Address; // The ipId that is the target of the dispute - disputeInitiator: Address; // The address of the dispute initiator - arbitrationPolicy: Address; // The address of the arbitration policy - linkToDisputeEvidence: string; // The link of the dispute evidence - targetTag: string; // The target tag of the dispute - currentTag: string; // The current tag of the dispute -}; - export type RaiseDisputeRequest = { targetIpId: Address; arbitrationPolicy: Address; @@ -26,20 +17,6 @@ export type RaiseDisputeResponse = { arbitrationPolicy?: Address; }; -export type SetDisputeJudgementRequest = { - disputeId: number; - decision: boolean; - calldata?: Address; - txOptions?: TxOptions; -}; - -export type SetDisputeJudgementResponse = { - txHash: string; - disputeId?: bigint; - decision?: boolean; - data?: Address; -}; - export type CancelDisputeRequest = { disputeId: number | string | bigint; calldata?: Address; diff --git a/packages/core-sdk/src/utils/sign.ts b/packages/core-sdk/src/utils/sign.ts index 3763c682..6eb98106 100644 --- a/packages/core-sdk/src/utils/sign.ts +++ b/packages/core-sdk/src/utils/sign.ts @@ -1,9 +1,9 @@ -import { Hex, encodeFunctionData, toFunctionSelector } from "viem"; +import { encodeFunctionData, toFunctionSelector } from "viem"; import { accessControllerAbi, accessControllerAddress } from "../abi/generated"; import { getAddress } from "./utils"; import { defaultFunctionSelector } from "../constants/common"; -import { PermissionSignatureRequest } from "../types/common"; +import { PermissionSignatureRequest, PermissionSignatureResponse } from "../types/common"; /** * Get the signature for setting permissions. @@ -17,7 +17,9 @@ import { PermissionSignatureRequest } from "../types/common"; * @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 => { +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."); @@ -32,17 +34,17 @@ export const getPermissionSignature = async (param: PermissionSignatureRequest): args: permissionFunction === "setPermission" ? [ - permissions[0].ipId, - permissions[0].signer, - permissions[0].to, + getAddress(permissions[0].ipId, "permissions[0].ipId"), + getAddress(permissions[0].signer, "permissions[0].signer"), + getAddress(permissions[0].to, "permissions[0].to"), permissions[0].func ? toFunctionSelector(permissions[0].func) : defaultFunctionSelector, permissions[0].permission, ] : [ - permissions.map((item) => ({ - ipAccount: item.ipId, - signer: item.signer, - to: item.to, + permissions.map((item, index) => ({ + ipAccount: getAddress(item.ipId, `permissions[${index}].ipId`), + signer: getAddress(item.signer, `permissions[${index}].signer`), + to: getAddress(item.to, `permissions[${index}].to`), func: item.func ? toFunctionSelector(item.func) : defaultFunctionSelector, permission: item.permission, })), @@ -54,7 +56,7 @@ export const getPermissionSignature = async (param: PermissionSignatureRequest): name: "Story Protocol IP Account", version: "1", chainId: Number(chainId), - verifyingContract: ipId, + verifyingContract: getAddress(ipId, "ipId"), }, types: { Execute: [ @@ -74,7 +76,7 @@ export const getPermissionSignature = async (param: PermissionSignatureRequest): value: BigInt(0), data, nonce: BigInt(nonce), - deadline, + deadline: BigInt(deadline), }, }); }; diff --git a/packages/core-sdk/src/utils/utils.ts b/packages/core-sdk/src/utils/utils.ts index 8c7009ae..20885244 100644 --- a/packages/core-sdk/src/utils/utils.ts +++ b/packages/core-sdk/src/utils/utils.ts @@ -4,8 +4,6 @@ import { Abi, decodeEventLog, PublicClient, - encodeAbiParameters, - parseAbiParameters, Chain, ContractEventName, Hex, @@ -15,14 +13,8 @@ import { } from "viem"; import { sepolia } from "viem/chains"; -import { TypedData } from "../types/common"; import { SupportedChainIds } from "../types/config"; -export function isIntegerString(s: string): boolean { - const num = Number(s); - return !isNaN(num) && parseInt(s, 10) === num; -} - export async function waitTxAndFilterLog< const TAbi extends Abi | readonly unknown[], TEventName extends ContractEventName | undefined = ContractEventName, @@ -85,64 +77,6 @@ export async function waitTx( }); } -export function dictToQueryParams(params: Record): string { - const queryParamList: string[] = []; - for (const key in params) { - const value = params[key]; - queryParamList.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); - } - - return queryParamList.join("&"); -} - -export function typedDataArrayToBytesArray(typedDataArray: Array): Array { - const result: Array = []; - typedDataArray.forEach(function (typedData: TypedData) { - result.push(typedDataToBytes(typedData)); - }); - return result; -} - -export function typedDataToBytes(typedData: TypedData): Hex { - return encodeAbiParameters(parseAbiParameters(typedData.interface), typedData.data); -} - -export function decodeShortstring(hexString: string): string { - if (hexString.startsWith("0x")) { - hexString = hexString.slice(2); - } - - // Replace the last two hexadecimal digits with zero - hexString = hexString.substring(0, hexString.length - 2) + "00"; - - let asciiString = ""; - for (let i = 0; i < hexString.length; i += 2) { - const hexByte = hexString.substring(i, i + 2); - if (hexByte === "00") { - break; // Stop if padding (00) is found - } - asciiString += String.fromCharCode(parseInt(hexByte, 16)); - } - return asciiString; -} - -export function splitIntoBytes32(hexString: string): Hex[] { - if (hexString.startsWith("0x")) { - hexString = hexString.slice(2); // Remove the '0x' prefix - } - - if (hexString.length % 64 !== 0) { - throw new Error("Hex string length must be a multiple of 64."); - } - - const bytes32Array: string[] = []; - for (let i = 0; i < hexString.length; i += 64) { - bytes32Array.push("0x" + hexString.substring(i, i + 64)); - } - - return bytes32Array as Hex[]; -} - export function chainStringToViemChain(chainId: SupportedChainIds): Chain { switch (chainId) { case "11155111": diff --git a/packages/core-sdk/test/unit/client.test.ts b/packages/core-sdk/test/unit/client.test.ts index 6f80c610..10882ff8 100644 --- a/packages/core-sdk/test/unit/client.test.ts +++ b/packages/core-sdk/test/unit/client.test.ts @@ -2,17 +2,7 @@ import { expect } from "chai"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { sepolia } from "viem/chains"; import { createPublicClient, createWalletClient, http, Transport } from "viem"; -import { - DisputeClient, - LicenseClient, - NftClient, - PermissionClient, - StoryClient, - StoryConfig, -} from "../../src"; -import { StoryAPIClient } from "../../src/clients/storyAPI"; -import { RoyaltyClient } from "../../src/resources/royalty"; -import { sepoliaChainId } from "../integration/utils/util"; +import { StoryClient, StoryConfig } from "../../src"; const rpc = "http://127.0.0.1:8545"; describe("Test StoryClient", () => { @@ -95,45 +85,14 @@ describe("Test StoryClient", () => { }); const client: StoryClient = StoryClient.newClient(config); - const storyAPIClient = new StoryAPIClient(); - it("client ipAsset should not be empty", () => { - expect(client.ipAsset).to.not.equal(null); - expect(client.ipAsset).to.not.equal(undefined); - }); - - it("should return client permission", () => { - const permission = new PermissionClient(rpcClient, wallet, "sepolia"); - expect(client.permission).to.not.equal(null); - expect(client.permission).to.not.equal(undefined); - }); - - it("should return client license", () => { - const license = new LicenseClient(rpcClient, wallet); - expect(client.license).to.not.equal(null); - expect(client.license).to.not.equal(undefined); - }); - - it("should return client account", () => { - expect(client.ipAccount).to.not.equal(null); - expect(client.ipAccount).to.not.equal(undefined); - }); - - it("should return client dispute", () => { - const dispute = new DisputeClient(rpcClient, wallet); - expect(client.dispute).to.not.equal(null); - expect(client.dispute).to.not.equal(undefined); - }); - - it("should return client royalty", () => { - const royalty = new RoyaltyClient(rpcClient, wallet); - expect(client.royalty).to.not.equal(null); - expect(client.royalty).to.not.equal(undefined); - }); - - it("should return client nft", () => { - const royalty = new NftClient(rpcClient, wallet); - expect(client.nftClient).to.not.equal(null); - expect(client.nftClient).to.not.equal(undefined); + it("client modules should not be empty", () => { + expect(client.ipAsset).to.not.equal(null).and.to.not.equal(undefined); + expect(client.permission).to.not.equal(null).and.to.not.equal(undefined); + expect(client.license).to.not.equal(null).and.to.not.equal(undefined); + expect(client.ipAccount).to.not.equal(null).and.to.not.equal(undefined); + expect(client.dispute).to.not.equal(null).and.to.not.equal(undefined); + expect(client.royalty).to.not.equal(null).and.to.not.equal(undefined); + expect(client.nftClient).to.not.equal(null).and.to.not.equal(undefined); }); }); }); diff --git a/packages/core-sdk/test/unit/clients/storyAPI.test.ts b/packages/core-sdk/test/unit/clients/storyAPI.test.ts deleted file mode 100644 index d93ff996..00000000 --- a/packages/core-sdk/test/unit/clients/storyAPI.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import sinon from "sinon"; -import chai from "chai"; -import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; -import { StoryAPIClient } from "../../../src/clients/storyAPI"; -import chaiAsPromised from "chai-as-promised"; -chai.use(chaiAsPromised); -const expect = chai.expect; -describe("Test StoryAPIClient", () => { - afterEach(() => { - sinon.restore(); - }); - - describe("test for getRoyaltyPolicy", () => { - afterEach(() => { - sinon.restore(); - }); - it("should resolve error if network error occurs", async () => { - sinon.stub(axios, "create").returns({ - get: (url: string, config?: AxiosRequestConfig): Promise => - Promise.reject(new Error("http 500")), - } as AxiosInstance); - const storyClient: StoryAPIClient = new StoryAPIClient(); - await expect(storyClient.getRoyaltyPolicy("1")).to.be.rejectedWith("http 500"); - }); - - it("should resolve royalty policy when request succeeded", async () => { - sinon.stub(axios, "create").returns({ - get: (url: string, config?: AxiosRequestConfig): Promise => - Promise.resolve({ - status: 200, - data: { - data: { - id: "3", - targetAncestors: ["0x222222222222222222222222222222222"], - targetRoyaltyAmount: ["1"], - }, - }, - } as AxiosResponse), - } as AxiosInstance); - const storyClient: StoryAPIClient = new StoryAPIClient(); - const { id, targetAncestors, targetRoyaltyAmount } = await storyClient.getRoyaltyPolicy("1"); - expect(id).to.equal("3"); - expect(targetAncestors[0]).to.equal("0x222222222222222222222222222222222"); - expect(targetRoyaltyAmount[0]).to.equal("1"); - }); - }); - - describe("test for getLicense", () => { - afterEach(() => { - sinon.restore(); - }); - it("should resolve error if network error occurs", async () => { - sinon.stub(axios, "create").returns({ - get: (url: string, config?: AxiosRequestConfig): Promise => - Promise.reject(new Error("http 500")), - } as AxiosInstance); - const storyClient: StoryAPIClient = new StoryAPIClient(); - await expect(storyClient.getLicense("1")).to.be.rejectedWith("http 500"); - }); - - it("should resolve license when request succeeded", async () => { - sinon.stub(axios, "create").returns({ - get: (url: string, config?: AxiosRequestConfig): Promise => - Promise.resolve({ - status: 200, - data: { - data: { - id: "3", - policyId: "2", - licensorIpId: "1", - }, - }, - } as AxiosResponse), - } as AxiosInstance); - const storyClient: StoryAPIClient = new StoryAPIClient(); - const { id, policyId, licensorIpId } = await storyClient.getLicense("1"); - expect(id).to.equal("3"); - expect(policyId).to.equal("2"); - expect(licensorIpId).to.equal("1"); - }); - }); -}); diff --git a/packages/core-sdk/test/unit/resources/dispute.test.ts b/packages/core-sdk/test/unit/resources/dispute.test.ts index a4419750..a28b60c1 100644 --- a/packages/core-sdk/test/unit/resources/dispute.test.ts +++ b/packages/core-sdk/test/unit/resources/dispute.test.ts @@ -3,8 +3,7 @@ import { createMock } from "../testUtils"; import chai, { expect } from "chai"; import chaiAsPromised from "chai-as-promised"; import { PublicClient, WalletClient } from "viem"; -import { CancelDisputeRequest, DisputeClient, ResolveDisputeRequest } from "../../../src"; -import { DisputeModuleClient } from "../../../src/abi/generated"; +import { DisputeClient } from "../../../src"; chai.use(chaiAsPromised); @@ -13,9 +12,7 @@ describe("Test DisputeClient", () => { let rpcMock: PublicClient; let walletMock: WalletClient; - const mock = { - txHash: "0x063834efe214f4199b1ad7181ce8c5ced3e15d271c8e866da7c89e86ee629cfb", - }; + const txHash = "0x063834efe214f4199b1ad7181ce8c5ced3e15d271c8e866da7c89e86ee629cfb"; beforeEach(() => { rpcMock = createMock(); @@ -27,228 +24,125 @@ describe("Test DisputeClient", () => { sinon.restore(); }); - describe("Should be able to", async () => { - const targetIpId = - "0xa3028b46ff4aeba585ebfa1c241ad4a453b6f10dc7bc3d3ebaa9cecc680a6f71" as `0x${string}`; - const arbitrationPolicy = "0xC6A1c49BCeeE2E512167d5c03e4753776477730b" as `0x${string}`; - const linkToDisputeEvidence = "foo"; - const targetTag = "bar"; - - it("should throw simulateContract error if simulateContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().throws(new Error("simulateContract error")); - const raiseDisputeRequest = { - targetIpId, - arbitrationPolicy, - linkToDisputeEvidence, - targetTag, - txOptions: { - waitForTransaction: true, - }, - }; + describe("Test raiseDispute", () => { + it("throw address error when call raiseDispute with invalid targetIpId", async () => { try { - await disputeClient.raiseDispute(raiseDisputeRequest); - } catch (err) { - expect((err as Error).message).includes("simulateContract error"); + await disputeClient.raiseDispute({ + targetIpId: "0x", + arbitrationPolicy: "0x", + linkToDisputeEvidence: "link", + targetTag: "tag", + }); + } catch (e) { + expect((e as Error).message).equal( + "Failed to raise dispute: request.targetIpId address is invalid: 0x, Address must be a hex value of 20 bytes (40 hex characters) and match its checksum counterpart.", + ); } }); - it("should throw writeContract error if writeContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().throws(new Error("writeContract error")); - const raiseDisputeRequest = { - targetIpId, - arbitrationPolicy, - linkToDisputeEvidence, - targetTag, - txOptions: { - waitForTransaction: true, - }, - }; - try { - await disputeClient.raiseDispute(raiseDisputeRequest); - } catch (err) { - expect((err as Error).message).includes("writeContract error"); - } - }); + it("should return txHash when call raiseDispute successfully", async () => { + sinon.stub(disputeClient.disputeModuleClient, "raiseDispute").resolves(txHash); + const result = await disputeClient.raiseDispute({ + targetIpId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + arbitrationPolicy: "0x", + linkToDisputeEvidence: "link", + targetTag: "tag", + }); - it("should return txHash only if request.txOptions is missing", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - disputeClient.disputeModuleClient.parseTxDisputeRaisedEvent = sinon.stub().resolves([ - { - disputeId: "7", - }, - ]); - const raiseDisputeRequest = { - targetIpId, - arbitrationPolicy, - linkToDisputeEvidence, - targetTag, - }; - - const result = await disputeClient.raiseDispute(raiseDisputeRequest); - expect(Object.keys(result).length).to.equal(1); - expect(Object.keys(result)[0]).to.equal("txHash"); - expect(result.txHash).to.equal(mock.txHash); + expect(result.txHash).equal(txHash); }); - it("should return txHash and disputeId if request.txOptions is present", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves({}); - - disputeClient.disputeModuleClient = createMock(); - disputeClient.disputeModuleClient.raiseDispute = sinon.stub().resolves(mock.txHash); - disputeClient.disputeModuleClient.parseTxDisputeRaisedEvent = sinon.stub().returns([ + it("should return txHash and disputeId when call raiseDispute successfully with waitForTransaction", async () => { + sinon.stub(disputeClient.disputeModuleClient, "raiseDispute").resolves(txHash); + sinon.stub(disputeClient.disputeModuleClient, "parseTxDisputeRaisedEvent").returns([ { - disputeId: "7", + disputeId: 1n, + targetIpId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + disputeInitiator: "0x", + arbitrationPolicy: "0x", + linkToDisputeEvidence: "0x", + targetTag: "0x", + data: "0x", }, ]); - const raiseDisputeRequest = { - targetIpId, - arbitrationPolicy, - linkToDisputeEvidence, - targetTag, - txOptions: { - waitForTransaction: true, - }, - }; - - const result = await disputeClient.raiseDispute(raiseDisputeRequest); - expect(result.txHash).to.equal(mock.txHash); - expect(result.disputeId).to.equal(BigInt(7).toString()); + const result = await disputeClient.raiseDispute({ + targetIpId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + arbitrationPolicy: "0x", + linkToDisputeEvidence: "link", + targetTag: "tag", + txOptions: { waitForTransaction: true }, + }); + + expect(result.txHash).equal(txHash); + expect(result.disputeId).equal(1n); }); }); - describe("test for cancelDispute", () => { - it("should throw simulateContract error if simulateContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().throws(new Error("simulateContract error")); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves(); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - - const cancelDisputeRequest: CancelDisputeRequest = { - disputeId: 1, - txOptions: { - waitForTransaction: true, - }, - }; + describe("Test cancelDispute", () => { + it("should throw error when call cancelDispute failed", async () => { + sinon.stub(disputeClient.disputeModuleClient, "cancelDispute").rejects(new Error("500")); try { - await disputeClient.cancelDispute(cancelDisputeRequest); - } catch (err) { - expect((err as Error).message.includes("simulateContract error")); + await disputeClient.cancelDispute({ + disputeId: 1, + }); + } catch (e) { + expect((e as Error).message).equal("Failed to cancel dispute: 500"); } }); - it("should throw writeContract error if writeContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().throws(new Error("writeContract error")); - const cancelDisputeRequest: CancelDisputeRequest = { + it("should return txHash when call cancelDispute successfully", async () => { + sinon.stub(disputeClient.disputeModuleClient, "cancelDispute").resolves(txHash); + const result = await disputeClient.cancelDispute({ disputeId: 1, - txOptions: { - waitForTransaction: true, - }, - }; - try { - await disputeClient.cancelDispute(cancelDisputeRequest); - } catch (err) { - expect((err as Error).message.includes("writeContract error")); - } - }); + calldata: "0x", + }); - it("should return txHash", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves(); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - const cancelDisputeRequest: CancelDisputeRequest = { - disputeId: 1, - txOptions: { - waitForTransaction: true, - }, - }; - - const result = await disputeClient.cancelDispute(cancelDisputeRequest); - expect(result.txHash).to.equal(mock.txHash); + expect(result.txHash).equal(txHash); }); - it("should return txHash if txOptions.waitForTransaction is falsy", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - const cancelDisputeRequest: CancelDisputeRequest = { + + it("should return txHash when call cancelDispute successfully with waitForTransaction", async () => { + sinon.stub(disputeClient.disputeModuleClient, "cancelDispute").resolves(txHash); + const result = await disputeClient.cancelDispute({ disputeId: 1, - calldata: "0x1111", - txOptions: { - waitForTransaction: false, - }, - }; + txOptions: { waitForTransaction: true }, + }); - const result = await disputeClient.cancelDispute(cancelDisputeRequest); - expect(result.txHash).to.equal(mock.txHash); + expect(result.txHash).equal(txHash); }); }); - describe("test for resolveDispute", () => { - it("should throw simulateContract error if simulateContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().throws(new Error("simulateContract error")); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves(); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - - const resolveDisputeRequest: ResolveDisputeRequest = { - disputeId: 1, - data: "0x1111", - txOptions: { - waitForTransaction: true, - }, - }; + describe("Test resolveDispute", () => { + it("should throw error when call resolveDispute failed", async () => { + sinon.stub(disputeClient.disputeModuleClient, "resolveDispute").rejects(new Error("500")); try { - await disputeClient.resolveDispute(resolveDisputeRequest); - } catch (err) { - expect((err as Error).message.includes("simulateContract error")); + await disputeClient.resolveDispute({ + disputeId: 1, + data: "0x", + }); + } catch (e) { + expect((e as Error).message).equal("Failed to resolve dispute: 500"); } }); - it("should throw writeContract error if writeContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().throws(new Error("writeContract error")); - const resolveDisputeRequest: ResolveDisputeRequest = { + it("should return txHash when call resolveDispute successfully", async () => { + sinon.stub(disputeClient.disputeModuleClient, "resolveDispute").resolves(txHash); + const result = await disputeClient.resolveDispute({ disputeId: 1, - data: "0x1111", - txOptions: { - waitForTransaction: true, - }, - }; - try { - await disputeClient.resolveDispute(resolveDisputeRequest); - } catch (err) { - expect((err as Error).message.includes("writeContract error")); - } - }); - - it("should return txHash", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves(); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - const resolveDisputeRequest: ResolveDisputeRequest = { - disputeId: 1, - data: "0x1111", - txOptions: { - waitForTransaction: true, - }, - }; + data: "0x", + }); - const result = await disputeClient.resolveDispute(resolveDisputeRequest); - expect(result.txHash).to.equal(mock.txHash); + expect(result.txHash).equal(txHash); }); - it("should return txHash if txOptions.waitForTransaction is falsy", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - const resolveDisputeRequest: ResolveDisputeRequest = { + + it("should return txHash when call resolveDispute successfully with waitForTransaction", async () => { + sinon.stub(disputeClient.disputeModuleClient, "resolveDispute").resolves(txHash); + const result = await disputeClient.resolveDispute({ disputeId: 1, - data: "0x1111", - txOptions: { - waitForTransaction: false, - }, - }; + data: "0x", + txOptions: { waitForTransaction: true }, + }); - const result = await disputeClient.resolveDispute(resolveDisputeRequest); - expect(result.txHash).to.equal(mock.txHash); + expect(result.txHash).equal(txHash); }); }); }); diff --git a/packages/core-sdk/test/unit/resources/ipAccount.test.ts b/packages/core-sdk/test/unit/resources/ipAccount.test.ts index 77b3dc0c..dda58f98 100644 --- a/packages/core-sdk/test/unit/resources/ipAccount.test.ts +++ b/packages/core-sdk/test/unit/resources/ipAccount.test.ts @@ -11,7 +11,7 @@ describe("Test IPAccountClient", () => { let ipAccountClient: IPAccountClient; let rpcMock: PublicClient; let walletMock: WalletClient; - + const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; beforeEach(() => { rpcMock = createMock(); walletMock = createMock(); @@ -40,95 +40,32 @@ describe("Test IPAccountClient", () => { ); } }); - it("should throw simulateContract error when simulateContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().rejects(new Error("simulateContract error")); - const request: IPAccountExecuteRequest = { - ipId: zeroAddress, - to: zeroAddress, - value: 2, - data: "0x11111111111111111111111111111", - }; - try { - await ipAccountClient.execute(request); - } catch (err) { - expect((err as Error).message).includes("simulateContract error"); - } - }); - it("should throw writeContract error when writeContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().rejects(new Error("writeContract error")); - const request: IPAccountExecuteRequest = { - ipId: zeroAddress, + it("should return txHash when call execute successfully", async () => { + IpAccountImplClient.prototype.execute = sinon.stub().resolves(txHash); + const result = await ipAccountClient.execute({ + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", to: zeroAddress, value: 2, data: "0x11111111111111111111111111111", - }; - try { - await ipAccountClient.execute(request); - } catch (err) { - expect((err as Error).message).includes("writeContract error"); - } - }); + }); - it("should throw waitTx error when waitTx throws an error", async () => { - const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves({}); - walletMock.writeContract = sinon.stub().resolves(txHash); - sinon.stub(utils, "waitTx").rejects(new Error("waitTx error")); - const request: IPAccountExecuteRequest = { - ipId: zeroAddress, - to: zeroAddress, - value: 2, - data: "0x11111111111111111111111111111", - txOptions: { - waitForTransaction: true, - }, - }; - try { - await ipAccountClient.execute(request); - } catch (err) { - expect((err as Error).message).includes("waitTx error"); - } + expect(result.txHash).to.equal(txHash); }); - it("should return txHash if waitTx succeeds", async () => { - const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves({}); - walletMock.writeContract = sinon.stub().resolves(txHash); + it("should return txHash when call execute successfully with waitForTransaction", async () => { + IpAccountImplClient.prototype.execute = sinon.stub().resolves(txHash); sinon.stub(utils, "waitTx").resolves(); - const request: IPAccountExecuteRequest = { - ipId: zeroAddress, + const result = await ipAccountClient.execute({ + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", to: zeroAddress, value: 2, data: "0x11111111111111111111111111111", txOptions: { waitForTransaction: true, }, - }; - - const result = await ipAccountClient.execute(request); - expect(result.txHash).to.equal(txHash); - }); - - it("should return txHash if txOptions is falsy", async () => { - const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().resolves(txHash); - sinon.stub(utils, "waitTx").rejects(new Error("waitTx error")); - const request: IPAccountExecuteRequest = { - ipId: zeroAddress, - to: zeroAddress, - value: 2, - data: "0x11111111111111111111111111111", - txOptions: { - waitForTransaction: false, - }, - }; + }); - const result = await ipAccountClient.execute(request); expect(result.txHash).to.equal(txHash); }); }); @@ -153,76 +90,26 @@ describe("Test IPAccountClient", () => { } }); - it("should throw simulateContract error when simulateContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().rejects(new Error("simulateContract error")); - const request: IPAccountExecuteWithSigRequest = { - ipId: zeroAddress, - to: zeroAddress, - value: 2, - data: "0x11111111111111111111111111111", - signer: zeroAddress, - deadline: 20, - signature: zeroAddress, - }; - try { - await ipAccountClient.executeWithSig(request); - } catch (err) { - expect((err as Error).message).includes("simulateContract error"); - } - }); - - it("should throw writeContract error when writeContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().rejects(new Error("writeContract error")); - const request: IPAccountExecuteWithSigRequest = { - ipId: zeroAddress, + it("should return txHash when call executeWithSig successfully", async () => { + IpAccountImplClient.prototype.executeWithSig = sinon.stub().resolves(txHash); + const result = await ipAccountClient.executeWithSig({ + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", to: zeroAddress, value: 2, data: "0x11111111111111111111111111111", signer: zeroAddress, deadline: 20, signature: zeroAddress, - }; - try { - await ipAccountClient.executeWithSig(request); - } catch (err) { - expect((err as Error).message).includes("writeContract error"); - } - }); + }); - it("should throw waitTx error when waitTx throws an error", async () => { - const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves({}); - walletMock.writeContract = sinon.stub().resolves(txHash); - sinon.stub(utils, "waitTx").rejects(new Error("waitTx error")); - const request: IPAccountExecuteWithSigRequest = { - ipId: zeroAddress, - to: zeroAddress, - value: 2, - data: "0x11111111111111111111111111111", - signer: zeroAddress, - deadline: 20, - signature: zeroAddress, - txOptions: { - waitForTransaction: true, - }, - }; - try { - await ipAccountClient.executeWithSig(request); - } catch (err) { - expect((err as Error).message).includes("waitTx error"); - } + expect(result.txHash).to.equal(txHash); }); - it("should return txHash when waitTx succeeds", async () => { - const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves({}); - walletMock.writeContract = sinon.stub().resolves(txHash); + it("should return txHash when call executeWithSig successfully with waitForTransaction", async () => { + IpAccountImplClient.prototype.executeWithSig = sinon.stub().resolves(txHash); sinon.stub(utils, "waitTx").resolves(); - const request: IPAccountExecuteWithSigRequest = { - ipId: zeroAddress, + const result = await ipAccountClient.executeWithSig({ + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", to: zeroAddress, value: 2, data: "0x11111111111111111111111111111", @@ -232,30 +119,8 @@ describe("Test IPAccountClient", () => { txOptions: { waitForTransaction: true, }, - }; - const result = await ipAccountClient.executeWithSig(request); - expect(result.txHash).to.equal(txHash); - }); - - it("should return txHash if txOptions is falsy", async () => { - const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().resolves(txHash); - sinon.stub(utils, "waitTx").rejects(new Error("waitTx error")); - const request: IPAccountExecuteWithSigRequest = { - ipId: zeroAddress, - to: zeroAddress, - value: 2, - data: "0x11111111111111111111111111111", - signer: zeroAddress, - deadline: 20, - signature: zeroAddress, - txOptions: { - waitForTransaction: false, - }, - }; + }); - const result = await ipAccountClient.executeWithSig(request); expect(result.txHash).to.equal(txHash); }); }); diff --git a/packages/core-sdk/test/unit/resources/nftClient.test.ts b/packages/core-sdk/test/unit/resources/nftClient.test.ts index 2c9809e2..3698bc23 100644 --- a/packages/core-sdk/test/unit/resources/nftClient.test.ts +++ b/packages/core-sdk/test/unit/resources/nftClient.test.ts @@ -12,11 +12,8 @@ describe("Test NftClient", () => { let nftClient: NftClient; let rpcMock: PublicClient; let walletMock: WalletClient; - - const mock = { - txHash: "0x063834efe214f4199b1ad7181ce8c5ced3e15d271c8e866da7c89e86ee629cfb", - nftContract: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", - }; + const txHash = "0x063834efe214f4199b1ad7181ce8c5ced3e15d271c8e866da7c89e86ee629cfb"; + const mintFeeToken = "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"; beforeEach(() => { rpcMock = createMock(); @@ -29,83 +26,64 @@ describe("Test NftClient", () => { }); describe("test for CreateNFTCollection", () => { - const reqBody: CreateNFTCollectionRequest = { - name: "test-collection", - symbol: "TEST", - maxSupply: 100, - owner: "0x0000000000000000000000000000000000000001" as Hex, - txOptions: { - waitForTransaction: false, - }, - }; - - it("should throw simulateContract error if simulateContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().throws(new Error("simulateContract error")); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves(); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - + it("should throw mint fee error when call createNFTCollection given mintFee less than 0", async () => { try { - await nftClient.createNFTCollection(reqBody); - } catch (err) { - expect((err as Error).message.includes("simulateContract error")); - } - }); - - it("should throw writeContract error if writeContract throws an error", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().throws(new Error("writeContract error")); - - try { - await nftClient.createNFTCollection(reqBody); - } catch (err) { - expect((err as Error).message.includes("writeContract error")); + await nftClient.createNFTCollection({ + name: "name", + symbol: "symbol", + maxSupply: 1, + mintFee: -1n, + }); + } catch (e) { + expect((e as Error).message).equal( + "Failed to create a SPG NFT collection: Invalid mint fee token address, mint fee is greater than 0.", + ); } }); - it("should throw Invalid mintFee and mintFeeToken error if mintFee is 0", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); + it("should throw mint fee error when call createNFTCollection given mintFeeToken is invalid", async () => { try { - await nftClient.createNFTCollection({ ...reqBody, mintFee: 0n }); - } catch (err) { - expect((err as Error).message).equal( + await nftClient.createNFTCollection({ + name: "name", + symbol: "symbol", + maxSupply: 1, + mintFee: 1n, + }); + } catch (e) { + expect((e as Error).message).equal( "Failed to create a SPG NFT collection: Invalid mint fee token address, mint fee is greater than 0.", ); } }); - it("should return txHash and nftContract if txOptions.waitForTransaction is truthy", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - rpcMock.waitForTransactionReceipt = sinon.stub().resolves(); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); + it("should return txHash when call createNFTCollection successfully", async () => { + sinon.stub(nftClient.spgClient, "createCollection").resolves(txHash); + const result = await nftClient.createNFTCollection({ + name: "name", + symbol: "symbol", + maxSupply: 1, + mintFee: 1n, + mintFeeToken: mintFeeToken, + }); - sinon.stub(nftClient.spgClient, "parseTxCollectionCreatedEvent").returns([ - { - nftContract: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", - }, - ]); + expect(result.txHash).equal(txHash); + }); + it("should return txHash when call createNFTCollection successfully with waitForTransaction", async () => { + const nftContract = "0x73fcb515cee99e4991465ef586cfe2b072ebb512"; + sinon.stub(nftClient.spgClient, "createCollection").resolves(txHash); + sinon.stub(nftClient.spgClient, "parseTxCollectionCreatedEvent").returns([{ nftContract }]); const result = await nftClient.createNFTCollection({ - ...reqBody, - owner: undefined, + name: "name", + symbol: "symbol", + owner: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", txOptions: { waitForTransaction: true, }, }); - expect(result.txHash).to.equal(mock.txHash); - expect(result.nftContract).to.equal(mock.nftContract); - }); - - it("should return txHash if txOptions.waitForTransaction is falsy", async () => { - rpcMock.simulateContract = sinon.stub().resolves({ request: null }); - walletMock.writeContract = sinon.stub().resolves(mock.txHash); - const result = await nftClient.createNFTCollection({ - ...reqBody, - maxSupply: undefined, - mintFee: undefined, - mintFeeToken: undefined, - }); - expect(result.txHash).to.equal(mock.txHash); + expect(result.txHash).equal(txHash); + expect(result.nftContract).equal(nftContract); }); }); }); diff --git a/packages/core-sdk/test/unit/resources/permission.test.ts b/packages/core-sdk/test/unit/resources/permission.test.ts index 130ff20b..7b482ff3 100644 --- a/packages/core-sdk/test/unit/resources/permission.test.ts +++ b/packages/core-sdk/test/unit/resources/permission.test.ts @@ -2,7 +2,7 @@ import { expect } from "chai"; import { createMock } from "../testUtils"; import * as sinon from "sinon"; import { PermissionClient, AddressZero } from "../../../src"; -import { PublicClient, WalletClient, Account, LocalAccount } from "viem"; +import { PublicClient, WalletClient, LocalAccount } from "viem"; import { AccessPermission } from "../../../src/types/resources/permission"; const { IpAccountImplClient } = require("../../../src/abi/generated"); diff --git a/packages/core-sdk/test/unit/utils/utils.test.ts b/packages/core-sdk/test/unit/utils/utils.test.ts index a69eda28..7825a741 100644 --- a/packages/core-sdk/test/unit/utils/utils.test.ts +++ b/packages/core-sdk/test/unit/utils/utils.test.ts @@ -5,12 +5,7 @@ import { sepolia } from "viem/chains"; import { TypedData } from "../../../src/types/common"; import { SupportedChainIds } from "../../../src/types/config"; import { - isIntegerString, - decodeShortstring, - splitIntoBytes32, waitTxAndFilterLog, - dictToQueryParams, - typedDataArrayToBytesArray, chainStringToViemChain, waitTx, getAddress, @@ -18,57 +13,6 @@ import { import { createMock } from "../testUtils"; import { licensingModuleAbi } from "../../../src/abi/generated"; -describe("Test isIntegerString", () => { - it("should return true when passing in an integer string", () => { - expect(isIntegerString("7")).to.be.true; - }); - - it("should return false when passing in a non-integer string", () => { - expect(isIntegerString("a")).to.be.false; - }); -}); - -describe("Test decodeShortstring", () => { - it("should decode a short string", () => { - const encodedString = "0x4368616e6e656c732d4f662d446973747269627574696f6e0000000000000018"; // "Hello World" in hex - const decodedString = decodeShortstring(encodedString); - expect(decodedString).to.equal("Channels-Of-Distribution"); - }); - - it("should return an empty string for an empty input", () => { - const encodedString = "0x"; - const decodedString = decodeShortstring(encodedString); - expect(decodedString).to.equal(""); - }); -}); - -describe("splitIntoBytes32", () => { - it("splits a valid hex string into bytes32 chunks", () => { - const hexString = "a".repeat(64); - const result = splitIntoBytes32(hexString); - expect(result).to.deep.equal(["0x" + "a".repeat(64)]); - }); - - it("handles hex strings with 0x prefix correctly", () => { - const hexString = "0x" + "b".repeat(64); - const result = splitIntoBytes32(hexString); - expect(result).to.deep.equal(["0x" + "b".repeat(64)]); - }); - - it("throws an error if hex string length is not a multiple of 64", () => { - const hexString = "c".repeat(63); - expect(() => splitIntoBytes32(hexString)).to.throw( - "Hex string length must be a multiple of 64.", - ); - }); - - it("handles empty hex string", () => { - const hexString = ""; - const result = splitIntoBytes32(hexString); - expect(result).to.deep.equal([]); - }); -}); - describe("Test waitTxAndFilterLog", () => { const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; let rpcMock: viem.PublicClient = createMock(); @@ -200,17 +144,6 @@ describe("Test waitTxAndFilterLog", () => { }); }); -describe("Test dictToQueryParams", () => { - it("should return expected query string", () => { - const query = { - package: "sdk", - test: "dictToQueryParams", - }; - const queryStr = dictToQueryParams(query); - expect(queryStr).to.equal("package=sdk&test=dictToQueryParams"); - }); -}); - describe("Test chainStringToViemChain", () => { it("should throw error if chainId is not supported", () => { try { @@ -220,42 +153,6 @@ describe("Test chainStringToViemChain", () => { } }); - describe("Test typedDataArrayToBytesArray", () => { - it("should return expected bytes array", () => { - const data = [ - [ - [viem.zeroAddress], - [20, 2, 4], - [viem.zeroAddress], - [viem.zeroAddress], - [1, 2, 3], - [1, 2, 3], - ], - ]; - const typedDataArray: Array = [ - { - interface: "(address[], uint32[], address[], address[], uint32[], uint32[])", - data, - }, - ]; - const result = typedDataArrayToBytesArray(typedDataArray); - const decodedResult = viem.decodeAbiParameters( - viem.parseAbiParameters("(address[], uint32[], address[], address[], uint32[], uint32[])"), - result[0], - ); - expect(result.length).to.equal(1); - expect(result[0].startsWith("0x")).to.equal(true); - expect(decodedResult.length).to.equal(1); - expect(decodedResult[0].length).to.equal(6); - expect(decodedResult[0][0]).includes.all.members(data[0][0]); - expect(decodedResult[0][1]).includes.all.members(data[0][1]); - expect(decodedResult[0][2]).includes.all.members(data[0][2]); - expect(decodedResult[0][3]).includes.all.members(data[0][3]); - expect(decodedResult[0][4]).includes.all.members(data[0][4]); - expect(decodedResult[0][5]).includes.all.members(data[0][5]); - }); - }); - it("should return sepolia if id is 11155111", () => { const chain = chainStringToViemChain("11155111"); expect(chain).to.equal(sepolia); From 003e6dff05441884023f35e7c5a497370d01a282 Mon Sep 17 00:00:00 2001 From: Bonnie57 <146059114+bonnie57@users.noreply.github.com> Date: Sat, 22 Jun 2024 01:31:46 +0800 Subject: [PATCH 2/3] React sdk (#212) * Modify calling signTypedData by wallet client * Upload pnpm-lock.yaml * Generator resource files * Generate index file * Add some config * Add comments for method * Fix react bundle issue * Update README.md * Update bundle config * Update core-sdk version in order to solve linking react-sdk * reset core-sdk version * Link core sdk * Modify core-sdk reference * Cancel fix code command * Set link-workspace-packages false * Modify pnpm-lock file * test * Rename StoryProtocolContext * Add comments about npmrc * Modify package.json * Add getPermissionSignature and export the related types * Update README.md --- .npmrc | 2 + packages/react-sdk/.eslintrc.cjs | 7 + packages/react-sdk/README.MD | 84 +++ packages/react-sdk/generator/index.js | 125 ++++ .../react-sdk/generator/templates/index.js | 13 + .../react-sdk/generator/templates/resource.js | 44 ++ packages/react-sdk/package.json | 75 +++ .../react-sdk/src/StoryProtocolContext.tsx | 20 + packages/react-sdk/src/index.ts | 76 +++ .../react-sdk/src/resources/useDispute.ts | 126 ++++ .../react-sdk/src/resources/useIpAccount.ts | 111 ++++ .../react-sdk/src/resources/useIpAsset.ts | 291 ++++++++++ .../react-sdk/src/resources/useLicense.ts | 236 ++++++++ .../react-sdk/src/resources/useNftClient.ts | 55 ++ .../react-sdk/src/resources/usePermission.ts | 223 ++++++++ .../react-sdk/src/resources/useRoyalty.ts | 211 +++++++ packages/react-sdk/tsconfig.json | 9 + pnpm-lock.yaml | 540 ++++++++++++++---- 18 files changed, 2121 insertions(+), 127 deletions(-) create mode 100644 .npmrc create mode 100644 packages/react-sdk/.eslintrc.cjs create mode 100644 packages/react-sdk/README.MD create mode 100644 packages/react-sdk/generator/index.js create mode 100644 packages/react-sdk/generator/templates/index.js create mode 100644 packages/react-sdk/generator/templates/resource.js create mode 100644 packages/react-sdk/package.json create mode 100644 packages/react-sdk/src/StoryProtocolContext.tsx create mode 100644 packages/react-sdk/src/index.ts create mode 100644 packages/react-sdk/src/resources/useDispute.ts create mode 100644 packages/react-sdk/src/resources/useIpAccount.ts create mode 100644 packages/react-sdk/src/resources/useIpAsset.ts create mode 100644 packages/react-sdk/src/resources/useLicense.ts create mode 100644 packages/react-sdk/src/resources/useNftClient.ts create mode 100644 packages/react-sdk/src/resources/usePermission.ts create mode 100644 packages/react-sdk/src/resources/useRoyalty.ts create mode 100644 packages/react-sdk/tsconfig.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..a831eb9e --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +# Once core-react is published to npm, react-sdk can be updated to use it +link-workspace-packages: false \ No newline at end of file diff --git a/packages/react-sdk/.eslintrc.cjs b/packages/react-sdk/.eslintrc.cjs new file mode 100644 index 00000000..7a32ad11 --- /dev/null +++ b/packages/react-sdk/.eslintrc.cjs @@ -0,0 +1,7 @@ +module.exports = { + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir: __dirname, + }, + extends: ["@story-protocol/eslint-config"], +}; diff --git a/packages/react-sdk/README.MD b/packages/react-sdk/README.MD new file mode 100644 index 00000000..39414e2a --- /dev/null +++ b/packages/react-sdk/README.MD @@ -0,0 +1,84 @@ +# Story Protocol React SDK + +The react-sdk is a library that provides a set of hooks to interact with the SDK. It is designed to be used in a React application. + +## How to use Story Protocol SDK in Your Project + +### Generate React SDK + +1. Install the dependencies + +```bash +pnpm install +``` + +2. Update the `@story-protocol/core-sdk` package version in the `packages/react-sdk/package.json` file to the latest version. + +Important: Once publish core-sdk, you need to update the core-sdk version in the react-sdk package.json file. + +3. Generate the SDK + +```bash +pnpm run generate +``` + +This SDK is generated using the command `pnpm run generate`. The source code resides in the `packages/sdk` directory and the generated SDK can be found in the `packages/react-sdk` folder. + +### How to use Story Protocol React SDK in Your Project + +- Install Story Protocol React SDK + +```bash +pnpm install @story-protocol/react-sdk +``` + +- Import the provider in your React application + +```typescript +import { StoryProvider } from "@story-protocol/react-sdk"; +const client = StoryClient.newClient(config); + +``` + +- Use the hooks in your component + +```typescript +import { useIpAsset } from "@story-protocol/react-sdk"; +const { data, error, loading, register } = useIpAsset(); +register({ nftContract: "0x1234", tokenId: "1" }); +``` + +### How To Build and Test Story Protocol React SDK for local testing + +- Install yalc + +```bash +npm install -g yalc +``` + +- For manual testing of the react-sdk, set up a separate web project. The guide below uses `yalc` to link the `react-sdk` locally, enabling its installation and import for testing. + +Under the `typescript-sdk/packages/react-sdk` directory: + +- Execute `npm run build` to build your latest code. +- Run `yalc publish`. You should see a message like `@story-protocol/react-sdk@ published in store.` (Note: The version number may vary). +- To set up your testing environment (e.g., a new Next.js project), use `yalc add @story-protocol/react-sdk@` (ensure the version number is updated accordingly). + +- Run `pnpm install`. This installs `@story-protocol/react-sdk@` with your local changes. + +### Steps to Refresh the Changes + +Under the `typescript-sdk/packages/react-sdk` directory: + +- Execute `npm run build` to build your latest code. +- Run `yalc push`. + +In your testing environment: + +- Run `yalc update` to pull the latest changes. diff --git a/packages/react-sdk/generator/index.js b/packages/react-sdk/generator/index.js new file mode 100644 index 00000000..5eab06ac --- /dev/null +++ b/packages/react-sdk/generator/index.js @@ -0,0 +1,125 @@ +const ejs = require("ejs"); +const fs = require("fs"); +const path = require("path"); +const ts = require("typescript"); +const resourcesFolder = path.resolve(__dirname, "../../core-sdk/src/resources"); +const resourceTemplate = require("./templates/resource"); +const indexTemplate = require("./templates/index"); + +console.log("🚀🚀 React SDK generator started!\n"); +const isPrimitiveType = (type) => { + return [ + "string", + "number", + "boolean", + "symbol", + "undefined", + "null", + "bigint", + "string|bigint|number", + ].includes(type); +}; +const isViemType = (type) => { + return ["Hex", "Address"].includes(type); +}; +const visit = (file) => { + let program = ts.createProgram([file], { allowJs: true }); + const sourceFile = program.getSourceFile(file); + ts.createProgram([sourceFile.fileName], {}).getTypeChecker(); + const publicMethods = []; + ts.forEachChild(sourceFile, (node) => { + if (ts.isClassDeclaration(node)) { + for (const member of node.members) { + if ( + ts.isMethodDeclaration(member) && + (member.modifiers?.some( + (m) => m.kind === ts.SyntaxKind.PublicKeyword + ) ?? + true) && + member.name && + ts.isIdentifier(member.name) + ) { + const requests = []; + program.getTypeChecker().getSignatureFromDeclaration(member); + member.parameters.forEach((parameter) => { + requests.push({ + name: parameter.name.escapedText, + type: parameter.type.getText(), + }); + }); + const method = { + name: member.name.text, + requests, + responseType: member.type + ?.getText() + .replace("Promise<", "") + .replace(">", ""), + comments: + ts + .getLeadingCommentRanges(sourceFile.text, member.pos) + ?.map((range) => + sourceFile.text.substring(range.pos, range.end).trim() + ) || [], + }; + publicMethods.push(method); + } + } + } + }); + return publicMethods; +}; +let fileNames = []; +let exportTypes = []; +fs.readdirSync(resourcesFolder).forEach((file) => { + let sources = []; + const fileName = + file.replace(".ts", "").charAt(0).toUpperCase() + + file.replace(".ts", "").slice(1); + fileNames.push(fileName); + const methods = visit(path.resolve(resourcesFolder, file)); + const methodNames = methods.map((method) => method.name); + const types = methods.reduce( + (acc, curr) => + acc.concat( + curr.requests.map((item) => item.type), + curr.responseType + ), + [] + ); + const filteredTypes = [ + ...new Set( + types + .filter((type) => !isPrimitiveType(type)) + .filter((type) => !isViemType(type)) + ), + ]; + exportTypes.push(...filteredTypes); + sources.push( + ejs.render(resourceTemplate.startTemplate, { + types: [filteredTypes], + name: fileName, + methodNames, + viemTypes: [...new Set(types.filter((type) => isViemType(type)))], + }) + ); + const methodTemplates = methods.map((method) => { + return ejs.render(resourceTemplate.methodTemplate, { + method: method, + fileName: file.replace(".ts", ""), + comments: method.comments, + }); + }); + + sources = sources.concat( + methodTemplates, + ejs.render(resourceTemplate.endTemplate, { methodNames, name: fileName }) + ); + fs.writeFileSync(`src/resources/use${fileName}.ts`, sources.join("\n")); +}); +const indexSource = ejs.render(indexTemplate, { + resources: fileNames, + types: exportTypes, +}); +fs.writeFileSync("src/index.ts", indexSource); + +console.log("👍👍 React SDK templates generated successfully!"); diff --git a/packages/react-sdk/generator/templates/index.js b/packages/react-sdk/generator/templates/index.js new file mode 100644 index 00000000..84bd19a3 --- /dev/null +++ b/packages/react-sdk/generator/templates/index.js @@ -0,0 +1,13 @@ +const indexTemplate = ` +export { StoryProvider } from "./StoryProtocolContext"; +export { getPermissionSignature, AccessPermission, PIL_TYPE, } from "@story-protocol/core-sdk"; +export type { PermissionSignatureRequest, StoryConfig, SupportedChainIds, + <%types.forEach((type,index)=>{%> + <%=type%><%=index === types.length - 1 ? '' : ','%> + <%})%> + } from "@story-protocol/core-sdk"; +<% resources.forEach((resource) => {%> +export { default as use<%=resource %> } from "./resources/use<%=resource %>"; +<%})%> +`; +module.exports = indexTemplate; diff --git a/packages/react-sdk/generator/templates/resource.js b/packages/react-sdk/generator/templates/resource.js new file mode 100644 index 00000000..0bc69e71 --- /dev/null +++ b/packages/react-sdk/generator/templates/resource.js @@ -0,0 +1,44 @@ +const methodTemplate = `<%=comments%>\nconst <%=method.name %> = async (<% method.requests.forEach((item, index)=> { %> + <%= item.name %>: <%= item.type %><%= index === method.requests.length - 1 ? '' : ',' %> + <% }); %>): Promise<<%- method.responseType %>> => { + try { + setLoadings((prev) => ({ ...prev, <%=method.name %>: true })); + setErrors((prev) => ({ ...prev, <%=method.name %>: null })); + const response = await client.<%= fileName%>.<%=method.name %>(<% method.requests.forEach((item,index)=>{%> + <%=item.name %><%=index === method.requests.length - 1 ? '' : ',' %> + <% })%>); + setLoadings((prev ) => ({ ...prev, <%=method.name %>: false })); + return response; + }catch(e){ + if(e instanceof Error){ + setErrors((prev) => ({ ...prev, <%=method.name %>: e.message })); + setLoadings((prev) => ({ ...prev, <%=method.name %>: false })); + } + throw new Error(\`unhandled error type\`); + } + }; + `; + +const startTemplate = `import { <% types.forEach((type,index)=>{%>\n<%=type %><%= index===types.length-1?'':','%><%})%> + } from "@story-protocol/core-sdk"; + <% if (viemTypes.length > 0) { %> + import { <% viemTypes.forEach((type, index) => { %>\n<%= type %><%= index === viemTypes.length - 1 ? '' : ',' %><% }) %> + } from "viem"; + <% } %> + + import { useState } from "react"; + import { useStoryContext } from "../StoryProtocolContext"; + const use<%=name %> = () => { + const client = useStoryContext(); + const [loadings,setLoadings] = useState>({<% methodNames.forEach((name,index)=>{%><%=name %>: false<%=index === methodNames.length - 1 ? '' : ',' %> <%})%>}); + const [errors,setErrors] = useState>({ <% methodNames.forEach((name,index)=>{%><%=name %>: null<%=index === methodNames.length - 1 ? '' : ',' %><%})%> }); + `; + +const endTemplate = `return { + loadings, + errors, + <% methodNames.forEach((name,index)=>{%><%=name %><%=index === methodNames.length - 1 ? '' : ',' %> + <%})%> + };}\nexport default use<%=name %>;`; + +module.exports = { startTemplate, endTemplate, methodTemplate }; diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json new file mode 100644 index 00000000..944efa8e --- /dev/null +++ b/packages/react-sdk/package.json @@ -0,0 +1,75 @@ +{ + "name": "@story-protocol/react-sdk", + "version": "1.0.0-rc.14", + "description": "The Story Protocol React SDK", + "main": "dist/story-protocol-react-sdk.cjs.js", + "module": "dist/story-protocol-react-sdk.esm.js", + "exports": { + ".": { + "module": "./dist/story-protocol-react-sdk.esm.js", + "default": "./dist/story-protocol-react-sdk.cjs.js" + }, + "./package.json": "./package.json" + }, + "scripts": { + "generate": "node ./generator/index.js && npm run fix", + "build": "pnpm run fix && preconstruct build", + "fix": "pnpm run format:fix && pnpm run lint:fix", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "lint:fix": "pnpm run lint --fix", + "lint": "eslint ./src", + "tsc": "tsc --noEmit" + }, + "sideEffects": false, + "files": [ + "dist/**/*" + ], + "preconstruct": { + "entrypoints": [ + "index.ts" + ], + "exports": true, + "externals": [ + "react", + "@story-protocol/core-sdk" + ] + }, + "keywords": [ + "story-protocol", + "react", + "sdk", + "react hooks" + ], + "babel": { + "presets": [ + "@babel/preset-env", + "@babel/preset-typescript", + [ + "@babel/preset-react", + { + "runtime": "automatic" + } + ] + ] + }, + "license": "MIT", + "dependencies": { + "@story-protocol/core-sdk": "1.0.0-rc.14", + "react": "^18.3.1", + "viem": "^2.8.12", + "@types/react": "^18.3.3" + }, + "devDependencies": { + "@babel/preset-env": "^7.22.20", + "@babel/preset-react": "^7.24.7", + "@babel/preset-typescript": "^7.23.0", + "@preconstruct/cli": "^2.8.1", + "@story-protocol/eslint-config": "workspace:*", + "@story-protocol/prettier-config": "workspace:*", + "@story-protocol/tsconfig": "workspace:*", + "ts-node": "^10.9.1", + "typescript": "^5.4.5", + "ejs": "^3.1.10" + } +} diff --git a/packages/react-sdk/src/StoryProtocolContext.tsx b/packages/react-sdk/src/StoryProtocolContext.tsx new file mode 100644 index 00000000..56d0b62f --- /dev/null +++ b/packages/react-sdk/src/StoryProtocolContext.tsx @@ -0,0 +1,20 @@ +import { createContext, useContext, ReactNode } from "react"; +import { StoryClient, StoryConfig } from "@story-protocol/core-sdk"; + +type Props = { + config: StoryConfig; + children: ReactNode; +}; + +const StoryContext = createContext({} as StoryClient); + +const StoryProvider = ({ config, children }: Props) => { + const client = StoryClient.newClient(config); + return ( + {children} + ); +}; +const useStoryContext = (): StoryClient => { + return useContext(StoryContext); +}; +export { useStoryContext, StoryProvider }; diff --git a/packages/react-sdk/src/index.ts b/packages/react-sdk/src/index.ts new file mode 100644 index 00000000..51840acd --- /dev/null +++ b/packages/react-sdk/src/index.ts @@ -0,0 +1,76 @@ +export { StoryProvider } from "./StoryProtocolContext"; +export { + getPermissionSignature, + AccessPermission, + PIL_TYPE, +} from "@story-protocol/core-sdk"; +export type { + PermissionSignatureRequest, + StoryConfig, + SupportedChainIds, + RaiseDisputeRequest, + RaiseDisputeResponse, + CancelDisputeRequest, + CancelDisputeResponse, + ResolveDisputeRequest, + ResolveDisputeResponse, + IPAccountExecuteRequest, + IPAccountExecuteResponse, + IPAccountExecuteWithSigRequest, + IPAccountExecuteWithSigResponse, + IpAccountImplStateResponse, + RegisterRequest, + RegisterIpResponse, + RegisterDerivativeRequest, + RegisterDerivativeResponse, + RegisterDerivativeWithLicenseTokensRequest, + RegisterDerivativeWithLicenseTokensResponse, + CreateIpAssetWithPilTermsRequest, + CreateIpAssetWithPilTermsResponse, + RegisterIpAndAttachPilTermsRequest, + RegisterIpAndAttachPilTermsResponse, + RegisterIpAndMakeDerivativeRequest, + RegisterIpAndMakeDerivativeResponse, + RegisterNonComSocialRemixingPILRequest, + RegisterPILResponse, + RegisterCommercialUsePILRequest, + RegisterCommercialRemixPILRequest, + AttachLicenseTermsRequest, + AttachLicenseTermsResponse, + MintLicenseTokensRequest, + MintLicenseTokensResponse, + LicenseTermsId, + PiLicenseTemplateGetLicenseTermsResponse, + CreateNFTCollectionRequest, + CreateNFTCollectionResponse, + SetPermissionsRequest, + SetPermissionsResponse, + CreateSetPermissionSignatureRequest, + SetAllPermissionsRequest, + SetBatchPermissionsRequest, + CreateBatchPermissionSignatureRequest, + CollectRoyaltyTokensRequest, + CollectRoyaltyTokensResponse, + PayRoyaltyOnBehalfRequest, + PayRoyaltyOnBehalfResponse, + ClaimableRevenueRequest, + ClaimableRevenueResponse, + ClaimRevenueRequest, + ClaimRevenueResponse, + SnapshotRequest, + SnapshotResponse, +} from "@story-protocol/core-sdk"; + +export { default as useDispute } from "./resources/useDispute"; + +export { default as useIpAccount } from "./resources/useIpAccount"; + +export { default as useIpAsset } from "./resources/useIpAsset"; + +export { default as useLicense } from "./resources/useLicense"; + +export { default as useNftClient } from "./resources/useNftClient"; + +export { default as usePermission } from "./resources/usePermission"; + +export { default as useRoyalty } from "./resources/useRoyalty"; diff --git a/packages/react-sdk/src/resources/useDispute.ts b/packages/react-sdk/src/resources/useDispute.ts new file mode 100644 index 00000000..e4bbc07c --- /dev/null +++ b/packages/react-sdk/src/resources/useDispute.ts @@ -0,0 +1,126 @@ +import { + RaiseDisputeRequest, + RaiseDisputeResponse, + CancelDisputeRequest, + CancelDisputeResponse, + ResolveDisputeRequest, + ResolveDisputeResponse, +} from "@story-protocol/core-sdk"; +import { useState } from "react"; + +import { useStoryContext } from "../StoryProtocolContext"; + +const useDispute = () => { + const client = useStoryContext(); + const [loadings, setLoadings] = useState>({ + raiseDispute: false, + cancelDispute: false, + resolveDispute: false, + }); + const [errors, setErrors] = useState>({ + raiseDispute: null, + cancelDispute: null, + resolveDispute: null, + }); + + /** + * Raises a dispute on a given ipId + * @param request - The request object containing necessary data to raise a dispute. + * @param request.targetIpId - The IP ID that is the target of the dispute. + * @param request.arbitrationPolicy - The address of the arbitration policy. + * @param request.linkToDisputeEvidence - The link to the dispute evidence. + * @param request.targetTag - The target tag of the dispute. + * @param request.calldata - Optional calldata to initialize the policy. + * @param request.txOptions - Optional transaction options. + * @returns A Promise that resolves to a RaiseDisputeResponse containing the transaction hash. + * @throws `NotRegisteredIpId` if targetIpId is not registered in the IPA Registry. + * @throws `NotWhitelistedDisputeTag` if targetTag is not whitelisted. + * @throws `ZeroLinkToDisputeEvidence` if linkToDisputeEvidence is empty + * @calls raiseDispute(address _targetIpId, string memory _linkToDisputeEvidence, bytes32 _targetTag, bytes calldata _data) external nonReentrant returns (uint256) { + * @emits DisputeRaised (disputeId_, targetIpId, msg.sender, arbitrationPolicy, linkToDisputeEvidence, targetTag, calldata); + */ + const raiseDispute = async ( + request: RaiseDisputeRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, raiseDispute: true })); + setErrors((prev) => ({ ...prev, raiseDispute: null })); + const response = await client.dispute.raiseDispute(request); + setLoadings((prev) => ({ ...prev, raiseDispute: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, raiseDispute: e.message })); + setLoadings((prev) => ({ ...prev, raiseDispute: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Cancels an ongoing dispute + * @param request - The request object containing details to cancel the dispute. + * @param request.disputeId The ID of the dispute to be cancelled. + * @param request.calldata Optional additional data used in the cancellation process. + * @returns A Promise that resolves to a CancelDisputeResponse containing the transaction hash. + * @throws NotInDisputeState, if the currentTag of the Dispute is not being disputed + * @throws NotDisputeInitiator, if the transaction executor is not the one that initiated the dispute + * @throws error if the Dispute's ArbitrationPolicy contract is not valid + * @calls cancelDispute(uint256 _disputeId, bytes calldata _data) external nonReentrant { + * @emits DisputeCancelled (_disputeId, _data); + */ + const cancelDispute = async ( + request: CancelDisputeRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, cancelDispute: true })); + setErrors((prev) => ({ ...prev, cancelDispute: null })); + const response = await client.dispute.cancelDispute(request); + setLoadings((prev) => ({ ...prev, cancelDispute: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, cancelDispute: e.message })); + setLoadings((prev) => ({ ...prev, cancelDispute: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Resolves a dispute after it has been judged + * @param request - The request object containing details to resolve the dispute. + * @param request.disputeId The ID of the dispute to be resolved. + * @param request.data The data to resolve the dispute. + * @returns A Promise that resolves to a ResolveDisputeResponse. + * @throws NotAbleToResolve, if currentTag is still in dispute (i.e still needs a judgement to be set) + * @throws NotDisputeInitiator, if the transaction executor is not the one that initiated the dispute + * @emits DisputeResolved (_disputeId) + */ + const resolveDispute = async ( + request: ResolveDisputeRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, resolveDispute: true })); + setErrors((prev) => ({ ...prev, resolveDispute: null })); + const response = await client.dispute.resolveDispute(request); + setLoadings((prev) => ({ ...prev, resolveDispute: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, resolveDispute: e.message })); + setLoadings((prev) => ({ ...prev, resolveDispute: false })); + } + throw new Error(`unhandled error type`); + } + }; + + return { + loadings, + errors, + raiseDispute, + cancelDispute, + resolveDispute, + }; +}; +export default useDispute; diff --git a/packages/react-sdk/src/resources/useIpAccount.ts b/packages/react-sdk/src/resources/useIpAccount.ts new file mode 100644 index 00000000..9177757b --- /dev/null +++ b/packages/react-sdk/src/resources/useIpAccount.ts @@ -0,0 +1,111 @@ +import { + IPAccountExecuteRequest, + IPAccountExecuteResponse, + IPAccountExecuteWithSigRequest, + IPAccountExecuteWithSigResponse, + IpAccountImplStateResponse, +} from "@story-protocol/core-sdk"; +import { useState } from "react"; + +import { useStoryContext } from "../StoryProtocolContext"; + +const useIpAccount = () => { + const client = useStoryContext(); + const [loadings, setLoadings] = useState>({ + execute: false, + executeWithSig: false, + getIpAccountNonce: false, + }); + const [errors, setErrors] = useState>({ + execute: null, + executeWithSig: null, + getIpAccountNonce: null, + }); + + /** Executes a transaction from the IP Account. + * @param request - The request object containing necessary data to execute IP Account a transaction. + * @param request.ipId The Ip Id to get ip account. + * @param request.to The recipient of the transaction. + * @param request.value The amount of Ether to send. + * @param request.accountAddress The ipId to send. + * @param request.data The data to send along with the transaction. + * @returns Tx hash for the transaction. + */ + const execute = async ( + request: IPAccountExecuteRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, execute: true })); + setErrors((prev) => ({ ...prev, execute: null })); + const response = await client.ipAccount.execute(request); + setLoadings((prev) => ({ ...prev, execute: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, execute: e.message })); + setLoadings((prev) => ({ ...prev, execute: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** Executes a transaction from the IP Account. + * @param request - The request object containing necessary data to execute IP Account a transaction. + * @param request.ipId The Ip Id to get ip account. + * @param request.to The recipient of the transaction. + * @param request.value The amount of Ether to send. + * @param request.data The data to send along with the transaction. + * @param request.signer The signer of the transaction. + * @param request.deadline The deadline of the transaction signature. + * @param request.signature The signature of the transaction, EIP-712 encoded. + * @returns Tx hash for the transaction. + */ + const executeWithSig = async ( + request: IPAccountExecuteWithSigRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, executeWithSig: true })); + setErrors((prev) => ({ ...prev, executeWithSig: null })); + const response = await client.ipAccount.executeWithSig(request); + setLoadings((prev) => ({ ...prev, executeWithSig: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, executeWithSig: e.message })); + setLoadings((prev) => ({ ...prev, executeWithSig: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** Returns the IPAccount's internal nonce for transaction ordering. + * @param ipId The IP ID + * @returns The nonce for transaction ordering. + */ + const getIpAccountNonce = async ( + ipId: string + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, getIpAccountNonce: true })); + setErrors((prev) => ({ ...prev, getIpAccountNonce: null })); + const response = await client.ipAccount.getIpAccountNonce(ipId); + setLoadings((prev) => ({ ...prev, getIpAccountNonce: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, getIpAccountNonce: e.message })); + setLoadings((prev) => ({ ...prev, getIpAccountNonce: false })); + } + throw new Error(`unhandled error type`); + } + }; + + return { + loadings, + errors, + execute, + executeWithSig, + getIpAccountNonce, + }; +}; +export default useIpAccount; diff --git a/packages/react-sdk/src/resources/useIpAsset.ts b/packages/react-sdk/src/resources/useIpAsset.ts new file mode 100644 index 00000000..0a752bf0 --- /dev/null +++ b/packages/react-sdk/src/resources/useIpAsset.ts @@ -0,0 +1,291 @@ +import { + RegisterRequest, + RegisterIpResponse, + RegisterDerivativeRequest, + RegisterDerivativeResponse, + RegisterDerivativeWithLicenseTokensRequest, + RegisterDerivativeWithLicenseTokensResponse, + CreateIpAssetWithPilTermsRequest, + CreateIpAssetWithPilTermsResponse, + RegisterIpAndAttachPilTermsRequest, + RegisterIpAndAttachPilTermsResponse, + RegisterIpAndMakeDerivativeRequest, + RegisterIpAndMakeDerivativeResponse, +} from "@story-protocol/core-sdk"; +import { useState } from "react"; + +import { useStoryContext } from "../StoryProtocolContext"; + +const useIpAsset = () => { + const client = useStoryContext(); + const [loadings, setLoadings] = useState>({ + register: false, + registerDerivative: false, + registerDerivativeWithLicenseTokens: false, + mintAndRegisterIpAssetWithPilTerms: false, + registerIpAndAttachPilTerms: false, + registerDerivativeIp: false, + }); + const [errors, setErrors] = useState>({ + register: null, + registerDerivative: null, + registerDerivativeWithLicenseTokens: null, + mintAndRegisterIpAssetWithPilTerms: null, + registerIpAndAttachPilTerms: null, + registerDerivativeIp: null, + }); + + /** + * Registers an NFT as IP, creating a corresponding IP record. + * @param request - The request object that contains all data needed to register IP. + * @param request.nftContract The address of the NFT. + * @param request.tokenId The token identifier of the NFT. + * @param request.metadata - [Optional] The metadata for the IP. + * @param request.metadata.metadataURI [Optional] The URI of the metadata for the IP. + * @param request.metadata.metadataHash [Optional] The metadata for the IP. + * @param request.metadata.nftMetadataHash [Optional] The metadata for the IP NFT. + * @param request.deadline [Optional] The deadline for the signature in milliseconds, default is 1000ms. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash and optional IP ID if waitForTxn is set to true. + * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, resolverAddr, metadataProviderAddress, metadata) + */ + const register = async ( + request: RegisterRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, register: true })); + setErrors((prev) => ({ ...prev, register: null })); + const response = await client.ipAsset.register(request); + setLoadings((prev) => ({ ...prev, register: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, register: e.message })); + setLoadings((prev) => ({ ...prev, register: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * 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. + */ + const registerDerivative = async ( + request: RegisterDerivativeRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, registerDerivative: true })); + setErrors((prev) => ({ ...prev, registerDerivative: null })); + const response = await client.ipAsset.registerDerivative(request); + setLoadings((prev) => ({ ...prev, registerDerivative: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, registerDerivative: e.message })); + setLoadings((prev) => ({ ...prev, registerDerivative: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * 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. + */ + const registerDerivativeWithLicenseTokens = async ( + request: RegisterDerivativeWithLicenseTokensRequest + ): Promise => { + try { + setLoadings((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: true, + })); + setErrors((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: null, + })); + const response = await client.ipAsset.registerDerivativeWithLicenseTokens( + request + ); + setLoadings((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: false, + })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: e.message, + })); + setLoadings((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: false, + })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Mint an NFT from a collection and register it as an IP. + * @param request - The request object that contains all data needed to mint and register ip. + * @param request.nftContract The address of the NFT collection. + * @param request.pilType The type of the PIL. + * @param request.metadata - [Optional] The metadata for the IP. + * @param request.metadata.metadataURI [Optional] The URI of the metadata for the IP. + * @param request.metadata.metadataHash [Optional] The metadata for the IP. + * @param request.metadata.nftMetadataHash [Optional] The metadata for the IP NFT. + * @param request.recipient [Optional] The address of the recipient of the minted NFT. + * @param request.mintingFee [Optional] The fee to be paid when minting a license. + * @param request.commercialRevShare [Optional] Percentage of revenue that must be shared with the licensor. + * @param request.currency [Optional] The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash and optional IP ID, Token ID, License Terms Id if waitForTxn is set to true. + * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) + * @emits LicenseTermsAttached (caller, ipId, licenseTemplate, licenseTermsId) + */ + const mintAndRegisterIpAssetWithPilTerms = async ( + request: CreateIpAssetWithPilTermsRequest + ): Promise => { + try { + setLoadings((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: true, + })); + setErrors((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: null, + })); + const response = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms( + request + ); + setLoadings((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: false, + })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: e.message, + })); + setLoadings((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: false, + })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Register a given NFT as an IP and attach Programmable IP License Terms.R. + * @param request - The request object that contains all data needed to mint and register ip. + * @param request.nftContract The address of the NFT collection. + * @param request.tokenId The ID of the NFT. + * @param request.pilType The type of the PIL. + * @param request.metadata - [Optional] The desired metadata for the newly registered IP. + * @param request.metadata.metadataURI [Optional] The the metadata for the IP hash. + * @param request.metadata.metadataHash [Optional] The metadata for the IP. + * @param request.metadata.nftMetadataHash [Optional] The metadata for the IP NFT. + * @param request.deadline [Optional] The deadline for the signature in milliseconds, default is 1000ms. + * @param request.mintingFee [Optional] The fee to be paid when minting a license. + * @param request.commercialRevShare [Optional] Percentage of revenue that must be shared with the licensor. + * @param request.currency [Optional] The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash and optional IP ID, License Terms Id if waitForTxn is set to true. + * @emits LicenseTermsAttached (caller, ipId, licenseTemplate, licenseTermsId) + */ + const registerIpAndAttachPilTerms = async ( + request: RegisterIpAndAttachPilTermsRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, registerIpAndAttachPilTerms: true })); + setErrors((prev) => ({ ...prev, registerIpAndAttachPilTerms: null })); + const response = await client.ipAsset.registerIpAndAttachPilTerms( + request + ); + setLoadings((prev) => ({ ...prev, registerIpAndAttachPilTerms: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ + ...prev, + registerIpAndAttachPilTerms: e.message, + })); + setLoadings((prev) => ({ + ...prev, + registerIpAndAttachPilTerms: false, + })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Register the given NFT as a derivative IP with metadata without using license tokens. + * @param request - The request object that contains all data needed to register derivative IP. + * @param request.nftContract The address of the NFT collection. + * @param request.tokenId The ID of the NFT. + * @param request.derivData The derivative data to be used for registerDerivative. + * @param request.derivData.parentIpIds The IDs of the parent IPs to link the registered derivative IP. + * @param request.derivData.licenseTemplate [Optional] The address of the license template to be used for the linking. + * @param request.derivData.licenseTermsIds The IDs of the license terms to be used for the linking. + * @param request.metadata - [Optional] The desired metadata for the newly registered IP. + * @param request.metadata.metadataURI [Optional] The URI of the metadata for the IP. + * @param request.metadata.metadataHash [Optional] The metadata for the IP. + * @param request.metadata.nftMetadataHash [Optional] The the metadata for the IP NFT. + * @param request.deadline [Optional] The deadline for the signature in milliseconds,default is 1000ms. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash and optional IP ID if waitForTxn is set to true. + * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) + */ + const registerDerivativeIp = async ( + request: RegisterIpAndMakeDerivativeRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, registerDerivativeIp: true })); + setErrors((prev) => ({ ...prev, registerDerivativeIp: null })); + const response = await client.ipAsset.registerDerivativeIp(request); + setLoadings((prev) => ({ ...prev, registerDerivativeIp: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, registerDerivativeIp: e.message })); + setLoadings((prev) => ({ ...prev, registerDerivativeIp: false })); + } + throw new Error(`unhandled error type`); + } + }; + + return { + loadings, + errors, + register, + registerDerivative, + registerDerivativeWithLicenseTokens, + mintAndRegisterIpAssetWithPilTerms, + registerIpAndAttachPilTerms, + registerDerivativeIp, + }; +}; +export default useIpAsset; diff --git a/packages/react-sdk/src/resources/useLicense.ts b/packages/react-sdk/src/resources/useLicense.ts new file mode 100644 index 00000000..9397cd36 --- /dev/null +++ b/packages/react-sdk/src/resources/useLicense.ts @@ -0,0 +1,236 @@ +import { + RegisterNonComSocialRemixingPILRequest, + RegisterPILResponse, + RegisterCommercialUsePILRequest, + RegisterCommercialRemixPILRequest, + AttachLicenseTermsRequest, + AttachLicenseTermsResponse, + MintLicenseTokensRequest, + MintLicenseTokensResponse, + LicenseTermsId, + PiLicenseTemplateGetLicenseTermsResponse, +} from "@story-protocol/core-sdk"; +import { useState } from "react"; + +import { useStoryContext } from "../StoryProtocolContext"; + +const useLicense = () => { + const client = useStoryContext(); + const [loadings, setLoadings] = useState>({ + registerNonComSocialRemixingPIL: false, + registerCommercialUsePIL: false, + registerCommercialRemixPIL: false, + attachLicenseTerms: false, + mintLicenseTokens: false, + getLicenseTerms: false, + }); + const [errors, setErrors] = useState>({ + registerNonComSocialRemixingPIL: null, + registerCommercialUsePIL: null, + registerCommercialRemixPIL: null, + attachLicenseTerms: null, + mintLicenseTokens: null, + getLicenseTerms: null, + }); + + /** + * Convenient function to register a PIL non commercial social remix license to the registry + * @param request - [Optional] The request object that contains all data needed to register a PIL non commercial social remix license. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the optional transaction hash and optional license terms Id. + * @emits LicenseTermsRegistered (licenseTermsId, licenseTemplate, licenseTerms); + */ + const registerNonComSocialRemixingPIL = async ( + request: RegisterNonComSocialRemixingPILRequest + ): Promise => { + try { + setLoadings((prev) => ({ + ...prev, + registerNonComSocialRemixingPIL: true, + })); + setErrors((prev) => ({ ...prev, registerNonComSocialRemixingPIL: null })); + const response = await client.license.registerNonComSocialRemixingPIL( + request + ); + setLoadings((prev) => ({ + ...prev, + registerNonComSocialRemixingPIL: false, + })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ + ...prev, + registerNonComSocialRemixingPIL: e.message, + })); + setLoadings((prev) => ({ + ...prev, + registerNonComSocialRemixingPIL: false, + })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Convenient function to register a PIL commercial use license to the registry. + * @param request - The request object that contains all data needed to register a PIL commercial use license. + * @param request.mintingFee The fee to be paid when minting a license. + * @param request.currency The ERC20 token to be used to pay the minting fee and the token must be registered in story protocol. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the optional transaction hash and optional license terms Id. + * @emits LicenseTermsRegistered (licenseTermsId, licenseTemplate, licenseTerms); + */ + const registerCommercialUsePIL = async ( + request: RegisterCommercialUsePILRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: true })); + setErrors((prev) => ({ ...prev, registerCommercialUsePIL: null })); + const response = await client.license.registerCommercialUsePIL(request); + setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, registerCommercialUsePIL: e.message })); + setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Convenient function to register a PIL commercial Remix license to the registry. + * @param request - The request object that contains all data needed to register license. + * @param request.mintingFee The fee to be paid when minting a license. + * @param request.commercialRevShare Percentage of revenue that must be shared with the licensor. + * @param request.currency The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the optional transaction hash and optional license terms Id. + * @emits LicenseTermsRegistered (licenseTermsId, licenseTemplate, licenseTerms); + */ + const registerCommercialRemixPIL = async ( + request: RegisterCommercialRemixPILRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: true })); + setErrors((prev) => ({ ...prev, registerCommercialRemixPIL: null })); + const response = await client.license.registerCommercialRemixPIL(request); + setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ + ...prev, + registerCommercialRemixPIL: e.message, + })); + setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Attaches license terms to an IP. + * @param request - The request object that contains all data needed to attach license terms. + * @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. + * @returns A Promise that resolves to an object containing the transaction hash. + */ + const attachLicenseTerms = async ( + request: AttachLicenseTermsRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, attachLicenseTerms: true })); + setErrors((prev) => ({ ...prev, attachLicenseTerms: null })); + const response = await client.license.attachLicenseTerms(request); + setLoadings((prev) => ({ ...prev, attachLicenseTerms: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, attachLicenseTerms: e.message })); + setLoadings((prev) => ({ ...prev, attachLicenseTerms: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Mints license tokens for the license terms attached to an IP. + * The license tokens are minted to the receiver. + * The license terms must be attached to the IP before calling this function. + * But it can mint license token of default license terms without attaching the default license terms, + * since it is attached to all IPs by default. + * IP owners can mint license tokens for their IPs for arbitrary license terms + * without attaching the license terms to IP. + * It might require the caller pay the minting fee, depending on the license terms or configured by the iP owner. + * The minting fee is paid in the minting fee token specified in the license terms or configured by the IP owner. + * IP owners can configure the minting fee of their IPs or + * configure the minting fee module to determine the minting fee. + * @param request - The request object that contains all data needed to mint license tokens. + * @param request.licensorIpId The licensor IP ID. + * @param request.licenseTemplate The address of the license template. + * @param request.licenseTermsId The ID of the license terms within the license template. + * @param request.amount The amount of license tokens to mint. + * @param request.receiver The address of the receiver. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash and optional license token IDs if waitForTxn is set to true. + * @emits LicenseTokensMinted (msg.sender, licensorIpId, licenseTemplate, licenseTermsId, amount, receiver, startLicenseTokenId); + */ + const mintLicenseTokens = async ( + request: MintLicenseTokensRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, mintLicenseTokens: true })); + setErrors((prev) => ({ ...prev, mintLicenseTokens: null })); + const response = await client.license.mintLicenseTokens(request); + setLoadings((prev) => ({ ...prev, mintLicenseTokens: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, mintLicenseTokens: e.message })); + setLoadings((prev) => ({ ...prev, mintLicenseTokens: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Gets license terms of the given ID. + * @param selectedLicenseTermsId The ID of the license terms. + * @returns A Promise that resolves to an object containing the PILTerms associate with the given ID. + */ + const getLicenseTerms = async ( + selectedLicenseTermsId: LicenseTermsId + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, getLicenseTerms: true })); + setErrors((prev) => ({ ...prev, getLicenseTerms: null })); + const response = await client.license.getLicenseTerms( + selectedLicenseTermsId + ); + setLoadings((prev) => ({ ...prev, getLicenseTerms: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, getLicenseTerms: e.message })); + setLoadings((prev) => ({ ...prev, getLicenseTerms: false })); + } + throw new Error(`unhandled error type`); + } + }; + + return { + loadings, + errors, + registerNonComSocialRemixingPIL, + registerCommercialUsePIL, + registerCommercialRemixPIL, + attachLicenseTerms, + mintLicenseTokens, + getLicenseTerms, + }; +}; +export default useLicense; diff --git a/packages/react-sdk/src/resources/useNftClient.ts b/packages/react-sdk/src/resources/useNftClient.ts new file mode 100644 index 00000000..9a9fb082 --- /dev/null +++ b/packages/react-sdk/src/resources/useNftClient.ts @@ -0,0 +1,55 @@ +import { + CreateNFTCollectionRequest, + CreateNFTCollectionResponse, +} from "@story-protocol/core-sdk"; +import { useState } from "react"; + +import { useStoryContext } from "../StoryProtocolContext"; + +const useNftClient = () => { + const client = useStoryContext(); + const [loadings, setLoadings] = useState>({ + createNFTCollection: false, + }); + const [errors, setErrors] = useState>({ + createNFTCollection: null, + }); + + /** + * Creates a new SPG NFT Collection. + * @param request - The request object containing necessary data to create a SPG NFT Collection. + * @param request.name - The name of the collection. + * @param request.symbol - The symbol of the collection. + * @param request.maxSupply - The maximum supply of the collection. + * @param request.mintFee - The cost to mint a token. + * @param request.mintFeeToken - The token to mint. + * @param request.owner - The owner of the collection. + * @param request.txOptions - Optional transaction options. + * @returns A Promise that resolves to a CreateNFTCollectionResponse containing the transaction hash and collection address. + * @emits CollectionCreated (nftContract); + */ + const createNFTCollection = async ( + request: CreateNFTCollectionRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, createNFTCollection: true })); + setErrors((prev) => ({ ...prev, createNFTCollection: null })); + const response = await client.nftClient.createNFTCollection(request); + setLoadings((prev) => ({ ...prev, createNFTCollection: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, createNFTCollection: e.message })); + setLoadings((prev) => ({ ...prev, createNFTCollection: false })); + } + throw new Error(`unhandled error type`); + } + }; + + return { + loadings, + errors, + createNFTCollection, + }; +}; +export default useNftClient; diff --git a/packages/react-sdk/src/resources/usePermission.ts b/packages/react-sdk/src/resources/usePermission.ts new file mode 100644 index 00000000..31b122a1 --- /dev/null +++ b/packages/react-sdk/src/resources/usePermission.ts @@ -0,0 +1,223 @@ +import { + SetPermissionsRequest, + SetPermissionsResponse, + CreateSetPermissionSignatureRequest, + SetAllPermissionsRequest, + SetBatchPermissionsRequest, + CreateBatchPermissionSignatureRequest, +} from "@story-protocol/core-sdk"; +import { useState } from "react"; + +import { useStoryContext } from "../StoryProtocolContext"; + +const usePermission = () => { + const client = useStoryContext(); + const [loadings, setLoadings] = useState>({ + setPermission: false, + createSetPermissionSignature: false, + setAllPermissions: false, + setBatchPermissions: false, + createBatchPermissionSignature: false, + }); + const [errors, setErrors] = useState>({ + setPermission: null, + createSetPermissionSignature: null, + setAllPermissions: null, + setBatchPermissions: null, + createBatchPermissionSignature: null, + }); + + /** + * Sets the permission for a specific function call + * Each policy is represented as a mapping from an IP account address to a signer address to a recipient + * address to a function selector to a permission level. The permission level can be 0 (ABSTAIN), 1 (ALLOW), or + * 2 (DENY). + * By default, all policies are set to 0 (ABSTAIN), which means that the permission is not set. + * The owner of ipAccount by default has all permission. + * address(0) => wildcard + * bytes4(0) => wildcard + * Specific permission overrides wildcard permission. + * @param request - The request object containing necessary data to set `permission`. + * @param request.ipId The IP ID that grants the permission for `signer`. + * @param request.signer The address that can call `to` on behalf of the `ipAccount`. + * @param request.to The address that can be called by the `signer` (currently only modules can be `to`). + * @param request.permission The new permission level. + * @param request.func [Optional] The function selector string of `to` that can be called by the `signer` on behalf of the `ipAccount`. Be default, it allows all functions. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash. + * @emits PermissionSet (ipAccountOwner, ipAccount, signer, to, func, permission) + */ + const setPermission = async ( + request: SetPermissionsRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, setPermission: true })); + setErrors((prev) => ({ ...prev, setPermission: null })); + const response = await client.permission.setPermission(request); + setLoadings((prev) => ({ ...prev, setPermission: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, setPermission: e.message })); + setLoadings((prev) => ({ ...prev, setPermission: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Specific permission overrides wildcard permission with signature. + * @param request - The request object containing necessary data to set permissions. + * @param request.ipId The IP ID that grants the permission for `signer` + * @param request.signer The address that can call `to` on behalf of the `ipAccount` + * @param request.to The address that can be called by the `signer` (currently only modules can be `to`) + * @param request.permission The new permission level. + * @param request.func [Optional] The function selector string of `to` that can be called by the `signer` on behalf of the `ipAccount`. Be default, it allows all functions. + * @param request.deadline [Optional] The deadline for the signature in milliseconds, default is 1000ms. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash. + * @emits PermissionSet (ipAccountOwner, ipAccount, signer, to, func, permission) + */ + const createSetPermissionSignature = async ( + request: CreateSetPermissionSignatureRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, createSetPermissionSignature: true })); + setErrors((prev) => ({ ...prev, createSetPermissionSignature: null })); + const response = await client.permission.createSetPermissionSignature( + request + ); + setLoadings((prev) => ({ ...prev, createSetPermissionSignature: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ + ...prev, + createSetPermissionSignature: e.message, + })); + setLoadings((prev) => ({ + ...prev, + createSetPermissionSignature: false, + })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Sets permission to a signer for all functions across all modules. + * @param request - The request object containing necessary data to set all permissions. + * @param request.ipId The IP ID that grants the permission for `signer` + * @param request.signer The address of the signer receiving the permissions. + * @param request.permission The new permission. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash + * @emits PermissionSet (ipAccountOwner, ipAccount, signer, to, func, permission) + */ + const setAllPermissions = async ( + request: SetAllPermissionsRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, setAllPermissions: true })); + setErrors((prev) => ({ ...prev, setAllPermissions: null })); + const response = await client.permission.setAllPermissions(request); + setLoadings((prev) => ({ ...prev, setAllPermissions: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, setAllPermissions: e.message })); + setLoadings((prev) => ({ ...prev, setAllPermissions: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Sets a batch of permissions in a single transaction. + * @param request - The request object containing necessary data to set all permissions. + * @param {Array} request.permissions - An array of `Permission` structure, each representing the permission to be set. + * @param request.permissions[].ipId The IP ID that grants the permission for `signer`. + * @param request.permissions[].signer The address that can call `to` on behalf of the `ipAccount`. + * @param request.permissions[].to The address that can be called by the `signer` (currently only modules can be `to`). + * @param request.permissions[].permission The new permission level. + * @param request.permissions[].func [Optional] The function selector string of `to` that can be called by the `signer` on behalf of the `ipAccount`. Be default, it allows all functions. + * @param request.deadline [Optional] The deadline for the signature in milliseconds, default is 1000ms. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash + * @emits PermissionSet (ipAccountOwner, ipAccount, signer, to, func, permission) + */ + const setBatchPermissions = async ( + request: SetBatchPermissionsRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, setBatchPermissions: true })); + setErrors((prev) => ({ ...prev, setBatchPermissions: null })); + const response = await client.permission.setBatchPermissions(request); + setLoadings((prev) => ({ ...prev, setBatchPermissions: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, setBatchPermissions: e.message })); + setLoadings((prev) => ({ ...prev, setBatchPermissions: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Sets a batch of permissions in a single transaction with signature. + * @param request - The request object containing necessary data to set permissions. + * @param request.ipId The IP ID that grants the permission for `signer` + * @param {Array} request.permissions - An array of `Permission` structure, each representing the permission to be set. + * @param request.permissions[].ipId The IP ID that grants the permission for `signer`. + * @param request.permissions[].signer The address that can call `to` on behalf of the `ipAccount`. + * @param request.permissions[].to The address that can be called by the `signer` (currently only modules can be `to`). + * @param request.permissions[].permission The new permission level. + * @param request.permissions[].func [Optional] The function selector string of `to` that can be called by the `signer` on behalf of the `ipAccount`. Be default, it allows all functions. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash. + * @emits PermissionSet (ipAccountOwner, ipAccount, signer, to, func, permission) + */ + const createBatchPermissionSignature = async ( + request: CreateBatchPermissionSignatureRequest + ): Promise => { + try { + setLoadings((prev) => ({ + ...prev, + createBatchPermissionSignature: true, + })); + setErrors((prev) => ({ ...prev, createBatchPermissionSignature: null })); + const response = await client.permission.createBatchPermissionSignature( + request + ); + setLoadings((prev) => ({ + ...prev, + createBatchPermissionSignature: false, + })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ + ...prev, + createBatchPermissionSignature: e.message, + })); + setLoadings((prev) => ({ + ...prev, + createBatchPermissionSignature: false, + })); + } + throw new Error(`unhandled error type`); + } + }; + + return { + loadings, + errors, + setPermission, + createSetPermissionSignature, + setAllPermissions, + setBatchPermissions, + createBatchPermissionSignature, + }; +}; +export default usePermission; diff --git a/packages/react-sdk/src/resources/useRoyalty.ts b/packages/react-sdk/src/resources/useRoyalty.ts new file mode 100644 index 00000000..4a7945e6 --- /dev/null +++ b/packages/react-sdk/src/resources/useRoyalty.ts @@ -0,0 +1,211 @@ +import { + CollectRoyaltyTokensRequest, + CollectRoyaltyTokensResponse, + PayRoyaltyOnBehalfRequest, + PayRoyaltyOnBehalfResponse, + ClaimableRevenueRequest, + ClaimableRevenueResponse, + ClaimRevenueRequest, + ClaimRevenueResponse, + SnapshotRequest, + SnapshotResponse, +} from "@story-protocol/core-sdk"; +import { Hex, Address } from "viem"; +import { useState } from "react"; + +import { useStoryContext } from "../StoryProtocolContext"; + +const useRoyalty = () => { + const client = useStoryContext(); + const [loadings, setLoadings] = useState>({ + collectRoyaltyTokens: false, + payRoyaltyOnBehalf: false, + claimableRevenue: false, + claimRevenue: false, + snapshot: false, + getRoyaltyVaultAddress: false, + }); + const [errors, setErrors] = useState>({ + collectRoyaltyTokens: null, + payRoyaltyOnBehalf: null, + claimableRevenue: null, + claimRevenue: null, + snapshot: null, + getRoyaltyVaultAddress: null, + }); + + /** + * Allows ancestors to claim the royalty tokens and any accrued revenue tokens + * @param request - The request object that contains all data needed to collect royalty tokens. + * @param request.parentIpId The ip id of the ancestor to whom the royalty tokens belong to. + * @param request.royaltyVaultIpId The id of the royalty vault. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash and optional the amount of royalty tokens collected if waitForTxn is set to true. + * @emits RoyaltyTokensCollected (ancestorIpId, royaltyTokensCollected) + */ + const collectRoyaltyTokens = async ( + request: CollectRoyaltyTokensRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: true })); + setErrors((prev) => ({ ...prev, collectRoyaltyTokens: null })); + const response = await client.royalty.collectRoyaltyTokens(request); + setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, collectRoyaltyTokens: e.message })); + setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Allows the function caller to pay royalties to the receiver IP asset on behalf of the payer IP asset. + * @param request - The request object that contains all data needed to pay royalty on behalf. + * @param request.receiverIpId The ipId that receives the royalties. + * @param request.payerIpId The ID of the IP asset that pays the royalties. + * @param request.token The token to use to pay the royalties. + * @param request.amount The amount to pay. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash. + */ + const payRoyaltyOnBehalf = async ( + request: PayRoyaltyOnBehalfRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: true })); + setErrors((prev) => ({ ...prev, payRoyaltyOnBehalf: null })); + const response = await client.royalty.payRoyaltyOnBehalf(request); + setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, payRoyaltyOnBehalf: e.message })); + setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Calculates the amount of revenue token claimable by a token holder at certain snapshot. + * @param request - The request object that contains all data needed to claim Revenue. + * @param request.royaltyVaultIpId The id of the royalty vault. + * @param request.account The address of the token holder. + * @param request.snapshotId The snapshot id. + * @param request.token The revenue token to claim. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that contains the amount of revenue token claimable + */ + const claimableRevenue = async ( + request: ClaimableRevenueRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, claimableRevenue: true })); + setErrors((prev) => ({ ...prev, claimableRevenue: null })); + const response = await client.royalty.claimableRevenue(request); + setLoadings((prev) => ({ ...prev, claimableRevenue: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, claimableRevenue: e.message })); + setLoadings((prev) => ({ ...prev, claimableRevenue: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Allows token holders to claim by a list of snapshot ids based on the token balance at certain snapshot + * @param request - The request object that contains all data needed to claim revenue. + * @param request.snapshotIds The list of snapshot ids. + * @param request.royaltyVaultIpId The id of the royalty vault. + * @param request.token The revenue token to claim. + * @param request.account [Optional] The ipId to send. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash and optional claimableToken if waitForTxn is set to true. + * @emits RevenueTokenClaimed (claimer, token, amount). + */ + const claimRevenue = async ( + request: ClaimRevenueRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, claimRevenue: true })); + setErrors((prev) => ({ ...prev, claimRevenue: null })); + const response = await client.royalty.claimRevenue(request); + setLoadings((prev) => ({ ...prev, claimRevenue: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, claimRevenue: e.message })); + setLoadings((prev) => ({ ...prev, claimRevenue: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Snapshots the claimable revenue and royalty token amounts. + * @param request - The request object that contains all data needed to snapshot. + * @param request.royaltyVaultIpId The id of the royalty vault. + * @param request.txOptions [Optional] The transaction options. + * @returns A Promise that resolves to an object containing the transaction hash and optional snapshotId if waitForTxn is set to true. + * @emits SnapshotCompleted (snapshotId, snapshotTimestamp, unclaimedTokens). + */ + const snapshot = async ( + request: SnapshotRequest + ): Promise => { + try { + setLoadings((prev) => ({ ...prev, snapshot: true })); + setErrors((prev) => ({ ...prev, snapshot: null })); + const response = await client.royalty.snapshot(request); + setLoadings((prev) => ({ ...prev, snapshot: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, snapshot: e.message })); + setLoadings((prev) => ({ ...prev, snapshot: false })); + } + throw new Error(`unhandled error type`); + } + }; + + /** + * Get the royalty vault proxy address of given royaltyVaultIpId. + * @param royaltyVaultIpId the id of the royalty vault. + * @returns A Promise that resolves to an object containing the royalty vault address. + */ + const getRoyaltyVaultAddress = async ( + royaltyVaultIpId: Hex + ): Promise
=> { + try { + setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: true })); + setErrors((prev) => ({ ...prev, getRoyaltyVaultAddress: null })); + const response = await client.royalty.getRoyaltyVaultAddress( + royaltyVaultIpId + ); + setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: false })); + return response; + } catch (e) { + if (e instanceof Error) { + setErrors((prev) => ({ ...prev, getRoyaltyVaultAddress: e.message })); + setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: false })); + } + throw new Error(`unhandled error type`); + } + }; + + return { + loadings, + errors, + collectRoyaltyTokens, + payRoyaltyOnBehalf, + claimableRevenue, + claimRevenue, + snapshot, + getRoyaltyVaultAddress, + }; +}; +export default useRoyalty; diff --git a/packages/react-sdk/tsconfig.json b/packages/react-sdk/tsconfig.json new file mode 100644 index 00000000..e2c07b36 --- /dev/null +++ b/packages/react-sdk/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@story-protocol/tsconfig/base.json", + "compilerOptions": { + "resolveJsonModule": true, + "module": "ESNext", + "lib": ["dom", "es2015"], + "jsx": "react-jsx" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 87e4a676..2fc77308 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,6 +137,52 @@ importers: specifier: ^3.0.0 version: 3.2.5 + packages/react-sdk: + dependencies: + '@story-protocol/core-sdk': + specifier: 1.0.0-rc.14 + version: 1.0.0-rc.14(typescript@5.4.5) + '@types/react': + specifier: ^18.3.3 + version: 18.3.3 + react: + specifier: ^18.3.1 + version: 18.3.1 + viem: + specifier: ^2.8.12 + version: 2.9.16(typescript@5.4.5)(zod@3.22.4) + devDependencies: + '@babel/preset-env': + specifier: ^7.22.20 + version: 7.24.4(@babel/core@7.24.4) + '@babel/preset-react': + specifier: ^7.24.7 + version: 7.24.7(@babel/core@7.24.4) + '@babel/preset-typescript': + specifier: ^7.23.0 + version: 7.24.1(@babel/core@7.24.4) + '@preconstruct/cli': + specifier: ^2.8.1 + version: 2.8.3 + '@story-protocol/eslint-config': + specifier: workspace:* + version: link:../eslint-config-story + '@story-protocol/prettier-config': + specifier: workspace:* + version: link:../prettier-config + '@story-protocol/tsconfig': + specifier: workspace:* + version: link:../tsconfig + ejs: + specifier: ^3.1.10 + version: 3.1.10 + ts-node: + specifier: ^10.9.1 + version: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) + typescript: + specifier: ^5.4.5 + version: 5.4.5 + packages/tsconfig: {} packages/wagmi-generator: @@ -150,10 +196,10 @@ importers: version: 0.0.0-canary-20240313013119(typescript@5.4.5) '@wagmi/connectors': specifier: ^4.1.14 - version: 4.1.25(@wagmi/core@2.6.16)(react-native@0.73.6)(react@18.2.0)(typescript@5.4.5)(viem@2.9.16) + version: 4.1.25(@wagmi/core@2.6.16)(react-native@0.73.6)(react@18.3.1)(typescript@5.4.5)(viem@2.9.16) '@wagmi/core': specifier: ^2.6.5 - version: 2.6.16(react@18.2.0)(typescript@5.4.5)(viem@2.9.16) + version: 2.6.16(react@18.3.1)(typescript@5.4.5)(viem@2.9.16) abitype: specifier: ^1.0.2 version: 1.0.2(typescript@5.4.5) @@ -168,7 +214,7 @@ importers: version: 2.9.16(typescript@5.4.5)(zod@3.22.4) wagmi: specifier: ^2.5.7 - version: 2.5.19(@tanstack/react-query@5.29.2)(react-native@0.73.6)(react@18.2.0)(typescript@5.4.5)(viem@2.9.16) + version: 2.5.19(@tanstack/react-query@5.29.2)(react-native@0.73.6)(react@18.3.1)(typescript@5.4.5)(viem@2.9.16) packages: @@ -195,6 +241,14 @@ packages: picocolors: 1.0.0 dev: true + /@babel/code-frame@7.24.7: + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.0 + dev: true + /@babel/compat-data@7.24.4: resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} engines: {node: '>=6.9.0'} @@ -233,6 +287,16 @@ packages: jsesc: 2.5.2 dev: true + /@babel/generator@7.24.7: + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.7 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + dev: true + /@babel/helper-annotate-as-pure@7.22.5: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} @@ -240,6 +304,13 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/helper-annotate-as-pure@7.24.7: + resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.7 + dev: true + /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} engines: {node: '>=6.9.0'} @@ -308,6 +379,13 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-environment-visitor@7.24.7: + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.7 + dev: true + /@babel/helper-function-name@7.23.0: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} @@ -316,6 +394,14 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/helper-function-name@7.24.7: + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + dev: true + /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} @@ -323,6 +409,13 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/helper-hoist-variables@7.24.7: + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.7 + dev: true + /@babel/helper-member-expression-to-functions@7.23.0: resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} @@ -337,6 +430,16 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/helper-module-imports@7.24.7: + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.4): resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} engines: {node: '>=6.9.0'} @@ -363,6 +466,11 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-plugin-utils@7.24.7: + resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.4): resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} @@ -408,21 +516,43 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/helper-split-export-declaration@7.24.7: + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.7 + dev: true + /@babel/helper-string-parser@7.24.1: resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-string-parser@7.24.7: + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-identifier@7.24.7: + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-option@7.23.5: resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-option@7.24.7: + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-wrap-function@7.22.20: resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==} engines: {node: '>=6.9.0'} @@ -453,6 +583,16 @@ packages: picocolors: 1.0.0 dev: true + /@babel/highlight@7.24.7: + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.0 + dev: true + /@babel/parser@7.24.4: resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==} engines: {node: '>=6.0.0'} @@ -461,6 +601,14 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/parser@7.24.7: + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.7 + dev: true + /@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.4(@babel/core@7.24.4): resolution: {integrity: sha512-qpl6vOOEEzTLLcsuqYYo8yDtrTocmu2xkGvgNebvPjT9DTtfFYGmgDqY+rBYXNlqL4s9qLDn6xkrJv4RxAPiTA==} engines: {node: '>=6.9.0'} @@ -513,8 +661,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.4) '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.4) dev: true @@ -528,7 +676,7 @@ packages: dependencies: '@babel/core': 7.24.4 '@babel/helper-create-class-features-plugin': 7.24.4(@babel/core@7.24.4) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-proposal-export-default-from@7.24.1(@babel/core@7.24.4): @@ -538,7 +686,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-export-default-from': 7.24.1(@babel/core@7.24.4) dev: true @@ -550,7 +698,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.4) dev: true @@ -562,7 +710,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.4) dev: true @@ -576,7 +724,7 @@ packages: '@babel/compat-data': 7.24.4 '@babel/core': 7.24.4 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.4) '@babel/plugin-transform-parameters': 7.24.1(@babel/core@7.24.4) dev: true @@ -589,7 +737,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.4) dev: true @@ -601,7 +749,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.4) dev: true @@ -659,7 +807,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.4): @@ -678,7 +826,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-syntax-import-assertions@7.24.1(@babel/core@7.24.4): @@ -729,6 +877,16 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true + /@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.4): + resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.4): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -1001,7 +1159,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 '@babel/plugin-syntax-flow': 7.24.1(@babel/core@7.24.4) dev: true @@ -1251,14 +1409,26 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true - /@babel/plugin-transform-react-display-name@7.24.1(@babel/core@7.24.4): - resolution: {integrity: sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==} + /@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.24.4): + resolution: {integrity: sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + + /@babel/plugin-transform-react-jsx-development@7.24.7(@babel/core@7.24.4): + resolution: {integrity: sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.4) + transitivePeerDependencies: + - supports-color dev: true /@babel/plugin-transform-react-jsx-self@7.24.1(@babel/core@7.24.4): @@ -1268,7 +1438,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-transform-react-jsx-source@7.24.1(@babel/core@7.24.4): @@ -1278,21 +1448,34 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.7 dev: true - /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.24.4): - resolution: {integrity: sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==} + /@babel/plugin-transform-react-jsx@7.24.7(@babel/core@7.24.4): + resolution: {integrity: sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-module-imports': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.4) - '@babel/types': 7.24.0 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.4) + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-react-pure-annotations@7.24.7(@babel/core@7.24.4): + resolution: {integrity: sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 dev: true /@babel/plugin-transform-regenerator@7.24.1(@babel/core@7.24.4): @@ -1323,8 +1506,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-module-imports': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.7 babel-plugin-polyfill-corejs2: 0.4.10(@babel/core@7.24.4) babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.4) babel-plugin-polyfill-regenerator: 0.6.1(@babel/core@7.24.4) @@ -1539,8 +1722,8 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.24.4 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/helper-validator-option': 7.23.5 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 '@babel/plugin-transform-flow-strip-types': 7.24.1(@babel/core@7.24.4) dev: true @@ -1555,6 +1738,23 @@ packages: esutils: 2.0.3 dev: true + /@babel/preset-react@7.24.7(@babel/core@7.24.4): + resolution: {integrity: sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.4) + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.4) + '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.24.4) + '@babel/plugin-transform-react-pure-annotations': 7.24.7(@babel/core@7.24.4) + transitivePeerDependencies: + - supports-color + dev: true + /@babel/preset-typescript@7.24.1(@babel/core@7.24.4): resolution: {integrity: sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==} engines: {node: '>=6.9.0'} @@ -1603,6 +1803,15 @@ packages: '@babel/types': 7.24.0 dev: true + /@babel/template@7.24.7: + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + dev: true + /@babel/traverse@7.24.1: resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==} engines: {node: '>=6.9.0'} @@ -1621,6 +1830,24 @@ packages: - supports-color dev: true + /@babel/traverse@7.24.7: + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + debug: 4.3.4(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types@7.24.0: resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} engines: {node: '>=6.9.0'} @@ -1630,6 +1857,15 @@ packages: to-fast-properties: 2.0.0 dev: true + /@babel/types@7.24.7: + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + dev: true + /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true @@ -1880,7 +2116,7 @@ packages: resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} dev: true - /@emotion/react@11.11.4(react@18.2.0): + /@emotion/react@11.11.4(react@18.3.1): resolution: {integrity: sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==} peerDependencies: '@types/react': '*' @@ -1893,11 +2129,11 @@ packages: '@emotion/babel-plugin': 11.11.0 '@emotion/cache': 11.11.0 '@emotion/serialize': 1.1.4 - '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) + '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.3.1) '@emotion/utils': 1.2.1 '@emotion/weak-memoize': 0.3.1 hoist-non-react-statics: 3.3.2 - react: 18.2.0 + react: 18.3.1 dev: true /@emotion/serialize@1.1.4: @@ -1914,7 +2150,7 @@ packages: resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==} dev: true - /@emotion/styled@11.11.5(@emotion/react@11.11.4)(react@18.2.0): + /@emotion/styled@11.11.5(@emotion/react@11.11.4)(react@18.3.1): resolution: {integrity: sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==} peerDependencies: '@emotion/react': ^11.0.0-rc.0 @@ -1927,23 +2163,23 @@ packages: '@babel/runtime': 7.24.4 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.2 - '@emotion/react': 11.11.4(react@18.2.0) + '@emotion/react': 11.11.4(react@18.3.1) '@emotion/serialize': 1.1.4 - '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) + '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.3.1) '@emotion/utils': 1.2.1 - react: 18.2.0 + react: 18.3.1 dev: true /@emotion/unitless@0.8.1: resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} dev: true - /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0): + /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.3.1): resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} peerDependencies: react: '>=16.8.0' dependencies: - react: 18.2.0 + react: 18.3.1 dev: true /@emotion/utils@1.2.1: @@ -2499,19 +2735,19 @@ packages: /@metamask/sdk-install-modal-web@0.14.1(react-native@0.73.6): resolution: {integrity: sha512-emT8HKbnfVwGhPxyUfMja6DWzvtJvDEBQxqCVx93H0HsyrrOzOC43iGCAosslw6o5h7gOfRKLqWmK8V7jQAS2Q==} dependencies: - '@emotion/react': 11.11.4(react@18.2.0) - '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(react@18.2.0) + '@emotion/react': 11.11.4(react@18.3.1) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(react@18.3.1) i18next: 22.5.1 qr-code-styling: 1.6.0-rc.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-i18next: 13.5.0(i18next@22.5.1)(react-dom@18.2.0)(react-native@0.73.6)(react@18.2.0) + react: 18.3.1 + react-dom: 18.2.0(react@18.3.1) + react-i18next: 13.5.0(i18next@22.5.1)(react-dom@18.2.0)(react-native@0.73.6)(react@18.3.1) transitivePeerDependencies: - '@types/react' - react-native dev: true - /@metamask/sdk@0.14.3(react-native@0.73.6)(react@18.2.0): + /@metamask/sdk@0.14.3(react-native@0.73.6)(react@18.3.1): resolution: {integrity: sha512-BYLs//nY2wioVSih78gOQI6sLIYY3vWkwVqXGYUgkBV+bi49bv+9S0m+hZ2cwiRaxfMYtKs0KvhAQ8weiYwDrg==} peerDependencies: react: ^18.2.0 @@ -2540,10 +2776,10 @@ packages: obj-multiplex: 1.0.0 pump: 3.0.0 qrcode-terminal-nooctal: 0.12.1 - react: 18.2.0 - react-i18next: 13.5.0(i18next@22.5.1)(react-dom@18.2.0)(react-native@0.73.6)(react@18.2.0) - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) - react-native-webview: 11.26.1(react-native@0.73.6)(react@18.2.0) + react: 18.3.1 + react-i18next: 13.5.0(i18next@22.5.1)(react-dom@18.2.0)(react-native@0.73.6)(react@18.3.1) + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.3.1) + react-native-webview: 11.26.1(react-native@0.73.6)(react@18.3.1) readable-stream: 2.3.8 rollup-plugin-visualizer: 5.12.0 socket.io-client: 4.7.5(bufferutil@4.0.8)(utf-8-validate@6.0.3) @@ -2682,7 +2918,6 @@ packages: /@noble/hashes@1.3.3: resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} engines: {node: '>= 16'} - dev: true /@noble/hashes@1.4.0: resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} @@ -2911,7 +3146,7 @@ packages: react-native: ^0.0.0-0 || >=0.60 <1.0 dependencies: merge-options: 3.0.4 - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.3.1) dev: true /@react-native-community/cli-clean@12.3.6: @@ -3130,8 +3365,8 @@ packages: '@babel/plugin-transform-parameters': 7.24.1(@babel/core@7.24.4) '@babel/plugin-transform-private-methods': 7.24.1(@babel/core@7.24.4) '@babel/plugin-transform-private-property-in-object': 7.24.1(@babel/core@7.24.4) - '@babel/plugin-transform-react-display-name': 7.24.1(@babel/core@7.24.4) - '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.4) + '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.4) + '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.4) '@babel/plugin-transform-react-jsx-self': 7.24.1(@babel/core@7.24.4) '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.4) '@babel/plugin-transform-runtime': 7.24.3(@babel/core@7.24.4) @@ -3140,7 +3375,7 @@ packages: '@babel/plugin-transform-sticky-regex': 7.24.1(@babel/core@7.24.4) '@babel/plugin-transform-typescript': 7.24.4(@babel/core@7.24.4) '@babel/plugin-transform-unicode-regex': 7.24.1(@babel/core@7.24.4) - '@babel/template': 7.24.0 + '@babel/template': 7.24.7 '@react-native/babel-plugin-codegen': 0.73.4(@babel/preset-env@7.24.4) babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.24.4) react-refresh: 0.14.0 @@ -3155,7 +3390,7 @@ packages: peerDependencies: '@babel/preset-env': ^7.1.6 dependencies: - '@babel/parser': 7.24.4 + '@babel/parser': 7.24.7 '@babel/preset-env': 7.24.4(@babel/core@7.24.4) flow-parser: 0.206.0 glob: 7.2.3 @@ -3255,7 +3490,7 @@ packages: dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.3.1) dev: true /@rollup/plugin-alias@3.1.9(rollup@2.79.1): @@ -3366,7 +3601,7 @@ packages: resolution: {integrity: sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==} dependencies: '@noble/curves': 1.2.0 - '@noble/hashes': 1.3.2 + '@noble/hashes': 1.3.3 '@scure/base': 1.1.6 /@scure/bip32@1.3.3: @@ -3380,7 +3615,7 @@ packages: /@scure/bip39@1.2.1: resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} dependencies: - '@noble/hashes': 1.3.2 + '@noble/hashes': 1.3.3 '@scure/base': 1.1.6 /@scure/bip39@1.2.2: @@ -3564,17 +3799,32 @@ packages: '@stablelib/wipe': 1.0.1 dev: true + /@story-protocol/core-sdk@1.0.0-rc.14(typescript@5.4.5): + resolution: {integrity: sha512-7/CLt12G/HHdJazWTg5wDjYUb5oxbVJW2EUEmelP32rhVAHVbR+RhqgDtuM+4ISX+Eg1nbUHGG/dbhwbgBV4+A==} + dependencies: + abitype: 0.10.3(typescript@5.4.5) + axios: 1.6.8 + dotenv: 16.4.5 + viem: 2.9.16(typescript@5.4.5)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - debug + - typescript + - utf-8-validate + - zod + dev: false + /@tanstack/query-core@5.29.0: resolution: {integrity: sha512-WgPTRs58hm9CMzEr5jpISe8HXa3qKQ8CxewdYZeVnA54JrPY9B1CZiwsCoLpLkf0dGRZq+LcX5OiJb0bEsOFww==} dev: true - /@tanstack/react-query@5.29.2(react@18.2.0): + /@tanstack/react-query@5.29.2(react@18.3.1): resolution: {integrity: sha512-nyuWILR4u7H5moLGSiifLh8kIqQDLNOHGuSz0rcp+J75fNc8aQLyr5+I2JCHU3n+nJrTTW1ssgAD8HiKD7IFBQ==} peerDependencies: react: ^18.0.0 dependencies: '@tanstack/query-core': 5.29.0 - react: 18.2.0 + react: 18.3.1 dev: true /@tsconfig/node10@1.0.11: @@ -3700,6 +3950,17 @@ packages: resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} dev: true + /@types/prop-types@15.7.12: + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + dev: false + + /@types/react@18.3.3: + resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + dependencies: + '@types/prop-types': 15.7.12 + csstype: 3.1.3 + dev: false + /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: @@ -3918,7 +4179,7 @@ packages: - utf-8-validate dev: true - /@wagmi/connectors@4.1.25(@wagmi/core@2.6.16)(react-native@0.73.6)(react@18.2.0)(typescript@5.4.5)(viem@2.9.16): + /@wagmi/connectors@4.1.25(@wagmi/core@2.6.16)(react-native@0.73.6)(react@18.3.1)(typescript@5.4.5)(viem@2.9.16): resolution: {integrity: sha512-4Tot1Gtiv7uhiUAxZ9On37aai35l5S0sV7N2yQSNgzqXe55bAlI0cqyBAIJRvyKwOe1+hzKfoFqYQSaoCgj5Lg==} peerDependencies: '@wagmi/core': 2.6.16 @@ -3929,12 +4190,12 @@ packages: optional: true dependencies: '@coinbase/wallet-sdk': 3.9.1 - '@metamask/sdk': 0.14.3(react-native@0.73.6)(react@18.2.0) + '@metamask/sdk': 0.14.3(react-native@0.73.6)(react@18.3.1) '@safe-global/safe-apps-provider': 0.18.1(typescript@5.4.5) '@safe-global/safe-apps-sdk': 8.1.0(typescript@5.4.5) - '@wagmi/core': 2.6.16(react@18.2.0)(typescript@5.4.5)(viem@2.9.16) - '@walletconnect/ethereum-provider': 2.11.2(react@18.2.0) - '@walletconnect/modal': 2.6.2(react@18.2.0) + '@wagmi/core': 2.6.16(react@18.3.1)(typescript@5.4.5)(viem@2.9.16) + '@walletconnect/ethereum-provider': 2.11.2(react@18.3.1) + '@walletconnect/modal': 2.6.2(react@18.3.1) typescript: 5.4.5 viem: 2.9.16(typescript@5.4.5)(zod@3.22.4) transitivePeerDependencies: @@ -3964,7 +4225,7 @@ packages: - zod dev: true - /@wagmi/core@2.6.16(react@18.2.0)(typescript@5.4.5)(viem@2.9.16): + /@wagmi/core@2.6.16(react@18.3.1)(typescript@5.4.5)(viem@2.9.16): resolution: {integrity: sha512-95r+2CCf4Yz4CWG7UZMALIcGSUfpr9YbZ2HOqmz6gJEBaW9Cf9xUEZj2MXOHZIP+Ri/3CZJtbBEclDot4enZWA==} peerDependencies: '@tanstack/query-core': '>=5.0.0' @@ -3980,7 +4241,7 @@ packages: mipd: 0.0.5(typescript@5.4.5) typescript: 5.4.5 viem: 2.9.16(typescript@5.4.5)(zod@3.22.4) - zustand: 4.4.1(react@18.2.0) + zustand: 4.4.1(react@18.3.1) transitivePeerDependencies: - '@types/react' - bufferutil @@ -4036,14 +4297,14 @@ packages: tslib: 1.14.1 dev: true - /@walletconnect/ethereum-provider@2.11.2(react@18.2.0): + /@walletconnect/ethereum-provider@2.11.2(react@18.3.1): resolution: {integrity: sha512-BUDqee0Uy2rCZVkW5Ao3q6Ado/3fePYnFdryVF+YL6bPhj+xQZ5OfKodl+uvs7Rwq++O5wTX2RqOTzpW7+v+Mg==} dependencies: '@walletconnect/jsonrpc-http-connection': 1.0.7 '@walletconnect/jsonrpc-provider': 1.0.13 '@walletconnect/jsonrpc-types': 1.0.3 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/modal': 2.6.2(react@18.2.0) + '@walletconnect/modal': 2.6.2(react@18.3.1) '@walletconnect/sign-client': 2.11.2 '@walletconnect/types': 2.11.2 '@walletconnect/universal-provider': 2.11.2 @@ -4166,19 +4427,19 @@ packages: pino: 7.11.0 dev: true - /@walletconnect/modal-core@2.6.2(react@18.2.0): + /@walletconnect/modal-core@2.6.2(react@18.3.1): resolution: {integrity: sha512-cv8ibvdOJQv2B+nyxP9IIFdxvQznMz8OOr/oR/AaUZym4hjXNL/l1a2UlSQBXrVjo3xxbouMxLb3kBsHoYP2CA==} dependencies: - valtio: 1.11.2(react@18.2.0) + valtio: 1.11.2(react@18.3.1) transitivePeerDependencies: - '@types/react' - react dev: true - /@walletconnect/modal-ui@2.6.2(react@18.2.0): + /@walletconnect/modal-ui@2.6.2(react@18.3.1): resolution: {integrity: sha512-rbdstM1HPGvr7jprQkyPggX7rP4XiCG85ZA+zWBEX0dVQg8PpAgRUqpeub4xQKDgY7pY/xLRXSiCVdWGqvG2HA==} dependencies: - '@walletconnect/modal-core': 2.6.2(react@18.2.0) + '@walletconnect/modal-core': 2.6.2(react@18.3.1) lit: 2.8.0 motion: 10.16.2 qrcode: 1.5.3 @@ -4187,11 +4448,11 @@ packages: - react dev: true - /@walletconnect/modal@2.6.2(react@18.2.0): + /@walletconnect/modal@2.6.2(react@18.3.1): resolution: {integrity: sha512-eFopgKi8AjKf/0U4SemvcYw9zlLpx9njVN8sf6DAkowC2Md0gPU/UNEbH1Wwj407pEKnEds98pKWib1NN1ACoA==} dependencies: - '@walletconnect/modal-core': 2.6.2(react@18.2.0) - '@walletconnect/modal-ui': 2.6.2(react@18.2.0) + '@walletconnect/modal-core': 2.6.2(react@18.3.1) + '@walletconnect/modal-ui': 2.6.2(react@18.3.1) transitivePeerDependencies: - '@types/react' - react @@ -4666,6 +4927,10 @@ packages: tslib: 2.6.2 dev: true + /async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + dev: true + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false @@ -5423,7 +5688,6 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dev: true /csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} @@ -5725,6 +5989,14 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true + /ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.9.1 + dev: true + /electron-to-chromium@1.4.735: resolution: {integrity: sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A==} dev: true @@ -6337,6 +6609,12 @@ packages: dependencies: flat-cache: 3.2.0 + /filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + dependencies: + minimatch: 5.0.1 + dev: true + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -7308,6 +7586,17 @@ packages: istanbul-lib-report: 3.0.1 dev: true + /jake@10.9.1: + resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==} + engines: {node: '>=10'} + hasBin: true + dependencies: + async: 3.2.5 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: true + /jest-environment-node@29.7.0: resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7329,7 +7618,7 @@ packages: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/code-frame': 7.24.2 + '@babel/code-frame': 7.24.7 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -7417,7 +7706,6 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} @@ -7448,7 +7736,7 @@ packages: '@babel/preset-env': ^7.1.6 dependencies: '@babel/core': 7.24.4 - '@babel/parser': 7.24.4 + '@babel/parser': 7.24.7 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.4) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.4) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.4) @@ -7766,7 +8054,6 @@ packages: hasBin: true dependencies: js-tokens: 4.0.0 - dev: true /loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} @@ -8002,8 +8289,8 @@ packages: resolution: {integrity: sha512-+OVISBkPNxjD4eEKhblRpBf463nTMk3KMEeYS8Z4xM/z3qujGJGSsWUGRtH27+c6zElaSGtZFiDMshEb8mMKQg==} engines: {node: '>=18'} dependencies: - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 invariant: 2.2.4 metro-symbolicate: 0.80.8 nullthrows: 1.1.1 @@ -8034,9 +8321,9 @@ packages: engines: {node: '>=18'} dependencies: '@babel/core': 7.24.4 - '@babel/generator': 7.24.4 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 + '@babel/generator': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color @@ -8047,9 +8334,9 @@ packages: engines: {node: '>=18'} dependencies: '@babel/core': 7.24.4 - '@babel/generator': 7.24.4 - '@babel/parser': 7.24.4 - '@babel/types': 7.24.0 + '@babel/generator': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 metro: 0.80.8 metro-babel-transformer: 0.80.8 metro-cache: 0.80.8 @@ -8070,13 +8357,13 @@ packages: engines: {node: '>=18'} hasBin: true dependencies: - '@babel/code-frame': 7.24.2 + '@babel/code-frame': 7.24.7 '@babel/core': 7.24.4 - '@babel/generator': 7.24.4 - '@babel/parser': 7.24.4 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/generator': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -9105,17 +9392,17 @@ packages: - utf-8-validate dev: true - /react-dom@18.2.0(react@18.2.0): + /react-dom@18.2.0(react@18.3.1): resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: react: ^18.2.0 dependencies: loose-envify: 1.4.0 - react: 18.2.0 + react: 18.3.1 scheduler: 0.23.0 dev: true - /react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.2.0)(react-native@0.73.6)(react@18.2.0): + /react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.2.0)(react-native@0.73.6)(react@18.3.1): resolution: {integrity: sha512-CFJ5NDGJ2MUyBohEHxljOq/39NQ972rh1ajnadG9BjTk+UXbHLq4z5DKEbEQBDoIhUmmbuS/fIMJKo6VOax1HA==} peerDependencies: i18next: '>= 23.2.3' @@ -9131,9 +9418,9 @@ packages: '@babel/runtime': 7.24.4 html-parse-stringify: 3.0.1 i18next: 22.5.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) + react: 18.3.1 + react-dom: 18.2.0(react@18.3.1) + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.3.1) dev: true /react-is@16.13.1: @@ -9148,7 +9435,7 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true - /react-native-webview@11.26.1(react-native@0.73.6)(react@18.2.0): + /react-native-webview@11.26.1(react-native@0.73.6)(react@18.3.1): resolution: {integrity: sha512-hC7BkxOpf+z0UKhxFSFTPAM4shQzYmZHoELa6/8a/MspcjEP7ukYKpuSUTLDywQditT8yI9idfcKvfZDKQExGw==} peerDependencies: react: '*' @@ -9156,11 +9443,11 @@ packages: dependencies: escape-string-regexp: 2.0.0 invariant: 2.2.4 - react: 18.2.0 - react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0) + react: 18.3.1 + react-native: 0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.3.1) dev: true - /react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.2.0): + /react-native@0.73.6(@babel/core@7.24.4)(@babel/preset-env@7.24.4)(react@18.3.1): resolution: {integrity: sha512-oqmZe8D2/VolIzSPZw+oUd6j/bEmeRHwsLn1xLA5wllEYsZ5zNuMsDus235ONOnCRwexqof/J3aztyQswSmiaA==} engines: {node: '>=18'} hasBin: true @@ -9196,10 +9483,10 @@ packages: nullthrows: 1.1.1 pretty-format: 26.6.2 promise: 8.3.0 - react: 18.2.0 + react: 18.3.1 react-devtools-core: 4.28.5 react-refresh: 0.14.0 - react-shallow-renderer: 16.15.0(react@18.2.0) + react-shallow-renderer: 16.15.0(react@18.3.1) regenerator-runtime: 0.13.11 scheduler: 0.24.0-canary-efb381bbf-20230505 stacktrace-parser: 0.1.10 @@ -9220,22 +9507,21 @@ packages: engines: {node: '>=0.10.0'} dev: true - /react-shallow-renderer@16.15.0(react@18.2.0): + /react-shallow-renderer@16.15.0(react@18.3.1): resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==} peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 dependencies: object-assign: 4.1.1 - react: 18.2.0 + react: 18.3.1 react-is: 18.2.0 dev: true - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + /react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} dependencies: loose-envify: 1.4.0 - dev: true /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} @@ -10596,12 +10882,12 @@ packages: dependencies: punycode: 2.3.1 - /use-sync-external-store@1.2.0(react@18.2.0): + /use-sync-external-store@1.2.0(react@18.3.1): resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - react: 18.2.0 + react: 18.3.1 dev: true /utf-8-validate@6.0.3: @@ -10665,7 +10951,7 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /valtio@1.11.2(react@18.2.0): + /valtio@1.11.2(react@18.3.1): resolution: {integrity: sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==} engines: {node: '>=12.20.0'} peerDependencies: @@ -10678,8 +10964,8 @@ packages: optional: true dependencies: proxy-compare: 2.5.1 - react: 18.2.0 - use-sync-external-store: 1.2.0(react@18.2.0) + react: 18.3.1 + use-sync-external-store: 1.2.0(react@18.3.1) dev: true /vary@1.1.2: @@ -10741,7 +11027,7 @@ packages: engines: {node: '>=0.10.0'} dev: true - /wagmi@2.5.19(@tanstack/react-query@5.29.2)(react-native@0.73.6)(react@18.2.0)(typescript@5.4.5)(viem@2.9.16): + /wagmi@2.5.19(@tanstack/react-query@5.29.2)(react-native@0.73.6)(react@18.3.1)(typescript@5.4.5)(viem@2.9.16): resolution: {integrity: sha512-fy6s3qTuXpfrrghhoNXuV92yqOqJI7m/9iLIejHxEYxiddVDTR8BVdkt0BuBQZzoXSAutDkyIlJbtFcpX5dfrQ==} peerDependencies: '@tanstack/react-query': '>=5.0.0' @@ -10752,12 +11038,12 @@ packages: typescript: optional: true dependencies: - '@tanstack/react-query': 5.29.2(react@18.2.0) - '@wagmi/connectors': 4.1.25(@wagmi/core@2.6.16)(react-native@0.73.6)(react@18.2.0)(typescript@5.4.5)(viem@2.9.16) - '@wagmi/core': 2.6.16(react@18.2.0)(typescript@5.4.5)(viem@2.9.16) - react: 18.2.0 + '@tanstack/react-query': 5.29.2(react@18.3.1) + '@wagmi/connectors': 4.1.25(@wagmi/core@2.6.16)(react-native@0.73.6)(react@18.3.1)(typescript@5.4.5)(viem@2.9.16) + '@wagmi/core': 2.6.16(react@18.3.1)(typescript@5.4.5)(viem@2.9.16) + react: 18.3.1 typescript: 5.4.5 - use-sync-external-store: 1.2.0(react@18.2.0) + use-sync-external-store: 1.2.0(react@18.3.1) viem: 2.9.16(typescript@5.4.5)(zod@3.22.4) transitivePeerDependencies: - '@azure/app-configuration' @@ -11099,7 +11385,7 @@ packages: /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - /zustand@4.4.1(react@18.2.0): + /zustand@4.4.1(react@18.3.1): resolution: {integrity: sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==} engines: {node: '>=12.7.0'} peerDependencies: @@ -11114,6 +11400,6 @@ packages: react: optional: true dependencies: - react: 18.2.0 - use-sync-external-store: 1.2.0(react@18.2.0) + react: 18.3.1 + use-sync-external-store: 1.2.0(react@18.3.1) dev: true From 2629456873cd9b293d36b49a0f49cffbf13f4ec4 Mon Sep 17 00:00:00 2001 From: Bonnie57 <146059114+bonnie57@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:30:36 +0800 Subject: [PATCH 3/3] Add integration test in react sdk (#214) * Modify calling signTypedData by wallet client * Upload pnpm-lock.yaml * Remove useless code * Set up test env * Modify error handle logic * Modify timeout time * Add util methods and wrapper * Add dispute integration test * Make test serially execute * Add ipAsset integration test * Add ipAccount integration * Add useLicense integration test * Add permission, royalty, nftClient integration test in react sdk * Modify test description * Add nftClient integration test * Move babel config into package.json * Return false when license Terms are already attached to IP Asset --- packages/core-sdk/src/resources/license.ts | 4 +- .../core-sdk/src/types/resources/dispute.ts | 1 - .../core-sdk/test/integration/dispute.test.ts | 6 +- .../test/integration/ipAccount.test.ts | 2 - .../test/integration/nftClient.test.ts | 21 + .../core-sdk/test/integration/utils/util.ts | 8 +- .../test/unit/resources/license.test.ts | 21 +- packages/react-sdk/.env.example | 2 + .../react-sdk/generator/templates/resource.js | 10 +- packages/react-sdk/jest.config.js | 9 + packages/react-sdk/package.json | 32 +- .../react-sdk/src/resources/useDispute.ts | 28 +- .../react-sdk/src/resources/useIpAccount.ts | 28 +- .../react-sdk/src/resources/useIpAsset.ts | 88 +- .../react-sdk/src/resources/useLicense.ts | 76 +- .../react-sdk/src/resources/useNftClient.ts | 10 +- .../react-sdk/src/resources/usePermission.ts | 67 +- .../react-sdk/src/resources/useRoyalty.ts | 55 +- packages/react-sdk/src/util.ts | 7 + .../test/integration/useDispute.test.ts | 69 + .../test/integration/useIpAccount.test.ts | 149 ++ .../test/integration/useIpAsset.test.ts | 327 ++++ .../test/integration/useLicense.test.ts | 144 ++ .../test/integration/useNftClient.test.ts | 29 + .../test/integration/usePermission.test.ts | 160 ++ .../test/integration/useRoyalty.test.ts | 258 ++++ .../test/integration/utils/Wrapper.tsx | 18 + .../react-sdk/test/integration/utils/util.ts | 63 + .../react-sdk/test/jest-environment-jsdom.ts | 55 + packages/react-sdk/test/jest-setup.ts | 1 + pnpm-lock.yaml | 1321 ++++++++++++++++- 31 files changed, 2807 insertions(+), 262 deletions(-) create mode 100644 packages/core-sdk/test/integration/nftClient.test.ts create mode 100644 packages/react-sdk/.env.example create mode 100644 packages/react-sdk/jest.config.js create mode 100644 packages/react-sdk/src/util.ts create mode 100644 packages/react-sdk/test/integration/useDispute.test.ts create mode 100644 packages/react-sdk/test/integration/useIpAccount.test.ts create mode 100644 packages/react-sdk/test/integration/useIpAsset.test.ts create mode 100644 packages/react-sdk/test/integration/useLicense.test.ts create mode 100644 packages/react-sdk/test/integration/useNftClient.test.ts create mode 100644 packages/react-sdk/test/integration/usePermission.test.ts create mode 100644 packages/react-sdk/test/integration/useRoyalty.test.ts create mode 100644 packages/react-sdk/test/integration/utils/Wrapper.tsx create mode 100644 packages/react-sdk/test/integration/utils/util.ts create mode 100644 packages/react-sdk/test/jest-environment-jsdom.ts create mode 100644 packages/react-sdk/test/jest-setup.ts diff --git a/packages/core-sdk/src/resources/license.ts b/packages/core-sdk/src/resources/license.ts index ee35db2e..ae88194b 100644 --- a/packages/core-sdk/src/resources/license.ts +++ b/packages/core-sdk/src/resources/license.ts @@ -186,9 +186,7 @@ export class LicenseClient { licenseTermsId: request.licenseTermsId, }); if (isAttachedLicenseTerms) { - throw new Error( - `License terms id ${request.licenseTermsId} is already attached to the IP with id ${request.ipId}.`, - ); + return { txHash: "", success: false }; } const txHash = await this.licensingModuleClient.attachLicenseTerms({ ipId: request.ipId, diff --git a/packages/core-sdk/src/types/resources/dispute.ts b/packages/core-sdk/src/types/resources/dispute.ts index a650b561..c2000bc3 100644 --- a/packages/core-sdk/src/types/resources/dispute.ts +++ b/packages/core-sdk/src/types/resources/dispute.ts @@ -14,7 +14,6 @@ export type RaiseDisputeRequest = { export type RaiseDisputeResponse = { txHash: string; disputeId?: bigint; - arbitrationPolicy?: Address; }; export type CancelDisputeRequest = { diff --git a/packages/core-sdk/test/integration/dispute.test.ts b/packages/core-sdk/test/integration/dispute.test.ts index 6d837303..7bb2ce56 100644 --- a/packages/core-sdk/test/integration/dispute.test.ts +++ b/packages/core-sdk/test/integration/dispute.test.ts @@ -18,7 +18,7 @@ describe("Dispute Functions", () => { before(async () => { clientA = getStoryClientInSepolia(); - clientB = getStoryClientInSepolia(process.env.SEPOLIA_WALLET_PRIVATE_KEY2 as Address); + clientB = getStoryClientInSepolia(); const mockERC20 = new MockERC20(); await mockERC20.approve(arbitrationPolicyAddress); const tokenId = await getTokenId(); @@ -33,7 +33,7 @@ describe("Dispute Functions", () => { ).ipId!; }); - it("raise a dispute", async () => { + it("should not throw error when raise a dispute", async () => { const raiseDisputeRequest: RaiseDisputeRequest = { targetIpId: ipIdB, arbitrationPolicy: arbitrationPolicyAddress, @@ -50,7 +50,7 @@ describe("Dispute Functions", () => { expect(response.disputeId).to.be.a("bigint"); }); - it("cancel a dispute", async () => { + it("should not throw error when cancel a dispute", async () => { const cancelDispute: CancelDisputeRequest = { disputeId: disputeId, txOptions: { diff --git a/packages/core-sdk/test/integration/ipAccount.test.ts b/packages/core-sdk/test/integration/ipAccount.test.ts index 590e4c9c..5bfd10a8 100644 --- a/packages/core-sdk/test/integration/ipAccount.test.ts +++ b/packages/core-sdk/test/integration/ipAccount.test.ts @@ -14,7 +14,6 @@ import { accessControllerAddress, coreMetadataModuleAddress, } from "../../src/abi/generated"; -import { privateKeyToAccount } from "viem/accounts"; import { getDeadline } from "../../src/utils/sign"; chai.use(chaiAsPromised); @@ -61,7 +60,6 @@ describe("Ip Account functions", () => { }); it("should not throw error when executeWithSig setting permission", async () => { - const account = privateKeyToAccount(process.env.SEPOLIA_WALLET_PRIVATE_KEY as Hex); const state = await client.ipAccount.getIpAccountNonce(ipId); const expectedState = state + 1n; const deadline = getDeadline(60000n); diff --git a/packages/core-sdk/test/integration/nftClient.test.ts b/packages/core-sdk/test/integration/nftClient.test.ts new file mode 100644 index 00000000..cb557014 --- /dev/null +++ b/packages/core-sdk/test/integration/nftClient.test.ts @@ -0,0 +1,21 @@ +import { expect } from "chai"; +import { StoryClient } from "../../src"; +import { getStoryClientInSepolia } from "./utils/util"; + +describe("nftClient Functions", () => { + let client: StoryClient; + before(async () => { + client = getStoryClientInSepolia(); + }); + it("should success when create nft collection", async () => { + const txData = await client.nftClient.createNFTCollection({ + name: "test-collection", + symbol: "TEST", + maxSupply: 100, + txOptions: { + waitForTransaction: true, + }, + }); + expect(txData.nftContract).to.be.a("string").and.not.empty; + }); +}); diff --git a/packages/core-sdk/test/integration/utils/util.ts b/packages/core-sdk/test/integration/utils/util.ts index 9ece9950..414b6f0b 100644 --- a/packages/core-sdk/test/integration/utils/util.ts +++ b/packages/core-sdk/test/integration/utils/util.ts @@ -41,15 +41,11 @@ export const getTokenId = async (nftContract?: Address): Promise { +export const getStoryClientInSepolia = (): StoryClient => { const config: StoryConfig = { chainId: "sepolia", transport: http(RPC), - account: privateKeyToAccount(privateKey || (process.env.SEPOLIA_WALLET_PRIVATE_KEY as Address)), + account: privateKeyToAccount(process.env.SEPOLIA_WALLET_PRIVATE_KEY as Address), }; return StoryClient.newClient(config); }; - -export const getBlockTimestamp = async (): Promise => { - return (await publicClient.getBlock()).timestamp; -}; diff --git a/packages/core-sdk/test/unit/resources/license.test.ts b/packages/core-sdk/test/unit/resources/license.test.ts index 579997c7..5d6edb6d 100644 --- a/packages/core-sdk/test/unit/resources/license.test.ts +++ b/packages/core-sdk/test/unit/resources/license.test.ts @@ -291,23 +291,20 @@ describe("Test LicenseClient", () => { } }); - it("should throw attached error when call attachLicenseTerms given licenseTermsId is already attached", async () => { + it("should return txHash of empty and success of false when call attachLicenseTerms given licenseTermsId is already attached", async () => { sinon.stub(licenseClient.ipAssetRegistryClient, "isRegistered").resolves(true); sinon.stub(licenseClient.piLicenseTemplateReadOnlyClient, "exists").resolves(true); sinon .stub(licenseClient.licenseRegistryReadOnlyClient, "hasIpAttachedLicenseTerms") .resolves(true); - - try { - await licenseClient.attachLicenseTerms({ - ipId: zeroAddress, - licenseTermsId: "1", - }); - } catch (error) { - expect((error as Error).message).equal( - "Failed to attach license terms: License terms id 1 is already attached to the IP with id 0x0000000000000000000000000000000000000000.", - ); - } + const result = await licenseClient.attachLicenseTerms({ + ipId: zeroAddress, + licenseTermsId: "1", + }); + expect(result).to.deep.equal({ + txHash: "", + success: false, + }); }); it("should return txHash when call attachLicenseTerms given args is correct", async () => { diff --git a/packages/react-sdk/.env.example b/packages/react-sdk/.env.example new file mode 100644 index 00000000..b915d5a4 --- /dev/null +++ b/packages/react-sdk/.env.example @@ -0,0 +1,2 @@ +SEPOLIA_TEST_WALLET_ADDRESS = 0x0000000000000000000000000000000000000000 +SEPOLIA_WALLET_PRIVATE_KEY = 0x0000000000000000000000000000000000000000 \ No newline at end of file diff --git a/packages/react-sdk/generator/templates/resource.js b/packages/react-sdk/generator/templates/resource.js index 0bc69e71..7d9154ad 100644 --- a/packages/react-sdk/generator/templates/resource.js +++ b/packages/react-sdk/generator/templates/resource.js @@ -10,11 +10,10 @@ const methodTemplate = `<%=comments%>\nconst <%=method.name %> = async (<% metho setLoadings((prev ) => ({ ...prev, <%=method.name %>: false })); return response; }catch(e){ - if(e instanceof Error){ - setErrors((prev) => ({ ...prev, <%=method.name %>: e.message })); - setLoadings((prev) => ({ ...prev, <%=method.name %>: false })); - } - throw new Error(\`unhandled error type\`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, <%=method.name %>: errorMessage })); + setLoadings((prev) => ({ ...prev, <%=method.name %>: false })); + throw new Error(errorMessage); } }; `; @@ -28,6 +27,7 @@ const startTemplate = `import { <% types.forEach((type,index)=>{%>\n<%=type %><% import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; + import { handleError } from "../util"; const use<%=name %> = () => { const client = useStoryContext(); const [loadings,setLoadings] = useState>({<% methodNames.forEach((name,index)=>{%><%=name %>: false<%=index === methodNames.length - 1 ? '' : ',' %> <%})%>}); diff --git a/packages/react-sdk/jest.config.js b/packages/react-sdk/jest.config.js new file mode 100644 index 00000000..c9ae5171 --- /dev/null +++ b/packages/react-sdk/jest.config.js @@ -0,0 +1,9 @@ +/** @type {import('jest').Config} */ +const config = { + verbose: true, + testEnvironment: "./test/jest-environment-jsdom.ts", + setupFiles: ["./test/jest-setup.ts"], + testTimeout: 1000 * 60, +}; + +module.exports = config; diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 944efa8e..21273033 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -14,6 +14,7 @@ "scripts": { "generate": "node ./generator/index.js && npm run fix", "build": "pnpm run fix && preconstruct build", + "test": "jest -i", "fix": "pnpm run format:fix && pnpm run lint:fix", "format": "prettier --check .", "format:fix": "prettier --write .", @@ -43,7 +44,14 @@ ], "babel": { "presets": [ - "@babel/preset-env", + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + } + } + ], "@babel/preset-typescript", [ "@babel/preset-react", @@ -57,19 +65,29 @@ "dependencies": { "@story-protocol/core-sdk": "1.0.0-rc.14", "react": "^18.3.1", - "viem": "^2.8.12", - "@types/react": "^18.3.3" + "viem": "^2.8.12" }, "devDependencies": { - "@babel/preset-env": "^7.22.20", + "@babel/core": "^7.23.0", + "@babel/preset-env": "^7.24.4", "@babel/preset-react": "^7.24.7", - "@babel/preset-typescript": "^7.23.0", + "@babel/preset-typescript": "^7.24.1", "@preconstruct/cli": "^2.8.1", "@story-protocol/eslint-config": "workspace:*", "@story-protocol/prettier-config": "workspace:*", "@story-protocol/tsconfig": "workspace:*", + "@testing-library/react": "^16.0.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.8.2", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "babel-jest": "^29.7.0", + "dotenv": "^16.3.1", + "ejs": "^3.1.10", + "eslint-plugin-jest-dom": "^5.4.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "ts-node": "^10.9.1", - "typescript": "^5.4.5", - "ejs": "^3.1.10" + "typescript": "^5.4.5" } } diff --git a/packages/react-sdk/src/resources/useDispute.ts b/packages/react-sdk/src/resources/useDispute.ts index e4bbc07c..5013182f 100644 --- a/packages/react-sdk/src/resources/useDispute.ts +++ b/packages/react-sdk/src/resources/useDispute.ts @@ -9,6 +9,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useDispute = () => { const client = useStoryContext(); @@ -49,11 +50,10 @@ const useDispute = () => { setLoadings((prev) => ({ ...prev, raiseDispute: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, raiseDispute: e.message })); - setLoadings((prev) => ({ ...prev, raiseDispute: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, raiseDispute: errorMessage })); + setLoadings((prev) => ({ ...prev, raiseDispute: false })); + throw new Error(errorMessage); } }; @@ -79,11 +79,10 @@ const useDispute = () => { setLoadings((prev) => ({ ...prev, cancelDispute: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, cancelDispute: e.message })); - setLoadings((prev) => ({ ...prev, cancelDispute: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, cancelDispute: errorMessage })); + setLoadings((prev) => ({ ...prev, cancelDispute: false })); + throw new Error(errorMessage); } }; @@ -107,11 +106,10 @@ const useDispute = () => { setLoadings((prev) => ({ ...prev, resolveDispute: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, resolveDispute: e.message })); - setLoadings((prev) => ({ ...prev, resolveDispute: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, resolveDispute: errorMessage })); + setLoadings((prev) => ({ ...prev, resolveDispute: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useIpAccount.ts b/packages/react-sdk/src/resources/useIpAccount.ts index 9177757b..f1a596b9 100644 --- a/packages/react-sdk/src/resources/useIpAccount.ts +++ b/packages/react-sdk/src/resources/useIpAccount.ts @@ -8,6 +8,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useIpAccount = () => { const client = useStoryContext(); @@ -41,11 +42,10 @@ const useIpAccount = () => { setLoadings((prev) => ({ ...prev, execute: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, execute: e.message })); - setLoadings((prev) => ({ ...prev, execute: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, execute: errorMessage })); + setLoadings((prev) => ({ ...prev, execute: false })); + throw new Error(errorMessage); } }; @@ -70,11 +70,10 @@ const useIpAccount = () => { setLoadings((prev) => ({ ...prev, executeWithSig: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, executeWithSig: e.message })); - setLoadings((prev) => ({ ...prev, executeWithSig: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, executeWithSig: errorMessage })); + setLoadings((prev) => ({ ...prev, executeWithSig: false })); + throw new Error(errorMessage); } }; @@ -92,11 +91,10 @@ const useIpAccount = () => { setLoadings((prev) => ({ ...prev, getIpAccountNonce: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, getIpAccountNonce: e.message })); - setLoadings((prev) => ({ ...prev, getIpAccountNonce: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, getIpAccountNonce: errorMessage })); + setLoadings((prev) => ({ ...prev, getIpAccountNonce: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useIpAsset.ts b/packages/react-sdk/src/resources/useIpAsset.ts index 0a752bf0..962e08fe 100644 --- a/packages/react-sdk/src/resources/useIpAsset.ts +++ b/packages/react-sdk/src/resources/useIpAsset.ts @@ -15,6 +15,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useIpAsset = () => { const client = useStoryContext(); @@ -59,11 +60,10 @@ const useIpAsset = () => { setLoadings((prev) => ({ ...prev, register: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, register: e.message })); - setLoadings((prev) => ({ ...prev, register: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, register: errorMessage })); + setLoadings((prev) => ({ ...prev, register: false })); + throw new Error(errorMessage); } }; @@ -90,11 +90,10 @@ const useIpAsset = () => { setLoadings((prev) => ({ ...prev, registerDerivative: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, registerDerivative: e.message })); - setLoadings((prev) => ({ ...prev, registerDerivative: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, registerDerivative: errorMessage })); + setLoadings((prev) => ({ ...prev, registerDerivative: false })); + throw new Error(errorMessage); } }; @@ -130,17 +129,16 @@ const useIpAsset = () => { })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - registerDerivativeWithLicenseTokens: e.message, - })); - setLoadings((prev) => ({ - ...prev, - registerDerivativeWithLicenseTokens: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: errorMessage, + })); + setLoadings((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: false, + })); + throw new Error(errorMessage); } }; @@ -183,17 +181,16 @@ const useIpAsset = () => { })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - mintAndRegisterIpAssetWithPilTerms: e.message, - })); - setLoadings((prev) => ({ - ...prev, - mintAndRegisterIpAssetWithPilTerms: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: errorMessage, + })); + setLoadings((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: false, + })); + throw new Error(errorMessage); } }; @@ -227,17 +224,13 @@ const useIpAsset = () => { setLoadings((prev) => ({ ...prev, registerIpAndAttachPilTerms: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - registerIpAndAttachPilTerms: e.message, - })); - setLoadings((prev) => ({ - ...prev, - registerIpAndAttachPilTerms: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerIpAndAttachPilTerms: errorMessage, + })); + setLoadings((prev) => ({ ...prev, registerIpAndAttachPilTerms: false })); + throw new Error(errorMessage); } }; @@ -269,11 +262,10 @@ const useIpAsset = () => { setLoadings((prev) => ({ ...prev, registerDerivativeIp: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, registerDerivativeIp: e.message })); - setLoadings((prev) => ({ ...prev, registerDerivativeIp: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, registerDerivativeIp: errorMessage })); + setLoadings((prev) => ({ ...prev, registerDerivativeIp: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useLicense.ts b/packages/react-sdk/src/resources/useLicense.ts index 9397cd36..2cf563f1 100644 --- a/packages/react-sdk/src/resources/useLicense.ts +++ b/packages/react-sdk/src/resources/useLicense.ts @@ -13,6 +13,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useLicense = () => { const client = useStoryContext(); @@ -58,17 +59,16 @@ const useLicense = () => { })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - registerNonComSocialRemixingPIL: e.message, - })); - setLoadings((prev) => ({ - ...prev, - registerNonComSocialRemixingPIL: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerNonComSocialRemixingPIL: errorMessage, + })); + setLoadings((prev) => ({ + ...prev, + registerNonComSocialRemixingPIL: false, + })); + throw new Error(errorMessage); } }; @@ -91,11 +91,13 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, registerCommercialUsePIL: e.message })); - setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerCommercialUsePIL: errorMessage, + })); + setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: false })); + throw new Error(errorMessage); } }; @@ -119,14 +121,13 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - registerCommercialRemixPIL: e.message, - })); - setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerCommercialRemixPIL: errorMessage, + })); + setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: false })); + throw new Error(errorMessage); } }; @@ -149,11 +150,10 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, attachLicenseTerms: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, attachLicenseTerms: e.message })); - setLoadings((prev) => ({ ...prev, attachLicenseTerms: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, attachLicenseTerms: errorMessage })); + setLoadings((prev) => ({ ...prev, attachLicenseTerms: false })); + throw new Error(errorMessage); } }; @@ -189,11 +189,10 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, mintLicenseTokens: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, mintLicenseTokens: e.message })); - setLoadings((prev) => ({ ...prev, mintLicenseTokens: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, mintLicenseTokens: errorMessage })); + setLoadings((prev) => ({ ...prev, mintLicenseTokens: false })); + throw new Error(errorMessage); } }; @@ -214,11 +213,10 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, getLicenseTerms: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, getLicenseTerms: e.message })); - setLoadings((prev) => ({ ...prev, getLicenseTerms: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, getLicenseTerms: errorMessage })); + setLoadings((prev) => ({ ...prev, getLicenseTerms: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useNftClient.ts b/packages/react-sdk/src/resources/useNftClient.ts index 9a9fb082..bc165f10 100644 --- a/packages/react-sdk/src/resources/useNftClient.ts +++ b/packages/react-sdk/src/resources/useNftClient.ts @@ -5,6 +5,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useNftClient = () => { const client = useStoryContext(); @@ -38,11 +39,10 @@ const useNftClient = () => { setLoadings((prev) => ({ ...prev, createNFTCollection: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, createNFTCollection: e.message })); - setLoadings((prev) => ({ ...prev, createNFTCollection: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, createNFTCollection: errorMessage })); + setLoadings((prev) => ({ ...prev, createNFTCollection: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/usePermission.ts b/packages/react-sdk/src/resources/usePermission.ts index 31b122a1..6b723159 100644 --- a/packages/react-sdk/src/resources/usePermission.ts +++ b/packages/react-sdk/src/resources/usePermission.ts @@ -9,6 +9,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const usePermission = () => { const client = useStoryContext(); @@ -57,11 +58,10 @@ const usePermission = () => { setLoadings((prev) => ({ ...prev, setPermission: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, setPermission: e.message })); - setLoadings((prev) => ({ ...prev, setPermission: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, setPermission: errorMessage })); + setLoadings((prev) => ({ ...prev, setPermission: false })); + throw new Error(errorMessage); } }; @@ -90,17 +90,13 @@ const usePermission = () => { setLoadings((prev) => ({ ...prev, createSetPermissionSignature: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - createSetPermissionSignature: e.message, - })); - setLoadings((prev) => ({ - ...prev, - createSetPermissionSignature: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + createSetPermissionSignature: errorMessage, + })); + setLoadings((prev) => ({ ...prev, createSetPermissionSignature: false })); + throw new Error(errorMessage); } }; @@ -124,11 +120,10 @@ const usePermission = () => { setLoadings((prev) => ({ ...prev, setAllPermissions: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, setAllPermissions: e.message })); - setLoadings((prev) => ({ ...prev, setAllPermissions: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, setAllPermissions: errorMessage })); + setLoadings((prev) => ({ ...prev, setAllPermissions: false })); + throw new Error(errorMessage); } }; @@ -156,11 +151,10 @@ const usePermission = () => { setLoadings((prev) => ({ ...prev, setBatchPermissions: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, setBatchPermissions: e.message })); - setLoadings((prev) => ({ ...prev, setBatchPermissions: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, setBatchPermissions: errorMessage })); + setLoadings((prev) => ({ ...prev, setBatchPermissions: false })); + throw new Error(errorMessage); } }; @@ -196,17 +190,16 @@ const usePermission = () => { })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - createBatchPermissionSignature: e.message, - })); - setLoadings((prev) => ({ - ...prev, - createBatchPermissionSignature: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + createBatchPermissionSignature: errorMessage, + })); + setLoadings((prev) => ({ + ...prev, + createBatchPermissionSignature: false, + })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useRoyalty.ts b/packages/react-sdk/src/resources/useRoyalty.ts index 4a7945e6..e02e53fe 100644 --- a/packages/react-sdk/src/resources/useRoyalty.ts +++ b/packages/react-sdk/src/resources/useRoyalty.ts @@ -14,6 +14,7 @@ import { Hex, Address } from "viem"; import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useRoyalty = () => { const client = useStoryContext(); @@ -53,11 +54,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, collectRoyaltyTokens: e.message })); - setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, collectRoyaltyTokens: errorMessage })); + setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: false })); + throw new Error(errorMessage); } }; @@ -81,11 +81,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, payRoyaltyOnBehalf: e.message })); - setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, payRoyaltyOnBehalf: errorMessage })); + setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: false })); + throw new Error(errorMessage); } }; @@ -109,11 +108,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, claimableRevenue: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, claimableRevenue: e.message })); - setLoadings((prev) => ({ ...prev, claimableRevenue: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, claimableRevenue: errorMessage })); + setLoadings((prev) => ({ ...prev, claimableRevenue: false })); + throw new Error(errorMessage); } }; @@ -138,11 +136,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, claimRevenue: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, claimRevenue: e.message })); - setLoadings((prev) => ({ ...prev, claimRevenue: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, claimRevenue: errorMessage })); + setLoadings((prev) => ({ ...prev, claimRevenue: false })); + throw new Error(errorMessage); } }; @@ -164,11 +161,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, snapshot: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, snapshot: e.message })); - setLoadings((prev) => ({ ...prev, snapshot: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, snapshot: errorMessage })); + setLoadings((prev) => ({ ...prev, snapshot: false })); + throw new Error(errorMessage); } }; @@ -189,11 +185,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, getRoyaltyVaultAddress: e.message })); - setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, getRoyaltyVaultAddress: errorMessage })); + setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/util.ts b/packages/react-sdk/src/util.ts new file mode 100644 index 00000000..362bc955 --- /dev/null +++ b/packages/react-sdk/src/util.ts @@ -0,0 +1,7 @@ +export const handleError = (error: unknown): string => { + if (error instanceof Error) { + return error.message; + } else { + return "Unhandled error type"; + } +}; diff --git a/packages/react-sdk/test/integration/useDispute.test.ts b/packages/react-sdk/test/integration/useDispute.test.ts new file mode 100644 index 00000000..47dedf5f --- /dev/null +++ b/packages/react-sdk/test/integration/useDispute.test.ts @@ -0,0 +1,69 @@ +import { renderHook, act, waitFor } from "@testing-library/react"; +import { Address } from "viem"; +import { RaiseDisputeResponse, useDispute } from "../../src"; +import { useIpAsset } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { mockERC721Address, getTokenId } from "./utils/util"; + +const arbitrationPolicyAddress = "0xc07Bc791CF55E718BA7D70cE650B3152BbE3325e"; +describe("useDispute Functions", () => { + const { + result: { current: disputeHook }, + } = renderHook(() => useDispute(), { wrapper: Wrapper }); + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + let ipId: Address; + let disputedId: number; + + beforeAll(async () => { + const tokenId = await getTokenId(); + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + }); + + it("should success when call raise dispute", async () => { + let result: RaiseDisputeResponse; + await act(async () => { + result = await disputeHook.raiseDispute({ + targetIpId: ipId, + arbitrationPolicy: arbitrationPolicyAddress, + linkToDisputeEvidence: "foo", + targetTag: "PLAGIARISM", + txOptions: { + waitForTransaction: true, + }, + }); + disputedId = Number(result.disputeId); + }); + await waitFor(() => { + expect(result).toEqual( + expect.objectContaining({ + txHash: expect.any(String), + disputeId: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when cancel dispute", async () => { + await act(async () => { + await expect( + disputeHook.cancelDispute({ disputeId: disputedId }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useIpAccount.test.ts b/packages/react-sdk/test/integration/useIpAccount.test.ts new file mode 100644 index 00000000..c1214358 --- /dev/null +++ b/packages/react-sdk/test/integration/useIpAccount.test.ts @@ -0,0 +1,149 @@ +import { renderHook, act } from "@testing-library/react"; +import { mockERC721Address, getTokenId, walletClient } from "./utils/util"; +import { + AccessPermission, + getPermissionSignature, + useIpAccount, + useIpAsset, +} from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { + Address, + Hex, + encodeFunctionData, + getAddress, + toFunctionSelector, +} from "viem"; +const coreMetadataModuleAddress = "0xDa498A3f7c8a88cb72201138C366bE3778dB9575"; +const permissionAddress = "0xF9936a224b3Deb6f9A4645ccAfa66f7ECe83CF0A"; +const accessControllerAbi = [ + { + inputs: [ + { + internalType: "address", + name: "ipId", + type: "address", + }, + { + internalType: "address", + name: "signer", + type: "address", + }, + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "bytes4", + name: "func", + type: "bytes4", + }, + { + internalType: "uint8", + name: "permission", + type: "uint8", + }, + ], + name: "setPermission", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +describe("useIpAccount Functions", () => { + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + const { + result: { current: ipAccountHook }, + } = renderHook(() => useIpAccount(), { wrapper: Wrapper }); + + let ipId: Address; + let data: Hex; + beforeAll(async () => { + const tokenId = await getTokenId(); + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + data = encodeFunctionData({ + abi: accessControllerAbi, + functionName: "setPermission", + args: [ + getAddress(ipId), + getAddress(process.env.SEPOLIA_TEST_WALLET_ADDRESS as Hex), + getAddress(coreMetadataModuleAddress), + toFunctionSelector("function setAll(address,string,bytes32,bytes32)"), + AccessPermission.ALLOW, + ], + }); + }); + + it("should success when call execute", async () => { + await act(async () => { + await expect( + ipAccountHook.execute({ + to: permissionAddress, + value: 0, + data, + ipId: ipId, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when executeWithSig setting permission", async () => { + const state = await ipAccountHook.getIpAccountNonce(ipId); + const expectedState = state + 1n; + const deadline = Date.now() + 60000; + const signature = await getPermissionSignature({ + ipId, + wallet: walletClient, + permissions: [ + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Hex, + to: coreMetadataModuleAddress, + permission: AccessPermission.ALLOW, + func: "function setAll(address,string,bytes32,bytes32)", + }, + ], + nonce: expectedState, + + chainId: 11155111n, + deadline: BigInt(deadline), + }); + await act(async () => { + await expect( + ipAccountHook.executeWithSig({ + ipId: ipId, + value: 0, + to: permissionAddress, + data: data, + deadline: deadline, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Hex, + signature: signature, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useIpAsset.test.ts b/packages/react-sdk/test/integration/useIpAsset.test.ts new file mode 100644 index 00000000..8696b7f3 --- /dev/null +++ b/packages/react-sdk/test/integration/useIpAsset.test.ts @@ -0,0 +1,327 @@ +import { renderHook, act, waitFor } from "@testing-library/react"; +import { Address, toHex } from "viem"; +import { + CreateIpAssetWithPilTermsResponse, + PIL_TYPE, + RegisterIpResponse, + useLicense, + useNftClient, +} from "../../src"; +import { useIpAsset } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { mockERC721Address, getTokenId, mockERC20Address } from "./utils/util"; +describe("useIpAsset Functions", () => { + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + const { + result: { current: licenseHook }, + } = renderHook(() => useLicense(), { wrapper: Wrapper }); + const { + result: { current: nftClientHook }, + } = renderHook(() => useNftClient(), { wrapper: Wrapper }); + + let childIpId: Address; + let noCommercialLicenseTermsId: bigint; + let parentIpId: Address; + it("should success when call register", async () => { + const tokenId = await getTokenId(); + let result: RegisterIpResponse; + await act(async () => { + result = await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }); + childIpId = result.ipId!; + }); + await waitFor(() => { + expect(result).toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when call register given metadata", async () => { + const tokenId = await getTokenId(); + await act(async () => { + await expect( + ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + metadata: { + metadataURI: "test-uri", + metadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when registering derivative", async () => { + const tokenId = await getTokenId(); + await act(async () => { + parentIpId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + await act(async () => { + noCommercialLicenseTermsId = ( + await licenseHook.registerNonComSocialRemixingPIL({ + txOptions: { + waitForTransaction: true, + }, + }) + ).licenseTermsId!; + }); + await act(async () => { + await licenseHook.attachLicenseTerms({ + ipId: parentIpId, + licenseTermsId: noCommercialLicenseTermsId, + txOptions: { + waitForTransaction: true, + }, + }); + }); + await act(async () => { + await expect( + ipAssetHook.registerDerivative({ + parentIpIds: [parentIpId], + childIpId: childIpId, + licenseTermsIds: [noCommercialLicenseTermsId], + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when registering derivative with license tokens", async () => { + const tokenId = await getTokenId(); + let ipId: Address; + let licenseTokenId: bigint; + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + await act(async () => { + licenseTokenId = ( + await licenseHook.mintLicenseTokens({ + licenseTermsId: noCommercialLicenseTermsId, + licensorIpId: parentIpId, + txOptions: { + waitForTransaction: true, + }, + }) + ).licenseTokenIds![0]!; + }); + await act(async () => { + await expect( + ipAssetHook.registerDerivativeWithLicenseTokens({ + childIpId: ipId, + licenseTokenIds: [licenseTokenId], + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); + + describe("NFT Client (SPG)", () => { + let nftContract: Address; + beforeAll(async () => { + await act(async () => { + nftContract = ( + await nftClientHook.createNFTCollection({ + name: "test-collection", + symbol: "TEST", + maxSupply: 100, + txOptions: { + waitForTransaction: true, + }, + }) + ).nftContract!; + }); + }); + describe("should success when mint and register ip and attach pil terms", () => { + it("Non-Commercial Remix", async () => { + await act(async () => { + await expect( + ipAssetHook.mintAndRegisterIpAssetWithPilTerms({ + nftContract, + pilType: PIL_TYPE.NON_COMMERCIAL_REMIX, + metadata: { + metadataURI: "test-uri", + metadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + + it("Commercial Use", async () => { + await act(async () => { + await expect( + ipAssetHook.mintAndRegisterIpAssetWithPilTerms({ + nftContract, + pilType: PIL_TYPE.COMMERCIAL_USE, + commercialRevShare: 10, + mintingFee: "100", + currency: mockERC20Address, + metadata: { + metadataURI: "test-uri", + metadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + + it("Commercial Remix", async () => { + await act(async () => { + await expect( + ipAssetHook.mintAndRegisterIpAssetWithPilTerms({ + nftContract, + pilType: PIL_TYPE.COMMERCIAL_REMIX, + commercialRevShare: 10, + mintingFee: "100", + currency: mockERC20Address, + metadata: { + metadataURI: "test-uri", + metadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + }); + + it("should success when register derivative ip", async () => { + const tokenChildId = await getTokenId(nftContract); + let createIpAssetWithPilTermsResponse: CreateIpAssetWithPilTermsResponse; + await act(async () => { + createIpAssetWithPilTermsResponse = + await ipAssetHook.mintAndRegisterIpAssetWithPilTerms({ + nftContract, + pilType: PIL_TYPE.NON_COMMERCIAL_REMIX, + commercialRevShare: 10, + mintingFee: "100", + currency: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }); + }); + await act(async () => { + await expect( + ipAssetHook.registerDerivativeIp({ + nftContract: nftContract, + tokenId: tokenChildId!, + derivData: { + parentIpIds: [createIpAssetWithPilTermsResponse.ipId!], + licenseTermsIds: [ + createIpAssetWithPilTermsResponse.licenseTermsId!, + ], + }, + deadline: 1000n, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + ipId: expect.any(String), + }) + ); + }); + }); + + it("should success when register ip and attach pil terms", async () => { + const tokenId = await getTokenId(nftContract); + const deadline = 1000n; + await act(async () => { + await expect( + ipAssetHook.registerIpAndAttachPilTerms({ + nftContract: nftContract, + tokenId: tokenId!, + deadline, + pilType: PIL_TYPE.NON_COMMERCIAL_REMIX, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useLicense.test.ts b/packages/react-sdk/test/integration/useLicense.test.ts new file mode 100644 index 00000000..06903ade --- /dev/null +++ b/packages/react-sdk/test/integration/useLicense.test.ts @@ -0,0 +1,144 @@ +import { renderHook } from "@testing-library/react"; +import { useIpAsset, useLicense } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { act } from "react"; +import { getTokenId, mockERC20Address, mockERC721Address } from "./utils/util"; +import { Hex } from "viem"; + +describe("useLicense Functions", () => { + const { + result: { current: licenseHook }, + } = renderHook(() => useLicense(), { wrapper: Wrapper }); + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + describe("registering license with different types", () => { + it("should success when register license with non commercial social remixing PIL", async () => { + await act(async () => { + await expect( + licenseHook.registerNonComSocialRemixingPIL({ + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + licenseTermsId: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when register license with commercial use", async () => { + await act(async () => { + await expect( + licenseHook.registerCommercialUsePIL({ + mintingFee: "1", + currency: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + licenseTermsId: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when register license with commercial Remix use", async () => { + await act(async () => { + await expect( + licenseHook.registerCommercialRemixPIL({ + mintingFee: "1", + commercialRevShare: 100, + currency: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + licenseTermsId: expect.any(BigInt), + }) + ); + }); + }); + }); + + describe("attach License Terms and mint license tokens", () => { + let ipId: Hex; + let licenseId: bigint; + let tokenId; + beforeAll(async () => { + tokenId = await getTokenId(); + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + await act(async () => { + licenseId = ( + await licenseHook.registerNonComSocialRemixingPIL({ + txOptions: { + waitForTransaction: true, + }, + }) + ).licenseTermsId!; + }); + }); + + it("should success when attach License Terms", async () => { + await act(async () => { + await expect( + licenseHook.attachLicenseTerms({ + ipId: ipId, + licenseTermsId: licenseId, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: expect.any(Boolean), + }) + ); + }); + }); + + it("should success when minting license tokens", async () => { + await act(async () => { + await expect( + licenseHook.mintLicenseTokens({ + licenseTermsId: licenseId, + licensorIpId: ipId, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + licenseTokenIds: expect.any(Array), + }) + ); + }); + }); + + it("should success when get license terms", async () => { + await act(async () => { + await expect(licenseHook.getLicenseTerms(licenseId)).resolves.toEqual( + expect.any(Object) + ); + }); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useNftClient.test.ts b/packages/react-sdk/test/integration/useNftClient.test.ts new file mode 100644 index 00000000..ff3fcb13 --- /dev/null +++ b/packages/react-sdk/test/integration/useNftClient.test.ts @@ -0,0 +1,29 @@ +import { renderHook } from "@testing-library/react"; +import { useNftClient } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { act } from "react"; + +describe("useNftClient Functions", () => { + const { + result: { current: nftClientHook }, + } = renderHook(() => useNftClient(), { wrapper: Wrapper }); + + it("should success when create nft collection", async () => { + await act(async () => { + await expect( + nftClientHook.createNFTCollection({ + name: "test-collection", + symbol: "TEST", + maxSupply: 100, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + nftContract: expect.any(String), + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/usePermission.test.ts b/packages/react-sdk/test/integration/usePermission.test.ts new file mode 100644 index 00000000..f02b4422 --- /dev/null +++ b/packages/react-sdk/test/integration/usePermission.test.ts @@ -0,0 +1,160 @@ +import { act, renderHook } from "@testing-library/react"; +import { useIpAsset, usePermission } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { Address } from "viem"; +import { getTokenId, mockERC721Address } from "./utils/util"; + +describe("usePermission Functions", () => { + const { + result: { current: permissionHook }, + } = renderHook(() => usePermission(), { wrapper: Wrapper }); + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + + let ipId: Address; + beforeAll(async () => { + const tokenId = await getTokenId(); + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + }); + it("should success when call setPermission", async () => { + await act(async () => { + await expect( + permissionHook.setPermission({ + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); + + it("should success when call setAllPermissions", async () => { + await act(async () => { + await expect( + permissionHook.setAllPermissions({ + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + permission: 1, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); + + it("should success when call createSetPermissionSignature", async () => { + await act(async () => { + await expect( + permissionHook.createSetPermissionSignature({ + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + deadline: 60000n, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); + + it("should success when call setBatchPermissions", async () => { + await act(async () => { + await expect( + permissionHook.setBatchPermissions({ + permissions: [ + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + func: "function setAll(address,string,bytes32,bytes32)", + }, + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + func: "function freezeMetadata(address)", + }, + ], + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); + + it("should success when call createBatchPermissionSignature", async () => { + await act(async () => { + await expect( + permissionHook.createBatchPermissionSignature({ + ipId: ipId, + permissions: [ + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + func: "function setAll(address,string,bytes32,bytes32)", + }, + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + func: "function freezeMetadata(address)", + }, + ], + deadline: 60000n, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useRoyalty.test.ts b/packages/react-sdk/test/integration/useRoyalty.test.ts new file mode 100644 index 00000000..2625f412 --- /dev/null +++ b/packages/react-sdk/test/integration/useRoyalty.test.ts @@ -0,0 +1,258 @@ +import { act, renderHook, waitFor } from "@testing-library/react"; +import { useIpAccount, useIpAsset, useLicense, useRoyalty } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { Address, encodeFunctionData } from "viem"; +import { getTokenId, mockERC20Address, mockERC721Address } from "./utils/util"; + +describe("useRoyalty Functions", () => { + const { + result: { current: royaltyHook }, + } = renderHook(() => useRoyalty(), { wrapper: Wrapper }); + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + const { + result: { current: licenseHook }, + } = renderHook(() => useLicense(), { wrapper: Wrapper }); + const { + result: { current: ipAccountHook }, + } = renderHook(() => useIpAccount(), { wrapper: Wrapper }); + let ipId1: Address; + let ipId2: Address; + + const getIpId = async (): Promise
=> { + const tokenId = await getTokenId(); + return await act(async () => { + return ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + }; + const getCommercialPolicyId = async (): Promise => { + return await act(async () => { + return ( + await licenseHook.registerCommercialUsePIL({ + mintingFee: "1", + currency: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).licenseTermsId!; + }); + }; + + const attachLicenseTerms = async (ipId: Address, licenseTermsId: bigint) => { + await act(async () => { + await licenseHook.attachLicenseTerms({ + ipId, + licenseTermsId: licenseTermsId, + txOptions: { + waitForTransaction: true, + }, + }); + }); + }; + beforeAll(async () => { + ipId1 = await getIpId(); + ipId2 = await getIpId(); + const licenseTermsId = await getCommercialPolicyId(); + await attachLicenseTerms(ipId1, licenseTermsId); + await act(async () => { + await ipAssetHook.registerDerivative({ + childIpId: ipId2, + parentIpIds: [ipId1], + licenseTermsIds: [licenseTermsId], + txOptions: { + waitForTransaction: true, + }, + }); + }); + }); + + it("should success when collect royalty tokens", async () => { + await act(async () => { + await expect( + royaltyHook.collectRoyaltyTokens({ + parentIpId: ipId1, + royaltyVaultIpId: ipId2, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + royaltyTokensCollected: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when pay royalty on behalf", async () => { + await act(async () => { + await expect( + royaltyHook.payRoyaltyOnBehalf({ + receiverIpId: ipId1, + payerIpId: ipId2, + token: mockERC20Address, + amount: "10", + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when snapshot", async () => { + await act(async () => { + await expect( + royaltyHook.snapshot({ + royaltyVaultIpId: ipId1, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + snapshotId: expect.any(BigInt), + }) + ); + }); + }); + it("should success when claimable revenue", async () => { + await act(async () => { + await expect( + royaltyHook.claimableRevenue({ + royaltyVaultIpId: ipId1, + account: ipId1, + snapshotId: "1", + token: mockERC20Address, + }) + ).resolves.toEqual(expect.any(BigInt)); + }); + }); + + it("should success when claim revenue by ipAccount", async () => { + await act(async () => { + await expect( + royaltyHook.claimRevenue({ + royaltyVaultIpId: ipId1, + snapshotIds: ["1"], + account: ipId1, + token: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + claimableToken: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when claim revenue by ipAccount by EOA", async () => { + const proxyAddress = await royaltyHook.getRoyaltyVaultAddress(ipId1); + //1.transfer token to eoa + await act(async () => { + await ipAccountHook.execute({ + to: proxyAddress, + value: 0, + ipId: ipId1, + txOptions: { + waitForTransaction: true, + }, + data: encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "transfer", + args: [ + process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + BigInt(10 * 10 ** 6), + ], + }), + }); + }); + //2. transfer token to royaltyVault,revenue token + await act(async () => { + await royaltyHook.payRoyaltyOnBehalf({ + receiverIpId: ipId1, + payerIpId: ipId2, + token: mockERC20Address, + amount: "10", + txOptions: { + waitForTransaction: true, + }, + }); + }); + //3. snapshot + let snapshotId: bigint; + await act(async () => { + snapshotId = ( + await royaltyHook.snapshot({ + royaltyVaultIpId: ipId1, + txOptions: { + waitForTransaction: true, + }, + }) + ).snapshotId!; + }); + //4. claim revenue + await act(async () => { + await expect( + royaltyHook.claimRevenue({ + royaltyVaultIpId: ipId1, + snapshotIds: [snapshotId], + token: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + claimableToken: expect.any(BigInt), + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/utils/Wrapper.tsx b/packages/react-sdk/test/integration/utils/Wrapper.tsx new file mode 100644 index 00000000..2fb69051 --- /dev/null +++ b/packages/react-sdk/test/integration/utils/Wrapper.tsx @@ -0,0 +1,18 @@ +import { Address, http } from "viem"; +import { StoryProvider } from "../../../src"; +import { privateKeyToAccount } from "viem/accounts"; +import { ReactNode } from "react"; +type Props = { children: ReactNode }; +const Wrapper = ({ children }: Props) => ( + + {children} + +); +export default Wrapper; diff --git a/packages/react-sdk/test/integration/utils/util.ts b/packages/react-sdk/test/integration/utils/util.ts new file mode 100644 index 00000000..aec30725 --- /dev/null +++ b/packages/react-sdk/test/integration/utils/util.ts @@ -0,0 +1,63 @@ +import { privateKeyToAccount } from "viem/accounts"; +import { + http, + createPublicClient, + createWalletClient, + Hex, + Address, + Chain, +} from "viem"; +import { sepolia } from "viem/chains"; + +import { SupportedChainIds } from "@story-protocol/core-sdk"; +export const RPC = "http://127.0.0.1:8545"; +export const mockERC721Address = "0x7ee32b8B515dEE0Ba2F25f612A04a731eEc24F49"; +export const mockERC20Address = "0xB132A6B7AE652c974EE1557A3521D53d18F6739f"; + +function chainStringToViemChain(chainId: SupportedChainIds): Chain { + switch (chainId) { + case "11155111": + case "sepolia": + return sepolia; + default: + throw new Error(`chainId ${chainId as string} not supported`); + } +} +const baseConfig = { + chain: chainStringToViemChain("sepolia"), + transport: http(RPC), +} as const; +export const publicClient = createPublicClient(baseConfig); +export const walletClient = createWalletClient({ + ...baseConfig, + account: privateKeyToAccount(process.env.SEPOLIA_WALLET_PRIVATE_KEY as Hex), +}); + +export const getTokenId = async ( + nftContract?: Address +): Promise => { + const { request } = await publicClient.simulateContract({ + abi: [ + { + inputs: [{ internalType: "address", name: "to", type: "address" }], + name: "mint", + outputs: [ + { internalType: "uint256", name: "tokenId", type: "uint256" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ], + address: nftContract || mockERC721Address, + functionName: "mint", + args: [process.env.SEPOLIA_TEST_WALLET_ADDRESS as Hex], + 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); + } +}; diff --git a/packages/react-sdk/test/jest-environment-jsdom.ts b/packages/react-sdk/test/jest-environment-jsdom.ts new file mode 100644 index 00000000..6596f25e --- /dev/null +++ b/packages/react-sdk/test/jest-environment-jsdom.ts @@ -0,0 +1,55 @@ +import $JSDOMEnvironment, { + TestEnvironment as $TestEnvironment, +} from "jest-environment-jsdom"; +import { TextDecoder, TextEncoder } from "util"; + +/** + * This patched JSDOMEnvironment serves as a proxy for the default JSDOMEnvironment from Jest. + * It patches the global objects TextEncoder, TextDecoder, and Uint8Array + * which are missing, or improperly implemented (Uint8Array is a node Buffer) in the JSDOM environment. + * + * A proxy pattern is used due to ES5 transpilation limitations, as direct + * class extension isn't possible in ES5. The class delegates other methods + * and properties to the original JSDOMEnvironment. + * + * We use this wrapper for full compatibility with browser global + * objects in our Jest testing environment. + */ +class JSDOMEnvironment { + private environment: $JSDOMEnvironment; + + constructor(...args: ConstructorParameters) { + this.environment = new $JSDOMEnvironment(...args); + this.environment.global.TextEncoder = TextEncoder; + this.environment.global.TextDecoder = + TextDecoder as typeof global.TextDecoder; + this.environment.global.Uint8Array = Uint8Array; + this.environment.global.Request = Request; + this.environment.global.Response = Response; + this.environment.global.Headers = Headers; + this.environment.global.fetch = fetch; + + return new Proxy(this, { + get: (target, prop, receiver) => { + if (Reflect.has(target, prop)) { + return Reflect.get(target, prop, receiver); + } else { + return Reflect.get(target.environment, prop, receiver); + } + }, + set: (target, prop, value) => { + if (Reflect.has(target, prop)) { + return Reflect.set(target, prop, value); + } else { + return Reflect.set(target.environment, prop, value); + } + }, + }); + } +} + +const TestEnvironment = + $TestEnvironment === $JSDOMEnvironment ? JSDOMEnvironment : $TestEnvironment; + +export default JSDOMEnvironment; +export { TestEnvironment }; diff --git a/packages/react-sdk/test/jest-setup.ts b/packages/react-sdk/test/jest-setup.ts new file mode 100644 index 00000000..3d0d62ec --- /dev/null +++ b/packages/react-sdk/test/jest-setup.ts @@ -0,0 +1 @@ +import "dotenv/config"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2fc77308..6ab49a01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,9 +142,6 @@ importers: '@story-protocol/core-sdk': specifier: 1.0.0-rc.14 version: 1.0.0-rc.14(typescript@5.4.5) - '@types/react': - specifier: ^18.3.3 - version: 18.3.3 react: specifier: ^18.3.1 version: 18.3.1 @@ -152,14 +149,17 @@ importers: specifier: ^2.8.12 version: 2.9.16(typescript@5.4.5)(zod@3.22.4) devDependencies: + '@babel/core': + specifier: ^7.23.0 + version: 7.24.4 '@babel/preset-env': - specifier: ^7.22.20 + specifier: ^7.24.4 version: 7.24.4(@babel/core@7.24.4) '@babel/preset-react': specifier: ^7.24.7 version: 7.24.7(@babel/core@7.24.4) '@babel/preset-typescript': - specifier: ^7.23.0 + specifier: ^7.24.1 version: 7.24.1(@babel/core@7.24.4) '@preconstruct/cli': specifier: ^2.8.1 @@ -173,9 +173,39 @@ importers: '@story-protocol/tsconfig': specifier: workspace:* version: link:../tsconfig + '@testing-library/react': + specifier: ^16.0.0 + version: 16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0)(react@18.3.1) + '@types/jest': + specifier: ^29.5.12 + version: 29.5.12 + '@types/node': + specifier: ^20.8.2 + version: 20.12.7 + '@types/react': + specifier: ^18.3.3 + version: 18.3.3 + '@types/react-dom': + specifier: ^18.3.0 + version: 18.3.0 + babel-jest: + specifier: ^29.7.0 + version: 29.7.0(@babel/core@7.24.4) + dotenv: + specifier: ^16.3.1 + version: 16.4.5 ejs: specifier: ^3.1.10 version: 3.1.10 + eslint-plugin-jest-dom: + specifier: ^5.4.0 + version: 5.4.0(@testing-library/dom@10.1.0)(eslint@8.57.0) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + jest-environment-jsdom: + specifier: ^29.7.0 + version: 29.7.0 ts-node: specifier: ^10.9.1 version: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) @@ -259,15 +289,15 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.4 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4) '@babel/helpers': 7.24.4 - '@babel/parser': 7.24.4 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 convert-source-map: 2.0.0 debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 @@ -772,6 +802,15 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.4): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.4): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -2494,11 +2533,77 @@ packages: engines: {node: '>=12'} dev: true + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} dev: true + /@jest/console@29.7.0: + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/core@29.7.0(ts-node@10.9.2): + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /@jest/create-cache-key-function@29.7.0: resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2516,6 +2621,23 @@ packages: jest-mock: 29.7.0 dev: true + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + dev: true + + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/fake-timers@29.7.0: resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2528,6 +2650,55 @@ packages: jest-util: 29.7.0 dev: true + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.7.0: + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 20.12.7 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2535,6 +2706,58 @@ packages: '@sinclair/typebox': 0.27.8 dev: true + /@jest/source-map@29.6.3: + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.7.0: + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@29.7.0: + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.24.4 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/types@26.6.2: resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} engines: {node: '>= 10.14.2'} @@ -3827,6 +4050,48 @@ packages: react: 18.3.1 dev: true + /@testing-library/dom@10.1.0: + resolution: {integrity: sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==} + engines: {node: '>=18'} + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/runtime': 7.24.4 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + dev: true + + /@testing-library/react@16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0)(react@18.3.1): + resolution: {integrity: sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 + '@types/react-dom': ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.4 + '@testing-library/dom': 10.1.0 + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.2.0(react@18.3.1) + dev: true + + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true + /@tsconfig/node10@1.0.11: resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} dev: true @@ -3843,6 +4108,39 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@types/aria-query@5.0.4: + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + dev: true + + /@types/babel__core@7.20.5: + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + dev: true + + /@types/babel__generator@7.6.8: + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + dependencies: + '@babel/types': 7.24.7 + dev: true + + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + dev: true + + /@types/babel__traverse@7.20.6: + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + dependencies: + '@babel/types': 7.24.7 + dev: true + /@types/chai-as-promised@7.1.8: resolution: {integrity: sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==} dependencies: @@ -3888,6 +4186,12 @@ packages: resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} dev: true + /@types/graceful-fs@4.1.9: + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + dependencies: + '@types/node': 20.12.7 + dev: true + /@types/har-format@1.2.15: resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==} dev: true @@ -3908,6 +4212,21 @@ packages: '@types/istanbul-lib-report': 3.0.3 dev: true + /@types/jest@29.5.12: + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /@types/jsdom@20.0.1: + resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} + dependencies: + '@types/node': 20.12.7 + '@types/tough-cookie': 4.0.5 + parse5: 7.1.2 + dev: true + /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: false @@ -3952,14 +4271,20 @@ packages: /@types/prop-types@15.7.12: resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - dev: false + dev: true + + /@types/react-dom@18.3.0: + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + dependencies: + '@types/react': 18.3.3 + dev: true /@types/react@18.3.3: resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 - dev: false + dev: true /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} @@ -3990,6 +4315,10 @@ packages: resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} dev: true + /@types/tough-cookie@4.0.5: + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + dev: true + /@types/trusted-types@2.0.7: resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} dev: true @@ -4625,6 +4954,11 @@ packages: tslib: 1.14.1 dev: true + /abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + dev: true + /abitype@0.10.3(typescript@5.4.5): resolution: {integrity: sha512-tRN+7XIa7J9xugdbRzFv/95ka5ivR/sRe01eiWvM0HWWjHuigSZEACgKa0sj4wGuekTDtghCx+5Izk/cOi78pQ==} peerDependencies: @@ -4711,6 +5045,13 @@ packages: negotiator: 0.6.3 dev: true + /acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + dependencies: + acorn: 8.11.3 + acorn-walk: 8.3.2 + dev: true + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -4728,6 +5069,15 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -4750,6 +5100,13 @@ packages: engines: {node: '>=6'} dev: true + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + /ansi-fragments@0.2.1: resolution: {integrity: sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==} dependencies: @@ -4815,6 +5172,12 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + /array-back@3.1.0: resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} engines: {node: '>=6'} @@ -4933,7 +5296,6 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false /at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} @@ -4969,21 +5331,62 @@ packages: '@babel/core': 7.24.4 dev: true - /babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} - dependencies: - '@babel/runtime': 7.24.4 - cosmiconfig: 7.1.0 - resolve: 1.22.8 - dev: true - - /babel-plugin-polyfill-corejs2@0.4.10(@babel/core@7.24.4): - resolution: {integrity: sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==} + /babel-jest@29.7.0(@babel/core@7.24.4): + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/core': ^7.8.0 dependencies: - '@babel/compat-data': 7.24.4 + '@babel/core': 7.24.4 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.24.4) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.24.7 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + dev: true + + /babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + dependencies: + '@babel/runtime': 7.24.4 + cosmiconfig: 7.1.0 + resolve: 1.22.8 + dev: true + + /babel-plugin-polyfill-corejs2@0.4.10(@babel/core@7.24.4): + resolution: {integrity: sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/compat-data': 7.24.4 '@babel/core': 7.24.4 '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.4) semver: 6.3.1 @@ -5022,6 +5425,37 @@ packages: - '@babel/core' dev: true + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.4): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.4) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.24.4): + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.4 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.4) + dev: true + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -5322,6 +5756,11 @@ packages: resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} dev: true + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true @@ -5403,6 +5842,10 @@ packages: consola: 3.2.3 dev: true + /cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + dev: true + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -5475,6 +5918,15 @@ packages: engines: {node: '>=6'} dev: true + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + dev: true + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -5503,7 +5955,6 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 - dev: false /command-exists@1.2.9: resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} @@ -5641,6 +6092,25 @@ packages: hasBin: true dev: true + /create-jest@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true @@ -5686,8 +6156,24 @@ packages: optional: true dev: true + /cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + dev: true + + /cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + dev: true + + /cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + dependencies: + cssom: 0.3.8 + dev: true + /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: true /csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} @@ -5711,6 +6197,15 @@ packages: stream-transform: 2.1.3 dev: true + /data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + dev: true + /data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -5802,6 +6297,10 @@ packages: engines: {node: '>=10'} dev: true + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: true + /decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} @@ -5811,6 +6310,15 @@ packages: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true + /dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + /deep-eql@4.1.3: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} @@ -5865,7 +6373,6 @@ packages: /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dev: false /denodeify@1.2.1: resolution: {integrity: sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==} @@ -5885,6 +6392,11 @@ packages: prop-types: 15.8.1 dev: true + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: true + /destr@2.0.3: resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} dev: true @@ -5909,6 +6421,16 @@ packages: hasBin: true dev: true + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -5947,6 +6469,18 @@ packages: dependencies: esutils: 2.0.3 + /dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dev: true + + /domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + dependencies: + webidl-conversions: 7.0.0 + dev: true + /dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: @@ -6013,6 +6547,11 @@ packages: minimalistic-crypto-utils: 1.0.1 dev: true + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -6059,6 +6598,11 @@ packages: strip-ansi: 6.0.1 dev: true + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: true + /envinfo@7.12.0: resolution: {integrity: sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==} engines: {node: '>=4'} @@ -6227,6 +6771,18 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + /escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + /eslint-config-prettier@8.10.0(eslint@8.57.0): resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true @@ -6319,6 +6875,22 @@ packages: - supports-color dev: false + /eslint-plugin-jest-dom@5.4.0(@testing-library/dom@10.1.0)(eslint@8.57.0): + resolution: {integrity: sha512-yBqvFsnpS5Sybjoq61cJiUsenRkC9K32hYQBFS9doBR7nbQZZ5FyO+X7MlmfM1C48Ejx/qTuOCgukDUNyzKZ7A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6', yarn: '>=1'} + peerDependencies: + '@testing-library/dom': ^8.0.0 || ^9.0.0 || ^10.0.0 + eslint: ^6.8.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + peerDependenciesMeta: + '@testing-library/dom': + optional: true + dependencies: + '@babel/runtime': 7.24.4 + '@testing-library/dom': 10.1.0 + eslint: 8.57.0 + requireindex: 1.2.0 + dev: true + /eslint-plugin-tsdoc@0.2.17: resolution: {integrity: sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==} dependencies: @@ -6533,6 +7105,22 @@ packages: strip-final-newline: 3.0.0 dev: true + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + /extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} dev: true @@ -6753,7 +7341,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: false /fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} @@ -6852,6 +7439,11 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + /get-port-please@3.1.2: resolution: {integrity: sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==} dev: true @@ -7114,6 +7706,13 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true + /html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + dependencies: + whatwg-encoding: 2.0.0 + dev: true + /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true @@ -7135,11 +7734,32 @@ packages: toidentifier: 1.0.1 dev: true + /http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /http-shutdown@1.2.2: resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: true + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} dev: true @@ -7179,6 +7799,13 @@ packages: safer-buffer: 2.1.2 dev: true + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + /idb-keyval@6.2.1: resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} dev: true @@ -7220,6 +7847,15 @@ packages: parent-module: 1.0.1 resolve-from: 4.0.0 + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -7356,6 +7992,11 @@ packages: engines: {node: '>=8'} dev: true + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} @@ -7433,6 +8074,10 @@ packages: isobject: 3.0.1 dev: true + /is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true + /is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} dependencies: @@ -7569,6 +8214,32 @@ packages: engines: {node: '>=8'} dev: true + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.24.4 + '@babel/parser': 7.24.7 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-instrument@6.0.2: + resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} + engines: {node: '>=10'} + dependencies: + '@babel/core': 7.24.4 + '@babel/parser': 7.24.7 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + /istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} @@ -7578,6 +8249,17 @@ packages: supports-color: 7.2.0 dev: true + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + /istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} @@ -7591,10 +8273,168 @@ packages: engines: {node: '>=10'} hasBin: true dependencies: - async: 3.2.5 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 + async: 3.2.5 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: true + + /jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.3 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-cli@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jest-config@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.24.4 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + babel-jest: 29.7.0(@babel/core@7.24.4) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /jest-environment-jsdom@29.7.0: + resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/jsdom': 20.0.1 + '@types/node': 20.12.7 + jest-mock: 29.7.0 + jest-util: 29.7.0 + jsdom: 20.0.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate dev: true /jest-environment-node@29.7.0: @@ -7614,6 +8454,43 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 20.12.7 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + /jest-message-util@29.7.0: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7638,6 +8515,135 @@ packages: jest-util: 29.7.0 dev: true + /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.7.0 + dev: true + + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + cjs-module-lexer: 1.3.1 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.24.4 + '@babel/generator': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.4) + '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.4) + '@babel/types': 7.24.7 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.4) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + /jest-util@29.7.0: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7662,6 +8668,20 @@ packages: pretty-format: 29.7.0 dev: true + /jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + dev: true + /jest-worker@26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} @@ -7681,6 +8701,27 @@ packages: supports-color: 8.1.1 dev: true + /jest@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /jiti@1.21.0: resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} hasBin: true @@ -7759,6 +8800,47 @@ packages: - supports-color dev: true + /jsdom@20.0.3: + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.6 + acorn: 8.11.3 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.4.3 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.0 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.10 + parse5: 7.1.2 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.13.0 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -8091,6 +9173,11 @@ packages: dependencies: yallist: 4.0.0 + /lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + dev: true + /magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} dependencies: @@ -8750,6 +9837,10 @@ packages: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} dev: true + /nwsapi@2.2.10: + resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} + dev: true + /ob1@0.80.8: resolution: {integrity: sha512-QHJQk/lXMmAW8I7AIM3in1MSlwe1umR72Chhi8B7Xnq6mzjhBKkA6Fy/zAhQnGkA4S912EPCEvTij5yh+EQTAA==} engines: {node: '>=18'} @@ -9045,6 +10136,12 @@ packages: lines-and-columns: 1.2.4 dev: true + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + dev: true + /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -9242,6 +10339,15 @@ packages: react-is: 17.0.2 dev: true + /pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -9297,6 +10403,10 @@ packages: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true + /psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + dev: true + /pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} dependencies: @@ -9308,6 +10418,10 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + /pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + dev: true + /qr-code-styling@1.6.0-rc.1: resolution: {integrity: sha512-ModRIiW6oUnsP18QzrRYZSc/CFKFKIdj7pUs57AEVH20ajlglRpN3HukjHk0UbNMTlKGuaYl7Gt6/O5Gg2NU2Q==} dependencies: @@ -9344,6 +10458,10 @@ packages: strict-uri-encode: 2.0.0 dev: true + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: true + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -9686,6 +10804,22 @@ packages: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true + /requireindex@1.2.0: + resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} + engines: {node: '>=0.10.5'} + dev: true + + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: true + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + /resolve-from@3.0.0: resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} engines: {node: '>=4'} @@ -9700,6 +10834,11 @@ packages: engines: {node: '>=8'} dev: true + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + /resolve@1.19.0: resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} dependencies: @@ -9812,6 +10951,13 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: true + /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: @@ -10072,6 +11218,13 @@ packages: atomic-sleep: 1.0.0 dev: true + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -10200,6 +11353,14 @@ packages: resolution: {integrity: sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==} dev: true + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -10275,6 +11436,11 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -10336,6 +11502,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + /symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true + /system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} @@ -10435,10 +11605,27 @@ packages: engines: {node: '>=0.6'} dev: true + /tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: true + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true + /tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.3.1 + dev: true + /trim-newlines@3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} @@ -10613,6 +11800,11 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -10772,6 +11964,11 @@ packages: engines: {node: '>= 4.0.0'} dev: true + /universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -10882,6 +12079,13 @@ packages: dependencies: punycode: 2.3.1 + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: true + /use-sync-external-store@1.2.0(react@18.3.1): resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: @@ -11027,6 +12231,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + dependencies: + xml-name-validator: 4.0.0 + dev: true + /wagmi@2.5.19(@tanstack/react-query@5.29.2)(react-native@0.73.6)(react@18.3.1)(typescript@5.4.5)(viem@2.9.16): resolution: {integrity: sha512-fy6s3qTuXpfrrghhoNXuV92yqOqJI7m/9iLIejHxEYxiddVDTR8BVdkt0BuBQZzoXSAutDkyIlJbtFcpX5dfrQ==} peerDependencies: @@ -11104,10 +12315,35 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: true + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + + /whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: true + /whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} dev: true + /whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: true + + /whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: true + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: @@ -11201,6 +12437,14 @@ packages: signal-exit: 3.0.7 dev: true + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + /ws@6.2.2: resolution: {integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==} peerDependencies: @@ -11256,6 +12500,15 @@ packages: utf-8-validate: optional: true + /xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: true + + /xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: true + /xmlhttprequest-ssl@2.0.0: resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} engines: {node: '>=0.4.0'}