Use Case Guides/Template Libraries/EVM Templates

Intellectual Property

The intellectual property management system is designed to streamline the registration, verification, and dispute resolution processes for IP assets in a decentralized environment. It addresses the challenges faced by creators and organizations in protecting their intellectual property by providing a secure and transparent platform for asset registration. This solution integrates with the Ethereum Attestation Service to generate immutable proofs of ownership and asset details, thereby reducing administrative overhead and improving trust among stakeholders. It is particularly useful for industries where the protection and transfer of intellectual property rights are critical, such as technology, media, and pharmaceuticals.


The smart contract is built using Solidity and leverages OpenZeppelin’s secure libraries for role-based access control, reentrancy protection, and pausing capabilities, ensuring robust security for production deployment.

Disclaimer

This smart contract implementation is provided for educational and illustrative purposes only. It represents a conceptual framework for blockchain-based Intellectual Property management system.

Key considerations before any production use:

  • Legal Compliance: Intellectual property registrations are highly regulated and vary significantly by jurisdiction. This implementation would require substantial modification to meet specific legal requirements in any given location.

  • Security Review: The contract should undergo comprehensive security auditing by qualified blockchain security professionals before handling real registration or financial transactions.

  • No Warranty: The authors and contributors disclaim all liability for any use of this code. Users assume all risks associated with implementation and operation.

  • Consultation Required: Any organization considering using this template should obtain advice from qualified legal counsel, and blockchain security experts before deployment.

 
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
 
// Import OpenZeppelin contracts for security and role management.
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
 
/// @notice Interface for the Ethereum Attestation Service (EAS)
interface IEAS {
    /**
     * @notice Creates an attestation.
     * @param schema The attestation schema identifier.
     * @param recipient The address receiving the attestation.
     * @param expirationTime Unix timestamp for expiration (0 for non-expiring).
     * @param revocable Whether the attestation is revocable.
     * @param data Encoded attestation data.
     * @return attestationId The unique identifier for the created attestation.
     */
    function attest(
        bytes32 schema,
        address recipient,
        uint256 expirationTime,
        bool revocable,
        bytes calldata data
    ) external payable returns (uint256 attestationId);
 
    /**
     * @notice Revokes an attestation.
     * @param attestationId The identifier of the attestation to revoke.
     * @param data Encoded revocation data.
     * @return success A boolean indicating whether revocation was successful.
     */
    function revoke(
        uint256 attestationId,
        bytes calldata data
    ) external payable returns (bool success);
}
 
