跳转到内容

赞助交易

通常情况下,执行交易的账户需要支付 gas 费用。通过赞助(费用支付者)交易,一个单独的账户可以代替发送者承担 gas 费用。这对于引导尚未持有 APT 的新用户、为您的应用用户补贴交易成本或从中央金库管理 gas 费用非常有用。

  1. 构建入口函数负载。

    为发送者要执行的交易创建负载。

    use aptos_sdk::types::EntryFunctionPayload;
    let payload = EntryFunctionPayload::new(
    "0x1::aptos_account::transfer".parse()?,
    vec![],
    vec![bob.address().into(), 10_000_000u64.into()],
    );
  2. 使用 TransactionBuilder 构建原始交易。

    从发送者账户构造原始交易。

    use aptos_sdk::transaction_builder::TransactionBuilder;
    let raw_txn = TransactionBuilder::new(payload, aptos.get_chain_id().await?)
    .sender(alice.address())
    .sequence_number(aptos.get_sequence_number(alice.address()).await?)
    .max_gas_amount(10_000)
    .gas_unit_price(100)
    .expiration_timestamp_secs(
    aptos.get_latest_ledger_info().await?.timestamp() + 60,
    )
    .build();
  3. 创建指定赞助者的 FeePayerRawTransaction

    用费用支付者的地址包装原始交易。当没有额外的次要签名者时使用 new_simple

    use aptos_sdk::types::FeePayerRawTransaction;
    let fee_payer_txn = FeePayerRawTransaction::new_simple(
    raw_txn,
    sponsor.address(),
    );
  4. 让发送者和赞助者同时签署交易。

    使用 sign_fee_payer_transaction 生成已签名交易。第二个参数是发送者,第三个是空切片(用于多代理赞助交易中的次要签名者),第四个是费用支付者。

    let signed_txn = aptos.sign_fee_payer_transaction(
    &fee_payer_txn,
    &alice, // Sender
    &[], // Secondary signers (empty for simple transactions)
    &sponsor, // Fee payer
    )?;
  5. 提交交易并等待确认。

    let result = aptos.submit_and_wait(signed_txn).await?;
    let success = result
    .data
    .get("success")
    .and_then(|v| v.as_bool())
    .unwrap_or(false);
    println!("Transaction success: {}", success);

您可以将费用支付者赞助与多代理交易结合使用。在这种情况下,在构造 FeePayerRawTransaction 和签名时提供次要签名者。

use aptos_sdk::types::FeePayerRawTransaction;
// Create fee payer transaction with secondary signers
let fee_payer_txn = FeePayerRawTransaction::new(
raw_txn,
vec![bob.address()], // Secondary signer addresses
sponsor.address(), // Fee payer address
);
// Sign with all parties
let signed_txn = aptos.sign_fee_payer_transaction(
&fee_payer_txn,
&alice, // Primary sender
&[&bob as &dyn Account], // Secondary signers
&sponsor, // Fee payer
)?;
  • INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE — 赞助者账户没有足够的 APT 来支付最大可能的 gas 费用。赞助者必须至少持有 max_gas_amount * gas_unit_price octas。您可以先模拟交易以获得更精确的 gas 估算,然后相应地设置 max_gas_amount。详见模拟交易
  • INVALID_AUTH_KEYFeePayerRawTransaction 中指定的费用支付者地址与签名为费用支付者的账户不匹配。请验证您在构造函数和签名函数中传递的是正确的赞助者账户。
/// This example demonstrates a sponsored transaction where a separate
/// account (sponsor) pays the gas fees for Alice's transfer to Bob.
use aptos_sdk::{Aptos, AptosConfig};
use aptos_sdk::account::Ed25519Account;
use aptos_sdk::types::{EntryFunctionPayload, FeePayerRawTransaction};
use aptos_sdk::transaction_builder::TransactionBuilder;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Connect to testnet
let aptos = Aptos::new(AptosConfig::testnet())?;
// Generate accounts
let alice = Ed25519Account::generate();
let bob = Ed25519Account::generate();
let sponsor = Ed25519Account::generate();
// Fund accounts -- note that Alice only needs a small amount for the
// transfer itself, while the sponsor needs enough to cover gas.
aptos.fund_account(alice.address(), 100_000_000).await?;
aptos.fund_account(bob.address(), 100_000_000).await?;
aptos.fund_account(sponsor.address(), 100_000_000).await?;
println!("Alice: {}", alice.address());
println!("Bob: {}", bob.address());
println!("Sponsor: {}", sponsor.address());
// Check initial balances
let alice_balance = aptos.get_balance(alice.address()).await?;
let sponsor_balance = aptos.get_balance(sponsor.address()).await?;
println!("\n=== Initial Balances ===");
println!("Alice: {} octas", alice_balance);
println!("Sponsor: {} octas", sponsor_balance);
// 1. Build the payload
let payload = EntryFunctionPayload::new(
"0x1::aptos_account::transfer".parse()?,
vec![],
vec![bob.address().into(), 10_000_000u64.into()],
);
// 2. Build the raw transaction
let raw_txn = TransactionBuilder::new(payload, aptos.get_chain_id().await?)
.sender(alice.address())
.sequence_number(aptos.get_sequence_number(alice.address()).await?)
.max_gas_amount(10_000)
.gas_unit_price(100)
.expiration_timestamp_secs(
aptos.get_latest_ledger_info().await?.timestamp() + 60,
)
.build();
// 3. Create the fee payer transaction
let fee_payer_txn = FeePayerRawTransaction::new_simple(
raw_txn,
sponsor.address(),
);
// 4. Sign with both the sender and the sponsor
let signed_txn = aptos.sign_fee_payer_transaction(
&fee_payer_txn,
&alice,
&[],
&sponsor,
)?;
// 5. Submit and wait
let result = aptos.submit_and_wait(signed_txn).await?;
let success = result
.data
.get("success")
.and_then(|v| v.as_bool())
.unwrap_or(false);
println!("\nTransaction success: {}", success);
// Verify that the sponsor paid the gas, not Alice
let new_alice_balance = aptos.get_balance(alice.address()).await?;
let new_sponsor_balance = aptos.get_balance(sponsor.address()).await?;
println!("\n=== Balances After Transfer ===");
println!("Alice: {} octas (paid only the transfer amount)", new_alice_balance);
println!("Sponsor: {} octas (paid the gas fee)", new_sponsor_balance);
Ok(())
}