ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

如何用 OpenZeppelin ERC7984 与 FHEVM 构建机密通证

如何用 OpenZeppelin ERC7984 与 FHEVM 构建机密通证 如何用 OpenZeppelin ERC7984 与 FHEVM 构建机密通证【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm如果你想在链上发行一种余额和转账金额默认不可见、但仍然支持完整通证操作的代币FHEVM 的文档提供了一个直接路径基于 OpenZeppelin 的ERC7984机密通证基类结合 FHEVM 的全同态加密FHE能力构建一个转账后余额只以加密 handle 形式存在链上的通证。本文按文档给出的流程从环境准备、合约编写、测试脚本到验证方式完整走一遍这条集成路径。适用前提你使用 Hardhat 工作流并且可以访问 FHEVM-enabled 网络以及 Zama 的 gateway/relayer。准备环境按 OpenZeppelin 集成指南 的要求开始前需要Node.js 20并且使用偶数版本v18.x、v20.x这类快速上手文档 说明 Hardhat 不支持奇数版本 Node.js使用 v21.x、v23.x 等会持续报警且行为可能异常Hardhat^2.24访问 FHEVM-enabled 网络和 Zama 的 gateway/relayer 的能力基于FHEVM Hardhat template创建的项目在 指南 中有模板说明。初始化项目在指南中给出的命令序列如下在项目目录内依次执行# 1. 安装模板项目的依赖 npm ci # 2. 安装 OpenZeppelin 的机密合约库 npm i openzeppelin/confidential-contracts # 3. 编译合约 npm run compile安装完成后可以用npm test跑一遍模板自带的测试确认环境本身没有问题再开始加入你自己的合约。编写 ERC7984Example 合约在项目的contracts/目录下创建ERC7984Example.sol。合约继承三个部分ERC7984—— OpenZeppelin 提供的机密通证基类Ownable2Step—— 为铸币和管理功能提供访问控制ZamaEthereumConfig—— FHEVM 的 FHE 配置用于 Ethereum 主网或 Ethereum Sepolia 测试网该抽象合约定义在本仓库的 ZamaConfig.sol 中。文档给出的完整合约如下// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity ^0.8.27; import {Ownable2Step, Ownable} from openzeppelin/contracts/access/Ownable2Step.sol; import {FHE, externalEuint64, euint64} from fhevm/solidity/lib/FHE.sol; import {ZamaEthereumConfig} from fhevm/solidity/config/ZamaConfig.sol; import {ERC7984} from openzeppelin/confidential-contracts/token/ERC7984/ERC7984.sol; contract ERC7984Example is ZamaEthereumConfig, ERC7984, Ownable2Step { constructor( address owner, uint64 amount, string memory name_, string memory symbol_, string memory contractURI_ ) ERC7984(name_, symbol_, contractURI_) Ownable(owner) { euint64 encryptedAmount FHE.asEuint64(amount); _mint(owner, encryptedAmount); } }几个需要理解的行为构造时以明文金额完成首次 mintFHE.asEuint64(amount)把明文uint64就地加密FHE.asEuint64(uint64)定义在 FHE.sol用于建立代币的初始总量首次 mint 只在构造时发生一次之后的转账全部是加密的链上余额以 handle 形式存在文档同时提示示例使用明文初始 mint 是为了简化生产环境中可以考虑从创世起就用加密 mint、实现更复杂的 mint 计划或覆盖部分隐私假设。编写测试脚本把测试文件放进项目的test/目录。文档中该示例标签页的文件名为confToken.test.ts而文档给出的运行命令引用的是test/ERC7984Example.test.ts——文件名以你实际运行的命令为准二者保持一致即可。测试覆盖初始化、转账流程和两类应 revert 的非法操作import { expect } from chai; import { ethers, fhevm } from hardhat; describe(ERC7984Example, function () { let token: any; let owner: any; let recipient: any; let other: any; const INITIAL_AMOUNT 1000; const TRANSFER_AMOUNT 100; beforeEach(async function () { [owner, recipient, other] await ethers.getSigners(); // Deploy ERC7984Example contract token await ethers.deployContract(ERC7984Example, [ owner.address, INITIAL_AMOUNT, Confidential Token, CTKN, https://example.com/token ]); }); describe(Initialization, function () { it(should set the correct name, async function () { expect(await token.name()).to.equal(Confidential Token); }); it(should set the correct symbol, async function () { expect(await token.symbol()).to.equal(CTKN); }); it(should set the correct contract URI, async function () { expect(await token.contractURI()).to.equal(https://example.com/token); }); it(should mint initial amount to owner, async function () { // Verify that the owner has a balance (without decryption for now) const balanceHandle await token.confidentialBalanceOf(owner.address); expect(balanceHandle).to.not.be.undefined; }); }); describe(Transfer Process, function () { it(should transfer tokens from owner to recipient, async function () { // Create encrypted input for transfer amount const encryptedInput await fhevm .createEncryptedInput(await token.getAddress(), owner.address) .add64(TRANSFER_AMOUNT) .encrypt(); // Perform the transfer await expect(token .connect(owner) confidentialTransfer(address,bytes32,bytes)).to.not.be.reverted; // Check that both addresses have balance handles (without decryption for now) const recipientBalanceHandle await token.confidentialBalanceOf(recipient.address); const ownerBalanceHandle await token.confidentialBalanceOf(owner.address); expect(recipientBalanceHandle).to.not.be.undefined; expect(ownerBalanceHandle).to.not.be.undefined; }); it(should allow recipient to transfer received tokens, async function () { // First transfer from owner to recipient const encryptedInput1 await fhevm .createEncryptedInput(await token.getAddress(), owner.address) .add64(TRANSFER_AMOUNT) .encrypt(); await expect(token .connect(owner) confidentialTransfer(address,bytes32,bytes)).to.not.be.reverted; // Second transfer from recipient to other const encryptedInput2 await fhevm .createEncryptedInput(await token.getAddress(), recipient.address) .add64(50) // Transfer half of what recipient received .encrypt(); await expect(token .connect(recipient) confidentialTransfer(address,bytes32,bytes)).to.not.be.reverted; // Check that all addresses have balance handles (without decryption for now) const otherBalanceHandle await token.confidentialBalanceOf(other.address); const recipientBalanceHandle await token.confidentialBalanceOf(recipient.address); expect(otherBalanceHandle).to.not.be.undefined; expect(recipientBalanceHandle).to.not.be.undefined; }); it(should revert when trying to transfer more than balance, async function () { const excessiveAmount INITIAL_AMOUNT 100; const encryptedInput await fhevm .createEncryptedInput(await token.getAddress(), recipient.address) .add64(excessiveAmount) .encrypt(); await expect( token .connect(recipient) confidentialTransfer(address,bytes32,bytes) ).to.be.revertedWithCustomError(token, ERC7984ZeroBalance) .withArgs(recipient.address); }); it(should revert when transferring to zero address, async function () { const encryptedInput await fhevm .createEncryptedInput(await token.getAddress(), owner.address) .add64(TRANSFER_AMOUNT) .encrypt(); await expect( token .connect(owner) confidentialTransfer(address,bytes32,bytes) ).to.be.revertedWithCustomError(token, ERC7984InvalidReceiver) .withArgs(ethers.ZeroAddress); }); }); });其中两个关键 API 值得说明fhevm.createEncryptedInput(contractAddress, signerAddress)创建的加密值绑定到合约地址和用户地址只能由该用户在指定合约内使用不能在其他用户或其他合约中复用confidentialTransfer(address,bytes32,bytes)的第三个参数inputProof是对加密输入完整性与来源的验证凭证与 handle 一起传入合约。运行测试并验证结果在项目根目录执行文档给出的命令npx hardhat test test/ERC7984Example.test.ts验证方式就是测试脚本里的断言本身它们覆盖了四个层面初始化name()、symbol()、contractURI()返回部署时传入的值confidentialBalanceOf(owner)返回的余额 handle 不为空说明初始 mint 生效正常转账confidentialTransfer不 revert且转出入双方都能取到余额 handle收款方还能继续把收到的代币转出链上密态计算在正常工作超额转账期望触发自定义错误ERC7984ZeroBalance参数为转账方地址转账到零地址期望触发ERC7984InvalidReceiver参数为ethers.ZeroAddress。这四组断言都通过即说明机密通证的部署、加密输入、链上密态转账和错误路径都符合预期。如果需要真正读出余额的明文值快速上手测试文档 给出了fhevm.userDecryptEuint的用法它接收四个参数——FHE 类型FhevmType、要解密的 handle、对该 handle 有权限的合约地址、有权限的用户 signer。handle 的访问权限通过链上的FHE.allow()授予解密类型要与 handle 的实际类型一致示例中uint32计数对应FhevmType.euint32。ERC7984 的余额是euint64对应的解密类型就是FhevmType.euint64。注意 ERC7984 示例测试本身只做 handle 存在性检查没有解密余额要解密需要先确保相应地址获得授权。可选扩展加密 mint/burn 与总量可见性如果通证生命周期需要更多管理功能文档 给出了三段可直接加入合约的扩展代码。加密 mint金额对链外保密function confidentialMint( address to, externalEuint64 encryptedAmount, bytes calldata inputProof ) external onlyOwner returns (euint64 transferred) { return _mint(to, FHE.fromExternal(encryptedAmount, inputProof)); }对应的链外调用示例const enc await fhevm .createEncryptedInput(await token.getAddress(), owner.address) .add64(1_000) .encrypt(); await token.confidentialMint(recipient.address, enc.handles[0], enc.inputProof);文档提醒encryptedAmount和inputProof必须由 SDK 在链外生成并对畸形输入做校验和 revert机密操作 gas 成本更高批量 mint 应克制、优先用少量大额 mint。加密 burnfunction confidentialBurn( address from, externalEuint64 encryptedAmount, bytes calldata inputProof ) external onlyOwner returns (euint64 transferred) { return _burn(from, FHE.fromExternal(encryptedAmount, inputProof)); }文档同时给出明文版本的mint和burn_mint(to, FHE.asEuint64(amount))/_burn(from, FHE.asEuint64(amount))并说明明文 mint 的金额会出现在 calldata 和事件中适合需要透明的公开铸造场景从任意账户 burn 的权限很大文档建议配合角色、多签或用户授权来控制。总量可见性允许 owner 解密最新总量的做法是覆写_updatefunction _update(address from, address to, euint64 amount) internal virtual override returns (euint64 transferred) { transferred super._update(from, to, amount); FHE.allow(confidentialTotalSupply(), owner()); }这样 owner 可以在每次状态变更后调用confidentialTotalSupply()并用其链外密钥材料解密返回的 handle。文档的提醒Ownable2Step会自动授权当前owner()所有权变更后只有新 owner 可解密授予总量可见性属于特权访问应记录密钥持有者及原因。限制与继续深入本例的初始 mint 是明文的链上可见若要求从首次铸造起就保密文档建议改用confidentialMint这类加密 mint 路径ZamaEthereumConfig面向 Ethereum 主网和 Sepolia 测试网其他链的配置不在此文档范围内围绕同一套库的更多集成示例ERC-20 包装成 ERC-7984、反向 unwrap、机密 vesting见 OpenZeppelin 集成目录以及 erc7984 教程原文。【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表