@ledgerhq/coin-ton 0.7.0 → 0.8.0-next.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 (126) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/jest.config.js +1 -0
  3. package/lib/__tests__/fixtures/common.fixtures.d.ts +3 -4
  4. package/lib/__tests__/fixtures/common.fixtures.d.ts.map +1 -1
  5. package/lib/__tests__/fixtures/common.fixtures.js +9 -1
  6. package/lib/__tests__/fixtures/common.fixtures.js.map +1 -1
  7. package/lib/__tests__/integration/bridge.integration.test.d.ts.map +1 -1
  8. package/lib/__tests__/integration/bridge.integration.test.js +2 -2
  9. package/lib/__tests__/integration/bridge.integration.test.js.map +1 -1
  10. package/lib/__tests__/unit/api.unit.test.js +1 -0
  11. package/lib/__tests__/unit/api.unit.test.js.map +1 -1
  12. package/lib/__tests__/unit/deviceTransactionConfig.unit.test.js +12 -3
  13. package/lib/__tests__/unit/deviceTransactionConfig.unit.test.js.map +1 -1
  14. package/lib/__tests__/unit/prepareTransaction.unit.test.js +38 -0
  15. package/lib/__tests__/unit/prepareTransaction.unit.test.js.map +1 -1
  16. package/lib/__tests__/unit/utils.unit.test.js +43 -1
  17. package/lib/__tests__/unit/utils.unit.test.js.map +1 -1
  18. package/lib/bridge/js.d.ts +3 -3
  19. package/lib/bridge/js.d.ts.map +1 -1
  20. package/lib/config.d.ts +2 -0
  21. package/lib/config.d.ts.map +1 -1
  22. package/lib/config.js.map +1 -1
  23. package/lib/constants.d.ts +18 -0
  24. package/lib/constants.d.ts.map +1 -1
  25. package/lib/constants.js +20 -1
  26. package/lib/constants.js.map +1 -1
  27. package/lib/deviceTransactionConfig.d.ts.map +1 -1
  28. package/lib/deviceTransactionConfig.js +14 -3
  29. package/lib/deviceTransactionConfig.js.map +1 -1
  30. package/lib/estimateMaxSpendable.d.ts +3 -3
  31. package/lib/estimateMaxSpendable.d.ts.map +1 -1
  32. package/lib/estimateMaxSpendable.js +6 -5
  33. package/lib/estimateMaxSpendable.js.map +1 -1
  34. package/lib/getTransactionStatus.d.ts +3 -3
  35. package/lib/getTransactionStatus.d.ts.map +1 -1
  36. package/lib/getTransactionStatus.js +2 -2
  37. package/lib/getTransactionStatus.js.map +1 -1
  38. package/lib/prepareTransaction.d.ts +3 -3
  39. package/lib/prepareTransaction.d.ts.map +1 -1
  40. package/lib/prepareTransaction.js +1 -2
  41. package/lib/prepareTransaction.js.map +1 -1
  42. package/lib/signOperation.d.ts +4 -4
  43. package/lib/signOperation.d.ts.map +1 -1
  44. package/lib/signOperation.js.map +1 -1
  45. package/lib/synchronisation.d.ts +3 -3
  46. package/lib/synchronisation.d.ts.map +1 -1
  47. package/lib/synchronisation.js +4 -3
  48. package/lib/synchronisation.js.map +1 -1
  49. package/lib/types.d.ts +79 -2
  50. package/lib/types.d.ts.map +1 -1
  51. package/lib/utils.d.ts +8 -4
  52. package/lib/utils.d.ts.map +1 -1
  53. package/lib/utils.js +30 -5
  54. package/lib/utils.js.map +1 -1
  55. package/lib-es/__tests__/fixtures/common.fixtures.d.ts +3 -4
  56. package/lib-es/__tests__/fixtures/common.fixtures.d.ts.map +1 -1
  57. package/lib-es/__tests__/fixtures/common.fixtures.js +9 -1
  58. package/lib-es/__tests__/fixtures/common.fixtures.js.map +1 -1
  59. package/lib-es/__tests__/integration/bridge.integration.test.d.ts.map +1 -1
  60. package/lib-es/__tests__/integration/bridge.integration.test.js +1 -1
  61. package/lib-es/__tests__/integration/bridge.integration.test.js.map +1 -1
  62. package/lib-es/__tests__/unit/api.unit.test.js +1 -0
  63. package/lib-es/__tests__/unit/api.unit.test.js.map +1 -1
  64. package/lib-es/__tests__/unit/deviceTransactionConfig.unit.test.js +12 -3
  65. package/lib-es/__tests__/unit/deviceTransactionConfig.unit.test.js.map +1 -1
  66. package/lib-es/__tests__/unit/prepareTransaction.unit.test.js +15 -0
  67. package/lib-es/__tests__/unit/prepareTransaction.unit.test.js.map +1 -1
  68. package/lib-es/__tests__/unit/utils.unit.test.js +20 -1
  69. package/lib-es/__tests__/unit/utils.unit.test.js.map +1 -1
  70. package/lib-es/bridge/js.d.ts +3 -3
  71. package/lib-es/bridge/js.d.ts.map +1 -1
  72. package/lib-es/bridge/js.js +1 -1
  73. package/lib-es/bridge/js.js.map +1 -1
  74. package/lib-es/config.d.ts +2 -0
  75. package/lib-es/config.d.ts.map +1 -1
  76. package/lib-es/config.js.map +1 -1
  77. package/lib-es/constants.d.ts +18 -0
  78. package/lib-es/constants.d.ts.map +1 -1
  79. package/lib-es/constants.js +19 -0
  80. package/lib-es/constants.js.map +1 -1
  81. package/lib-es/deviceTransactionConfig.d.ts.map +1 -1
  82. package/lib-es/deviceTransactionConfig.js +15 -4
  83. package/lib-es/deviceTransactionConfig.js.map +1 -1
  84. package/lib-es/estimateMaxSpendable.d.ts +3 -3
  85. package/lib-es/estimateMaxSpendable.d.ts.map +1 -1
  86. package/lib-es/estimateMaxSpendable.js +7 -6
  87. package/lib-es/estimateMaxSpendable.js.map +1 -1
  88. package/lib-es/getTransactionStatus.d.ts +3 -3
  89. package/lib-es/getTransactionStatus.d.ts.map +1 -1
  90. package/lib-es/getTransactionStatus.js +2 -2
  91. package/lib-es/getTransactionStatus.js.map +1 -1
  92. package/lib-es/prepareTransaction.d.ts +3 -3
  93. package/lib-es/prepareTransaction.d.ts.map +1 -1
  94. package/lib-es/prepareTransaction.js +1 -2
  95. package/lib-es/prepareTransaction.js.map +1 -1
  96. package/lib-es/signOperation.d.ts +4 -4
  97. package/lib-es/signOperation.d.ts.map +1 -1
  98. package/lib-es/signOperation.js.map +1 -1
  99. package/lib-es/synchronisation.d.ts +3 -3
  100. package/lib-es/synchronisation.d.ts.map +1 -1
  101. package/lib-es/synchronisation.js +4 -3
  102. package/lib-es/synchronisation.js.map +1 -1
  103. package/lib-es/types.d.ts +79 -2
  104. package/lib-es/types.d.ts.map +1 -1
  105. package/lib-es/utils.d.ts +8 -4
  106. package/lib-es/utils.d.ts.map +1 -1
  107. package/lib-es/utils.js +29 -5
  108. package/lib-es/utils.js.map +1 -1
  109. package/package.json +8 -8
  110. package/src/__tests__/fixtures/common.fixtures.ts +12 -5
  111. package/src/__tests__/integration/bridge.integration.test.ts +3 -3
  112. package/src/__tests__/unit/api.unit.test.ts +1 -0
  113. package/src/__tests__/unit/deviceTransactionConfig.unit.test.ts +20 -3
  114. package/src/__tests__/unit/prepareTransaction.unit.test.ts +17 -0
  115. package/src/__tests__/unit/utils.unit.test.ts +22 -1
  116. package/src/bridge/js.ts +3 -3
  117. package/src/config.ts +2 -0
  118. package/src/constants.ts +20 -0
  119. package/src/deviceTransactionConfig.ts +23 -4
  120. package/src/estimateMaxSpendable.ts +13 -17
  121. package/src/getTransactionStatus.ts +9 -9
  122. package/src/prepareTransaction.ts +5 -6
  123. package/src/signOperation.ts +6 -4
  124. package/src/synchronisation.ts +17 -12
  125. package/src/types.ts +103 -1
  126. package/src/utils.ts +39 -8
