You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Bob签名、Alice代付手续费的Hedera交易?

如何让Alice在Bob先冻结交易的情况下承担Hedera交易手续费?

问题场景

你需要实现以下流程:

  • Bob发起Hedera交易(如TokenCreateTransaction),但无HBAR支付手续费
  • Alice代Bob完成交易执行并承担所有手续费

原流程步骤:

  1. Bob创建交易对象
  2. Bob冻结并签名交易
  3. Alice接收已签名交易并添加自身签名
  4. Alice执行交易

但原代码中,Bob使用绑定了自己账号的bobClient调用freezeWith()时,SDK会自动将transactionId和operatorAccountId设为Bob的账号,导致最终手续费仍由Bob承担,而非Alice。

解决方案

核心是避免Bob在冻结交易时将自己设置为付款人,让Alice在处理交易时完成付款人的绑定。以下是两种可行实现方式:

方式一:Bob冻结时不绑定自身Operator Client

Bob仅冻结交易字节(不关联自己的账号),由Alice用自己的Client完成付款人绑定和最终冻结:

import {
  AccountId,
  TokenCreateTransaction,
  TokenType,
  PrivateKey,
  Client,
} from "@hashgraph/sdk";

//  --- BOB (SIGNER account) ---
const BOB_ACCOUNTID = "0.0.5782085";
const BOB_PRIVATE_KEY = "";

// --- ALICE (PAYER account) ---
const ALICE_ACCOUNTID = "0.0.1079726";
const ALICE_PRIVATE_KEY = "";

async function main() {
  //  --- BOB (SIGNER account) ---
  const bobPrivateKey = PrivateKey.fromStringED25519(BOB_PRIVATE_KEY);
  const bobAccountId = AccountId.fromString(BOB_ACCOUNTID);
  // Bob创建无Operator的Client,避免自动关联付款人
  const bobClient = Client.forTestnet();

  // --- ALICE (PAYER account) ---
  const alicePrivateKey = PrivateKey.fromStringED25519(ALICE_PRIVATE_KEY);
  const aliceAccountId = AccountId.fromString(ALICE_ACCOUNTID);
  const aliceClient = Client.forTestnet().setOperator(
    aliceAccountId,
    alicePrivateKey
  );

  // 1. Bob创建交易对象
  const transaction = new TokenCreateTransaction()
    .setTokenName("New Token 123")
    .setTokenSymbol("NT123")
    .setTokenType(TokenType.FungibleCommon)
    .setInitialSupply(2000)
    .setTreasuryAccountId(bobAccountId);

  // 2. Bob冻结交易(不绑定自身Operator,避免设置自己为付款人)
  const frozenTx = await transaction.freeze(); 
  const signedTx = await frozenTx.sign(bobPrivateKey);

  // 3. Alice接收交易,用自己的Client完成冻结(绑定自己为付款人)并签名
  const aliceFrozenTx = await signedTx.freezeWith(aliceClient);
  const aliceSignedTx = await aliceFrozenTx.sign(alicePrivateKey);

  // 4. Alice执行交易,手续费由Alice承担
  const txResponse = await aliceSignedTx.execute(aliceClient);
  const receipt = await txResponse.getReceipt(aliceClient);
  console.log("TransactionId: " + txResponse.transactionId);
  console.log("Transaction status: " + receipt.status.toString());
  console.log("Created tokenId: " + receipt.tokenId);

  process.exit();
}

main();

方式二:Bob创建交易时手动指定付款人为Alice

Bob在构建交易时直接将付款人设为Alice的账号,冻结时无需绑定自身Client:

import {
  AccountId,
  TokenCreateTransaction,
  TokenType,
  PrivateKey,
  Client,
} from "@hashgraph/sdk";

//  --- BOB (SIGNER account) ---
const BOB_ACCOUNTID = "0.0.5782085";
const BOB_PRIVATE_KEY = "";

// --- ALICE (PAYER account) ---
const ALICE_ACCOUNTID = "0.0.1079726";
const ALICE_PRIVATE_KEY = "";

async function main() {
  //  --- BOB (SIGNER account) ---
  const bobPrivateKey = PrivateKey.fromStringED25519(BOB_PRIVATE_KEY);
  const bobAccountId = AccountId.fromString(BOB_ACCOUNTID);

  // --- ALICE (PAYER account) ---
  const alicePrivateKey = PrivateKey.fromStringED25519(ALICE_PRIVATE_KEY);
  const aliceAccountId = AccountId.fromString(ALICE_ACCOUNTID);
  const aliceClient = Client.forTestnet().setOperator(
    aliceAccountId,
    alicePrivateKey
  );

  // 1. Bob创建交易对象,手动指定付款人为Alice
  const transaction = new TokenCreateTransaction()
    .setTokenName("New Token 123")
    .setTokenSymbol("NT123")
    .setTokenType(TokenType.FungibleCommon)
    .setInitialSupply(2000)
    .setTreasuryAccountId(bobAccountId)
    .setOperatorAccountId(aliceAccountId); // 手动设置付款人

  // 2. Bob冻结并签名交易
  const frozenTx = await transaction.freeze();
  const signedTx = await frozenTx.sign(bobPrivateKey);

  // 3. Alice添加自身签名
  const aliceSignedTx = await signedTx.sign(alicePrivateKey);

  // 4. Alice执行交易
  const txResponse = await aliceSignedTx.execute(aliceClient);
  const receipt = await txResponse.getReceipt(aliceClient);
  console.log("TransactionId: " + txResponse.transactionId);
  console.log("Transaction status: " + receipt.status.toString());
  console.log("Created tokenId: " + receipt.tokenId);

  process.exit();
}

main();

关键原理说明

  • freezeWith(client)方法会自动将交易的transactionId中的账号设为client的Operator账号,同时设置operatorAccountId,这是原代码中Bob成为付款人的核心原因。
  • 调用无参数的freeze()仅冻结交易的字节结构,不会关联任何付款人账号,后续由Alice用自己的Client调用freezeWith()时,会将付款人设为Alice。
  • 手动调用setOperatorAccountId(aliceAccountId)可以直接指定交易的付款人,后续冻结时不会被Bob的Client覆盖。

内容的提问来源于stack exchange,提问作者Diego Escalona

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 12:35:24