newsence

草擬 ERC:隱私型 ERC-1155 — 具備隱私保護的多代幣標準

Ethereum Magicians·大約 1 個月前

本提案為 ERC-1155 標準引入了隱私保護擴展,利用 Poseidon 哈希承諾與 Groth16 零知識證明來隱藏各地址的餘額與轉帳金額,同時保持代幣類型與總供應量的公開透明。

欄位

狀態
草案 (Draft)

類型
標準軌跡 (Standards Track)

類別
ERC

依賴項目
EIP-712, ERC-5564

摘要 (Abstract)

本 ERC 定義了一種具備隱私保護功能的 ERC-1155 多代幣標準。每個地址、每個 tokenId 的餘額透過 Poseidon 哈希承諾(hash commitments)進行隱藏,而其有效性則透過鏈上的 Groth16 零知識證明(ZK proofs)進行驗證。代幣類型(tokenId)和總供應量保持公開。

本標準將 VOSA (虛擬一次性子帳戶) 模型擴展至多代幣場景——每個(地址, tokenId)對存儲一個隱藏餘額數量的承諾。所有權透過標準 ECDSA 簽名證明(兼容 MetaMask,無需自定義錢包)。

隱私模型:數量和各地址持有量被隱藏。代幣類型、轉帳圖譜(哪些 VOSA 進行了交易)以及每個 tokenId 的總供應量保持可見。這在保護餘額隱私的同時,也能滿足合規性要求。

核心特性

  • 每個 tokenId 的餘額隱私(數量隱藏在 Poseidon 承諾中)

  • 每個 tokenId 可進行任意數量的轉帳(非固定面額)

  • 鏈上 Groth16 ZK 證明驗證(完全去中心化)

  • 標準 ECDSA 簽名 + EVM 原生 20 字節地址

  • O(1) 支出追蹤,具備可配置的週期性清理功能

  • 跨多個 tokenId 的批量鑄造

  • 針對每個 tokenId 的策略(Policy,循環授權)和許可(Permit,一次性授權)

動機 (Motivation)

ERC-1155 的隱私缺口

ERC-1155 公開了所有持有資訊:

balanceOf(0x1234, tokenId=42) → 500 // 任何人都能看到各項目的持有量
TransferSingle(from, to, id, 100) // 數量是公開的

這在多代幣場景中會產生問題:

  • 遊戲:對手可以看到你的庫存(稀有道具、強化藥水、遊戲幣)

  • 忠誠度計畫:競爭對手可以看到積分餘額和兌換模式

  • 代幣化資產:投資組合構成完全透明

  • 收藏品:限量版項目的持有情況公開,可能導致針對性的社交工程攻擊

為何選擇 ERC-1155(多代幣)而非 ERC-721(單一 NFT)?

ERC-1155 涵蓋了更廣泛的使用場景。許多「NFT」應用實際上需要每種代幣類型具備同質化數量

使用場景
代幣模型
為何使用 1155?

遊戲道具
500 把劍,200 瓶藥水
每種類型內同質化,存在多種類型

活動門票
1000 張普通票,50 張 VIP 票
每個等級有對應數量

忠誠度積分
多個積分類別
每個類別內餘額同質化

代幣化商品
100 盎司黃金,50 盎司白銀
每個資產類型有對應數量

半同質化代幣
藝術品的第 1-100 號版本
相同類型,多個副本

對於真正獨一無二的 1-of-1 項目,只需將數量設為 1 —— 協議會將其視為特殊情況處理。

隱私對比

解決方案
數量隱私
持有者身份
轉帳圖譜
代幣類型可見
合規性

ERC-1155




透明

本標準


❌ (設計使然)

可審計

全隱私 NFT




困難

設計理念

本標準旨在實現餘額隱私,而非匿名混幣

  • ✅ 隱藏你持有每個項目的數量

  • ✅ 隱藏轉帳數量

  • ✅ 符合監管要求(資金流向可追溯,代幣類型可見)

  • ❌ 隱藏正在轉帳的代幣類型(tokenId 是公開的)

  • ❌ 完全匿名(轉帳圖譜是可見的)

規範 (Specification)

