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

  • Bob想要向Hedera网络发送交易,但他没有HBAR来支付交易费
  • 爱丽丝可以通过在Hedera网络上发送/执行他的交易来向Bob提供这项服务,包括相关的交易费

此场景涉及以下步骤:

  1. Bob创建事务对象(例如TokenCreateTransaction)
  2. 鲍勃冻结并在交易上签字
  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. 鲍勃冻结并在交易上签字
  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执行交易并支付交易费用
  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(Account ID 0.0.5782085)是付款人帐户:https://hashscan.io/testnet/transaction/1698410500.114199003

我们如何确保爱丽丝是支付交易费的人,特别是当鲍勃是最初冻结交易费的人时?任何 idea 都将受到极大的赞赏.

推荐答案

经过多次try ,我找到了解决问题的办法.

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

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

transactionId参数在这一过程中至关重要.Bob必须使用Alice的Account ID创建它,以将Alice指定为交易的付款人.要生成它,Bob可以使用TransactionId class中的generate()方法.确保在代码的开头导入类:

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

const transactionId = TransactionId.generate(aliceAccountId)

通过这种方式,我们可以确保Bob发起、冻结和签署交易,同时指定Alice作为支付交易费用的支付账户.

重要提示:此流程要求Bob在创建事务之前知道Alice的Account ID.

以下是所有更新的代码(只添加了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(Account ID 0.0.1079726)是付款人帐户:https://hashscan.io/testnet/transaction/1698423106.148047003

希望能对某些人有所帮助!

Typescript相关问答推荐

"@ clerker/nextjs/server没有名为clerkMiddleware的导入成员'

TypScript如何在 struct 上定义基元类型?

如何为ViewContainerRef加载的Angular组件实现CanDeactivate保护?

在将对象从一个对象转换成另一个对象时,可以缩小对象的键吗?

React router 6.22.3不只是在生产模式下显示,为什么?

React typescribe Jest调用函数

扩展函数签名中的参数类型,而不使用泛型

如何在另一个参数类型中获取一个参数字段的类型?

在不更改类型签名的情况下在数组并集上映射文字

通过字符串索引访问对象';S的值时出现文字脚本错误

如何使用Geojson模块和@Types/Geojson类型解析TypeScrip中的GeoJSON?

如何创建一家Reduxstore ?

在类型脚本中创建泛型类型以动态追加属性后缀

有没有办法防止类型交集绕过联合类型限制?

VITEST警告:当前的测试运行器不支持After Each/teardown挂钩

在Google授权后由客户端接收令牌

在TS泛型和记录类型映射方面有问题

Typescript循环函数引用

如何使用 Angular 确定给定值是否与应用程序中特定单选按钮的值匹配?

如何为字符串模板文字创建类型保护