创建 Token Mint

如何创建带有机密转账扩展的 mint

机密转账扩展通过向 mint account 添加额外状态来实现私密代币转账。本节介绍如何创建启用了此扩展的 token mint。

下图展示了创建带有机密转账扩展的 mint 所涉及的步骤:

Create Mint with Confidential Transfer Extension

机密转账 Mint 状态

该扩展将 ConfidentialTransferMint 状态添加到 mint account:

Confidential Mint State
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct ConfidentialTransferMint {
/// Authority to modify the `ConfidentialTransferMint` configuration and to
/// approve new accounts (if `auto_approve_new_accounts` is true)
///
/// The legacy Token Multisig account is not supported as the authority
pub authority: OptionalNonZeroPubkey,
/// Indicate if newly configured accounts must be approved by the
/// `authority` before they may be used by the user.
///
/// * If `true`, no approval is required and new accounts may be used
/// immediately
/// * If `false`, the authority must approve newly configured accounts (see
/// `ConfidentialTransferInstruction::ConfigureAccount`)
pub auto_approve_new_accounts: PodBool,
/// Authority to decode any transfer amount in a confidential transfer.
pub auditor_elgamal_pubkey: OptionalNonZeroElGamalPubkey,
}

ConfidentialTransferMint 包含三个配置字段:

  • authority:有权更改 mint 的机密转账设置,并在未启用自动审批时批准新机密账户的账户。

  • auto_approve_new_accounts:设置为 true 时,用户可以默认创建启用机密转账的 token account。设置为 false 时,authority 必须在每个新 token account 用于机密转账之前对其进行审批。

  • auditor_elgamal_pubkey:一个可选的审计方,能够解密机密交易中的转账金额,在保护公众隐私的同时提供合规机制。

所需指令

创建启用了机密转账的 mint 需要在单笔交易中包含三条指令:

  1. 创建 Mint Account:调用 System Program 的 CreateAccount 指令以创建 mint account。

  2. 初始化机密转账扩展:调用 Token Extensions Program 的 ConfidentialTransferInstruction::InitializeMint 指令,为 mint 配置 ConfidentialTransferMint 状态。

  3. 初始化 Mint:调用 Token Extensions Program 的 Instruction::InitializeMint 指令以初始化标准 mint 状态。

虽然您可以手动编写这些指令,但 spl_token_client crate 提供了 create_mint 方法,可在单次函数调用中构建并发送包含全部三条指令的交易,如下方示例所示。

示例代码

以下代码演示了如何使用机密转账扩展创建铸币。

机密转账依赖于 ZK ElGamal 证明程序,该程序已在主网和开发网上启用。标准的 solana-test-validator 不会启用它,但主网分叉本地 validator(例如 Surfpool)会启用。请使用已充值的付款账户在上述环境之一中运行示例(代码默认使用开发网),并将占位符铸币地址和账户地址替换为您自己的地址。

Rust

fn main() -> Result<()> {
let rpc_client = RpcClient::new_with_commitment(
String::from("https://api.devnet.solana.com"),
CommitmentConfig::confirmed(),
);
let payer = load_keypair()?;
let mint = Keypair::new();
let decimals: u8 = 2;
// Allocate space for a mint that carries the ConfidentialTransferMint
// extension, then fund it for rent exemption.
let space =
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::ConfidentialTransferMint])?;
let rent = rpc_client.get_minimum_balance_for_rent_exemption(space)?;
// The auditor ElGamal key lets the issuer decrypt transfer amounts for
// compliance. Persist this key. Pass `None` to create a mint with no auditor.
let auditor = ElGamalKeypair::new_rand();
let auditor_pubkey: PodElGamalPubkey = (*auditor.pubkey()).into();
let create_account_ix = system_instruction::create_account(
&payer.pubkey(),
&mint.pubkey(),
rent,
space as u64,
&spl_token_2022::id(),
);
// The confidential-transfer extension must be initialized before the base
// mint and cannot be added later.
let init_confidential_ix = initialize_confidential_transfer_mint(
&spl_token_2022::id(),
&mint.pubkey(),
Some(payer.pubkey()), // authority that can update confidential settings
true, // auto-approve new accounts
Some(auditor_pubkey),
)?;
let init_mint_ix = initialize_mint_base(
&spl_token_2022::id(),
&mint.pubkey(),
&payer.pubkey(), // mint authority
None, // freeze authority
decimals,
)?;
let blockhash = rpc_client.get_latest_blockhash()?;
let transaction = Transaction::new_signed_with_payer(
&[create_account_ix, init_confidential_ix, init_mint_ix],
Some(&payer.pubkey()),
&[&payer, &mint],
blockhash,
);
let signature = rpc_client.send_and_confirm_transaction(&transaction)?;
println!("Created confidential mint {}: {signature}", mint.pubkey());
Ok(())
}
fn load_keypair() -> Result<Keypair> {
let keypair_path = dirs::home_dir()
.context("could not find home directory")?
.join(".config/solana/id.json");
let bytes: Vec<u8> = serde_json::from_reader(std::fs::File::open(keypair_path)?)?;
let mut secret = [0u8; 32];
secret.copy_from_slice(&bytes[0..32]);
Ok(Keypair::new_from_array(secret))
}

Typescript

const client = await createClient()
.use(signerFromFile(join(homedir(), ".config/solana/id.json")))
.use(
solanaRpc({
rpcUrl: "https://api.devnet.solana.com"
})
);
const payer = client.payer;
const mint = await generateKeyPairSigner();
// The auditor ElGamal key lets the issuer decrypt transfer amounts for
// compliance. Persist it; omit `auditorElgamalPubkey` to create a mint with no
// auditor.
const auditor = await deriveElGamalKeypairForOwnerMint({
signer: payer,
owner: payer.address,
mint: mint.address
});
const plan = getCreateMintInstructionPlan({
payer,
newMint: mint,
decimals: 2,
mintAuthority: payer,
extensions: [
{
__kind: "ConfidentialTransferMint",
authority: some(payer.address),
autoApproveNewAccounts: true,
auditorElgamalPubkey: some(auditor.elgamalPubkey)
}
]
});
const result = await client.sendTransaction(plan);
console.log(
`Created confidential mint ${mint.address}: ${result.context.signature}`
);

Is this page helpful?

Table of Contents

Edit Page
©️ 2026 Solana 基金会版权所有