本文件中的關鍵詞「必須」(MUST)、「不得」(MUST NOT)、「要求」(REQUIRED)、「應當」(SHALL)、「不應當」(SHALL NOT)、「應該」(SHOULD)、「不應該」(SHOULD NOT)、「推薦」(RECOMMENDED)、「不推薦」(NOT RECOMMENDED)、「可以」(MAY) 和「可選」(OPTIONAL) 應按照 RFC 2119 和 RFC 8174 的描述進行解釋。

概述

┌──────────────────────────────────────────────────────────────────────────┐
│ 隱私 ERC-1155 架構 │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ 存儲: │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ mapping(address => mapping(uint256 => bytes32)) balanceCommitment │ │
│ │ mapping(uint256 => uint256) totalSupply │ │
│ │ mapping(address => mapping(uint256 => PolicyMeta)) policies │ │
│ │ mapping(address => mapping(uint256 => PermitMeta)) permits │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ 操作: │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ mint(recipient, tokenId, amount, commitment, proof) │ │
│ │ mintBatch(recipient, tokenIds[], amounts[], commitments[], ...) │ │
│ │ burn(input, tokenId, amount, proof, signature) │ │
│ │ transfer(tokenId, inputs[], outputs[], proof, signatures) │ │
│ │ consolidate(tokenId, inputs[], output, proof, signatures) │ │
│ │ createPolicy(tokenId, spender, commitment, proof, signature) │ │
│ │ createPermit(tokenId, spender, commitment, proof, signature) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘

每個 (address, tokenId) 的鏈上狀態:

mapping(address => mapping(uint256 => bytes32)) balanceCommitment;
// bytes32(0) → 從未使用
// Poseidon(tokenId, amount, blinder, ts) → 活躍餘額(數量已隱藏)
// SPENT_PREFIX | block.number → 已支出(在週期窗口後可清理)

與隱私 ERC-20 的關鍵差異

維度
隱私 ERC-20
隱私 ERC-1155

存儲
address → commitment
address → tokenId → commitment

承諾 (Commitment)
Poseidon(amount, blinder, ts)
Poseidon(tokenId, amount, blinder, ts)

供應量
單一 totalSupply
每個代幣類型對應 totalSupply[tokenId]

策略/許可 (Policy/Permit)
按地址
按 (address, tokenId)

EIP-712
類型哈希中無 tokenId
所有類型哈希均包含 tokenId

批量鑄造
不適用
針對多個 tokenId 的 mintBatch()

轉帳
任意數量,單一代幣
每次轉帳調用僅限一個 tokenId

常量

bytes16 public constant SPENT_PREFIX = 0xDEADDEADDEADDEADDEADDEADDEADDEAD;
uint256 public constant MAX_INPUTS = 10;
uint256 public constant MAX_OUTPUTS = 10;
uint256 public constant TIMESTAMP_WINDOW = 2 hours;
uint256 public constant DEFAULT_CLEANUP_WINDOW = 216_000; // 約 1 個月

數據結構

struct PolicyMeta {
address owner;
address spender;
uint256 expiry; // 0 = 永不過期
bool revoked;
}

struct PermitMeta {
address owner;
address spender;
uint256 expiry;
bool revoked;
bool used; // 單次使用
}

struct CreatePolicyParams {
address input;
bytes32 inputCommitment;
address policyAddress;
bytes32 policyCommitment;
uint256 policyTimestamp;
address changeAddress;
bytes32 changeCommitment;
uint256 changeTimestamp;
address spender;
uint256 expiry;
uint256 deadline;
}

struct CreatePermitParams {
address input;
bytes32 inputCommitment;
address permitAddress;
bytes32 permitCommitment;
uint256 permitTimestamp;
address changeAddress;
bytes32 changeCommitment;
uint256 changeTimestamp;
address spender;
uint256 expiry;
uint256 deadline;
}

核心接口

元數據 (Metadata)

interface IPrivateERC1155Metadata {
function uri(uint256 tokenId) external view returns (string memory);
function totalSupply(uint256 tokenId) external view returns (uint256);
}

VOSA 狀態管理

interface IPrivateERC1155VOSA {
function balanceCommitment(address vosa, uint256 tokenId) external view returns (bytes32);
function hasBalance(address vosa, uint256 tokenId) external view returns (bool);
function isEverUsed(address vosa, uint256 tokenId) external view returns (bool);
function isSpent(address vosa, uint256 tokenId) external view returns (bool);
function batchHasBalance(address[] calldata addrs, uint256[] calldata tokenIds)
external view returns (bool[] memory);
}

核心操作