/// @title Intellectual Property Management Contract
/// @notice This contract manages the registration, verification, dispute resolution,
///         and attestation of intellectual property (IP) assets.
///         It integrates with the Ethereum Attestation Service (EAS) for immutable proofs.
contract IntellectualPropertyManagement is AccessControl, Pausable, ReentrancyGuard {
    using Counters for Counters.Counter;
 
    // ====================================================
    // Role Definitions
    // ====================================================
    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
    // Role for addresses authorized to register new IP assets.
    bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE");
    // Role for verifying IP assets.
    bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE");
    // Role for resolving disputes related to IP assets.
    bytes32 public constant DISPUTE_RESOLVER_ROLE = keccak256("DISPUTE_RESOLVER_ROLE");
 
    // ====================================================
    // EAS Integration and Attestation Schema
    // ====================================================
    IEAS public eas;
    // Attestation schema for IP assets; should be set according to the deployed schema.
    bytes32 public constant IP_ASSET_SCHEMA = keccak256(
        "ipAssetSchema(uint256 assetId,string title,string description,string ipfsHash,uint256 registrationTime,address owner)"
    );
 
    // ====================================================
    // Asset Management Data Structures
    // ====================================================
    // Counter to generate unique asset IDs.
    Counters.Counter private _assetIdCounter;
 
    /// @notice Structure representing an Intellectual Property asset.
    struct IPAsset {
        uint256 id;                // Unique asset identifier.
        address owner;             // Current owner of the asset.
        string title;              // Title of the IP asset.
        string description;        // Detailed description of the asset.
        string ipfsHash;           // IPFS hash linking to off-chain metadata.
        uint256 registrationTime;  // Timestamp when the asset was registered.
        bool verified;             // Verification status by a verifier.
        uint256 attestationId;     // Attestation ID returned by the EAS.
        bool disputeFiled;         // Indicates if a dispute has been filed.
        string disputeDetails;     // Details about the dispute.
        bool disputeResolved;      // Indicates if the dispute has been resolved.
        string disputeResolution;  // Outcome or remarks regarding dispute resolution.
    }
 
    // Mapping from asset ID to its corresponding IPAsset details.
    mapping(uint256 => IPAsset) public ipAssets;
 
    // ====================================================
    // Events for Off-Chain Tracking and Auditing
    // ====================================================
    event AssetRegistered(
        uint256 indexed assetId,
        address indexed owner,
        string title,
        uint256 registrationTime,
        uint256 attestationId
    );
    event AssetVerified(uint256 indexed assetId, bool verified, string verificationComments);
    event DisputeFiled(uint256 indexed assetId, address indexed filer, string disputeDetails);
    event DisputeResolved(uint256 indexed assetId, string resolution);
    event AssetTransferred(uint256 indexed assetId, address indexed from, address indexed to);
    event AssetReattested(uint256 indexed assetId, uint256 oldAttestationId, uint256 newAttestationId);
    event EASAddressUpdated(address indexed newEASAddress);
 
    // ====================================================
    // Constructor and Role Setup
    // ====================================================
    constructor(address easAddress) {
        require(easAddress != address(0), "Invalid EAS address");
        // Set up default admin role.
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        // Grant deployer additional roles.
        _setupRole(REGISTRAR_ROLE, msg.sender);
        _setupRole(VERIFIER_ROLE, msg.sender);
        _setupRole(DISPUTE_RESOLVER_ROLE, msg.sender);
        // Set the EAS contract address.
        eas = IEAS(easAddress);
    }
 
    // ====================================================
    // IP Asset Registration with EAS Attestation
    // ====================================================
 
    /**
     * @notice Registers a new IP asset and creates an attestation using EAS.
     * @dev Only accounts with the REGISTRAR_ROLE can register an asset.
     * @param title The title of the asset (must be non-empty).
     * @param description A detailed description of the asset.
     * @param ipfsHash The IPFS hash pointing to off-chain asset metadata.
     * @return assetId The unique identifier assigned to the registered asset.
     */
    function registerAsset(
        string memory title,
        string memory description,
        string memory ipfsHash
    ) external whenNotPaused nonReentrant onlyRole(REGISTRAR_ROLE) returns (uint256 assetId) {
        require(bytes(title).length > 0, "Title is required");
        require(bytes(ipfsHash).length > 0, "IPFS hash is required");
 
        // Increment asset counter and assign new asset ID.
        _assetIdCounter.increment();
        assetId = _assetIdCounter.current();
 
        uint256 registrationTime = block.timestamp;
 
        // Create a new asset record.
        IPAsset storage asset = ipAssets[assetId];
        asset.id = assetId;
        asset.owner = msg.sender;
        asset.title = title;
        asset.description = description;
        asset.ipfsHash = ipfsHash;
        asset.registrationTime = registrationTime;
        asset.verified = false;
        asset.disputeFiled = false;
        asset.disputeResolved = false;
 
        // Encode the attestation data per the defined schema.
        bytes memory attestationData = abi.encode(
            assetId,
            title,
            description,
            ipfsHash,
            registrationTime,
            msg.sender
        );
 
        // Create an attestation via the EAS.
        // Parameters: schema, recipient, expiration (0 means non-expiring), revocable flag, and data.
        uint256 attestationId = eas.attest(IP_ASSET_SCHEMA, msg.sender, 0, true, attestationData);
        asset.attestationId = attestationId;
 
        emit AssetRegistered(assetId, msg.sender, title, registrationTime, attestationId);
    }
 
    // ====================================================
    // Asset Verification and Attestation Update
    // ====================================================
 
    /**
     * @notice Verifies an IP asset.
     * @dev Only accounts with the VERIFIER_ROLE can call this function.
     * @param assetId The ID of the asset to verify.
     * @param isVerified The verification result (true if verified).
     * @param verificationComments Additional comments regarding the verification.
     */
    function verifyAsset(
        uint256 assetId,
        bool isVerified,
        string memory verificationComments
    ) external whenNotPaused nonReentrant onlyRole(VERIFIER_ROLE) {
        IPAsset storage asset = ipAssets[assetId];
        require(asset.id != 0, "Asset does not exist");
 
        asset.verified = isVerified;
        emit AssetVerified(assetId, isVerified, verificationComments);
    }
 
    // ====================================================
    // Dispute Resolution with Attestation Revocation Option
    // ====================================================
 
    /**
     * @notice Files a dispute for an asset.
     * @dev Only the asset owner is allowed to file a dispute to prevent frivolous claims.
     * @param assetId The ID of the asset for which the dispute is filed.
     * @param disputeDetails Detailed information about the dispute.
     */
    function fileDispute(
        uint256 assetId,
        string memory disputeDetails
    ) external whenNotPaused nonReentrant {
        IPAsset storage asset = ipAssets[assetId];
        require(asset.id != 0, "Asset does not exist");
        require(msg.sender == asset.owner, "Only asset owner can file a dispute");
        require(!asset.disputeFiled, "Dispute already filed");
 
        asset.disputeFiled = true;
        asset.disputeDetails = disputeDetails;
        asset.disputeResolved = false;
        asset.disputeResolution = "";
 
        emit DisputeFiled(assetId, msg.sender, disputeDetails);
    }
 
    /**
     * @notice Resolves a dispute for an asset.
     * @dev Only accounts with the DISPUTE_RESOLVER_ROLE can resolve disputes.
     * @param assetId The ID of the asset with the dispute.
     * @param resolution The outcome of the dispute resolution.
     * @param revokeAttestation If true, revokes the asset's attestation using EAS.
     */
    function resolveDispute(
        uint256 assetId,
        string memory resolution,
        bool revokeAttestation
    ) external whenNotPaused nonReentrant onlyRole(DISPUTE_RESOLVER_ROLE) {
        IPAsset storage asset = ipAssets[assetId];
        require(asset.id != 0, "Asset does not exist");
        require(asset.disputeFiled, "No dispute filed");
        require(!asset.disputeResolved, "Dispute already resolved");
 
        asset.disputeResolved = true;
        asset.disputeResolution = resolution;
 
        // If required, revoke the attestation through EAS.
        if (revokeAttestation && asset.attestationId != 0) {
            // Prepare revocation data with resolution details.
            bytes memory revocationData = abi.encode(assetId, resolution, block.timestamp);
            bool success = eas.revoke(asset.attestationId, revocationData);
            require(success, "Attestation revocation failed");
            asset.attestationId = 0;
        }
 
        emit DisputeResolved(assetId, resolution);
    }
 
    // ====================================================
    // Asset Ownership Transfer and Re-Attestation
    // ====================================================
 
    /**
     * @notice Transfers ownership of an asset to a new owner.
     * @dev Only the current asset owner can initiate the transfer.
     * @param assetId The ID of the asset to transfer.
     * @param newOwner The address of the new owner.
     */
    function transferAsset(
        uint256 assetId,
        address newOwner
    ) external whenNotPaused nonReentrant {
        require(newOwner != address(0), "New owner address cannot be zero");
        IPAsset storage asset = ipAssets[assetId];
        require(asset.id != 0, "Asset does not exist");
        require(msg.sender == asset.owner, "Only owner can transfer asset");
 
        address previousOwner = asset.owner;
        asset.owner = newOwner;
        emit AssetTransferred(assetId, previousOwner, newOwner);
    }
 
    /**
     * @notice Re-attests an asset to update its attestation (e.g., after transfer).
     * @dev Callable only by the current asset owner. Revokes the old attestation if it exists.
     * @param assetId The ID of the asset to re-attest.
     */
    function reattestAsset(
        uint256 assetId
    ) external whenNotPaused nonReentrant {
        IPAsset storage asset = ipAssets[assetId];
        require(asset.id != 0, "Asset does not exist");
        require(msg.sender == asset.owner, "Only owner can re-attest asset");
        require(!asset.disputeFiled, "Cannot re-attest during active dispute");
 
        uint256 oldAttestationId = asset.attestationId;
 
        // If an attestation exists, revoke it.
        if (oldAttestationId != 0) {
            bytes memory revocationData = abi.encode(assetId, "Re-attestation", block.timestamp);
            bool success = eas.revoke(oldAttestationId, revocationData);
            require(success, "Old attestation revocation failed");
        }
 
        // Update registration time for new attestation.
        asset.registrationTime = block.timestamp;
        // Re-encode attestation data.
        bytes memory attestationData = abi.encode(
            assetId,
            asset.title,
            asset.description,
            asset.ipfsHash,
            asset.registrationTime,
            asset.owner
        );
        // Create a new attestation.
        uint256 newAttestationId = eas.attest(IP_ASSET_SCHEMA, asset.owner, 0, true, attestationData);
        asset.attestationId = newAttestationId;
 
        emit AssetReattested(assetId, oldAttestationId, newAttestationId);
    }
 
    // ====================================================
    // Administrative Functions
    // ====================================================
 
    /**
     * @notice Updates the EAS contract address.
     * @dev Only callable by an account with DEFAULT_ADMIN_ROLE.
     * @param newEASAddress The new EAS contract address.
     */
    function updateEASAddress(address newEASAddress) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(newEASAddress != address(0), "Invalid EAS address");
        eas = IEAS(newEASAddress);
        emit EASAddressUpdated(newEASAddress);
    }
 
    /**
     * @notice Pauses the contract in case of emergency.
     * @dev Only callable by an account with DEFAULT_ADMIN_ROLE.
     */
    function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _pause();
    }
 
    /**
     * @notice Unpauses the contract.
     * @dev Only callable by an account with DEFAULT_ADMIN_ROLE.
     */
    function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _unpause();
    }
}

