Abstract

This proposal is an extension of ERC-721 and implements additional functionality and information pertaining to the NFT’s underlying physical asset by capturing information that enables the holder of physical asset backed NFTs to verify authenticity and facilitate redemption of the underlying physical assets. This proposal is primarily aimed at providing transparency by disclosing details of involved parties and provides opportunity to define and make readily available relevant legal relationship between NFT holder and the owner/holder of the respective underlying physical asset. This proposal makes the token issuer accountable to embed accurate information on a set of standardized information about the underlying physical asset and the involved key parties.

Motivation

The first wave of NFT use cases encompass predominately the representation of ownership of digital assets. In view of the anticipated trend to tokenize any real-world asset, it is to be expected that the use cases of NFTs will rapidly grow and expand around physical assets. The absence of an embedded standardized set of information pertaining to the underlying physical asset together with lack of transparency of involved key parties, creates an unnecessary hurdle for NFT holders and potential users which might, as a result, hinder mass adoption of NFTs that are used as ownership representation of a specific physical asset.

Addressing the lack of readily available information and paving the way for mass adoption for a tokenized economy, this proposal requires that each minted token includes a defined number of predefined variables enabling verification of authenticity and facilitating redemption of the underlying physical asset.

Specification

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY” and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

When a token is minted, its properties SHOULD be initialized beforehand, with each field being defined as follows:

  • Token issuer: identification of an individual or entity minting the NFT
    The token issuer is the key person connecting the physical asset and the digital representation. By identifying and disclosing the token issuer, a reference point is made instantly available to the NFT holder which allows them to conduct a due diligence on the NFT issuer and assessment of the NFT issuer’s trustworthiness. At the same time, it creates accountability for the token issuer which leads to overall improvement and standardisation of the NFT minting process. Token issuers will compete for best practices and recognition to gain advantages over competitors. A reputable NFT issuer will e.g. keep information on the legal owner of the physical asset prior to the minting of the underlying physical asset to satisfy any AML and KYC concerns. Ideally the NFT issuer is identified by a name but may also be identifiable via unique identification number or network ID that is issued by a service provider who stores relevant information on the NFT issuer.

  • Asset holder: identification of legal owner of underlying physical asset
    In view of a redemption of the underlying physical asset and enforcing of legal rights, it is (from a legal perspective) essential for the NFT holder to identify the legal owner of the underlying physical asset. It allows the NFT holder to consider the legal counterparty risk. It cannot be assumed that the NFT issuer is the legal owner of the underlying physical asset, therefore it is vital for the NFT holder to have instant access to this additional information. Same as with the NFT issuer’s identity, the legal owner is ideally identified by a name but may also be identifiable via unique identification number or network ID that is issued by a service provider who stores relevant information on the legal owner.

  • Storage location: identification of storage location of underlying physical asset
    Certain physical assets require specific storage conditions. A digital representation of an inappropriately stored physical asset may impact the value of the NFT significantly. Disclosing the storage location and making it directly accessible to the NFT holder, allows them to evaluate the storage risk of the underlying physical asset. In addition, it provides the NFT holder with a second point of contact for the enforcement of the redemption of the underlying physical asset.

  • Terms: identification of legal relationship
    The disclosure and accessibility of the legal basis of the relationship between NFT holder and legal owner of the underlying physical asset promotes token issuers to stipulate and define the legal rights of the involved key parties. It furthermore allows the NFT Holder to conduct a legal risk and enforcement assessment. Ideally, the information is provided by embedding a link to the actual legal documentation such as an agreement or terms. The more information is accessible via the NFT, the better the NFT holder can assess the legal risks associated with enforcement of the redemption of the underlying physical asset.

  • Jurisdiction: governing law and jurisdiction
    The applicable law is an extension of the legal contract disclosure and makes instantly available to the NFT holder or smart contract under what jurisdiction an enforcement would be governed without the need to review the details legal contract. This allows for an instant assessment of jurisdiction risk.

  • Declared value: value of the underlying asset
    Certain auxiliary services such as insurance are tied to a value of the NFT and underlying physical asset. By defining a declared value, NFTs are able to be categorised in certain ways while the declared value provides an indication regarding the underlying asset’s value. The declared value of the underlying physical asset does not necessarily represent the market value.

The terms parameter SHOULD be an HTTP link to a document that is stored on IPFS. This is to ensure that the document is immutable and can be verified by the NFT holder.

When a token with valid properties is to be burned, the properties MUST be removed.

Contract Interface

pragma solidity ^0.8.21;