interface IPrivateERC1155Core {
/// @notice 鑄造代幣 — 發行時 tokenId 和數量是公開的
function mint(
address recipient, uint256 tokenId, uint256 amount, bytes32 commitment,
uint256 outputTimestamp, bytes calldata ephemeralPubKey,
bytes calldata proof, bytes calldata memo
) external returns (bool);

/// @notice 跨多個 tokenId 批量鑄造
function mintBatch(
    address recipient, uint256[] calldata tokenIds, uint256[] calldata amounts,
    bytes32[] calldata commitments, uint256[] calldata outputTimestamps,
    bytes[] calldata ephemeralPubKeys, bytes[] calldata proofs, bytes calldata memo
) external returns (bool);

/// @notice 銷毀代幣(全部或部分銷毀並找零)
function burn(
    address input, uint256 tokenId, bytes32 inputCommitment, uint256 amount,
    address changeAddress, bytes32 changeCommitment, uint256 changeTimestamp,
    bytes calldata changeEphemeralPubKey, bytes calldata signature,
    bytes calldata proof, uint256 deadline
) external returns (bool);

/// @notice 隱私轉帳(N 個輸入 → M 個輸出,單一 tokenId,數量隱藏)
function transfer(
    uint256 tokenId, address[] calldata inputs, bytes32[] calldata inputCommitments,
    address[] calldata outputs, bytes32[] calldata outputCommitments,
    uint256[] calldata outputTimestamps, bytes[] calldata ephemeralPubKeys,
    bytes[] calldata signatures, bytes calldata proof, uint256 deadline,
    int256[] calldata policyChangeIndices, bytes calldata memo
) external returns (bool);

/// @notice 將相同 tokenId 的多個 VOSA 合併為一個
function consolidate(
    uint256 tokenId, address[] calldata inputs, bytes32[] calldata inputCommitments,
    address output, bytes32 outputCommitment, uint256 outputTimestamp,
    bytes calldata ephemeralPubKey, bytes[] calldata signatures,
    bytes calldata proof, uint256 deadline
) external returns (bool);

}

授權接口

策略(循環授權,按 tokenId)

interface IPrivateERC1155Policy {
function createPolicy(
uint256 tokenId, CreatePolicyParams calldata params,
bytes[] calldata ephemeralPubKeys, bytes calldata ownerSignature,
bytes calldata proof, bytes calldata memo
) external returns (bool);

function revokePolicy(address policyAddress, uint256 tokenId) external;

function getPolicy(address policyAddress, uint256 tokenId) external view returns (
    address owner, address spender, uint256 expiry, bool revoked, bool isActive
);

}

許可(一次性授權,按 tokenId)

interface IPrivateERC1155Permit {
function createPermit(
uint256 tokenId, CreatePermitParams calldata params,
bytes[] calldata ephemeralPubKeys, bytes calldata ownerSignature,
bytes calldata proof, bytes calldata memo
) external returns (bool);

function revokePermit(address permitAddress, uint256 tokenId) external;

function getPermit(address permitAddress, uint256 tokenId) external view returns (
    address owner, address spender, uint256 expiry, bool revoked, bool used, bool isActive
);

}

事件 (Events)

interface IPrivateERC1155Events {
event TransferSingle(address indexed operator, address[] inputs, address[] outputs,
uint256 indexed tokenId, bytes32[] outputCommitments,
bytes[] ephemeralPubKeys, bytes memo);
event TransferBatch(address indexed operator, address from, address to,
uint256[] tokenIds, bytes32[] outputCommitments,
bytes[] ephemeralPubKeys, bytes memo);
event Burn(address indexed input, uint256 indexed tokenId, uint256 amount,
address changeAddress, bytes32 changeCommitment, bytes changeEphemeralPubKey);
event Consolidate(uint256 indexed tokenId, address[] inputs, address indexed output,
bytes32 outputCommitment, bytes ephemeralPubKey);
event PolicyCreated(uint256 indexed tokenId, address indexed policyAddress,
address indexed owner, address spender, uint256 expiry,
bytes32 policyCommitment, address changeAddress, bytes32 changeCommitment,
bytes[] ephemeralPubKeys, bytes memo);
event PolicyRevoked(address indexed policyAddress, uint256 indexed tokenId);
event PolicyMigrated(address indexed oldAddress, address indexed newAddress,
uint256 indexed tokenId);
event PermitCreated(uint256 indexed tokenId, address indexed permitAddress,
address indexed owner, address spender, uint256 expiry,
bytes32 permitCommitment, address changeAddress, bytes32 changeCommitment,
bytes[] ephemeralPubKeys, bytes memo);
event PermitUsed(address indexed permitAddress, uint256 indexed tokenId);
event PermitRevoked(address indexed permitAddress, uint256 indexed tokenId);
event AddressCleaned(address indexed addr, uint256 indexed tokenId, uint256 spentBlock);
event MinterUpdated(address indexed newMinter);
event VerifierUpdated(string indexed verifierType, address indexed newVerifier);
event CleanupWindowUpdated(uint256 oldWindow, uint256 newWindow);
}

