Skip to content

Commit

Permalink
add new tagDerivativeIfParentInfringed function
Browse files Browse the repository at this point in the history
  • Loading branch information
Spablob committed Apr 11, 2024
1 parent 9edc3f0 commit 7ebc219
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 54 deletions.
28 changes: 27 additions & 1 deletion contracts/interfaces/modules/dispute/IDisputeModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ interface IDisputeModule {
/// @param linkToDisputeEvidence The link of the dispute evidence
/// @param targetTag The target tag of the dispute
/// @param currentTag The current tag of the dispute
/// @param parentDisputeId The parent dispute id
struct Dispute {
address targetIpId;
address disputeInitiator;
address arbitrationPolicy;
bytes32 linkToDisputeEvidence;
bytes32 targetTag;
bytes32 currentTag;
uint256 parentDisputeId;
}

/// @notice Event emitted when a dispute tag whitelist status is updated
Expand Down Expand Up @@ -73,6 +75,18 @@ interface IDisputeModule {
/// @param data Custom data adjusted to each policy
event DisputeCancelled(uint256 disputeId, bytes data);

/// @notice Event emitted when a derivative is tagged on a parent infringement
/// @param parentIpId The parent ipId which infringed
/// @param derivativeIpId The derivative ipId which was tagged
/// @param parentDisputeId The parent dispute id in which infringement was found
/// @param tag The tag of the dispute applied to the derivative
event DerivativeTaggedOnParentInfringement(
address parentIpId,
address derivativeIpId,
uint256 parentDisputeId,
bytes32 tag
);

/// @notice Event emitted when a dispute is resolved
/// @param disputeId The dispute id
event DisputeResolved(uint256 disputeId);
Expand All @@ -94,6 +108,7 @@ interface IDisputeModule {
/// @return linkToDisputeEvidence The link of the dispute summary
/// @return targetTag The target tag of the dispute
/// @return currentTag The current tag of the dispute
/// @return parentDisputeId The parent dispute id
function disputes(
uint256 disputeId
)
Expand All @@ -105,7 +120,8 @@ interface IDisputeModule {
address arbitrationPolicy,
bytes32 linkToDisputeEvidence,
bytes32 targetTag,
bytes32 currentTag
bytes32 currentTag,
uint256 parentDisputeId
);

/// @notice Indicates if a dispute tag is whitelisted
Expand Down Expand Up @@ -181,6 +197,16 @@ interface IDisputeModule {
/// @param data The data to cancel the dispute
function cancelDispute(uint256 disputeId, bytes calldata data) external;

/// @notice Tags a derivative if a parent has been tagged with an infringement tag
/// @param parentIpId The infringing parent ipId
/// @param derivativeIpId The derivative ipId
/// @param parentDisputeId The dispute id that tagged the parent ipId as infringing
function tagDerivativeIfParentInfringed(
address parentIpId,
address derivativeIpId,
uint256 parentDisputeId
) external;

/// @notice Resolves a dispute after it has been judged
/// @param disputeId The dispute id
function resolveDispute(uint256 disputeId) external;
Expand Down
6 changes: 6 additions & 0 deletions contracts/interfaces/registries/ILicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ interface ILicenseRegistry {
/// @return parentIpId The address of the parent IP.
function getParentIp(address childIpId, uint256 index) external view returns (address parentIpId);

/// @notice Checks if an IP is a parent IP.
/// @param parentIpId The address of the parent IP.
/// @param childIpId The address of the child IP.
/// @return Whether the IP is a parent IP.
function isParentIp(address parentIpId, address childIpId) external view returns (bool);

/// @notice Gets the count of parent IPs.
/// @param childIpId The address of the childIP.
/// @return The count o parent IPs.
Expand Down
12 changes: 12 additions & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,19 @@ library Errors {
error DisputeModule__NotInDisputeState();
error DisputeModule__NotAbleToResolve();
error DisputeModule__NotRegisteredIpId();
error DisputeModule__ParentIpIdMismatch();
error DisputeModule__ParentNotTagged();
error DisputeModule__NotDerivative();
error DisputeModule__ParentDisputeNotResolved();
error DisputeModule__ZeroLicenseRegistry();
error DisputeModule__ZeroAssetRegistry();
error DisputeModule__ZeroController();
error DisputeModule__ZeroAccessManager();

error ArbitrationPolicySP__ZeroDisputeModule();
error ArbitrationPolicySP__ZeroPaymentToken();
error ArbitrationPolicySP__NotDisputeModule();
error ArbitrationPolicySP__ZeroAccessManager();

////////////////////////////////////////////////////////////////////////////
// Royalty Module //
Expand All @@ -259,6 +268,7 @@ library Errors {
error RoyaltyModule__NoParentsOnLinking();
error RoyaltyModule__ZeroDisputeModule();
error RoyaltyModule__IpIsTagged();
error RoyaltyModule__ZeroAccessManager();

error RoyaltyPolicyLAP__ZeroRoyaltyModule();
error RoyaltyPolicyLAP__ZeroLiquidSplitFactory();
Expand All @@ -275,8 +285,10 @@ library Errors {
error RoyaltyPolicyLAP__UnlinkableToParents();
error RoyaltyPolicyLAP__LastPositionNotAbleToMintLicense();
error RoyaltyPolicyLAP__ZeroIpRoyaltyVaultBeacon();
error RoyaltyPolicyLAP__ZeroAccessManager();

error IpRoyaltyVault__ZeroIpId();
error IpRoyaltyVault__ZeroSupply();
error IpRoyaltyVault__ZeroRoyaltyPolicyLAP();
error IpRoyaltyVault__NotRoyaltyPolicyLAP();
error IpRoyaltyVault__SnapshotIntervalTooShort();
Expand Down
97 changes: 84 additions & 13 deletions contracts/modules/dispute/DisputeModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
// solhint-disable-next-line max-line-length
import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";

import { DISPUTE_MODULE_KEY } from "../../lib/modules/Module.sol";
import { BaseModule } from "../../modules/BaseModule.sol";
import { AccessControlled } from "../../access/AccessControlled.sol";
import { IIPAssetRegistry } from "../../interfaces/registries/IIPAssetRegistry.sol";
import { ILicenseRegistry } from "../../interfaces/registries/ILicenseRegistry.sol";
import { IDisputeModule } from "../../interfaces/modules/dispute/IDisputeModule.sol";
import { IArbitrationPolicy } from "../../interfaces/modules/dispute/policies/IArbitrationPolicy.sol";
import { Errors } from "../../lib/Errors.sol";
Expand All @@ -25,7 +27,8 @@ contract DisputeModule is
AccessManagedUpgradeable,
ReentrancyGuardUpgradeable,
AccessControlled,
UUPSUpgradeable
UUPSUpgradeable,
MulticallUpgradeable
{
using EnumerableSet for EnumerableSet.Bytes32Set;

Expand Down Expand Up @@ -64,21 +67,38 @@ contract DisputeModule is
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IIPAssetRegistry public immutable IP_ASSET_REGISTRY;

/// @notice Protocol-wide license registry
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
ILicenseRegistry public immutable LICENSE_REGISTRY;

/// Constructor
/// @param controller The address of the access controller
/// @param assetRegistry The address of the asset registry
/// @param licenseRegistry The address of the license registry
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address controller, address assetRegistry) AccessControlled(controller, assetRegistry) {
constructor(
address controller,
address assetRegistry,
address licenseRegistry
) AccessControlled(controller, assetRegistry) {
if (licenseRegistry == address(0)) revert Errors.DisputeModule__ZeroLicenseRegistry();
if (assetRegistry == address(0)) revert Errors.DisputeModule__ZeroAssetRegistry();
if (controller == address(0)) revert Errors.DisputeModule__ZeroController();

IP_ASSET_REGISTRY = IIPAssetRegistry(assetRegistry);
LICENSE_REGISTRY = ILicenseRegistry(licenseRegistry);
_disableInitializers();
}

/// @notice initializer for this implementation contract
/// @notice Initializer for this implementation contract
/// @param accessManager The address of the protocol admin roles contract
function initialize(address accessManager) external initializer {
if (accessManager == address(0)) revert Errors.DisputeModule__ZeroAccessManager();

__AccessManaged_init(accessManager);
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
__Multicall_init();
}

/// @notice Whitelists a dispute tag
Expand Down Expand Up @@ -178,7 +198,8 @@ contract DisputeModule is
arbitrationPolicy: arbitrationPolicy,
linkToDisputeEvidence: linkToDisputeEvidenceBytes,
targetTag: targetTag,
currentTag: IN_DISPUTE
currentTag: IN_DISPUTE,
parentDisputeId: 0
});

IArbitrationPolicy(arbitrationPolicy).onRaiseDispute(msg.sender, data);
Expand Down Expand Up @@ -239,13 +260,60 @@ contract DisputeModule is
emit DisputeCancelled(disputeId, data);
}

/// @notice Tags a derivative if a parent has been tagged with an infringement tag
/// @param parentIpId The infringing parent ipId
/// @param derivativeIpId The derivative ipId
/// @param parentDisputeId The dispute id that tagged the parent ipId as infringing
function tagDerivativeIfParentInfringed(
address parentIpId,
address derivativeIpId,
uint256 parentDisputeId
) external {
DisputeModuleStorage storage $ = _getDisputeModuleStorage();

Dispute memory parentDispute = $.disputes[parentDisputeId];
if (parentDispute.targetIpId != parentIpId) revert Errors.DisputeModule__ParentIpIdMismatch();
if (parentDispute.currentTag == IN_DISPUTE || parentDispute.currentTag == bytes32(0))
revert Errors.DisputeModule__ParentNotTagged();

if (!LICENSE_REGISTRY.isParentIp(parentIpId, derivativeIpId)) revert Errors.DisputeModule__NotDerivative();

address arbitrationPolicy = $.arbitrationPolicies[derivativeIpId];
if (!$.isWhitelistedArbitrationPolicy[arbitrationPolicy]) arbitrationPolicy = $.baseArbitrationPolicy;

uint256 disputeId = ++$.disputeCounter;

$.disputes[disputeId] = Dispute({
targetIpId: derivativeIpId,
disputeInitiator: msg.sender,
arbitrationPolicy: arbitrationPolicy,
linkToDisputeEvidence: "",
targetTag: parentDispute.currentTag,
currentTag: parentDispute.currentTag,
parentDisputeId: parentDisputeId
});

$.successfulDisputesPerIp[derivativeIpId]++;

emit DerivativeTaggedOnParentInfringement(
parentIpId,
derivativeIpId,
parentDisputeId,
parentDispute.currentTag
);
}

/// @notice Resolves a dispute after it has been judged
/// @param disputeId The dispute id
function resolveDispute(uint256 disputeId) external {
DisputeModuleStorage storage $ = _getDisputeModuleStorage();
Dispute memory dispute = $.disputes[disputeId];

if (msg.sender != dispute.disputeInitiator) revert Errors.DisputeModule__NotDisputeInitiator();
if (dispute.parentDisputeId == 0 && msg.sender != dispute.disputeInitiator)
revert Errors.DisputeModule__NotDisputeInitiator();
if (dispute.parentDisputeId > 0 && $.disputes[dispute.parentDisputeId].currentTag != bytes32(0))
revert Errors.DisputeModule__ParentDisputeNotResolved();

if (dispute.currentTag == IN_DISPUTE || dispute.currentTag == bytes32(0))
revert Errors.DisputeModule__NotAbleToResolve();

Expand Down Expand Up @@ -283,6 +351,7 @@ contract DisputeModule is
/// @return linkToDisputeEvidence The link of the dispute summary
/// @return targetTag The target tag of the dispute
/// @return currentTag The current tag of the dispute
/// @return parentDisputeId The parent dispute id
function disputes(
uint256 disputeId
)
Expand All @@ -294,17 +363,19 @@ contract DisputeModule is
address arbitrationPolicy,
bytes32 linkToDisputeEvidence,
bytes32 targetTag,
bytes32 currentTag
bytes32 currentTag,
uint256 parentDisputeId
)
{
DisputeModuleStorage storage $ = _getDisputeModuleStorage();
Dispute memory dispute = _getDisputeModuleStorage().disputes[disputeId];
return (
$.disputes[disputeId].targetIpId,
$.disputes[disputeId].disputeInitiator,
$.disputes[disputeId].arbitrationPolicy,
$.disputes[disputeId].linkToDisputeEvidence,
$.disputes[disputeId].targetTag,
$.disputes[disputeId].currentTag
dispute.targetIpId,
dispute.disputeInitiator,
dispute.arbitrationPolicy,
dispute.linkToDisputeEvidence,
dispute.targetTag,
dispute.currentTag,
dispute.parentDisputeId
);
}

Expand Down
6 changes: 5 additions & 1 deletion contracts/registries/LicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
return _getLicenseRegistryStorage().attachedLicenseTerms[ipId].length();
}

/// @notice got the derivative IP of an IP by its index.
/// @notice Gets the derivative IP of an IP by its index.
/// @param parentIpId The address of the IP.
/// @param index The index of the derivative IP within the array of all derivative IPs of the IP.
/// @return childIpId The address of the derivative IP.
Expand Down Expand Up @@ -364,6 +364,10 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
parentIpId = $.parentIps[childIpId].at(index);
}

function isParentIp(address parentIpId, address childIpId) external view returns (bool) {
return _getLicenseRegistryStorage().parentIps[childIpId].contains(parentIpId);
}

/// @notice Gets the count of parent IPs.
/// @param childIpId The address of the childIP.
/// @return The count o parent IPs.
Expand Down
24 changes: 12 additions & 12 deletions script/foundry/utils/DeployHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -215,18 +215,6 @@ contract DeployHelper is Script, BroadcastManager, JsonDeploymentHandler, Storag
impl = address(0);
_postdeploy(contractKey, address(royaltyModule));

contractKey = "DisputeModule";
_predeploy(contractKey);
impl = address(new DisputeModule(address(accessController), address(ipAssetRegistry)));
disputeModule = DisputeModule(
TestProxyHelper.deployUUPSProxy(
impl,
abi.encodeCall(DisputeModule.initialize, address(protocolAccessManager))
)
);
impl = address(0);
_postdeploy(contractKey, address(disputeModule));

contractKey = "LicenseRegistry";
_predeploy(contractKey);
impl = address(new LicenseRegistry());
Expand All @@ -239,6 +227,18 @@ contract DeployHelper is Script, BroadcastManager, JsonDeploymentHandler, Storag
impl = address(0); // Make sure we don't deploy wrong impl
_postdeploy(contractKey, address(licenseRegistry));

contractKey = "DisputeModule";
_predeploy(contractKey);
impl = address(new DisputeModule(address(accessController), address(ipAssetRegistry), address(licenseRegistry)));
disputeModule = DisputeModule(
TestProxyHelper.deployUUPSProxy(
impl,
abi.encodeCall(DisputeModule.initialize, address(protocolAccessManager))
)
);
impl = address(0);
_postdeploy(contractKey, address(disputeModule));

contractKey = "LicenseToken";
_predeploy(contractKey);
impl = address(new LicenseToken());
Expand Down
2 changes: 1 addition & 1 deletion test/foundry/integration/e2e/e2e.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ contract e2e is Test {
);
vm.label(address(royaltyModule), "RoyaltyModule");

impl = address(new DisputeModule(address(accessController), address(ipAssetRegistry)));
impl = address(new DisputeModule(address(accessController), address(ipAssetRegistry), address(licenseRegistry)));
disputeModule = DisputeModule(
TestProxyHelper.deployUUPSProxy(
impl,
Expand Down
Loading

0 comments on commit 7ebc219

Please sign in to comment.