Skip to content

Commit b4bf32d

Browse files
authored
Merge pull request #432 from storyprotocol/origin/bp/integration/423
Origin/bp/integration/423
2 parents 89c2d0e + f60c23f commit b4bf32d

File tree

1 file changed

+243
-2
lines changed

1 file changed

+243
-2
lines changed

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

Lines changed: 243 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,9 @@ describe("Dispute Functions", () => {
207207
transport: http(RPC),
208208
account: privateKeyToAccount(process.env.JUDGE_PRIVATE_KEY as Address),
209209
});
210+
});
210211

212+
beforeEach(async function (this: Mocha.Context) {
211213
// Setup NFT collection
212214
const txData = await clientA.nftClient.createNFTCollection({
213215
name: "test-collection",
@@ -335,8 +337,248 @@ describe("Dispute Functions", () => {
335337
expect(results[0].txHash).to.be.a("string").and.not.empty;
336338
});
337339

338-
// Test that dispute initiator can resolve the dispute after judgement
340+
it("should tag a single IP as infringing without using multicall", async () => {
341+
/**
342+
* Test Flow:
343+
* 1. Set judgment on an existing dispute to mark it as valid
344+
* 2. Verify the dispute state changed correctly after judgment
345+
* 3. Try to tag a derivative IP using the judged dispute
346+
*/
347+
348+
// Step 1: Set dispute judgment using the judge wallet
349+
// When judgment is true, the dispute's currentTag will be set to the targetTag
350+
// When false, currentTag would be set to bytes32(0)
351+
const { request } = await publicClient.simulateContract({
352+
address: DISPUTE_MODULE_ADDRESS,
353+
abi: [SET_DISPUTE_JUDGEMENT_ABI],
354+
functionName: "setDisputeJudgement",
355+
args: [disputeId, true, "0x"],
356+
account: judgeWalletClient.account!,
357+
});
358+
const judgmentTxHash = await judgeWalletClient.writeContract(request);
359+
await publicClient.waitForTransactionReceipt({ hash: judgmentTxHash });
360+
361+
// Step 2: Verify dispute state
362+
// The disputes() function returns multiple values about the dispute:
363+
// - targetTag: the tag we wanted to apply when raising the dispute
364+
// - currentTag: the current state of the dispute after judgment
365+
// After a successful judgment, currentTag should equal targetTag
366+
const [
367+
_targetIpId, // IP being disputed
368+
_disputeInitiator, // Address that raised the dispute
369+
_disputeTimestamp, // When dispute was raised
370+
_arbitrationPolicy, // Policy used for arbitration
371+
_disputeEvidenceHash, // Evidence hash for dispute
372+
targetTag, // Tag we want to apply (e.g. "IMPROPER_REGISTRATION")
373+
currentTag, // Current state of dispute
374+
_infringerDisputeId, // Related dispute ID if this is a propagated tag
375+
] = await publicClient.readContract({
376+
address: disputeModuleAddress[aeneid],
377+
abi: disputeModuleAbi,
378+
functionName: "disputes",
379+
args: [disputeId],
380+
});
381+
expect(currentTag).to.equal(targetTag); // Verify judgment was recorded correctly
382+
383+
// Step 3: Attempt to tag a derivative IP
384+
// This will fail if:
385+
// - The dispute is not in a valid state (still IN_DISPUTE or cleared)
386+
// - The IP we're trying to tag is not actually a derivative of the disputed IP
387+
// - The dispute has already been used to tag this IP
388+
const response = await clientA.dispute.tagIfRelatedIpInfringed({
389+
infringementTags: [
390+
{
391+
ipId: childIpId, // The derivative IP to tag
392+
disputeId: disputeId, // Using the judged dispute as basis for tagging
393+
},
394+
],
395+
options: {
396+
useMulticallWhenPossible: false, // Force single transaction instead of batch
397+
},
398+
txOptions: { waitForTransaction: true },
399+
});
400+
401+
// Verify we got the expected response
402+
expect(response).to.have.lengthOf(1);
403+
expect(response[0].txHash).to.be.a("string").and.not.empty;
404+
});
405+
406+
it("should tag multiple IPs as infringing using multicall", async () => {
407+
const disputeResponse = await clientA.dispute.raiseDispute({
408+
targetIpId: parentIpId,
409+
cid: await generateCID(),
410+
targetTag: "IMPROPER_REGISTRATION",
411+
liveness: 2592000,
412+
bond: 0,
413+
txOptions: { waitForTransaction: true },
414+
});
415+
const testDisputeId = disputeResponse.disputeId!;
416+
417+
const derivativeResponse2 = await clientA.ipAsset.mintAndRegisterIpAndMakeDerivative({
418+
spgNftContract: nftContract,
419+
derivData: {
420+
parentIpIds: [parentIpId!],
421+
licenseTermsIds: [licenseTermsId!],
422+
maxMintingFee: 1n,
423+
maxRts: 5 * 10 ** 6,
424+
maxRevenueShare: 100,
425+
},
426+
allowDuplicates: true,
427+
txOptions: { waitForTransaction: true },
428+
});
429+
const childIpId2 = derivativeResponse2.ipId!;
430+
431+
const { request } = await publicClient.simulateContract({
432+
address: DISPUTE_MODULE_ADDRESS,
433+
abi: [SET_DISPUTE_JUDGEMENT_ABI],
434+
functionName: "setDisputeJudgement",
435+
args: [testDisputeId, true, "0x"],
436+
account: judgeWalletClient.account!,
437+
});
438+
const judgmentTxHash = await judgeWalletClient.writeContract(request);
439+
await publicClient.waitForTransactionReceipt({ hash: judgmentTxHash });
440+
441+
const disputeState = await publicClient.readContract({
442+
address: disputeModuleAddress[aeneid],
443+
abi: disputeModuleAbi,
444+
functionName: "disputes",
445+
args: [testDisputeId],
446+
});
447+
expect(disputeState[6]).to.equal(disputeState[5]);
448+
449+
const response = await clientA.dispute.tagIfRelatedIpInfringed({
450+
infringementTags: [
451+
{
452+
ipId: childIpId,
453+
disputeId: testDisputeId,
454+
},
455+
{
456+
ipId: childIpId2,
457+
disputeId: testDisputeId,
458+
},
459+
],
460+
options: {
461+
useMulticallWhenPossible: true,
462+
},
463+
txOptions: { waitForTransaction: true },
464+
});
465+
466+
expect(response).to.have.lengthOf(1);
467+
expect(response[0].txHash).to.be.a("string").and.not.empty;
468+
});
469+
470+
it("should tag multiple IPs without multicall when specified", async () => {
471+
// Create two new derivative IPs sequentially
472+
const derivativeResponse3 = await clientA.ipAsset.mintAndRegisterIpAndMakeDerivative({
473+
spgNftContract: nftContract,
474+
derivData: {
475+
parentIpIds: [parentIpId!],
476+
licenseTermsIds: [licenseTermsId!],
477+
maxMintingFee: 1n,
478+
maxRts: 5 * 10 ** 6,
479+
maxRevenueShare: 100,
480+
},
481+
allowDuplicates: true,
482+
txOptions: { waitForTransaction: true },
483+
});
484+
485+
const derivativeResponse4 = await clientA.ipAsset.mintAndRegisterIpAndMakeDerivative({
486+
spgNftContract: nftContract,
487+
derivData: {
488+
parentIpIds: [parentIpId!],
489+
licenseTermsIds: [licenseTermsId!],
490+
maxMintingFee: 1n,
491+
maxRts: 5 * 10 ** 6,
492+
maxRevenueShare: 100,
493+
},
494+
allowDuplicates: true,
495+
txOptions: { waitForTransaction: true },
496+
});
497+
498+
const { request } = await publicClient.simulateContract({
499+
address: DISPUTE_MODULE_ADDRESS,
500+
abi: [SET_DISPUTE_JUDGEMENT_ABI],
501+
functionName: "setDisputeJudgement",
502+
args: [disputeId, true, "0x"],
503+
account: judgeWalletClient.account!,
504+
});
505+
const judgmentTxHash = await judgeWalletClient.writeContract(request);
506+
await publicClient.waitForTransactionReceipt({ hash: judgmentTxHash });
507+
508+
const disputeState = await publicClient.readContract({
509+
address: disputeModuleAddress[aeneid],
510+
abi: disputeModuleAbi,
511+
functionName: "disputes",
512+
args: [disputeId],
513+
});
514+
expect(disputeState[6]).to.equal(disputeState[5]);
515+
516+
const response1 = await clientA.dispute.tagIfRelatedIpInfringed({
517+
infringementTags: [
518+
{
519+
ipId: derivativeResponse3.ipId!,
520+
disputeId: disputeId,
521+
},
522+
],
523+
options: {
524+
useMulticallWhenPossible: false,
525+
},
526+
txOptions: { waitForTransaction: true },
527+
});
528+
529+
const response2 = await clientA.dispute.tagIfRelatedIpInfringed({
530+
infringementTags: [
531+
{
532+
ipId: derivativeResponse4.ipId!,
533+
disputeId: disputeId,
534+
},
535+
],
536+
options: {
537+
useMulticallWhenPossible: false,
538+
},
539+
txOptions: { waitForTransaction: true },
540+
});
541+
542+
const responses = [...response1, ...response2];
543+
expect(responses).to.have.lengthOf(2);
544+
expect(responses[0].txHash).to.be.a("string").and.not.empty;
545+
expect(responses[1].txHash).to.be.a("string").and.not.empty;
546+
});
547+
548+
it("should fail when trying to tag with invalid dispute ID", async () => {
549+
await expect(
550+
clientA.dispute.tagIfRelatedIpInfringed({
551+
infringementTags: [
552+
{
553+
ipId: childIpId,
554+
disputeId: 999999n,
555+
},
556+
],
557+
txOptions: { waitForTransaction: true },
558+
}),
559+
).to.be.rejected;
560+
});
561+
339562
it("should resolve a dispute successfully when initiated by dispute initiator", async () => {
563+
// First set judgment
564+
const { request } = await publicClient.simulateContract({
565+
address: DISPUTE_MODULE_ADDRESS,
566+
abi: [SET_DISPUTE_JUDGEMENT_ABI],
567+
functionName: "setDisputeJudgement",
568+
args: [disputeId, true, "0x"],
569+
account: judgeWalletClient.account!,
570+
});
571+
const judgmentTxHash = await judgeWalletClient.writeContract(request);
572+
await publicClient.waitForTransactionReceipt({ hash: judgmentTxHash });
573+
574+
const disputeState = await publicClient.readContract({
575+
address: disputeModuleAddress[aeneid],
576+
abi: disputeModuleAbi,
577+
functionName: "disputes",
578+
args: [disputeId],
579+
});
580+
expect(disputeState[6]).to.equal(disputeState[5]);
581+
340582
const response = await clientA.dispute.resolveDispute({
341583
disputeId: disputeId,
342584
data: "0x",
@@ -347,7 +589,6 @@ describe("Dispute Functions", () => {
347589
expect(response.txHash).to.be.a("string").and.not.empty;
348590
});
349591

350-
// Test that non-initiators cannot resolve the dispute
351592
it("should fail when non-initiator tries to resolve the dispute", async () => {
352593
await expect(
353594
clientB.dispute.resolveDispute({

0 commit comments

Comments
 (0)