EIP-712 類型定義

所有類型哈希均將 tokenId 作為第一個欄位,將簽名綁定到特定的代幣類型:

bytes32 constant TRANSFER_1155_TYPEHASH = keccak256(
"Transfer1155(uint256 tokenId,bytes32 inputsHash,bytes32 inputCommitmentsHash,"
"bytes32 outputsHash,bytes32 outputCommitmentsHash,uint256 deadline)"
);

bytes32 constant BURN_1155_TYPEHASH = keccak256(
"Burn1155(uint256 tokenId,address input,bytes32 inputCommitment,uint256 amount,"
"address changeAddress,bytes32 changeCommitment,uint256 changeTimestamp,uint256 deadline)"
);

bytes32 constant CONSOLIDATE_1155_TYPEHASH = keccak256(
"Consolidate1155(uint256 tokenId,bytes32 inputsHash,bytes32 inputCommitmentsHash,"
"address output,bytes32 outputCommitment,uint256 deadline)"
);

bytes32 constant CREATE_POLICY_1155_TYPEHASH = keccak256(
"CreatePolicy1155(uint256 tokenId,address input,bytes32 inputCommitment,"
"address policyAddress,bytes32 policyCommitment,uint256 policyTimestamp,"
"address changeAddress,bytes32 changeCommitment,uint256 changeTimestamp,"
"address spender,uint256 expiry,uint256 deadline)"
);

bytes32 constant CREATE_PERMIT_1155_TYPEHASH = keccak256(
"CreatePermit1155(uint256 tokenId,address input,bytes32 inputCommitment,"
"address permitAddress,bytes32 permitCommitment,uint256 permitTimestamp,"
"address changeAddress,bytes32 changeCommitment,uint256 changeTimestamp,"
"address spender,uint256 expiry,uint256 deadline)"
);

ZK 電路規範

承諾格式

commitment = Poseidon(tokenId, amount, blinder, timestamp)

其中:

  • tokenId: uint256,公開(識別代幣類型)
  • amount: uint256,必須 < 2^96
  • blinder: 域元素(Field element),不得為 0
  • timestamp: uint256,用於防重放攻擊

與 ERC-20 的關鍵差異:tokenId 被包含在承諾中。電路強制要求轉帳中的所有輸入和輸出必須使用相同的 tokenId,防止跨代幣類型的操縱。

Amount1155 電路(鑄造/銷毀)

公開輸入:tokenId, inputCommitments[], outputCommitments[], absAmount,
isWithdraw, outputTimestamps[], txHash
私有輸入:amounts[], blinders[], timestamps[]

約束條件:

  1. 每個承諾 == Poseidon(tokenId, amount, blinder, timestamp)
  2. 所有承諾使用相同的 tokenId
  3. 鑄造:outputAmount == absAmount
  4. 銷毀:inputAmount == absAmount + changeAmount
  5. 所有數量在 [0, 2^96) 範圍內
  6. 所有 blinder ≠ 0
  7. txHash 綁定(包含 tokenId)

變體
角色
公開信號
預估約束數

Amount_0_1_1155
鑄造
6 (tokenId, commitment, amount, isWithdraw, timestamp, txHash)
~900

Amount_1_0_1155
全額銷毀
5 (tokenId, commitment, amount, isWithdraw, txHash)
~900

Amount_1_1_1155
部分銷毀
7 (tokenId, 2 commitments, amount, isWithdraw, timestamp, txHash)
~1,400

Transfer1155 電路

公開輸入:tokenId, inputCommitments[], outputCommitments[],
outputTimestamps[], txHash
私有輸入:inputAmounts[], inputBlinders[], inputTimestamps[],
outputAmounts[], outputBlinders[]

