diff --git a/test/hardhat/e2e/license/attachLicenseTerms.test.ts b/test/hardhat/e2e/license/attachLicenseTerms.test.ts new file mode 100644 index 00000000..466998c7 --- /dev/null +++ b/test/hardhat/e2e/license/attachLicenseTerms.test.ts @@ -0,0 +1,37 @@ +// Test: LicensingModule - attachLicenseTerms + +import "../setup"; +import { expect } from "chai"; +import hre from "hardhat"; +import { MockERC721, PILicenseTemplate } from "../constants"; +import { mintNFT } from "../utils/nftHelper"; + +describe("LicensingModule - attachLicenseTerms", function () { + let signers: any; + + this.beforeAll("Get Signers", async function () { + // Get the signers + signers = await hre.ethers.getSigners(); + }); + + it("IP Asset attach a license except for default one", async function () { + const tokenId = await mintNFT(signers[0]); + const connectedRegistry = this.ipAssetRegistry.connect(signers[0]); + + const ipId = await expect( + connectedRegistry.register(this.chainId, MockERC721, tokenId) + ).not.to.be.rejectedWith(Error).then((tx) => tx.wait()).then((receipt) => receipt.logs[2].args[0]); + console.log("ipId:", ipId); + expect(ipId).to.not.be.empty.and.to.be.a("HexString"); + + const connectedLicensingModule = this.licensingModule.connect(signers[0]); + console.log(this.nonCommericialLicenseId); + + const attachLicenseTx = await expect( + connectedLicensingModule.attachLicenseTerms(ipId, PILicenseTemplate, this.commericialUseLicenseId) + ).not.to.be.rejectedWith(Error); + await attachLicenseTx.wait(); + console.log(attachLicenseTx.hash); + expect(attachLicenseTx.hash).to.not.be.empty.and.to.be.a("HexString"); + }); +}); diff --git a/test/hardhat/e2e/license/mintLicenseTokens.test.ts b/test/hardhat/e2e/license/mintLicenseTokens.test.ts new file mode 100644 index 00000000..d8ce7fac --- /dev/null +++ b/test/hardhat/e2e/license/mintLicenseTokens.test.ts @@ -0,0 +1,78 @@ +// Test: LicensingModule - mintLicenseTokens + +import "../setup"; +import { expect } from "chai"; +import hre from "hardhat"; +import { mintNFTAndRegisterIPA } from "../utils/mintNFTAndRegisterIPA"; +import { PILicenseTemplate } from "../constants"; + +describe("LicensingModule - mintLicenseTokens", function () { + let signers: any; + let tokenId: any; + let ipId: any; + + this.beforeAll("Get Signers", async function () { + // Get the signers + signers = await hre.ethers.getSigners(); + + const result = await mintNFTAndRegisterIPA(signers[0], signers[0]); + tokenId = result.tokenId; + ipId = result.ipId; + console.log("tokenId: ", tokenId); + console.log("ipId: ", ipId); + + const connectedLicensingModule = this.licensingModule.connect(signers[0]); + + const attachLicenseTx = await expect( + connectedLicensingModule.attachLicenseTerms(ipId, PILicenseTemplate, this.nonCommericialLicenseId) + ).not.to.be.rejectedWith(Error); + expect(attachLicenseTx.hash).to.not.be.empty.and.to.be.a("HexString"); + }); + + it("IP asset owner mint license tokens", async function () { + const connectedLicensingModule = this.licensingModule.connect(signers[0]); + + const mintLicenseTokensTx = await expect( + connectedLicensingModule.mintLicenseTokens(ipId, PILicenseTemplate, this.nonCommericialLicenseId, 2, signers[0].address, hre.ethers.ZeroAddress, 100) + ).not.to.be.rejectedWith(Error); + expect(mintLicenseTokensTx.hash).to.not.be.empty.and.to.be.a("HexString"); + + const startLicenseTokenId = await mintLicenseTokensTx.wait().then((receipt:any) => receipt.logs[4].args[6]); + console.log(startLicenseTokenId); + expect(startLicenseTokenId).to.be.a("bigint"); + }); + + it("Non-IP asset owner mint license tokens", async function () { + const nonOwnerLicensingModule = this.licensingModule.connect(signers[1]); + + const mintLicenseTokensTx = await expect( + nonOwnerLicensingModule.mintLicenseTokens(ipId, PILicenseTemplate, this.nonCommericialLicenseId, 2, signers[0].address, hre.ethers.ZeroAddress, 100) + ).not.to.be.rejectedWith(Error); + expect(mintLicenseTokensTx.hash).to.not.be.empty.and.to.be.a("HexString"); + + const startLicenseTokenId = await mintLicenseTokensTx.wait().then((receipt:any) => receipt.logs[4].args[6]); + console.log(startLicenseTokenId); + expect(startLicenseTokenId).to.be.a("bigint"); + }); + + it("Mint license tokens with amount 0", async function () { + const connectedLicensingModule = this.licensingModule.connect(signers[0]); + + const mintLicenseTokensTx = await expect( + connectedLicensingModule.mintLicenseTokens(ipId, PILicenseTemplate, this.nonCommericialLicenseId, 0, signers[0].address, hre.ethers.ZeroAddress, 100) + ).to.be.rejectedWith("execution reverted"); + }); + + it("Mint license tokens with different receivers", async function () { + const nonOwnerLicensingModule = this.licensingModule.connect(signers[0]); + + const mintLicenseTokensTx = await expect( + nonOwnerLicensingModule.mintLicenseTokens(ipId, PILicenseTemplate, this.nonCommericialLicenseId, 2, signers[1].address, hre.ethers.ZeroAddress, 100) + ).not.to.be.rejectedWith(Error); + expect(mintLicenseTokensTx.hash).to.not.be.empty.and.to.be.a("HexString"); + + const startLicenseTokenId = await mintLicenseTokensTx.wait().then((receipt:any) => receipt.logs[4].args[6]); + console.log(startLicenseTokenId); + expect(startLicenseTokenId).to.be.a("bigint"); + }); +}); diff --git a/test/hardhat/e2e/license/registerDerivative.test.ts b/test/hardhat/e2e/license/registerDerivative.test.ts new file mode 100644 index 00000000..77f98e53 --- /dev/null +++ b/test/hardhat/e2e/license/registerDerivative.test.ts @@ -0,0 +1,104 @@ +// Test: LicensingModule - registerDerivative, registerDerivativeWithLicenseTokens + +import "../setup"; +import { expect } from "chai"; +import hre from "hardhat"; +import { PILicenseTemplate } from "../constants"; +import { mintNFTAndRegisterIPA } from "../utils/mintNFTAndRegisterIPA"; + +describe("LicensingModule - registerDerivative", function () { + let signers:any; + let ipId1: any; + let ipId2: any; + + this.beforeAll("Get Signers", async function () { + // Get the signers + signers = await hre.ethers.getSigners(); + }); + + it("Register derivative with the license that parent IP attached", async function () { + const mintAndRegisterResp1 = await mintNFTAndRegisterIPA(signers[0], signers[0]); + ipId1 = mintAndRegisterResp1.ipId; + const mintAndRegisterResp2 = await mintNFTAndRegisterIPA(signers[1], signers[1]); + ipId2 = mintAndRegisterResp2.ipId; + + const connectedLicensingModule = this.licensingModule.connect(signers[0]); + // IP1 attach a non-commercial license + const attachLicenseTx = await expect( + connectedLicensingModule.attachLicenseTerms(ipId1, PILicenseTemplate, this.nonCommericialLicenseId) + ).not.to.be.rejectedWith(Error); + await attachLicenseTx.wait(); + console.log("Attach license transaction hash: ", attachLicenseTx.hash); + expect(attachLicenseTx.hash).to.not.be.empty.and.to.be.a("HexString"); + + // IP2 is registered as IP1's derivative + const user1ConnectedLicensingModule = this.licensingModule.connect(signers[1]); + const registerDerivativeTx = await expect( + user1ConnectedLicensingModule.registerDerivative(ipId2, [ipId1], [this.nonCommericialLicenseId], PILicenseTemplate, hre.ethers.ZeroAddress, 0, 0) + ).not.to.be.rejectedWith(Error); + await registerDerivativeTx.wait(); + console.log("Register derivative transaction hash: ", registerDerivativeTx.hash); + expect(registerDerivativeTx.hash).to.not.be.empty.and.to.be.a("HexString"); + }); + + it("Register derivative with the license that parent IP doesn’t attached", async function () { + const mintAndRegisterResp1 = await mintNFTAndRegisterIPA(signers[0], signers[0]); + ipId1 = mintAndRegisterResp1.ipId; + const mintAndRegisterResp2 = await mintNFTAndRegisterIPA(signers[1], signers[1]); + ipId2 = mintAndRegisterResp2.ipId; + + // IP2 is registered as IP1's derivative + const user1ConnectedLicensingModule= this.licensingModule.connect(signers[1]); + const registerDerivativeTx = await expect( + user1ConnectedLicensingModule.registerDerivative(ipId2, [ipId1], [this.nonCommericialLicenseId], PILicenseTemplate, hre.ethers.ZeroAddress, 100, 10) + ).to.be.rejectedWith(`execution reverted`); + }); + + it("IP asset already attached a non-default license and register derivative", async function () { + const mintAndRegisterResp1 = await mintNFTAndRegisterIPA(signers[0], signers[0]); + ipId1 = mintAndRegisterResp1.ipId; + const mintAndRegisterResp2 = await mintNFTAndRegisterIPA(signers[1], signers[1]); + ipId2 = mintAndRegisterResp2.ipId; + + const user1ConnectedLicensingModule = this.licensingModule.connect(signers[1]); + + // IP2 attach a non-commercial license + const attachLicenseTx = await expect( + user1ConnectedLicensingModule.attachLicenseTerms(ipId2, PILicenseTemplate, this.nonCommericialLicenseId) + ).not.to.be.rejectedWith(Error); + await attachLicenseTx.wait(); + console.log("Attach license transaction hash: ", attachLicenseTx.hash); + expect(attachLicenseTx.hash).to.not.be.empty.and.to.be.a("HexString"); + + // IP2 is registered as IP1's derivative + const registerDerivativeTx = await expect( + user1ConnectedLicensingModule.registerDerivative(ipId2, [ipId1], [1n], PILicenseTemplate, hre.ethers.ZeroAddress, 0, 0) + ).to.be.rejectedWith(`execution reverted`); + }); + + it("License token holder register derivative with the license token", async function () { + const mintAndRegisterResp1 = await mintNFTAndRegisterIPA(signers[0], signers[0]); + ipId1 = mintAndRegisterResp1.ipId; + const mintAndRegisterResp2 = await mintNFTAndRegisterIPA(signers[1], signers[1]); + ipId2 = mintAndRegisterResp2.ipId; + + const connectedLicensingModule = this.licensingModule.connect(signers[0]); + + const mintLicenseTokensTx = await expect( + connectedLicensingModule.mintLicenseTokens(ipId1, PILicenseTemplate, 1n, 2, signers[1].address, hre.ethers.ZeroAddress, 100) + ).not.to.be.rejectedWith(Error); + const startLicenseTokenId = await mintLicenseTokensTx.wait().then((receipt:any) => receipt.logs[4].args[6]); + expect(mintLicenseTokensTx.hash).to.not.be.empty.and.to.be.a("HexString"); + expect(startLicenseTokenId).to.be.a("bigint"); + console.log("Start license token id: ", startLicenseTokenId); + + // registerDerivativeWithLicenseTokens + const user1ConnectedLicensingModule = this.licensingModule.connect(signers[1]); + const registerDerivativeTx = await expect( + user1ConnectedLicensingModule.registerDerivativeWithLicenseTokens(ipId2, [startLicenseTokenId], hre.ethers.ZeroAddress, 10) + ).not.to.be.rejectedWith(Error); + await registerDerivativeTx.wait(); + console.log("Register derivative transaction hash: ", registerDerivativeTx.hash); + expect(registerDerivativeTx.hash).to.not.be.empty.and.to.be.a("HexString"); + }); +}); diff --git a/test/hardhat/e2e/license/registerLicenseTerms.test.ts b/test/hardhat/e2e/license/registerLicenseTerms.test.ts new file mode 100644 index 00000000..dfe1fbfa --- /dev/null +++ b/test/hardhat/e2e/license/registerLicenseTerms.test.ts @@ -0,0 +1,100 @@ +// Test: PILicenseTemplate - registerLicenseTerms + +import "../setup"; +import { expect } from "chai"; +import hre from "hardhat"; +import { MockERC20, RoyaltyPolicyLAP, RoyaltyPolicyLRP } from "../constants"; +import { terms } from "../licenseTermsTemplate"; + +describe("PILicenseTemplate - registerLicenseTerms", function () { + let signers:any; + + this.beforeAll("Get Signers", async function () { + // Get the signers + signers = await hre.ethers.getSigners(); + }); + + it("Register non-commercial PIL license terms", async function () { + const connectedLicense = this.licenseTemplate.connect(signers[0]); + const tx = await expect( + connectedLicense.registerLicenseTerms(terms) + ).to.not.be.rejectedWith(Error); + await tx.wait(); + + console.log("Transaction hash: ", tx.hash); + expect(tx.hash).not.to.be.empty.and.to.be.a("HexString"); + + const licenseTermsId = await connectedLicense.getLicenseTermsId(terms); + console.log("licenseTermsId: ", licenseTermsId); + + expect(licenseTermsId).and.to.be.a("bigint"); + }); + + it("Register commercial use license terms", async function () { + const testTerms = terms; + testTerms.royaltyPolicy = RoyaltyPolicyLAP; + testTerms.defaultMintingFee = 30; + testTerms.commercialUse = true; + testTerms.currency = MockERC20; + + const connectedLicense = this.licenseTemplate.connect(signers[0]); + const tx = await expect( + connectedLicense.registerLicenseTerms(testTerms) + ).to.not.be.rejectedWith(Error); + await tx.wait(); + + console.log("Transaction hash: ", tx.hash); + expect(tx.hash).not.to.be.empty.and.to.be.a("HexString"); + + const licenseTermsId = await connectedLicense.getLicenseTermsId(terms); + console.log("licenseTermsId: ", licenseTermsId); + + expect(licenseTermsId).and.to.be.a("bigint"); + }); + + it("Register commercial remix license terms", async function () { + const testTerms = terms; + testTerms.royaltyPolicy = RoyaltyPolicyLRP; + testTerms.defaultMintingFee = 60; + testTerms.commercialUse = true; + testTerms.commercialRevShare = 100; + testTerms.currency = MockERC20; + + const connectedLicense = this.licenseTemplate.connect(signers[0]); + const tx = await expect( + connectedLicense.registerLicenseTerms(terms) + ).to.not.be.rejectedWith(Error); + await tx.wait(); + + console.log("Transaction hash: ", tx.hash); + expect(tx.hash).not.to.be.empty.and.to.be.a("HexString"); + + const licenseTermsId = await connectedLicense.getLicenseTermsId(testTerms); + console.log("licenseTermsId: ", licenseTermsId); + + expect(licenseTermsId).and.to.be.a("bigint"); + }); + + it("Register commercial remix license terms with commercialRevShare larger than max value", async function () { + const testTerms = terms; + testTerms.royaltyPolicy = RoyaltyPolicyLAP; + testTerms.defaultMintingFee = 160; + testTerms.commercialUse = true; + testTerms.commercialRevShare = 101 * 10 ** 6; + testTerms.currency = MockERC20; + + const connectedLicense = this.licenseTemplate.connect(signers[0]); + const tx = await expect( + connectedLicense.registerLicenseTerms(terms) + ).to.not.be.rejectedWith(Error); + await tx.wait(); + + console.log("Transaction hash: ", tx.hash); + expect(tx.hash).not.to.be.empty.and.to.be.a("HexString"); + + const licenseTermsId = await connectedLicense.getLicenseTermsId(testTerms); + console.log("licenseTermsId: ", licenseTermsId); + + expect(licenseTermsId).and.to.be.a("bigint"); + }); +}); diff --git a/test/hardhat/e2e/permission/permission.test.ts b/test/hardhat/e2e/permission/permission.test.ts index 1fb5843d..c97dd021 100644 --- a/test/hardhat/e2e/permission/permission.test.ts +++ b/test/hardhat/e2e/permission/permission.test.ts @@ -16,7 +16,7 @@ describe("Permission", function () { }) it("Add a new ALLOW permission of IP asset for an signer and change the permission to DENY", async function () { - const tokenId = await mintNFT(signers[0].address); + const tokenId = await mintNFT(signers[0]); const connectedRegistry = this.ipAssetRegistry.connect(signers[0]); const func = hre.ethers.encodeBytes32String("attachLicenseTerms").slice(0, 10); const ALLOW_permission = 1; @@ -53,4 +53,4 @@ describe("Permission", function () { permissionAfter = await connecedAccessController.getPermission(ipId, signers[0].address, LicensingModule, func); expect(permissionAfter).to.equal(DENY_permission); }); -}); \ No newline at end of file +}); diff --git a/test/hardhat/e2e/setup.ts b/test/hardhat/e2e/setup.ts index 4ce98511..2b464ed4 100644 --- a/test/hardhat/e2e/setup.ts +++ b/test/hardhat/e2e/setup.ts @@ -2,8 +2,10 @@ import hre from "hardhat"; import { network } from "hardhat"; -import { GroupingModule, IPAssetRegistry, LicenseRegistry, LicenseToken, LicensingModule, PILicenseTemplate, RoyaltyPolicyLAP, MockERC20, RoyaltyPolicyLRP, AccessController } from "./constants"; +import { GroupingModule, IPAssetRegistry, LicenseRegistry, LicenseToken, LicensingModule, PILicenseTemplate, RoyaltyPolicyLAP, MockERC20, RoyaltyPolicyLRP, AccessController, RoyaltyModule } from "./constants"; import { terms } from "./licenseTermsTemplate"; +import { approveSpender, checkAndApproveSpender, getAllowance, mintAmount } from "./utils/erc20Helper"; +import { check } from "prettier"; before(async function () { console.log(`================= Load Contract =================`); @@ -24,7 +26,7 @@ before(async function () { console.log("chainId: ", this.chainId); console.log(`================= Register non-commercial PIL license terms =================`); - await this.licenseTemplate.registerLicenseTerms(terms); + await this.licenseTemplate.registerLicenseTerms(terms).then((tx : any) => tx.wait()); this.nonCommericialLicenseId = await this.licenseTemplate.getLicenseTermsId(terms); console.log("Non-commercial licenseTermsId: ", this.nonCommericialLicenseId); @@ -34,7 +36,7 @@ before(async function () { testTerms.defaultMintingFee = 30; testTerms.commercialUse = true; testTerms.currency = MockERC20; - await this.licenseTemplate.registerLicenseTerms(testTerms); + await this.licenseTemplate.registerLicenseTerms(testTerms).then((tx : any) => tx.wait()); this.commericialUseLicenseId = await this.licenseTemplate.getLicenseTermsId(testTerms); console.log("Commercial-use licenseTermsId: ", this.commericialUseLicenseId); @@ -45,7 +47,16 @@ before(async function () { testTerms.commercialUse = true; testTerms.commercialRevShare = 100; testTerms.currency = MockERC20; - await this.licenseTemplate.registerLicenseTerms(testTerms); + await this.licenseTemplate.registerLicenseTerms(testTerms).then((tx : any) => tx.wait()); this.commericialRemixLicenseId = await this.licenseTemplate.getLicenseTermsId(testTerms); console.log("Commercial-remix licenseTermsId: ", this.commericialRemixLicenseId); + + console.log(`================= ERC20 approve spender =================`); + const amountToCheck = BigInt(200 * 10 ** 18); + await checkAndApproveSpender(this.owner, RoyaltyPolicyLAP, amountToCheck); + await checkAndApproveSpender(this.owner, RoyaltyPolicyLRP, amountToCheck); + await checkAndApproveSpender(this.owner, RoyaltyModule, amountToCheck); + await checkAndApproveSpender(this.user1, RoyaltyPolicyLAP, amountToCheck); + await checkAndApproveSpender(this.user1, RoyaltyPolicyLRP, amountToCheck); + await checkAndApproveSpender(this.user1, RoyaltyModule, amountToCheck); }); diff --git a/test/hardhat/e2e/utils/erc20Helper.ts b/test/hardhat/e2e/utils/erc20Helper.ts new file mode 100644 index 00000000..1126d0d9 --- /dev/null +++ b/test/hardhat/e2e/utils/erc20Helper.ts @@ -0,0 +1,106 @@ +// mockERC20 - mint, approveSpender, Allowance + +import hre from "hardhat" +import { MockERC20 } from "../constants"; +import { ethers } from "ethers"; + +// mockERC20 - approveSpender +export async function approveSpender(spender: string, amount: bigint, singer: ethers.Wallet):Promise { + const contractAbi = [ + { + type: "function", + inputs: [ + { name: "spender", internalType: "address", type: "address" }, + { name: "amount", internalType: "uint256", type: "uint256" }, + ], + name: "approve", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "nonpayable", + } + ]; + + const contract = new hre.ethers.Contract(MockERC20, contractAbi, singer); + + // approveSpender + try { + const tx = await contract.approve( + spender, + amount + ); + await tx.wait(); + console.log("hash", tx.hash); + console.log("approveSpender done"); + return tx.hash; + } catch (error) { + console.error("Error approveSpender:", error); + throw error; + }; +}; + +// mockERC20 - mint +export async function mintAmount(toAddress: string, amount: bigint, singer: ethers.Wallet):Promise { + const contractAbi = [ + { + type: "function", + inputs: [ + { name: "to", internalType: "address", type: "address" }, + { name: "amount", internalType: "uint256", type: "uint256" }, + ], + name: "mint", + outputs: [{ name: "", internalType: "bool", type: "bool" }], + stateMutability: "nonpayable", + } + ]; + + const contract = new hre.ethers.Contract(MockERC20, contractAbi, singer); + + // mintAmount + try { + const tx = await contract.mint( + toAddress, + amount + ); + await tx.wait(); + console.log("hash", tx.hash); + return tx.hash; + } catch (error) { + console.error("Error mintAmount:", error); + throw error; + }; +}; + +// mockERC20 - Allowance +export async function getAllowance(owner: string, spender: string, singer: ethers.Wallet):Promise { + const contractAbi = [ + { + type: "function", + inputs: [ + { name: "owner", internalType: "address", type: "address" }, + { name: "spender", internalType: "address", type: "address" }, + ], + name: "allowance", + outputs: [{ name: "", internalType: "uint256", type: "uint256" }], + stateMutability: "view", + } + ]; + + const contract = new hre.ethers.Contract(MockERC20, contractAbi, singer); + + // getAllowance + try { + const tx = await contract.allowance(owner, spender); + console.log("Allowance", tx); + return tx; + } catch (error) { + console.error("Error getAllowance:", error); + throw error; + }; +}; + +export async function checkAndApproveSpender(owner: any, spender: any, amount: bigint) { + const currentAllowance = await getAllowance(owner.address, spender, owner); + if (currentAllowance < amount) { + await mintAmount(owner.address, amount, owner); + await approveSpender(spender, amount, owner); + } +};