
ERC:適用於 ERC-6551 代幣綁定帳戶的插槽式裝備接口
我為 6551 帳戶開發了一套裝備接口,解決了 NFT 錢包缺乏統一裝備邏輯的問題,並透過永久鎖定插槽的功能來實現可證明的鏈上不可變身份。
我一直在為 6551 帳戶開發裝備介面,我不想定義它應該或不應該用於什麼,但其核心問題顯而易見,足以圍繞它編寫一個標準;6551 賦予了 NFT 錢包功能,但並未說明錢包裡應該裝什麼,而每個想要實現「角色裝備欄(loadouts)」功能的專案最終都得從頭編寫自己的插槽邏輯,且彼此之間互不相容。
因此,這是我在經歷了看似無窮無盡的迭代過程、對設計決策進行壓力測試直到架構感覺正確後,最終確定的介面。包含九個函數、三個事件和一個結構體。
沒有種子輪融資,不依賴框架,沒有目錄基礎設施。僅僅是一個原語(primitive)。
interface IERC6551Equipment {
struct SlotEntry {
bytes32 slotId;
address tokenContract;
uint256 tokenId;
uint256 amount;
bool locked;
}
event Equipped(bytes32 indexed slotId, address indexed tokenContract, uint256 indexed tokenId, uint256 amount);
event Unequipped(bytes32 indexed slotId, address indexed tokenContract, uint256 indexed tokenId, uint256 amount);
event SlotLocked(bytes32 indexed slotId, address indexed tokenContract, uint256 tokenId);
function equip(bytes32 slotId, address tokenContract, uint256 tokenId, uint256 amount) external;
function unequip(bytes32 slotId) external;
function lockSlot(bytes32 slotId) external;
function equipBatch(bytes32[] calldata slotIds, address[] calldata tokenContracts, uint256[] calldata tokenIds, uint256[] calldata amounts) external;
function lockSlots(bytes32[] calldata slotIds) external;
function getEquipped(bytes32 slotId) external view returns (address, uint256, uint256);
function getLoadout() external view returns (SlotEntry[] memory);
function isSlotOccupied(bytes32 slotId) external view returns (bool);
function isSlotLocked(bytes32 slotId) external view returns (bool);
}
ERC-165 介面 ID:0xd38f0891
在迭代過程中保留下來的設計決策及其重要性:
插槽為 bytes32 —— 應用程式透過 keccak256("slot.head")、keccak256("slot.weapon") 或任何符合情境的方式定義自己的分類。沒有中央註冊表,不對應該存在哪些插槽預設立場,標準定義的是機制而非分類。如果在不同情境下共用同一個 TBA(代幣綁定帳戶),可以使用命名空間。
鎖定是永久性的 —— 這一點我非常在意。lockSlot() 是不可逆的,沒有解鎖、沒有時間鎖、沒有管理員覆蓋。如果 isSlotLocked() 回傳 true,它將永遠回傳 true,跨越所有權轉移,跨越時間。新持有者將繼承此鎖定。這就是重點所在:可證明的鏈上不可變身份。你在鑄造時鎖定角色的核心特徵,它們就永遠成為了該角色的一部分。如果你想要可更換的東西,就不要鎖定它。
CEI 是強制性的而非建議 —— equip() 和 unequip() 會觸發 safeTransferFrom 並調用接收者回調,規範要求在進行外部調用之前必須更新狀態。我們在自己的實現中經歷了捕捉並修復此問題的過程,因此認為將其在規範中列為「必須(MUST)」而非僅僅是建議是非常重要的。
getLoadout() 一次調用回傳所有內容 —— 所有已佔用的插槽,鎖定狀態也透過結構體中的 bool locked 包含在內。前端和市場無需進行 N 次獨立的 view 調用即可獲得完整圖像。這是一個刻意的結構體變更,將 locked 加入 SlotEntry,以便一次調用就能呈現完整的角色。
批量操作的存在是因為鑄造流程的需求 —— 裝備 12 個特徵並鎖定 5 個身份插槽不應該花費 17 筆交易。equipBatch() + lockSlots() 將其減少到 2 筆。其全有或全無(all-or-nothing)的語義與 1155 的模式一致。
刻意不存在的功能及其原因:
- 沒有
unlockSlot()—— 永久性是特性而非限制。 - 沒有
swap()—— 先卸下再裝備,如果需要原子性,請透過execute()進行多重調用(multicall)。 - 沒有
getLockedSlots()——getLoadout()已涵蓋此功能,事件則負責索引。 - 不對應該存在哪些插槽或其含義預設立場。
先前技術(Prior Art)
我了解來自 RMRK 的 ERC-6220(利用可裝備部件的可組合 NFT)。不同的方法,不同的範疇。6220 直接擴展了 ERC-721,需要一個帶有預定義部件、可裝備地址白名單的目錄(Catalog)系統,以及 RMRK 套件中的 5 個以上介面。它是為視覺可組合性設計的;從組件部分組裝渲染圖像。
本 ERC 專門擴展 ERC-6551,除了 TBA 本身之外不需要額外的基礎設施,並專注於所有權可組合性;代幣物理性地轉移到角色的錢包中。介面總共只有 9 個函數。我在 6220 或其他地方都沒發現的關鍵原語是永久插槽鎖定;這是一種在所有權轉移後依然存在的不可逆鏈上身份。
關於 6551 上的裝備問題之前在原始 ERC-6551 討論串(第 128 帖)中被提出過,當時有人建議將 6220 作為解決方案。但 6220 並未擴展 6551;它直接擴展 721 並需要 RMRK 的目錄基礎設施。本 ERC 是該問題的原生答案。
EIP-4973(帳戶綁定代幣)使用了「裝備/卸下」的術語,但在不同的語境中;它指的是顯示或隱藏靈魂綁定憑證,而不是將可轉移代幣放入命名的插槽中。4973 使代幣不可移動,而本 ERC 使插槽不可移動;不同的原語,不同的使用場景。
ERC-7635(多同質化代幣)提出了一種內建插槽的新代幣標準,但它完全取代了現有的代幣模型,而不是擴展它。本 ERC 在現有的 721 + 1155 + 6551 生態系統中運作;沒有新的代幣類型,只是為已部署的內容提供一個介面。
我還研究了 ERC-998(可組合的自上而下 NFT)和 ERC-4883(可組合的 SVG NFT)。998 早於 6551,處理父子嵌套但未定義命名插槽、鎖定語義或批量操作。4883 僅限於視覺,沒有插槽邏輯。
如果我遺漏了涵蓋此精確領域(針對 TBA 的帶有永久鎖定的插槽式裝備)的先前技術,我真心希望能了解。
參考實現
包含 27 個 Foundry 測試,涵蓋核心流程、鎖定、批量操作、所有權轉移、CEI 驗證和 ERC-165。該介面也正被 https://phantoma.io 的線上前端使用,該前端讀取 getLoadout()、渲染鎖定狀態,並處理注入式和嵌入式錢包提供商的裝備管理。
CC0。歡迎提供反饋。
1 則貼文 - 1 位參與者
[閱讀完整主題](https://ethereum-magicians.org/t/erc-slot-based-equipment-for-erc-6551-token-bound-accounts/28139)