typescript - 如何创建由一个账户签名但使用另一个账户支付的 Hedera 交易

标签 typescript sdk hedera-hashgraph

我正在尝试使用 Hedera JavaScript SDK 复制以下场景

  • Bob 希望向 Hedera 网络发送一笔交易,但他没有 HBAR 来支付交易费用
  • Alice 可以通过在 Hedera 网络上发送/执行 Bob 的交易来向 Bob 提供该服务,并支付相关的交易费用

此场景涉及以下步骤:

  1. Bob 创建交易对象(例如 TokenCreateTransaction)
  2. Bob 卡住并签署交易
  3. Alice 收到 Bob 签名的交易并添加她的签名
  4. Alice 执行交易并支付交易费用

您可以在下面看到实现:

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);
  const bobClient = Client.forTestnet().setOperator(
    bobAccountId,
    bobPrivateKey
  );

  // --- 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 creates the transaction object (e.g. a `TokenCreateTransaction`)
  const transaction = new TokenCreateTransaction()
    .setTokenName("New Token 123")
    .setTokenSymbol("NT123")
    .setTokenType(TokenType.FungibleCommon)
    .setInitialSupply(2000)
    .setTreasuryAccountId(bobAccountId);

  // 2. Bob freezes and signs the transaction
  const frozenTx = await transaction.freezeWith(bobClient);
  const signedTx = await frozenTx.sign(bobPrivateKey);

  // 3. Alice receives the signed transaction from Bob and adds her signature
  const aliceSignedTx = await signedTx.sign(alicePrivateKey);

  // 4. Alice executes the transaction and pays for the transaction fees
  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 卡住交易时就会出现问题,因为某些交易属性会被 SDK 自动修改。具体来说,transactionIdoperatorAccountId 属性是根据卡住帐户设置的,因此将 Bob 指定为交易的付款人。

因此,即使 Alice 在流程结束时执行了交易,付款人帐户实际上是 Bob。

您可以在此处看到结果,其中 Bob (AccountId 0.0.5782085) 是付款人帐户:https://hashscan.io/testnet/transaction/1698410500.114199003

我们如何确保 Alice 是支付交易费用的人,特别是当 Bob 是第一个卡住交易费用的人时?任何想法将不胜感激。

最佳答案

经过多次尝试,我找到了解决问题的方法。

Bob创建交易时,需要设置transactionId使用setTransactionId()方法如下图:

const transaction = new TokenCreateTransaction()
  .setTransactionId(transactionId)

transactionId参数在此过程中至关重要。 Bob 必须使用 Alice 的 AccountId 创建它,以指定 Alice 作为交易的付款人。为了生成它,Bob 可以利用 generate()方法来自 TransactionId class 。确保在代码开头导入该类:

import { TransactionId } from "@hashgraph/sdk";

const transactionId = TransactionId.generate(aliceAccountId)

这样,我们可以确保Bob发起、卡住和签署交易,同时指定Alice作为支付交易费用的付款账户。

Important: this flow requires that Bob knows Alice's AccountId before creating the transaction.

这里是所有更新的代码(只添加了2行,注释为:<--- New Line):

import {
  AccountId,
  TokenCreateTransaction,
  TokenType,
  PrivateKey,
  Client,
  TransactionId, // <--- New Line
} from "@hashgraph/sdk";

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

// --- ALICE Account (PAYER) ---
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);
  const bobClient = Client.forTestnet().setOperator(
    bobAccountId,
    bobPrivateKey
  );

  // --- 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 creates the transaction object (e.g. a `TokenCreateTransaction`) 
  // and sets a specific transactionId to designate Alice as the payer account 
  const transaction = new TokenCreateTransaction()
    .setTokenName("New Token 123")
    .setTokenSymbol("NT123")
    .setTokenType(TokenType.FungibleCommon)
    .setInitialSupply(2000)
    .setTreasuryAccountId(bobAccountId)
    .setTransactionId(TransactionId.generate(aliceAccountId)); // <--- New Line

  // 2. Bob freezes and signs the transaction
  const frozenTx = await transaction.freezeWith(bobClient);
  const signedTx = await frozenTx.sign(bobPrivateKey);

  // 3. Alice receives the signed transaction from Bob and adds her signature
  const doubleSignedTx = await signedTx.sign(alicePrivateKey);

  // 4. Alice executes the transaction and pays for the transaction fees
  const txResponse = await doubleSignedTx.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();

您可以在此处看到结果,其中 Alice (AccountId 0.0.1079726 ) 是付款人帐户:https://hashscan.io/testnet/transaction/1698423106.148047003

希望对大家有帮助!

关于typescript - 如何创建由一个账户签名但使用另一个账户支付的 Hedera 交易,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77374328/

相关文章:

javascript - 是否可以将删除/放置请求发送到 Azure AD 安全 api 或使用 aadHttpClientFactory 以字符串形式获取 jwt token

ruby-on-rails - 如何使用 Rails Invoice SDK 插入 X-PAYPAL header ?

hedera-hashgraph - 我可以控制客户端将交易提交到哪个共识节点吗?

hedera-hashgraph - 如何将Hedera native 地址转换为EVM地址?​

angular - 包含第三方库的类型会导致未通过 angular CLI 找到模块?

reactjs - 为什么 vscode 在边缘而不是 chrome 中打开 React 应用程序?

node.js - 设置 "pure" typescript 模块的正确方法是什么

java - 如何使用 socks 配置 Android SDK 管理器?

iOS SDK : Moving the button into the center of screen by code

hedera-hashgraph - 向节点提交请求时出现问题,请使用新节点重试 : BUSY