@ledgerhq/coin-sui 0.10.0-nightly.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +26 -14
  3. package/lib/bridge/buildTransaction.d.ts +1 -1
  4. package/lib/bridge/buildTransaction.d.ts.map +1 -1
  5. package/lib/bridge/buildTransaction.integration.test.js +0 -12
  6. package/lib/bridge/buildTransaction.integration.test.js.map +1 -1
  7. package/lib/bridge/buildTransaction.js +4 -8
  8. package/lib/bridge/buildTransaction.js.map +1 -1
  9. package/lib/bridge/buildTransaction.test.js +10 -14
  10. package/lib/bridge/buildTransaction.test.js.map +1 -1
  11. package/lib/bridge/getFeesForTransaction.d.ts.map +1 -1
  12. package/lib/bridge/getFeesForTransaction.js +3 -2
  13. package/lib/bridge/getFeesForTransaction.js.map +1 -1
  14. package/lib/logic/craftTransaction.js +1 -1
  15. package/lib/logic/craftTransaction.js.map +1 -1
  16. package/lib/network/sdk.d.ts +3 -18
  17. package/lib/network/sdk.d.ts.map +1 -1
  18. package/lib/network/sdk.js +51 -64
  19. package/lib/network/sdk.js.map +1 -1
  20. package/lib/network/sdk.test.js +65 -148
  21. package/lib/network/sdk.test.js.map +1 -1
  22. package/lib-es/bridge/buildTransaction.d.ts +1 -1
  23. package/lib-es/bridge/buildTransaction.d.ts.map +1 -1
  24. package/lib-es/bridge/buildTransaction.integration.test.js +0 -12
  25. package/lib-es/bridge/buildTransaction.integration.test.js.map +1 -1
  26. package/lib-es/bridge/buildTransaction.js +5 -9
  27. package/lib-es/bridge/buildTransaction.js.map +1 -1
  28. package/lib-es/bridge/buildTransaction.test.js +10 -14
  29. package/lib-es/bridge/buildTransaction.test.js.map +1 -1
  30. package/lib-es/bridge/getFeesForTransaction.d.ts.map +1 -1
  31. package/lib-es/bridge/getFeesForTransaction.js +3 -2
  32. package/lib-es/bridge/getFeesForTransaction.js.map +1 -1
  33. package/lib-es/logic/craftTransaction.js +1 -1
  34. package/lib-es/logic/craftTransaction.js.map +1 -1
  35. package/lib-es/network/sdk.d.ts +3 -18
  36. package/lib-es/network/sdk.d.ts.map +1 -1
  37. package/lib-es/network/sdk.js +48 -61
  38. package/lib-es/network/sdk.js.map +1 -1
  39. package/lib-es/network/sdk.test.js +65 -148
  40. package/lib-es/network/sdk.test.js.map +1 -1
  41. package/package.json +8 -8
  42. package/src/bridge/buildTransaction.integration.test.ts +0 -13
  43. package/src/bridge/buildTransaction.test.ts +25 -25
  44. package/src/bridge/buildTransaction.ts +5 -10
  45. package/src/bridge/getFeesForTransaction.ts +3 -2
  46. package/src/logic/craftTransaction.ts +1 -1
  47. package/src/network/sdk.test.ts +77 -186
  48. package/src/network/sdk.ts +62 -82
