欢迎光临
我们一直在努力

Solidity 隐私代币揭秘:自动回调与操作员机制详解

目录

  • 引言
  • 一、Operator
      • 代码解释
  • 二、Callback
      • 发送方合约(Token 合约)
      • 接收方合约(Receiver)
      • 代码解释
  • 三、Swaps
    • (一)Swap ERC20 to ConfidentialFungibleToken
    • 代码解释
      • 1、函数签名及参数
      • 2、扣除用户明文代币
      • 3、铸造对应加密代币
    • (二)Swap ConfidentialFungibleToken to ConfidentialFungibleToken
      • 代码解释
    • (三)Swap ConfidentialFungibleToken to ERC20
      • 代码解释
  • 总结

引言

在以太坊智能合约的开发中,传统的代币转账方式通常依赖于用户直接调用 transfer 或 transferFrom 方法。然而,这种方式在某些场景下可能不够灵活,尤其是当需要第三方代表用户执行代币操作时。为了解决这一问题,引入了“操作员(Operator)”机制,使得用户可以授权第三方在特定条件下代表自己进行代币操作。

在 OpenZeppelin 的 Confidential Fungible Token(CFT)实现中,除了传统的转账方式,还引入了带回调(callback)的转账函数 confidentialTransferWithCallback。这种方式允许在代币转账完成后,自动触发接收方合约的特定函数,从而实现更复杂的业务逻辑。

本文将详细介绍操作员机制的实现方式,以及带回调的转账函数如何在发送方和接收方合约中协同工作,实现代币的安全转移和自动处理。

一、Operator

在标准的 ERC-20 代币合约中,用户只能直接调用 transfer 和 transferFrom 方法来转移代币。然而,在某些场景下,我们希望允许第三方代表用户进行代币转移操作,这时就引入了操作员机制。操作员是被授权代表用户执行特定操作的地址,通常具有一定的权限和有效期限。

const alice: Wallet;
const expirationTimestamp = Math.round(Date.now() / 1000) + 60 * 60 * 24; // 当前时间 + 24 小时

await tokenContract.connect(alice).setOperator(bob, expirationTimestamp);

代码解释

创建一个钱包实例 alice,用于签名交易。 计算当前时间加上 24 小时后的时间戳,作为操作权限的过期时间。 通过 alice 钱包连接到代币合约,并调用 setOperator 方法,将 bob 设置为操作员,权限在指定的时间戳后过期。

二、Callback

回调就是转账完成后,“自动触发”对接收方合约的特定函数进行调用。 如果你选择使用带回调的转账函数,就不需要再手动调用中间授权(如“操作员审批”)步骤,系统会自动处理。

被回调函数调用的智能合约必须实现 IConfidentialFungibleTokenReceiver 接口。

发送方合约(Token 合约)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IConfidentialFungibleTokenReceiver} from "@openzeppelin/confidential-contracts/interfaces/IConfidentialFungibleTokenReceiver.sol";
import {ConfidentialFungibleToken} from "@openzeppelin/confidential-contracts/token/ConfidentialFungibleToken.sol";

contract ConfidentialToken is ConfidentialFungibleToken {
constructor() ConfidentialFungibleToken("ConfidentialToken", "CFT") {}

function confidentialTransferWithCallback(
address recipient,
externalEuint64 encryptedAmount,
bytes calldata inputProof,
bytes calldata callbackData
) external {
confidentialTransferFrom(msg.sender, address(this), encryptedAmount, inputProof);

bool success = IConfidentialFungibleTokenReceiver(recipient)
.onConfidentialTransferReceived(
msg.sender,
encryptedAmount,
callbackData
);

require(success, "Callback failed");
}
}

接收方合约(Receiver)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IConfidentialFungibleTokenReceiver} from "@openzeppelin/confidential-contracts/interfaces/IConfidentialFungibleTokenReceiver.sol";

contract Receiver is IConfidentialFungibleTokenReceiver {
mapping(address => uint256) public balances;

function onConfidentialTransferReceived(
address from,
bytes calldata ciphertext,
bytes calldata data
) external override returns (ebool) {
uint256 decryptedAmount = 100; // 假设解密后的金额为 100
balances[from] += decryptedAmount;
return true;
}
}

代码解释

发送方调用 confidentialTransferWithCallback:将加密金额转账给接收方合约。

Token 合约自动调用回调函数:在转账完成后,自动调用接收方合约的 onConfidentialTransferReceived 方法。

