EIP 3664 是一种扩展 NFT 标准的提案,允许 NFT 属性的可定制性、互操作性和演变能力。本文详细介绍了 EIP 3664 的实现原理、与现有标准的不同之处,以及其对 NFT 生态系统的影响,结合具体示例阐述了多种属性类型.
EIP 3664 扩展了 NFT 标准,以支持可定制、互操作和可进化的 NFT 属性。你将学习如何使用它,它是如何工作的,以及它对生态系统的影响。
如果你的 NFT 可以升级,获得技能,并像视频游戏中的角色一样进化,那会怎样?
近年来,NFT 在实用性和技术方面都有了巨大的进步。
从数字艺术和收藏品到进入元宇宙、房地产和 web3 游戏,NFT 推动着新的数字拥有权和价值范式的形成。但随之而来的新用例带来了新的功能,这反过来又要求开发更新和更强大的技术标准。
《以太坊改进提案(EIP 3664)》于 2021 年 7 月发布, 这就是一种这样的演变,扩展了现有的 NFT 标准,以启用可定制、互操作和可进化的 NFT 属性。
在本文中,我们将介绍 EIP 3664 是什么,为什么我们需要它,它与现有的 NFT 协议有何不同,它是如何工作的,以及它对 NFT 生态系统带来了什么影响。
EIP 3664 是一个可扩展的 NFT 元数据协议,允许所有现有的 NFT 协议,如 ERC 721 和 1155 标准,增强其属性的描述能力。如果你不知道 EIP 是什么,请查阅 从零到英雄的智能合约审计师指南。
EIP 3664 不是像 ERC 721 或 1155 的 NFT 创建协议。
相反,它充当一个可扩展的 NFT 元数据框架,以为使用 ERC 721 或 ERC 1155 创建的 NFT 提供属性可扩展性。该规范规定了可以 将属性标记化并用于自定义 NFT 的各种方式。
每个属性都有一个与之相关的 attrId,借助于这个属性,独立的属性可以被 标记化、转移和组合,以创建动态艺术作品、游戏内资产和化身。
EIP 3664 背后的目标是将静态 NFT 转换为可变 NFT。 允许开发者创建模块化的基于组件的 NFT,其属性可以根据特定应用事件转化、转移和过渡。
用户还可以组合和交易这些属性组件,以创造出更好的可组合 NFT。
因此,EIP 3664 旨在补充基础标准,而不是取代它们。
要理解它的必要性,我们必须花一点时间理解基础的 ERC 721 和 ERC 1155 合同提供了什么,以及它们缺少什么。
ERC-721 的关键特性:
ERC-1155 的关键特性:
751 和 1155 的比较:
ERC 721 | ERC1155 | |
---|---|---|
支持的代币创建类型 | 仅限不可替代 | 两者,可替代和不可替代 |
一份合约可以创建的 NFT 收藏数量 | 仅一个 | 多个(无穷无尽) |
开箱即用的批量交易支持 | 不支持 | 支持 |
代币供给 | 固定 | 可铸造和可烧毁 |
开箱即用的交易撤销 | 不能回退交易 | 安全转移功能支持交易撤销 |
支持属性可变性 | 不支持 | 不支持 |
让我们假设你在玩一个 基于 web3 的宝可梦游戏,每个宝可梦都是一个 NFT。
在你的游戏开始时,教授“X”赠送给你 Auditchu - 一个 Eth 类型 的宝可梦 - 它热爱审计智能合约,寻找漏洞,并保护 dApps 免受恶意利用。
Aditchu。图片来源:Midjourney
Auditchu 的属性(特征)可以是它的物理特征,比如爪子和翅膀。
属性也可以描述它的整体特征,如“力量”、“技能”、“类型”和“等级”。
让我们来看一下 Auditchu 的一些属性:
宝可梦类型 | 值 | 主要属性(不变) |
---|---|---|
WingsLevel | 等级 1 | 在完成游戏内任务和使命时升级。 |
WingsImage | 图片 | 每个 wingsLevel 有不同的 wingsImage。可以附加、拆卸或转移。 |
Strength | 30 / 100 | 可变的 - 可能根据审计记录增加或减少 |
Logic | 40 / 100 | 可变的 - 可能根据审计记录增加或减少 |
Vision | 35 /100 | 可变的 - 可能根据审计记录增加或减少 |
Aura | 美学 | 可转移特征,可以交易其他光环 |
每个属性 ID (attrId) 有一个唯一的 URI。URI 是某些数据或资源的唯一标识符。
function attrURI(uint256 attrId) external view returns (string memory);
该 URI 可以指向描述属性的 JSON 元数据文件。
{
"name": "WingsImage",
"description": "在完成游戏内任务和使命时升级。",
"type": "转移可能",
"visual": {
"src": "ipfs://wing.svg",
"alt": "wing component"
}
}
作为 Auditchu 逐渐变老(游戏内活动时间),审计更多合约并在 CodeHawks 赢得安全比赛后,它的力量和经验逐渐增加。
力量的增加带来了像更大翅膀这样的新特征升级!
最终,Auditchu 解锁了他的进化种子,成功转变为更大、更强、聪明的宝可梦 - Auditmax!
图像:Auditmax(Auditchu 的进化版)
宝可梦类型 | 值 | 主要属性(不变) |
---|---|---|
WingsLevel | 等级 3 | 在完成游戏内任务和使命时升级。 |
WingsImage | 图片 | 每个 wingsLevel 有不同的 wingsImage。可以附加、拆卸或转移。 |
Strength | 80 / 100 | 可变的 - 可能根据审计记录增加或减少 |
Logic | 90 / 100 | 可变的 - 可能根据审计记录增加或减少 |
Vision | 90 / 100 | 可变的 - 可能根据审计记录增加或减少 |
Aura | 威严 | 可转移特征,可以交易其他光环。在进化时解锁新光环。 |
Crown (新解锁的特征!) | 图片 | 在进化后附加的特征 |
理解这一点至关重要,在我们的例子中,进化的 NFT 并不是被其他新的 NFT 替代。旧的 NFT 特征正在被更新特征所修改。
这就是属性可组合性的强大之处!
现在我们已经理解了 EIP 3664 的目的,是时候看看代码了!
当前实现的 EIP 3664 扩展了 ERC1155 标准。但 EIP 3664 被设计为 兼容任何类型的不可替代代币标准,不仅仅是 ERC 721 或 1155。
ERC 3664 的主接口定义了某些函数和事件,用于 附加、管理和删除属性
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev ERC3664 合约所需的接口。
*/
interface ERC3664 {
/**
* @dev 当新的属性类型 `attrId` 被铸造时触发。
*/
event AttributeCreated(
uint256 indexed attrId,
string name,
string symbol,
string uri
);
/**
* @dev 当属性类型 `attrId` 的 `value` 被附加到 "to"
* 或从 `from` 被移除时触发,操作员为 `operator`。
*/
event TransferSingle(
address indexed operator,
uint256 from,
uint256 to,
uint256 indexed attrId,
uint256 value
);
/**
* @dev 等同于多个 {TransferSingle} 事件。
*/
event TransferBatch(
address indexed operator,
uint256 from,
uint256 to,
uint256[] indexed attrIds,
uint256[] values
);
/**
* @dev 返回 `tokenId` 所拥有的主要属性类型。
*/
function primaryAttributeOf(uint256 tokenId)
external
view
returns (uint256);
/**
* @dev 返回 `tokenId` 所拥有的所有属性类型。
*/
function attributesOf(uint256 tokenId)
external
view
returns (uint256[] memory);
/**
* @dev 返回 `tokenId` 所拥有的属性类型 `attrId` 的值。
*/
function balanceOf(uint256 tokenId, uint256 attrId)
external
view
returns (uint256);
/**
* @dev 返回 `tokenId` 所拥有的属性类型 `attrIds` 的批量值。
*/
function balanceOfBatch(uint256 tokenId, uint256[] calldata attrIds)
external
view
returns (uint256[] memory);
/**
* @dev 设置 `tokenId` 所拥有的主要属性类型。
*/
function setPrimaryAttribute(uint256 tokenId, uint256 attrId) external;
/**
* @dev 将 `amount` 值的属性类型 `attrId` 附加到 `tokenId`。
*/
function attach(
uint256 tokenId,
uint256 attrId,
uint256 amount
) external;
/**
* @dev {attach} 的 [批量] 版本。
*/
function batchAttach(
uint256 tokenId,
uint256[] calldata attrIds,
uint256[] calldata amounts
) external;
}
interface ERC165 {
/// @notice 查询合约是否实现某个接口
/// @param interfaceID 接口标识符,如 ERC-165 中所述
/// @dev 接口识别由 ERC-165 中描述。本函数使用不到 30,000 gas。
/// @return `true` 如果合约实现了 `interfaceID` 且
/// `interfaceID` 不是 0xffffffff,否则返回 `false`
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
将 ERC-3664 应用于 EIP-1155 合约的第一步是定义一个或多个属性,这些属性将附加到 NFT 上。这些属性通常在使用 (_mint)
和 (_mintBatch)
函数进行代币创建时定义。
_mint(address account, uint256 id, uint256 amount, bytes data)
_mintBatch(address to, uint256[] ids, uint256[] amounts, bytes data)
铸造的属性以两种方式附加到代币/NFT 上:
有三个状态变化的方法用于附加和定义属性。
这些函数只能由代币的所有者或被所有者授权的其他人调用:
1) attach():
用于单个属性
/**
* @dev 将 `amount` 值的属性类型 `attrId` 附加到 `tokenId`。
*/
function attach(
uint256 tokenId,
uint256 attrId,
uint256 amount
) external;
2) batchAttach():
用于从预先确定的属性列表中附加多个可组合的属性,通过 (calldata) 指定
/**
* @dev {attach} 的 [批量] 版本。
*/
function batchAttach(
uint256 tokenId,
uint256[] calldata attrIds,
uint256[] calldata amounts
) external;
3 setPrimaryAttribute():
有助于定义 NFT 的“主要”属性。通过此功能,NFT 可以被归类到特定的类别或类型。
示例:武器类型属性定义一种武器属性类别
事件允许跟踪属性的变化,例如新铸造和属性值更新。
EIP 3664 接口触发3个事件。前端 dApps 可以监听这些事件以自动更新 UI/元数据。
AttributeCreated 在新的属性类型被铸造时触发,包含 ID、名称、符号和 URI 等详细信息。TransferSingle 在属性值变化时触发,或者在属性附加到 NFT 或从 NFT 中删除时触发。TransferBatch 在多个属性值变化(附加或移除)时触发。
视图函数使得能够查询链上任何 NFT 当前组合的属性。
primaryAttributeOf 返回Token的主要属性 ID。attributesOf 返回给定 (tokenId) 的 NFT 所拥有的所有属性 ID 的数组。balanceOf 获取属于 (tokenId) 的特定属性类型 (attrId) 的值。balanceOfBatch 与 (balanceOf) 相似,但允许调用者一次检查多个属性的值。
提供一个 (attrId) 数组。函数返回与每个 ID 相对应的值数组。
EIP 3664 主要支持以下类型的属性可扩展性(确认):
每个这些属性接口都从 IERC3664Metadata
接口继承了一些属性。
元数据接口:
IERC3664Metadata
接口是可选的,使得可以管理属性的元数据。
/**
* @dev ERC3664 标准中的可选元数据函数接口。
*/
interface IERC3664Metadata is IERC3664 {
/**
* @dev 返回属性的名称。
*/
function name(uint256 attrId) external view returns (string memory);
/**
* @dev 返回属性的符号。
*/
function symbol(uint256 attrId) external view returns (string memory);
/**
* @dev 返回 `attrId` 属性的统一资源标识符 (URI)。
*/
function attrURI(uint256 attrId) external view returns (string memory);
}
我们即将研究的每个属性都扩展自 IERC3664Metada
接口以方便处理。
1) 一般属性
2) 可变或可更新属性
remove(uint256 tokenId, uint256 attrId)
:此函数从某个代币( tokenId
)中移除属性 ( attrId
)。在此操作之后,该代币将不再拥有该属性。在游戏场景中很有用,例如角色可能会失去某种能力或特征。attrId
)的值可以通过某个 amount
的值来增加或减少,使用 increase
和 decrease
函数。
/**
* @dev ERC3664 中的可更新函数接口。
*/
interface IERC3664Updatable is IERC3664Metadata {
/**
* @dev 从 `tokenId` 中移除属性类型 `attrId`。
*/
function remove(uint256 tokenId, uint256 attrId) external;
/**
* @dev 增加 `tokenId` 的属性类型 `attrId` 的 `amount` 值。
*/
function increase(
uint256 tokenId,
uint256 attrId,
uint256 amount
) external;
/**
* @dev 从 `tokenId` 中减少 `attrId` 的 `amount` 值。
*/
function decrease(
uint256 tokenId,
uint256 attrId,
uint256 amount
) external;
}
3) 可转移属性
isApproved(uint256 from, uint256 to, uint256 attrId)
:isApproved 是一个返回布尔值的函数,用于检查 属性 ( attrId
) 是否被批准可以从一个 tokenID ( from
) 转移到另一个 tokenID ( to
)。approve(uint256 from, uint256 to, uint256 attrId)
:批准属性 ( attrId
) 从一个代币 ( from
) 转移到另一个代币 ( to
)。→ 该函数只能由 from
代币的所有者调用。
→ 触发 AttributeApproval
事件。
/**
* @dev ERC3664 中可转移的函数接口。
*/
interface IERC3664Transferable is IERC3664Metadata {
/**
* @dev 当属性类型 `attrId` 被批准转移 "to" 从 `from` 由 `operator` 时触发。
*/
event AttributeApproval(
address indexed operator,
uint256 from,
uint256 to,
uint256 attrId
);
/**
* @dev 如果 `attrId` 被批准转移至 `to` 代币,那么返回 true。
*/
function isApproved(
uint256 from,
uint256 to,
uint256 attrId
) external view returns (bool);
/**
* @dev 由 `from` 持有者调用,批准 `from` 的 token 中的属性 `attrId` 转移到 `to` 代币
* 触发 {AttributeApproval} 事件。
*/
function approve(
uint256 from,
uint256 to,
uint256 attrId
) external;
/**
* @dev 将属性类型 `attrId` 从代币 `from` 转移到 `to`
* 触发 {TransferSingle} 事件。
*/
function transferFrom(
uint256 from,
uint256 to,
uint256 attrId
) external;
}
4) 可升级属性
mintWithLevel(uint256 attrId, string memory name, string memory symbol, string memory uri, uint8 maxLevel)
函数用于铸造具有给定 attrId、名称、符号、URI 和最大等级的属性。upgrade(uint256 tokenId, uint256 attrId, uint8 level)
是一个将特定属性 ( attrId
) 的代币 ( tokenId
) 升级到某个等级的函数。该函数只能由代币的所有者调用。
interface IERC3664Upgradable is IERC3664Metadata {
/*当属性 (attrId) 的代币 (tokenId) 升级到某个等级时触发。*/
event AttributeUpgraded(
uint256 indexed tokenId,
uint256 indexed attrId,
uint8 level
);
/*返回特定属性 (attrId) 的代币 (tokenId) 的当前等级。*/
function levelOf(uint256 tokenId, uint256 attrId)
external
view
returns (uint8);
function mintWithLevel(
uint256 attrId,
string memory name,
string memory symbol,
string memory uri,
uint8 maxLevel
) external;
function upgrade(
uint256 tokenId,
uint256 attrId,
uint8 level
) external;
}
5) 可进化属性:
示例:在进化过程中解锁的王冠属性。
period(uint256 tokenId, uint256 attrId)
- 返回在何种时间段后,代币 ( tokenId
) 的属性 ( attrId
) 可以进化或尝试进化。evolutive(uint256 tokenId, uint256 attrId)
: 到达进化资格后,持有者可以调用该函数尝试进化。进化的成功可能并不确定,可能依赖于 NFT 创建者定义的各种因素。
一些方法可能包括:
→ 在达到某个阈值(例如:等级 10、魔法技能达到-黄金等级)时触发资格。
→ 硬编码进化成功概率(60%)。
→ 随机数生成。
→ 每次失败后增加成功的机会,以保证最后能够进化。
→ 或者一些基于时间的解锁技术,如区块编号。
repair(uint256 tokenId, uint256 attrId)
: 无法进化的属性需要自我修复才能正常操作。( repair
)函数用于修复代币 ( tokenId
) 的属性 ( attrId
),允许其在进化失败后重新进化
/**
* @dev ERC3664 中可进化函数的接口。
*/
interface IERC3664Evolvable is IERC3664Metadata {
/*当代币 (tokenId) 的属性 (attrId) 可进化(状态为 true)或不再可进化(状态为 false)时触发。*/
event AttributeEvolvable(
address indexed operator,
uint256 tokenId,
uint256 attrId,
bool status
);
/*当代币 (tokenId) 的属性 (attrId) 被修复时触发。*/
event AttributeRepaired(
address indexed operator,
uint256 tokenId,
uint256 attrId
);
function period(uint256 tokenId, uint256 attrId)
external
view
returns (uint256);
/*此函数只能由代币的所有者或被授权的人调用。*/
function evolutive(uint256 tokenId, uint256 attrId) external;
/*此函数只能由代币的所有者或被授权的人调用。*/
function repair(uint256 tokenId, uint256 attrId) external;
}
6) 文本属性
attachWithText(uint256 tokenId, uint256 attrId, uint256 amount, bytes memory text)
: 将文本 (text
) 附加到代币 ( tokenId
) 的属性 ( attrId
) 上。amount
参数表示该属性的值。
interface IERC3664TextBased is IERC3664Metadata {
/*返回与代币 (tokenId) 的特定属性 (attrId) 相关联的文本。*/
function textOf(uint256 tokenId, uint256 attrId)
external
view
returns (bytes memory);
function attachWithText(
uint256 tokenId,
uint256 attrId,
uint256 amount,
bytes memory text
) external;
function batchAttachWithTexts(
uint256 tokenId,
uint256[] calldata attrIds,
uint256[] calldata amounts,
bytes[] calldata texts
) external;
}
最后,如果我们考虑我们的宝可梦示例,Auditmax 的属性可以分类为以下几种。
属性 | 值 | 类型 |
---|---|---|
WingsLevel | 等级 3 | 可升级 |
WingsImage | 图片 | 可转移 |
Strength | 80 / 100 | 可变 |
Logic | 85 / 100 | 可变 |
Vision | 90 / 100 | 可变 |
Aura | - | 可转移 |
Crown (新解锁的特征!) | 等级 1 | 可进化 |
因此,ERC-3664 仅能通过其合约定义可定制的补充属性。
恭喜你到达了终点! 🏁🏁🏁 现在你成功了解了整个新的 NFT 标准。
我们首先了解了 EIP 3664 的内容,它的重要性,以及它与其他 NFT 标准的比较。接下来,我们通过我们的宝可梦示例理解了 可组合属性 的概念。Auditmax 是个高手!⚡
最后,我们深入研究了合约,理解了它的流程,并研究了 EIP 3664 提供的各种属性类型的细微差别,以创造出终极的、可定制的、可进化的非同质化资产。
如果你想了解更多关于智能合约开发的知识,请务必查看 Cyfrin Updraft - 这是智能合约工程师最全面的学习平台。
祝你发挥出色 💪
- 原文链接: cyfrin.io/blog/eip-3664-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!