如何实现Bob签名、Alice代付手续费的Hedera交易?
如何让Alice在Bob先冻结交易的情况下承担Hedera交易手续费?
问题场景
你需要实现以下流程:
- Bob发起Hedera交易(如
TokenCreateTransaction),但无HBAR支付手续费 - Alice代Bob完成交易执行并承担所有手续费
原流程步骤:
- Bob创建交易对象
- Bob冻结并签名交易
- Alice接收已签名交易并添加自身签名
- 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
相关产品推荐
相关产品推荐