接收方合约处理回调:接收方合约在 onConfidentialTransferReceived 方法中进行加密金额的解密和验证。如果验证通过,更新接收方的余额并返回 true;否则,返回 false 或抛出异常,导致转账失败。

三、Swaps

(一)Swap ERC20 to ConfidentialFungibleToken

下面这段 Solidity 代码展示了一个将普通 ERC20 代币 **“包裹”(wrap)**成保密形式加密代币的过程。它主要做两件事:扣除用户的明文代币,然后 mint(铸造)等量的加密代币。

ERC20 代币仅需直接转入,若转入失败则交易自动回滚。随后,我们通过内部的 _mint 函数转出正确数量的 ConfidentialFungibleToken,该函数保证转出操作必成功。

function wrap(address to, uint256 amount) public virtual {
// take ownership of the tokens
SafeERC20.safeTransferFrom(underlying(), msg.sender, address(this), amount (amount % rate()));

// mint confidential token
_mint(to, (amount / rate()).toUint64().asEuint64());
}

代码解释

1、函数签名及参数

wrap(address to, uint256 amount):

to 是接收新铸造的加密代币的地址;

amount 是用户想包裹的明文 ERC20 代币数量。

2、扣除用户明文代币

underlying() 返回被包裹的原始 ERC20 代币合约地址;

safeTransferFrom 从调用者 msg.sender 扣除 (amount – (amount % rate())) 数量的明文代币,转入当前合约;

amount % rate() 残余部分被抹去,确保只扣整倍的代币;

rate() 是一个兑换比例,比如 100:1 ——意味着你需要 100 个明文资产对应 1 个加密代币。

3、铸造对应加密代币

计算铸造数量:amount / rate(),得到你能获取多少单位的加密代币;

toUint64():确保铸造数量不超过 uint64 的最大值;

.asEuint64():将普通整数转换为加密整数类型 euint64;

_mint(to, …) 将等量的加密代币(保密状态)铸造/记账给 to 地址。

(二)Swap ConfidentialFungibleToken to ConfidentialFungibleToken

Solidity 代码实现了一个基于 Zama FHE 协议的加密代币交换功能,允许用户在链上进行隐私保护的代币交换。在 Zama 的 FHE 协议中,所有数据都是加密的,只有授权的合约才能解密和操作这些数据。

function swapConfidentialForConfidential(
IConfidentialFungibleToken fromToken,
IConfidentialFungibleToken toToken,
externalEuint64 amountInput,
bytes calldata inputProof
) public virtual {
require(fromToken.isOperator(msg.sender, address(this)));

euint64 amount = FHE.fromExternal(amountInput, inputProof);

FHE.allowTransient(amount, address(fromToken));
euint64 amountTransferred = fromToken.confidentialTransferFrom(msg.sender, address(this), amount);

FHE.allowTransient(amountTransferred, address(toToken));
toToken.confidentialTransfer(msg.sender, amountTransferred);
}

代码解释

①require(fromToken.isOperator(msg.sender, address(this))); 作用:验证调用者(msg.sender)是否被授权为 fromToken 的操作员。

背景:在 Zama 的 FHE 协议中,只有被授权的操作员才能执行加密代币的转账操作。

② euint64 amount = FHE.fromExternal(amountInput, inputProof); 作用:将外部传入的加密数据(amountInput)和证明(inputProof)转换为内部可操作的加密整数类型 euint64。

背景:euint64 是 Zama FHE 协议中用于表示加密整数的类型。

③ FHE.allowTransient(amount, address(fromToken)); 作用:允许对 fromToken 合约地址的临时访问权限,以便执行加密代币的转账操作。

背景:在 FHE 协议中,操作加密数据需要临时授权。

④euint64 amountTransferred = fromToken.confidentialTransferFrom(msg.sender, address(this), amount); 作用:从调用者(msg.sender)向当前合约(address(this))转移指定数量的加密代币。

背景:confidentialTransferFrom 是 Zama FHE 协议中用于执行加密代币转账的函数。

⑤ FHE.allowTransient(amountTransferred, address(toToken)); 作用:允许对 toToken 合约地址的临时访问权限,以便将转账的加密代币发送给目标地址。

背景:在 FHE 协议中,操作加密数据需要临时授权。

⑥ toToken.confidentialTransfer(msg.sender, amountTransferred); 作用:将转账的加密代币发送到目标地址(msg.sender)。

背景:confidentialTransfer 是 Zama FHE 协议中用于执行加密代币转账的函数。

(三)Swap ConfidentialFungibleToken to ERC20

