DeedNFT

Overview

The DeedNFT contract is responsible for representing real assets and physical properties as NFTs on the blockchain. Each NFT is uniquely identifiable and holds comprehensive metadata, including the asset's type, operating agreements, definitions, configurations, and a URI pointing to the asset's detailed information. The contract leverages advanced features such as role-based access control, upgradeability, pausable operations, and validator integration to ensure security, flexibility, and scalability.

Key features of the DeedNFT contract:

  • Inherits from ERC721 and AccessControl: Ensures compliance with the ERC721 standard and enables robust role-based access control using OpenZeppelin's AccessControlUpgradeable.

  • Upgradeability: Implements the UUPS (Universal Upgradeable Proxy Standard) pattern via UUPSUpgradeable, allowing seamless contract upgrades without disrupting existing state or functionality.

  • Pausable Operations: Incorporates PausableUpgradeable to allow administrators to pause and unpause contract operations in case of emergencies or vulnerabilities.

  • Validator Integration: Integrates with external validator and registry contracts (IValidator and IValidatorRegistry) to ensure that only registered and compliant validators can mint and validate deeds.

  • Comprehensive Metadata Management: Utilizes an enhanced DeedInfo structure to store detailed metadata, including operating agreements, definitions, configurations, and associated validators.

  • Batch Operations: Supports batch minting and burning of DeedNFTs, improving efficiency for large-scale operations.

  • Role Definitions: Defines specific roles such as DEFAULT_ADMIN_ROLE and VALIDATOR_ROLE for precise access management.

  • Event Emissions: Emits detailed events for actions like minting, burning, validation changes, and metadata updates, facilitating transparency and off-chain tracking.

Minting and Burning of DeedNFT Tokens

Minting & Burning

The implementation of DeedNFT minting and burning are facilitated using the _mint and _burn functions provided by the OpenZeppelin ERC721 contract. However, the new contract introduces enhanced access controls, batch operations, and validator integrations to ensure secure and efficient token management.

Single Minting and Burning

solidityCopy codefunction mintAsset(
    address owner,
    AssetType assetType,
    string memory ipfsDetailsHash,
    string memory operatingAgreement,
    string memory definition,
    string memory configuration,
    address validator
) external onlyRole(VALIDATOR_ROLE) whenNotPaused returns (uint256) {
    // Minting logic with validations
}

function burnAsset(uint256 deedId)
    public
    onlyDeedOwner(deedId)
    whenNotPaused
{
    _burn(deedId);
    delete deedInfoMap[deedId];
    emit DeedNFTBurned(deedId);
}

Batch Minting and Burning

solidityCopy codefunction mintBatchAssets(
    address[] memory owners,
    AssetType[] memory assetTypes,
    string[] memory ipfsDetailsHashes,
    string[] memory operatingAgreements,
    string[] memory definitions,
    string[] memory configurations,
    address[] memory validators
) external onlyRole(VALIDATOR_ROLE) whenNotPaused returns (uint256[] memory) {
    // Batch minting logic with validations
}

function burnBatchAssets(uint256[] memory deedIds)
    external
    whenNotPaused
{
    for (uint256 i = 0; i < deedIds.length; i++) {
        uint256 deedId = deedIds[i];
        require(
            ownerOf(deedId) == msg.sender,
            "DeedNFT: Caller is not the owner of all deeds"
        );
        burnAsset(deedId);
    }
}

DeedNFT Metadata

The DeedNFT metadata is stored off-chain in a JSON file and includes comprehensive information about the property ownership deed. The metadata structure is enhanced to accommodate detailed descriptions and configurations, ensuring each deed is richly described and easily verifiable.