/**
 * @notice Struct encapsulating fields required to by the ERC-7578 standard to represent the physical asset
 * @param tokenIssuer The network or entity minting the token
 * @param assetHolder The legal owner of the physical asset
 * @param storageLocation The physical location where the asset is stored
 * @param terms Link to IPFS contract, agreement or terms
 * @param jurisdiction The legal justification set out in the terms
 * @param declaredValue The declared value at time of token minting
 */
struct Properties {
    string tokenIssuer;
    string assetHolder;
    string storageLocation;
    string terms;
    string jurisdiction;
    Amount declaredValue;
}

/**
 * @notice Struct encapsulating fields describing the declared value of the physical asset
 * @param currency The currency of the amount
 * @param value The value of the amount
 */
struct Amount {
    string currency;
    uint256 value;
}

/**
 * @notice Required interface of an ERC-7578 compliant contract
 */
interface IERC7578 {
    /**
     * @notice Emitted when the properties of the `tokenId` token are set
     * @param tokenId The ID of the token
     * @param properties The properties of the token
     */
    event PropertiesSet(uint256 indexed tokenId, Properties properties);

    /**
     * @notice Emitted when the properties of the `tokenId` token are removed
     * @param tokenId The ID of the token
     */
    event PropertiesRemoved(uint256 indexed tokenId);

    /**
     * @notice Retrieves all properties of the `tokenId` token
     * @dev Does NOT revert if token doesn't exist
     * @param tokenId The token ID of the minted token
     */
    function getProperties(uint256 tokenId) external view returns (Properties memory properties);
}

When properties are set, the PropertiesSet(uint256 indexed tokenId, Properties properties) event is emitted.

When properties are removed, the PropertiesRemoved(uint256 indexed tokenId) event is emitted.

The getProperties(uint256 tokenId) function MUST return the unique properties of a token. If the ERC-721 token is burned or has no properties set, it SHOULD return an empty Properties struct.

Rationale

By not initializing a token’s properties before minting, one risks that the asset’s provenance represented by the token cannot be established.

Contract level validation is not used on the properties as we believe the accuracy of the data declared is the responsibility of the token issuer. This builds trust on the token issuer and the token itself.

Backwards Compatibility

This standard is compatible with ERC-721.

Reference Implementation

An example of an ERC-721 that includes this proposal using the OpenZeppelin ERC-721 v5 library:

pragma solidity ^0.8.21;

import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { IERC7578, Properties, Amount } from "./interfaces/IERC7578.sol";

/**
 * @title ERC7578
 * @author DESAT
 * @notice Implementation of the ERC-7578: Physical Asset Redemption standard
 **/
contract ERC7578 is ERC721, IERC7578 {
    /**
     * @notice Thrown when the properties of a token are not initialized
     */
    error PropertiesUninitialized();

    /**
     * @notice Retrieves the properties of the `tokenId` token
     */
    mapping(uint256 tokenId => Properties) private _properties;

    /**
     * @notice Initializes the [ERC-721](./eip-721.md) dependency contract by setting a `name` and a `symbol` to the token collection
     */
    constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {}

    /**
     * @notice Initializes the ERC-7578 properties of the `tokenId` token
     */
    function setProperties(uint256 tokenId, Properties calldata properties) public {
        _properties[tokenId] = Properties({
            tokenIssuer: properties.tokenIssuer,
            assetHolder: properties.assetHolder,
            storageLocation: properties.storageLocation,
            terms: properties.terms,
            jurisdiction: properties.jurisdiction,
            declaredValue: Amount({
                currency: properties.declaredValue.currency,
                value: properties.declaredValue.value
            })
        });

        emit PropertiesSet(tokenId, _properties[tokenId]);
    }

    /**
     * @inheritdoc IERC7578
     */
    function getProperties(uint256 tokenId) public view override returns (Properties memory properties) {
        properties = _properties[tokenId];
    }

    /**
     * @notice Removes the properties of the `tokenId` token
     * @param tokenId The ID of the token from which to remove the properties
     */
    function _removeProperties(uint256 tokenId) internal {
        delete _properties[tokenId];
        emit PropertiesRemoved(tokenId);
    }

    /**
     * @notice Override of the {_update} function to remove the properties of the `tokenId` token or
     * to check if they are set before minting
     * @param tokenId The ID of the token being minted or burned
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
        address from = _ownerOf(tokenId);
        if (to == address(0)) {
            _removeProperties(tokenId);
        } else if (from == address(0)) {
            if (bytes(_properties[tokenId].tokenIssuer).length == 0) revert PropertiesUninitialized();
        }

        return super._update(to, tokenId, auth);
    }
}

Security Considerations

To ensure the authenticity of a token’s properties, the setProperties() method should only be called by a trusted externally owned account (EOA) or contract. This trusted entity must verify that the properties accurately reflect the real physical attributes of the token.

Copyright and related rights waived via CC0.