Contract overview

The contract leverages OpenZeppelin libraries, such as AccessControl, Pausable, ReentrancyGuard, and Counters, to implement secure role management, emergency stops, reentrancy protection, and unique ID generation for IP assets. It also integrates with an external EAS via a defined interface, allowing the creation and revocation of immutable attestations that certify IP asset registration.

Role management and access control

Several roles are defined to segregate duties and protect sensitive operations:

  • Default admin role: Holds ultimate control to perform administrative tasks like updating the EAS address and pausing or unpausing the contract.
  • Registrar role: Authorized to register new IP assets. Only accounts with this role can call the registration function.
  • Verifier role: Permitted to verify the authenticity of IP assets. Accounts with this role can update an asset’s verification status.
  • Dispute resolver role: Empowered to resolve disputes related to IP assets. Only these accounts can resolve disputes and, if needed, revoke attestations.

Access to each function is restricted by modifiers that check the caller’s role, ensuring that only authorized addresses can perform sensitive operations.

EAS integration and attestation schema

The contract interacts with the Ethereum Attestation Service (EAS) using the defined IEAS interface. Two main functions are used:

  • attest: Creates an attestation for an IP asset using a predefined schema that includes the asset ID, title, description, IPFS hash, registration time, and owner address.
  • revoke: Revokes an existing attestation, typically used during dispute resolution or asset re-attestation.

