# pERC20:私有代币标准(草案)

本文为机器翻译
展示原文

作者: Cyimon ( @Cyimon ) ·状态:草案 ·类型:标准跟踪 (ERC) · EIP: 8287 (PR) ·创建日期: 2026-06-09

描述: EVM 的可互换代币标准,默认情况下是私有的。

讨论: ethresear.ch #25089 · Ethereum Magicians #28702 ·实现: PERC20Labs/pERC20_


我们扩展了之前发布的 pERC20 协议设计( Ethereum Magicians #28702 / ethresear.ch #25089 )。此次修订的主要新增功能是通过ZIP-32子账户实现 ERC-20 认可的支出——包括approveallowancetransferFrom 。更新后的标准在功能上ERC-20完全兼容,但在字节上不兼容(ABI 不同,且不支持公开余额)。

ERC-20 pERC20
name / symbol / decimals / totalSupply是的——公众观点相同链上
balanceOf是的——仅限持卡人扫描链下
transfer是的——私人聚会及金额链上
approve / allowance / transferFrom新增——已批准EOA支出者通过ZIP-32子账户进行支出;链上= transfer (不支持合约支出者)链下
mint / burn是的——常用扩展链上
Transfer / Approval事件省略(隐私)

以下是最新的 pERC20 标准:私有代币标准


抽象的

pERC20是 EVM 的默认私有同质化代币标准,是ERC-20的隐私版本。其底层采用Zcash协议规范中的 Orchard 屏蔽池模型。它保留了 ERC-20 的所有方法,但部分方法是私有的(仅限持有者访问,链下),而非公开的链上读取,并且transfer / approve / transferFrom在链上显示为相同的transfer操作。请参阅下方的pERC20 接口

动机

以太坊的公共账本使所有ERC-20余额、转账和授权永久可见。随着支付、工资、国库和链上金融迁移到 L1 层,用户和发行方需要的是私有的同质化代币,而不仅仅是围绕公共余额的私密信息交流。

隐私问题越来越多地在协议层得到解决。例如, EIP-8182定义了一个协议层面的保护池:用户可以存入公开的ETH或兼容的 ERC-20 代币,在共享池内进行私密转移,然后再提取回公开形式。该模型保护了现有的公共资产;但它并未定义如何发行从创建之初就具有私密性的代币

pERC20填补了后一个空白。它是一个应用层代币标准,用于原生私有同质化代币:从一开始就以私有票据的形式通过approve / transferFrom进行铸造、持有、转移和使用,没有公开的balanceOf阶段,也不存入共享的屏蔽池。它定义了ERC-20的私有对应物——相同的方法界面,不同的开放程度——因此发行方可以立即发行私有资产,同时协议级隐私(例如 EIP-8182)也在同步发展。两者是互补的,而非竞争的:EIP-8182 将公共资产私有化; pERC20定义了私有资产的发行。

规格

关键词MUSTMUST NOTSHOULDMAY的解释遵循 RFC 2119。Solidity 语法版本为0.8.20或更高版本。

底层协议

价值以屏蔽票据的形式存在,而非账户余额。票据格式、作废符、承诺树、票据加密以及操作/捆绑结构均遵循Zcash协议规范中的Orchard 屏蔽池,并在此处改编为基于资产的 EVM 合约,且已通过 Groth16 验证。下文未重复的字段级格式在参考实现部分中具有规范性。

pERC20接口

本节将所有 pERC20 接口集中列出,并标记每个接口是否对应于ERC-20标准接口。ERC -20yes = ERC-20 标准; extension = 通用扩展(铸造/销毁); no = pERC20 特有。层级on-chain = 合约 ABI; off-chain = 钱包/SDK(无合约方法)。

pERC20接口ERC-20开放性描述
name() / symbol() / decimals()是的链上民众与 ERC-20 相同
totalSupply()是的链上民众公共计数器( mint - burn
balanceOf(addr)是的链下私人的使用查看密钥扫描Orchard纸币;仅限持卡人使用
transfer(PrivacyCall)是的链上私人(参与方 + 金额)果园行动包
approve(spender, N)是的链下私密(关系已隐藏) ZIP-32子账户;资金 + 交付密钥;链上提交为transfer(PrivacyCall)
allowance(owner, spender)是的链下私人的扫描子账户剩余余额
transferFrom(from, to, amount)是的链下私密(关系已隐藏)支出者在子账户中支出;链上提交为transfer(PrivacyCall)
mint(amount, PrivacyCall)扩大链上金额公开;收款人私密仅限发行方;果园行动 + totalSupply量增加
burn(amount, PrivacyCall)扩大链上公开金额;私人燃烧器持票人烧毁自己的票据;果园行动 + totalSupply减少
issuer()链上民众代币发行者地址
cmxFrozenRoot() / setFrozenRoot()链上公共根目录;管理员写入合规冻结记录根
cmxRoot() / isValidAnchor() / isSpent() / treeSize()链上民众果园承诺树状态

活动

pERC20事件ERC-20描述
Transfer(from, to, value)是的链下(省略)未公开;参与方和金额均属私人信息。
Approval(owner, spender, value)是的链下(省略)未发行;将所有者与消费者关联
NoteAdded / NoteConfirmed取代Transfer链上单张票据可观察性
Mint / Burn扩大链上公开金额
Perc20Created / FrozenRootUpdated / BundleExecuted链上部署、合规性、捆绑元数据

链上操作不可区分。 transferapprovetransferFrom和撤销操作的资金步骤在链上都是同一个调用: transfer(PrivacyCall) 。观察者无法分辨正在执行的是哪个 ERC-20 操作。

原生不支持。ERC -20 的 ` approve(contractAddress, amount) `(合约自主调用transferFrom )没有原生等效项:支出需要私钥,而合约无法持有私钥。参见原理。

合同接口

pERC20公开了一个链上 ABI( IPERC20 ;参见上文的pERC20 接口)。该表中标记为链下的方法没有合约入口点;其行为在下文“方法语义”中指定。

interface IPERC20 {struct PrivacyCall { bytes actions; uint256[3] bindingSig; }struct BundleAction {bytes32 cmx;bytes encCiphertext;bytes outCiphertext;bytes32 epk;bytes32 nfOld; // nullifier of the consumed (or dummy) input notebytes32 anchor; // historical root of the consumed (or dummy) input notebytes proof;uint256[8] pubFields;uint256[3] spendAuthSig;}// ERC-20-aligned public viewsfunction name() external view returns (string memory);function symbol() external view returns (string memory);function decimals() external view returns (uint8);function totalSupply() external view returns (uint256);function issuer() external view returns (address);// Value-changing operations (private parties; see Method Semantics)function transfer(PrivacyCall calldata call) external returns (bool success);function mint(uint256 amount, PrivacyCall calldata call) external;function burn(uint256 amount, PrivacyCall calldata call) external;// Compliancefunction cmxFrozenRoot() external view returns (uint256);function setFrozenRoot(uint256 newRoot) external; // onlyAdmin// Note state machine (Orchard commitment tree)function cmxRoot() external view returns (bytes32);function isValidAnchor(bytes32 root) external view returns (bool);function isSpent(bytes32 nf) external view returns (bool);function treeSize() external view returns (uint256);event Mint(address indexed issuer, uint256 amount);event Burn(uint256 amount);event FrozenRootUpdated(uint256 oldRoot, uint256 newRoot);event Perc20Created(address indexed pool, address indexed issuer,string name, string symbol, uint8 decimals);event NoteAdded(bytes32 indexed cmx, bytes encCiphertext, bytes outCiphertext,bytes32 epk, bytes32 nfOld, bytes32 cvNetX);event NoteConfirmed(bytes32 indexed cmx, bytes32 newRoot, uint256 position);event BundleExecuted(uint256 valueBalance, uint256 amount, bytes32 recipientMeta);}

符合性:

  • transfer成功必须返回true
  • 核心包执行路径绝对不能公开调用(供应不变式;见下文)。
  • 实现可以将 Solidity 拆分为多个合约(例如IPERC20 + 验证器基础),但可观察的 ABI 和事件必须与上述统一接口匹配。
  • cmxRoot()是最新的承诺树根; isValidAnchor(root)如果root曾经是活跃的则返回 true; isSpent(nf)公开空值集; treeSize()是插入的承诺数。

呼叫格式

每次更改值的操作都会提交一个PrivacyCall编码一个或多个Orchard 操作Zcash协议规范):

  • actions = abi.encode(BundleAction[]) .
  • bindingSig = Schnorr 绑定签名[Rx, Ry, s]证明值守恒。

每个BundleAction都是pubFields适用于 EVM 验证的 Orchard 操作:一份输出票据承诺 ( cmx ) 以及其(真实或虚拟)输入的证明材料。pubFields 必须按以下顺序排列(Orchard 操作的主要输入):

指数场地角色
[0] anchor消耗输入的默克尔根
[1] cv_net_x净值承诺 X(具有约束力的签名)
[2] cv_net_y净值承诺 Y(具有约束力的签名)
[3] nf_old消耗输入的无效化
[4] rk_x随机消费授权密钥 X
[5] rk_y随机消费授权密钥 Y
[6] cmx输出说明承诺
[7] rt_frozen合规性冻结根绑定

每个pubFields[i]必须< Fr ,其中Fr是验证器(参考实现中为 BN254)使用的 SNARK 曲线的标量场模;否则,将返回错误( PubFieldOutOfRange )。实现必须通过ActionPubHash (Poseidon sponge)将这八个字段哈希到一个 Groth16 公共信号中,使其与电路的PubHashAction()相匹配。

绑定到调用数据字段。证明公共输入必须与操作的顶级字段匹配;如果出现以下任何情况,实现必须回滚:

查看恢复
pubFields[0] == anchorisValidAnchor(anchor) BadAnchor
pubFields[3] == nfOld必须还原(参考实现: NullifierSpent
pubFields[6] == cmxcmx != 0 InvalidProof / ZeroCommitment
pubFields[7] == cmxFrozenRoot() BadFrozenRoot
spendAuthSig验证pubFields[4]pubFields[5]是否在(nfOld, cmx, epk, encCiphertext, outCiphertext)范围内。 BadSpendAuthSig

如果没有pubFields ↔ calldata 相等性检查,有效的证明可以用不同的nfOldcmx重放,绕过空值集或插入未经证明的承诺。

encCiphertext必须为 580 字节(Orchard 笔记outCiphertext + 收件人密钥下的 AEAD 标签)。outCiphertext 应为 80 字节(OVK 下的发送方自恢复)。更改笔记加密布局的实现必须发布此 ERC 的单独变体。

在进行任何状态变更之前,必须对所有操作无效符、承诺和操作的valueBalance验证捆绑包级别的bindingSig (有关编码,请参阅方法语义)。

注意加密和密钥派生遵循Zcash协议规范中的 Orchard 笔记格式;确切的编码在参考实现库中。

方法语义

name / symbol / decimals / totalSupply

与 ERC-20 相同:公开的链上视图。

transfer(PrivacyCall) → bool

使用Orchard票据作为输入,并创建一个价值守恒的操作包( valueBalance == 0 )。发送者、接收者和金额必须保持私密。成功时返回true ;发出NoteAdded / NoteConfirmed ,而不是Transfer(from,to,value)

mint(amount, PrivacyCall) / burn(amount, PrivacyCall)

transfer相同的Orchard操作验证路径,并公开totalSupply统计:

  • minttotalSupply += amountonlyIssuer ); amount公开,接收方私有。铸币必须使用与transfer相同的锚点/无效符/支出授权路径(无仅输出分支)。电路必须将消耗的输入限制为v = 0 ,以便该操作代表净流入;合约不直接读取票据价值。
  • burntotalSupply -= amount ;任何持有人均可销毁自己的票据; amount公开,销毁者私密。
手术valueBalance编码
transfer 0
burn bit255 = 0,低位 = 金额
mint bit255 = 1,低位 = 金额

balanceOf (链下,私有)

只有持有者才能通过扫描NoteAdded事件、使用查看密钥试解密Orchard笔记并排除已花费的无效符来计算余额。链上没有balanceOf ,也无法查询第三方的余额。

approve / allowance / transferFrom (链下语义;链上 = transfer

已批准的支出( approve / allowance / transferFrom )基于ZIP-32分层账户:每个EOA 支出者都会获得一个专用的子账户( account_S ),该子账户拥有自己的支出密钥和查看密钥,并与所有者的主账户以及所有其他支出者进行加密隔离。ZIP -32定义了密钥派生;此 ERC 将 ERC-20 的approve / transferFrom映射到 ZIP-32,具体如下:

  1. approve(spender, N) — 所有者创建一个未使用的 ZIP-32 子账户,通过transfer(PrivacyCall)向其充值N ,并将该子账户的支出密钥交付给 EOA 支出者(链下加密)。链上操作:一次transfer
  2. allowance(owner, spender) — 该子账户的剩余余额,使用子账户查看密钥扫描。无链上映射。
  3. transferFrom(owner, to, amount) — 支出者从子账户支出to目标账户;找零返回到子账户。链上:一次transfer
  4. approve(spender, 0) / revoke — 所有者通过transfer将子账户退回。

限额由子账户的实际票据余额决定,而非链上计数器。钱包区分“自有”资产和“限额”资产的方式,是依据解密票据的查看密钥,而非链上标记。

执行要求

链上状态机遵循 Orchard 屏蔽池( Zcash协议规范)。实现必须:

  • 维护一个无效化符集;同一个无效nf不能使用两次。
  • 维护一个仅追加的承诺树;可通过isValidAnchor查询历史根节点。
  • 验证 Groth16 证明、支出认证和绑定签名,以及所有 pubFields 绑定检查(不仅仅是pubFields[7] )。
  • 拒绝重复或零承诺;拒绝空操作数组;限制每次调用的操作数( maxActions ,一个有限的可配置正界限)。
  • 仅通过mint / burn / transfer公开价值变更(没有公共捆绑包入口点)。
  • 在部署时发出Perc20Created一次(建议进行工厂部署,但并非必须)。

合规性。cmxFrozenRoot cmxFrozenRoot()是链下黑名单 SMT 的根;该电路用于证明已花费的笔记不属于该黑名单setFrozenRoot仅限admin使用;初始根0表示黑名单为空。实现可以在更新后的短暂宽限期内接受紧邻的前一个根,以避免正在进行的证明被搁置。

理由

  • Orchard ZK-UTXO 模型。注释、无效化符和承诺树遵循Zcash协议规范;此 ERC 定义了私有代币接口和在 EVM 上的每个资产的部署。
  • 私有 ERC-20 代币,并非不同的资产。方法表面相同;隐私改变的是开放性(公开查看 vs 私密查询 vs 不可区分的传输),而不是用户意图。
  • 所有资金转移只需一次链上操作。合并transfer / approve / transferFrom会移除 ERC-20 不可避免的授权元数据泄露。
  • 通过ZIP-32子账户批准支出。每个 EOA 支出者都会获得一个独立的层级账户,而不是链上allowance映射;参见方法语义。
  • 无需approve(contract)合约没有私钥;将支出密钥放在链上会使其暴露给所有人。可编程的私有支出(谓词授权的票据、MPC托管)是未来的工作,不属于本次ERC的范围。
  • 钱包行为不在链上ABI的范围内。子账户布局、加密密钥交付和票据扫描属于钱包/SDK的职责范围;请参阅参考实现。

向后兼容性

pERC20具备ERC-20 的所有功能,但与字节不兼容:没有公开的balanceOf属性,没有链上allowance ,没有approve / transferFrom ABI,也没有Transfer / Approval事件。现有的 ERC-20 索引器和可组合合约在没有隐私保护钱包/SDK 的情况下无法驱动 pERC20。

可选的bridgeOut到公共 ERC-20 孪生服务器可能会在出口处终止隐私;本提案不要求这样做。

测试用例

参考实现库包含:

  • 铸造单元测试( test/PERC20Test.t.sol ):构造函数保护、铸造/销毁/转移会计、供应不变量。
  • 端到端测试( test/PERC20E2E.t.sole2e/ ):针对已部署的PERC20的真实 Groth16 证明,涵盖铸造、转移、销毁和approve / transferFrom流程。

参考实现

代码

参考实现: PERC20Labs/pERC20_

  • 规范性资产合约: contracts/ptoken/PERC20.solIPERC20 )。
  • 加密和钱包格式(密钥派生、 perc1地址、笔记加密、无效化器、 approve打包):参考库和 SDK 在同一个存储库中。

相关标准和协议

  • EIP-20 :代币标准pERC20映射到的公共可互换代币接口。
  • Zcash协议规范:Orchard 屏蔽池 — 注意承诺、无效化器、笔记加密和操作结构,此处针对 EVM Groth16 验证进行了调整。
  • ZIP-32 :受保护的分层确定性钱包——用于在approve / transferFrom中按消费者子账户的分层账户派生。

安全考量

  • 双花保护:空值设置 + 正确的nf推导。每个pubFields[i]必须< Fr (否则nf + Fr会重用具有不同isSpent键的证明); pubFields[0][3][6]必须分别等于anchornfOldcmx (否则有效的证明可以绑定到不同的 calldata)。
  • 供应不变性:价值仅通过mint / burn / transfer改变;核心执行路径不得公开调用。
  • 价值守恒:在状态突变之前验证结合特征。
  • 重放保护:sighash 绑定chainId 、合约地址和所有nf / cmx
  • 子账户支出密钥:用于approve密钥必须仅以密文形式出现;绝不能存储在合约中。
  • 合规权限setFrozenRoot是一个高信任度的管理员角色;应该使用多重签名/时间锁。

隐私考量

  • 操作应该通过中继器提交,以隐藏提交者的EOA。
  • mint / burn amounttotalSupply是公开的;转账金额和approve关系是私密的。
  • approve / transferFrom在链上与transfer无法区分(相同的transfer(PrivacyCall)选择器); mint / burn是具有公开数量的独立函数。
  • 试解密是收款的信任边界:仅凭NoteAdded事件无法证明付款;电路不会验证encCiphertext是否与cmx匹配。
  • setFrozenRoot允许管理员通过链下黑名单冻结已识别的笔记;这是对完全不信任的一种明确的合规性权衡。

版权

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

请引用此文档:Cyimon,“ERC-8287:私有代币标准”,以太坊改进提案,2026 年 6 月。


来源
免责声明:以上内容仅为作者观点,不代表Followin的任何立场,不构成与Followin相关的任何投资建议。
喜欢
收藏
评论