Skip to content

Commit fe1ac61

Browse files
authored
revert: license registration methods (#639)
* feat: add new PIL registration methods and update types * Implemented methods for registering non-commercial social remixing, commercial use, commercial remix, and creative commons attribution PILs in the LicenseClient. * Added corresponding request types for each new method to enhance type safety and clarity. * Updated integration and unit tests to cover the new PIL registration functionalities, ensuring proper handling of license terms and error scenarios. * Marked old methods as deprecated, guiding users to the new implementations for better consistency. * fix: update deprecation messages in LicenseClient
1 parent 06abdce commit fe1ac61

File tree

4 files changed

+692
-0
lines changed

4 files changed

+692
-0
lines changed

packages/core-sdk/src/resources/license.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ import {
3434
MintLicenseTokensRequest,
3535
MintLicenseTokensResponse,
3636
PredictMintingLicenseFeeRequest,
37+
RegisterCommercialRemixPILRequest,
38+
RegisterCommercialUsePILRequest,
39+
RegisterCreativeCommonsAttributionPILRequest,
40+
RegisterNonComSocialRemixingPILRequest,
3741
RegisterPILResponse,
3842
RegisterPilTermsAndAttachRequest,
3943
RegisterPilTermsAndAttachResponse,
@@ -530,6 +534,110 @@ export class LicenseClient {
530534
}
531535
}
532536

537+
/**
538+
* @deprecated Use {@link PILFlavor.nonCommercialSocialRemixing} with {@link LicenseClient.registerPILTerms} instead.
539+
* This method will be removed soon.
540+
*
541+
* Convenient function to register a PIL non commercial social remix license to the registry
542+
*
543+
* For more details, see {@link https://docs.story.foundation/concepts/programmable-ip-license/pil-flavors#flavor-%231%3A-non-commercial-social-remixing | Non Commercial Social Remixing}.
544+
*
545+
* Emits an on-chain {@link https://github.com/storyprotocol/protocol-core-v1/blob/v1.3.1/contracts/interfaces/modules/licensing/ILicenseTemplate.sol#L19 | `LicenseTermsRegistered`} event.
546+
*/
547+
public async registerNonComSocialRemixingPIL(
548+
request?: RegisterNonComSocialRemixingPILRequest,
549+
): Promise<RegisterPILResponse> {
550+
try {
551+
const licenseTerms = PILFlavor.nonCommercialSocialRemixing();
552+
return await this.registerPILTermsHelper(licenseTerms, request?.txOptions);
553+
} catch (error) {
554+
return handleError(error, "Failed to register non commercial social remixing PIL");
555+
}
556+
}
557+
558+
/**
559+
* @deprecated Use {@link PILFlavor.commercialUse} with {@link LicenseClient.registerPILTerms} instead.
560+
* This method will be removed soon.
561+
*
562+
* Convenient function to register a PIL commercial use license to the registry.
563+
*
564+
* For more details, see {@link https://docs.story.foundation/concepts/programmable-ip-license/pil-flavors#flavor-%232%3A-commercial-use | Commercial Use}.
565+
*
566+
* Emits an on-chain {@link https://github.com/storyprotocol/protocol-core-v1/blob/v1.3.1/contracts/interfaces/modules/licensing/ILicenseTemplate.sol#L19 | `LicenseTermsRegistered`} event.
567+
*/
568+
public async registerCommercialUsePIL(
569+
request: RegisterCommercialUsePILRequest,
570+
): Promise<RegisterPILResponse> {
571+
try {
572+
const licenseTerms = PILFlavor.commercialUse({
573+
defaultMintingFee: Number(request.defaultMintingFee),
574+
currency: request.currency,
575+
royaltyPolicy: request.royaltyPolicyAddress,
576+
});
577+
return await this.registerPILTermsHelper(licenseTerms, request.txOptions);
578+
} catch (error) {
579+
return handleError(error, "Failed to register commercial use PIL");
580+
}
581+
}
582+
583+
/**
584+
* @deprecated Use {@link PILFlavor.commercialRemix} with {@link LicenseClient.registerPILTerms} instead.
585+
* This method will be removed soon.
586+
*
587+
* Convenient function to register a PIL commercial Remix license to the registry.
588+
*
589+
* For more details, see {@link https://docs.story.foundation/concepts/programmable-ip-license/pil-flavors#flavor-%233%3A-commercial-remix | Commercial Remix }.
590+
*
591+
* Emits an on-chain {@link https://github.com/storyprotocol/protocol-core-v1/blob/v1.3.1/contracts/interfaces/modules/licensing/ILicenseTemplate.sol#L19 | `LicenseTermsRegistered`} event.
592+
*/
593+
public async registerCommercialRemixPIL({
594+
defaultMintingFee,
595+
currency,
596+
royaltyPolicyAddress,
597+
commercialRevShare,
598+
txOptions,
599+
}: RegisterCommercialRemixPILRequest): Promise<RegisterPILResponse> {
600+
try {
601+
const licenseTerms = PILFlavor.commercialRemix({
602+
defaultMintingFee: Number(defaultMintingFee),
603+
currency,
604+
royaltyPolicy: royaltyPolicyAddress,
605+
commercialRevShare,
606+
});
607+
return await this.registerPILTermsHelper(licenseTerms, txOptions);
608+
} catch (error) {
609+
return handleError(error, "Failed to register commercial remix PIL");
610+
}
611+
}
612+
613+
/**
614+
* @deprecated Use {@link PILFlavor.creativeCommonsAttribution} with {@link LicenseClient.registerPILTerms} instead.
615+
* This method will be removed soon.
616+
*
617+
* Convenient function to register a PIL creative commons attribution license to the registry.
618+
* Creates a Creative Commons Attribution (CC-BY) license terms flavor.
619+
*
620+
* For more details, see {@link https://docs.story.foundation/concepts/programmable-ip-license/pil-flavors#flavor-%234%3A-creative-commons-attribution | Creative Commons Attribution}.
621+
*
622+
* Emits an on-chain {@link https://github.com/storyprotocol/protocol-core-v1/blob/v1.3.1/contracts/interfaces/modules/licensing/ILicenseTemplate.sol#L19 | `LicenseTermsRegistered`} event.
623+
*/
624+
public async registerCreativeCommonsAttributionPIL({
625+
currency,
626+
royaltyPolicyAddress,
627+
txOptions,
628+
}: RegisterCreativeCommonsAttributionPILRequest): Promise<RegisterPILResponse> {
629+
try {
630+
return await this.registerPILTermsHelper(
631+
PILFlavor.creativeCommonsAttribution({
632+
currency,
633+
royaltyPolicy: royaltyPolicyAddress,
634+
}),
635+
txOptions,
636+
);
637+
} catch (error) {
638+
return handleError(error, "Failed to register creative commons attribution PIL");
639+
}
640+
}
533641
private async getLicenseTermsId(licenseTerms: LicenseTerms): Promise<LicenseTermsIdResponse> {
534642
const licenseRes = await this.licenseTemplateClient.getLicenseTermsId({
535643
terms: licenseTerms,

packages/core-sdk/src/types/resources/license.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,58 @@ export type LicenseTermsDataInput<T = LicenseTermsInput, C = LicensingConfigInpu
223223
*/
224224
maxLicenseTokens?: TokenAmountInput;
225225
};
226+
227+
export type RegisterNonComSocialRemixingPILRequest = {
228+
txOptions?: TxOptions;
229+
};
230+
export type RegisterCommercialUsePILRequest = {
231+
/** The fee to be paid when minting a license. */
232+
defaultMintingFee: string | number | bigint;
233+
/** The ERC20 token to be used to pay the minting fee */
234+
currency: Address;
235+
/**
236+
* The address of the royalty policy contract.
237+
* Defaults to {@link https://docs.story.foundation/docs/liquid-absolute-percentage | LAP} policy address if not provided.
238+
*/
239+
royaltyPolicyAddress?: Address;
240+
txOptions?: TxOptions;
241+
};
242+
243+
export type RegisterCommercialRemixPILRequest = {
244+
/** The fee to be paid when minting a license. */
245+
defaultMintingFee: string | number | bigint;
246+
/**
247+
* Percentage of revenue that must be shared with the licensor.
248+
* Must be between 0 and 100 (where 100% represents 100_000_000).
249+
*/
250+
commercialRevShare: number;
251+
/** The ERC20 token to be used to pay the minting fee */
252+
currency: Address;
253+
/**
254+
* The address of the royalty policy contract.
255+
* Defaults to {@link https://docs.story.foundation/docs/liquid-absolute-percentage | LAP} policy address if not provided.
256+
*/
257+
royaltyPolicyAddress?: Address;
258+
txOptions?: TxOptions;
259+
};
260+
261+
export type RegisterCreativeCommonsAttributionPILRequest = WithTxOptions & {
262+
/** The ERC20 or WIP token to be used to pay the minting fee. */
263+
currency: Address;
264+
/**
265+
* The address of the royalty policy contract.
266+
* Defaults to {@link https://docs.story.foundation/docs/liquid-absolute-percentage | LAP} policy address if not provided.
267+
*/
268+
royaltyPolicyAddress?: Address;
269+
};
270+
/**
271+
* @deprecated Use `PILFlavor.nonCommercialSocialRemixing`, `PILFlavor.commercialUse`, `PILFlavor.commercialRemix`, or `PILFlavor.creativeCommonsAttribution` instead.
272+
*
273+
* The type of PIL.
274+
*/
275+
export enum PIL_TYPE {
276+
NON_COMMERCIAL_REMIX,
277+
COMMERCIAL_USE,
278+
COMMERCIAL_REMIX,
279+
CREATIVE_COMMONS_ATTRIBUTION,
280+
}

packages/core-sdk/test/integration/license.test.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
erc20Address,
1818
LicenseRegistryReadOnlyClient,
1919
licensingModuleAddress,
20+
royaltyPolicyLapAddress,
2021
} from "../../src/abi/generated";
2122
import { WIP_TOKEN_ADDRESS } from "../../src/constants/common";
2223
import { ERC20Client } from "../../src/utils/token";
@@ -54,6 +55,35 @@ describe("License Functions", () => {
5455
});
5556
expect(result.licenseTermsId).to.be.a("bigint");
5657
});
58+
59+
it("should register license with non commercial social remixing PIL", async () => {
60+
const result = await client.license.registerNonComSocialRemixingPIL();
61+
expect(result.licenseTermsId).to.be.a("bigint");
62+
});
63+
it("should register license with commercial use", async () => {
64+
const result = await client.license.registerCommercialUsePIL({
65+
defaultMintingFee: "1",
66+
currency: WIP_TOKEN_ADDRESS,
67+
});
68+
expect(result.licenseTermsId).to.be.a("bigint");
69+
});
70+
71+
it("should register license with commercial Remix use", async () => {
72+
const result = await client.license.registerCommercialRemixPIL({
73+
defaultMintingFee: "1",
74+
commercialRevShare: 100,
75+
currency: WIP_TOKEN_ADDRESS,
76+
});
77+
expect(result.licenseTermsId).to.be.a("bigint");
78+
});
79+
80+
it("should register license with creative commons attribution PIL", async () => {
81+
const result = await client.license.registerCreativeCommonsAttributionPIL({
82+
currency: WIP_TOKEN_ADDRESS,
83+
royaltyPolicyAddress: royaltyPolicyLapAddress[aeneid],
84+
});
85+
expect(result.licenseTermsId).to.be.a("bigint");
86+
});
5787
});
5888