The attestation schema is stored as a constant (IP_ASSET_SCHEMA), ensuring that each attestation follows a consistent structure.

Asset registration with EAS attestation

The registerAsset function allows an account with the REGISTRAR_ROLE to register a new IP asset. Key steps include:

  • Validating that the title and IPFS hash are non-empty.
  • Generating a unique asset ID using a counter.
  • Storing asset details such as title, description, IPFS hash, and the registration timestamp.
  • Encoding the asset data as per the attestation schema and calling the EAS attest function.
  • Storing the returned attestation ID in the asset record.
  • Emitting an event to log the registration and attestation details.

This process creates an immutable on-chain record that is backed by an off-chain attestation.

Asset verification and update

The verifyAsset function enables an account with the VERIFIER_ROLE to update an asset’s verification status. This function sets the asset’s verified flag and logs the verification result along with any comments. This verification step is crucial for ensuring that only validated IP assets are recognized by the system.

Dispute resolution and attestation revocation

Disputes can be filed by the asset owner using the fileDispute function. This function:

  • Ensures that only the asset owner can file a dispute.
  • Checks that no dispute has been filed already.
  • Records dispute details and updates the asset’s dispute status.
  • Emits an event to log the dispute filing.

To resolve a dispute, an account with the DISPUTE_RESOLVER_ROLE calls the resolveDispute function. This function:

  • Updates the asset’s dispute resolution status and records the resolution outcome.
  • Optionally revokes the asset’s attestation via the EAS if the resolution deems the asset registration invalid.
  • Emits an event to log the dispute resolution and any attestation revocation.

Asset ownership transfer and re-attestation

Ownership transfer is handled by the transferAsset function, which allows the current owner to transfer an asset to a new owner. It verifies that:

  • The caller is the current asset owner.
  • The new owner address is valid.

Although the ownership change is recorded on-chain, the original attestation remains unless an update is required. For updating the attestation, the reattestAsset function can be called by the current asset owner. This function:

  • Revokes the old attestation (if one exists) by calling the EAS revoke function.
  • Updates the registration time.
  • Encodes new attestation data and creates a new attestation via the EAS.
  • Stores the new attestation ID in the asset record and emits an event to log the re-attestation.

Administrative functions

Critical administrative functions are restricted to accounts with the default admin role:

  • Updating the EAS address: The updateEASAddress function lets the admin update the EAS contract address, which is vital if the EAS implementation changes.
  • Pausing and unpausing the contract: The pause and unpause functions allow an emergency stop of all contract operations, safeguarding against unexpected issues.

Security enhancements and production-grade features

To ensure robust security and production readiness, the contract includes:

  • Reentrancy protection: All external state-changing functions use the nonReentrant modifier to prevent reentrancy attacks.
  • Pausability: The whenNotPaused modifier is applied to critical functions, allowing the contract to be paused during emergencies.
  • Role-based access control: Functions are strictly restricted by roles, ensuring proper separation of duties.
  • Detailed event logging: Every critical operation emits events to create an audit trail, facilitating off-chain monitoring and accountability.
  • Attestation management: Integration with EAS provides immutable attestations of asset registration, with options for revocation and re-attestation in case of disputes or ownership changes.