Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-7595: 抵押 NFT

ERC-721扩展,支持使用基于ERC-20的代币进行抵押。

Authors 571nKY (@571nKY), Cosmos (@Cosmos4k), f4t50 (@f4t50), Harpocrates (@harpocrates555)
Created 2023-03-13
Discussion Link https://ethereum-magicians.org/t/collateralized-nft-standard/18097
Requires EIP-20, EIP-721

摘要

本提案建议对 ERC-721 进行扩展,以允许使用 ERC-20 代币列表进行抵押。该 ERC 集合的所有者可以同时持有原生代币和 ERC-20 代币,其中 ownerOf tokenId 能够解锁相关部分的底层 ERC-20 余额。

动机

NFT 金融的新兴趋势侧重于 NFT 底价,以使 NFT 的市场价值能够作为借贷协议中的抵押品。NFT 底价容易受到 NFT 市场供需动态的影响,其特点是与更广泛的加密货币市场相比,波动性更高。此外,特定 NFT 集合中潜在的价格操纵可能会人为抬高 NFT 市场价格,从而影响借贷协议考虑的底价。仅仅依赖基于市场价值的 NFT 底价既不可预测也不可靠。

此 ERC 解决了加密社区在使用基于 ERC-721 的集合和资产时遇到的各种挑战。此 ERC 带来了诸多优势,例如由有形资产支持的可持续 NFT 版税、链上可验证的底价,以及为 NFT 集合创建者引入额外的货币化途径。

预设

  • 基本预设允许评估指定 NFT 资产的链上可验证价格底线。

  • 动态预设便于根据指定 NFT 资产的预定义抵押规则,在链上修改 tokenURI。

  • 通过版税预设,NFT 集合创建者可以获得资产所有者和外部所有账户 (EOA) 之间交易以及与智能合约交易的版税支付。

  • VRF 预设允许使用 Chainlink 的可验证随机函数 (VRF) 在多个 NFT 资产持有者之间分配抵押品。

扩展到现有的基于 ERC-721 的集合

对于许多无法重新部署的基于 ERC-721 的集合,我们建议实施由智能合约体现的抽象层。该智能合约将复制此 ERC 标准的所有功能,并通过映射授予对抵押品的访问权限。

规范

新的 NFT 集合的 ERC 标准


interface IERC721Envious is IERC721 {
	event Collateralized(uint256 indexed tokenId, uint256 amount, address tokenAddress);
	event Uncollateralized(uint256 indexed tokenId, uint256 amount, address tokenAddress);
	event Dispersed(address indexed tokenAddress, uint256 amount);
	event Harvested(address indexed tokenAddress, uint256 amount, uint256 scaledAmount);

	/**
	 * @dev 包含两个元素的数组。每个元素代表从抵押品中提取的佣金百分比。
	 * 第一个元素代表抵押佣金。第二个元素代表取消抵押佣金。
	 * 它们中的每一个都应有 3 位小数的缓冲区,例如 1000 = 1%。
	 *
	 * @param uint 数组中值的 256 索引。
	 */
	function commissions(uint256 index) external view returns (uint256);

	/**
	 * @dev “黑洞”是任何保证发送到它的代币不会被检索的地址。
	 * 注意:某些代币在转移到零地址时会恢复。
	 *
	 * @return address 黑洞的地址。
	 */
	function blackHole() external view returns (address);

	/**
	 * @dev 将用于收集所收集佣金的代币。
	 *
	 * @return address 代币的地址。
	 */
	function communityToken() external view returns (address);

	/**
	 * @dev 可用于收集的代币池。
	 *
	 * @param uint256 数组中的索引。
	 * @return address 代币的地址。
	 */
	function communityPool(uint256 index) external view returns (address);

	/**
	 * @dev 可用于收集的代币余额。
	 *
	 * @param address 代币的地址。
	 * @return uint256 代币余额。
	 */
	function communityBalance(address tokenAddress) external view returns (uint256);

	/**
	 * @dev 已分散的代币数组。
	 *
	 * @param uint256 数组中的索引。
	 * @return address 分散代币的地址。
	 */
	function disperseTokens(uint256 index) external view returns (address);

	/**
	 * @dev 已分散的代币数量。
	 *
	 * @param address 代币的地址。
	 * @return uint256 代币余额。
	 */
	function disperseBalance(address tokenAddress) external view returns (uint256);