Metadata Structure Includes:

  • Property Ownership Deed ID: Unique identifier for the deed.

  • Asset Type: Category of the asset (e.g., Land, Vehicle, Estate, Commercial Equipment).

  • Operating Agreement: Details of the operating agreement associated with the deed.

  • Definition: Detailed definition of the asset.

  • Configuration: Configuration data related to the asset.

  • Property Address: Physical address of the property.

  • Owner Details: Information about the current owner.

  • Legal Description: Legal description of the property.

  • Date of Ownership Transfer: Timestamp of when ownership was transferred.

  • Property Value: Monetary value of the property.

The metadata is referenced using the token URI, which is a URL pointing to the JSON file. The token URI can be set and updated using the updateMetadata function, ensuring that metadata remains current and accurate.

Example Metadata Setting

solidityCopy codefunction updateMetadata(
    uint256 deedId,
    string memory ipfsDetailsHash,
    string memory operatingAgreement,
    string memory definition,
    string memory configuration
) external onlyValidatorOrOwner(deedId) whenNotPaused {
    // Metadata update logic with validations
    _setTokenURI(deedId, ipfsDetailsHash);
    // Additional metadata fields update
    emit DeedNFTMetadataUpdated(deedId);
}

Role-Based Access Control

Role Definitions

The DeedNFT contract employs a robust role-based access control system using OpenZeppelin's AccessControlUpgradeable. This system ensures that only authorized entities can perform sensitive operations like minting, burning, and validating deeds.

Defined Roles:

  • DEFAULT_ADMIN_ROLE:

    • Description: Grants administrative privileges, including the ability to manage roles, pause/unpause contract operations, and authorize contract upgrades.

    • Permissions: Add or remove minters and validators, set default validator and registry addresses.

  • VALIDATOR_ROLE:

    • Description: Assigned to validator contracts or accounts responsible for minting and validating deeds.

    • Permissions: Mint DeedNFTs, validate or invalidate deeds, update metadata.

Managing Roles

Adding a Validator

solidityCopy codefunction grantRole(bytes32 role, address account) public onlyRole(getRoleAdmin(role)) {
    _grantRole(role, account);
}

Removing a Validator

solidityCopy codefunction revokeRole(bytes32 role, address account) public onlyRole(getRoleAdmin(role)) {
    _revokeRole(role, account);
}

Example Usage

solidityCopy code_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(VALIDATOR_ROLE, msg.sender);

Upgradeability

UUPS Upgradeable Pattern

The DeedNFT contract is designed with upgradeability in mind, utilizing the UUPS (Universal Upgradeable Proxy Standard) pattern. This approach allows the contract to be upgraded to new implementations without altering the proxy address or losing the existing state.

Authorization for Upgrades

solidityCopy codefunction _authorizeUpgrade(address newImplementation)
    internal
    override
    onlyRole(DEFAULT_ADMIN_ROLE)
{
    // Authorization logic handled by onlyRole modifier
}

Initializing Upgrades

During the initialization phase, the contract sets up essential parameters and roles to ensure smooth upgrade processes in the future.

solidityCopy codefunction initialize(
    address _defaultValidator,
    address _validatorRegistry
) public initializer {
    __ERC721_init("DeedNFT", "DEED");
    __ERC721URIStorage_init();
    __AccessControl_init();
    __Pausable_init();
    __UUPSUpgradeable_init(); // Initialize UUPSUpgradeable

    _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    _grantRole(VALIDATOR_ROLE, msg.sender);

    defaultValidator = _defaultValidator;
    validatorRegistry = _validatorRegistry;
    nextDeedId = 1;
}

Pausable Operations

Emergency Stop Mechanism

The DeedNFT contract incorporates a pausable mechanism, allowing administrators to halt all contract operations in case of emergencies or detected vulnerabilities. This feature enhances the contract's security and resilience.

Pausing the Contract

solidityCopy codefunction pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
    _pause();
    emit Paused(msg.sender);
}

Unpausing the Contract

solidityCopy codefunction unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
    _unpause();
    emit Unpaused(msg.sender);
}

Effects of Pausing