@@ -1,3 +1,50 @@
1
+ // Move all jest.mock calls to the very top
2
+ jest.mock("../config", () => ({
3
+ __esModule: true,
4
+ default: {
5
+ getCoinConfig: jest.fn(() => ({ node: { url: "http://test.com" } })),
6
+ setCoinConfig: jest.fn(),
7
+ },
8
+ }));
9
+
10
+ jest.mock("../utils", () => ({
11
+ ensureAddressFormat: jest.fn((addr: string) => addr),
12
+ }));
13
+
14
+ jest.mock("@ledgerhq/live-network/cache", () => ({
15
+ makeLRUCache: jest.fn(() => jest.fn()),
16
+ minutes: jest.fn(() => 60000),
17
+ }));
18
+
19
+ jest.mock("@ledgerhq/logs", () => ({
20
+ log: jest.fn(),
21
+ }));
22
+
23
+ jest.mock("@mysten/sui/client", () => {
24
+ const mockClient = {
25
+ queryTransactionBlocks: jest.fn(),
26
+ getBalance: jest.fn(),
27
+ getLatestCheckpointSequenceNumber: jest.fn(),
28
+ getCheckpoint: jest.fn(),
29
+ dryRunTransactionBlock: jest.fn(),
30
+ executeTransactionBlock: jest.fn(),
31
+ };
32
+ return {
33
+ SuiClient: jest.fn().mockImplementation(() => mockClient),
34
+ };
35
+ });
36
+
37
+ jest.mock("@mysten/sui/transactions", () => ({
38
+ Transaction: jest.fn().mockImplementation(() => ({
39
+ setSender: jest.fn().mockReturnThis(),
40
+ splitCoins: jest.fn().mockReturnValue([{ id: "coin1" }]),
41
+ transferObjects: jest.fn().mockReturnThis(),
42
+ gas: { id: "gas" },
43
+ build: jest.fn().mockResolvedValue(new Uint8Array([1, 2, 3])),
44
+ })),
45
+ }));
46
+
47
+ // Now import after mocks
1
48
  import * as sdk from "./sdk";
2
49
  import coinConfig from "../config";
3
50
 
