跳转到内容

机密资产(CA)

@aptos-labs/confidential-asset机密资产(CA) 协议的高级 TypeScript 客户端。它会自动处理证明生成、WASM 初始化、余额解密和交易构建,让开发者无需直接操作密码学原语。

Terminal window
npm i @aptos-labs/ts-sdk @aptos-labs/confidential-asset

该包依赖 @aptos-labs/confidential-asset-bindings 执行密码学操作(Bulletproof 范围证明和离散对数解密)。WASM 二进制文件按需加载:

  • 浏览器:自动从 unpkg CDN 获取。
  • Node.js:从本地 node_modules 加载,无需网络。
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
import {
ConfidentialAsset,
TwistedEd25519PrivateKey,
initializeWasm,
isWasmInitialized,
} from "@aptos-labs/confidential-asset";
const config = new AptosConfig({ network: Network.TESTNET });
const aptos = new Aptos(config);
// CA 模块部署在 0x1(aptos_framework)。
const ca = new ConfidentialAsset({ config });
// 在用户首次交互前预加载 WASM,避免首次证明生成时的延迟。
await initializeWasm();
console.log("WASM 就绪:", isWasmInitialized());

ConfidentialAsset 构造参数:

参数类型默认值说明
configAptosConfig必填网络配置
confidentialAssetModuleAddressstring"0x1"模块地址
withFeePayerbooleanfalse是否默认使用代付 Gas 交易

CA 操作使用一套独立于 Aptos 账户签名密钥的密钥对:

  • TwistedEd25519PrivateKey(解密密钥,DK):由用户私密保管,永不离开客户端。
  • TwistedEd25519PublicKey(加密密钥,EK):存储在链上,供他人为该用户加密金额。
import { TwistedEd25519PrivateKey } from "@aptos-labs/confidential-asset";
// 生成新的解密密钥
const dk = TwistedEd25519PrivateKey.generate();
// 派生对应的加密密钥(公开)
const ek = dk.publicKey();
// 序列化以便安全存储
const dkHex = dk.toString();
// 从已保存的十六进制字符串恢复
const restoredDk = new TwistedEd25519PrivateKey(dkHex);
import { Aptos, AptosConfig, Network, Account } from "@aptos-labs/ts-sdk";
import { ConfidentialAsset, TwistedEd25519PrivateKey } from "@aptos-labs/confidential-asset";
const aptos = new Aptos(new AptosConfig({ network: Network.TESTNET }));
const ca = new ConfidentialAsset({ config: aptos.config });
// APT 同质化资产 metadata 地址
const TOKEN = "0xa";
const alice = Account.generate();
const bob = Account.generate();
// 请先通过测试网水龙头为账户注资...
// 1. 生成解密密钥并注册机密余额
const aliceDk = TwistedEd25519PrivateKey.generate();
const bobDk = TwistedEd25519PrivateKey.generate();
await ca.registerBalance({ signer: alice, tokenAddress: TOKEN, decryptionKey: aliceDk });
await ca.registerBalance({ signer: bob, tokenAddress: TOKEN, decryptionKey: bobDk });
// 2. 将 Alice 公开余额中的 1000 个代币存入机密待处理余额
await ca.deposit({ signer: alice, tokenAddress: TOKEN, amount: 1000n });
// 3. 将待处理余额 rollover 为可用余额
await ca.rolloverPendingBalance({
signer: alice,
tokenAddress: TOKEN,
senderDecryptionKey: aliceDk,
});
// 4. 查询 Alice 的解密后余额
const balance = await ca.getBalance({
accountAddress: alice.accountAddress,
tokenAddress: TOKEN,
decryptionKey: aliceDk,
});
console.log("可用余额:", balance.availableBalance()); // bigint
console.log("待处理余额:", balance.pendingBalance()); // bigint
// 5. 向 Bob 发起 300 代币的机密转账(金额在链上隐藏)
await ca.transfer({
signer: alice,
tokenAddress: TOKEN,
recipient: bob.accountAddress,
amount: 300n,
senderDecryptionKey: aliceDk,
});
// 6. 将 200 代币提回 Alice 的公开余额
await ca.withdraw({
signer: alice,
tokenAddress: TOKEN,
senderDecryptionKey: aliceDk,
amount: 200n,
});

(signer, token) 创建机密存储,并将 EK 发布到链上。

await ca.registerBalance({
signer: alice,
tokenAddress: TOKEN,
decryptionKey: aliceDk,
});
// 注册前建议先检查
const registered = await ca.hasUserRegistered({
accountAddress: alice.accountAddress,
tokenAddress: TOKEN,
});

将代币从 signer 的公开 FA 余额转入 pending_balance。存入金额在链上可见,无需 ZKP。

await ca.deposit({
signer: alice,
tokenAddress: TOKEN,
amount: 1000n,
recipient: alice.accountAddress, // 可选,默认为 signer
});

从链上获取加密余额,并用 DK 在本地解密。

const balance = await ca.getBalance({
accountAddress: alice.accountAddress,
tokenAddress: TOKEN,
decryptionKey: aliceDk,
});
balance.availableBalance(); // bigint — 可花费金额
balance.pendingBalance(); // bigint — 待处理金额(不可花费)