When the contract is paused:

  • Minting and Burning: All minting and burning functions are halted.

  • Metadata Updates: Updates to deed metadata are disabled.

  • Validation: Validation and invalidation of deeds are restricted.

  • Transfer Operations: Transfers of DeedNFTs are prevented.

Validator and Registry Integration

External Validator Contracts

The DeedNFT contract integrates with external validator contracts to ensure that only legitimate and compliant validators can mint and validate deeds. This integration enhances the trustworthiness and reliability of the DeedNFT system.

Validator Roles and Registration

  • Validator Registration: Validators must be registered in the external IValidatorRegistry before they can interact with the DeedNFT contract.

  • Interface Compliance: Validators must implement the IValidator interface, ensuring they adhere to required standards and functionalities.

Setting Default Validator and Registry

solidityCopy codefunction setDefaultValidator(address validator)
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
{
    require(validator != address(0), "DeedNFT: Invalid validator address");
    require(
        IValidatorRegistry(validatorRegistry).isValidatorRegistered(
            validator
        ),
        "DeedNFT: Validator is not registered"
    );
    defaultValidator = validator;
}

function setValidatorRegistry(address registry)
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
{
    require(registry != address(0), "DeedNFT: Invalid registry address");
    validatorRegistry = registry;
}

Validator Functionality

Validators are responsible for:

  • Minting Deeds: Creating DeedNFTs with validated metadata.

  • Validating Deeds: Ensuring the authenticity and compliance of deeds.

  • Metadata Management: Updating and managing deed metadata in collaboration with asset owners.

Comprehensive Metadata Managemen

Enhanced DeedInfo Structure

The DeedInfo struct encapsulates detailed information about each deed, facilitating comprehensive metadata management and validation.

solidityCopy codestruct DeedInfo {
    AssetType assetType;
    bool isValidated;
    string operatingAgreement;
    string definition;
    string configuration;
    address validator;
    uint256[5] __gap; // Reserved for future use
}

Metadata Update Function

solidityCopy codefunction updateMetadata(
    uint256 deedId,
    string memory ipfsDetailsHash,
    string memory operatingAgreement,
    string memory definition,
    string memory configuration
) external onlyValidatorOrOwner(deedId) whenNotPaused {
    require(
        bytes(ipfsDetailsHash).length > 0,
        "DeedNFT: IPFS details hash is required"
    );
    require(
        bytes(operatingAgreement).length > 0,
        "DeedNFT: Operating agreement is required"
    );
    require(
        bytes(definition).length > 0,
        "DeedNFT: Definition is required"
    );

    // Validator address determination
    DeedInfo storage deedInfo = deedInfoMap[deedId];
    address validatorAddress = deedInfo.validator != address(0)
        ? deedInfo.validator
        : defaultValidator;

    require(
        validatorAddress != address(0),
        "DeedNFT: No validator available"
    );
    require(
        IERC165Upgradeable(validatorAddress).supportsInterface(
            type(IValidator).interfaceId
        ),
        "DeedNFT: Validator does not support IValidator interface"
    );

    // Operating agreement validation
    string memory agreementName = IValidator(validatorAddress)
        .operatingAgreementName(operatingAgreement);
    require(
        bytes(agreementName).length > 0,
        "DeedNFT: Invalid operating agreement"
    );

    _setTokenURI(deedId, ipfsDetailsHash);

    deedInfo.operatingAgreement = operatingAgreement;
    deedInfo.definition = definition;
    deedInfo.configuration = configuration;

    if (!hasRole(VALIDATOR_ROLE, msg.sender)) {
        deedInfo.isValidated = false;
    }

    emit DeedNFTMetadataUpdated(deedId);
}

Metadata Storage

The metadata is stored off-chain in a JSON file and includes the following information:

  • Property Ownership Deed ID

  • Asset Type

  • Operating Agreement

  • Definition

  • Configuration

  • Property Address

  • Owner Details

  • Legal Description of the Property

  • Date of Ownership Transfer

  • Property Value

