通过 EIP-3664 实现的高级 NFT 属性:完整指南
EIP 3664 是一种扩展 NFT 标准的提案,允许 NFT 属性的可定制性、互操作性和演变能力。本文详细介绍了 EIP 3664 的实现原理、与现有标准的不同之处,以及其对 NFT 生态系统的影响,结合具体示例阐述了多种属性类型.
EIP 3664 - 高级 NFT 属性的完整指南
EIP 3664 扩展了 NFT 标准,以支持可定制、互操作和可进化的 NFT 属性。你将学习如何使用它,它是如何工作的,以及它对生态系统的影响。
如果你的 NFT 可以升级,获得技能,并像视频游戏中的角色一样进化,那会怎样?
近年来,NFT 在实用性和技术方面都有了巨大的进步。
从数字艺术和收藏品到进入元宇宙、房地产和 web3 游戏,NFT 推动着新的数字拥有权和价值范式的形成。但随之而来的新用例带来了新的功能,这反过来又要求开发更新和更强大的技术标准。
《以太坊改进提案(EIP 3664)》于 2021 年 7 月发布, 这就是一种这样的演变,扩展了现有的 NFT 标准,以启用可定制、互操作和可进化的 NFT 属性。
在本文中,我们将介绍 EIP 3664 是什么,为什么我们需要它,它与现有的 NFT 协议有何不同,它是如何工作的,以及它对 NFT 生态系统带来了什么影响。
EIP 3664 - 概述
EIP 3664 是一个可扩展的 NFT 元数据协议,允许所有现有的 NFT 协议,如 ERC 721 和 1155 标准,增强其属性的描述能力。如果你不知道 EIP 是什么,请查阅 从零到英雄的智能合约审计师指南。
EIP 3664 不是像 ERC 721 或 1155 的 NFT 创建协议。
相反,它充当一个可扩展的 NFT 元数据框架,以为使用 ERC 721 或 ERC 1155 创建的 NFT 提供属性可扩展性。该规范规定了可以 将属性标记化并用于自定义 NFT 的各种方式。
每个属性都有一个与之相关的 attrId,借助于这个属性,独立的属性可以被 标记化、转移和组合,以创建动态艺术作品、游戏内资产和化身。
为什么 EIP 3664?
EIP 3664 背后的目标是将静态 NFT 转换为可变 NFT。 允许开发者创建模块化的基于组件的 NFT,其属性可以根据特定应用事件转化、转移和过渡。
用户还可以组合和交易这些属性组件,以创造出更好的可组合 NFT。
因此,EIP 3664 旨在补充基础标准,而不是取代它们。
要理解它的必要性,我们必须花一点时间理解基础的 ERC 721 和 ERC 1155 合同提供了什么,以及它们缺少什么。
ERC721 和 ERC1155 是什么?
ERC-721 的关键特性:
- 被确立为以太坊上 NFT 创建的基本标准。
- 仅支持不可替代代币的创建和转移。
- 提供简单的Token与所有者之间的映射。
- 包括跟踪和从一个帐户转移代币的基本方法。
- 拥有多个代币类型的所有者只能一次部署一个集合。因此,对于多个代币,必须部署多个智能合约。
ERC-1155 的关键特性:
- 多代币标准,支持可替代(ERC 20)和不可替代(ERC 721)代币。
- 允许在同一合约中配置多种代币类型,每种代币由唯一的 tokenID 表示。
- 每个 tokenId 可以有自己的元数据、供给和属性。
- 引入 批量转移 功能,允许在一个交易中转移多个代币,从而降低手续费和 避免重复 API 调用来铸造每个集合。
- 包含烧毁和批量烧毁方法,以永久从供给中移除代币。
- 安全转移规则 允许发送者指定在启动代币转账之前必须满足的条件。
751 和 1155 的比较:
| ERC 721 | ERC1155 | |
|---|---|---|
| 支持的代币创建类型 | 仅限不可替代 | 两者,可替代和不可替代 |
| 一份合约可以创建的 NFT 收藏数量 | 仅一个 | 多个(无穷无尽) |
| 开箱即用的批量交易支持 | 不支持 | 支持 |
| 代币供给 | 固定 | 可铸造和可烧毁 |
| 开箱即用的交易撤销 | 不能回退交易 | 安全转移功能支持交易撤销 |
| 支持属性可变性 | 不支持 | 不支持 |
什么是 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 | 美学 | 可转移特征,可以交易其他光环 |
EIP 3664 中的动态属性
- 属性是特定的 提供 NFT Token的额外细节或元数据的属性或特征。它们拥有独立于基础 NFT 的元数据和值。
- 它们作为 可组合组件,可以附加到基础 NFT 并从中删除 - 例如“力量”、“等级”和“世代”。
- 在 ERC-3664 中定义的每个属性都有一个自己的 唯一 ID。这些 ID 用于单独管理和操作属性(链上)。
- EIP-3664 目前扩展了对 可升级、可变、可转移、可进化和基于文本的属性 的支持。我们将在接下来的内容中详细了解它们。
每个属性 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 - 理解合同
当前实现的 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)
- to - 新铸造的代币将分配给的地址。
- id - 每个 ID 代表一个新的可配置代币类型。每种代币类型可以有自己的元数据、供给和其他属性。
- amount - 指定要铸造的代币数量。
- data: - 一些自定义实现的额外数据或签名。
铸造的属性以两种方式附加到代币/NFT 上:
- 要么是在铸造过程中
- 或者动态地应用于更新已经铸造的 NFT。
EIP 3664 合约函数
有三个状态变化的方法用于附加和定义属性。
这些函数只能由代币的所有者或被所有者授权的其他人调用:
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 可以被归类到特定的类别或类型。
示例:武器类型属性定义一种武器属性类别
EIP3664 合约事件
事件允许跟踪属性的变化,例如新铸造和属性值更新。
EIP 3664 接口触发3个事件。前端 dApps 可以监听这些事件以自动更新 UI/元数据。
AttributeCreated 在新的属性类型被铸造时触发,包含 ID、名称、符号和 URI 等详细信息。TransferSingle 在属性值变化时触发,或者在属性附加到 NFT 或从 NFT 中删除时触发。TransferBatch 在多个属性值变化(附加或移除)时触发。
EIP 3664 视图函数
视图函数使得能够查询链上任何 NFT 当前组合的属性。
primaryAttributeOf 返回Token的主要属性 ID。attributesOf 返回给定 (tokenId) 的 NFT 所拥有的所有属性 ID 的数组。balanceOf 获取属于 (tokenId) 的特定属性类型 (attrId) 的值。balanceOfBatch 与 (balanceOf) 相似,但允许调用者一次检查多个属性的值。
提供一个 (attrId) 数组。函数返回与每个 ID 相对应的值数组。
EIP 3664 属性类型
EIP 3664 主要支持以下类型的属性可扩展性(确认):
- 一般属性
- 可变属性
- 可转移属性
- 可升级属性
- 可进化属性
- 文本属性
每个这些属性接口都从 IERC3664Metadata 接口继承了一些属性。
元数据接口:
- ERC 3664 协议中的
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) 一般属性
- 一般属性指的是在本质上不可修改的属性(属性值不能被修改的)。
- 示例:NFT 的创作者,宝可梦类型
- 这些属性在铸造 NFT 代币时设定。
2) 可变或可更新属性
- 可变属性与数值相关联,这些数值可以根据游戏或故事情节中的事件而改变(增加或减少)。
- 示例:健康值、胜利场次、Auditmax 的力量和逻辑(来自我们的宝可梦示例),或武器的战斗力。
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) 可转移属性
- 显而易见,可转移属性可以在同一系列的 NFT 之间进行“转移”。
- 这为在游戏内市场上交易单独属性创造了机会,并与它们关联了稀缺性/稀有性。
- 一旦转移完成,NFT ID 保持不变,但其价格和稀缺性可能波动。
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) 可升级属性
- 可升级属性为属性提供了一个 离散的等级/级别进展标度。
- 示例:普通、稀有、史诗级、超超史诗,或者简单的,等级 1、2 或 3,正如我们的宝可梦示例的情况。
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) 可进化属性:
- 在我看来,这是最有趣和具有变革意义的属性。
- 可进化属性允许 NFT 有机会“进化”成更强大、稀有的版本。
- 所以,你可能会问 - 通过进化,你是指我们的 Auditchu 宝可梦 NFT 将会进化成一个更大的 Auditmax 宝可梦 NFT 吗?
- 这个问题的答案,是不完全是!
- ERC-3664 为 NFT 的属性提供了 进化能力(并不是让整个 NFT 转变为完全不同的 NFT)。该协议描述了 NFT 如何通过进化来强化其属性,而不是将根 NFT 转变为完全不同的 NFT。
示例:在进化过程中解锁的王冠属性。
- 函数
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) 文本属性
- 基于文本的属性允许将任意文本元数据与其他属性类型相关联。这使得 NFT 也能够保持非结构化的基于文本的属性。这与 Loot Project NFTs 类似。
- 函数
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 | 可进化 |
使用 EIP 3664 扩展现有的 NFT 收藏
- 将现有 NFT 收藏扩展到 3664 不需要对其核心实现进行任何修改。
- ERC-3664 可以简单地附加在现有 NFT 之上,以增强它们的额外属性。
- 对于 ERC-721 和 ERC-1155 NFTs,ERC-3664 属性可以在不需要更改基础合约的情况下添加。
- 基础的 ERC-721 和 ERC-1155 实现保持完全不变。
因此,ERC-3664 仅能通过其合约定义可定制的补充属性。
结论
恭喜你到达了终点! ??? 现在你成功了解了整个新的 NFT 标准。
我们首先了解了 EIP 3664 的内容,它的重要性,以及它与其他 NFT 标准的比较。接下来,我们通过我们的宝可梦示例理解了 可组合属性 的概念。Auditmax 是个高手!⚡
最后,我们深入研究了合约,理解了它的流程,并研究了 EIP 3664 提供的各种属性类型的细微差别,以创造出终极的、可定制的、可进化的非同质化资产。
如果你想了解更多关于智能合约开发的知识,请务必查看 Cyfrin Updraft - 这是智能合约工程师最全面的学习平台。
祝你发挥出色 ?
- 原文链接: cyfrin.io/blog/eip-3664-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~