@@ -168,16 +215,6 @@ const mockTransaction = {
168
215
 
169
216
  const mockApi = new SuiClient({ url: "mock" }) as jest.Mocked<SuiClient>;
170
217
 
171
- // Helper function to generate mock coins from an array of balances
172
- const createMockCoins = (balances: string[]): any[] => {
173
- return balances.map((balance, index) => ({
174
- coinObjectId: `0xcoin${index + 1}`,
175
- balance,
176
- digest: `0xdigest${index + 1}`,
177
- version: "1",
178
- }));
179
- };
180
-
181
218
  beforeAll(() => {
182
219
  coinConfig.setCoinConfig(() => ({
183
220
  status: {
@@ -283,9 +320,9 @@ describe("SDK Functions", () => {
283
320
  });
284
321
 
285
322
  test("getOperationDate should return correct date", () => {
286
- const date = sdk.getOperationDate(mockTransaction as SuiTransactionBlockResponse);
287
- expect(date).toBeDefined();
288
- expect(date).toBeInstanceOf(Date);
323
+ expect(sdk.getOperationDate(mockTransaction as SuiTransactionBlockResponse)).toEqual(
324
+ new Date("2025-03-18T10:40:54.878Z"),
325
+ );
289
326
  });
290
327
 
291
328
  test("getOperationCoinType should extract token coin type", () => {
@@ -497,6 +534,33 @@ describe("SDK Functions", () => {
497
534
  expect(info).toHaveProperty("fees");
498
535
  });
499
536
 
537
+ test("getCoinObjectIds should return array of object IDs for token transactions", async () => {
538
+ const address = "0x6e143fe0a8ca010a86580dafac44298e5b1b7d73efc345356a59a15f0d7824f0";
539
+ const transaction = {
540
+ mode: "token.send" as const,
541
+ coinType: "0x123::test::TOKEN",
542
+ amount: new BigNumber(100),
543
+ recipient: "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164",
544
+ };
545
+
546
+ const coinObjectIds = await sdk.getCoinObjectIds(address, transaction);
547
+ expect(Array.isArray(coinObjectIds)).toBe(true);
548
+ expect(coinObjectIds).toContain("0xtest_coin_object_id");
549
+ });
550
+
551
+ test("getCoinObjectIds should return null for SUI transactions", async () => {
552
+ const address = "0x6e143fe0a8ca010a86580dafac44298e5b1b7d73efc345356a59a15f0d7824f0";
553
+ const transaction = {
554
+ mode: "send" as const,
555
+ coinType: sdk.DEFAULT_COIN_TYPE,
556
+ amount: new BigNumber(100),
557
+ recipient: "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164",
558
+ };
559
+
560
+ const coinObjectIds = await sdk.getCoinObjectIds(address, transaction);
561
+ expect(coinObjectIds).toBeNull();
562
+ });
563
+
500
564
  test("createTransaction should build a transaction", async () => {
501
565
  const address = "0x6e143fe0a8ca010a86580dafac44298e5b1b7d73efc345356a59a15f0d7824f0";
502
566
  const transaction = {
@@ -1467,176 +1531,3 @@ describe("filterOperations", () => {
1467
1531
  });
1468
1532
  });
1469
1533
  });
1470
-
1471
- describe("getCoinsForAmount", () => {
1472
- const mockAddress = "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164";
1473
- const mockCoinType = "0x2::sui::SUI";
1474
-
1475
- beforeEach(() => {
1476
- mockApi.getCoins.mockReset();
1477
- });
1478
-
1479
- describe("basic functionality", () => {
1480
- test("handles single coin scenarios", async () => {
1481
- const sufficientCoins = createMockCoins(["1000"]);
1482
- mockApi.getCoins.mockResolvedValueOnce({ data: sufficientCoins, hasNextPage: false });
1483
-
1484
- let result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1485
- expect(result).toHaveLength(1);
1486
- expect(result[0].balance).toBe("1000");
1487
-
1488
- const insufficientCoins = createMockCoins(["500"]);
1489
- mockApi.getCoins.mockResolvedValueOnce({ data: insufficientCoins, hasNextPage: false });
1490
-
1491
- result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1492
- expect(result).toHaveLength(1);
1493
- expect(result[0].balance).toBe("500");
1494
- });
1495
-
1496
- test("selects minimum coins needed", async () => {
1497
- const exactMatchCoins = createMockCoins(["600", "400", "300"]);
1498
- mockApi.getCoins.mockResolvedValueOnce({ data: exactMatchCoins, hasNextPage: false });
1499
-
1500
- let result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1501
- expect(result).toHaveLength(2);
1502
- expect(result[0].balance).toBe("600");
1503
- expect(result[1].balance).toBe("400");
1504
-
1505
- const exceedCoins = createMockCoins(["800", "400", "200"]);
1506
- mockApi.getCoins.mockResolvedValueOnce({ data: exceedCoins, hasNextPage: false });
1507
-
1508
- result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1509
- expect(result).toHaveLength(2);
1510
- expect(result[0].balance).toBe("800");
1511
- expect(result[1].balance).toBe("400");
1512
- });
1513
-
1514
- test("handles edge cases", async () => {
1515
- mockApi.getCoins.mockResolvedValueOnce({ data: [], hasNextPage: false });
1516
-
1517
- let result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1518
- expect(result).toHaveLength(0);
1519
-
1520
- const coins = createMockCoins(["1000"]);
1521
- mockApi.getCoins.mockResolvedValueOnce({ data: coins, hasNextPage: false });
1522
-
1523
- result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 0);
1524
- expect(result).toHaveLength(0);
1525
- });
1526
- });
1527
-
1528
- describe("sorting and filtering", () => {
1529
- test("filters zero balance coins", async () => {
1530
- const mockCoins = createMockCoins(["1000", "500"]);
1531
- mockCoins.splice(1, 0, createMockCoins(["0"])[0]);
1532
- mockCoins.push({ coinObjectId: "0xcoin4", balance: "0", digest: "0xdigest4", version: "1" });
1533
-
1534
- mockApi.getCoins.mockResolvedValueOnce({ data: mockCoins, hasNextPage: false });
1535
-
1536
- const result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1537
-
1538
- expect(result).toHaveLength(1);
1539
- expect(result[0].balance).toBe("1000");
1540
- expect(result.every(coin => parseInt(coin.balance) > 0)).toBe(true);
1541
- });
1542
-
1543
- test("sorts and optimizes coin selection", async () => {
1544
- const unsortedCoins = createMockCoins(["100", "800", "300", "500"]);
1545
- mockApi.getCoins.mockResolvedValueOnce({ data: unsortedCoins, hasNextPage: false });
1546
-
1547
- let result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1548
- expect(result).toHaveLength(2);
1549
- expect(result[0].balance).toBe("800");
1550
- expect(result[1].balance).toBe("500");
1551
-
1552
- const mixedCoins = createMockCoins(["200", "800", "400"]);
1553
- mixedCoins.unshift(createMockCoins(["0"])[0]);
1554
- mixedCoins.splice(2, 0, createMockCoins(["0"])[0]);
1555
-
1556
- mockApi.getCoins.mockResolvedValueOnce({ data: mixedCoins, hasNextPage: false });
1557
-
1558
- result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1559
- expect(result).toHaveLength(2);
1560
- expect(result[0].balance).toBe("800");
1561
- expect(result[1].balance).toBe("400");
1562
- expect(result.every(coin => parseInt(coin.balance) > 0)).toBe(true);
1563
- });
1564
-
1565
- test("handles all zero balance coins", async () => {
1566
- const mockCoins = createMockCoins(["0", "0", "0"]);
1567
- mockApi.getCoins.mockResolvedValueOnce({ data: mockCoins, hasNextPage: false });
1568
-
1569
- const result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1570
-
1571
- expect(result).toHaveLength(0);
1572
- expect(result).toEqual([]);
1573
- });
1574
- });
1575
-
1576
- describe("pagination", () => {
1577
- test("handles single page scenarios", async () => {
1578
- const mockCoins = createMockCoins(["800", "400", "300"]);
1579
- mockApi.getCoins.mockResolvedValueOnce({
1580
- data: mockCoins,
1581
- hasNextPage: true,
1582
- nextCursor: "cursor1",
1583
- });
1584
-
1585
- const result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1586
-
1587
- expect(result).toHaveLength(2);
1588
- expect(result[0].balance).toBe("800");
1589
- expect(result[1].balance).toBe("400");
1590
- expect(mockApi.getCoins).toHaveBeenCalledTimes(1);
1591
- });
1592
-
1593
- test("handles multi-page scenarios", async () => {
1594
- const firstPageCoins = createMockCoins(["300", "200"]);
1595
- const secondPageCoins = createMockCoins(["600", "400", "100"]);
1596
-
1597
- mockApi.getCoins
1598
- .mockResolvedValueOnce({
1599
- data: firstPageCoins,
1600
- hasNextPage: true,
1601
- nextCursor: "cursor1",
1602
- })
1603
- .mockResolvedValueOnce({
1604
- data: secondPageCoins,
1605
- hasNextPage: false,
1606
- });
1607
-
1608
- const result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1609
-
1610
- expect(result).toHaveLength(3);
1611
- expect(result[0].balance).toBe("300");
1612
- expect(result[1].balance).toBe("200");
1613
- expect(result[2].balance).toBe("600");
1614
- expect(mockApi.getCoins).toHaveBeenCalledTimes(2);
1615
- });
1616
-
1617
- test("handles insufficient funds across pages", async () => {
1618
- const firstPageCoins = createMockCoins(["300", "200"]);
1619
- const secondPageCoins = createMockCoins(["200", "100"]);
1620
-
1621
- mockApi.getCoins
1622
- .mockResolvedValueOnce({
1623
- data: firstPageCoins,
1624
- hasNextPage: true,
1625
- nextCursor: "cursor1",
1626
- })
1627
- .mockResolvedValueOnce({
1628
- data: secondPageCoins,
1629
- hasNextPage: false,
1630
- });
1631
-
1632
- const result = await sdk.getCoinsForAmount(mockApi, mockAddress, mockCoinType, 1000);
1633
-
1634
- expect(result).toHaveLength(4);
1635
- expect(result[0].balance).toBe("300");
1636
- expect(result[1].balance).toBe("200");
1637
- expect(result[2].balance).toBe("200");
1638
- expect(result[3].balance).toBe("100");
1639
- expect(mockApi.getCoins).toHaveBeenCalledTimes(2);
1640
- });
1641
- });
1642
- });
@@ -1,18 +1,18 @@
1
1
  import {
2
- BalanceChange,
3
2
  Checkpoint,
4
3
  ExecuteTransactionBlockParams,
5
4
  PaginatedTransactionResponse,
6
- QueryTransactionBlocksParams,
7
5
  SuiCallArg,
8
6
  SuiClient,
9
- SuiHTTPTransport,
10
7
  SuiTransactionBlockResponse,
8
+ TransactionBlockData,
9
+ SuiHTTPTransport,
10
+ TransactionEffects,
11
+ QueryTransactionBlocksParams,
12
+ BalanceChange,
11
13
  SuiTransactionBlockResponseOptions,
12
14
  DelegatedStake,
13
15
  StakeObject,
14
- TransactionBlockData,
15
- TransactionEffects,
16
16
  } from "@mysten/sui/client";
17
17
  import { Transaction } from "@mysten/sui/transactions";
18
18
  import { BigNumber } from "bignumber.js";
@@ -481,79 +481,75 @@ const getTotalGasUsed = (effects?: TransactionEffects | null): bigint => {
481
481
  );
482
482
  };
483
483
 
484
- /**
485
- * Get coins for a given address and coin type, stopping when we have enough to cover the amount.
486
- * Returns the minimum coins needed to cover the required amount.
487
- */
488
- export const getCoinsForAmount = async (
489
- api: SuiClient,
490
- address: string,
491
- coinType: string,
492
- requiredAmount: number,
493
- ) => {
494
- const coins = [];
495
- let cursor = null;
496
- let hasNextPage = true;
497
- let totalBalance = 0;
498
-
499
- while (hasNextPage && totalBalance < requiredAmount) {
500
- const response = await api.getCoins({
501
- owner: address,
502
- coinType,
503
- cursor,
504
- });
484
+ const FALLBACK_GAS_BUDGET = {
485
+ SUI_TRANSFER: "3976000",
486
+ TOKEN_TRANSFER: "4461792",
487
+ };
505
488
 
506
- // Filter out zero-balance coins and sort by balance (largest first)
507
- const validCoins = response.data
508
- .filter(coin => parseInt(coin.balance) > 0)
509
- .sort((a, b) => parseInt(b.balance) - parseInt(a.balance));
510
-
511
- let currentBalance = totalBalance;
512
- let i = 0;
513
- while (i < validCoins.length && currentBalance < requiredAmount) {
514
- const coin = validCoins[i];
515
- coins.push(coin);
516
- currentBalance += parseInt(coin.balance);
517
- i++;
489
+ export const paymentInfo = async (sender: string, fakeTransaction: TransactionType) =>
490
+ withApi(async api => {
491
+ const tx = new Transaction();
492
+ tx.setSender(ensureAddressFormat(sender));
493
+ const coinObjects = await getCoinObjectIds(sender, fakeTransaction);
494
+
495
+ const [coin] = tx.splitCoins(Array.isArray(coinObjects) ? coinObjects[0] : tx.gas, [
496
+ fakeTransaction.amount.toNumber(),
497
+ ]);
498
+ tx.transferObjects([coin], fakeTransaction.recipient);
499
+
500
+ try {
501
+ const txb = await tx.build({ client: api });
502
+ const dryRunTxResponse = await api.dryRunTransactionBlock({ transactionBlock: txb });
503
+ const fees = getTotalGasUsed(dryRunTxResponse.effects);
504
+
505
+ return {
506
+ gasBudget: dryRunTxResponse.input.gasData.budget,
507
+ totalGasUsed: fees,
508
+ fees,
509
+ };
510
+ } catch (error) {
511
+ console.warn("Fee estimation failed:", error);
512
+ // If dry run fails return a reasonable default gas budget as fallback
513
+ return {
514
+ gasBudget: Array.isArray(coinObjects)
515
+ ? FALLBACK_GAS_BUDGET.TOKEN_TRANSFER
516
+ : FALLBACK_GAS_BUDGET.SUI_TRANSFER,
517
+ totalGasUsed: BigInt(1000000),
518
+ fees: BigInt(1000000),
519
+ };
518
520
  }
519
- totalBalance = currentBalance;
521
+ });
520
522
 
521
- cursor = response.nextCursor;
522
- hasNextPage = response.hasNextPage && totalBalance < requiredAmount;
523
- }
523
+ export const getCoinObjectIds = async (
524
+ address: string,
525
+ transaction: CreateExtrinsicArg | TransactionType,
526
+ ) =>
527
+ withApi(async api => {
528
+ const coinObjectId = null;
524
529
 
525
- return coins;
526
- };
530
+ if (transaction.coinType !== DEFAULT_COIN_TYPE) {
531
+ const tokenInfo = await api.getCoins({
532
+ owner: address,
533
+ coinType: transaction.coinType,
534
+ });
535
+ return tokenInfo.data.map(coin => coin.coinObjectId);
536
+ }
537
+ return coinObjectId;
538
+ });
527
539
 
528
- /**
529
- * Creates a Sui transaction block for transferring coins.
530
- *
531
- * @param address - The sender's address
532
- * @param transaction - The transaction details including recipient, amount, and coin type
533
- * @returns Promise<TransactionBlock> - A built transaction block ready for execution
534
- *
535
- */
536
540
  export const createTransaction = async (address: string, transaction: CreateExtrinsicArg) =>
537
541
  withApi(async api => {
538
542
  const tx = new Transaction();
539
543
  tx.setSender(ensureAddressFormat(address));
540
544
 
541
- if (transaction.coinType !== DEFAULT_COIN_TYPE) {
542
- const requiredAmount = transaction.amount.toNumber();
543
-
544
- const coins = await getCoinsForAmount(api, address, transaction.coinType, requiredAmount);
545
+ const coinObjects = await getCoinObjectIds(address, transaction);
545
546
 
546
- if (coins.length === 0) {
547
- throw new Error(`No coins found for type ${transaction.coinType}`);
547
+ if (Array.isArray(coinObjects) && transaction.coinType !== DEFAULT_COIN_TYPE) {
548
+ const coins = coinObjects.map(coinId => tx.object(coinId));
549
+ if (coins.length > 1) {
550
+ tx.mergeCoins(coins[0], coins.slice(1));
548
551
  }
549
-
550
- const coinObjects = coins.map(coin => tx.object(coin.coinObjectId));
551
-
552
- if (coinObjects.length > 1) {
553
- tx.mergeCoins(coinObjects[0], coinObjects.slice(1));
554
- }
555
-
556
- const [coin] = tx.splitCoins(coinObjects[0], [transaction.amount.toNumber()]);
552
+ const [coin] = tx.splitCoins(coins[0], [transaction.amount.toNumber()]);
557
553
  tx.transferObjects([coin], transaction.recipient);
558
554
  } else {
559
555
  const [coin] = tx.splitCoins(tx.gas, [transaction.amount.toNumber()]);
@@ -563,22 +559,6 @@ export const createTransaction = async (address: string, transaction: CreateExtr
563
559
  return tx.build({ client: api });
564
560
  });
565
561
 
566
- /**
567
- * Performs a dry run of a transaction to estimate gas costs and fees
568
- */
569
- export const paymentInfo = async (sender: string, fakeTransaction: TransactionType) =>
570
- withApi(async api => {
571
- const txb = await createTransaction(sender, fakeTransaction);
572
- const dryRunTxResponse = await api.dryRunTransactionBlock({ transactionBlock: txb });
573
- const fees = getTotalGasUsed(dryRunTxResponse.effects);
574
-
575
- return {
576
- gasBudget: dryRunTxResponse.input.gasData.budget,
577
- totalGasUsed: fees,
578
- fees,
579
- };
580
- });
581
-
582
562
  export const executeTransactionBlock = async (params: ExecuteTransactionBlockParams) =>
583
563
  withApi(async api => {
584
564
  return api.executeTransactionBlock(params);