从保密代币切换到非保密代币是最复杂的操作,因为需要访问解密后的数据才能准确完成请求。在我们的示例中,解密操作将在链下进行,并通过 Zama 的网关将结果回传。以下是一个合约示例,演示如何将保密代币与 ERC20 代币进行 1:1 兑换。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

// 导入 Zama FHE 库,用于处理加密整数类型
import {FHE, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
// 导入标准 ERC20 接口
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
// 导入 SafeERC20,用于安全转账
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// 导入保密代币接口
import {IConfidentialFungibleToken} from "@openzeppelin/confidential-contracts/interfaces/IConfidentialFungibleToken.sol";

contract SwapConfidentialToERC20 {
using FHE for *;

// 自定义错误:无效的解密回调请求 ID
error SwapConfidentialToERC20InvalidGatewayRequest(uint256 requestId);

// 映射:存储每个请求 ID 对应的接收者地址
mapping(uint256 => address) private _receivers;

// 保密代币合约(输入端)
IConfidentialFungibleToken private _fromToken;
// 普通 ERC-20 代币合约(输出端)
IERC20 private _toToken;

// 构造函数:初始化输入和输出代币合约地址
constructor(IConfidentialFungibleToken fromToken, IERC20 toToken) {
_fromToken = fromToken;
_toToken = toToken;
}

/**
* @notice 用户调用此函数发起 swap,将加密代币提交;
* @param encryptedInput 加密金额,externalEuint64 类型
* @param inputProof 加密金额的零知识证明
*/

function swapConfidentialToERC20(externalEuint64 encryptedInput, bytes memory inputProof) public {
// 1. 使用 proof 将加密输入转为链上可用的加密整数
euint64 amount = encryptedInput.fromExternal(inputProof);

// 2. 授权保密代币合约读取 this 合约中的这笔加密金额
amount.allowTransient(address(_fromToken));

// 3. 从用户 transfer 保密代币到本合约
euint64 amountTransferred = _fromToken.confidentialTransferFrom(msg.sender, address(this), amount);

// 4. 将 ciphertext 添加到解密请求列表
bytes32 ;
cts[0] = euint64.unwrap(amountTransferred);

// 5. 发出解密请求,并指定回调函数 finalizeSwap
uint256 requestID = FHE.requestDecryption(cts, this.finalizeSwap.selector);

// 6. 保存 requestID ↔ 用户地址 关联,等待回调发币
_receivers[requestID] = msg.sender;
}

/**
* @notice 解密服务调用此函数,将加密金额转成明文后,合约会发放同等 ERC-20 代币
* @param requestID 与 swap 请求对应的唯一 ID
* @param amount 解密后拿到的金额(uint64 明文)
* @param signatures 多重签名验证解密合法性
*/

function finalizeSwap(uint256 requestID, uint64 amount, bytes[] memory signatures) public virtual {
// 1. 验证解密签名是否合法
FHE.checkSignatures(requestID, signatures);

// 2. 获取之前保存的接收方
address to = _receivers[requestID];
if (to == address(0)) revert SwapConfidentialToERC20InvalidGatewayRequest(requestID);

// 3. 清除记录避免重复提现
delete _receivers[requestID];

// 4. 如果解密金额大于 0,将 ERC-20 转给接收者
if (amount != 0) {
SafeERC20.safeTransfer(_toToken, to, amount);
}
}
}

代码解释

注释写在代码上啦。这段 Solidity 合约 SwapConfidentialToERC20 用于将保密代币(encrypted token)兑换成普通 ERC‑20 代币,核心思路是:用户先将加密金额转移到合约,触发链下解密过程,最后由合约释放等值的 ERC‑20。它做了三件事:接收保密代币、提交解密请求、解密成功后,释放相应 ERC‑20 代币。

总结

通过引入操作员机制和带回调的转账函数,智能合约的代币操作变得更加灵活和安全。

操作员机制:用户可以通过 setOperator 方法授权第三方在指定时间内代表自己执行代币操作。这种方式避免了频繁的授权操作,提高了用户体验。

带回调的转账函数:发送方合约在执行代币转账后,自动调用接收方合约的 onConfidentialTransferReceived 方法,实现自动处理。这种方式简化了授权和转账的流程,提高了系统的自动化程度。

通过上述机制的结合,智能合约不仅实现了代币的安全转移,还能够在转账完成后自动触发特定的业务逻辑,为去中心化应用(DApp)提供了更强大的功能支持。

赞(0)
未经允许不得转载:171主机测评 » Solidity 隐私代币揭秘:自动回调与操作员机制详解
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址