約束條件:

  1. 每個 inputCommitment == Poseidon(tokenId, inputAmount, inputBlinder, inputTimestamp)
  2. 每個 outputCommitment == Poseidon(tokenId, outputAmount, outputBlinder, outputTimestamp)
  3. 所有承諾使用相同的 tokenId(由電路強制執行)
  4. sum(inputAmounts) == sum(outputAmounts)(數量守恆)
  5. 所有數量在 [0, 2^96) 範圍內
  6. 所有 blinder ≠ 0
  7. txHash == Poseidon(tokenId, inputCommitments..., outputCommitments...)

變體
角色
預估約束數

Transfer_1_2_1155
1→2 (轉帳 + 找零)
~1,800

Transfer_2_2_1155
2→2 (標準轉帳)
~2,300

Transfer_5_1_1155
5→1 (合併)
~3,200

註:合約允許最多 MAX_INPUTS=10,但目前的電路部署支持最多 5 個輸入。更大的變體可以從相同的模板編譯。

Poseidon 參數

哈希:Poseidon
寬度:承諾使用 t = 5 (4 輸入 + 1 容量);txHash 視情況而定
輪數:RF = 8 全輪,RP = 60 部分輪
域:BN254 Fr

授權邏輯

按 tokenId 授權 —— 每個 (address, tokenId) 最多有一個授權簽名者:

function _getAuthorizedSigner(address input, uint256 tokenId) internal view returns (address) {
PolicyMeta memory pMeta = _policyMeta[input][tokenId];
PermitMeta memory tMeta = _permitMeta[input][tokenId];

if (pMeta.owner != address(0)) {
    bool isActive = !pMeta.revoked && (pMeta.expiry == 0 || block.timestamp < pMeta.expiry);
    return isActive ? pMeta.spender : pMeta.owner;
} else if (tMeta.owner != address(0)) {
    bool isActive = !tMeta.used && !tMeta.revoked
        && (tMeta.expiry == 0 || block.timestamp < tMeta.expiry);
    return isActive ? tMeta.spender : tMeta.owner;
}
return input;

}

週期清理

與隱私 ERC-20 相同:SPENT 標記編碼了 block.number,在可配置的清理窗口後可刪除。

function cleanup(address[] calldata addrs, uint256[] calldata tokenIds) external returns (uint256 cleaned);

原理闡述 (Rationale)

為何使用 4 個輸入的 Poseidon?

ERC-20 承諾使用 Poseidon(amount, blinder, timestamp) —— 3 個輸入。ERC-1155 增加了 tokenId 作為第 4 個輸入:Poseidon(tokenId, amount, blinder, timestamp)。這將承諾綁定到特定的代幣類型,防止將 100 把劍的承諾冒充為 100 瓶藥水。

電路強制要求轉帳中的所有輸入和輸出使用相同的 tokenId,因此從構造上杜絕了跨類型轉帳。

為何每次轉帳僅限單一 tokenId?

每次 transfer() 調用僅針對一個 tokenId。這保持了電路的簡單性(無需跨代幣的數量守恆)和驗證器的高效性。轉帳多種代幣類型需要多次調用 —— 這在實踐中是可以接受的,因為多類型的原子轉帳較為少見。

跨多個 tokenId 的批量鑄造透過 mintBatch() 支持,因為鑄造不需要 ZK 證明來保證數量守恆(鑄造數量是公開的)。

為何不隱藏 tokenId?

隱藏 tokenId 將需要:

  • 一個有效 tokenId 的默克爾樹(證明 tokenId 存在而不洩露它)

  • 跨類型守恆證明(電路複雜度大幅增加)

  • 隱藏供應量追蹤(與大多數合規要求不相容)

對於大多數使用場景(遊戲道具、忠誠度積分、門票)來說,被轉帳的代幣類型並不敏感,隱藏它的收益微乎其微。保持 tokenId 公開極大地簡化了協議。

為何每個 (address, tokenId) 使用一個 SPENT_MARKER?

單個地址可以持有不同的 tokenId。支出一個 tokenId 不應影響其他 tokenId。SPENT 標記是按 (address, tokenId) 對設置的,允許獨立的生命週期管理。

性能 (Performance)

預估 Gas 消耗(使用模擬驗證器 —— 生產環境請為每個 Groth16 驗證增加約 200K):

操作
Gas
備註