	/**
	 * @dev 已从分散中提取的代币数量。
	 *
	 * @param address 代币的地址。
	 * @return uint256 已提取的代币总数。
	 */
	function disperseTotalTaken(address tokenAddress) external view returns (uint256);

	/**
	 * @dev 每个 tokenId 已提取的分散量。
	 *
	 * @param tokenId 单元的唯一标识符。
	 * @param address 代币的地址。
	 * @return uint256 已提取的代币数量。
	 */
	function disperseTaken(uint256 tokenId, address tokenAddress) external view returns (uint256);

	/**
	 * @dev `tokenId` 到之前已抵押的代币地址的映射。
	 *
	 * @param tokenId 单元的唯一标识符。
	 * @param 数组中的索引。
	 * @return address 代币的地址。
	 */
	function collateralTokens(uint256 tokenId, uint256 index) external view returns (address);

	/**
	 * @dev 存储在 `tokenId` 下的代币余额。
	 *
	 * @param tokenId 单元的唯一标识符。
	 * @param address 代币的地址。
	 * @return uint256 代币余额。
	 */
	function collateralBalances(uint256 tokenId, address tokenAddress) external view returns (uint256);

	/**
	 * @dev 用于收集的计算器函数。
	 *
	 * @param amount 要花费的 `communityToken` 数量
	 * @param address 要收集的代币的地址
	 * @return 基于输入的收集金额
	 */
	function getAmount(uint256 amount, address tokenAddress) external view returns (uint256);

	/**
	 * @dev 收集以 `communityToken` 交换收集的佣金费用。
	 *
	 * @param amounts[] 要抵押的数量数组
	 * @param address[] 代币地址数组
	 */
	function harvest(uint256[] memory amounts, address[] memory tokenAddresses) external;

	/**
	 * @dev 使用不同的代币和数量抵押 NFT。
	 *
	 * @param tokenId 特定 NFT 的唯一标识符
	 * @param amounts[] 要抵押的数量数组
	 * @param address[] 代币地址数组
	 */
	function collateralize(
		uint256 tokenId,
		uint256[] memory amounts,
		address[] memory tokenAddresses
	) external payable;

	/**
	 * @dev 提取基础抵押品。
	 *
	 * 要求:
	 * - 仅 NFT 所有者
	 *
	 * @param tokenId 特定 NFT 的唯一标识符
	 * @param amounts[] 要抵押的数量数组
	 * @param address[] 代币地址数组
	 */
	function uncollateralize(
		uint256 tokenId, 
		uint256[] memory amounts, 
		address[] memory tokenAddresses
	) external;

	/**
	 * @dev 在所有现有代币中拆分抵押品。
	 *
	 * @param amounts[] 要在所有 NFT 所有者之间分散
	 * @param address[] 要分散的代币的地址
	 */
	function disperse(uint256[] memory amounts, address[] memory tokenAddresses) external payable;
}

已经部署的 NFT 集合的抽象层


interface IEnviousHouse {
	event Collateralized(
		address indexed collection,
		uint256 indexed tokenId,
		uint256 amount,
		address tokenAddress
	);
	
	event Uncollateralized(
		address indexed collection,
		uint256 indexed tokenId,
		uint256 amount,
		address tokenAddress
	);
	
	event Dispersed(
		address indexed collection,
		address indexed tokenAddress,
		uint256 amount
	);
	
	event Harvested(
		address indexed collection,
		address indexed tokenAddress,
		uint256 amount,
		uint256 scaledAmount
	);

	/**
	 * @dev totalCollections 函数返回注册集合的总数。
	 *
	 * @return uint256 注册集合的数量。
	 */
	function totalCollections() external view returns (uint256);

	/**
	 * @dev “黑洞”是任何保证发送到它的代币不会被检索的地址。
	 * 注意:某些代币在转移到零地址时会恢复。
	 *
	 * @param address 集合地址。
	 * @return address 黑洞的地址。
	 */
	function blackHole(address collection) external view returns (address);

	/**
	 * @dev collections 函数基于集合索引输入返回集合地址。
	 *
	 * @param uint256 注册集合的索引。
	 * @return address 地址集合。
	 */
	function collections(uint256 index) external view returns (address);

