From b18d63352ba079a495c49af98e70fe2b7979da91 Mon Sep 17 00:00:00 2001 From: Bonnie57 <146059114+bonnie57@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:46:00 +0800 Subject: [PATCH] Support distributing royalty token on registration (#328) (#329) * Update smart contract methods * Fix multicall request types * Update registerPilTermsAndAttach to accept array of terms * Update mintAndRegisterIpAndAttachPILTerms to accept array of terms * Update signature method * Update smart contract methods in config * Add registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens method * Fix issue about how to get license terms id * Add registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens method * Add mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens and mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens methods * Add unit for mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens and mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens methods * Fix unit tests * Refactor code * Bump up version to 1.2.0-rc.3 * Remove integration tests in test command * Update package.json --- packages/core-sdk/package.json | 2 +- packages/core-sdk/src/abi/generated.ts | 1823 ++++++++++++++++- packages/core-sdk/src/constants/common.ts | 1 + packages/core-sdk/src/index.ts | 19 +- packages/core-sdk/src/resources/group.ts | 84 +- packages/core-sdk/src/resources/ipAsset.ts | 1049 +++++++--- packages/core-sdk/src/resources/permission.ts | 4 +- .../core-sdk/src/types/resources/ipAsset.ts | 145 +- .../src/types/resources/permission.ts | 4 +- packages/core-sdk/src/utils/sign.ts | 6 +- .../core-sdk/test/integration/group.test.ts | 33 +- .../core-sdk/test/integration/ipAsset.test.ts | 421 +++- .../test/unit/resources/ipAsset.test.ts | 958 +++++++-- .../core-sdk/test/unit/utils/ipfs.test.ts | 1 - .../core-sdk/test/unit/utils/sign.test.ts | 6 +- packages/wagmi-generator/wagmi.config.ts | 16 + 16 files changed, 3822 insertions(+), 750 deletions(-) diff --git a/packages/core-sdk/package.json b/packages/core-sdk/package.json index b874c2ec..73c2ac66 100644 --- a/packages/core-sdk/package.json +++ b/packages/core-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@story-protocol/core-sdk", - "version": "1.2.0-rc.2", + "version": "1.2.0-rc.3", "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/abi/generated.ts b/packages/core-sdk/src/abi/generated.ts index 58827679..4722b808 100644 --- a/packages/core-sdk/src/abi/generated.ts +++ b/packages/core-sdk/src/abi/generated.ts @@ -4894,6 +4894,83 @@ export const licenseAttachmentWorkflowsAbi = [ ], stateMutability: "nonpayable", }, + { + type: "function", + inputs: [ + { name: "spgNftContract", internalType: "address", type: "address" }, + { name: "recipient", internalType: "address", type: "address" }, + { + name: "ipMetadata", + internalType: "struct WorkflowStructs.IPMetadata", + type: "tuple", + components: [ + { name: "ipMetadataURI", internalType: "string", type: "string" }, + { name: "ipMetadataHash", internalType: "bytes32", type: "bytes32" }, + { name: "nftMetadataURI", internalType: "string", type: "string" }, + { name: "nftMetadataHash", internalType: "bytes32", type: "bytes32" }, + ], + }, + { + name: "terms", + internalType: "struct PILTerms[]", + type: "tuple[]", + components: [ + { name: "transferable", internalType: "bool", type: "bool" }, + { name: "royaltyPolicy", internalType: "address", type: "address" }, + { + name: "defaultMintingFee", + internalType: "uint256", + type: "uint256", + }, + { name: "expiration", internalType: "uint256", type: "uint256" }, + { name: "commercialUse", internalType: "bool", type: "bool" }, + { name: "commercialAttribution", internalType: "bool", type: "bool" }, + { + name: "commercializerChecker", + internalType: "address", + type: "address", + }, + { + name: "commercializerCheckerData", + internalType: "bytes", + type: "bytes", + }, + { + name: "commercialRevShare", + internalType: "uint32", + type: "uint32", + }, + { + name: "commercialRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "derivativesAllowed", internalType: "bool", type: "bool" }, + { + name: "derivativesAttribution", + internalType: "bool", + type: "bool", + }, + { name: "derivativesApproval", internalType: "bool", type: "bool" }, + { name: "derivativesReciprocal", internalType: "bool", type: "bool" }, + { + name: "derivativeRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "currency", internalType: "address", type: "address" }, + { name: "uri", internalType: "string", type: "string" }, + ], + }, + ], + name: "mintAndRegisterIpAndAttachPILTerms", + outputs: [ + { name: "ipId", internalType: "address", type: "address" }, + { name: "tokenId", internalType: "uint256", type: "uint256" }, + { name: "licenseTermsIds", internalType: "uint256[]", type: "uint256[]" }, + ], + stateMutability: "nonpayable", + }, { type: "function", inputs: [{ name: "data", internalType: "bytes[]", type: "bytes[]" }], @@ -5004,6 +5081,173 @@ export const licenseAttachmentWorkflowsAbi = [ ], stateMutability: "nonpayable", }, + { + type: "function", + inputs: [ + { name: "nftContract", internalType: "address", type: "address" }, + { name: "tokenId", internalType: "uint256", type: "uint256" }, + { + name: "ipMetadata", + internalType: "struct WorkflowStructs.IPMetadata", + type: "tuple", + components: [ + { name: "ipMetadataURI", internalType: "string", type: "string" }, + { name: "ipMetadataHash", internalType: "bytes32", type: "bytes32" }, + { name: "nftMetadataURI", internalType: "string", type: "string" }, + { name: "nftMetadataHash", internalType: "bytes32", type: "bytes32" }, + ], + }, + { + name: "terms", + internalType: "struct PILTerms[]", + type: "tuple[]", + components: [ + { name: "transferable", internalType: "bool", type: "bool" }, + { name: "royaltyPolicy", internalType: "address", type: "address" }, + { + name: "defaultMintingFee", + internalType: "uint256", + type: "uint256", + }, + { name: "expiration", internalType: "uint256", type: "uint256" }, + { name: "commercialUse", internalType: "bool", type: "bool" }, + { name: "commercialAttribution", internalType: "bool", type: "bool" }, + { + name: "commercializerChecker", + internalType: "address", + type: "address", + }, + { + name: "commercializerCheckerData", + internalType: "bytes", + type: "bytes", + }, + { + name: "commercialRevShare", + internalType: "uint32", + type: "uint32", + }, + { + name: "commercialRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "derivativesAllowed", internalType: "bool", type: "bool" }, + { + name: "derivativesAttribution", + internalType: "bool", + type: "bool", + }, + { name: "derivativesApproval", internalType: "bool", type: "bool" }, + { name: "derivativesReciprocal", internalType: "bool", type: "bool" }, + { + name: "derivativeRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "currency", internalType: "address", type: "address" }, + { name: "uri", internalType: "string", type: "string" }, + ], + }, + { + name: "sigMetadata", + internalType: "struct WorkflowStructs.SignatureData", + type: "tuple", + components: [ + { name: "signer", internalType: "address", type: "address" }, + { name: "deadline", internalType: "uint256", type: "uint256" }, + { name: "signature", internalType: "bytes", type: "bytes" }, + ], + }, + { + name: "sigAttach", + internalType: "struct WorkflowStructs.SignatureData", + type: "tuple", + components: [ + { name: "signer", internalType: "address", type: "address" }, + { name: "deadline", internalType: "uint256", type: "uint256" }, + { name: "signature", internalType: "bytes", type: "bytes" }, + ], + }, + ], + name: "registerIpAndAttachPILTerms", + outputs: [ + { name: "ipId", internalType: "address", type: "address" }, + { name: "licenseTermsIds", internalType: "uint256[]", type: "uint256[]" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [ + { name: "ipId", internalType: "address", type: "address" }, + { + name: "terms", + internalType: "struct PILTerms[]", + type: "tuple[]", + components: [ + { name: "transferable", internalType: "bool", type: "bool" }, + { name: "royaltyPolicy", internalType: "address", type: "address" }, + { + name: "defaultMintingFee", + internalType: "uint256", + type: "uint256", + }, + { name: "expiration", internalType: "uint256", type: "uint256" }, + { name: "commercialUse", internalType: "bool", type: "bool" }, + { name: "commercialAttribution", internalType: "bool", type: "bool" }, + { + name: "commercializerChecker", + internalType: "address", + type: "address", + }, + { + name: "commercializerCheckerData", + internalType: "bytes", + type: "bytes", + }, + { + name: "commercialRevShare", + internalType: "uint32", + type: "uint32", + }, + { + name: "commercialRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "derivativesAllowed", internalType: "bool", type: "bool" }, + { + name: "derivativesAttribution", + internalType: "bool", + type: "bool", + }, + { name: "derivativesApproval", internalType: "bool", type: "bool" }, + { name: "derivativesReciprocal", internalType: "bool", type: "bool" }, + { + name: "derivativeRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "currency", internalType: "address", type: "address" }, + { name: "uri", internalType: "string", type: "string" }, + ], + }, + { + name: "sigAttach", + internalType: "struct WorkflowStructs.SignatureData", + type: "tuple", + components: [ + { name: "signer", internalType: "address", type: "address" }, + { name: "deadline", internalType: "uint256", type: "uint256" }, + { name: "signature", internalType: "bytes", type: "bytes" }, + ], + }, + ], + name: "registerPILTermsAndAttach", + outputs: [{ name: "licenseTermsIds", internalType: "uint256[]", type: "uint256[]" }], + stateMutability: "nonpayable", + }, { type: "function", inputs: [ @@ -10443,16 +10687,24 @@ export const royaltyPolicyLrpConfig = { } as const; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// RoyaltyWorkflows +// RoyaltyTokenDistributionWorkflows ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * */ -export const royaltyWorkflowsAbi = [ +export const royaltyTokenDistributionWorkflowsAbi = [ { type: "constructor", - inputs: [{ name: "royaltyModule", internalType: "address", type: "address" }], + inputs: [ + { name: "accessController", internalType: "address", type: "address" }, + { name: "coreMetadataModule", internalType: "address", type: "address" }, + { name: "ipAssetRegistry", internalType: "address", type: "address" }, + { name: "licenseRegistry", internalType: "address", type: "address" }, + { name: "licensingModule", internalType: "address", type: "address" }, + { name: "pilTemplate", internalType: "address", type: "address" }, + { name: "royaltyModule", internalType: "address", type: "address" }, + ], stateMutability: "nonpayable", }, { @@ -10480,8 +10732,593 @@ export const royaltyWorkflowsAbi = [ }, { type: "error", - inputs: [{ name: "implementation", internalType: "address", type: "address" }], - name: "ERC1967InvalidImplementation", + inputs: [{ name: "account", internalType: "address", type: "address" }], + name: "AddressInsufficientBalance", + }, + { + type: "error", + inputs: [{ name: "implementation", internalType: "address", type: "address" }], + name: "ERC1967InvalidImplementation", + }, + { type: "error", inputs: [], name: "ERC1967NonPayable" }, + { type: "error", inputs: [], name: "FailedInnerCall" }, + { type: "error", inputs: [], name: "InvalidInitialization" }, + { type: "error", inputs: [], name: "NotInitializing" }, + { + type: "error", + inputs: [], + name: "RoyaltyTokenDistributionWorkflows__RoyaltyVaultNotDeployed", + }, + { + type: "error", + inputs: [], + name: "RoyaltyTokenDistributionWorkflows__TotalPercentagesExceeds100Percent", + }, + { + type: "error", + inputs: [], + name: "RoyaltyTokenDistributionWorkflows__ZeroAddressParam", + }, + { + type: "error", + inputs: [{ name: "token", internalType: "address", type: "address" }], + name: "SafeERC20FailedOperation", + }, + { type: "error", inputs: [], name: "UUPSUnauthorizedCallContext" }, + { + type: "error", + inputs: [{ name: "slot", internalType: "bytes32", type: "bytes32" }], + name: "UUPSUnsupportedProxiableUUID", + }, + { type: "error", inputs: [], name: "Workflow__CallerNotAuthorizedToMint" }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "authority", + internalType: "address", + type: "address", + indexed: false, + }, + ], + name: "AuthorityUpdated", + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "version", + internalType: "uint64", + type: "uint64", + indexed: false, + }, + ], + name: "Initialized", + }, + { + type: "event", + anonymous: false, + inputs: [ + { + name: "implementation", + internalType: "address", + type: "address", + indexed: true, + }, + ], + name: "Upgraded", + }, + { + type: "function", + inputs: [], + name: "ACCESS_CONTROLLER", + outputs: [{ name: "", internalType: "contract IAccessController", type: "address" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [], + name: "CORE_METADATA_MODULE", + outputs: [ + { + name: "", + internalType: "contract ICoreMetadataModule", + type: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + inputs: [], + name: "IP_ASSET_REGISTRY", + outputs: [{ name: "", internalType: "contract IIPAssetRegistry", type: "address" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [], + name: "LICENSE_REGISTRY", + outputs: [{ name: "", internalType: "contract ILicenseRegistry", type: "address" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [], + name: "LICENSING_MODULE", + outputs: [{ name: "", internalType: "contract ILicensingModule", type: "address" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [], + name: "PIL_TEMPLATE", + outputs: [ + { + name: "", + internalType: "contract IPILicenseTemplate", + type: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + inputs: [], + name: "ROYALTY_MODULE", + outputs: [{ name: "", internalType: "contract IRoyaltyModule", type: "address" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [], + name: "UPGRADE_INTERFACE_VERSION", + outputs: [{ name: "", internalType: "string", type: "string" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [], + name: "authority", + outputs: [{ name: "", internalType: "address", type: "address" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [ + { name: "ipId", internalType: "address", type: "address" }, + { name: "ipRoyaltyVault", internalType: "address", type: "address" }, + { + name: "royaltyShares", + internalType: "struct WorkflowStructs.RoyaltyShare[]", + type: "tuple[]", + components: [ + { name: "author", internalType: "address", type: "address" }, + { name: "percentage", internalType: "uint32", type: "uint32" }, + ], + }, + { + name: "sigApproveRoyaltyTokens", + internalType: "struct WorkflowStructs.SignatureData", + type: "tuple", + components: [ + { name: "signer", internalType: "address", type: "address" }, + { name: "deadline", internalType: "uint256", type: "uint256" }, + { name: "signature", internalType: "bytes", type: "bytes" }, + ], + }, + ], + name: "distributeRoyaltyTokens", + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [{ name: "accessManager", internalType: "address", type: "address" }], + name: "initialize", + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [], + name: "isConsumingScheduledOp", + outputs: [{ name: "", internalType: "bytes4", type: "bytes4" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [ + { name: "spgNftContract", internalType: "address", type: "address" }, + { name: "recipient", internalType: "address", type: "address" }, + { + name: "ipMetadata", + internalType: "struct WorkflowStructs.IPMetadata", + type: "tuple", + components: [ + { name: "ipMetadataURI", internalType: "string", type: "string" }, + { name: "ipMetadataHash", internalType: "bytes32", type: "bytes32" }, + { name: "nftMetadataURI", internalType: "string", type: "string" }, + { name: "nftMetadataHash", internalType: "bytes32", type: "bytes32" }, + ], + }, + { + name: "terms", + internalType: "struct PILTerms", + type: "tuple", + components: [ + { name: "transferable", internalType: "bool", type: "bool" }, + { name: "royaltyPolicy", internalType: "address", type: "address" }, + { + name: "defaultMintingFee", + internalType: "uint256", + type: "uint256", + }, + { name: "expiration", internalType: "uint256", type: "uint256" }, + { name: "commercialUse", internalType: "bool", type: "bool" }, + { name: "commercialAttribution", internalType: "bool", type: "bool" }, + { + name: "commercializerChecker", + internalType: "address", + type: "address", + }, + { + name: "commercializerCheckerData", + internalType: "bytes", + type: "bytes", + }, + { + name: "commercialRevShare", + internalType: "uint32", + type: "uint32", + }, + { + name: "commercialRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "derivativesAllowed", internalType: "bool", type: "bool" }, + { + name: "derivativesAttribution", + internalType: "bool", + type: "bool", + }, + { name: "derivativesApproval", internalType: "bool", type: "bool" }, + { name: "derivativesReciprocal", internalType: "bool", type: "bool" }, + { + name: "derivativeRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "currency", internalType: "address", type: "address" }, + { name: "uri", internalType: "string", type: "string" }, + ], + }, + { + name: "royaltyShares", + internalType: "struct WorkflowStructs.RoyaltyShare[]", + type: "tuple[]", + components: [ + { name: "author", internalType: "address", type: "address" }, + { name: "percentage", internalType: "uint32", type: "uint32" }, + ], + }, + ], + name: "mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens", + outputs: [ + { name: "ipId", internalType: "address", type: "address" }, + { name: "tokenId", internalType: "uint256", type: "uint256" }, + { name: "licenseTermsId", internalType: "uint256", type: "uint256" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [ + { name: "spgNftContract", internalType: "address", type: "address" }, + { name: "recipient", internalType: "address", type: "address" }, + { + name: "ipMetadata", + internalType: "struct WorkflowStructs.IPMetadata", + type: "tuple", + components: [ + { name: "ipMetadataURI", internalType: "string", type: "string" }, + { name: "ipMetadataHash", internalType: "bytes32", type: "bytes32" }, + { name: "nftMetadataURI", internalType: "string", type: "string" }, + { name: "nftMetadataHash", internalType: "bytes32", type: "bytes32" }, + ], + }, + { + name: "derivData", + internalType: "struct WorkflowStructs.MakeDerivative", + type: "tuple", + components: [ + { name: "parentIpIds", internalType: "address[]", type: "address[]" }, + { name: "licenseTemplate", internalType: "address", type: "address" }, + { + name: "licenseTermsIds", + internalType: "uint256[]", + type: "uint256[]", + }, + { name: "royaltyContext", internalType: "bytes", type: "bytes" }, + ], + }, + { + name: "royaltyShares", + internalType: "struct WorkflowStructs.RoyaltyShare[]", + type: "tuple[]", + components: [ + { name: "author", internalType: "address", type: "address" }, + { name: "percentage", internalType: "uint32", type: "uint32" }, + ], + }, + ], + name: "mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens", + outputs: [ + { name: "ipId", internalType: "address", type: "address" }, + { name: "tokenId", internalType: "uint256", type: "uint256" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [{ name: "data", internalType: "bytes[]", type: "bytes[]" }], + name: "multicall", + outputs: [{ name: "results", internalType: "bytes[]", type: "bytes[]" }], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [], + name: "proxiableUUID", + outputs: [{ name: "", internalType: "bytes32", type: "bytes32" }], + stateMutability: "view", + }, + { + type: "function", + inputs: [ + { name: "nftContract", internalType: "address", type: "address" }, + { name: "tokenId", internalType: "uint256", type: "uint256" }, + { + name: "ipMetadata", + internalType: "struct WorkflowStructs.IPMetadata", + type: "tuple", + components: [ + { name: "ipMetadataURI", internalType: "string", type: "string" }, + { name: "ipMetadataHash", internalType: "bytes32", type: "bytes32" }, + { name: "nftMetadataURI", internalType: "string", type: "string" }, + { name: "nftMetadataHash", internalType: "bytes32", type: "bytes32" }, + ], + }, + { + name: "terms", + internalType: "struct PILTerms", + type: "tuple", + components: [ + { name: "transferable", internalType: "bool", type: "bool" }, + { name: "royaltyPolicy", internalType: "address", type: "address" }, + { + name: "defaultMintingFee", + internalType: "uint256", + type: "uint256", + }, + { name: "expiration", internalType: "uint256", type: "uint256" }, + { name: "commercialUse", internalType: "bool", type: "bool" }, + { name: "commercialAttribution", internalType: "bool", type: "bool" }, + { + name: "commercializerChecker", + internalType: "address", + type: "address", + }, + { + name: "commercializerCheckerData", + internalType: "bytes", + type: "bytes", + }, + { + name: "commercialRevShare", + internalType: "uint32", + type: "uint32", + }, + { + name: "commercialRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "derivativesAllowed", internalType: "bool", type: "bool" }, + { + name: "derivativesAttribution", + internalType: "bool", + type: "bool", + }, + { name: "derivativesApproval", internalType: "bool", type: "bool" }, + { name: "derivativesReciprocal", internalType: "bool", type: "bool" }, + { + name: "derivativeRevCeiling", + internalType: "uint256", + type: "uint256", + }, + { name: "currency", internalType: "address", type: "address" }, + { name: "uri", internalType: "string", type: "string" }, + ], + }, + { + name: "sigMetadata", + internalType: "struct WorkflowStructs.SignatureData", + type: "tuple", + components: [ + { name: "signer", internalType: "address", type: "address" }, + { name: "deadline", internalType: "uint256", type: "uint256" }, + { name: "signature", internalType: "bytes", type: "bytes" }, + ], + }, + { + name: "sigAttach", + internalType: "struct WorkflowStructs.SignatureData", + type: "tuple", + components: [ + { name: "signer", internalType: "address", type: "address" }, + { name: "deadline", internalType: "uint256", type: "uint256" }, + { name: "signature", internalType: "bytes", type: "bytes" }, + ], + }, + ], + name: "registerIpAndAttachPILTermsAndDeployRoyaltyVault", + outputs: [ + { name: "ipId", internalType: "address", type: "address" }, + { name: "licenseTermsId", internalType: "uint256", type: "uint256" }, + { name: "ipRoyaltyVault", internalType: "address", type: "address" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [ + { name: "nftContract", internalType: "address", type: "address" }, + { name: "tokenId", internalType: "uint256", type: "uint256" }, + { + name: "ipMetadata", + internalType: "struct WorkflowStructs.IPMetadata", + type: "tuple", + components: [ + { name: "ipMetadataURI", internalType: "string", type: "string" }, + { name: "ipMetadataHash", internalType: "bytes32", type: "bytes32" }, + { name: "nftMetadataURI", internalType: "string", type: "string" }, + { name: "nftMetadataHash", internalType: "bytes32", type: "bytes32" }, + ], + }, + { + name: "derivData", + internalType: "struct WorkflowStructs.MakeDerivative", + type: "tuple", + components: [ + { name: "parentIpIds", internalType: "address[]", type: "address[]" }, + { name: "licenseTemplate", internalType: "address", type: "address" }, + { + name: "licenseTermsIds", + internalType: "uint256[]", + type: "uint256[]", + }, + { name: "royaltyContext", internalType: "bytes", type: "bytes" }, + ], + }, + { + name: "sigMetadata", + internalType: "struct WorkflowStructs.SignatureData", + type: "tuple", + components: [ + { name: "signer", internalType: "address", type: "address" }, + { name: "deadline", internalType: "uint256", type: "uint256" }, + { name: "signature", internalType: "bytes", type: "bytes" }, + ], + }, + { + name: "sigRegister", + internalType: "struct WorkflowStructs.SignatureData", + type: "tuple", + components: [ + { name: "signer", internalType: "address", type: "address" }, + { name: "deadline", internalType: "uint256", type: "uint256" }, + { name: "signature", internalType: "bytes", type: "bytes" }, + ], + }, + ], + name: "registerIpAndMakeDerivativeAndDeployRoyaltyVault", + outputs: [ + { name: "ipId", internalType: "address", type: "address" }, + { name: "ipRoyaltyVault", internalType: "address", type: "address" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [{ name: "newAuthority", internalType: "address", type: "address" }], + name: "setAuthority", + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [ + { + name: "newNftContractBeacon", + internalType: "address", + type: "address", + }, + ], + name: "setNftContractBeacon", + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + inputs: [ + { name: "newImplementation", internalType: "address", type: "address" }, + { name: "data", internalType: "bytes", type: "bytes" }, + ], + name: "upgradeToAndCall", + outputs: [], + stateMutability: "payable", + }, +] as const; + +/** + * + */ +export const royaltyTokenDistributionWorkflowsAddress = { + 1516: "0x39D9C7a23AA9e33E06aAAf51ebaDd11342b5be50", +} as const; + +/** + * + */ +export const royaltyTokenDistributionWorkflowsConfig = { + address: royaltyTokenDistributionWorkflowsAddress, + abi: royaltyTokenDistributionWorkflowsAbi, +} as const; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// RoyaltyWorkflows +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * + */ +export const royaltyWorkflowsAbi = [ + { + type: "constructor", + inputs: [{ name: "royaltyModule", internalType: "address", type: "address" }], + stateMutability: "nonpayable", + }, + { + type: "error", + inputs: [{ name: "authority", internalType: "address", type: "address" }], + name: "AccessManagedInvalidAuthority", + }, + { + type: "error", + inputs: [ + { name: "caller", internalType: "address", type: "address" }, + { name: "delay", internalType: "uint32", type: "uint32" }, + ], + name: "AccessManagedRequiredDelay", + }, + { + type: "error", + inputs: [{ name: "caller", internalType: "address", type: "address" }], + name: "AccessManagedUnauthorized", + }, + { + type: "error", + inputs: [{ name: "target", internalType: "address", type: "address" }], + name: "AddressEmptyCode", + }, + { + type: "error", + inputs: [{ name: "implementation", internalType: "address", type: "address" }], + name: "ERC1967InvalidImplementation", }, { type: "error", inputs: [], name: "ERC1967NonPayable" }, { type: "error", inputs: [], name: "FailedInnerCall" }, @@ -16444,6 +17281,17 @@ export type IpRoyaltyVaultImplSnapshotCompletedEvent = { snapshotTimestamp: bigint; }; +/** + * IpRoyaltyVaultImplBalanceOfRequest + * + * @param account address + */ +export type IpRoyaltyVaultImplBalanceOfRequest = { + account: Address; +}; + +export type IpRoyaltyVaultImplBalanceOfResponse = bigint; + /** * IpRoyaltyVaultImplClaimableRevenueRequest * @@ -16577,6 +17425,23 @@ export class IpRoyaltyVaultImplReadOnlyClient extends IpRoyaltyVaultImplEventCli super(rpcClient, address); } + /** + * method balanceOf for contract IpRoyaltyVaultImpl + * + * @param request IpRoyaltyVaultImplBalanceOfRequest + * @return Promise + */ + public async balanceOf( + request: IpRoyaltyVaultImplBalanceOfRequest, + ): Promise { + return await this.rpcClient.readContract({ + abi: ipRoyaltyVaultImplAbi, + address: this.address, + functionName: "balanceOf", + args: [request.account], + }); + } + /** * method claimableRevenue for contract IpRoyaltyVaultImpl * @@ -16731,6 +17596,44 @@ export type LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTermsRequest }; }; +/** + * LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTerms2Request + * + * @param spgNftContract address + * @param recipient address + * @param ipMetadata tuple + * @param terms tuple[] + */ +export type LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTerms2Request = { + spgNftContract: Address; + recipient: Address; + ipMetadata: { + ipMetadataURI: string; + ipMetadataHash: Hex; + nftMetadataURI: string; + nftMetadataHash: Hex; + }; + terms: { + transferable: boolean; + royaltyPolicy: Address; + defaultMintingFee: bigint; + expiration: bigint; + commercialUse: boolean; + commercialAttribution: boolean; + commercializerChecker: Address; + commercializerCheckerData: Hex; + commercialRevShare: number; + commercialRevCeiling: bigint; + derivativesAllowed: boolean; + derivativesAttribution: boolean; + derivativesApproval: boolean; + derivativesReciprocal: boolean; + derivativeRevCeiling: bigint; + currency: Address; + uri: string; + }[]; +}; + /** * LicenseAttachmentWorkflowsMulticallRequest * @@ -16790,14 +17693,99 @@ export type LicenseAttachmentWorkflowsRegisterIpAndAttachPilTermsRequest = { }; }; +/** + * LicenseAttachmentWorkflowsRegisterIpAndAttachPilTerms2Request + * + * @param nftContract address + * @param tokenId uint256 + * @param ipMetadata tuple + * @param terms tuple[] + * @param sigMetadata tuple + * @param sigAttach tuple + */ +export type LicenseAttachmentWorkflowsRegisterIpAndAttachPilTerms2Request = { + nftContract: Address; + tokenId: bigint; + ipMetadata: { + ipMetadataURI: string; + ipMetadataHash: Hex; + nftMetadataURI: string; + nftMetadataHash: Hex; + }; + terms: { + transferable: boolean; + royaltyPolicy: Address; + defaultMintingFee: bigint; + expiration: bigint; + commercialUse: boolean; + commercialAttribution: boolean; + commercializerChecker: Address; + commercializerCheckerData: Hex; + commercialRevShare: number; + commercialRevCeiling: bigint; + derivativesAllowed: boolean; + derivativesAttribution: boolean; + derivativesApproval: boolean; + derivativesReciprocal: boolean; + derivativeRevCeiling: bigint; + currency: Address; + uri: string; + }[]; + sigMetadata: { + signer: Address; + deadline: bigint; + signature: Hex; + }; + sigAttach: { + signer: Address; + deadline: bigint; + signature: Hex; + }; +}; + /** * LicenseAttachmentWorkflowsRegisterPilTermsAndAttachRequest * * @param ipId address - * @param terms tuple + * @param terms tuple[] * @param sigAttach tuple */ export type LicenseAttachmentWorkflowsRegisterPilTermsAndAttachRequest = { + ipId: Address; + terms: { + transferable: boolean; + royaltyPolicy: Address; + defaultMintingFee: bigint; + expiration: bigint; + commercialUse: boolean; + commercialAttribution: boolean; + commercializerChecker: Address; + commercializerCheckerData: Hex; + commercialRevShare: number; + commercialRevCeiling: bigint; + derivativesAllowed: boolean; + derivativesAttribution: boolean; + derivativesApproval: boolean; + derivativesReciprocal: boolean; + derivativeRevCeiling: bigint; + currency: Address; + uri: string; + }[]; + sigAttach: { + signer: Address; + deadline: bigint; + signature: Hex; + }; +}; + +/** + * LicenseAttachmentWorkflowsRegisterPilTermsAndAttach2Request + * + * @param ipId address + * @param terms tuple + * @param sigAttach tuple + */ +export type LicenseAttachmentWorkflowsRegisterPilTermsAndAttach2Request = { ipId: Address; terms: { transferable: boolean; @@ -16877,6 +17865,44 @@ export class LicenseAttachmentWorkflowsClient { }; } + /** + * method mintAndRegisterIpAndAttachPILTerms for contract LicenseAttachmentWorkflows + * + * @param request LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTerms2Request + * @return Promise + */ + public async mintAndRegisterIpAndAttachPilTerms2( + request: LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTerms2Request, + ): Promise { + const { request: call } = await this.rpcClient.simulateContract({ + abi: licenseAttachmentWorkflowsAbi, + address: this.address, + functionName: "mintAndRegisterIpAndAttachPILTerms", + account: this.wallet.account, + args: [request.spgNftContract, request.recipient, request.ipMetadata, request.terms], + }); + return await this.wallet.writeContract(call as WriteContractParameters); + } + + /** + * method mintAndRegisterIpAndAttachPILTerms for contract LicenseAttachmentWorkflows with only encode + * + * @param request LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTerms2Request + * @return EncodedTxData + */ + public mintAndRegisterIpAndAttachPilTerms2Encode( + request: LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTerms2Request, + ): EncodedTxData { + return { + to: this.address, + data: encodeFunctionData({ + abi: licenseAttachmentWorkflowsAbi, + functionName: "mintAndRegisterIpAndAttachPILTerms", + args: [request.spgNftContract, request.recipient, request.ipMetadata, request.terms], + }), + }; + } + /** * method multicall for contract LicenseAttachmentWorkflows * @@ -16942,11 +17968,63 @@ export class LicenseAttachmentWorkflowsClient { /** * method registerIpAndAttachPILTerms for contract LicenseAttachmentWorkflows with only encode * - * @param request LicenseAttachmentWorkflowsRegisterIpAndAttachPilTermsRequest + * @param request LicenseAttachmentWorkflowsRegisterIpAndAttachPilTermsRequest + * @return EncodedTxData + */ + public registerIpAndAttachPilTermsEncode( + request: LicenseAttachmentWorkflowsRegisterIpAndAttachPilTermsRequest, + ): EncodedTxData { + return { + to: this.address, + data: encodeFunctionData({ + abi: licenseAttachmentWorkflowsAbi, + functionName: "registerIpAndAttachPILTerms", + args: [ + request.nftContract, + request.tokenId, + request.ipMetadata, + request.terms, + request.sigMetadata, + request.sigAttach, + ], + }), + }; + } + + /** + * method registerIpAndAttachPILTerms for contract LicenseAttachmentWorkflows + * + * @param request LicenseAttachmentWorkflowsRegisterIpAndAttachPilTerms2Request + * @return Promise + */ + public async registerIpAndAttachPilTerms2( + request: LicenseAttachmentWorkflowsRegisterIpAndAttachPilTerms2Request, + ): Promise { + const { request: call } = await this.rpcClient.simulateContract({ + abi: licenseAttachmentWorkflowsAbi, + address: this.address, + functionName: "registerIpAndAttachPILTerms", + account: this.wallet.account, + args: [ + request.nftContract, + request.tokenId, + request.ipMetadata, + request.terms, + request.sigMetadata, + request.sigAttach, + ], + }); + return await this.wallet.writeContract(call as WriteContractParameters); + } + + /** + * method registerIpAndAttachPILTerms for contract LicenseAttachmentWorkflows with only encode + * + * @param request LicenseAttachmentWorkflowsRegisterIpAndAttachPilTerms2Request * @return EncodedTxData */ - public registerIpAndAttachPilTermsEncode( - request: LicenseAttachmentWorkflowsRegisterIpAndAttachPilTermsRequest, + public registerIpAndAttachPilTerms2Encode( + request: LicenseAttachmentWorkflowsRegisterIpAndAttachPilTerms2Request, ): EncodedTxData { return { to: this.address, @@ -17002,6 +18080,44 @@ export class LicenseAttachmentWorkflowsClient { }), }; } + + /** + * method registerPILTermsAndAttach for contract LicenseAttachmentWorkflows + * + * @param request LicenseAttachmentWorkflowsRegisterPilTermsAndAttach2Request + * @return Promise + */ + public async registerPilTermsAndAttach2( + request: LicenseAttachmentWorkflowsRegisterPilTermsAndAttach2Request, + ): Promise { + const { request: call } = await this.rpcClient.simulateContract({ + abi: licenseAttachmentWorkflowsAbi, + address: this.address, + functionName: "registerPILTermsAndAttach", + account: this.wallet.account, + args: [request.ipId, request.terms, request.sigAttach], + }); + return await this.wallet.writeContract(call as WriteContractParameters); + } + + /** + * method registerPILTermsAndAttach for contract LicenseAttachmentWorkflows with only encode + * + * @param request LicenseAttachmentWorkflowsRegisterPilTermsAndAttach2Request + * @return EncodedTxData + */ + public registerPilTermsAndAttach2Encode( + request: LicenseAttachmentWorkflowsRegisterPilTermsAndAttach2Request, + ): EncodedTxData { + return { + to: this.address, + data: encodeFunctionData({ + abi: licenseAttachmentWorkflowsAbi, + functionName: "registerPILTermsAndAttach", + args: [request.ipId, request.terms, request.sigAttach], + }), + }; + } } // Contract LicenseRegistry ============================================================= @@ -21442,6 +22558,17 @@ export class RegistrationWorkflowsClient extends RegistrationWorkflowsEventClien // Contract RoyaltyModule ============================================================= +/** + * RoyaltyModuleIpRoyaltyVaultDeployedEvent + * + * @param ipId address + * @param ipRoyaltyVault address + */ +export type RoyaltyModuleIpRoyaltyVaultDeployedEvent = { + ipId: Address; + ipRoyaltyVault: Address; +}; + /** * RoyaltyModuleIpRoyaltyVaultsRequest * @@ -21491,9 +22618,9 @@ export type RoyaltyModulePayRoyaltyOnBehalfRequest = { }; /** - * contract RoyaltyModule readonly method + * contract RoyaltyModule event */ -export class RoyaltyModuleReadOnlyClient { +export class RoyaltyModuleEventClient { protected readonly rpcClient: PublicClient; public readonly address: Address; @@ -21502,6 +22629,56 @@ export class RoyaltyModuleReadOnlyClient { this.rpcClient = rpcClient; } + /** + * event IpRoyaltyVaultDeployed for contract RoyaltyModule + */ + public watchIpRoyaltyVaultDeployedEvent( + onLogs: (txHash: Hex, ev: Partial) => void, + ): WatchContractEventReturnType { + return this.rpcClient.watchContractEvent({ + abi: royaltyModuleAbi, + address: this.address, + eventName: "IpRoyaltyVaultDeployed", + onLogs: (evs) => { + evs.forEach((it) => onLogs(it.transactionHash, it.args)); + }, + }); + } + + /** + * parse tx receipt event IpRoyaltyVaultDeployed for contract RoyaltyModule + */ + public parseTxIpRoyaltyVaultDeployedEvent( + txReceipt: TransactionReceipt, + ): Array { + const targetLogs: Array = []; + for (const log of txReceipt.logs) { + try { + const event = decodeEventLog({ + abi: royaltyModuleAbi, + eventName: "IpRoyaltyVaultDeployed", + data: log.data, + topics: log.topics, + }); + if (event.eventName === "IpRoyaltyVaultDeployed") { + targetLogs.push(event.args); + } + } catch (e) { + /* empty */ + } + } + return targetLogs; + } +} + +/** + * contract RoyaltyModule readonly method + */ +export class RoyaltyModuleReadOnlyClient extends RoyaltyModuleEventClient { + constructor(rpcClient: PublicClient, address?: Address) { + super(rpcClient, address); + } + /** * method ipRoyaltyVaults for contract RoyaltyModule * @@ -22351,252 +23528,708 @@ export class RoyaltyPolicyLrpClient extends RoyaltyPolicyLrpReadOnlyClient { to: this.address, data: encodeFunctionData({ abi: royaltyPolicyLrpAbi, - functionName: "initialize", - args: [request.accessManager], + functionName: "initialize", + args: [request.accessManager], + }), + }; + } + + /** + * method onLicenseMinting for contract RoyaltyPolicyLRP + * + * @param request RoyaltyPolicyLrpOnLicenseMintingRequest + * @return Promise + */ + public async onLicenseMinting( + request: RoyaltyPolicyLrpOnLicenseMintingRequest, + ): Promise { + const { request: call } = await this.rpcClient.simulateContract({ + abi: royaltyPolicyLrpAbi, + address: this.address, + functionName: "onLicenseMinting", + account: this.wallet.account, + args: [request[0], request[1], request[2]], + }); + return await this.wallet.writeContract(call as WriteContractParameters); + } + + /** + * method onLicenseMinting for contract RoyaltyPolicyLRP with only encode + * + * @param request RoyaltyPolicyLrpOnLicenseMintingRequest + * @return EncodedTxData + */ + public onLicenseMintingEncode(request: RoyaltyPolicyLrpOnLicenseMintingRequest): EncodedTxData { + return { + to: this.address, + data: encodeFunctionData({ + abi: royaltyPolicyLrpAbi, + functionName: "onLicenseMinting", + args: [request[0], request[1], request[2]], + }), + }; + } + + /** + * method onLinkToParents for contract RoyaltyPolicyLRP + * + * @param request RoyaltyPolicyLrpOnLinkToParentsRequest + * @return Promise + */ + public async onLinkToParents( + request: RoyaltyPolicyLrpOnLinkToParentsRequest, + ): Promise { + const { request: call } = await this.rpcClient.simulateContract({ + abi: royaltyPolicyLrpAbi, + address: this.address, + functionName: "onLinkToParents", + account: this.wallet.account, + args: [request[0], request[1], request[2], request[3], request[4]], + }); + return await this.wallet.writeContract(call as WriteContractParameters); + } + + /** + * method onLinkToParents for contract RoyaltyPolicyLRP with only encode + * + * @param request RoyaltyPolicyLrpOnLinkToParentsRequest + * @return EncodedTxData + */ + public onLinkToParentsEncode(request: RoyaltyPolicyLrpOnLinkToParentsRequest): EncodedTxData { + return { + to: this.address, + data: encodeFunctionData({ + abi: royaltyPolicyLrpAbi, + functionName: "onLinkToParents", + args: [request[0], request[1], request[2], request[3], request[4]], + }), + }; + } + + /** + * method pause for contract RoyaltyPolicyLRP + * + * @param request RoyaltyPolicyLrpPauseRequest + * @return Promise + */ + public async pause(): Promise { + const { request: call } = await this.rpcClient.simulateContract({ + abi: royaltyPolicyLrpAbi, + address: this.address, + functionName: "pause", + account: this.wallet.account, + }); + return await this.wallet.writeContract(call as WriteContractParameters); + } + + /** + * method pause for contract RoyaltyPolicyLRP with only encode + * + * @param request RoyaltyPolicyLrpPauseRequest + * @return EncodedTxData + */ + public pauseEncode(): EncodedTxData { + return { + to: this.address, + data: encodeFunctionData({ + abi: royaltyPolicyLrpAbi, + functionName: "pause", + }), + }; + } + + /** + * method setAuthority for contract RoyaltyPolicyLRP + * + * @param request RoyaltyPolicyLrpSetAuthorityRequest + * @return Promise + */ + public async setAuthority( + request: RoyaltyPolicyLrpSetAuthorityRequest, + ): Promise { + const { request: call } = await this.rpcClient.simulateContract({ + abi: royaltyPolicyLrpAbi, + address: this.address, + functionName: "setAuthority", + account: this.wallet.account, + args: [request.newAuthority], + }); + return await this.wallet.writeContract(call as WriteContractParameters); + } + + /** + * method setAuthority for contract RoyaltyPolicyLRP with only encode + * + * @param request RoyaltyPolicyLrpSetAuthorityRequest + * @return EncodedTxData + */ + public setAuthorityEncode(request: RoyaltyPolicyLrpSetAuthorityRequest): EncodedTxData { + return { + to: this.address, + data: encodeFunctionData({ + abi: royaltyPolicyLrpAbi, + functionName: "setAuthority", + args: [request.newAuthority], + }), + }; + } + + /** + * method transferToVault for contract RoyaltyPolicyLRP + * + * @param request RoyaltyPolicyLrpTransferToVaultRequest + * @return Promise + */ + public async transferToVault( + request: RoyaltyPolicyLrpTransferToVaultRequest, + ): Promise { + const { request: call } = await this.rpcClient.simulateContract({ + abi: royaltyPolicyLrpAbi, + address: this.address, + functionName: "transferToVault", + account: this.wallet.account, + args: [request.ipId, request.ancestorIpId, request.token, request.amount], + }); + return await this.wallet.writeContract(call as WriteContractParameters); + } + + /** + * method transferToVault for contract RoyaltyPolicyLRP with only encode + * + * @param request RoyaltyPolicyLrpTransferToVaultRequest + * @return EncodedTxData + */ + public transferToVaultEncode(request: RoyaltyPolicyLrpTransferToVaultRequest): EncodedTxData { + return { + to: this.address, + data: encodeFunctionData({ + abi: royaltyPolicyLrpAbi, + functionName: "transferToVault", + args: [request.ipId, request.ancestorIpId, request.token, request.amount], }), }; } /** - * method onLicenseMinting for contract RoyaltyPolicyLRP + * method unpause for contract RoyaltyPolicyLRP * - * @param request RoyaltyPolicyLrpOnLicenseMintingRequest + * @param request RoyaltyPolicyLrpUnpauseRequest * @return Promise */ - public async onLicenseMinting( - request: RoyaltyPolicyLrpOnLicenseMintingRequest, - ): Promise { + public async unpause(): Promise { const { request: call } = await this.rpcClient.simulateContract({ abi: royaltyPolicyLrpAbi, address: this.address, - functionName: "onLicenseMinting", + functionName: "unpause", account: this.wallet.account, - args: [request[0], request[1], request[2]], }); return await this.wallet.writeContract(call as WriteContractParameters); } /** - * method onLicenseMinting for contract RoyaltyPolicyLRP with only encode + * method unpause for contract RoyaltyPolicyLRP with only encode * - * @param request RoyaltyPolicyLrpOnLicenseMintingRequest + * @param request RoyaltyPolicyLrpUnpauseRequest * @return EncodedTxData */ - public onLicenseMintingEncode(request: RoyaltyPolicyLrpOnLicenseMintingRequest): EncodedTxData { + public unpauseEncode(): EncodedTxData { return { to: this.address, data: encodeFunctionData({ abi: royaltyPolicyLrpAbi, - functionName: "onLicenseMinting", - args: [request[0], request[1], request[2]], + functionName: "unpause", }), }; } /** - * method onLinkToParents for contract RoyaltyPolicyLRP + * method upgradeToAndCall for contract RoyaltyPolicyLRP * - * @param request RoyaltyPolicyLrpOnLinkToParentsRequest + * @param request RoyaltyPolicyLrpUpgradeToAndCallRequest * @return Promise */ - public async onLinkToParents( - request: RoyaltyPolicyLrpOnLinkToParentsRequest, + public async upgradeToAndCall( + request: RoyaltyPolicyLrpUpgradeToAndCallRequest, ): Promise { const { request: call } = await this.rpcClient.simulateContract({ abi: royaltyPolicyLrpAbi, address: this.address, - functionName: "onLinkToParents", + functionName: "upgradeToAndCall", account: this.wallet.account, - args: [request[0], request[1], request[2], request[3], request[4]], + args: [request.newImplementation, request.data], }); return await this.wallet.writeContract(call as WriteContractParameters); } /** - * method onLinkToParents for contract RoyaltyPolicyLRP with only encode + * method upgradeToAndCall for contract RoyaltyPolicyLRP with only encode * - * @param request RoyaltyPolicyLrpOnLinkToParentsRequest + * @param request RoyaltyPolicyLrpUpgradeToAndCallRequest * @return EncodedTxData */ - public onLinkToParentsEncode(request: RoyaltyPolicyLrpOnLinkToParentsRequest): EncodedTxData { + public upgradeToAndCallEncode(request: RoyaltyPolicyLrpUpgradeToAndCallRequest): EncodedTxData { return { to: this.address, data: encodeFunctionData({ abi: royaltyPolicyLrpAbi, - functionName: "onLinkToParents", - args: [request[0], request[1], request[2], request[3], request[4]], + functionName: "upgradeToAndCall", + args: [request.newImplementation, request.data], }), }; } +} + +// Contract RoyaltyTokenDistributionWorkflows ============================================================= + +/** + * RoyaltyTokenDistributionWorkflowsDistributeRoyaltyTokensRequest + * + * @param ipId address + * @param ipRoyaltyVault address + * @param royaltyShares tuple[] + * @param sigApproveRoyaltyTokens tuple + */ +export type RoyaltyTokenDistributionWorkflowsDistributeRoyaltyTokensRequest = { + ipId: Address; + ipRoyaltyVault: Address; + royaltyShares: { + author: Address; + percentage: number; + }[]; + sigApproveRoyaltyTokens: { + signer: Address; + deadline: bigint; + signature: Hex; + }; +}; + +/** + * RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokensRequest + * + * @param spgNftContract address + * @param recipient address + * @param ipMetadata tuple + * @param terms tuple + * @param royaltyShares tuple[] + */ +export type RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokensRequest = + { + spgNftContract: Address; + recipient: Address; + ipMetadata: { + ipMetadataURI: string; + ipMetadataHash: Hex; + nftMetadataURI: string; + nftMetadataHash: Hex; + }; + terms: { + transferable: boolean; + royaltyPolicy: Address; + defaultMintingFee: bigint; + expiration: bigint; + commercialUse: boolean; + commercialAttribution: boolean; + commercializerChecker: Address; + commercializerCheckerData: Hex; + commercialRevShare: number; + commercialRevCeiling: bigint; + derivativesAllowed: boolean; + derivativesAttribution: boolean; + derivativesApproval: boolean; + derivativesReciprocal: boolean; + derivativeRevCeiling: bigint; + currency: Address; + uri: string; + }; + royaltyShares: { + author: Address; + percentage: number; + }[]; + }; + +/** + * RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest + * + * @param spgNftContract address + * @param recipient address + * @param ipMetadata tuple + * @param derivData tuple + * @param royaltyShares tuple[] + */ +export type RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest = + { + spgNftContract: Address; + recipient: Address; + ipMetadata: { + ipMetadataURI: string; + ipMetadataHash: Hex; + nftMetadataURI: string; + nftMetadataHash: Hex; + }; + derivData: { + parentIpIds: readonly Address[]; + licenseTemplate: Address; + licenseTermsIds: readonly bigint[]; + royaltyContext: Hex; + }; + royaltyShares: { + author: Address; + percentage: number; + }[]; + }; + +/** + * RoyaltyTokenDistributionWorkflowsRegisterIpAndAttachPilTermsAndDeployRoyaltyVaultRequest + * + * @param nftContract address + * @param tokenId uint256 + * @param ipMetadata tuple + * @param terms tuple + * @param sigMetadata tuple + * @param sigAttach tuple + */ +export type RoyaltyTokenDistributionWorkflowsRegisterIpAndAttachPilTermsAndDeployRoyaltyVaultRequest = + { + nftContract: Address; + tokenId: bigint; + ipMetadata: { + ipMetadataURI: string; + ipMetadataHash: Hex; + nftMetadataURI: string; + nftMetadataHash: Hex; + }; + terms: { + transferable: boolean; + royaltyPolicy: Address; + defaultMintingFee: bigint; + expiration: bigint; + commercialUse: boolean; + commercialAttribution: boolean; + commercializerChecker: Address; + commercializerCheckerData: Hex; + commercialRevShare: number; + commercialRevCeiling: bigint; + derivativesAllowed: boolean; + derivativesAttribution: boolean; + derivativesApproval: boolean; + derivativesReciprocal: boolean; + derivativeRevCeiling: bigint; + currency: Address; + uri: string; + }; + sigMetadata: { + signer: Address; + deadline: bigint; + signature: Hex; + }; + sigAttach: { + signer: Address; + deadline: bigint; + signature: Hex; + }; + }; + +/** + * RoyaltyTokenDistributionWorkflowsRegisterIpAndMakeDerivativeAndDeployRoyaltyVaultRequest + * + * @param nftContract address + * @param tokenId uint256 + * @param ipMetadata tuple + * @param derivData tuple + * @param sigMetadata tuple + * @param sigRegister tuple + */ +export type RoyaltyTokenDistributionWorkflowsRegisterIpAndMakeDerivativeAndDeployRoyaltyVaultRequest = + { + nftContract: Address; + tokenId: bigint; + ipMetadata: { + ipMetadataURI: string; + ipMetadataHash: Hex; + nftMetadataURI: string; + nftMetadataHash: Hex; + }; + derivData: { + parentIpIds: readonly Address[]; + licenseTemplate: Address; + licenseTermsIds: readonly bigint[]; + royaltyContext: Hex; + }; + sigMetadata: { + signer: Address; + deadline: bigint; + signature: Hex; + }; + sigRegister: { + signer: Address; + deadline: bigint; + signature: Hex; + }; + }; + +/** + * contract RoyaltyTokenDistributionWorkflows write method + */ +export class RoyaltyTokenDistributionWorkflowsClient { + protected readonly wallet: SimpleWalletClient; + protected readonly rpcClient: PublicClient; + public readonly address: Address; + + constructor(rpcClient: PublicClient, wallet: SimpleWalletClient, address?: Address) { + this.address = + address || getAddress(royaltyTokenDistributionWorkflowsAddress, rpcClient.chain?.id); + this.rpcClient = rpcClient; + this.wallet = wallet; + } /** - * method pause for contract RoyaltyPolicyLRP + * method distributeRoyaltyTokens for contract RoyaltyTokenDistributionWorkflows * - * @param request RoyaltyPolicyLrpPauseRequest + * @param request RoyaltyTokenDistributionWorkflowsDistributeRoyaltyTokensRequest * @return Promise */ - public async pause(): Promise { + public async distributeRoyaltyTokens( + request: RoyaltyTokenDistributionWorkflowsDistributeRoyaltyTokensRequest, + ): Promise { const { request: call } = await this.rpcClient.simulateContract({ - abi: royaltyPolicyLrpAbi, + abi: royaltyTokenDistributionWorkflowsAbi, address: this.address, - functionName: "pause", + functionName: "distributeRoyaltyTokens", account: this.wallet.account, + args: [ + request.ipId, + request.ipRoyaltyVault, + request.royaltyShares, + request.sigApproveRoyaltyTokens, + ], }); return await this.wallet.writeContract(call as WriteContractParameters); } /** - * method pause for contract RoyaltyPolicyLRP with only encode + * method distributeRoyaltyTokens for contract RoyaltyTokenDistributionWorkflows with only encode * - * @param request RoyaltyPolicyLrpPauseRequest + * @param request RoyaltyTokenDistributionWorkflowsDistributeRoyaltyTokensRequest * @return EncodedTxData */ - public pauseEncode(): EncodedTxData { + public distributeRoyaltyTokensEncode( + request: RoyaltyTokenDistributionWorkflowsDistributeRoyaltyTokensRequest, + ): EncodedTxData { return { to: this.address, data: encodeFunctionData({ - abi: royaltyPolicyLrpAbi, - functionName: "pause", + abi: royaltyTokenDistributionWorkflowsAbi, + functionName: "distributeRoyaltyTokens", + args: [ + request.ipId, + request.ipRoyaltyVault, + request.royaltyShares, + request.sigApproveRoyaltyTokens, + ], }), }; } /** - * method setAuthority for contract RoyaltyPolicyLRP + * method mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens for contract RoyaltyTokenDistributionWorkflows * - * @param request RoyaltyPolicyLrpSetAuthorityRequest + * @param request RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokensRequest * @return Promise */ - public async setAuthority( - request: RoyaltyPolicyLrpSetAuthorityRequest, + public async mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens( + request: RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokensRequest, ): Promise { const { request: call } = await this.rpcClient.simulateContract({ - abi: royaltyPolicyLrpAbi, + abi: royaltyTokenDistributionWorkflowsAbi, address: this.address, - functionName: "setAuthority", + functionName: "mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens", account: this.wallet.account, - args: [request.newAuthority], + args: [ + request.spgNftContract, + request.recipient, + request.ipMetadata, + request.terms, + request.royaltyShares, + ], }); return await this.wallet.writeContract(call as WriteContractParameters); } /** - * method setAuthority for contract RoyaltyPolicyLRP with only encode + * method mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens for contract RoyaltyTokenDistributionWorkflows with only encode * - * @param request RoyaltyPolicyLrpSetAuthorityRequest + * @param request RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokensRequest * @return EncodedTxData */ - public setAuthorityEncode(request: RoyaltyPolicyLrpSetAuthorityRequest): EncodedTxData { + public mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokensEncode( + request: RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokensRequest, + ): EncodedTxData { return { to: this.address, data: encodeFunctionData({ - abi: royaltyPolicyLrpAbi, - functionName: "setAuthority", - args: [request.newAuthority], + abi: royaltyTokenDistributionWorkflowsAbi, + functionName: "mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens", + args: [ + request.spgNftContract, + request.recipient, + request.ipMetadata, + request.terms, + request.royaltyShares, + ], }), }; } /** - * method transferToVault for contract RoyaltyPolicyLRP + * method mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens for contract RoyaltyTokenDistributionWorkflows * - * @param request RoyaltyPolicyLrpTransferToVaultRequest + * @param request RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest * @return Promise */ - public async transferToVault( - request: RoyaltyPolicyLrpTransferToVaultRequest, + public async mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens( + request: RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest, ): Promise { const { request: call } = await this.rpcClient.simulateContract({ - abi: royaltyPolicyLrpAbi, + abi: royaltyTokenDistributionWorkflowsAbi, address: this.address, - functionName: "transferToVault", + functionName: "mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens", account: this.wallet.account, - args: [request.ipId, request.ancestorIpId, request.token, request.amount], + args: [ + request.spgNftContract, + request.recipient, + request.ipMetadata, + request.derivData, + request.royaltyShares, + ], }); return await this.wallet.writeContract(call as WriteContractParameters); } /** - * method transferToVault for contract RoyaltyPolicyLRP with only encode + * method mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens for contract RoyaltyTokenDistributionWorkflows with only encode * - * @param request RoyaltyPolicyLrpTransferToVaultRequest + * @param request RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest * @return EncodedTxData */ - public transferToVaultEncode(request: RoyaltyPolicyLrpTransferToVaultRequest): EncodedTxData { + public mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensEncode( + request: RoyaltyTokenDistributionWorkflowsMintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest, + ): EncodedTxData { return { to: this.address, data: encodeFunctionData({ - abi: royaltyPolicyLrpAbi, - functionName: "transferToVault", - args: [request.ipId, request.ancestorIpId, request.token, request.amount], + abi: royaltyTokenDistributionWorkflowsAbi, + functionName: "mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens", + args: [ + request.spgNftContract, + request.recipient, + request.ipMetadata, + request.derivData, + request.royaltyShares, + ], }), }; } /** - * method unpause for contract RoyaltyPolicyLRP + * method registerIpAndAttachPILTermsAndDeployRoyaltyVault for contract RoyaltyTokenDistributionWorkflows * - * @param request RoyaltyPolicyLrpUnpauseRequest + * @param request RoyaltyTokenDistributionWorkflowsRegisterIpAndAttachPilTermsAndDeployRoyaltyVaultRequest * @return Promise */ - public async unpause(): Promise { + public async registerIpAndAttachPilTermsAndDeployRoyaltyVault( + request: RoyaltyTokenDistributionWorkflowsRegisterIpAndAttachPilTermsAndDeployRoyaltyVaultRequest, + ): Promise { const { request: call } = await this.rpcClient.simulateContract({ - abi: royaltyPolicyLrpAbi, + abi: royaltyTokenDistributionWorkflowsAbi, address: this.address, - functionName: "unpause", + functionName: "registerIpAndAttachPILTermsAndDeployRoyaltyVault", account: this.wallet.account, + args: [ + request.nftContract, + request.tokenId, + request.ipMetadata, + request.terms, + request.sigMetadata, + request.sigAttach, + ], }); return await this.wallet.writeContract(call as WriteContractParameters); } /** - * method unpause for contract RoyaltyPolicyLRP with only encode + * method registerIpAndAttachPILTermsAndDeployRoyaltyVault for contract RoyaltyTokenDistributionWorkflows with only encode * - * @param request RoyaltyPolicyLrpUnpauseRequest + * @param request RoyaltyTokenDistributionWorkflowsRegisterIpAndAttachPilTermsAndDeployRoyaltyVaultRequest * @return EncodedTxData */ - public unpauseEncode(): EncodedTxData { + public registerIpAndAttachPilTermsAndDeployRoyaltyVaultEncode( + request: RoyaltyTokenDistributionWorkflowsRegisterIpAndAttachPilTermsAndDeployRoyaltyVaultRequest, + ): EncodedTxData { return { to: this.address, data: encodeFunctionData({ - abi: royaltyPolicyLrpAbi, - functionName: "unpause", + abi: royaltyTokenDistributionWorkflowsAbi, + functionName: "registerIpAndAttachPILTermsAndDeployRoyaltyVault", + args: [ + request.nftContract, + request.tokenId, + request.ipMetadata, + request.terms, + request.sigMetadata, + request.sigAttach, + ], }), }; } /** - * method upgradeToAndCall for contract RoyaltyPolicyLRP + * method registerIpAndMakeDerivativeAndDeployRoyaltyVault for contract RoyaltyTokenDistributionWorkflows * - * @param request RoyaltyPolicyLrpUpgradeToAndCallRequest + * @param request RoyaltyTokenDistributionWorkflowsRegisterIpAndMakeDerivativeAndDeployRoyaltyVaultRequest * @return Promise */ - public async upgradeToAndCall( - request: RoyaltyPolicyLrpUpgradeToAndCallRequest, + public async registerIpAndMakeDerivativeAndDeployRoyaltyVault( + request: RoyaltyTokenDistributionWorkflowsRegisterIpAndMakeDerivativeAndDeployRoyaltyVaultRequest, ): Promise { const { request: call } = await this.rpcClient.simulateContract({ - abi: royaltyPolicyLrpAbi, + abi: royaltyTokenDistributionWorkflowsAbi, address: this.address, - functionName: "upgradeToAndCall", + functionName: "registerIpAndMakeDerivativeAndDeployRoyaltyVault", account: this.wallet.account, - args: [request.newImplementation, request.data], + args: [ + request.nftContract, + request.tokenId, + request.ipMetadata, + request.derivData, + request.sigMetadata, + request.sigRegister, + ], }); return await this.wallet.writeContract(call as WriteContractParameters); } /** - * method upgradeToAndCall for contract RoyaltyPolicyLRP with only encode + * method registerIpAndMakeDerivativeAndDeployRoyaltyVault for contract RoyaltyTokenDistributionWorkflows with only encode * - * @param request RoyaltyPolicyLrpUpgradeToAndCallRequest + * @param request RoyaltyTokenDistributionWorkflowsRegisterIpAndMakeDerivativeAndDeployRoyaltyVaultRequest * @return EncodedTxData */ - public upgradeToAndCallEncode(request: RoyaltyPolicyLrpUpgradeToAndCallRequest): EncodedTxData { + public registerIpAndMakeDerivativeAndDeployRoyaltyVaultEncode( + request: RoyaltyTokenDistributionWorkflowsRegisterIpAndMakeDerivativeAndDeployRoyaltyVaultRequest, + ): EncodedTxData { return { to: this.address, data: encodeFunctionData({ - abi: royaltyPolicyLrpAbi, - functionName: "upgradeToAndCall", - args: [request.newImplementation, request.data], + abi: royaltyTokenDistributionWorkflowsAbi, + functionName: "registerIpAndMakeDerivativeAndDeployRoyaltyVault", + args: [ + request.nftContract, + request.tokenId, + request.ipMetadata, + request.derivData, + request.sigMetadata, + request.sigRegister, + ], }), }; } diff --git a/packages/core-sdk/src/constants/common.ts b/packages/core-sdk/src/constants/common.ts index e9dfb6a2..1fca29ac 100644 --- a/packages/core-sdk/src/constants/common.ts +++ b/packages/core-sdk/src/constants/common.ts @@ -3,3 +3,4 @@ import { Hex } from "viem"; export const AddressZero = "0x0000000000000000000000000000000000000000"; export const HashZero = "0x0000000000000000000000000000000000000000000000000000000000000000"; export const defaultFunctionSelector: Hex = "0x00000000"; +export const royaltySharesTotalSupply: number = 100000000; diff --git a/packages/core-sdk/src/index.ts b/packages/core-sdk/src/index.ts index bb0ad2f9..3506acc3 100644 --- a/packages/core-sdk/src/index.ts +++ b/packages/core-sdk/src/index.ts @@ -19,8 +19,8 @@ export type { RegisterDerivativeRequest, RegisterDerivativeWithLicenseTokensRequest, RegisterDerivativeWithLicenseTokensResponse, - CreateIpAssetWithPilTermsRequest, - CreateIpAssetWithPilTermsResponse, + MintAndRegisterIpAssetWithPilTermsRequest, + MintAndRegisterIpAssetWithPilTermsResponse, RegisterIpAndMakeDerivativeRequest, RegisterIpAndMakeDerivativeResponse, RegisterIpAndAttachPilTermsRequest, @@ -50,6 +50,15 @@ export type { BatchRegisterResponse, BatchRegisterDerivativeRequest, BatchRegisterDerivativeResponse, + RegisterIPAndAttachLicenseTermsAndDistributeRoyaltyTokensRequest, + RegisterIPAndAttachLicenseTermsAndDistributeRoyaltyTokensResponse, + RoyaltyShare, + RegisterDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensRequest, + RegisterDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensResponse, + MintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokensRequest, + MintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokensResponse, + MintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest, + MintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensResponse, } from "./types/resources/ipAsset"; export type { @@ -67,6 +76,7 @@ export type { SetLicensingConfigRequest, SetLicensingConfigResponse, } from "./types/resources/license"; + export { PIL_TYPE } from "./types/resources/license"; export type { @@ -94,11 +104,12 @@ export type { SetBatchPermissionsRequest, CreateBatchPermissionSignatureRequest, PermissionSignatureRequest, - PermissionSignatureResponse, SignatureRequest, SignatureResponse, } from "./types/resources/permission"; + export { AccessPermission } from "./types/resources/permission"; + export type { RaiseDisputeRequest, RaiseDisputeResponse, @@ -134,6 +145,7 @@ export type { RegisterIpAndAttachLicenseAndAddToGroupRequest, RegisterIpAndAttachLicenseAndAddToGroupResponse, } from "./types/resources/group"; + export type { PiLicenseTemplateGetLicenseTermsResponse, IpAccountImplStateResponse, @@ -142,4 +154,5 @@ export type { } from "./abi/generated"; export { getPermissionSignature, getSignature } from "./utils/sign"; + export { convertCIDtoHashIPFS, convertHashIPFStoCID } from "./utils/ipfs"; diff --git a/packages/core-sdk/src/resources/group.ts b/packages/core-sdk/src/resources/group.ts index 389a5b55..4793ab76 100644 --- a/packages/core-sdk/src/resources/group.ts +++ b/packages/core-sdk/src/resources/group.ts @@ -129,7 +129,7 @@ export class GroupClient { const { result: state } = await ipAccount.state(); const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, deadline); - const sigAddToGroupSignature = await getPermissionSignature({ + const { signature: sigAddToGroupSignature } = await getPermissionSignature({ ipId: groupId, deadline: calculatedDeadline, state, @@ -227,6 +227,46 @@ export class GroupClient { const { result: state } = await ipAccount.state(); const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, request.deadline); + const { signature: sigAddToGroupSignature } = await getPermissionSignature({ + ipId: getAddress(request.groupId, "request.groupId"), + deadline: calculatedDeadline, + state, + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + permissions: [ + { + ipId: getAddress(request.groupId, "request.groupId"), + signer: getAddress(this.groupingWorkflowsClient.address, "groupingWorkflowsClient"), + to: getAddress(this.groupingModuleClient.address, "groupingModuleClient"), + permission: AccessPermission.ALLOW, + func: "function addIp(address,address[])", + }, + ], + }); + const { signature: sigMetadataAndAttachSignature } = await getPermissionSignature({ + ipId: ipIdAddress, + deadline: calculatedDeadline, + state: toHex(0, { size: 32 }), + wallet: this.wallet as WalletClient, + permissionFunc: "setBatchPermissions", + chainId: chain[this.chainId], + permissions: [ + { + ipId: ipIdAddress, + signer: getAddress(this.groupingWorkflowsClient.address, "groupingWorkflowsClient"), + to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function setAll(address,string,bytes32,bytes32)", + }, + { + ipId: ipIdAddress, + signer: getAddress(this.groupingWorkflowsClient.address, "groupingWorkflowsClient"), + to: getAddress(this.licensingModuleClient.address, "licensingModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function attachLicenseTerms(address,address,uint256)", + }, + ], + }); const object: GroupingWorkflowsRegisterIpAndAttachLicenseAndAddToGroupRequest = { nftContract: getAddress(request.nftContract, "request.nftContract"), groupId: request.groupId, @@ -245,50 +285,12 @@ export class GroupClient { sigAddToGroup: { signer: getAddress(this.wallet.account!.address, "wallet.account.address"), deadline: calculatedDeadline, - signature: await getPermissionSignature({ - ipId: getAddress(request.groupId, "request.groupId"), - deadline: calculatedDeadline, - state, - wallet: this.wallet as WalletClient, - chainId: chain[this.chainId], - permissions: [ - { - ipId: getAddress(request.groupId, "request.groupId"), - signer: getAddress(this.groupingWorkflowsClient.address, "groupingWorkflowsClient"), - to: getAddress(this.groupingModuleClient.address, "groupingModuleClient"), - permission: AccessPermission.ALLOW, - func: "function addIp(address,address[])", - }, - ], - }), + signature: sigAddToGroupSignature, }, sigMetadataAndAttach: { signer: getAddress(this.wallet.account!.address, "wallet.account.address"), deadline: calculatedDeadline, - signature: await getPermissionSignature({ - ipId: ipIdAddress, - deadline: calculatedDeadline, - state: toHex(0, { size: 32 }), - wallet: this.wallet as WalletClient, - permissionFunc: "setBatchPermissions", - chainId: chain[this.chainId], - permissions: [ - { - ipId: ipIdAddress, - signer: getAddress(this.groupingWorkflowsClient.address, "groupingWorkflowsClient"), - to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), - permission: AccessPermission.ALLOW, - func: "function setAll(address,string,bytes32,bytes32)", - }, - { - ipId: ipIdAddress, - signer: getAddress(this.groupingWorkflowsClient.address, "groupingWorkflowsClient"), - to: getAddress(this.licensingModuleClient.address, "licensingModuleAddress"), - permission: AccessPermission.ALLOW, - func: "function attachLicenseTerms(address,address,uint256)", - }, - ], - }), + signature: sigMetadataAndAttachSignature, }, }; if (request.txOptions?.encodedTxDataOnly) { diff --git a/packages/core-sdk/src/resources/ipAsset.ts b/packages/core-sdk/src/resources/ipAsset.ts index 3ea0d75f..4006a57a 100644 --- a/packages/core-sdk/src/resources/ipAsset.ts +++ b/packages/core-sdk/src/resources/ipAsset.ts @@ -6,10 +6,7 @@ import { zeroHash, WalletClient, toHex, - encodeAbiParameters, encodeFunctionData, - keccak256, - toFunctionSelector, TransactionReceipt, } from "viem"; @@ -25,8 +22,8 @@ import { BatchRegisterDerivativeResponse, BatchRegisterRequest, BatchRegisterResponse, - CreateIpAssetWithPilTermsRequest, - CreateIpAssetWithPilTermsResponse, + MintAndRegisterIpAssetWithPilTermsRequest, + MintAndRegisterIpAssetWithPilTermsResponse, GenerateCreatorMetadataParam, GenerateIpMetadataParam, IpCreator, @@ -48,6 +45,18 @@ import { RegisterPilTermsAndAttachResponse, RegisterRequest, MintAndRegisterIpAndMakeDerivativeResponse, + RegisterIPAndAttachLicenseTermsAndDistributeRoyaltyTokensRequest, + DistributeRoyaltyTokens, + RoyaltyShare, + RegisterIPAndAttachLicenseTermsAndDistributeRoyaltyTokensResponse, + BatchMintAndRegisterIpAssetWithPilTermsResult, + IpIdAndTokenId, + RegisterDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensRequest, + RegisterDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensResponse, + MintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokensRequest, + MintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest, + MintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokensResponse, + MintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensResponse, } from "../types/resources/ipAsset"; import { AccessControllerClient, @@ -59,9 +68,10 @@ import { DerivativeWorkflowsRegisterIpAndMakeDerivativeWithLicenseTokensRequest, IpAccountImplClient, IpAssetRegistryClient, + IpRoyaltyVaultImplReadOnlyClient, LicenseAttachmentWorkflowsClient, - LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTermsRequest, - LicenseAttachmentWorkflowsRegisterIpAndAttachPilTermsRequest, + LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTerms2Request, + LicenseAttachmentWorkflowsRegisterIpAndAttachPilTerms2Request, LicenseAttachmentWorkflowsRegisterPilTermsAndAttachRequest, LicenseRegistryReadOnlyClient, LicenseTokenReadOnlyClient, @@ -71,15 +81,18 @@ import { RegistrationWorkflowsClient, RegistrationWorkflowsMintAndRegisterIpRequest, RegistrationWorkflowsRegisterIpRequest, + RoyaltyModuleEventClient, + RoyaltyTokenDistributionWorkflowsClient, SimpleWalletClient, - accessControllerAbi, ipAccountImplAbi, licensingModuleAbi, - royaltyPolicyLapAddress, + mockErc20Abi, } from "../abi/generated"; -import { getLicenseTermByType, validateLicenseTerms } from "../utils/licenseTermsHelper"; +import { validateLicenseTerms } from "../utils/licenseTermsHelper"; import { getDeadline, getPermissionSignature, getSignature } from "../utils/sign"; -import { AccessPermission, SetPermissionsRequest } from "../types/resources/permission"; +import { AccessPermission } from "../types/resources/permission"; +import { LicenseTerms } from "../types/resources/license"; +import { royaltySharesTotalSupply } from "../constants/common"; export class IPAssetClient { public licensingModuleClient: LicensingModuleClient; @@ -93,11 +106,12 @@ export class IPAssetClient { public licenseAttachmentWorkflowsClient: LicenseAttachmentWorkflowsClient; public derivativeWorkflowsClient: DerivativeWorkflowsClient; public multicall3Client: Multicall3Client; + public royaltyTokenDistributionWorkflowsClient: RoyaltyTokenDistributionWorkflowsClient; + public royaltyModuleEventClient: RoyaltyModuleEventClient; private readonly rpcClient: PublicClient; private readonly wallet: SimpleWalletClient; private readonly chainId: SupportedChainIds; - private defaultLicenseTermsId!: bigint; constructor(rpcClient: PublicClient, wallet: SimpleWalletClient, chainId: SupportedChainIds) { this.licensingModuleClient = new LicensingModuleClient(rpcClient, wallet); @@ -110,11 +124,15 @@ export class IPAssetClient { this.registrationWorkflowsClient = new RegistrationWorkflowsClient(rpcClient, wallet); this.licenseAttachmentWorkflowsClient = new LicenseAttachmentWorkflowsClient(rpcClient, wallet); this.derivativeWorkflowsClient = new DerivativeWorkflowsClient(rpcClient, wallet); + this.royaltyTokenDistributionWorkflowsClient = new RoyaltyTokenDistributionWorkflowsClient( + rpcClient, + wallet, + ); + this.royaltyModuleEventClient = new RoyaltyModuleEventClient(rpcClient); this.multicall3Client = new Multicall3Client(rpcClient, wallet); this.rpcClient = rpcClient; this.wallet = wallet; this.chainId = chainId; - void this.getDefaultLicenseTerms(); } /** @@ -265,7 +283,7 @@ export class IPAssetClient { if (request.ipMetadata) { const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, request.deadline); - const signature = await getPermissionSignature({ + const { signature } = await getPermissionSignature({ ipId: ipIdAddress, deadline: calculatedDeadline, state: toHex(0, { size: 32 }), @@ -318,10 +336,10 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - const targetLogs = this.getIpIdAndTokenIdFromEvent(txReceipt)[0]; - return { txHash: txHash, ipId: targetLogs.ipId, tokenId: targetLogs.tokenId }; + const log = this.getIpIdAndTokenIdsFromEvent(txReceipt)[0]; + return { txHash, ...log }; } else { - return { txHash: txHash }; + return { txHash }; } } } catch (error) { @@ -340,8 +358,8 @@ export class IPAssetClient { * @param request.args.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. * @param request.args.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. * @param request.args.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. - * @param request.txOptions [Optional] This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property. - * @returns A Promise that resolves to a transaction hash, if waitForTransaction is true, includes IP ID, Token ID. + * @param request.txOptions [Optional] This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property, without encodedTxDataOnly option. + * @returns A Promise that resolves to a transaction hash, if waitForTransaction is true, return an array of containing IP ID, Token ID, NFT Contract. * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, resolverAddr, metadataProviderAddress, metadata) */ public async batchRegister(request: BatchRegisterRequest): Promise { @@ -375,14 +393,10 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - const targetLogs = this.getIpIdAndTokenIdFromEvent(txReceipt); - const results = targetLogs.map((log) => ({ - ipId: log.ipId, - tokenId: log.tokenId, - })); - return { txHash: txHash, results }; + const results = this.getIpIdAndTokenIdsFromEvent(txReceipt, "nftContract"); + return { txHash, results }; } else { - return { txHash: txHash }; + return { txHash }; } } catch (error) { handleError(error, "Failed to batch register IP"); @@ -515,7 +529,7 @@ export class IPAssetClient { ], }); const { result: state } = await ipAccount.state(); - const signature = await getSignature({ + const { signature } = await getSignature({ state, to: licenseModuleAddress, encodeData: data, @@ -605,45 +619,51 @@ export class IPAssetClient { * 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.spgNftContract The address of the NFT collection. - * @param request.pilType The type of the PIL. + * @param {Array} request.terms The array of license terms to be attached. + * @param request.terms.transferable Indicates whether the license is transferable or not. + * @param request.terms.royaltyPolicy The address of the royalty policy contract which required to StoryProtocol in advance. + * @param request.terms.mintingFee The fee to be paid when minting a license. + * @param request.terms.expiration The expiration period of the license. + * @param request.terms.commercialUse Indicates whether the work can be used commercially or not. + * @param request.terms.commercialAttribution Whether attribution is required when reproducing the work commercially or not. + * @param request.terms.commercializerChecker Commercializers that are allowed to commercially exploit the work. If zero address, then no restrictions is enforced. + * @param request.terms.commercializerCheckerData The data to be passed to the commercializer checker contract. + * @param request.terms.commercialRevShare Percentage of revenue that must be shared with the licensor. + * @param request.terms.commercialRevCeiling The maximum revenue that can be generated from the commercial use of the work. + * @param request.terms.derivativesAllowed Indicates whether the licensee can create derivatives of his work or not. + * @param request.terms.derivativesAttribution Indicates whether attribution is required for derivatives of the work or not. + * @param request.terms.derivativesApproval Indicates whether the licensor must approve derivatives of the work before they can be linked to the licensor IP ID or not. + * @param request.terms.derivativesReciprocal Indicates whether the licensee must license derivatives of the work under the same terms or not. + * @param request.terms.derivativeRevCeiling The maximum revenue that can be generated from the derivative use of the work. + * @param request.terms.currency The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. + * @param request.terms.uri The URI of the license terms, which can be used to fetch the offchain license terms. * @param request.ipMetadata - [Optional] The desired metadata for the newly minted NFT and newly registered IP. - * @param request.ipMetadata.ipMetadataURI [Optional] The URI of the metadata for the IP. - * @param request.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. - * @param request.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. - * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. - * @param request.royaltyPolicyAddress [Optional] The address of the royalty policy contract, default value is LAP. + * @param request.ipMetadata.ipMetadataURI [Optional] The URI of the metadata for the IP. + * @param request.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. + * @param request.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. + * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. * @param request.recipient [Optional] The address of the recipient of the minted NFT,default value is your wallet address. - * @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] This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property. - * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, and if waitForTransaction is true, includes IP ID, Token ID, License Terms Id. + * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, and if waitForTransaction is true, including IP ID, Token ID, License Terms Ids. * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) * @emits LicenseTermsAttached (caller, ipId, licenseTemplate, licenseTermsId) */ public async mintAndRegisterIpAssetWithPilTerms( - request: CreateIpAssetWithPilTermsRequest, - ): Promise { + request: MintAndRegisterIpAssetWithPilTermsRequest, + ): Promise { try { - if (request.pilType === undefined || request.pilType === null) { - throw new Error("PIL type is required."); + const licenseTerms: LicenseTerms[] = []; + for (let i = 0; i < request.terms.length; i++) { + const licenseTerm = await validateLicenseTerms(request.terms[i], this.rpcClient); + licenseTerms.push(licenseTerm); } - const licenseTerm = getLicenseTermByType(request.pilType, { - defaultMintingFee: request.mintingFee, - currency: request.currency, - commercialRevShare: request.commercialRevShare, - royaltyPolicyAddress: - (request.royaltyPolicyAddress && - getAddress(request.royaltyPolicyAddress, "request.royaltyPolicyAddress")) || - royaltyPolicyLapAddress[chain[this.chainId]], - }); - const object: LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTermsRequest = { + const object: LicenseAttachmentWorkflowsMintAndRegisterIpAndAttachPilTerms2Request = { spgNftContract: getAddress(request.spgNftContract, "request.spgNftContract"), recipient: (request.recipient && getAddress(request.recipient, "request.recipient")) || this.wallet.account!.address, - terms: licenseTerm, + terms: licenseTerms, ipMetadata: { ipMetadataURI: request.ipMetadata?.ipMetadataURI || "", ipMetadataHash: request.ipMetadata?.ipMetadataHash || zeroHash, @@ -654,23 +674,22 @@ export class IPAssetClient { if (request.txOptions?.encodedTxDataOnly) { return { encodedTxData: - this.licenseAttachmentWorkflowsClient.mintAndRegisterIpAndAttachPilTermsEncode(object), + this.licenseAttachmentWorkflowsClient.mintAndRegisterIpAndAttachPilTerms2Encode(object), }; } else { const txHash = - await this.licenseAttachmentWorkflowsClient.mintAndRegisterIpAndAttachPilTerms(object); + await this.licenseAttachmentWorkflowsClient.mintAndRegisterIpAndAttachPilTerms2(object); if (request.txOptions?.waitForTransaction) { const txReceipt = await this.rpcClient.waitForTransactionReceipt({ ...request.txOptions, hash: txHash, }); - const iPRegisteredLog = this.getIpIdAndTokenIdFromEvent(txReceipt)[0]; - const licenseTermsId = this.getLicenseTermsId(txReceipt); + const ipIdAndTokenId = this.getIpIdAndTokenIdsFromEvent(txReceipt)[0]; + const licenseTermsIds = await this.getLicenseTermsId(licenseTerms); return { - txHash: txHash, - ipId: iPRegisteredLog.ipId, - licenseTermsId, - tokenId: iPRegisteredLog.tokenId, + txHash, + ...ipIdAndTokenId, + licenseTermsIds, }; } return { txHash }; @@ -684,19 +703,32 @@ export class IPAssetClient { * @param request - The request object that contains all data needed to batch mint and register ip. * @param {Array} request.args The array of mint and register IP requests. * @param request.args.spgNftContract The address of the NFT collection. - * @param request.args.pilType The type of the PIL. + * @param {Array} request.args.terms The array of license terms to be attached. + * @param request.args.terms.transferable Indicates whether the license is transferable or not. + * @param request.args.terms.royaltyPolicy The address of the royalty policy contract which required to StoryProtocol in advance. + * @param request.args.terms.mintingFee The fee to be paid when minting a license. + * @param request.args.terms.expiration The expiration period of the license. + * @param request.args.terms.commercialUse Indicates whether the work can be used commercially or not. + * @param request.args.terms.commercialAttribution Whether attribution is required when reproducing the work commercially or not. + * @param request.args.terms.commercializerChecker Commercializers that are allowed to commercially exploit the work. If zero address, then no restrictions is enforced. + * @param request.args.terms.commercializerCheckerData The data to be passed to the commercializer checker contract. + * @param request.args.terms.commercialRevShare Percentage of revenue that must be shared with the licensor. + * @param request.args.terms.commercialRevCeiling The maximum revenue that can be generated from the commercial use of the work. + * @param request.args.terms.derivativesAllowed Indicates whether the licensee can create derivatives of his work or not. + * @param request.args.terms.derivativesAttribution Indicates whether attribution is required for derivatives of the work or not. + * @param request.args.terms.derivativesApproval Indicates whether the licensor must approve derivatives of the work before they can be linked to the licensor IP ID or not. + * @param request.args.terms.derivativesReciprocal Indicates whether the licensee must license derivatives of the work under the same terms or not. + * @param request.args.terms.derivativeRevCeiling The maximum revenue that can be generated from the derivative use of the work. + * @param request.args.terms.currency The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. + * @param request.args.terms.uri The URI of the license terms, which can be used to fetch the offchain license terms. * @param request.args.ipMetadata - [Optional] The desired metadata for the newly minted NFT and newly registered IP. * @param request.args.ipMetadata.ipMetadataURI [Optional] The URI of the metadata for the IP. - * @param request.args.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. - * @param request.args.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. - * @param request.args.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. - * @param request.args.royaltyPolicyAddress [Optional] The address of the royalty policy contract, default value is LAP. + * @param request.args.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. + * @param request.args.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. + * @param request.args.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. * @param request.args.recipient [Optional] The address of the recipient of the minted NFT,default value is your wallet address. - * @param request.args.mintingFee [Optional] The fee to be paid when minting a license. - * @param request.args.commercialRevShare [Optional] Percentage of revenue that must be shared with the licensor. - * @param request.args.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] This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property, without encodedTxData option. - * @returns A Promise that resolves to a transaction hash, if waitForTransaction is true, includes IP ID, Token ID, License Terms Id. + * @returns A Promise that resolves to a transaction hash, if waitForTransaction is true, return an array containing IP ID, Token ID, License Terms Ids, SPG NFT Contract. * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) * @emits LicenseTermsAttached (caller, ipId, licenseTemplate, licenseTermsId) */ @@ -715,24 +747,31 @@ export class IPAssetClient { calldata.push(result.encodedTxData!.data); } const txHash = await this.licenseAttachmentWorkflowsClient.multicall({ data: calldata }); + if (request.txOptions?.waitForTransaction) { const txReceipt = await this.rpcClient.waitForTransactionReceipt({ ...request.txOptions, hash: txHash, }); - const licenseTermsEvent = - this.licensingModuleClient.parseTxLicenseTermsAttachedEvent(txReceipt); - let results = this.getIpIdAndTokenIdFromEvent(txReceipt); - results = results.map((result) => { - const licenseTerms = licenseTermsEvent.find((event) => event.ipId === result.ipId); - return { - ...result, - licenseTermsId: - licenseTerms?.licenseTermsId === undefined - ? this.defaultLicenseTermsId - : licenseTerms.licenseTermsId, - }; - }); + const results: BatchMintAndRegisterIpAssetWithPilTermsResult[] = this.ipAssetRegistryClient + .parseTxIpRegisteredEvent(txReceipt) + .map((log) => ({ + ipId: log.ipId, + tokenId: log.tokenId, + spgNftContract: log.tokenContract, + licenseTermsIds: [], + })); + // Due to emit event log by sequence, we need to get license terms id from request.args + for (let j = 0; j < request.args.length; j++) { + const licenseTerms: LicenseTerms[] = []; + const terms = request.args[j].terms; + for (let i = 0; i < terms.length; i++) { + const licenseTerm = await validateLicenseTerms(terms[i], this.rpcClient); + licenseTerms.push(licenseTerm); + } + const licenseTermsIds = await this.getLicenseTermsId(licenseTerms); + results[j].licenseTermsIds = licenseTermsIds; + } return { txHash: txHash, results, @@ -749,59 +788,76 @@ export class IPAssetClient { * @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 {Array} request.terms The array of license terms to be attached. + * @param request.terms.transferable Indicates whether the license is transferable or not. + * @param request.terms.royaltyPolicy The address of the royalty policy contract which required to StoryProtocol in advance. + * @param request.terms.mintingFee The fee to be paid when minting a license. + * @param request.terms.expiration The expiration period of the license. + * @param request.terms.commercialUse Indicates whether the work can be used commercially or not. + * @param request.terms.commercialAttribution Whether attribution is required when reproducing the work commercially or not. + * @param request.terms.commercializerChecker Commercializers that are allowed to commercially exploit the work. If zero address, then no restrictions is enforced. + * @param request.terms.commercializerCheckerData The data to be passed to the commercializer checker contract. + * @param request.terms.commercialRevShare Percentage of revenue that must be shared with the licensor. + * @param request.terms.commercialRevCeiling The maximum revenue that can be generated from the commercial use of the work. + * @param request.terms.derivativesAllowed Indicates whether the licensee can create derivatives of his work or not. + * @param request.terms.derivativesAttribution Indicates whether attribution is required for derivatives of the work or not. + * @param request.terms.derivativesApproval Indicates whether the licensor must approve derivatives of the work before they can be linked to the licensor IP ID or not. + * @param request.terms.derivativesReciprocal Indicates whether the licensee must license derivatives of the work under the same terms or not. + * @param request.terms.derivativeRevCeiling The maximum revenue that can be generated from the derivative use of the work. + * @param request.terms.currency The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. + * @param request.terms.uri The URI of the license terms, which can be used to fetch the offchain license terms. * @param request.ipMetadata - [Optional] The desired metadata for the newly minted NFT and newly registered IP. * @param request.ipMetadata.ipMetadataURI [Optional] The URI of the metadata for the IP. * @param request.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. * @param request.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. - * @param request.royaltyPolicyAddress [Optional] The address of the royalty policy contract, default value is LAP. * @param request.deadline [Optional] The deadline for the signature in seconds, default is 1000s. - * @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] transaction. This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property. - * @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. + * @returns A Promise that resolves to a transaction hash, if waitForTransaction is true, including IP ID, token ID and License terms IDs. * @emits LicenseTermsAttached (caller, ipId, licenseTemplate, licenseTermsId) */ public async registerIpAndAttachPilTerms( request: RegisterIpAndAttachPilTermsRequest, ): Promise { try { - if (request.pilType === undefined || request.pilType === null) { - throw new Error("PIL type is required."); - } request.tokenId = BigInt(request.tokenId); const ipIdAddress = await this.getIpIdAddress(request.nftContract, request.tokenId); const isRegistered = await this.isRegistered(ipIdAddress); if (isRegistered) { throw new Error(`The NFT with id ${request.tokenId} is already registered as IP.`); } - const licenseTerm = getLicenseTermByType(request.pilType, { - defaultMintingFee: request.mintingFee, - currency: request.currency, - royaltyPolicyAddress: - (request.royaltyPolicyAddress && - getAddress(request.royaltyPolicyAddress, "request.royaltyPolicyAddress")) || - royaltyPolicyLapAddress[chain[this.chainId]], - commercialRevShare: request.commercialRevShare, - }); + const licenseTerms: LicenseTerms[] = []; + for (let i = 0; i < request.terms.length; i++) { + const licenseTerm = await validateLicenseTerms(request.terms[i], this.rpcClient); + licenseTerms.push(licenseTerm); + } const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, request.deadline); + const { signature: sigMetadataSignature, nonce: sigMetadataState } = + await getPermissionSignature({ + ipId: ipIdAddress, + deadline: calculatedDeadline, + state: toHex(0, { size: 32 }), + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + permissions: [ + { + ipId: ipIdAddress, + signer: getAddress( + this.licenseAttachmentWorkflowsClient.address, + "licenseAttachmentWorkflowsClient", + ), + to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function setAll(address,string,bytes32,bytes32)", + }, + ], + }); - const sigAttachSignature = await getPermissionSignature({ + const { signature: sigAttachSignature } = await getPermissionSignature({ ipId: ipIdAddress, deadline: calculatedDeadline, - state: this.getSigSignatureState({ - ipId: ipIdAddress, - signer: getAddress( - this.licenseAttachmentWorkflowsClient.address, - "licenseAttachmentWorkflowsClient", - ), - to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), - permission: AccessPermission.ALLOW, - func: "function setAll(address,string,bytes32,bytes32)", - }), + state: sigMetadataState, wallet: this.wallet as WalletClient, chainId: chain[this.chainId], permissions: [ @@ -817,30 +873,10 @@ export class IPAssetClient { }, ], }); - const sigMetadataSignature = await getPermissionSignature({ - ipId: ipIdAddress, - deadline: calculatedDeadline, - state: toHex(0, { size: 32 }), - wallet: this.wallet as WalletClient, - chainId: chain[this.chainId], - permissions: [ - { - ipId: ipIdAddress, - signer: getAddress( - this.licenseAttachmentWorkflowsClient.address, - "licenseAttachmentWorkflowsClient", - ), - to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), - permission: AccessPermission.ALLOW, - func: "function setAll(address,string,bytes32,bytes32)", - }, - ], - }); - - const object: LicenseAttachmentWorkflowsRegisterIpAndAttachPilTermsRequest = { + const object: LicenseAttachmentWorkflowsRegisterIpAndAttachPilTerms2Request = { nftContract: getAddress(request.nftContract, "request.nftContract"), tokenId: request.tokenId, - terms: licenseTerm, + terms: licenseTerms, ipMetadata: { ipMetadataURI: request.ipMetadata?.ipMetadataURI || "", ipMetadataHash: request.ipMetadata?.ipMetadataHash || zeroHash, @@ -862,10 +898,10 @@ export class IPAssetClient { if (request.txOptions?.encodedTxDataOnly) { return { encodedTxData: - this.licenseAttachmentWorkflowsClient.registerIpAndAttachPilTermsEncode(object), + this.licenseAttachmentWorkflowsClient.registerIpAndAttachPilTerms2Encode(object), }; } else { - const txHash = await this.licenseAttachmentWorkflowsClient.registerIpAndAttachPilTerms( + const txHash = await this.licenseAttachmentWorkflowsClient.registerIpAndAttachPilTerms2( object, ); if (request.txOptions?.waitForTransaction) { @@ -873,13 +909,11 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - const ipRegisterEvent = this.getIpIdAndTokenIdFromEvent(txReceipt)[0]; - const licenseTermsId = this.getLicenseTermsId(txReceipt); + const log = this.getIpIdAndTokenIdsFromEvent(txReceipt)[0]; return { txHash, - licenseTermsId: licenseTermsId, - ipId: ipRegisterEvent.ipId, - tokenId: ipRegisterEvent.tokenId, + licenseTermsIds: await this.getLicenseTermsId(licenseTerms), + ...log, }; } return { txHash }; @@ -904,7 +938,7 @@ export class IPAssetClient { * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. * @param request.deadline [Optional] The deadline for the signature in seconds, default is 1000s. * @param request.txOptions - [Optional] transaction. This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property. - * @returns A Promise that resolves to an object containing the transaction hash and optional IP ID, token ID if waitForTxn is set to true. + * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, and if waitForTransaction is true, included IP ID, Token ID. * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) */ public async registerDerivativeIp( @@ -941,16 +975,30 @@ export class IPAssetClient { } const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, request.deadline); - const sigRegisterSignature = await getPermissionSignature({ + const { signature: sigMetadataSignature, nonce: sigMetadataState } = + await getPermissionSignature({ + ipId: ipIdAddress, + deadline: calculatedDeadline, + state: toHex(0, { size: 32 }), + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + permissions: [ + { + ipId: ipIdAddress, + signer: getAddress( + this.derivativeWorkflowsClient.address, + "derivativeWorkflowsClient", + ), + to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function setAll(address,string,bytes32,bytes32)", + }, + ], + }); + const { signature: sigRegisterSignature } = await getPermissionSignature({ ipId: ipIdAddress, deadline: calculatedDeadline, - state: this.getSigSignatureState({ - ipId: ipIdAddress, - signer: getAddress(this.derivativeWorkflowsClient.address, "derivativeWorkflowsClient"), - to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), - permission: AccessPermission.ALLOW, - func: "function setAll(address,string,bytes32,bytes32)", - }), + state: sigMetadataState, wallet: this.wallet as WalletClient, chainId: chain[this.chainId], permissions: [ @@ -994,22 +1042,7 @@ export class IPAssetClient { signature: zeroHash, }, }; - const sigMetadataSignature = await getPermissionSignature({ - ipId: ipIdAddress, - deadline: calculatedDeadline, - state: toHex(0, { size: 32 }), - wallet: this.wallet as WalletClient, - chainId: chain[this.chainId], - permissions: [ - { - ipId: ipIdAddress, - signer: getAddress(this.derivativeWorkflowsClient.address, "derivativeWorkflowsClient"), - to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), - permission: AccessPermission.ALLOW, - func: "function setAll(address,string,bytes32,bytes32)", - }, - ], - }); + object.sigMetadata = { signer: getAddress(this.wallet.account!.address, "wallet.account.address"), deadline: calculatedDeadline, @@ -1026,8 +1059,8 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - const log = this.getIpIdAndTokenIdFromEvent(receipt)[0]; - return { txHash, ipId: log.ipId, tokenId: log.tokenId }; + const log = this.getIpIdAndTokenIdsFromEvent(receipt)[0]; + return { txHash, ...log }; } return { txHash }; } @@ -1051,7 +1084,7 @@ export class IPAssetClient { * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. * @param request.recipient [Optional] The address of the recipient of the minted NFT,default value is your wallet address. * @param request.txOptions - [Optional] transaction. This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property. - * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, and if waitForTransaction is true, includes child ip id and token id. + * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, and if waitForTransaction is true, includes child IP ID and token ID. * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) */ public async mintAndRegisterIpAndMakeDerivative( @@ -1112,7 +1145,7 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - const log = this.getIpIdAndTokenIdFromEvent(receipt)[0]; + const log = this.getIpIdAndTokenIdsFromEvent(receipt)[0]; return { txHash, childIpId: log.ipId, tokenId: log.tokenId }; } return { txHash }; @@ -1137,7 +1170,7 @@ export class IPAssetClient { * @param request.args.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. * @param request.arg.recipient [Optional] The address of the recipient of the minted NFT,default value is your wallet address. * @param request.txOptions - [Optional] transaction. This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property, without encodedTxData option. - * @returns A Promise that resolves to a transaction hash, if waitForTransaction is true, includes child ip id and token id. + * @returns A Promise that resolves to a transaction hash, if waitForTransaction is true, return an array of containing IP ID and token ID, SPG NFT Contract. * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) */ public async batchMintAndRegisterIpAndMakeDerivative( @@ -1168,7 +1201,7 @@ export class IPAssetClient { }); return { txHash, - results: this.getIpIdAndTokenIdFromEvent(txReceipt), + results: this.getIpIdAndTokenIdsFromEvent(txReceipt, "spgNftContract"), }; } return { txHash }; @@ -1213,8 +1246,8 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - const ipRegisterEvent = this.getIpIdAndTokenIdFromEvent(txReceipt); - return { txHash, ipId: ipRegisterEvent[0].ipId, tokenId: ipRegisterEvent[0].tokenId }; + const log = this.getIpIdAndTokenIdsFromEvent(txReceipt)[0]; + return { txHash, ...log }; } return { txHash }; } @@ -1226,7 +1259,7 @@ export class IPAssetClient { * Register Programmable IP License Terms (if unregistered) and attach it to IP. * @param request - The request object that contains all data needed to attach license terms. * @param request.ipId The ID of the IP. - * @param request.terms The PIL terms to be registered. + * @param {Array} request.terms The array of license terms to be attached. * @param request.terms.transferable Indicates whether the license is transferable or not. * @param request.terms.royaltyPolicy The address of the royalty policy contract which required to StoryProtocol in advance. * @param request.terms.mintingFee The fee to be paid when minting a license. @@ -1246,7 +1279,7 @@ export class IPAssetClient { * @param request.terms.uri The URI of the license terms, which can be used to fetch the offchain license terms. * @param request.deadline [Optional] The deadline for the signature in milliseconds, default is 1000s. * @param request.txOptions [Optional] This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property. - * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, and if waitForTransaction is true, includes license terms id. + * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, and if waitForTransaction is true, returns an array containing the license terms ID. * @emits LicenseTermsAttached (caller, ipId, licenseTemplate, licenseTermsId) */ public async registerPilTermsAndAttach( @@ -1258,15 +1291,16 @@ export class IPAssetClient { if (!isRegistered) { throw new Error(`The IP with id ${ipId} is not registered.`); } - const licenseTerms = await validateLicenseTerms(terms, this.rpcClient); - const licenseRes = await this.licenseTemplateClient.getLicenseTermsId({ - terms: licenseTerms, - }); + const licenseTerms: LicenseTerms[] = []; + for (let i = 0; i < terms.length; i++) { + const licenseTerm = await validateLicenseTerms(terms[i], this.rpcClient); + licenseTerms.push(licenseTerm); + } const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, request.deadline); const ipAccount = new IpAccountImplClient(this.rpcClient, this.wallet, ipId); const { result: state } = await ipAccount.state(); - const sigAttachSignature = await getPermissionSignature({ + const { signature: sigAttachSignature } = await getPermissionSignature({ ipId: ipId, deadline: calculatedDeadline, state, @@ -1308,7 +1342,8 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - return { txHash, licenseTermsId: licenseRes.selectedLicenseTermsId }; + const licenseTermsIds = await this.getLicenseTermsId(licenseTerms); + return { txHash, licenseTermsIds }; } else { return { txHash }; } @@ -1369,8 +1404,8 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - const log = this.getIpIdAndTokenIdFromEvent(receipt)[0]; - return { txHash, ipId: log.ipId, tokenId: log.tokenId }; + const log = this.getIpIdAndTokenIdsFromEvent(receipt)[0]; + return { txHash, ...log }; } return { txHash }; } @@ -1391,7 +1426,7 @@ export class IPAssetClient { * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. * @param request.deadline [Optional] The deadline for the signature in seconds, default is 1000s. * @param request.txOptions [Optional] This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property. - * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, or if waitForTransaction is true, includes IP ID. + * @returns A Promise that resolves to a transaction hash, and if encodedTxDataOnly is true, includes encoded transaction data, or if waitForTransaction is true, includes IP ID, Token ID. */ public async registerIpAndMakeDerivativeWithLicenseTokens( request: RegisterIpAndMakeDerivativeWithLicenseTokensRequest, @@ -1406,32 +1441,30 @@ export class IPAssetClient { const licenseTokenIds = await this.validateLicenseTokenIds(request.licenseTokenIds); const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, request.deadline); - const sigMetadataSignature = await getPermissionSignature({ - ipId: ipIdAddress, - deadline: calculatedDeadline, - state: toHex(0, { size: 32 }), - wallet: this.wallet as WalletClient, - chainId: chain[this.chainId], - permissions: [ - { - ipId: ipIdAddress, - signer: getAddress(this.derivativeWorkflowsClient.address, "derivativeWorkflowsClient"), - to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), - permission: AccessPermission.ALLOW, - func: "function setAll(address,string,bytes32,bytes32)", - }, - ], - }); - const sigRegisterSignature = await getPermissionSignature({ + const { signature: sigMetadataSignature, nonce: sigMetadataState } = + await getPermissionSignature({ + ipId: ipIdAddress, + deadline: calculatedDeadline, + state: toHex(0, { size: 32 }), + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + permissions: [ + { + ipId: ipIdAddress, + signer: getAddress( + this.derivativeWorkflowsClient.address, + "derivativeWorkflowsClient", + ), + to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function setAll(address,string,bytes32,bytes32)", + }, + ], + }); + const { signature: sigRegisterSignature } = await getPermissionSignature({ ipId: ipIdAddress, deadline: calculatedDeadline, - state: this.getSigSignatureState({ - ipId: ipIdAddress, - signer: getAddress(this.derivativeWorkflowsClient.address, "derivativeWorkflowsClient"), - to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), - permission: AccessPermission.ALLOW, - func: "function setAll(address,string,bytes32,bytes32)", - }), + state: sigMetadataState, wallet: this.wallet as WalletClient, chainId: chain[this.chainId], permissions: [ @@ -1481,8 +1514,8 @@ export class IPAssetClient { ...request.txOptions, hash: txHash, }); - const log = this.getIpIdAndTokenIdFromEvent(receipt)[0]; - return { txHash, ipId: log.ipId }; + const log = this.getIpIdAndTokenIdsFromEvent(receipt)[0]; + return { txHash, ...log }; } return { txHash }; } @@ -1490,7 +1523,536 @@ export class IPAssetClient { handleError(error, "Failed to register IP and make derivative with license tokens"); } } + /** + * Register the given NFT and attach license terms and distribute royalty tokens. In order to successfully distribute royalty tokens, the license terms attached to the IP must be + * a commercial license. + * @param request - The request object that contains all data needed to register ip and attach license terms and distribute royalty tokens. + * @param request.nftContract The address of the NFT collection. + * @param request.tokenId The ID of the NFT. + * @param request.terms The array of license terms to be attached. + * @param request.terms.transferable Indicates whether the license is transferable or not. + * @param request.terms.royaltyPolicy The address of the royalty policy contract which required to StoryProtocol in advance. + * @param request.terms.mintingFee The fee to be paid when minting a license. + * @param request.terms.expiration The expiration period of the license. + * @param request.terms.commercialUse Indicates whether the work can be used commercially or not, Commercial use is required to deploy a royalty vault. + * @param request.terms.commercialAttribution Whether attribution is required when reproducing the work commercially or not. + * @param request.terms.commercializerChecker Commercializers that are allowed to commercially exploit the work. If zero address, then no restrictions is enforced. + * @param request.terms.commercializerCheckerData The data to be passed to the commercializer checker contract. + * @param request.terms.commercialRevShare Percentage of revenue that must be shared with the licensor. + * @param request.terms.commercialRevCeiling The maximum revenue that can be generated from the commercial use of the work. + * @param request.terms.derivativesAllowed Indicates whether the licensee can create derivatives of his work or not. + * @param request.terms.derivativesAttribution Indicates whether attribution is required for derivatives of the work or not. + * @param request.terms.derivativesApproval Indicates whether the licensor must approve derivatives of the work before they can be linked to the licensor IP ID or not. + * @param request.terms.derivativesReciprocal Indicates whether the licensee must license derivatives of the work under the same terms or not. + * @param request.terms.derivativeRevCeiling The maximum revenue that can be generated from the derivative use of the work. + * @param request.terms.currency The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. + * @param request.terms.uri The URI of the license terms, which can be used to fetch the offchain license terms. + * @param request.ipMetadata - [Optional] The desired metadata for the newly minted NFT and newly registered IP. + * @param request.ipMetadata.ipMetadataURI [Optional] The URI of the metadata for the IP. + * @param request.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. + * @param request.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. + * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. + * @param {Array} request.royaltyShares Authors of the IP and their shares of the royalty tokens. + * @param request.royaltyShares.author The address of the author. + * @param request.royaltyShares.percentage The percentage of the royalty share, 10 represents 10%. + * @param request.deadline [Optional] The deadline for the signature in seconds, default is 1000s. + * @param request.txOptions [Optional] This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property, without encodedTxData option. + * @returns A Promise that resolves to a transaction hashes, IP ID, License terms ID, and IP royalty vault. + * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) + * @emits IpRoyaltyVaultDeployed (ipId, ipRoyaltyVault) + */ + public async registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens( + request: RegisterIPAndAttachLicenseTermsAndDistributeRoyaltyTokensRequest, + ): Promise { + try { + if (!request.terms.commercialUse) { + throw new Error("Commercial use is required to deploy a royalty vault."); + } + const { royaltyShares, totalAmount } = this.getRoyaltyShares(request.royaltyShares); + const licenseTerm = await validateLicenseTerms(request.terms, this.rpcClient); + const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; + const calculatedDeadline = getDeadline(blockTimestamp, request.deadline); + const ipIdAddress = await this.getIpIdAddress( + getAddress(request.nftContract, "request.nftContract"), + request.tokenId, + ); + const isRegistered = await this.isRegistered(ipIdAddress); + if (isRegistered) { + throw new Error(`The NFT with id ${request.tokenId} is already registered as IP.`); + } + const { signature: sigMetadataSignature, nonce: sigMetadataState } = + await getPermissionSignature({ + ipId: ipIdAddress, + deadline: calculatedDeadline, + state: toHex(0, { size: 32 }), + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + permissions: [ + { + ipId: ipIdAddress, + signer: getAddress( + this.royaltyTokenDistributionWorkflowsClient.address, + "royaltyTokenDistributionWorkflowsClient", + ), + to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function setAll(address,string,bytes32,bytes32)", + }, + ], + }); + const { signature: sigAttachSignature, nonce: sigAttachState } = await getPermissionSignature( + { + ipId: ipIdAddress, + deadline: calculatedDeadline, + state: sigMetadataState, + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + permissions: [ + { + ipId: ipIdAddress, + signer: this.royaltyTokenDistributionWorkflowsClient.address, + to: getAddress(this.licensingModuleClient.address, "licensingModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function attachLicenseTerms(address,address,uint256)", + }, + ], + }, + ); + const registerIpAndAttachPilTermsAndDeployRoyaltyVaultTxHash = + await this.royaltyTokenDistributionWorkflowsClient.registerIpAndAttachPilTermsAndDeployRoyaltyVault( + { + nftContract: request.nftContract, + tokenId: BigInt(request.tokenId), + ipMetadata: { + ipMetadataURI: request.ipMetadata?.ipMetadataURI || "", + ipMetadataHash: request.ipMetadata?.ipMetadataHash || zeroHash, + nftMetadataURI: request.ipMetadata?.nftMetadataURI || "", + nftMetadataHash: request.ipMetadata?.nftMetadataHash || zeroHash, + }, + terms: licenseTerm, + sigMetadata: { + signer: this.wallet.account!.address, + deadline: calculatedDeadline, + signature: sigMetadataSignature, + }, + sigAttach: { + signer: getAddress(this.wallet.account!.address, "wallet.account.address"), + deadline: calculatedDeadline, + signature: sigAttachSignature, + }, + }, + ); + const txReceipt = await this.rpcClient.waitForTransactionReceipt({ + ...request.txOptions, + hash: registerIpAndAttachPilTermsAndDeployRoyaltyVaultTxHash, + }); + const { ipId } = this.getIpIdAndTokenIdsFromEvent(txReceipt)[0]; + const licenseTermsId = await this.getLicenseTermsId([licenseTerm]); + const { ipRoyaltyVault } = + this.royaltyModuleEventClient.parseTxIpRoyaltyVaultDeployedEvent(txReceipt)[0]; + const distributeRoyaltyTokensTxHash = await this.distributeRoyaltyTokens({ + ipId, + deadline: calculatedDeadline, + state: sigAttachState, + ipRoyaltyVault, + royaltyShares: royaltyShares, + totalAmount: totalAmount, + txOptions: request.txOptions, + }); + if (request.txOptions?.waitForTransaction) { + await this.rpcClient.waitForTransactionReceipt({ + ...request.txOptions, + hash: distributeRoyaltyTokensTxHash, + }); + } + return { + registerIpAndAttachPilTermsAndDeployRoyaltyVaultTxHash, + distributeRoyaltyTokensTxHash, + ipId, + licenseTermsId: licenseTermsId[0], + ipRoyaltyVault, + }; + } catch (error) { + handleError( + error, + "Failed to register IP and attach license terms and distribute royalty tokens", + ); + } + } + /** + * Register the given NFT as a derivative IP and attach license terms and distribute royalty tokens. In order to successfully distribute royalty tokens, the license terms attached to the IP must be + * a commercial license. + * @param request - The request object that contains all data needed to register derivative IP and distribute royalty tokens. + * @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.ipMetadata - [Optional] The desired metadata for the newly minted NFT and newly registered IP. + * @param request.ipMetadata.ipMetadataURI [Optional] The URI of the metadata for the IP. + * @param request.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. + * @param request.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. + * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. + * @param request.deadline [Optional] The deadline for the signature in seconds, default is 1000s. + * @param {Array} request.royaltyShares Authors of the IP and their shares of the royalty tokens. + * @param request.royaltyShares.author The address of the author. + * @param request.royaltyShares.percentage The percentage of the royalty share, 10 represents 10%. + * @param request.txOptions - [Optional] transaction. This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property, without encodedTxData option. + * @returns A Promise that resolves to a transaction hashes, IP ID and IP royalty vault, token ID. + * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) + * @emits IpRoyaltyVaultDeployed (ipId, ipRoyaltyVault) + */ + public async registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens( + request: RegisterDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensRequest, + ): Promise { + try { + const { royaltyShares, totalAmount } = this.getRoyaltyShares(request.royaltyShares); + const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; + const calculatedDeadline = getDeadline(blockTimestamp, request.deadline); + const ipIdAddress = await this.getIpIdAddress(request.nftContract, request.tokenId); + const isRegistered = await this.isRegistered(ipIdAddress); + if (isRegistered) { + throw new Error(`The NFT with id ${request.tokenId} is already registered as IP.`); + } + const { signature: sigMetadataSignature, nonce: sigMetadataState } = + await getPermissionSignature({ + ipId: ipIdAddress, + deadline: calculatedDeadline, + state: toHex(0, { size: 32 }), + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + permissions: [ + { + ipId: ipIdAddress, + signer: getAddress( + this.royaltyTokenDistributionWorkflowsClient.address, + "royaltyTokenDistributionWorkflowsClient", + ), + to: getAddress(this.coreMetadataModuleClient.address, "coreMetadataModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function setAll(address,string,bytes32,bytes32)", + }, + ], + }); + const { signature: sigAttachSignature, nonce: sigAttachState } = await getPermissionSignature( + { + ipId: ipIdAddress, + deadline: calculatedDeadline, + state: sigMetadataState, + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + permissions: [ + { + ipId: ipIdAddress, + signer: this.royaltyTokenDistributionWorkflowsClient.address, + to: getAddress(this.licensingModuleClient.address, "licensingModuleAddress"), + permission: AccessPermission.ALLOW, + func: "function registerDerivative(address,address[],uint256[],address,bytes)", + }, + ], + }, + ); + const txHash = + await this.royaltyTokenDistributionWorkflowsClient.registerIpAndMakeDerivativeAndDeployRoyaltyVault( + { + nftContract: request.nftContract, + tokenId: BigInt(request.tokenId), + ipMetadata: { + ipMetadataURI: request.ipMetadata?.ipMetadataURI || "", + ipMetadataHash: request.ipMetadata?.ipMetadataHash || zeroHash, + nftMetadataURI: request.ipMetadata?.nftMetadataURI || "", + nftMetadataHash: request.ipMetadata?.nftMetadataHash || zeroHash, + }, + derivData: { + ...request.derivData, + licenseTemplate: + request.derivData.licenseTemplate || this.licenseTemplateClient.address, + royaltyContext: zeroAddress, + }, + sigMetadata: { + signer: getAddress(this.wallet.account!.address, "wallet.account.address"), + deadline: calculatedDeadline, + signature: sigMetadataSignature, + }, + sigRegister: { + signer: this.wallet.account!.address, + deadline: calculatedDeadline, + signature: sigAttachSignature, + }, + }, + ); + const txReceipt = await this.rpcClient.waitForTransactionReceipt({ + ...request.txOptions, + hash: txHash, + }); + const { ipId, tokenId } = this.getIpIdAndTokenIdsFromEvent(txReceipt)[0]; + const { ipRoyaltyVault } = this.royaltyModuleEventClient + .parseTxIpRoyaltyVaultDeployedEvent(txReceipt) + .filter((item) => item.ipId === ipId)[0]; + const distributeRoyaltyTokensTxHash = await this.distributeRoyaltyTokens({ + ipId, + deadline: calculatedDeadline, + state: sigAttachState, + ipRoyaltyVault, + royaltyShares: royaltyShares, + totalAmount: totalAmount, + txOptions: request.txOptions, + }); + if (request.txOptions?.waitForTransaction) { + await this.rpcClient.waitForTransactionReceipt({ + ...request.txOptions, + hash: distributeRoyaltyTokensTxHash, + }); + } + return { + registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensTxHash: txHash, + distributeRoyaltyTokensTxHash, + ipId, + tokenId, + ipRoyaltyVault, + }; + } catch (error) { + handleError( + error, + "Failed to register derivative IP and attach license terms and distribute royalty tokens", + ); + } + } + /** + * Mint an NFT and register the IP, attach PIL terms, and distribute royalty tokens. In order to successfully distribute royalty tokens, the license terms attached to the IP must be + * a commercial license. + * @param request - The request object that contains all data needed to mint an NFT and register the IP, attach PIL terms, and distribute royalty tokens. + * @param request.spgNftContract The address of the SPG NFT contract. + * @param request.terms The array of license terms to be attached. + * @param request.terms.transferable Indicates whether the license is transferable or not. + * @param request.terms.royaltyPolicy The address of the royalty policy contract which required to StoryProtocol in advance. + * @param request.terms.mintingFee The fee to be paid when minting a license. + * @param request.terms.expiration The expiration period of the license. + * @param request.terms.commercialUse Indicates whether the work can be used commercially or not, Commercial use is required to deploy a royalty vault. + * @param request.terms.commercialAttribution Whether attribution is required when reproducing the work commercially or not. + * @param request.terms.commercializerChecker Commercializers that are allowed to commercially exploit the work. If zero address, then no restrictions is enforced. + * @param request.terms.commercializerCheckerData The data to be passed to the commercializer checker contract. + * @param request.terms.commercialRevShare Percentage of revenue that must be shared with the licensor. + * @param request.terms.commercialRevCeiling The maximum revenue that can be generated from the commercial use of the work. + * @param request.terms.derivativesAllowed Indicates whether the licensee can create derivatives of his work or not. + * @param request.terms.derivativesAttribution Indicates whether attribution is required for derivatives of the work or not. + * @param request.terms.derivativesApproval Indicates whether the licensor must approve derivatives of the work before they can be linked to the licensor IP ID or not. + * @param request.terms.derivativesReciprocal Indicates whether the licensee must license derivatives of the work under the same terms or not. + * @param request.terms.derivativeRevCeiling The maximum revenue that can be generated from the derivative use of the work. + * @param request.terms.currency The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. + * @param request.terms.uri The URI of the license terms, which can be used to fetch the offchain license terms. + * @param request.ipMetadata - [Optional] The desired metadata for the newly minted NFT and newly registered IP. + * @param request.ipMetadata.ipMetadataURI [Optional] The URI of the metadata for the IP. + * @param request.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. + * @param request.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. + * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. + * @param {Array} request.royaltyShares Authors of the IP and their shares of the royalty tokens. + * @param request.royaltyShares.author The address of the author. + * @param request.royaltyShares.percentage The percentage of the royalty share, 10 represents 10%. + * @param request.recipient - [Optional] The address to receive the minted NFT,default value is your wallet address. + * @param request.txOptions [Optional] This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property, without encodedTxData option. + * @returns A Promise that resolves to a transaction hash, IP ID, License terms ID, and IP royalty vault, Token ID. + * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) + */ + public async mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens( + request: MintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokensRequest, + ): Promise { + try { + if (!request.terms.commercialUse) { + throw new Error("Commercial use is required to deploy a royalty vault."); + } + const licenseTerm = await validateLicenseTerms(request.terms, this.rpcClient); + const { royaltyShares } = this.getRoyaltyShares(request.royaltyShares); + const txHash = + await this.royaltyTokenDistributionWorkflowsClient.mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens( + { + spgNftContract: getAddress(request.spgNftContract, "request.spgNftContract"), + recipient: + (request.recipient && getAddress(request.recipient, "request.recipient")) || + this.wallet.account!.address, + ipMetadata: { + ipMetadataURI: request.ipMetadata?.ipMetadataURI || "", + ipMetadataHash: request.ipMetadata?.ipMetadataHash || zeroHash, + nftMetadataURI: request.ipMetadata?.nftMetadataURI || "", + nftMetadataHash: request.ipMetadata?.nftMetadataHash || zeroHash, + }, + terms: licenseTerm, + royaltyShares, + }, + ); + if (request.txOptions?.waitForTransaction) { + const txReceipt = await this.rpcClient.waitForTransactionReceipt({ + ...request.txOptions, + hash: txHash, + }); + const { ipId, tokenId } = this.getIpIdAndTokenIdsFromEvent(txReceipt)[0]; + const licenseTermsId = await this.getLicenseTermsId([licenseTerm]); + const { ipRoyaltyVault } = + this.royaltyModuleEventClient.parseTxIpRoyaltyVaultDeployedEvent(txReceipt)[0]; + return { + txHash, + ipId, + licenseTermsId: licenseTermsId[0], + ipRoyaltyVault, + tokenId, + }; + } + return { txHash }; + } catch (error) { + handleError( + error, + "Failed to mint and register IP and attach PIL terms and distribute royalty tokens", + ); + } + } + /** + * Mint an NFT and register the IP, make a derivative, and distribute royalty tokens. In order to successfully distribute royalty tokens, the license terms attached to the IP must be + * a commercial license. + * @param request - The request object that contains all data needed to mint an NFT and register the IP, make a derivative, and distribute royalty tokens. + * @param request.spgNftContract The address of the SPG NFT collection. + * @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.ipMetadata - [Optional] The desired metadata for the newly minted NFT and newly registered IP. + * @param request.ipMetadata.ipMetadataURI [Optional] The URI of the metadata for the IP. + * @param request.ipMetadata.ipMetadataHash [Optional] The hash of the metadata for the IP. + * @param request.ipMetadata.nftMetadataURI [Optional] The URI of the metadata for the NFT. + * @param request.ipMetadata.nftMetadataHash [Optional] The hash of the metadata for the IP NFT. + * @param {Array} request.royaltyShares Authors of the IP and their shares of the royalty tokens. + * @param request.royaltyShares.author The address of the author. + * @param request.royaltyShares.percentage The percentage of the royalty share, 10 represents 10%. + * @param request.recipient - [Optional] The address to receive the minted NFT,default value is your wallet address. + * @param request.txOptions - [Optional] transaction. This extends `WaitForTransactionReceiptParameters` from the Viem library, excluding the `hash` property, without encodedTxData option.. + * @returns A Promise that resolves to a transaction hash, IP ID and token ID. + * @emits IPRegistered (ipId, chainId, tokenContract, tokenId, name, uri, registrationDate) + */ + public async mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens( + request: MintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest, + ): Promise { + try { + const licenseTerms: bigint[] = []; + for (const id of request.derivData.licenseTermsIds) { + const licenseTermsId = BigInt(id); + const { terms } = await this.licenseTemplateClient.getLicenseTerms({ + selectedLicenseTermsId: licenseTermsId, + }); + if (!terms.commercialUse) { + throw new Error( + "The license terms attached to the IP must be a commercial license to distribute royalty tokens.", + ); + } + licenseTerms.push(licenseTermsId); + } + const { royaltyShares } = this.getRoyaltyShares(request.royaltyShares); + const txHash = + await this.royaltyTokenDistributionWorkflowsClient.mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens( + { + spgNftContract: getAddress(request.spgNftContract, "request.spgNftContract"), + recipient: + (request.recipient && getAddress(request.recipient, "request.recipient")) || + this.wallet.account!.address, + ipMetadata: { + ipMetadataURI: request.ipMetadata?.ipMetadataURI || "", + ipMetadataHash: request.ipMetadata?.ipMetadataHash || zeroHash, + nftMetadataURI: request.ipMetadata?.nftMetadataURI || "", + nftMetadataHash: request.ipMetadata?.nftMetadataHash || zeroHash, + }, + derivData: { + ...request.derivData, + licenseTemplate: + (request.derivData.licenseTemplate && + getAddress( + request.derivData.licenseTemplate, + "request.derivData.licenseTemplate", + )) || + this.licenseTemplateClient.address, + royaltyContext: zeroAddress, + licenseTermsIds: licenseTerms, + }, + royaltyShares: royaltyShares, + }, + ); + if (request.txOptions?.waitForTransaction) { + const txReceipt = await this.rpcClient.waitForTransactionReceipt({ + ...request.txOptions, + hash: txHash, + }); + const { ipId, tokenId } = this.getIpIdAndTokenIdsFromEvent(txReceipt)[0]; + return { txHash, ipId, tokenId }; + } + return { txHash }; + } catch (error) { + handleError( + error, + "Failed to mint and register IP and make derivative and distribute royalty tokens", + ); + } + } + private getRoyaltyShares(royaltyShares: RoyaltyShare[]) { + let actualTotal = 0; + let sum = 0; + const shares = royaltyShares.map((share) => { + if (share.percentage <= 0) { + throw new Error("The percentage of the royalty shares must be greater than 0."); + } + if (share.percentage > 100) { + throw new Error("The percentage of the royalty shares must be less than or equal to 100."); + } + sum += share.percentage; + if (sum > 100) { + throw new Error("The sum of the royalty shares cannot exceeds 100."); + } + const value = (share.percentage / 100) * royaltySharesTotalSupply; + actualTotal += value; + return { ...share, percentage: value }; + }); + return { royaltyShares: shares, totalAmount: actualTotal } as const; + } + + private async distributeRoyaltyTokens(request: DistributeRoyaltyTokens): Promise { + const { ipId, deadline, state, ipRoyaltyVault, totalAmount } = request; + const ipRoyaltyVaultImpl = new IpRoyaltyVaultImplReadOnlyClient(this.rpcClient, ipRoyaltyVault); + const balance = await ipRoyaltyVaultImpl.balanceOf({ account: ipId }); + if (BigInt(balance) < BigInt(totalAmount)) { + throw new Error( + `The balance of the IP account in the IP Royalty Vault is insufficient to distribute the royalty tokens.`, + ); + } + const { signature: signatureApproveRoyaltyTokens } = await getSignature({ + verifyingContract: ipId, + deadline: deadline, + state: state, + wallet: this.wallet as WalletClient, + chainId: chain[this.chainId], + to: ipRoyaltyVault, + encodeData: encodeFunctionData({ + abi: mockErc20Abi, + functionName: "approve", + args: [this.royaltyTokenDistributionWorkflowsClient.address, BigInt(totalAmount)], + }), + }); + const txHash = await this.royaltyTokenDistributionWorkflowsClient.distributeRoyaltyTokens({ + ipId, + ipRoyaltyVault: ipRoyaltyVault, + royaltyShares: request.royaltyShares, + sigApproveRoyaltyTokens: { + signer: this.wallet.account!.address, + deadline: deadline, + signature: signatureApproveRoyaltyTokens, + }, + }); + if (request.txOptions?.waitForTransaction) { + await this.rpcClient.waitForTransactionReceipt({ + ...request.txOptions, + hash: txHash, + }); + return txHash; + } + return txHash; + } private async getIpIdAddress( nftContract: Address, tokenId: bigint | string | number, @@ -1507,44 +2069,15 @@ export class IPAssetClient { return await this.ipAssetRegistryClient.isRegistered({ id: getAddress(ipId, "ipId") }); } - private getSigSignatureState(permission: Omit) { - const data = encodeFunctionData({ - abi: accessControllerAbi, - functionName: "setPermission", - args: [ - getAddress(permission.ipId, "permission.ipId"), - getAddress(permission.signer, "permission.signer"), - getAddress(permission.to, "permission.to"), - toFunctionSelector(permission.func!), - permission.permission, - ], - }); - const sigAttachState = keccak256( - encodeAbiParameters( - [ - { name: "", type: "bytes32" }, - { name: "", type: "bytes" }, - ], - [ - toHex(0, { size: 32 }), - encodeFunctionData({ - abi: ipAccountImplAbi, - functionName: "execute", - args: [this.accessControllerClient.address, 0n, data], - }), - ], - ), - ); - return sigAttachState; - } - - private getLicenseTermsId(txReceipt: TransactionReceipt): bigint { - const licensingModuleLicenseTermsAttachedEvent = - this.licensingModuleClient.parseTxLicenseTermsAttachedEvent(txReceipt); - const licenseTermsId = - licensingModuleLicenseTermsAttachedEvent.length >= 1 && - licensingModuleLicenseTermsAttachedEvent[0].licenseTermsId; - return licenseTermsId === false ? this.defaultLicenseTermsId : licenseTermsId; + private async getLicenseTermsId(licenseTerms: LicenseTerms[]): Promise { + const licenseTermsIds: bigint[] = []; + for (const licenseTerm of licenseTerms) { + const licenseRes = await this.licenseTemplateClient.getLicenseTermsId({ + terms: licenseTerm, + }); + licenseTermsIds.push(licenseRes.selectedLicenseTermsId); + } + return licenseTermsIds; } private async validateLicenseTokenIds( @@ -1565,16 +2098,20 @@ export class IPAssetClient { return newLicenseTokenIds; } - private getIpIdAndTokenIdFromEvent(txReceipt: TransactionReceipt) { + private getIpIdAndTokenIdsFromEvent( + txReceipt: TransactionReceipt, + key?: K, + ): IpIdAndTokenId[] { const IPRegisteredLog = this.ipAssetRegistryClient.parseTxIpRegisteredEvent(txReceipt); return IPRegisteredLog.map((log) => { - return { ipId: log.ipId, tokenId: log.tokenId }; + const baseResult = { ipId: log.ipId, tokenId: log.tokenId }; + if (key) { + return { + ...baseResult, + [key]: log.tokenContract, + } as IpIdAndTokenId; + } + return baseResult as IpIdAndTokenId; }); } - - private async getDefaultLicenseTerms() { - this.defaultLicenseTermsId = ( - await this.licenseRegistryReadOnlyClient.getDefaultLicenseTerms() - ).licenseTermsId; - } } diff --git a/packages/core-sdk/src/resources/permission.ts b/packages/core-sdk/src/resources/permission.ts index 353db0c6..cb8ea25a 100644 --- a/packages/core-sdk/src/resources/permission.ts +++ b/packages/core-sdk/src/resources/permission.ts @@ -124,7 +124,7 @@ export class PermissionClient { const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, deadline); - const signature = await getPermissionSignature({ + const { signature } = await getPermissionSignature({ ipId, deadline: calculatedDeadline, state, @@ -293,7 +293,7 @@ export class PermissionClient { const { result: state } = await ipAccountClient.state(); const blockTimestamp = (await this.rpcClient.getBlock()).timestamp; const calculatedDeadline = getDeadline(blockTimestamp, deadline); - const signature = await getPermissionSignature({ + const { signature } = await getPermissionSignature({ ipId, deadline: calculatedDeadline, state, diff --git a/packages/core-sdk/src/types/resources/ipAsset.ts b/packages/core-sdk/src/types/resources/ipAsset.ts index 9f1206f4..acb0089b 100644 --- a/packages/core-sdk/src/types/resources/ipAsset.ts +++ b/packages/core-sdk/src/types/resources/ipAsset.ts @@ -1,7 +1,7 @@ import { Address, Hex } from "viem"; import { TxOptions } from "../options"; -import { PIL_TYPE, RegisterPILTermsRequest } from "./license"; +import { RegisterPILTermsRequest } from "./license"; import { EncodedTxData } from "../../abi/generated"; import { IpMetadataAndTxOption } from "../common"; @@ -42,22 +42,19 @@ export type RegisterDerivativeResponse = { encodedTxData?: EncodedTxData; }; -export type CreateIpAssetWithPilTermsRequest = { +export type MintAndRegisterIpAssetWithPilTermsRequest = { spgNftContract: Address; - pilType: PIL_TYPE; - currency?: Address; - mintingFee?: string | number | bigint; + terms: RegisterPILTermsRequest[]; recipient?: Address; - commercialRevShare?: number; royaltyPolicyAddress?: Address; } & IpMetadataAndTxOption; -export type CreateIpAssetWithPilTermsResponse = { +export type MintAndRegisterIpAssetWithPilTermsResponse = { txHash?: Hex; encodedTxData?: EncodedTxData; ipId?: Address; tokenId?: bigint; - licenseTermsId?: bigint; + licenseTermsIds?: bigint[]; }; export type RegisterIpAndMakeDerivativeRequest = { @@ -81,19 +78,15 @@ export type RegisterIpAndMakeDerivativeResponse = { export type RegisterIpAndAttachPilTermsRequest = { nftContract: Address; tokenId: bigint | string | number; - pilType: PIL_TYPE; - mintingFee: string | number | bigint; - currency: Address; + terms: RegisterPILTermsRequest[]; deadline?: bigint | number | string; - commercialRevShare?: number; - royaltyPolicyAddress?: Address; } & IpMetadataAndTxOption; export type RegisterIpAndAttachPilTermsResponse = { txHash?: Hex; encodedTxData?: EncodedTxData; ipId?: Address; - licenseTermsId?: bigint; + licenseTermsIds?: bigint[]; tokenId?: bigint; }; @@ -165,6 +158,15 @@ export type IPRobotTerms = { allow: string; }; +type IPMetadataInfo = { + ipMetadata?: { + ipMetadataURI?: string; + ipMetadataHash?: Hex; + nftMetadataURI?: string; + nftMetadataHash?: Hex; + }; +}; + export type GenerateIpMetadataParam = { title?: string; description?: string; @@ -203,7 +205,7 @@ export type MintAndRegisterIpRequest = { export type RegisterPilTermsAndAttachRequest = { ipId: Address; - terms: RegisterPILTermsRequest; + terms: RegisterPILTermsRequest[]; deadline?: string | number | bigint; txOptions?: TxOptions; }; @@ -211,7 +213,7 @@ export type RegisterPilTermsAndAttachRequest = { export type RegisterPilTermsAndAttachResponse = { txHash?: Hex; encodedTxData?: EncodedTxData; - licenseTermsId?: bigint; + licenseTermsIds?: bigint[]; }; export type MintAndRegisterIpAndMakeDerivativeWithLicenseTokensRequest = { @@ -228,19 +230,24 @@ export type RegisterIpAndMakeDerivativeWithLicenseTokensRequest = { } & IpMetadataAndTxOption; export type BatchMintAndRegisterIpAssetWithPilTermsRequest = { - args: Omit[]; + args: Omit[]; txOptions?: Omit; }; - +export type BatchMintAndRegisterIpAssetWithPilTermsResult = { + ipId: Address; + tokenId: bigint; + licenseTermsIds: bigint[]; + spgNftContract: Address; +}; export type BatchMintAndRegisterIpAssetWithPilTermsResponse = { txHash: Hex; - results?: Omit[]; + results?: BatchMintAndRegisterIpAssetWithPilTermsResult[]; }; export type BatchRegisterDerivativeRequest = { args: RegisterDerivativeRequest[]; deadline?: string | number | bigint; - txOptions?: TxOptions; + txOptions?: Omit; }; export type BatchRegisterDerivativeResponse = { @@ -252,15 +259,107 @@ export type BatchMintAndRegisterIpAndMakeDerivativeRequest = { }; export type BatchMintAndRegisterIpAndMakeDerivativeResponse = { txHash: string; - results?: { ipId: Address; tokenId: bigint }[]; + results?: IpIdAndTokenId<"spgNftContract">[]; }; export type BatchRegisterRequest = { args: Omit[]; - txOptions?: TxOptions; + txOptions?: Omit; }; export type BatchRegisterResponse = { txHash: Hex; - results?: { ipId: Address; tokenId: bigint }[]; + results?: IpIdAndTokenId<"nftContract">[]; +}; + +export type RegisterIPAndAttachLicenseTermsAndDistributeRoyaltyTokensRequest = { + nftContract: Address; + tokenId: bigint | string | number; + terms: RegisterPILTermsRequest; + deadline?: string | number | bigint; + royaltyShares: RoyaltyShare[]; + txOptions?: Omit; +} & IPMetadataInfo; +export type RegisterIPAndAttachLicenseTermsAndDistributeRoyaltyTokensResponse = { + registerIpAndAttachPilTermsAndDeployRoyaltyVaultTxHash: Hex; + distributeRoyaltyTokensTxHash: Hex; + ipId: Address; + licenseTermsId: bigint; + ipRoyaltyVault: Address; +}; +export type DistributeRoyaltyTokens = { + ipId: Address; + deadline: bigint; + state: Hex; + ipRoyaltyVault: Address; + royaltyShares: RoyaltyShare[]; + totalAmount: number; + txOptions?: Omit; +}; +export type RoyaltyShare = { + author: Address; + percentage: number; +}; +export type IpIdAndTokenId = T extends undefined + ? { ipId: Address; tokenId: bigint } + : { ipId: Address; tokenId: bigint } & { [T: string]: Address }; + +export type RegisterDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensRequest = { + nftContract: Address; + tokenId: bigint | string | number; + deadline?: string | number | bigint; + derivData: { + parentIpIds: Address[]; + licenseTemplate?: Address; + licenseTermsIds: bigint[]; + }; + royaltyShares: RoyaltyShare[]; + txOptions?: Omit; +} & IPMetadataInfo; + +export type RegisterDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensResponse = { + registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensTxHash: Address; + distributeRoyaltyTokensTxHash: Address; + ipId: Address; + tokenId: bigint; + ipRoyaltyVault: Address; +}; + +export type MintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokensRequest = { + spgNftContract: Address; + terms: RegisterPILTermsRequest; + royaltyShares: { + author: Address; + percentage: number; + }[]; + recipient?: Address; + txOptions?: Omit; +} & IPMetadataInfo; + +export type MintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokensResponse = { + txHash: Hex; + ipId?: Address; + licenseTermsId?: bigint; + ipRoyaltyVault?: Address; + tokenId?: bigint; +}; +export type MintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensRequest = { + spgNftContract: Address; + derivData: { + parentIpIds: Address[]; + licenseTermsIds: string[] | bigint[] | number[]; + licenseTemplate?: Address; + }; + royaltyShares: { + author: Address; + percentage: number; + }[]; + recipient?: Address; + txOptions?: Omit; +} & IPMetadataInfo; + +export type MintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokensResponse = { + txHash: Hex; + ipId?: Address; + tokenId?: bigint; }; diff --git a/packages/core-sdk/src/types/resources/permission.ts b/packages/core-sdk/src/types/resources/permission.ts index c7df1fe2..7878aa93 100644 --- a/packages/core-sdk/src/types/resources/permission.ts +++ b/packages/core-sdk/src/types/resources/permission.ts @@ -61,8 +61,6 @@ export type PermissionSignatureRequest = { permissionFunc?: "setPermission" | "setBatchPermissions"; }; -export type PermissionSignatureResponse = Hex; - export type SignatureRequest = { state: Hex; to: Address; @@ -73,4 +71,4 @@ export type SignatureRequest = { chainId: number | bigint | string; }; -export type SignatureResponse = Hex; +export type SignatureResponse = { signature: Hex; nonce: Hex }; diff --git a/packages/core-sdk/src/utils/sign.ts b/packages/core-sdk/src/utils/sign.ts index b7d7870e..5a13f401 100644 --- a/packages/core-sdk/src/utils/sign.ts +++ b/packages/core-sdk/src/utils/sign.ts @@ -11,7 +11,6 @@ import { getAddress } from "./utils"; import { defaultFunctionSelector } from "../constants/common"; import { PermissionSignatureRequest, - PermissionSignatureResponse, SignatureRequest, SignatureResponse, } from "../types/resources/permission"; @@ -30,7 +29,7 @@ import { */ export const getPermissionSignature = async ( param: PermissionSignatureRequest, -): Promise => { +): Promise => { const { ipId, deadline, state, wallet, chainId, permissions, permissionFunc } = param; const permissionFunction = permissionFunc ? permissionFunc : "setPermission"; const accessAddress = @@ -118,7 +117,7 @@ export const getSignature = async ({ ], ), ); - return await (wallet as WalletClient).signTypedData({ + const signature = await (wallet as WalletClient).signTypedData({ account: wallet.account, domain: { name: "Story Protocol IP Account", @@ -144,4 +143,5 @@ export const getSignature = async ({ deadline: BigInt(deadline), }, }); + return { signature, nonce }; }; diff --git a/packages/core-sdk/test/integration/group.test.ts b/packages/core-sdk/test/integration/group.test.ts index 2ba2e5e4..73d1cff7 100644 --- a/packages/core-sdk/test/integration/group.test.ts +++ b/packages/core-sdk/test/integration/group.test.ts @@ -1,17 +1,17 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { Address } from "viem"; +import { Address, zeroAddress } from "viem"; import { getStoryClient, odyssey, mintBySpg } from "./utils/util"; import { PIL_TYPE, StoryClient } from "../../src"; import { MockERC20 } from "./utils/mockERC20"; -import { evenSplitGroupPoolAddress } from "../../src/abi/generated"; +import { evenSplitGroupPoolAddress, royaltyPolicyLapAddress } from "../../src/abi/generated"; const groupPoolAddress = evenSplitGroupPoolAddress[odyssey]; chai.use(chaiAsPromised); const expect = chai.expect; describe("Group Functions", () => { - let groupId: Address = "0xd275eCFe9b4754Ed7D80a6d667E15Ef5bb6F68e8"; + let groupId: Address; let client: StoryClient; let spgNftContract: Address; let licenseTermsId: bigint; @@ -34,15 +34,32 @@ describe("Group Functions", () => { ).spgNftContract!; const result = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms({ spgNftContract: spgNftContract, - pilType: PIL_TYPE.COMMERCIAL_USE, - commercialRevShare: 10, - mintingFee: "0", - currency: MockERC20.address, + terms: [ + { + transferable: true, + royaltyPolicy: royaltyPolicyLapAddress[odyssey], + defaultMintingFee: 0n, + expiration: BigInt(1000), + commercialUse: true, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "test case", + }, + ], txOptions: { waitForTransaction: true, }, }); - licenseTermsId = result.licenseTermsId!; + licenseTermsId = result.licenseTermsIds![0]; ipId = result.ipId!; }); diff --git a/packages/core-sdk/test/integration/ipAsset.test.ts b/packages/core-sdk/test/integration/ipAsset.test.ts index d46a310e..cefb6ad4 100644 --- a/packages/core-sdk/test/integration/ipAsset.test.ts +++ b/packages/core-sdk/test/integration/ipAsset.test.ts @@ -1,5 +1,5 @@ import chai from "chai"; -import { StoryClient, PIL_TYPE } from "../../src"; +import { StoryClient } from "../../src"; import { Address, Hex, toHex, zeroAddress } from "viem"; import chaiAsPromised from "chai-as-promised"; import { @@ -11,7 +11,11 @@ import { approveForLicenseToken, } from "./utils/util"; import { MockERC20 } from "./utils/mockERC20"; -import { derivativeWorkflowsAddress } from "../../src/abi/generated"; +import { + derivativeWorkflowsAddress, + royaltyPolicyLapAddress, + royaltyTokenDistributionWorkflowsAddress, +} from "../../src/abi/generated"; chai.use(chaiAsPromised); const expect = chai.expect; @@ -142,90 +146,38 @@ describe("IP Asset Functions ", () => { const result = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms({ spgNftContract: nftContract, - pilType: PIL_TYPE.COMMERCIAL_REMIX, - commercialRevShare: 10, - mintingFee: "100", - currency: MockERC20.address, + terms: [ + { + transferable: true, + royaltyPolicy: royaltyPolicyLapAddress[odyssey], + defaultMintingFee: BigInt(1), + expiration: BigInt(0), + commercialUse: true, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 90, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "", + }, + ], txOptions: { waitForTransaction: true, }, }); parentIpId = result.ipId!; - licenseTermsId = result.licenseTermsId!; + licenseTermsId = result.licenseTermsIds![0]; const mockERC20 = new MockERC20(); await mockERC20.approve(derivativeWorkflowsAddress[odyssey]); + await mockERC20.approve(royaltyTokenDistributionWorkflowsAddress[odyssey]); await mockERC20.mint(); }); - - describe("should not throw error when mint and register ip and attach pil terms", async () => { - it("Non-Commercial Remix", async () => { - const result = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms({ - spgNftContract: nftContract, - pilType: PIL_TYPE.NON_COMMERCIAL_REMIX, - ipMetadata: { - ipMetadataURI: "test-uri", - ipMetadataHash: toHex("test-metadata-hash", { size: 32 }), - nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), - nftMetadataURI: "test-nft-uri", - }, - txOptions: { waitForTransaction: true }, - }); - expect(result.txHash).to.be.a("string").and.not.empty; - }); - it("Commercial Use", async () => { - const result = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms({ - spgNftContract: nftContract, - pilType: PIL_TYPE.COMMERCIAL_USE, - commercialRevShare: 10, - mintingFee: "100", - currency: MockERC20.address, - ipMetadata: { - ipMetadataURI: "test-uri", - ipMetadataHash: toHex("test-metadata-hash", { size: 32 }), - nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), - }, - txOptions: { - waitForTransaction: true, - }, - }); - expect(result.txHash).to.be.a("string").and.not.empty; - }); - - it("Commercial Remix", async () => { - const result = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms({ - spgNftContract: nftContract, - pilType: PIL_TYPE.COMMERCIAL_REMIX, - commercialRevShare: 10, - mintingFee: "100", - currency: MockERC20.address, - ipMetadata: { - ipMetadataURI: "test-uri", - ipMetadataHash: toHex("test-metadata-hash", { size: 32 }), - nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), - }, - txOptions: { - waitForTransaction: true, - }, - }); - expect(result.txHash).to.be.a("string").and.not.empty; - }); - it("should get the related log when createIpAssetWithPilTerms given waitForTransaction is true ", async () => { - const result = await client.ipAsset.mintAndRegisterIpAssetWithPilTerms({ - spgNftContract: nftContract, - pilType: PIL_TYPE.COMMERCIAL_REMIX, - commercialRevShare: 10, - mintingFee: "100", - currency: MockERC20.address, - txOptions: { - waitForTransaction: true, - }, - }); - expect(result.txHash).to.be.a("string").and.not.empty; - expect(result.ipId).to.be.a("string").and.not.empty; - expect(result.tokenId).to.be.a("bigint"); - expect(result.licenseTermsId).to.be.a("bigint"); - }); - }); it("should not throw error when register a IP Asset given metadata", async () => { const tokenId = await mintBySpg(nftContract, "test-metadata"); const response = await client.ipAsset.register({ @@ -268,16 +220,53 @@ describe("IP Asset Functions ", () => { nftContract: nftContract, tokenId: tokenId!, deadline, - pilType: PIL_TYPE.COMMERCIAL_USE, - mintingFee: "100", - currency: MockERC20.address, + terms: [ + { + transferable: true, + royaltyPolicy: zeroAddress, + defaultMintingFee: BigInt(1), + expiration: BigInt(0), + commercialUse: false, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "", + }, + { + transferable: true, + royaltyPolicy: royaltyPolicyLapAddress[odyssey], + defaultMintingFee: BigInt(10000), + expiration: BigInt(1000), + commercialUse: true, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "test case", + }, + ], txOptions: { waitForTransaction: true, }, }); expect(result.txHash).to.be.a("string").and.not.empty; expect(result.ipId).to.be.a("string").and.not.empty; - expect(result.licenseTermsId).to.be.a("bigint"); + expect(result.licenseTermsIds).to.be.an("array").and.not.empty; }); it("should not throw error when mint and register ip and make derivative", async () => { @@ -324,31 +313,53 @@ describe("IP Asset Functions ", () => { ).ipId!; const result = await client.ipAsset.registerPilTermsAndAttach({ ipId: parentIpId, - terms: { - transferable: true, - royaltyPolicy: zeroAddress, - defaultMintingFee: BigInt(1), - expiration: BigInt(0), - commercialUse: false, - commercialAttribution: false, - commercializerChecker: zeroAddress, - commercializerCheckerData: zeroAddress, - commercialRevShare: 0, - commercialRevCeiling: BigInt(0), - derivativesAllowed: true, - derivativesAttribution: true, - derivativesApproval: false, - derivativesReciprocal: true, - derivativeRevCeiling: BigInt(0), - currency: MockERC20.address, - uri: "", - }, + terms: [ + { + transferable: true, + royaltyPolicy: zeroAddress, + defaultMintingFee: BigInt(1), + expiration: BigInt(0), + commercialUse: false, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "", + }, + { + transferable: true, + royaltyPolicy: royaltyPolicyLapAddress[odyssey], + defaultMintingFee: BigInt(10000), + expiration: BigInt(1000), + commercialUse: true, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "test case", + }, + ], deadline: 1000n, txOptions: { waitForTransaction: true, }, }); expect(result.txHash).to.be.a("string").and.not.empty; + expect(result.licenseTermsIds).to.be.an("array").and.not.empty; }); it("should not throw error when call mint and register ip and make derivative with license tokens", async () => { @@ -410,6 +421,145 @@ describe("IP Asset Functions ", () => { expect(result.txHash).to.be.a("string").and.not.empty; expect(result.ipId).to.be.a("string").and.not.empty; }); + + it("should not throw error when call register ip and attach license terms and distribute royalty tokens", async () => { + const tokenId = await mintBySpg(nftContract, "test-metadata"); + const result = await client.ipAsset.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens( + { + nftContract: nftContract, + tokenId: tokenId!, + terms: { + transferable: true, + royaltyPolicy: royaltyPolicyLapAddress[odyssey], + defaultMintingFee: BigInt(10000), + expiration: BigInt(1000), + commercialUse: true, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "test case", + }, + ipMetadata: { + ipMetadataURI: "test-uri", + ipMetadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + royaltyShares: [ + { + author: process.env.TEST_WALLET_ADDRESS! as Address, + percentage: 1, + }, + ], + txOptions: { + waitForTransaction: true, + }, + }, + ); + expect(result.registerIpAndAttachPilTermsAndDeployRoyaltyVaultTxHash).to.be.a("string").and + .not.empty; + expect(result.distributeRoyaltyTokensTxHash).to.be.a("string").and.not.empty; + expect(result.ipId).to.be.a("string").and.not.empty; + expect(result.licenseTermsId).to.be.a("bigint"); + }); + + it("should not throw error when call register derivative and attach license terms and distribute royalty tokens", async () => { + const tokenId = await getTokenId(); + const result = + await client.ipAsset.registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: mockERC721, + tokenId: tokenId!, + derivData: { + parentIpIds: [parentIpId!], + licenseTermsIds: [licenseTermsId], + }, + royaltyShares: [ + { + author: process.env.TEST_WALLET_ADDRESS! as Address, + percentage: 10, //100% + }, + ], + }); + expect( + result.registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensTxHash, + ).to.be.a("string"); + expect(result.distributeRoyaltyTokensTxHash).to.be.a("string"); + expect(result.ipId).to.be.a("string"); + expect(result.tokenId).to.be.a("bigint"); + }); + + it("should not throw error when call mint and register ip and attach pil terms and distribute royalty tokens", async () => { + const result = + await client.ipAsset.mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens({ + spgNftContract: nftContract, + terms: { + transferable: true, + royaltyPolicy: royaltyPolicyLapAddress[odyssey], + defaultMintingFee: BigInt(10000), + expiration: BigInt(1000), + commercialUse: true, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "test case", + }, + ipMetadata: { + ipMetadataURI: "test-uri", + ipMetadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + royaltyShares: [ + { + author: process.env.TEST_WALLET_ADDRESS! as Address, + percentage: 10, //100% + }, + ], + txOptions: { + waitForTransaction: true, + }, + }); + expect(result.txHash).to.be.a("string"); + expect(result.ipId).to.be.a("string"); + expect(result.licenseTermsId).to.be.a("bigint"); + expect(result.tokenId).to.be.a("bigint"); + }); + it("should not throw error when call mint and register ip and make derivative and distribute royalty tokens", async () => { + const result = + await client.ipAsset.mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens({ + spgNftContract: nftContract, + derivData: { + parentIpIds: [parentIpId!], + licenseTermsIds: [licenseTermsId], + }, + royaltyShares: [ + { + author: process.env.TEST_WALLET_ADDRESS! as Address, + percentage: 10, //100% + }, + ], + txOptions: { + waitForTransaction: true, + }, + }); + expect(result.txHash).to.be.a("string"); + expect(result.ipId).to.be.a("string"); + expect(result.tokenId).to.be.a("bigint"); + }); }); describe("Multicall", () => { @@ -474,17 +624,70 @@ describe("IP Asset Functions ", () => { args: [ { spgNftContract: nftContract, - pilType: PIL_TYPE.COMMERCIAL_REMIX, - commercialRevShare: 10, - mintingFee: "100", - currency: MockERC20.address, + terms: [ + { + transferable: true, + royaltyPolicy: zeroAddress, + defaultMintingFee: BigInt(8), + expiration: BigInt(0), + commercialUse: false, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "", + }, + { + transferable: true, + royaltyPolicy: zeroAddress, + defaultMintingFee: BigInt(1), + expiration: BigInt(0), + commercialUse: false, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "", + }, + ], }, { spgNftContract: nftContract, - pilType: PIL_TYPE.COMMERCIAL_REMIX, - commercialRevShare: 10, - mintingFee: "100", - currency: MockERC20.address, + terms: [ + { + transferable: true, + royaltyPolicy: zeroAddress, + defaultMintingFee: BigInt(1), + expiration: BigInt(0), + commercialUse: false, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "", + }, + ], }, ], txOptions: { @@ -493,6 +696,8 @@ describe("IP Asset Functions ", () => { }); expect(result.txHash).to.be.a("string").and.not.empty; expect(result.results).to.be.an("array").and.not.empty; + expect(result.results![0].licenseTermsIds).to.be.an("array").and.length(2); + expect(result.results![1].licenseTermsIds).to.be.an("array").and.length(1); }); it("should not throw error when call batch mint and register ip asset and make derivative", async () => { diff --git a/packages/core-sdk/test/unit/resources/ipAsset.test.ts b/packages/core-sdk/test/unit/resources/ipAsset.test.ts index b20877e0..b70e78f5 100644 --- a/packages/core-sdk/test/unit/resources/ipAsset.test.ts +++ b/packages/core-sdk/test/unit/resources/ipAsset.test.ts @@ -1,12 +1,7 @@ import chai from "chai"; import { createMock } from "../testUtils"; import * as sinon from "sinon"; -import { - CreateIpAssetWithPilTermsRequest, - IPAssetClient, - LicenseTerms, - PIL_TYPE, -} from "../../../src"; +import { IPAssetClient, LicenseTerms } from "../../../src"; import { PublicClient, WalletClient, @@ -18,18 +13,36 @@ import { Address, } from "viem"; import chaiAsPromised from "chai-as-promised"; -import { RegisterIpAndAttachPilTermsRequest } from "../../../src/types/resources/ipAsset"; import { MockERC20 } from "../../integration/utils/mockERC20"; -import { - LicenseRegistryReadOnlyClient, - LicensingModuleLicenseTermsAttachedEvent, -} from "../../../src/abi/generated"; -const { RoyaltyModuleReadOnlyClient } = require("../../../src/abi/generated"); -const { IpAccountImplClient } = require("../../../src/abi/generated"); +import { LicenseRegistryReadOnlyClient } from "../../../src/abi/generated"; +import { royaltySharesTotalSupply } from "../../../src/constants/common"; +const { + RoyaltyModuleReadOnlyClient, + IpRoyaltyVaultImplReadOnlyClient, + IpAccountImplClient, +} = require("../../../src/abi/generated"); const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"; chai.use(chaiAsPromised); const expect = chai.expect; - +const licenseTerms: LicenseTerms = { + transferable: true, + royaltyPolicy: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + defaultMintingFee: BigInt(1), + expiration: BigInt(0), + commercialUse: true, + commercialAttribution: false, + commercializerChecker: zeroAddress, + commercializerCheckerData: zeroAddress, + commercialRevShare: 0, + commercialRevCeiling: BigInt(0), + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCeiling: BigInt(0), + currency: MockERC20.address, + uri: "", +}; describe("Test IpAssetClient", () => { let ipAssetClient: IPAssetClient; let rpcMock: PublicClient; @@ -45,7 +58,11 @@ describe("Test IpAssetClient", () => { licenseTemplate: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", licenseTermsId: 5n, }); - + RoyaltyModuleReadOnlyClient.prototype.isWhitelistedRoyaltyPolicy = sinon.stub().resolves(true); + RoyaltyModuleReadOnlyClient.prototype.isWhitelistedRoyaltyToken = sinon.stub().resolves(true); + IpRoyaltyVaultImplReadOnlyClient.prototype.balanceOf = sinon + .stub() + .resolves(royaltySharesTotalSupply); ipAssetClient = new IPAssetClient(rpcMock, walletMock, "1516"); walletMock.signTypedData = sinon .stub() @@ -64,6 +81,8 @@ describe("Test IpAssetClient", () => { "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"; (ipAssetClient.licenseTemplateClient as any).address = "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"; + (ipAssetClient.royaltyTokenDistributionWorkflowsClient as any).address = + "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"; }); afterEach(() => { @@ -678,27 +697,13 @@ describe("Test IpAssetClient", () => { }); describe("Test ipAssetClient.createIpAssetWithPilTerms", async () => { - it("throw PIL_TYPE error when createIpAssetWithPilTerms given PIL_TYPE is not match", async () => { - try { - await ipAssetClient.mintAndRegisterIpAssetWithPilTerms({ - spgNftContract, - } as unknown as CreateIpAssetWithPilTermsRequest); - } catch (err) { - expect((err as Error).message).equal( - "Failed to mint and register IP and attach PIL terms: PIL type is required.", - ); - } - }); - it("should throw address error when createIpAssetWithPilTerms given spgNftContract is wrong address", async () => { sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); try { await ipAssetClient.mintAndRegisterIpAssetWithPilTerms({ spgNftContract: "0x", - pilType: PIL_TYPE.COMMERCIAL_USE, - mintingFee: "100", - currency: zeroAddress, + terms: [licenseTerms], }); } catch (err) { expect((err as Error).message).equal( @@ -709,13 +714,11 @@ describe("Test IpAssetClient", () => { it("should return txHash when createIpAssetWithPilTerms given correct args", async () => { sinon - .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "mintAndRegisterIpAndAttachPilTerms") + .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "mintAndRegisterIpAndAttachPilTerms2") .resolves(txHash); const result = await ipAssetClient.mintAndRegisterIpAssetWithPilTerms({ spgNftContract, - pilType: PIL_TYPE.COMMERCIAL_USE, - mintingFee: "100", - currency: zeroAddress, + terms: [licenseTerms], recipient: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", royaltyPolicyAddress: zeroAddress, ipMetadata: { @@ -729,7 +732,7 @@ describe("Test IpAssetClient", () => { it("should return ipId, tokenId, licenseTermsId,txHash when createIpAssetWithPilTerms given correct args and waitForTransaction of true", async () => { sinon - .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "mintAndRegisterIpAndAttachPilTerms") + .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "mintAndRegisterIpAndAttachPilTerms2") .resolves(txHash); sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ { @@ -742,19 +745,47 @@ describe("Test IpAssetClient", () => { registrationDate: 0n, }, ]); - sinon.stub(ipAssetClient.licensingModuleClient, "parseTxLicenseTermsAttachedEvent").returns([ + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 5n }); + const result = await ipAssetClient.mintAndRegisterIpAssetWithPilTerms({ + spgNftContract, + terms: [licenseTerms], + ipMetadata: { + nftMetadataHash: toHex(0, { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }); + + expect(result.txHash).to.equal(txHash); + expect(result.ipId).to.equal("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); + expect(result.licenseTermsIds![0]).to.equal(5n); + expect(result.tokenId).to.equal(1n); + }); + + it("should return ipId, tokenId, licenseTermsId,txHash when createIpAssetWithPilTerms given correct args and waitForTransaction of true with default license terms id", async () => { + sinon + .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "mintAndRegisterIpAndAttachPilTerms2") + .resolves(txHash); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ { ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - caller: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", - licenseTemplate: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - licenseTermsId: 0n, + chainId: 0n, + tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 1n, + name: "", + uri: "", + registrationDate: 0n, }, ]); + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 5n }); const result = await ipAssetClient.mintAndRegisterIpAssetWithPilTerms({ spgNftContract, - pilType: PIL_TYPE.COMMERCIAL_USE, - mintingFee: "100", - currency: zeroAddress, + terms: [licenseTerms], ipMetadata: { nftMetadataHash: toHex(0, { size: 32 }), }, @@ -765,19 +796,14 @@ describe("Test IpAssetClient", () => { expect(result.txHash).to.equal(txHash); expect(result.ipId).to.equal("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); - expect(result.licenseTermsId).to.equal(0n); + expect(result.licenseTermsIds![0]).to.equal(5n); expect(result.tokenId).to.equal(1n); }); it("should return encoded tx data when createIpAssetWithPilTerms given correct args and encodedTxDataOnly is true", async () => { - // sinon - // .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "mintAndRegisterIpAndAttachPilTerms") - // .resolves(txHash); const result = await ipAssetClient.mintAndRegisterIpAssetWithPilTerms({ spgNftContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - pilType: 0, - mintingFee: "100", - currency: zeroAddress, + terms: [licenseTerms], recipient: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", ipMetadata: { ipMetadataURI: "", @@ -1021,9 +1047,7 @@ describe("Test IpAssetClient", () => { ipMetadataHash: toHex("metadata", { size: 32 }), nftMetadataHash: toHex("nftMetadata", { size: 32 }), }, - pilType: PIL_TYPE.COMMERCIAL_USE, - mintingFee: "100", - currency: zeroAddress, + terms: [licenseTerms], }); } catch (err) { expect((err as Error).message).equal( @@ -1031,50 +1055,10 @@ describe("Test IpAssetClient", () => { ); } }); - it("should throw PIL_TYPE error when registerIpAndAttachPilTerms given PIL_TYPE is not match", async () => { - try { - await ipAssetClient.registerIpAndAttachPilTerms({ - spgNftContract, - tokenId: "3", - } as unknown as RegisterIpAndAttachPilTermsRequest); - } catch (err) { - expect((err as Error).message).equal( - "Failed to register IP and attach PIL terms: PIL type is required.", - ); - } - }); - - it("should called with initial metadata when registerIpAndAttachPilTerms given empty ipMetadataURI", async () => { - const stub = sinon.stub( - ipAssetClient.licenseAttachmentWorkflowsClient, - "registerIpAndAttachPilTerms", - ); - sinon - .stub(ipAssetClient.ipAssetRegistryClient, "ipId") - .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); - sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); - await ipAssetClient.registerIpAndAttachPilTerms({ - nftContract: spgNftContract, - tokenId: "3", - ipMetadata: { - ipMetadataHash: toHex(0, { size: 32 }), - ipMetadataURI: "", - }, - pilType: PIL_TYPE.COMMERCIAL_USE, - mintingFee: "100", - currency: zeroAddress, - }); - expect(stub.args[0][0].ipMetadata).to.deep.equal({ - ipMetadataURI: "", - ipMetadataHash: toHex(0, { size: 32 }), - nftMetadataHash: toHex(0, { size: 32 }), - nftMetadataURI: "", - }); - }); it("should return hash when registerIpAndAttachPilTerms given correct args", async () => { sinon - .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "registerIpAndAttachPilTerms") + .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "registerIpAndAttachPilTerms2") .resolves(txHash); sinon .stub(ipAssetClient.ipAssetRegistryClient, "ipId") @@ -1087,10 +1071,7 @@ describe("Test IpAssetClient", () => { ipMetadata: { ipMetadataHash: toHex(0, { size: 32 }), }, - pilType: PIL_TYPE.COMMERCIAL_USE, - mintingFee: "100", - currency: zeroAddress, - royaltyPolicyAddress: zeroAddress, + terms: [licenseTerms], }); expect(result.txHash).to.equal(txHash); @@ -1103,11 +1084,11 @@ describe("Test IpAssetClient", () => { sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); sinon - .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "registerIpAndAttachPilTerms") + .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "registerIpAndAttachPilTerms2") .resolves(txHash); sinon - .stub(ipAssetClient.licensingModuleClient, "parseTxLicenseTermsAttachedEvent") - .returns([]); + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 5n }); sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ { ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", @@ -1125,9 +1106,7 @@ describe("Test IpAssetClient", () => { ipMetadata: { ipMetadataURI: "https://", }, - pilType: PIL_TYPE.COMMERCIAL_USE, - mintingFee: 1, - currency: zeroAddress, + terms: [licenseTerms], txOptions: { waitForTransaction: true, }, @@ -1135,7 +1114,7 @@ describe("Test IpAssetClient", () => { expect(result.txHash).to.equal(txHash); expect(result.ipId).to.equal("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); - expect(result.licenseTermsId).to.equal(5n); + expect(result.licenseTermsIds).to.deep.equal([5n]); }); it("should return encoded tx data when registerIpAndAttachPilTerms given correct args and encodedTxDataOnly of true", async () => { @@ -1143,27 +1122,19 @@ describe("Test IpAssetClient", () => { .stub(ipAssetClient.ipAssetRegistryClient, "ipId") .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); - sinon - .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "registerIpAndAttachPilTerms") + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 5n }); + sinon + .stub(ipAssetClient.licenseAttachmentWorkflowsClient, "registerIpAndAttachPilTerms2") .resolves(txHash); - sinon.stub(ipAssetClient.licensingModuleClient, "parseTxLicenseTermsAttachedEvent").returns([ - { - ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - caller: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", - licenseTemplate: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - licenseTermsId: 0n, - }, - ]); const result = await ipAssetClient.registerIpAndAttachPilTerms({ nftContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c662ac", tokenId: "3", ipMetadata: { ipMetadataURI: "https://", }, - pilType: 0, - mintingFee: 1, - currency: zeroAddress, + terms: [licenseTerms], txOptions: { encodedTxDataOnly: true, }, @@ -1427,38 +1398,15 @@ describe("Test IpAssetClient", () => { describe("Test ipAssetClient.registerPilTermsAndAttach", async () => { beforeEach(() => { - RoyaltyModuleReadOnlyClient.prototype.isWhitelistedRoyaltyPolicy = sinon - .stub() - .resolves(true); - RoyaltyModuleReadOnlyClient.prototype.isWhitelistedRoyaltyToken = sinon.stub().resolves(true); sinon .stub(IpAccountImplClient.prototype, "state") .resolves({ result: "0x2e778894d11b5308e4153f094e190496c1e0609652c19f8b87e5176484b9a56e" }); }); - const licenseTerms: LicenseTerms = { - defaultMintingFee: 1513n, - currency: MockERC20.address, - royaltyPolicy: zeroAddress, - transferable: false, - expiration: 0n, - commercialUse: false, - commercialAttribution: false, - commercializerChecker: zeroAddress, - commercializerCheckerData: "0x", - commercialRevShare: 0, - commercialRevCeiling: 0n, - derivativesAllowed: false, - derivativesAttribution: false, - derivativesApproval: false, - derivativesReciprocal: false, - derivativeRevCeiling: 0n, - uri: "", - }; it("should throw ipId error when registerPilTermsAndAttach given ipId is wrong address", async () => { try { await ipAssetClient.registerPilTermsAndAttach({ ipId: "0x", - terms: licenseTerms, + terms: [licenseTerms], }); } catch (err) { expect((err as Error).message).equal( @@ -1473,7 +1421,7 @@ describe("Test IpAssetClient", () => { try { await ipAssetClient.registerPilTermsAndAttach({ ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - terms: licenseTerms, + terms: [licenseTerms], }); } catch (err) { expect((err as Error).message).equal( @@ -1490,7 +1438,7 @@ describe("Test IpAssetClient", () => { const result = await ipAssetClient.registerPilTermsAndAttach({ ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - terms: licenseTerms, + terms: [licenseTerms], txOptions: { encodedTxDataOnly: true, }, @@ -1509,7 +1457,7 @@ describe("Test IpAssetClient", () => { .resolves({ selectedLicenseTermsId: 0n }); const result = await ipAssetClient.registerPilTermsAndAttach({ ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - terms: licenseTerms, + terms: [licenseTerms], }); expect(result.txHash).to.equal(txHash); @@ -1523,25 +1471,16 @@ describe("Test IpAssetClient", () => { sinon .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") .resolves({ selectedLicenseTermsId: 0n }); - sinon.stub(ipAssetClient.licensingModuleClient, "parseTxLicenseTermsAttachedEvent").returns([ - { - ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - caller: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", - licenseTemplate: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - licenseTermsId: 0n, - }, - ]); - const result = await ipAssetClient.registerPilTermsAndAttach({ ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - terms: licenseTerms, + terms: [licenseTerms, licenseTerms], txOptions: { waitForTransaction: true, }, }); expect(result.txHash).to.equal(txHash); - expect(result.licenseTermsId).to.equal(0n); + expect(result.licenseTermsIds).to.deep.equal([0n, 0n]); }); }); @@ -1800,9 +1739,7 @@ describe("Test IpAssetClient", () => { ipMetadataURI: "", ipMetadataHash: toHex(0, { size: 32 }), }, - pilType: 0, - mintingFee: 1, - currency: zeroAddress, + terms: [licenseTerms], }, ], }); @@ -1824,9 +1761,7 @@ describe("Test IpAssetClient", () => { ipMetadataURI: "", ipMetadataHash: toHex(0, { size: 32 }), }, - pilType: 0, - mintingFee: 1, - currency: zeroAddress, + terms: [licenseTerms], }, { spgNftContract, @@ -1834,9 +1769,7 @@ describe("Test IpAssetClient", () => { ipMetadataURI: "", ipMetadataHash: toHex(0, { size: 32 }), }, - pilType: 0, - mintingFee: 1, - currency: zeroAddress, + terms: [licenseTerms], }, ], }); @@ -1859,15 +1792,6 @@ describe("Test IpAssetClient", () => { { ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD94c6627c", chainId: 0n, - tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - tokenId: 2n, - name: "", - uri: "", - registrationDate: 0n, - }, - { - ipId: "0x1daAE3197Bc469Cb97B9171a460a12dD94c6627c", - chainId: 0n, tokenContract: "0x1daAE3197Bc469Cb97B917a460a12dD95c6627c", tokenId: 3n, name: "", @@ -1875,25 +1799,9 @@ describe("Test IpAssetClient", () => { registrationDate: 0n, }, ]); - sinon.stub(ipAssetClient.licensingModuleClient, "parseTxLicenseTermsAttachedEvent").returns([ - { - ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - caller: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", - licenseTemplate: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - licenseTermsId: 0n, - }, - { - ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD94c6627c", - caller: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", - licenseTemplate: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - licenseTermsId: 4n, - }, - { - ipId: "0x1daAE3197Bc469Cb97B9171a460a12dD94c6627c", - caller: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", - licenseTemplate: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", - } as unknown as LicensingModuleLicenseTermsAttachedEvent, - ]); + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 5n }); const result = await ipAssetClient.batchMintAndRegisterIpAssetWithPilTerms({ args: [ { @@ -1902,9 +1810,7 @@ describe("Test IpAssetClient", () => { ipMetadataURI: "", ipMetadataHash: toHex(0, { size: 32 }), }, - pilType: 0, - mintingFee: 1, - currency: zeroAddress, + terms: [licenseTerms], }, { spgNftContract, @@ -1912,9 +1818,7 @@ describe("Test IpAssetClient", () => { ipMetadataURI: "", ipMetadataHash: toHex(0, { size: 32 }), }, - pilType: 0, - mintingFee: 1, - currency: zeroAddress, + terms: [licenseTerms], }, ], txOptions: { @@ -1924,9 +1828,18 @@ describe("Test IpAssetClient", () => { expect(result.txHash).to.equal(txHash); expect(result.results).to.deep.equal([ - { ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", licenseTermsId: 0n, tokenId: 1n }, - { ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD94c6627c", licenseTermsId: 4n, tokenId: 2n }, - { ipId: "0x1daAE3197Bc469Cb97B9171a460a12dD94c6627c", licenseTermsId: 5n, tokenId: 3n }, + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + licenseTermsIds: [5n], + tokenId: 1n, + spgNftContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD94c6627c", + licenseTermsIds: [5n], + spgNftContract: "0x1daAE3197Bc469Cb97B917a460a12dD95c6627c", + tokenId: 3n, + }, ]); }); }); @@ -2004,7 +1917,7 @@ describe("Test IpAssetClient", () => { { ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", chainId: 0n, - tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenContract: "0x1daAE3197Bc469Cb972B917aa460a12dD95c6627c", tokenId: 1n, name: "", uri: "", @@ -2013,7 +1926,7 @@ describe("Test IpAssetClient", () => { { ipId: "0x11aAE3197Bc469Cb97B9171a460a12dD95c6627c", chainId: 0n, - tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenContract: "0x1daAE3197Bc469C8b97B917aa460a12dD95c6627c", tokenId: 2n, name: "", uri: "", @@ -2057,10 +1970,12 @@ describe("Test IpAssetClient", () => { expect(result.results).to.deep.equal([ { ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + spgNftContract: "0x1daAE3197Bc469Cb972B917aa460a12dD95c6627c", tokenId: 1n, }, { ipId: "0x11aAE3197Bc469Cb97B9171a460a12dD95c6627c", + spgNftContract: "0x1daAE3197Bc469C8b97B917aa460a12dD95c6627c", tokenId: 2n, }, ]); @@ -2130,7 +2045,7 @@ describe("Test IpAssetClient", () => { { ipId: "0x1daAE3197Bc469Cb87B917aa460a12dD95c6627c", chainId: 0n, - tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenContract: "0x1daAE3197Bc469Cbd97B917aa460a12dD95c6627c", tokenId: 2n, name: "", uri: "", @@ -2163,10 +2078,12 @@ describe("Test IpAssetClient", () => { expect(result.results).to.deep.equal([ { ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + nftContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", tokenId: 1n, }, { ipId: "0x1daAE3197Bc469Cb87B917aa460a12dD95c6627c", + nftContract: "0x1daAE3197Bc469Cbd97B917aa460a12dD95c6627c", tokenId: 2n, }, ]); @@ -2267,4 +2184,637 @@ describe("Test IpAssetClient", () => { .false; }); }); + + describe("Test ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens", async () => { + it("should throw ipId registered error when registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens given ipId is registered", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(true); + sinon + .stub(ipAssetClient.ipAssetRegistryClient, "ipId") + .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); + try { + await ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: spgNftContract, + tokenId: "1", + terms: licenseTerms, + royaltyShares: [ + { + author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", + percentage: 1, + }, + ], + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to register IP and attach license terms and distribute royalty tokens: The NFT with id 1 is already registered as IP.", + ); + } + }); + it("should throw commercial terms error when registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens given commercial terms is not false", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(true); + try { + await ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: spgNftContract, + tokenId: "1", + terms: { + ...licenseTerms, + commercialUse: false, + }, + royaltyShares: [ + { + author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", + percentage: 1, + }, + ], + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to register IP and attach license terms and distribute royalty tokens: Commercial use is required to deploy a royalty vault.", + ); + } + }); + + it("should throw percentage error when registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens given percentage is less 0", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(true); + try { + await ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: spgNftContract, + tokenId: "1", + terms: licenseTerms, + royaltyShares: [{ author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: -1 }], + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to register IP and attach license terms and distribute royalty tokens: The percentage of the royalty shares must be greater than 0.", + ); + } + }); + + it("should throw percentage error when registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens given percentage is greater 100", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(true); + try { + await ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: spgNftContract, + tokenId: "1", + terms: licenseTerms, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 101 }, + ], + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to register IP and attach license terms and distribute royalty tokens: The percentage of the royalty shares must be less than or equal to 100.", + ); + } + }); + + it("should throw percentage error when registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens given total percentage is greater 100", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(true); + + try { + await ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: spgNftContract, + tokenId: "1", + terms: licenseTerms, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 10 }, + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }, + ], + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to register IP and attach license terms and distribute royalty tokens: The sum of the royalty shares cannot exceeds 100.", + ); + } + }); + it("should return txHash when registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens given correct args", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); + sinon + .stub(ipAssetClient.ipAssetRegistryClient, "ipId") + .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "registerIpAndAttachPilTermsAndDeployRoyaltyVault", + ) + .resolves("0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"); + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 8n }); + sinon + .stub(ipAssetClient.royaltyModuleEventClient, "parseTxIpRoyaltyVaultDeployedEvent") + .returns([ + { + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + ]); + sinon + .stub(ipAssetClient.royaltyTokenDistributionWorkflowsClient, "distributeRoyaltyTokens") + .resolves(txHash); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + chainId: 0n, + tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + name: "", + uri: "", + registrationDate: 0n, + }, + ]); + const result = await ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: spgNftContract, + tokenId: "1", + terms: licenseTerms, + royaltyShares: [{ author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }], + }); + expect(result).to.deep.equal({ + registerIpAndAttachPilTermsAndDeployRoyaltyVaultTxHash: + "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997", + distributeRoyaltyTokensTxHash: txHash, + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + licenseTermsId: 8n, + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }); + }); + + it("should throw error when registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens given IpAccount balance is not enough", async () => { + IpRoyaltyVaultImplReadOnlyClient.prototype.balanceOf = sinon.stub().resolves(100); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); + sinon + .stub(ipAssetClient.ipAssetRegistryClient, "ipId") + .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "registerIpAndAttachPilTermsAndDeployRoyaltyVault", + ) + .resolves("0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + chainId: 0n, + tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + name: "", + uri: "", + registrationDate: 0n, + }, + ]); + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 8n }); + sinon + .stub(ipAssetClient.royaltyModuleEventClient, "parseTxIpRoyaltyVaultDeployedEvent") + .returns([ + { + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + ]); + sinon + .stub(ipAssetClient.royaltyTokenDistributionWorkflowsClient, "distributeRoyaltyTokens") + .resolves(txHash); + try { + await ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: spgNftContract, + tokenId: "1", + terms: licenseTerms, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }, + ], + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to register IP and attach license terms and distribute royalty tokens: The balance of the IP account in the IP Royalty Vault is insufficient to distribute the royalty tokens.", + ); + } + }); + it("should return txHash when registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens given correct args and waitForTransaction of true", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); + sinon + .stub(ipAssetClient.ipAssetRegistryClient, "ipId") + .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "registerIpAndAttachPilTermsAndDeployRoyaltyVault", + ) + .resolves("0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997"); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + chainId: 0n, + tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + name: "", + uri: "", + registrationDate: 0n, + }, + ]); + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 8n }); + sinon + .stub(ipAssetClient.royaltyModuleEventClient, "parseTxIpRoyaltyVaultDeployedEvent") + .returns([ + { + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + ]); + sinon + .stub(ipAssetClient.royaltyTokenDistributionWorkflowsClient, "distributeRoyaltyTokens") + .resolves(txHash); + + const result = await ipAssetClient.registerIPAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + nftContract: spgNftContract, + tokenId: "1", + terms: licenseTerms, + royaltyShares: [{ author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }], + ipMetadata: { + ipMetadataURI: "", + ipMetadataHash: toHex(0, { size: 32 }), + nftMetadataHash: toHex("nftMetadata", { size: 32 }), + nftMetadataURI: "", + }, + txOptions: { + waitForTransaction: true, + }, + }); + expect(result).to.deep.equal({ + registerIpAndAttachPilTermsAndDeployRoyaltyVaultTxHash: + "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997", + distributeRoyaltyTokensTxHash: txHash, + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + licenseTermsId: 8n, + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }); + }); + }); + + describe("Test ipAssetClient.registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens", async () => { + it("should throw ipId registered error when registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens given ipId is registered", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(true); + sinon + .stub(ipAssetClient.ipAssetRegistryClient, "ipId") + .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); + try { + await ipAssetClient.registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + derivData: { + parentIpIds: ["0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"], + licenseTermsIds: [1n], + }, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }, + ], + nftContract: spgNftContract, + tokenId: "1", + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to register derivative IP and attach license terms and distribute royalty tokens: The NFT with id 1 is already registered as IP.", + ); + } + }); + + it("should return txHash when registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens given correct args", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); + sinon + .stub(ipAssetClient.ipAssetRegistryClient, "ipId") + .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "registerIpAndMakeDerivativeAndDeployRoyaltyVault", + ) + .resolves(txHash); + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 8n }); + sinon + .stub(ipAssetClient.royaltyModuleEventClient, "parseTxIpRoyaltyVaultDeployedEvent") + .returns([ + { + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + ]); + sinon + .stub(ipAssetClient.royaltyTokenDistributionWorkflowsClient, "distributeRoyaltyTokens") + .resolves(txHash); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + chainId: 0n, + tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + name: "", + uri: "", + registrationDate: 0n, + }, + ]); + const result = + await ipAssetClient.registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + derivData: { + parentIpIds: ["0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"], + licenseTermsIds: [1n], + }, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }, + ], + nftContract: spgNftContract, + tokenId: "1", + }); + expect(result).to.deep.equal({ + registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensTxHash: + "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997", + distributeRoyaltyTokensTxHash: txHash, + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + }); + }); + + it("should return txHash when registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens given correct args with waitForTransaction of true", async () => { + sinon.stub(ipAssetClient.ipAssetRegistryClient, "isRegistered").resolves(false); + sinon + .stub(ipAssetClient.ipAssetRegistryClient, "ipId") + .resolves("0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"); + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "registerIpAndMakeDerivativeAndDeployRoyaltyVault", + ) + .resolves(txHash); + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 8n }); + sinon + .stub(ipAssetClient.royaltyModuleEventClient, "parseTxIpRoyaltyVaultDeployedEvent") + .returns([ + { + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + ]); + sinon + .stub(ipAssetClient.royaltyTokenDistributionWorkflowsClient, "distributeRoyaltyTokens") + .resolves(txHash); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + chainId: 0n, + tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + name: "", + uri: "", + registrationDate: 0n, + }, + ]); + const result = + await ipAssetClient.registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokens({ + derivData: { + parentIpIds: ["0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"], + licenseTermsIds: [1n], + }, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }, + ], + ipMetadata: { + ipMetadataURI: "", + ipMetadataHash: toHex(0, { size: 32 }), + nftMetadataHash: toHex("nftMetadata", { size: 32 }), + nftMetadataURI: "", + }, + nftContract: spgNftContract, + tokenId: "1", + txOptions: { + waitForTransaction: true, + }, + }); + expect(result).to.deep.equal({ + registerDerivativeAndAttachLicenseTermsAndDistributeRoyaltyTokensTxHash: + "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997", + distributeRoyaltyTokensTxHash: txHash, + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + }); + }); + }); + + describe("Test ipAssetClient.mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens", async () => { + it("should commercial terms error when mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens given commercial terms is not false", async () => { + try { + await ipAssetClient.mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens({ + spgNftContract, + terms: { + ...licenseTerms, + commercialUse: false, + }, + royaltyShares: [ + { + author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", + percentage: 1, + }, + ], + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to mint and register IP and attach PIL terms and distribute royalty tokens: Commercial use is required to deploy a royalty vault.", + ); + } + }); + + it("should return txHash when mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens given correct args", async () => { + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens", + ) + .resolves(txHash); + const result = + await ipAssetClient.mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens({ + spgNftContract, + terms: licenseTerms, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }, + ], + recipient: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", + ipMetadata: { + ipMetadataURI: "", + ipMetadataHash: toHex(0, { size: 32 }), + nftMetadataHash: toHex("nftMetadata", { size: 32 }), + nftMetadataURI: "", + }, + }); + + expect(result.txHash).to.equal(txHash); + }); + + it("should return txHash when mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens given correct args and waitForTransaction of true", async () => { + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens", + ) + .resolves(txHash); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + chainId: 0n, + tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + name: "", + uri: "", + registrationDate: 0n, + }, + ]); + sinon + .stub(ipAssetClient.royaltyModuleEventClient, "parseTxIpRoyaltyVaultDeployedEvent") + .returns([ + { + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + ]); + sinon + .stub(ipAssetClient.licenseTemplateClient, "getLicenseTermsId") + .resolves({ selectedLicenseTermsId: 5n }); + const result = + await ipAssetClient.mintAndRegisterIpAndAttachPilTermsAndDistributeRoyaltyTokens({ + spgNftContract, + terms: licenseTerms, + royaltyShares: [ + { + author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", + percentage: 100, + }, + ], + txOptions: { + waitForTransaction: true, + }, + }); + expect(result).to.deep.equal({ + txHash: txHash, + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + licenseTermsId: 5n, + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }); + }); + }); + + describe("Test ipAssetClient.mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens", async () => { + it("should throw commercial terms error when mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens given license terms id is not commercial", async () => { + try { + sinon.stub(ipAssetClient.licenseTemplateClient, "getLicenseTerms").resolves({ + terms: { + ...licenseTerms, + commercialUse: false, + }, + }); + await ipAssetClient.mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens({ + spgNftContract, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }, + ], + derivData: { + parentIpIds: ["0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"], + licenseTermsIds: [1n], + }, + }); + } catch (err) { + expect((err as Error).message).equal( + "Failed to mint and register IP and make derivative and distribute royalty tokens: The license terms attached to the IP must be a commercial license to distribute royalty tokens.", + ); + } + }); + + it("should return txHash when mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens given correct args", async () => { + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens", + ) + .resolves(txHash); + sinon.stub(ipAssetClient.licenseTemplateClient, "getLicenseTerms").resolves({ + terms: licenseTerms, + }); + const result = + await ipAssetClient.mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens({ + spgNftContract, + royaltyShares: [ + { + author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", + percentage: 100, + }, + ], + derivData: { + parentIpIds: ["0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"], + licenseTermsIds: [1n], + }, + }); + expect(result.txHash).to.equal(txHash); + }); + + it("should return txHash when mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens given correct args and waitForTransaction of true", async () => { + sinon + .stub( + ipAssetClient.royaltyTokenDistributionWorkflowsClient, + "mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens", + ) + .resolves(txHash); + sinon.stub(ipAssetClient.licenseTemplateClient, "getLicenseTerms").resolves({ + terms: licenseTerms, + }); + sinon.stub(ipAssetClient.ipAssetRegistryClient, "parseTxIpRegisteredEvent").returns([ + { + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + chainId: 0n, + tokenContract: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + name: "", + uri: "", + registrationDate: 0n, + }, + ]); + sinon + .stub(ipAssetClient.royaltyModuleEventClient, "parseTxIpRoyaltyVaultDeployedEvent") + .returns([ + { + ipRoyaltyVault: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + ]); + const result = + await ipAssetClient.mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens({ + spgNftContract, + royaltyShares: [ + { author: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", percentage: 100 }, + ], + derivData: { + parentIpIds: ["0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c"], + licenseTermsIds: [1n], + licenseTemplate: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + }, + recipient: "0x73fcb515cee99e4991465ef586cfe2b072ebb512", + ipMetadata: { + ipMetadataURI: "", + ipMetadataHash: toHex(0, { size: 32 }), + nftMetadataHash: toHex("nftMetadata", { size: 32 }), + nftMetadataURI: "", + }, + txOptions: { + waitForTransaction: true, + }, + }); + expect(result).to.deep.equal({ + txHash: txHash, + ipId: "0x1daAE3197Bc469Cb97B917aa460a12dD95c6627c", + tokenId: 0n, + }); + }); + }); }); diff --git a/packages/core-sdk/test/unit/utils/ipfs.test.ts b/packages/core-sdk/test/unit/utils/ipfs.test.ts index d49862ff..b24700b4 100644 --- a/packages/core-sdk/test/unit/utils/ipfs.test.ts +++ b/packages/core-sdk/test/unit/utils/ipfs.test.ts @@ -10,7 +10,6 @@ describe("IPFS", () => { const result = convertCIDtoHashIPFS( "bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku", ); - console.log(result); expect(result).to.equal("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); }); diff --git a/packages/core-sdk/test/unit/utils/sign.test.ts b/packages/core-sdk/test/unit/utils/sign.test.ts index 055e3fef..a8f64366 100644 --- a/packages/core-sdk/test/unit/utils/sign.test.ts +++ b/packages/core-sdk/test/unit/utils/sign.test.ts @@ -64,7 +64,8 @@ describe("Sign", () => { wallet: walletClient, chainId: BigInt(odyssey), }); - expect(result).is.a("string").and.not.empty; + expect(result.signature).is.a("string").and.not.empty; + expect(result.nonce).is.a("string").and.not.empty; }); it("should return signature when call getPermissionSignature given account support signTypedData and multiple permissions", async () => { @@ -91,7 +92,8 @@ describe("Sign", () => { wallet: walletClient, chainId: BigInt(odyssey), }); - expect(result).is.a("string").and.not.empty; + expect(result.signature).is.a("string").and.not.empty; + expect(result.nonce).is.a("string").and.not.empty; }); }); describe("Get Deadline", () => { diff --git a/packages/wagmi-generator/wagmi.config.ts b/packages/wagmi-generator/wagmi.config.ts index c92fc4b0..56deb709 100644 --- a/packages/wagmi-generator/wagmi.config.ts +++ b/packages/wagmi-generator/wagmi.config.ts @@ -135,6 +135,12 @@ export default defineConfig(async () => { [odysseyChainId]: "0x44Bad1E4035a44eAC1606B222873E4a85E8b7D9c", }, }, + { + name: "RoyaltyTokenDistributionWorkflows", + address: { + [odysseyChainId]: "0x39D9C7a23AA9e33E06aAAf51ebaDd11342b5be50", + }, + }, { name: "GroupingModule", address: { @@ -209,11 +215,13 @@ export default defineConfig(async () => { "SnapshotCompleted", "RevenueTokenClaimed", "claimRevenueOnBehalfBySnapshotBatch", + "balanceOf", ], PiLicenseTemplate: [ "getLicenseTermsId", "registerLicenseTerms", "LicenseTermsRegistered", + "getLicenseTerms", ], LicensingModule: [ "attachLicenseTerms", @@ -232,6 +240,7 @@ export default defineConfig(async () => { "isWhitelistedRoyaltyPolicy", "isWhitelistedRoyaltyToken", "ipRoyaltyVaults", + "IpRoyaltyVaultDeployed", ], RoyaltyPolicyLAP: ["onRoyaltyPayment", "getRoyaltyData"], LicenseToken: ["ownerOf"], @@ -270,6 +279,13 @@ export default defineConfig(async () => { "snapshotAndClaimBySnapshotBatch", ], Multicall3: ["aggregate3"], + RoyaltyTokenDistributionWorkflows: [ + "mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens", + "mintAndRegisterIpAndMakeDerivativeAndDistributeRoyaltyTokens", + "registerIpAndAttachPILTermsAndDeployRoyaltyVault", + "distributeRoyaltyTokens", + "registerIpAndMakeDerivativeAndDeployRoyaltyVault", + ], }, }), ],