mint (單個)
~320–360K
單一 tokenId

mintBatch (5 個 tokenId)
~1.4–1.6M
一筆交易 5 個 tokenId

burn (帶找零)
~350–390K
部分銷毀

transfer (1→2)
~370–410K
1 輸入, 2 輸出

transfer (2→2)
~420–460K
標準轉帳

consolidate (5→1)
~500–550K
合併 5 個 VOSA

createPolicy
~410–460K
按 tokenId

revokePolicy
~30–35K

由於承諾計算中增加了 tokenId 欄位(4 輸入 Poseidon 對比 3 輸入),電路約束數量比 ERC-20 等效項高出約 15%。

向後兼容性 (Backwards Compatibility)

本標準實現 ERC-1155 接口(如 balanceOf(address,uint256), safeTransferFrom 等)。它是一個獨立的隱私保護多代幣標準。保留了 uri() 函數以實現元數據兼容性。

安全考量 (Security Considerations)

跨代幣類型攻擊防禦

電路強制要求轉帳中的所有承諾使用相同的 tokenId。這在 ZK 證明內部進行驗證 —— 無法在不使證明失效的情況下混合代幣類型。

可信設置 (Trusted Setup)

與隱私 ERC-20 相同:Groth16 每個電路變體都需要一個可信設置儀式。

搶跑保護 (Front-Running Protection)

EIP-712 簽名綁定了 tokenId 和所有可變參數。針對 tokenId=42 的簽名無法在 tokenId=7 上重放。

雙花 (Double-Spending)

使用與隱私 ERC-20 相同的 O(1) SPENT_MARKER 機制,但以 (address, tokenId) 為索引。

數量安全

範圍證明強制執行 0 ≤ amount < 2^96。數量守恆在 ZK 電路中按 tokenId 執行。

批量鑄造原子性

mintBatch() 是原子的 —— 要麼所有 tokenId 都鑄造成功,要麼都不成功。每個 tokenId 都有自己的承諾和 ZK 證明。

參考實現 (Reference Implementation)

nft-native/
├── contracts/
│ ├── src/
│ │ ├── PrivateERC1155.sol # 主合約 (約 1160 行)
│ │ ├── libraries/
│ │ │ └── PublicInputBuilder.sol # ZK 公開輸入構造輔助工具
│ │ ├── interfaces/
│ │ │ ├── IGroth16Verifier.sol
│ │ │ └── IPoseidon.sol
│ │ └── mocks/
│ │ ├── MockVerifier.sol
│ │ └── MockPoseidon.sol
│ └── test/
│ └── PrivateERC1155.test.ts
├── circuits/
│ ├── src/
│ │ ├── commitment1155.circom # Poseidon(tokenId, amount, blinder, ts)
│ │ ├── amount1155.circom # 鑄造/銷毀電路 (0→1, 1→0, 1→1)
│ │ ├── transfer1155.circom # 轉帳電路 (N→M)
│ │ └── amount_0_1_main.circom # 鑄造電路入口點
│ └── lib/
│ ├── poseidon.circom # circomlib Poseidon
│ └── comparators.circom # circomlib 比較器 + bitify
└── circuits/scripts/
├── compile.sh # 電路編譯
└── test.sh # Witness + 證明生成測試

討論問題

  • 每次轉帳單一 tokenId:這是否可以接受?或者標準是否應該支持原子的多 tokenId 轉帳(代價是電路複雜度大幅增加)?

  • 批量操作:transferBatch()(一次調用轉帳多個 tokenId)是否應該成為標準的一部分,還是順序調用 transfer() 就足夠了?

  • tokenId 可見性:是否存在某些場景,隱藏代幣類型的重要性足以抵消電路複雜度的增加?

  • 元數據隱私:目前的 uri() 是公開的。是否應該有一種機制為每個持有者提供加密的元數據?

  • 與 ERC-1155 的互操作性:是否應該提供一個 CompatibleERC1155 變體(類似 CompatibleERC20),具備公開餘額 + 隱私模式切換功能?

版權

透過 CC0 放棄版權及相關權利。

        1 則貼文 - 1 位參與者

        [閱讀完整主題](https://ethereum-magicians.org/t/draft-erc-private-erc-1155-privacy-preserving-multi-token-standard/27894)
https://ethereum-magicians.org/t/draft-erc-private-erc-1155-privacy-preserving-multi-token-standard/27894