解密通过 WASM 模块对每个 16 位块执行 BSGS(Baby-Step Giant-Step)算法。

pending_balance 移入 available_balance

await ca.rolloverPendingBalance({
signer: alice,
tokenAddress: TOKEN,
senderDecryptionKey: aliceDk, // 需要归一化时必填
withPauseIncoming: false, // 密钥轮换前设为 true
});

available_balance 各块重新压缩到 16 位范围。通常由 rolloverPendingBalance 自动调用。

const normalized = await ca.isBalanceNormalized({
accountAddress: alice.accountAddress,
tokenAddress: TOKEN,
});
if (!normalized) {
await ca.normalizeBalance({
signer: alice,
tokenAddress: TOKEN,
senderDecryptionKey: aliceDk,
});
}

隐私金额从发送方 available_balance 转移到接收方 pending_balance,金额在链上永不公开。

await ca.transfer({
signer: alice,
tokenAddress: TOKEN,
recipient: bob.accountAddress,
amount: 300n,
senderDecryptionKey: aliceDk,
additionalAuditorEncryptionKeys: [], // 可选的自愿审计员
memo: new TextEncoder().encode("发票 #42"), // 可选,最大 256 字节
});

如需在转账前自动 rollover,使用 transferWithTotalBalance

await ca.transferWithTotalBalance({
signer: alice,
tokenAddress: TOKEN,
recipient: bob.accountAddress,
amount: 300n,
senderDecryptionKey: aliceDk,
});

将代币从 available_balance 提回公开 FA 存储。

await ca.withdraw({
signer: alice,
tokenAddress: TOKEN,
senderDecryptionKey: aliceDk,
amount: 200n,
recipient: bob.accountAddress, // 可选,默认为 signer
});

如需在提款前自动 rollover,使用 withdrawWithTotalBalance

替换链上 EK,并用新密钥重新加密 available_balance 的 R 分量。

const newDk = TwistedEd25519PrivateKey.generate();
// SDK 自动处理:rollover + 暂停入账 → 轮换 → 恢复入账
await ca.rotateEncryptionKey({
signer: alice,
tokenAddress: TOKEN,
senderDecryptionKey: aliceDk, // 旧 DK
newSenderDecryptionKey: newDk, // 新 DK
});
// 请立即保存 newDk——旧 DK 此后对该余额无效。

当代币配置了审计员(全局或资产级),SDK 会在每次花费操作时自动包含为该审计员加密的金额密文,无需额外配置。

// 查询资产级审计员密钥(未设置时返回 undefined)
const auditorEk = await ca.getAssetAuditorEncryptionKey({ tokenAddress: TOKEN });
// 添加自愿审计员
await ca.transfer({
// ...其他参数
additionalAuditorEncryptionKeys: [auditorEk],
});

所有写入方法都支持代付 Gas 交易:

// 全局启用
const caFeePayer = new ConfidentialAsset({ config, withFeePayer: true });
// 或按次启用
await ca.deposit({ signer: alice, tokenAddress: TOKEN, amount: 1000n, withFeePayer: true });
错误码含义解决方法
E_CONFIDENTIAL_STORE_NOT_REGISTERED (3)(用户, 代币) 无机密存储先调用 registerBalance
E_CONFIDENTIAL_STORE_ALREADY_REGISTERED (2)已注册注册前先检查 hasUserRegistered
E_INCOMING_TRANSFERS_PAUSED (4)接收方已暂停入账等待接收方恢复,或换其他接收方
E_PENDING_BALANCE_MUST_BE_ROLLED_OVER (6)待处理计数达到 65536 上限调用 rolloverPendingBalance
E_NORMALIZATION_REQUIRED (7)Rollover 前需要先归一化向 rollover 传入 senderDecryptionKey,或手动调用 normalizeBalance
E_ALREADY_NORMALIZED (8)余额已归一化先检查 isBalanceNormalized
E_PENDING_BALANCE_NOT_ZERO_BEFORE_KEY_ROTATION (5)密钥轮换前待处理余额必须为零rotateEncryptionKey 会自动处理
E_SELF_TRANSFER (15)发送方与接收方相同换一个接收方
E_ASSET_TYPE_DISALLOWED (9)代币不在白名单中该代币类型尚未启用 CA
E_EMERGENCY_PAUSED (20)协议被治理紧急暂停检查 isEmergencyPaused() 并等待
E_UNSAFE_DISPATCHABLE_FA (16)不支持 dispatchable FA仅支持非 dispatchable FA 类型
E_MEMO_TOO_LONG (19)Memo 超过 256 字节缩短 memo

预热 WASM。 在用户打开 CA 界面时调用 initializeWasm(),避免首次证明生成时出现明显延迟。

花费前检查是否需要 rollover。pendingBalance() 非零且合并后余额充足,使用 *WithTotalBalance 系列方法一次性完成 rollover 和花费。

验证接收方是否已注册。 转账前调用 hasUserRegistered 确认接收方已注册。

密钥轮换后立即保存新密钥。 rotateEncryptionKey 会使旧 DK 失效,请立即持久化新 DK,并在丢弃旧密钥前验证它能成功解密余额。

隐藏不隐藏
转账金额发送方和接收方地址
用户余额机密模式下的代币总量(get_total_confidential_supply