Creating a new ERC proposal for NFT Lien

Hi all, we are proposing a standard for “lien” of NFT, mimicking the property lien Lien - Wikipedia. Please comment here

See the EIP draft EIP-5604 NFT Lien by xinbenlv · Pull Request #5604 · ethereum/EIPs · GitHub

1 Like

A draft of the EIP is published at EIP-5604. We also look forward to contributions and co-authors. Let me know if you are interested. I personally think it’s an important financial vehicle.

1 Like

Hello xinben, just saw your post. I love this idea, actually we are building a business model upon this idea, would love to cooperate with you and dig into that. :grinning:

1 Like

Glad to hear. Happy to collaborate!

1 Like

If we can establish the standard of lien on NFT, the debtor don’t need to transfer the ownership of the collateral to the creditor, which solves the question of trust and low capital utilization rate due to over collateralized. Moreover, the debtor can still use or benefit from the collateral while borrowing against the NFT.

Meanwhile, a lien secures the loan and vests the creditor with the right to realize the value of collateral by acquiring or selling the collateral if the debtor fail to repay the loans.

The proposed standard may sound like the concept of non-possessory contractual lien under Common Law I would say.

2 Likes

Great Idear! We really need it.

2 Likes

Hi @xinbenlv , I am very interested in your proposal. Actually, we have implemented similar functionality in our project Ubiloan. Now I re-wrote a reference implementation based on your interface. You can check the code in Github.

Reference Implementation

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";

import "./IERC5604.sol";

/// @author Allen Zhou
abstract contract ERC5604 is ERC721, IERC5604 {
    
    // Mapping from token ID to lien holder address.
    mapping(unit256 => address) private _tokenLienHolders;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC5604).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC5604-addLienHolder}.
     */
    function addLienHolder(uint256 tokenId, address holder, bytes calldata extraParams) public virtual override(IERC165, ERC721) {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC5604: caller is not token owner or approved");
        require(_tokenLienHolders[tokenId] == address(0), "ERC5604: token lien holder existed");
        require(holder != address(0), "ERC5604: add lien to address(0)");

        _tokenLienHolders[tokenId] = holder;

        emit OnLienPlaced(tokenId, holder, extraParams);
    }

    /**
     * @dev See {IERC5604-removeLienHolder}.
     */
    function removeLienHolder(uint256 tokenId, address holder, bytes calldata extraParams) public virtual override(IERC165, ERC721) {
        require(_tokenLienHolders[tokenId] == _msgSender(), "ERC5604: caller is not token lien holder");

        delete _tokenLienHolders[tokenId];

        emit OnLienRemoved(tokenId, holder, extraParams);
    }

    /**
     * @dev See {IERC5604-hasLien}.
     */
    function hasLien(uint256 tokenId, address holder, bytes calldata extraParams) public view virtual override returns (bool) {
        return _lienHolderOf(tokenId) == holder;
    }

    /**
     * @dev Returns the lien holder of the `tokenId`. Does NOT revert if token doesn't exist.
     */
    function _lienHolderOf(uint256 tokenId) internal view virtual returns (address) {
        return _tokenLienHolders[tokenId];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");

        require(_tokenLienHolders[tokenId] == _msgSender() || _tokenLienHolders[tokenId] == address(0), "ERC5604: caller is not token lien holder");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");

        require(_tokenLienHolders[tokenId] == _msgSender() || _tokenLienHolders[tokenId] == address(0), "ERC5604: caller is not token lien holder");

        _safeTransfer(from, to, tokenId, data);
    }
}
2 Likes

This is wonderful. I can see the implementation very well implemented. Do you have any other comments about the EIP and how it can be improved? I wonder if you would be interested in co-authoring this EIP with me?

Of course, we’ve been trying to provide a better way for NFT Lending Protocol. We can refine this technical solution so that it can pass the rigorous testing of industrial practice directly and share it with the community :smiley:

Look forward to collaborating with you and other interested community members!!