	/**
	 * @dev collectionIds 函数基于集合地址输入返回集合索引。
	 * 
	 * @param address 集合地址。
	 * @return uint256 集合索引。
	 */
	function collectionIds(address collection) external view returns (uint256);
	
	/**
	 * @dev specificCollections 函数返回特定集合是否遵循 ERC721 标准。
	 * 
	 * @param address 集合地址。
	 * @return bool 特定集合。
	 */
	function specificCollections(address collection) external view returns (bool);
	
	/**
	 * @dev 包含两个元素的数组。每个元素代表从抵押品中提取的佣金百分比。
	 * 第一个元素代表抵押佣金。第二个元素代表取消抵押佣金。
	 * 它们中的每一个都应有 3 位小数的缓冲区,例如 1000 = 1%。
	 *
	 * @param address 集合地址。
	 * @param uint256 数组中值的索引。
	 * @return uint256 收集的佣金。
	 */
	function commissions(address collection, uint256 index) external view returns (uint256);
	
	/**
	 * @dev 将用于收集所收集佣金的代币。
	 *
	 * @param address 集合地址。
	 * @return address 代币的地址。
	 */
	function communityToken(address collection) external view returns (address);

	/**
	 * @dev 可用于收集的代币池。
	 *
	 * @param address 集合地址。
	 * @param uint256 数组中的索引。
	 * @return address 代币的地址。
	 */
	function communityPool(address collection, uint256 index) external view returns (address);
	
	/**
	 * @dev 可用于收集的代币余额。
	 *
	 * @param address 集合地址。
	 * @param address 代币的地址。
	 * @return uint256 代币余额。
	 */
	function communityBalance(address collection, address tokenAddress) external view returns (uint256);

	/**
	 * @dev 已分散的代币数组。
	 *
	 * @param address 集合地址。
	 * @param uint256 数组中的索引。
	 * @return address 分散代币的地址。
	 */
	function disperseTokens(address collection, uint256 index) external view returns (address);
	
	/**
	 * @dev 已分散的代币数量。
	 *
	 * @param address 集合地址。
	 * @param address 代币的地址。
	 * @return uint256 代币余额。
	 */
	function disperseBalance(address collection, address tokenAddress) external view returns (uint256);
	
	/**
	 * @dev 已从分散中提取的代币数量。
	 *
	 * @param address 集合地址。
	 * @param address 代币的地址。
	 * @return uint256 已提取的代币总数。
	 */
	function disperseTotalTaken(address collection, address tokenAddress) external view returns (uint256);
	
	/**
	 * @dev 每个 tokenId 已提取的分散量。
	 *
	 * @param address 集合地址。
	 * @param tokenId 单元的唯一标识符。
	 * @param address 代币的地址。
	 * @return uint256 已提取的代币数量。
	 */
	function disperseTaken(address collection, uint256 tokenId, address tokenAddress) external view returns (uint256);
	
	/**
	 * @dev `tokenId` 到之前已抵押的代币地址的映射。
	 *
	 * @param address 集合地址。
	 * @param tokenId 单元的唯一标识符。
	 * @param 数组中的索引。
	 * @return address 代币的地址。
	 */
	function collateralTokens(address collection, uint256 tokenId, uint256 index) external view returns (address);

	/**
	 * @dev 存储在 `tokenId` 下的代币余额。
	 *
	 * @param address 集合地址。
	 * @param tokenId 单元的唯一标识符。
	 * @param address 代币的地址。
	 * @return uint256 代币余额。
	 */
	function collateralBalances(address collection, uint256 tokenId, address tokenAddress) external view returns (uint256);
	
	/**
	 * @dev 用于收集的计算器函数。
	 *
	 * @param address 集合地址。
	 * @param amount 要花费的 `communityToken` 数量。
	 * @param address 要收集的代币的地址。
	 * @return amount 基于输入的收集金额。
	 */
	function getAmount(address collection, uint256 amount, address tokenAddress) external view returns (uint256);
	
	/**
	 * @dev setSpecificCollection 函数支持将任何与 ERC721 标准不兼容的集合添加到异常列表中。
	 *
	 * @param address 集合地址。
	 */
	function setSpecificCollection(address collection) external;
	