5989
describe("attach License Terms and mint license tokens", () => {
@@ -343,4 +373,100 @@ describe("License Functions", () => {
343373
expect(result.maxLicenseTokensTxHashes?.length).to.be.equal(2);
344374
});
345375
});
376+
377+
describe("Creative Commons Attribution License Tests", () => {
378+
let ipId: Hex;
379+
let ccLicenseTermsId: bigint;
380+
let tokenId: number | undefined;
381+
382+
before(async () => {
383+
tokenId = await getTokenId();
384+
385+
// Register an IP asset
386+
const registerResult = await client.ipAsset.register({
387+
nftContract: mockERC721,
388+
tokenId: tokenId!,
389+
});
390+
ipId = registerResult.ipId!;
391+
392+
// Create a Creative Commons Attribution license
393+
const ccLicenseResult = await client.license.registerCreativeCommonsAttributionPIL({
394+
currency: WIP_TOKEN_ADDRESS,
395+
royaltyPolicyAddress: royaltyPolicyLapAddress[aeneid],
396+
});
397+
ccLicenseTermsId = ccLicenseResult.licenseTermsId!;
398+
});
399+
400+
it("should verify the license terms match Creative Commons Attribution specifications", async () => {
401+
const licenseTerms = await client.license.getLicenseTerms(ccLicenseTermsId);
402+
403+
expect(licenseTerms.terms.transferable).to.equal(true);
404+
expect(licenseTerms.terms.commercialUse).to.equal(true);
405+
expect(licenseTerms.terms.derivativesAllowed).to.equal(true);
406+
expect(licenseTerms.terms.derivativesAttribution).to.equal(true);
407+
expect(licenseTerms.terms.derivativesReciprocal).to.equal(true);
408+
expect(licenseTerms.terms.derivativesApproval).to.equal(false);
409+
expect(licenseTerms.terms.commercialAttribution).to.equal(true);
410+
expect(licenseTerms.terms.commercialRevShare).to.equal(0);
411+
expect(licenseTerms.terms.defaultMintingFee).to.equal(0n);
412+
413+
expect(licenseTerms.terms.royaltyPolicy).to.equal(royaltyPolicyLapAddress[aeneid]);
414+
expect(licenseTerms.terms.expiration).to.equal(0n);
415+
});
416+
417+
it("should attach Creative Commons Attribution license to an IP", async () => {
418+
const attachResult = await client.license.attachLicenseTerms({
419+
ipId: ipId,
420+
licenseTermsId: ccLicenseTermsId,
421+
});
422+
423+
expect(attachResult.txHash).to.be.a("string");
424+
expect(attachResult.success).to.equal(true);
425+
426+
const licenseRegistryReadOnlyClient = new LicenseRegistryReadOnlyClient(publicClient);
427+
const hasLicense = await licenseRegistryReadOnlyClient.hasIpAttachedLicenseTerms({
428+
ipId: ipId,
429+
licenseTemplate: client.ipAsset.licenseTemplateClient.address,
430+
licenseTermsId: ccLicenseTermsId,
431+
});
432+
expect(hasLicense).to.equal(true);
433+
});
434+
435+
it("should mint CC-BY license tokens with no minting fee", async () => {
436+
// Get wallet balance before minting
437+
const balanceBefore = await client.getWalletBalance();
438+
439+
// Predict the minting fee (should be zero for CC-BY)
440+
const feePredict = await client.license.predictMintingLicenseFee({
441+
licenseTermsId: ccLicenseTermsId,
442+
licensorIpId: ipId,
443+
amount: 1,
444+
});
445+
446+
// CC-BY licenses should have zero minting fee
447+
expect(feePredict.tokenAmount).to.equal(0n);
448+
449+
const mintResult = await client.license.mintLicenseTokens({
450+
licenseTermsId: ccLicenseTermsId,
451+
licensorIpId: ipId,
452+
maxMintingFee: 0n,
453+
maxRevenueShare: 0,
454+
});
455+
456+
expect(mintResult.txHash).to.be.a("string");
457+
expect(mintResult.licenseTokenIds).to.be.a("array");
458+
459+
const balanceAfter = await client.getWalletBalance();
460+
461+
// Verify no fee was charged just gas
462+
// This checks that any difference is very small (just gas costs)
463+
const balanceDiff = balanceBefore - balanceAfter;
464+
const gasUsed = mintResult.receipt!.gasUsed;
465+
const effectiveGasPrice = mintResult.receipt!.effectiveGasPrice;
466+
const totalGas = gasUsed * effectiveGasPrice;
467+
468+
// Confirms the balance diff only reflects gas cost, since license fee is zero.
469+
expect(balanceDiff).to.equal(totalGas); // Small amount for gas
470+
});
471+
});
346472
});

0 commit comments

Comments
 (0)