The metadata is referenced using the token URI, which is a URL pointing to the JSON file. The token URI can be set and updated using the updateMetadata function, ensuring that metadata remains current and accurate.

Getter Functions

Retrieve Deed Information

solidityCopy codefunction getDeedInfo(uint256 deedId)
    external
    view
    deedExists(deedId)
    returns (DeedInfo memory)
{
    return deedInfoMap[deedId];
}

Check Subdivisibility of Assets

solidityCopy codefunction canSubdivide(uint256 deedId)
    external
    view
    deedExists(deedId)
    returns (bool)
{
    AssetType assetType = deedInfoMap[deedId].assetType;
    return assetType == AssetType.Land || assetType == AssetType.Estate;
}

Token URI Retrieval

The tokenURI function is overridden to integrate with the validator contract, allowing dynamic URI generation based on validation logic.

solidityCopy codefunction tokenURI(uint256 deedId)
    public
    view
    override
    deedExists(deedId)
    returns (string memory)
{
    DeedInfo storage deedInfo = deedInfoMap[deedId];
    address validatorAddress = deedInfo.validator != address(0)
        ? deedInfo.validator
        : defaultValidator;

    require(
        validatorAddress != address(0),
        "DeedNFT: No validator available"
    );
    require(
        validatorAddress.isContract(),
        "DeedNFT: Validator address is not a contract"
    );
    require(
        IERC165Upgradeable(validatorAddress).supportsInterface(
            type(IValidator).interfaceId
        ),
        "DeedNFT: Validator does not support IValidator interface"
    );

    IValidator validator = IValidator(validatorAddress);
    return validator.tokenURI(deedId);
}

Access Control Management

Adding and Removing Validators

Administrators can manage validators using the grantRole and revokeRole functions provided by AccessControlUpgradeable.

Adding a Validator

solidityCopy codefunction addValidator(address validator) external onlyRole(DEFAULT_ADMIN_ROLE) {
    grantRole(VALIDATOR_ROLE, validator);
}

Removing a Validator

solidityCopy codefunction removeValidator(address validator) external onlyRole(DEFAULT_ADMIN_ROLE) {
    revokeRole(VALIDATOR_ROLE, validator);
}

Events

Event Emissions

The DeedNFT contract emits detailed events for various actions, facilitating transparency and off-chain tracking.

Minting Event

solidityCopy codeevent DeedNFTMinted(
    uint256 indexed deedId,
    DeedInfo deedInfo,
    address indexed minter,
    address validator
);

Burning Event

solidityCopy codeevent DeedNFTBurned(uint256 indexed deedId);

Validation Change Event

solidityCopy codeevent DeedNFTValidatedChanged(uint256 indexed deedId, bool isValid);

Metadata Update Event

solidityCopy codeevent DeedNFTMetadataUpdated(uint256 indexed deedId);

Example Usage

solidityCopy codeemit DeedNFTMinted(deedId, deedInfo, msg.sender, validator);
emit DeedNFTBurned(deedId);
emit DeedNFTValidatedChanged(deedId, isValid);
emit DeedNFTMetadataUpdated(deedId);

Interface and Token URI Handling

Interface Support

The supportsInterface function is overridden to ensure compatibility with multiple interfaces, including ERC721 and AccessControl.

solidityCopy codefunction supportsInterface(bytes4 interfaceId)
    public
    view
    override(ERC721URIStorageUpgradeable, AccessControlUpgradeable)
    returns (bool)
{
    return super.supportsInterface(interfaceId);
}

Dynamic Token URI

The tokenURI function integrates with validator contracts to enable dynamic generation and management of token URIs, enhancing flexibility and ensuring that metadata remains consistent with validation logic.


By integrating these advanced features and adhering to best practices, the DeedNFT contract offers a more secure, flexible, and scalable solution for representing real assets and physical properties as NFTs on the blockchain.

Last updated