@@ -1,4 +1,5 @@
1
1
  import { Address, toNano } from "@ton/core";
2
+ import { setCoinConfig } from "../../config";
2
3
  import {
3
4
  TOKEN_TRANSFER_FORWARD_AMOUNT,
4
5
  TOKEN_TRANSFER_MAX_FEE,
@@ -13,6 +14,7 @@ import {
13
14
  getTransferExpirationTime,
14
15
  isAddressValid,
15
16
  } from "../../utils";
17
+ import mockServer, { API_TON_ENDPOINT } from "../fixtures/api.fixtures";
16
18
  import {
17
19
  account,
18
20
  transaction as baseTransaction,
@@ -90,6 +92,7 @@ describe("TON transfers", () => {
90
92
  customPayload: null,
91
93
  forwardAmount: BigInt(0),
92
94
  forwardPayload: null,
95
+ knownJetton: null,
93
96
  };
94
97
 
95
98
  const nftPayload: TonPayloadFormat = {
@@ -139,6 +142,23 @@ describe("Get TON paths", () => {
139
142
  });
140
143
 
141
144
  describe("Build TON transaction", () => {
145
+ beforeAll(() => {
146
+ setCoinConfig(() => ({
147
+ status: {
148
+ type: "active",
149
+ },
150
+ infra: {
151
+ API_TON_ENDPOINT: API_TON_ENDPOINT,
152
+ KNOWN_JETTONS: [],
153
+ },
154
+ }));
155
+ mockServer.listen();
156
+ });
157
+
158
+ afterAll(() => {
159
+ mockServer.close();
160
+ });
161
+
142
162
  const seqno = 22;
143
163
 
144
164
  test("Build TON transaction with an specific amount", () => {
@@ -184,7 +204,7 @@ describe("Build TON transaction", () => {
184
204
  to: jettonTransfer.to.toString(),
185
205
  payload: undefined,
186
206
  }).toStrictEqual({
187
- to: Address.parse(account.subAccounts?.[0].token.contractAddress ?? "").toString(),
207
+ to: Address.parse(account.subAccounts?.[0].jettonWallet ?? "").toString(),
188
208
  seqno,
189
209
  amount: toNano(TOKEN_TRANSFER_MAX_FEE),
190
210
  bounce: true,
@@ -213,6 +233,7 @@ describe("Build TON transaction", () => {
213
233
  customPayload: null,
214
234
  forwardAmount: TOKEN_TRANSFER_FORWARD_AMOUNT.toString(),
215
235
  forwardPayload: null,
236
+ knownJetton: null,
216
237
  });
217
238
  });
218
239
  });
package/src/bridge/js.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper";
2
2
  import {
3
3
  getSerializedAddressParameters,
4
- updateTransaction,
5
4
  makeAccountBridgeReceive,
6
5
  makeScanAccounts,
6
+ updateTransaction,
7
7
  } from "@ledgerhq/coin-framework/bridge/jsHelpers";
8
8
  import { SignerContext } from "@ledgerhq/coin-framework/signer";
9
9
 
@@ -18,7 +18,7 @@ import prepareTransaction from "../prepareTransaction";
18
18
  import { buildSignOperation } from "../signOperation";
19
19
  import { TonSigner } from "../signer";
20
20
  import { getAccountShape, sync } from "../synchronisation";
21
- import type { Transaction } from "../types";
21
+ import type { TonAccount, Transaction } from "../types";
22
22
 
23
23
  export function buildCurrencyBridge(signerContext: SignerContext<TonSigner>): CurrencyBridge {
24
24
  const getAddress = resolver(signerContext);
@@ -37,7 +37,7 @@ export function buildCurrencyBridge(signerContext: SignerContext<TonSigner>): Cu
37
37
 
38
38
  export function buildAccountBridge(
39
39
  signerContext: SignerContext<TonSigner>,
40
- ): AccountBridge<Transaction> {
40
+ ): AccountBridge<Transaction, TonAccount> {
41
41
  const getAddress = resolver(signerContext);
42
42
 
43
43
  const receive = makeAccountBridgeReceive(getAddressWrapper(getAddress));
package/src/config.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { CurrencyConfig } from "@ledgerhq/coin-framework/config";
2
+ import { KnownJetton } from "./types";
2
3
 
3
4
  export type TonCoinConfig = () => CurrencyConfig & {
4
5
  infra: {
5
6
  API_TON_ENDPOINT: string;
7
+ KNOWN_JETTONS: KnownJetton[];
6
8
  };
7
9
  };
8
10
 
package/src/constants.ts CHANGED
@@ -26,6 +26,10 @@ export const TOKEN_TRANSFER_QUERY_ID = 0;
26
26
  */
27
27
  export const MAX_COMMENT_BYTES = 120;
28
28
 
29
+ /**
30
+ * Operation codes for Jetton (TON token standard) transactions.
31
+ * These codes are used to identify different types of token operations.
32
+ */
29
33
  export enum JettonOpCode {
30
34
  Transfer = 0xf8a7ea5,
31
35
  TransferNotification = 0x7362d09c,
@@ -34,3 +38,19 @@ export enum JettonOpCode {
34
38
  Burn = 0x595f07bc,
35
39
  BurnNotification = 0x7bdd97de,
36
40
  }
41
+
42
+ /**
43
+ * TON blockchain workchain identifiers.
44
+ * MasterChain (-1) is used for validator operations and governance.
45
+ * BaseChain (0) is the main workchain used for regular accounts and smart contracts.
46
+ */
47
+ export enum Workchain {
48
+ MasterChain = -1,
49
+ BaseChain = 0,
50
+ }
51
+
52
+ /**
53
+ * Default workchain used for TON addresses.
54
+ * BaseChain (0) is the standard workchain for user accounts and transactions.
55
+ */
56
+ export const WORKCHAIN = Workchain.BaseChain;
@@ -1,6 +1,9 @@
1
- import { isTokenAccount } from "@ledgerhq/coin-framework/account/index";
1
+ import { getAccountCurrency, isTokenAccount } from "@ledgerhq/coin-framework/account/index";
2
+ import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
2
3
  import { CommonDeviceTransactionField as DeviceTransactionField } from "@ledgerhq/coin-framework/transaction/common";
3
4
  import type { Account, AccountLike } from "@ledgerhq/types-live";
5
+ import { toNano } from "@ton/core";
6
+ import { BigNumber } from "bignumber.js";
4
7
  import { TOKEN_TRANSFER_MAX_FEE } from "./constants";
5
8
  import type { Transaction, TransactionStatus } from "./types";
6
9
 
@@ -22,13 +25,29 @@ function getDeviceTransactionConfig(input: {
22
25
  if (tokenTransfer) {
23
26
  fields.push({
24
27
  type: "text",
25
- label: "Jetton units",
26
- value: input.transaction.amount.toString(),
28
+ label: "Jetton amount",
29
+ value: formatCurrencyUnit(
30
+ getAccountCurrency(input.account).units[0],
31
+ input.transaction.amount,
32
+ {
33
+ showCode: true,
34
+ disableRounding: true,
35
+ },
36
+ ),
27
37
  });
28
38
  fields.push({
29
39
  type: "text",
30
40
  label: "Amount",
31
- value: TOKEN_TRANSFER_MAX_FEE,
41
+ value: input.parentAccount
42
+ ? formatCurrencyUnit(
43
+ getAccountCurrency(input.parentAccount).units[0],
44
+ BigNumber(toNano(TOKEN_TRANSFER_MAX_FEE).toString()),
45
+ {
46
+ showCode: true,
47
+ disableRounding: true,
48
+ },
49
+ )
50
+ : TOKEN_TRANSFER_MAX_FEE,
32
51
  });
33
52
  } else {
34
53
  if (input.transaction.useAllAmount) {
@@ -1,15 +1,14 @@
1
- import {
2
- findSubAccountById,
3
- getMainAccount,
4
- isTokenAccount,
5
- } from "@ledgerhq/coin-framework/account/index";
1
+ import { getMainAccount, isTokenAccount } from "@ledgerhq/coin-framework/account/index";
6
2
  import type { Account, AccountBridge, AccountLike, TokenAccount } from "@ledgerhq/types-live";
7
3
  import { BigNumber } from "bignumber.js";
8
4
  import { fetchAccountInfo } from "./bridge/bridgeHelpers/api";
9
- import type { Transaction } from "./types";
10
- import { buildTonTransaction, getTonEstimatedFees } from "./utils";
5
+ import type { TonAccount, Transaction } from "./types";
6
+ import { buildTonTransaction, findSubAccountById, getTonEstimatedFees } from "./utils";
11
7
 
12
- const estimateMaxSpendable: AccountBridge<Transaction, Account>["estimateMaxSpendable"] = async ({
8
+ const estimateMaxSpendable: AccountBridge<
9
+ Transaction,
10
+ TonAccount
11
+ >["estimateMaxSpendable"] = async ({
13
12
  account,
14
13
  parentAccount,
15
14
  transaction,
@@ -18,7 +17,7 @@ const estimateMaxSpendable: AccountBridge<Transaction, Account>["estimateMaxSpen
18
17
  parentAccount?: Account | null | undefined;
19
18
  transaction?: Transaction | null | undefined;
20
19
  }): Promise<BigNumber> => {
21
- const mainAccount = getMainAccount(account, parentAccount);
20
+ const mainAccount = getMainAccount(account, parentAccount) as TonAccount;
22
21
  let balance = mainAccount.spendableBalance;
23
22
 
24
23
  if (balance.eq(0)) return balance;
@@ -45,14 +44,11 @@ const estimateMaxSpendable: AccountBridge<Transaction, Account>["estimateMaxSpen
45
44
  return subAccount.spendableBalance;
46
45
  }
47
46
 
48
- const estimatedFees = transaction
49
- ? transaction.fees ??
50
- (await getTonEstimatedFees(
51
- mainAccount,
52
- accountInfo.status === "uninit",
53
- buildTonTransaction(transaction, accountInfo.seqno, mainAccount),
54
- ))
55
- : BigNumber(0);
47
+ let estimatedFees = BigNumber(0);
48
+ if (transaction) {
49
+ const tonTx = buildTonTransaction(transaction, accountInfo.seqno, mainAccount);
50
+ estimatedFees = await getTonEstimatedFees(mainAccount, accountInfo.status === "uninit", tonTx);
51
+ }
56
52
 
57
53
  if (balance.lte(estimatedFees)) return new BigNumber(0);
58
54
 
@@ -1,4 +1,4 @@
1
- import { findSubAccountById, isTokenAccount } from "@ledgerhq/coin-framework/account/index";
1
+ import { isTokenAccount } from "@ledgerhq/coin-framework/account/index";
2
2
  import {
3
3
  AmountRequired,
4
4
  InvalidAddress,
@@ -6,7 +6,7 @@ import {
6
6
  NotEnoughBalance,
7
7
  RecipientRequired,
8
8
  } from "@ledgerhq/errors";
9
- import { Account, AccountBridge } from "@ledgerhq/types-live";
9
+ import { AccountBridge } from "@ledgerhq/types-live";
10
10
  import { toNano } from "@ton/core";
11
11
  import BigNumber from "bignumber.js";
12
12
  import { MINIMUM_REQUIRED_BALANCE, TOKEN_TRANSFER_MAX_FEE } from "./constants";
@@ -16,8 +16,8 @@ import {
16
16
  TonMinimumRequired,
17
17
  TonNotEnoughBalanceInParentAccount,
18
18
  } from "./errors";
19
- import { Transaction, TransactionStatus } from "./types";
20
- import { addressesAreEqual, commentIsValid, isAddressValid } from "./utils";
19
+ import { TonAccount, Transaction, TransactionStatus } from "./types";
20
+ import { addressesAreEqual, commentIsValid, findSubAccountById, isAddressValid } from "./utils";
21
21
 
22
22
  type ValidatedTransactionFields = "recipient" | "sender" | "amount" | "transaction";
23
23
  type ValidationIssues = Partial<Record<ValidatedTransactionFields, Error>>;
@@ -25,7 +25,7 @@ type ValidationIssues = Partial<Record<ValidatedTransactionFields, Error>>;
25
25
  /**
26
26
  * Validate an address for account transaction
27
27
  */
28
- const validateRecipient = (account: Account, tx: Transaction): Array<ValidationIssues> => {
28
+ const validateRecipient = (account: TonAccount, tx: Transaction): Array<ValidationIssues> => {
29
29
  const errors: ValidationIssues = {};
30
30
 
31
31
  if (tx.recipient) {
@@ -52,7 +52,7 @@ const validateRecipient = (account: Account, tx: Transaction): Array<ValidationI
52
52
  /**
53
53
  * Validate the sender address for account transaction
54
54
  */
55
- const validateSender = (account: Account): Array<ValidationIssues> => {
55
+ const validateSender = (account: TonAccount): Array<ValidationIssues> => {
56
56
  const errors: ValidationIssues = {};
57
57
 
58
58
  // Check if sender is matching the format of account valid ton address or not
@@ -68,7 +68,7 @@ const validateSender = (account: Account): Array<ValidationIssues> => {
68
68
  };
69
69
 
70
70
  const validateAmount = (
71
- account: Account,
71
+ account: TonAccount,
72
72
  transaction: Transaction,
73
73
  totalSpent: BigNumber,
74
74
  ): Array<ValidationIssues> => {
@@ -120,10 +120,10 @@ const validateComment = (transaction: Transaction): Array<ValidationIssues> => {
120
120
 
121
121
  export const getTransactionStatus: AccountBridge<
122
122
  Transaction,
123
- Account,
123
+ TonAccount,
124
124
  TransactionStatus
125
125
  >["getTransactionStatus"] = async (
126
- account: Account,
126
+ account: TonAccount,
127
127
  transaction: Transaction,
128
128
  ): Promise<TransactionStatus> => {
129
129
  const subAccount = findSubAccountById(account, transaction.subAccountId ?? "");
@@ -1,12 +1,11 @@
1
- import { findSubAccountById } from "@ledgerhq/coin-framework/account/index";
2
1
  import { updateTransaction } from "@ledgerhq/coin-framework/bridge/jsHelpers";
3
- import { Account, AccountBridge } from "@ledgerhq/types-live";
2
+ import { AccountBridge } from "@ledgerhq/types-live";
4
3
  import { fetchAccountInfo } from "./bridge/bridgeHelpers/api";
5
- import type { Transaction } from "./types";
6
- import { buildTonTransaction, getTonEstimatedFees } from "./utils";
4
+ import type { TonAccount, Transaction } from "./types";
5
+ import { buildTonTransaction, findSubAccountById, getTonEstimatedFees } from "./utils";
7
6
 
8
- const prepareTransaction: AccountBridge<Transaction, Account>["prepareTransaction"] = async (
9
- account: Account,
7
+ const prepareTransaction: AccountBridge<Transaction, TonAccount>["prepareTransaction"] = async (
8
+ account: TonAccount,
10
9
  transaction: Transaction,
11
10
  ): Promise<Transaction> => {
12
11
  const accountInfo = await fetchAccountInfo(account.freshAddress);
@@ -8,7 +8,7 @@ import { Observable } from "rxjs";
8
8
  import { fetchAccountInfo } from "./bridge/bridgeHelpers/api";
9
9
  import { TOKEN_TRANSFER_MAX_FEE } from "./constants";
10
10
  import type { TonSigner } from "./signer";
11
- import type { TonCell, TonOperation, Transaction } from "./types";
11
+ import type { TonAccount, TonCell, TonOperation, Transaction } from "./types";
12
12
  import { buildTonTransaction, getLedgerTonPath } from "./utils";
13
13
 
14
14
  const packTransaction = (account: Account, needsInit: boolean, signature: TonCell): string => {
@@ -30,13 +30,15 @@ const packTransaction = (account: Account, needsInit: boolean, signature: TonCel
30
30
  * Sign Transaction with Ledger hardware
31
31
  */
32
32
  export const buildSignOperation =
33
- (signerContext: SignerContext<TonSigner>): AccountBridge<Transaction>["signOperation"] =>
33
+ (
34
+ signerContext: SignerContext<TonSigner>,
35
+ ): AccountBridge<Transaction, TonAccount>["signOperation"] =>
34
36
  ({
35
37
  account,
36
38
  transaction,
37
39
  deviceId,
38
40
  }: {
39
- account: Account;
41
+ account: TonAccount;
40
42
  transaction: Transaction;
41
43
  deviceId: DeviceId;
42
44
  }): Observable<SignOperationEvent> =>
@@ -87,7 +89,7 @@ export const buildSignOperation =
87
89
  });
88
90
 
89
91
  export const buildOptimisticOperation = (
90
- account: Account,
92
+ account: TonAccount,
91
93
  transaction: Transaction,
92
94
  ): TonOperation => {
93
95
  const { recipient, amount, fees, comment, useAllAmount, subAccountId } = transaction;
@@ -14,7 +14,6 @@ import {
14
14
  import { decodeOperationId } from "@ledgerhq/coin-framework/operation";
15
15
  import { log } from "@ledgerhq/logs";
16
16
  import { TokenCurrency } from "@ledgerhq/types-cryptoassets";
17
- import { Account, SubAccount } from "@ledgerhq/types-live";
18
17
  import BigNumber from "bignumber.js";
19
18
  import flatMap from "lodash/flatMap";
20
19
  import {
@@ -30,9 +29,12 @@ import {
30
29
  mapTxToOps,
31
30
  } from "./bridge/bridgeHelpers/txn";
32
31
  import { getSyncHash } from "./logic";
33
- import { TonOperation } from "./types";
32
+ import { TonAccount, TonOperation, TonSubAccount } from "./types";
34
33
 
35
- export const getAccountShape: GetAccountShape<Account> = async (info, { blacklistedTokenIds }) => {
34
+ export const getAccountShape: GetAccountShape<TonAccount> = async (
35
+ info,
36
+ { blacklistedTokenIds },
37
+ ) => {
36
38
  const { address, rest, currency, derivationMode, initialAccount } = info;
37
39
 
38
40
  const publicKey = reconciliatePubkey(rest?.publicKey, initialAccount);
@@ -85,6 +87,7 @@ export const getAccountShape: GetAccountShape<Account> = async (info, { blacklis
85
87
  mapJettonTxToOps(accountId, address, newTxs.address_book),
86
88
  );
87
89
  const operations = shouldSyncFromScratch ? newOps : mergeOps(oldOps, newOps);
90
+
88
91
  const subAccounts = await getSubAccounts(
89
92
  info,
90
93
  accountId,
@@ -103,7 +106,7 @@ export const getAccountShape: GetAccountShape<Account> = async (info, { blacklis
103
106
  blockHeight,
104
107
  xpub: publicKey,
105
108
  lastSyncDate: new Date(),
106
- } as Partial<Account>;
109
+ } as Partial<TonAccount>;
107
110
  return toReturn;
108
111
  };
109
112
 
@@ -113,14 +116,15 @@ const getSubAccountShape = async (
113
116
  token: TokenCurrency,
114
117
  ops: TonOperation[],
115
118
  shouldSyncFromScratch: boolean,
116
- ): Promise<Partial<SubAccount>> => {
117
- const tokenAccountId = encodeTokenAccountId(parentId, token);
119
+ ): Promise<Partial<TonSubAccount>> => {
118
120
  const walletsInfo = await fetchJettonWallets({
119
121
  address: info.address,
120
122
  jettonMaster: token.contractAddress,
121
123
  });
124
+
122
125
  if (walletsInfo.length !== 1) throw new Error("[ton] unexpected api response");
123
- const { balance, address: jettonWalletAddress } = walletsInfo[0];
126
+ const { balance, address: jettonWallet } = walletsInfo[0];
127
+ const tokenAccountId = encodeTokenAccountId(parentId, token);
124
128
  const oldOps = info.initialAccount?.subAccounts?.find(a => a.id === tokenAccountId)?.operations;
125
129
  const operations = !oldOps || shouldSyncFromScratch ? ops : mergeOps(oldOps, ops);
126
130
  const maybeExistingSubAccount =
@@ -132,7 +136,7 @@ const getSubAccountShape = async (
132
136
  type: "TokenAccount",
133
137
  id: tokenAccountId,
134
138
  parentId,
135
- token: { ...token, contractAddress: jettonWalletAddress }, // the contract address is replaced for the jetton wallet address, it will be use for the token transfer
139
+ token,
136
140
  balance: new BigNumber(balance),
137
141
  spendableBalance: new BigNumber(balance),
138
142
  operations,
@@ -141,6 +145,7 @@ const getSubAccountShape = async (
141
145
  creationDate: operations.length > 0 ? operations[operations.length - 1].date : new Date(),
142
146
  balanceHistoryCache: emptyHistoryCache, // calculated in the jsHelpers
143
147
  swapHistory: [],
148
+ jettonWallet, // Address of the jetton wallet contract that holds the token balance and handles transfers
144
149
  };
145
150
  };
146
151
 
@@ -150,7 +155,7 @@ async function getSubAccounts(
150
155
  newOps: TonOperation[],
151
156
  blacklistedTokenIds: string[] = [],
152
157
  shouldSyncFromScratch: boolean,
153
- ): Promise<Partial<SubAccount>[]> {
158
+ ): Promise<Partial<TonSubAccount>[]> {
154
159
  const opsPerToken = newOps.reduce((acc, op) => {
155
160
  const { accountId: tokenAccountId } = decodeOperationId(op.id);
156
161
  const { token } = decodeTokenAccountId(tokenAccountId);
@@ -159,7 +164,7 @@ async function getSubAccounts(
159
164
  acc.get(token)?.push(op);
160
165
  return acc;
161
166
  }, new Map<TokenCurrency, TonOperation[]>());
162
- const subAccountsPromises: Promise<Partial<SubAccount>>[] = [];
167
+ const subAccountsPromises: Promise<Partial<TonSubAccount>>[] = [];
163
168
  for (const [token, ops] of opsPerToken.entries()) {
164
169
  subAccountsPromises.push(
165
170
  getSubAccountShape(info, accountId, token, ops, shouldSyncFromScratch),
@@ -168,7 +173,7 @@ async function getSubAccounts(
168
173
  return Promise.all(subAccountsPromises);
169
174
  }
170
175
 
171
- const postSync = (initial: Account, synced: Account): Account => {
176
+ const postSync = (initial: TonAccount, synced: TonAccount): TonAccount => {
172
177
  // Set of ids from the already existing subAccount from previous sync
173
178
  const initialSubAccountsIds = new Set();
174
179
  for (const subAccount of initial.subAccounts || []) {
@@ -206,7 +211,7 @@ const postSync = (initial: Account, synced: Account): Account => {
206
211
  };
207
212
  };
208
213
 
209
- function reconciliatePubkey(publicKey?: string, initialAccount?: Account): string {
214
+ function reconciliatePubkey(publicKey?: string, initialAccount?: TonAccount): string {
210
215
  if (publicKey?.length === 64) return publicKey;
211
216
  if (initialAccount) {
212
217
  if (initialAccount.xpub?.length === 64) return initialAccount.xpub;
package/src/types.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import {
2
+ Account,
2
3
  Operation,
4
+ TokenAccount,
3
5
  TransactionCommon,
4
6
  TransactionCommonRaw,
5
7
  TransactionStatusCommon,
@@ -43,6 +45,10 @@ export type TonPayloadJettonTransfer = {
43
45
  customPayload: TonCell | null;
44
46
  forwardAmount: bigint;
45
47
  forwardPayload: TonCell | null;
48
+ knownJetton: {
49
+ jettonId: number;
50
+ workchain: number;
51
+ } | null;
46
52
  };
47
53
 
48
54
  export type TonPayloadNftTransfer = {
@@ -60,7 +66,91 @@ export type TonPayloadComment = {
60
66
  text: string;
61
67
  };
62
68
 
63
- export type TonPayloadFormat = TonPayloadComment | TonPayloadJettonTransfer | TonPayloadNftTransfer;
69
+ export type TonPayloadUnsafe = {
70
+ type: "unsafe";
71
+ message: TonCell;
72
+ };
73
+
74
+ export type TonPayloadJettonBurn = {
75
+ type: "jetton-burn";
76
+ queryId: bigint | null;
77
+ amount: bigint;
78
+ responseDestination: Address;
79
+ customPayload: TonCell | Buffer | null;
80
+ };
81
+
82
+ export type TonPayloadAddWhitelist = {
83
+ type: "add-whitelist";
84
+ queryId: bigint | null;
85
+ address: Address;
86
+ };
87
+
88
+ export type TonPayloadSingleNominatorWithdraw = {
89
+ type: "single-nominator-withdraw";
90
+ queryId: bigint | null;
91
+ amount: bigint;
92
+ };
93
+
94
+ export type TonPayloadSingleNominatorChangeValidator = {
95
+ type: "single-nominator-change-validator";
96
+ queryId: bigint | null;
97
+ address: Address;
98
+ };
99
+
100
+ export type TonPayloadTonStakersDeposit = {
101
+ type: "tonstakers-deposit";
102
+ queryId: bigint | null;
103
+ appId: bigint | null;
104
+ };
105
+
106
+ export type TonPayloadVoteForProposal = {
107
+ type: "vote-for-proposal";
108
+ queryId: bigint | null;
109
+ votingAddress: Address;
110
+ expirationDate: number;
111
+ vote: boolean;
112
+ needConfirmation: boolean;
113
+ };
114
+
115
+ export type TonPayloadChangeDnsRecord = {
116
+ type: "change-dns-record";
117
+ queryId: bigint | null;
118
+ record:
119
+ | {
120
+ type: "wallet";
121
+ value: {
122
+ address: Address;
123
+ capabilities: {
124
+ isWallet: boolean;
125
+ } | null;
126
+ } | null;
127
+ }
128
+ | {
129
+ type: "unknown";
130
+ key: Buffer;
131
+ value: TonCell | null;
132
+ };
133
+ };
134
+
135
+ export type TonPayloadTokenBridgePaySwap = {
136
+ type: "token-bridge-pay-swap";
137
+ queryId: bigint | null;
138
+ swapId: Buffer;
139
+ };
140
+
141
+ export type TonPayloadFormat =
142
+ | TonPayloadComment
143
+ | TonPayloadJettonTransfer
144
+ | TonPayloadNftTransfer
145
+ | TonPayloadUnsafe
146
+ | TonPayloadJettonBurn
147
+ | TonPayloadAddWhitelist
148
+ | TonPayloadSingleNominatorWithdraw
149
+ | TonPayloadSingleNominatorChangeValidator
150
+ | TonPayloadTonStakersDeposit
151
+ | TonPayloadVoteForProposal
152
+ | TonPayloadChangeDnsRecord
153
+ | TonPayloadTokenBridgePaySwap;
64
154
 
65
155
  export interface TonTransaction {
66
156
  to: Address;
@@ -80,3 +170,15 @@ export type TonOperationExtra = {
80
170
  lt: string;
81
171
  explorerHash: string;
82
172
  };
173
+
174
+ export type KnownJetton = {
175
+ symbol: string;
176
+ masterAddress: Address;
177
+ };
178
+
179
+ export type TonSubAccount = TokenAccount & {
180
+ jettonWallet: string;
181
+ };
182
+ export type TonAccount = Omit<Account, "subAccounts"> & {
183
+ subAccounts?: TonSubAccount[];
184
+ };