	/**
	 * @dev registerCollection 函数授予任何与 ERC721 兼容的集合 Envious 功能,并简化
	 * 向所有 NFT 持有者分发初始最低分配。
	 *
	 * @param address 集合地址。
	 * @param address `communityToken` 的地址。
	 * @param uint256 抵押手续费,传入 / 1e5 * 100%。
	 * @param uint256 取消抵押手续费,传入 / 1e5 * 100%。
	 */
	function registerCollection(
		address collection,
		address token,
		uint256 incoming,
		uint256 outcoming
	) external payable;	

	/**
	 * @dev 收集以 `communityToken` 交换收集的佣金费用。
	 *
	 * @param address 集合地址。
	 * @param amounts[] 要抵押的数量数组。
	 * @param address[] 代币地址数组。
	 */
	function harvest(
		address collection,
		uint256[] memory amounts,
		address[] memory tokenAddresses
	) external;
	
	/**
	 * @dev 使用不同的代币和数量抵押 NFT。
	 *
	 * @param address 集合地址。
	 * @param tokenId 特定 NFT 的唯一标识符。
	 * @param amounts[] 要抵押的数量数组。
	 * @param address[] 代币地址数组。
	 */
	function collateralize(
		address collection,
		uint256 tokenId,
		uint256[] memory amounts,
		address[] memory tokenAddresses
	) external payable;
	
	/**
	 * @dev 提取基础抵押品。
	 *
	 * 要求:
	 * - 仅 NFT 所有者
	 *
	 * @param address 集合地址。
	 * @param tokenId 特定 NFT 的唯一标识符。
	 * @param amounts[] 要抵押的数量数组。
	 * @param address[] 代币地址数组。
	 */
	function uncollateralize(
		address collection,
		uint256 tokenId,
		uint256[] memory amounts,
		address[] memory tokenAddresses
	) external;
	
	/**
	 * @dev 在所有现有代币中拆分抵押品。
	 *
	 * @param address 集合地址。
	 * @param amounts[] 要在所有 NFT 所有者之间分散。
	 * @param address[] 要分散的代币的地址。
	 */
	function disperse(
		address collection,
		uint256[] memory amounts,
		address[] memory tokenAddresses
	) external payable;
}

理由

“Envious” 术语选择

我们建议采用术语“Envious”来描述使用此 ERC 标准铸造的任何 NFT 集合,或使用 EnviousHouse 抽象层的任何基于 ERC-721 的 NFT 集合。

使用多个代币进行 NFT 抵押

一些 Web3 项目主要使用一个 ERC-20 代币来抵押特定的 NFT 资产,从而导致 gas 费用增加和用户体验 (UX) 复杂化。

此 ERC 旨在支持在单个交易中使用多个 ERC-20 代币来抵押指定的 NFT 资产。

使用原生代币进行 NFT 抵押

每个 ERC-20 代币都具有不同的地址。但是,原生代币没有地址。为了解决这个问题,我们建议使用空地址 (0x0000000000000000000000000000000000000000) 作为抵押期间原生代币的标识符,因为它消除了与智能合约地址发生冲突的可能性。

分散功能

我们实现了在单个交易中抵押特定 NFT 集合中的所有资产的功能。完整的抵押金额存入智能合约,使每个用户在添加或兑换该特定资产的抵押品时,都可以声明其各自的抵押品份额。

收集功能

每个 Envious NFT 集合都提供了一个选项,可以合并一个社区 ERC-20 代币,该代币可以兑换从抵押和取消抵押活动中累积的佣金。

BlackHole 实例

一些 ERC-20 代币实现禁止转移到空地址,因此有必要在收集交易中拥有可靠的销毁机制。blackHole 智能合约从流通供应中删除 ERC-20 communityTokens,以换取提取的佣金费用。

blackHole 旨在防止从自身转移任何代币,并且只能执行读取操作。它旨在与 Envious 扩展一起用于与佣金收集相关的实现中。

向后兼容性

建议对已经部署的基于 ERC-721 的 NFT 集合使用 EnviousHouse 抽象层。

安全考虑

Envious 可能会遇到与 ERC-721 相似的安全问题,例如 burn、添加资源、接受资源等功能中的隐藏逻辑。

版权

版权和相关权利通过 CC0 放弃。

Citation

Please cite this document as:

571nKY (@571nKY), Cosmos (@Cosmos4k), f4t50 (@f4t50), Harpocrates (@harpocrates555), "ERC-7595: 抵押 NFT [DRAFT]," Ethereum Improvement Proposals, no. 7595, March 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7595.