@ledgerhq/live-common 24.1.0 → 25.0.0-next.1

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 (235) hide show
  1. package/lib/apps/support.js +1 -1
  2. package/lib/apps/support.js.map +1 -1
  3. package/lib/bot/index.d.ts.map +1 -1
  4. package/lib/bot/index.js +4 -2
  5. package/lib/bot/index.js.map +1 -1
  6. package/lib/bot/specs.d.ts +1 -0
  7. package/lib/bot/specs.d.ts.map +1 -1
  8. package/lib/bot/specs.js +6 -1
  9. package/lib/bot/specs.js.map +1 -1
  10. package/lib/currencies/support.d.ts +1 -1
  11. package/lib/currencies/support.d.ts.map +1 -1
  12. package/lib/env.d.ts +15 -5
  13. package/lib/env.d.ts.map +1 -1
  14. package/lib/env.js +10 -0
  15. package/lib/env.js.map +1 -1
  16. package/lib/env.react.d.ts +1 -1
  17. package/lib/env.react.d.ts.map +1 -1
  18. package/lib/errors.d.ts +101 -306
  19. package/lib/errors.d.ts.map +1 -1
  20. package/lib/errors.js +3 -4
  21. package/lib/errors.js.map +1 -1
  22. package/lib/errors.test.d.ts +2 -0
  23. package/lib/errors.test.d.ts.map +1 -0
  24. package/lib/errors.test.js +20 -0
  25. package/lib/errors.test.js.map +1 -0
  26. package/lib/exchange/swap/utils/index.test.js +2 -2
  27. package/lib/exchange/swap/utils/index.test.js.map +1 -1
  28. package/lib/families/bitcoin/datasets/bitcoin.d.ts.map +1 -1
  29. package/lib/families/bitcoin/datasets/bitcoin.js +2 -4
  30. package/lib/families/bitcoin/datasets/bitcoin.js.map +1 -1
  31. package/lib/families/bitcoin/descriptor.d.ts.map +1 -1
  32. package/lib/families/bitcoin/descriptor.js +3 -1
  33. package/lib/families/bitcoin/descriptor.js.map +1 -1
  34. package/lib/families/bitcoin/errors.d.ts +1 -3
  35. package/lib/families/bitcoin/errors.d.ts.map +1 -1
  36. package/lib/families/bitcoin/exchange.js +1 -1
  37. package/lib/families/bitcoin/exchange.js.map +1 -1
  38. package/lib/families/bitcoin/hw-getAddress.d.ts +1 -1
  39. package/lib/families/bitcoin/hw-getAddress.d.ts.map +1 -1
  40. package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
  41. package/lib/families/bitcoin/js-synchronisation.js +20 -2
  42. package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
  43. package/lib/families/bitcoin/wallet-btc/storage/index.d.ts +0 -36
  44. package/lib/families/bitcoin/wallet-btc/storage/index.d.ts.map +1 -1
  45. package/lib/families/bitcoin/wallet-btc/storage/index.js +14 -14
  46. package/lib/families/bitcoin/wallet-btc/storage/index.js.map +1 -1
  47. package/lib/families/bitcoin/wallet-btc/storage/types.d.ts +1 -0
  48. package/lib/families/bitcoin/wallet-btc/storage/types.d.ts.map +1 -1
  49. package/lib/families/bitcoin/xpub.txs.loadExport.unit.test.d.ts +2 -0
  50. package/lib/families/bitcoin/xpub.txs.loadExport.unit.test.d.ts.map +1 -0
  51. package/lib/families/bitcoin/xpub.txs.loadExport.unit.test.js +146 -0
  52. package/lib/families/bitcoin/xpub.txs.loadExport.unit.test.js.map +1 -0
  53. package/lib/families/cardano/errors.d.ts +1 -3
  54. package/lib/families/cardano/errors.d.ts.map +1 -1
  55. package/lib/families/crypto_org/errors.d.ts +3 -9
  56. package/lib/families/crypto_org/errors.d.ts.map +1 -1
  57. package/lib/families/ethereum/exchange.js +1 -1
  58. package/lib/families/ethereum/exchange.js.map +1 -1
  59. package/lib/families/ethereum/modules/index.d.ts +1 -1
  60. package/lib/families/ethereum/modules/index.d.ts.map +1 -1
  61. package/lib/families/ethereum/signOperation.js +2 -2
  62. package/lib/families/ethereum/signOperation.js.map +1 -1
  63. package/lib/families/ethereum/specs.d.ts +1 -1
  64. package/lib/families/ethereum/specs.js +5 -5
  65. package/lib/families/ethereum/specs.js.map +1 -1
  66. package/lib/families/polkadot/errors.d.ts +12 -36
  67. package/lib/families/polkadot/errors.d.ts.map +1 -1
  68. package/lib/families/ripple/exchange.js +1 -1
  69. package/lib/families/ripple/exchange.js.map +1 -1
  70. package/lib/families/solana/api/chain/instruction/token/types.d.ts +3 -3
  71. package/lib/families/solana/errors.d.ts +20 -60
  72. package/lib/families/solana/errors.d.ts.map +1 -1
  73. package/lib/families/stellar/api/horizon.d.ts +29 -7
  74. package/lib/families/stellar/api/horizon.d.ts.map +1 -1
  75. package/lib/families/stellar/api/horizon.js +114 -58
  76. package/lib/families/stellar/api/horizon.js.map +1 -1
  77. package/lib/families/stellar/api/index.d.ts +1 -1
  78. package/lib/families/stellar/api/index.d.ts.map +1 -1
  79. package/lib/families/stellar/api/index.js +5 -1
  80. package/lib/families/stellar/api/index.js.map +1 -1
  81. package/lib/families/stellar/bridge/mock.d.ts.map +1 -1
  82. package/lib/families/stellar/bridge/mock.js +4 -1
  83. package/lib/families/stellar/bridge/mock.js.map +1 -1
  84. package/lib/families/stellar/bridge.integration.test.js +12 -4
  85. package/lib/families/stellar/bridge.integration.test.js.map +1 -1
  86. package/lib/families/stellar/cli-transaction.d.ts +3 -1
  87. package/lib/families/stellar/cli-transaction.d.ts.map +1 -1
  88. package/lib/families/stellar/cli-transaction.js +48 -2
  89. package/lib/families/stellar/cli-transaction.js.map +1 -1
  90. package/lib/families/stellar/deviceTransactionConfig.d.ts +7 -1
  91. package/lib/families/stellar/deviceTransactionConfig.d.ts.map +1 -1
  92. package/lib/families/stellar/deviceTransactionConfig.js +12 -1
  93. package/lib/families/stellar/deviceTransactionConfig.js.map +1 -1
  94. package/lib/families/stellar/errors.d.ts +12 -0
  95. package/lib/families/stellar/errors.d.ts.map +1 -0
  96. package/lib/families/stellar/errors.js +16 -0
  97. package/lib/families/stellar/errors.js.map +1 -0
  98. package/lib/families/stellar/exchange.js +1 -1
  99. package/lib/families/stellar/exchange.js.map +1 -1
  100. package/lib/families/stellar/js-buildTransaction.d.ts.map +1 -1
  101. package/lib/families/stellar/js-buildTransaction.js +34 -16
  102. package/lib/families/stellar/js-buildTransaction.js.map +1 -1
  103. package/lib/families/stellar/js-createTransaction.d.ts.map +1 -1
  104. package/lib/families/stellar/js-createTransaction.js +4 -1
  105. package/lib/families/stellar/js-createTransaction.js.map +1 -1
  106. package/lib/families/stellar/js-estimateMaxSpendable.js +2 -1
  107. package/lib/families/stellar/js-estimateMaxSpendable.js.map +1 -1
  108. package/lib/families/stellar/js-getTransactionStatus.d.ts.map +1 -1
  109. package/lib/families/stellar/js-getTransactionStatus.js +127 -57
  110. package/lib/families/stellar/js-getTransactionStatus.js.map +1 -1
  111. package/lib/families/stellar/js-prepareTransaction.d.ts.map +1 -1
  112. package/lib/families/stellar/js-prepareTransaction.js +25 -7
  113. package/lib/families/stellar/js-prepareTransaction.js.map +1 -1
  114. package/lib/families/stellar/js-signOperation.d.ts.map +1 -1
  115. package/lib/families/stellar/js-signOperation.js +33 -6
  116. package/lib/families/stellar/js-signOperation.js.map +1 -1
  117. package/lib/families/stellar/js-synchronization.d.ts +1 -1
  118. package/lib/families/stellar/js-synchronization.d.ts.map +1 -1
  119. package/lib/families/stellar/js-synchronization.js +42 -16
  120. package/lib/families/stellar/js-synchronization.js.map +1 -1
  121. package/lib/families/stellar/logic.d.ts +21 -14
  122. package/lib/families/stellar/logic.d.ts.map +1 -1
  123. package/lib/families/stellar/logic.js +158 -57
  124. package/lib/families/stellar/logic.js.map +1 -1
  125. package/lib/families/stellar/specs.d.ts.map +1 -1
  126. package/lib/families/stellar/specs.js +182 -7
  127. package/lib/families/stellar/specs.js.map +1 -1
  128. package/lib/families/stellar/speculos-deviceActions.d.ts.map +1 -1
  129. package/lib/families/stellar/speculos-deviceActions.js +60 -28
  130. package/lib/families/stellar/speculos-deviceActions.js.map +1 -1
  131. package/lib/families/stellar/tokens.d.ts +11 -0
  132. package/lib/families/stellar/tokens.d.ts.map +1 -0
  133. package/lib/families/stellar/tokens.js +82 -0
  134. package/lib/families/stellar/tokens.js.map +1 -0
  135. package/lib/families/stellar/transaction.d.ts +2 -2
  136. package/lib/families/stellar/transaction.d.ts.map +1 -1
  137. package/lib/families/stellar/transaction.js +32 -6
  138. package/lib/families/stellar/transaction.js.map +1 -1
  139. package/lib/families/stellar/types.d.ts +45 -0
  140. package/lib/families/stellar/types.d.ts.map +1 -1
  141. package/lib/families/stellar/types.js +7 -1
  142. package/lib/families/stellar/types.js.map +1 -1
  143. package/lib/families/tezos/exchange.js +1 -1
  144. package/lib/families/tezos/exchange.js.map +1 -1
  145. package/lib/generated/cli-transaction.d.ts +1 -0
  146. package/lib/generated/cli-transaction.d.ts.map +1 -1
  147. package/lib/generated/specs.d.ts +1 -1
  148. package/lib/generated/transaction.d.ts +1 -1
  149. package/lib/hw/signMessage/index.d.ts +4 -3
  150. package/lib/hw/signMessage/index.d.ts.map +1 -1
  151. package/lib/hw/signMessage/index.js +25 -11
  152. package/lib/hw/signMessage/index.js.map +1 -1
  153. package/lib/hw/signMessage/index.test.d.ts +2 -0
  154. package/lib/hw/signMessage/index.test.d.ts.map +1 -0
  155. package/lib/hw/signMessage/index.test.js +126 -0
  156. package/lib/hw/signMessage/index.test.js.map +1 -0
  157. package/lib/load/speculos.d.ts.map +1 -1
  158. package/lib/load/speculos.js +1 -3
  159. package/lib/load/speculos.js.map +1 -1
  160. package/lib/mock/Btc.d.ts +1 -1
  161. package/lib/mock/Btc.d.ts.map +1 -1
  162. package/lib/performance.d.ts +10 -0
  163. package/lib/performance.d.ts.map +1 -0
  164. package/lib/performance.js +72 -0
  165. package/lib/performance.js.map +1 -0
  166. package/lib/performance.test.d.ts +2 -0
  167. package/lib/performance.test.d.ts.map +1 -0
  168. package/lib/performance.test.js +28 -0
  169. package/lib/performance.test.js.map +1 -0
  170. package/lib/platform/providers/RemoteLiveAppProvider/api/index.d.ts.map +1 -1
  171. package/lib/platform/providers/RemoteLiveAppProvider/api/index.js +28 -0
  172. package/lib/platform/providers/RemoteLiveAppProvider/api/index.js.map +1 -1
  173. package/lib/platform/providers/RemoteLiveAppProvider/api/mock.json +0 -31
  174. package/lib/types/currencies.d.ts +1 -1
  175. package/lib/types/currencies.d.ts.map +1 -1
  176. package/lib/walletconnect/Provider.d.ts.map +1 -1
  177. package/lib/walletconnect/Provider.js +1 -3
  178. package/lib/walletconnect/Provider.js.map +1 -1
  179. package/package.json +71 -19
  180. package/src/apps/support.ts +1 -1
  181. package/src/bot/index.ts +3 -1
  182. package/src/bot/specs.ts +7 -1
  183. package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +34 -6
  184. package/src/currencies/support.ts +1 -1
  185. package/src/env.ts +10 -0
  186. package/src/errors.test.ts +21 -0
  187. package/src/errors.ts +1 -6
  188. package/src/exchange/swap/utils/index.test.ts +1 -1
  189. package/src/families/bitcoin/datasets/bitcoin.ts +0 -2
  190. package/src/families/bitcoin/descriptor.ts +4 -1
  191. package/src/families/bitcoin/exchange.ts +1 -1
  192. package/src/families/bitcoin/hw-getAddress.ts +1 -1
  193. package/src/families/bitcoin/js-synchronisation.ts +23 -1
  194. package/src/families/bitcoin/wallet-btc/storage/index.ts +17 -31
  195. package/src/families/bitcoin/wallet-btc/storage/types.ts +1 -0
  196. package/src/families/bitcoin/wallet-btc/wallet.ts +1 -1
  197. package/src/families/bitcoin/xpub.txs.loadExport.unit.test.ts +101 -0
  198. package/src/families/ethereum/exchange.ts +1 -1
  199. package/src/families/ethereum/modules/index.ts +1 -1
  200. package/src/families/ethereum/signOperation.ts +2 -2
  201. package/src/families/ethereum/specs.ts +5 -5
  202. package/src/families/ripple/exchange.ts +1 -1
  203. package/src/families/stellar/__snapshots__/bridge.integration.test.ts.snap +765 -339
  204. package/src/families/stellar/api/horizon.ts +115 -33
  205. package/src/families/stellar/api/index.ts +4 -0
  206. package/src/families/stellar/bridge/mock.ts +3 -0
  207. package/src/families/stellar/bridge.integration.test.ts +10 -2
  208. package/src/families/stellar/cli-transaction.ts +75 -2
  209. package/src/families/stellar/deviceTransactionConfig.ts +22 -0
  210. package/src/families/stellar/errors.ts +34 -0
  211. package/src/families/stellar/exchange.ts +1 -1
  212. package/src/families/stellar/js-buildTransaction.ts +56 -16
  213. package/src/families/stellar/js-createTransaction.ts +3 -0
  214. package/src/families/stellar/js-estimateMaxSpendable.ts +1 -1
  215. package/src/families/stellar/js-getTransactionStatus.ts +152 -60
  216. package/src/families/stellar/js-prepareTransaction.ts +4 -3
  217. package/src/families/stellar/js-signOperation.ts +37 -6
  218. package/src/families/stellar/js-synchronization.ts +53 -11
  219. package/src/families/stellar/logic.ts +198 -62
  220. package/src/families/stellar/specs.ts +227 -8
  221. package/src/families/stellar/speculos-deviceActions.ts +66 -22
  222. package/src/families/stellar/tokens.ts +118 -0
  223. package/src/families/stellar/transaction.ts +31 -3
  224. package/src/families/stellar/types.ts +52 -0
  225. package/src/families/tezos/exchange.ts +1 -1
  226. package/src/hw/signMessage/index.test.ts +128 -0
  227. package/src/hw/signMessage/index.ts +37 -15
  228. package/src/load/speculos.ts +1 -3
  229. package/src/mock/Btc.ts +1 -1
  230. package/src/performance.test.ts +26 -0
  231. package/src/performance.ts +61 -0
  232. package/src/platform/providers/RemoteLiveAppProvider/api/index.ts +6 -0
  233. package/src/platform/providers/RemoteLiveAppProvider/api/mock.json +1 -32
  234. package/src/types/currencies.ts +1 -1
  235. package/src/walletconnect/Provider.tsx +4 -5
@@ -1,19 +1,37 @@
1
1
  import { BigNumber } from "bignumber.js";
2
2
  import StellarSdk, { ServerApi } from "stellar-sdk";
3
+ import { findSubAccountById } from "../../account";
3
4
  import type { CacheRes } from "../../cache";
4
5
  import { makeLRUCache } from "../../cache";
5
- import type { Account, Operation, OperationType } from "../../types";
6
- import { fetchSigners, fetchBaseFee, loadAccount } from "./api";
6
+ import type {
7
+ Account,
8
+ Operation,
9
+ OperationType,
10
+ TokenAccount,
11
+ } from "../../types";
12
+ import {
13
+ fetchSigners,
14
+ fetchBaseFee,
15
+ loadAccount,
16
+ BASE_RESERVE,
17
+ BASE_RESERVE_MIN_COUNT,
18
+ } from "./api";
7
19
  import { getCryptoCurrencyById, parseCurrencyUnit } from "../../currencies";
8
20
  import { encodeOperationId } from "../../operation";
21
+ import type {
22
+ Transaction,
23
+ TransactionRaw,
24
+ BalanceAsset,
25
+ RawOperation,
26
+ } from "./types";
9
27
 
10
28
  const currency = getCryptoCurrencyById("stellar");
11
29
 
12
30
  const getMinimumBalance = (account: ServerApi.AccountRecord): BigNumber => {
13
- const baseReserve = 0.5;
14
- const numberOfEntries = account.subentry_count;
15
- const minimumBalance = (2 + numberOfEntries) * baseReserve;
16
- return parseCurrencyUnit(currency.units[0], minimumBalance.toString());
31
+ return parseCurrencyUnit(
32
+ currency.units[0],
33
+ getReservedBalance(account).toString()
34
+ );
17
35
  };
18
36
 
19
37
  export const getAccountSpendableBalance = async (
@@ -21,12 +39,69 @@ export const getAccountSpendableBalance = async (
21
39
  account: ServerApi.AccountRecord
22
40
  ): Promise<BigNumber> => {
23
41
  const minimumBalance = getMinimumBalance(account);
24
- const baseFee = await fetchBaseFee();
25
- return BigNumber.max(balance.minus(minimumBalance).minus(baseFee), 0);
42
+ const { recommendedFee } = await fetchBaseFee();
43
+ return BigNumber.max(balance.minus(minimumBalance).minus(recommendedFee), 0);
44
+ };
45
+
46
+ export const getAmountValue = (
47
+ account: Account,
48
+ transaction: Transaction,
49
+ fees: BigNumber
50
+ ): BigNumber => {
51
+ // Asset
52
+ if (transaction.subAccountId) {
53
+ const asset = findSubAccountById(
54
+ account,
55
+ transaction.subAccountId
56
+ ) as TokenAccount;
57
+ return transaction.useAllAmount
58
+ ? new BigNumber(asset.spendableBalance)
59
+ : transaction.amount;
60
+ }
61
+
62
+ // Native
63
+ return transaction.useAllAmount && transaction.networkInfo
64
+ ? BigNumber.max(account.spendableBalance.minus(fees), 0)
65
+ : transaction.amount;
66
+ };
67
+
68
+ export const getBalanceId = (balance: BalanceAsset): string | null => {
69
+ switch (balance.asset_type) {
70
+ case "native":
71
+ return "native";
72
+ case "liquidity_pool_shares":
73
+ return balance.liquidity_pool_id || null;
74
+ case "credit_alphanum4":
75
+ case "credit_alphanum12":
76
+ return `${balance.asset_code}:${balance.asset_issuer}`;
77
+ default:
78
+ return null;
79
+ }
80
+ };
81
+
82
+ export const getReservedBalance = (
83
+ account: ServerApi.AccountRecord
84
+ ): BigNumber => {
85
+ const numOfSponsoringEntries = Number(account.num_sponsoring);
86
+ const numOfSponsoredEntries = Number(account.num_sponsored);
87
+
88
+ const nativeAsset = account.balances?.find(
89
+ (b) => b.asset_type === "native"
90
+ ) as BalanceAsset;
91
+
92
+ const amountInOffers = new BigNumber(nativeAsset?.buying_liabilities || 0);
93
+ const numOfEntries = new BigNumber(account.subentry_count);
94
+
95
+ return new BigNumber(BASE_RESERVE_MIN_COUNT)
96
+ .plus(numOfEntries)
97
+ .plus(numOfSponsoringEntries)
98
+ .minus(numOfSponsoredEntries)
99
+ .times(BASE_RESERVE)
100
+ .plus(amountInOffers);
26
101
  };
27
102
 
28
103
  export const getOperationType = (
29
- operation: ServerApi.OperationRecord,
104
+ operation: RawOperation,
30
105
  addr: string
31
106
  ): OperationType => {
32
107
  switch (operation.type) {
@@ -41,11 +116,19 @@ export const getOperationType = (
41
116
  return "IN";
42
117
 
43
118
  case "path_payment_strict_send":
119
+ if (operation.to === addr) return "IN";
44
120
  return "OUT";
45
121
 
46
122
  case "path_payment_strict_receive":
47
123
  return "IN";
48
124
 
125
+ case "change_trust":
126
+ if (new BigNumber(operation.limit).eq(0)) {
127
+ return "OPT_OUT";
128
+ }
129
+
130
+ return "OPT_IN";
131
+
49
132
  default:
50
133
  if (operation.source_account === addr) {
51
134
  return "OUT";
@@ -55,12 +138,26 @@ export const getOperationType = (
55
138
  }
56
139
  };
57
140
 
141
+ export const getAssetCodeIssuer = (
142
+ tr: Transaction | TransactionRaw
143
+ ): string[] => {
144
+ if (tr.subAccountId) {
145
+ const assetString = tr.subAccountId.split("+")[1];
146
+ return assetString.split(":");
147
+ }
148
+
149
+ return [tr.assetCode || "", tr.assetIssuer || ""];
150
+ };
151
+
58
152
  const getRecipients = (operation): string[] => {
59
153
  switch (operation.type) {
60
154
  case "create_account":
61
155
  return [operation.account];
62
156
 
63
157
  case "payment":
158
+ return [operation.to_muxed || operation.to];
159
+
160
+ case "path_payment_strict_send":
64
161
  return [operation.to];
65
162
 
66
163
  default:
@@ -69,7 +166,7 @@ const getRecipients = (operation): string[] => {
69
166
  };
70
167
 
71
168
  export const formatOperation = async (
72
- rawOperation: ServerApi.OperationRecord,
169
+ rawOperation: RawOperation,
73
170
  accountId: string,
74
171
  addr: string
75
172
  ): Promise<Operation> => {
@@ -86,34 +183,42 @@ export const formatOperation = async (
86
183
  id: encodeOperationId(accountId, rawOperation.transaction_hash, type),
87
184
  accountId,
88
185
  fee: new BigNumber(transaction.fee_charged),
89
- value,
90
- type: type,
186
+ value: rawOperation?.asset_code
187
+ ? new BigNumber(transaction.fee_charged)
188
+ : value,
189
+ // Using type NONE to hide asset operations from the main account (show them
190
+ // only on sub-account)
191
+ type:
192
+ rawOperation?.asset_code && !["OPT_IN", "OPT_OUT"].includes(type)
193
+ ? "NONE"
194
+ : type,
91
195
  hash: rawOperation.transaction_hash,
92
196
  blockHeight: transaction.ledger_attr,
93
197
  date: new Date(rawOperation.created_at),
94
198
  senders: [rawOperation.source_account],
95
199
  recipients,
96
200
  transactionSequenceNumber: Number(transaction.source_account_sequence),
97
- // @ts-expect-error check transaction_successful property
98
201
  hasFailed: !rawOperation.transaction_successful,
99
202
  blockHash: null,
100
- extra: memo
101
- ? {
102
- memo,
103
- }
104
- : {},
203
+ extra: {
204
+ pagingToken: rawOperation.paging_token,
205
+ assetCode: rawOperation?.asset_code,
206
+ assetIssuer: rawOperation?.asset_issuer,
207
+ assetAmount: rawOperation?.asset_code ? value.toString() : undefined,
208
+ ledgerOpType: type,
209
+ memo,
210
+ },
105
211
  };
106
212
  return operation;
107
213
  };
108
214
 
109
215
  const getValue = (
110
- operation: ServerApi.OperationRecord,
216
+ operation: RawOperation,
111
217
  transaction: ServerApi.TransactionRecord,
112
218
  type: OperationType
113
219
  ): BigNumber => {
114
220
  let value = new BigNumber(0);
115
221
 
116
- // @ts-expect-error check transaction_successful property
117
222
  if (!operation.transaction_successful) {
118
223
  return type === "IN" ? value : new BigNumber(transaction.fee_charged || 0);
119
224
  }
@@ -131,38 +236,13 @@ const getValue = (
131
236
  case "payment":
132
237
  case "path_payment_strict_send":
133
238
  case "path_payment_strict_receive":
134
- value =
135
- operation.asset_type === "native"
136
- ? parseCurrencyUnit(currency.units[0], operation.amount)
137
- : new BigNumber(0);
138
-
139
- if (type === "OUT") {
140
- value = value.plus(transaction.fee_charged);
141
- }
142
-
143
- return value;
239
+ return parseCurrencyUnit(currency.units[0], operation.amount);
144
240
 
145
241
  default:
146
242
  return type !== "IN" ? new BigNumber(transaction.fee_charged) : value;
147
243
  }
148
244
  };
149
245
 
150
- // TODO: Move to cache.js
151
- export const checkRecipientExist: CacheRes<
152
- Array<{
153
- account: Account;
154
- recipient: string;
155
- }>,
156
- boolean
157
- > = makeLRUCache(
158
- async ({ recipient }) => await addressExists(recipient),
159
- (extract) => extract.recipient,
160
- {
161
- max: 300,
162
- maxAge: 5 * 60,
163
- } // 5 minutes
164
- );
165
-
166
246
  export const isMemoValid = (memoType: string, memoValue: string): boolean => {
167
247
  switch (memoType) {
168
248
  case "MEMO_TEXT":
@@ -191,7 +271,9 @@ export const isMemoValid = (memoType: string, memoValue: string): boolean => {
191
271
  return true;
192
272
  };
193
273
 
194
- export const isAccountMultiSign = async (account: Account) => {
274
+ export const isAccountMultiSign = async (
275
+ account: Account
276
+ ): Promise<boolean> => {
195
277
  const signers = await fetchSigners(account);
196
278
  return signers.length > 1;
197
279
  };
@@ -205,43 +287,97 @@ export const isAddressValid = (address: string): boolean => {
205
287
  if (!address) return false;
206
288
 
207
289
  try {
208
- return StellarSdk.StrKey.isValidEd25519PublicKey(address);
290
+ return (
291
+ StellarSdk.StrKey.isValidEd25519PublicKey(address) ||
292
+ StellarSdk.StrKey.isValidMed25519PublicKey(address)
293
+ );
209
294
  } catch (err) {
210
295
  return false;
211
296
  }
212
297
  };
213
298
 
214
- /**
215
- * Checks if the current account exists on the stellar Network. If it doesn't the account needs
216
- * to be activated by sending an account creation operation with an amount of at least the base reserve.
217
- *
218
- * @param {*} address
219
- */
220
- export const addressExists = async (address: string): Promise<boolean> => {
221
- const account = await loadAccount(address);
222
- return !!account;
299
+ export const getRecipientAccount: CacheRes<
300
+ Array<{
301
+ account: Account;
302
+ recipient: string;
303
+ }>,
304
+ {
305
+ id: string | null;
306
+ isMuxedAccount: boolean;
307
+ assetIds: string[];
308
+ } | null
309
+ > = makeLRUCache(
310
+ async ({ recipient }) => await recipientAccount(recipient),
311
+ (extract) => extract.recipient,
312
+ {
313
+ max: 300,
314
+ maxAge: 5 * 60,
315
+ } // 5 minutes
316
+ );
317
+
318
+ export const recipientAccount = async (
319
+ address?: string
320
+ ): Promise<{
321
+ id: string | null;
322
+ isMuxedAccount: boolean;
323
+ assetIds: string[];
324
+ } | null> => {
325
+ if (!address) {
326
+ return null;
327
+ }
328
+
329
+ let accountAddress = address;
330
+
331
+ const isMuxedAccount = Boolean(
332
+ StellarSdk.StrKey.isValidMed25519PublicKey(address)
333
+ );
334
+
335
+ if (isMuxedAccount) {
336
+ const muxedAccount = new StellarSdk.MuxedAccount.fromAddress(address, "0");
337
+ accountAddress = muxedAccount.baseAccount().accountId();
338
+ }
339
+
340
+ const account = await loadAccount(accountAddress);
341
+
342
+ if (!account) {
343
+ return null;
344
+ }
345
+
346
+ return {
347
+ id: account.id,
348
+ isMuxedAccount,
349
+ assetIds: account.balances.reduce((allAssets, balance) => {
350
+ return [...allAssets, getBalanceId(balance)];
351
+ }, []),
352
+ };
223
353
  };
224
354
 
225
355
  export const rawOperationsToOperations = async (
226
- operations: ServerApi.OperationRecord[],
356
+ operations: RawOperation[],
227
357
  addr: string,
228
358
  accountId: string
229
359
  ): Promise<Operation[]> => {
360
+ const supportedOperationTypes = [
361
+ "create_account",
362
+ "payment",
363
+ "path_payment_strict_send",
364
+ "path_payment_strict_receive",
365
+ "change_trust",
366
+ ];
367
+
230
368
  return Promise.all(
231
369
  operations
232
370
  .filter((operation) => {
233
371
  return (
234
- // @ts-expect-error check from property
235
372
  operation.from === addr ||
236
- // @ts-expect-error check to property
237
373
  operation.to === addr ||
238
- // @ts-expect-error check funder property
239
374
  operation.funder === addr ||
240
- // @ts-expect-error check account property
241
375
  operation.account === addr ||
376
+ operation.trustor === addr ||
242
377
  operation.source_account === addr
243
378
  );
244
379
  })
380
+ .filter((operation) => supportedOperationTypes.includes(operation.type))
245
381
  .map((operation) => formatOperation(operation, accountId, addr))
246
382
  );
247
383
  };
@@ -1,13 +1,30 @@
1
1
  import expect from "expect";
2
2
  import invariant from "invariant";
3
+ import BigNumber from "bignumber.js";
3
4
  import type { Transaction } from "./types";
4
- import { getCryptoCurrencyById, parseCurrencyUnit } from "../../currencies";
5
+ import {
6
+ getCryptoCurrencyById,
7
+ parseCurrencyUnit,
8
+ listTokensForCryptoCurrency,
9
+ } from "../../currencies";
5
10
  import { pickSiblings } from "../../bot/specs";
6
11
  import type { AppSpec } from "../../bot/types";
7
12
  import { DeviceModelId } from "@ledgerhq/devices";
13
+ import { TokenCurrency, SubAccount } from "../../types";
14
+
8
15
  const currency = getCryptoCurrencyById("stellar");
9
16
  const minAmountCutoff = parseCurrencyUnit(currency.units[0], "0.1");
10
- const reserve = parseCurrencyUnit(currency.units[0], "1");
17
+ const reserve = parseCurrencyUnit(currency.units[0], "1.5");
18
+
19
+ const MAX_FEE = 5000;
20
+ const USDC_CODE = "USDC";
21
+ const USDC_ISSUER = "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN";
22
+ const USDC_ASSET_ID = `${USDC_CODE}:${USDC_ISSUER}`;
23
+ const MIN_ASSET_BALANCE = parseCurrencyUnit(currency.units[0], "0.01");
24
+
25
+ const findAssetUSDC = <T extends { id: string }>(subAccounts?: T[]) =>
26
+ (subAccounts || []).find((s) => s.id.endsWith(USDC_ASSET_ID));
27
+
11
28
  const stellar: AppSpec<Transaction> = {
12
29
  name: "Stellar",
13
30
  currency,
@@ -15,12 +32,14 @@ const stellar: AppSpec<Transaction> = {
15
32
  model: DeviceModelId.nanoS,
16
33
  appName: "Stellar",
17
34
  },
35
+ testTimeout: 2 * 60 * 1000,
18
36
  mutations: [
19
37
  {
20
- name: "move ~50%",
38
+ name: "move ~50% XLM",
21
39
  maxRun: 2,
22
40
  transaction: ({ account, siblings, bridge, maxSpendable }) => {
23
- invariant(maxSpendable.gt(minAmountCutoff), "balance is too low");
41
+ invariant(maxSpendable.gt(minAmountCutoff), "XLM balance is too low");
42
+
24
43
  const transaction = bridge.createTransaction(account);
25
44
  const sibling = pickSiblings(siblings, 4);
26
45
  const recipient = sibling.freshAddress;
@@ -29,17 +48,81 @@ const stellar: AppSpec<Transaction> = {
29
48
  if (!sibling.used && amount.lt(reserve)) {
30
49
  invariant(
31
50
  maxSpendable.gt(reserve.plus(minAmountCutoff)),
32
- "not enough funds to send to new account"
51
+ "not enough XLM funds to send to new account"
33
52
  );
34
53
  amount = reserve;
35
54
  }
36
55
 
37
56
  const updates: Array<Partial<Transaction>> = [
57
+ {
58
+ recipient,
59
+ // Setting higher max fee here to make sure transaction doesn't
60
+ // time out.
61
+ fees: new BigNumber(MAX_FEE),
62
+ },
38
63
  {
39
64
  amount,
40
65
  },
66
+ ];
67
+
68
+ if (Math.random() < 0.5) {
69
+ updates.push({
70
+ memoType: "MEMO_TEXT",
71
+ memoValue: "Ledger Live",
72
+ });
73
+ }
74
+
75
+ return {
76
+ transaction,
77
+ updates,
78
+ };
79
+ },
80
+ test: ({ account, accountBeforeTransaction, operation, transaction }) => {
81
+ // We don't know what the final fee will be until after the tx is
82
+ // submitted. Using higher max fee to make sure tx doesn't time out.
83
+ expect(account.balance.toNumber()).toBeLessThanOrEqual(
84
+ accountBeforeTransaction.balance.minus(operation.value).toNumber()
85
+ );
86
+
87
+ if (transaction.memoValue) {
88
+ expect(operation.extra).toMatchObject({
89
+ memo: transaction.memoValue,
90
+ });
91
+ }
92
+
93
+ const getType = () => {
94
+ switch (transaction.mode) {
95
+ case "send":
96
+ return "send";
97
+ case "changeTrust":
98
+ return /change_trust/;
99
+ default:
100
+ return "";
101
+ }
102
+ };
103
+
104
+ expect(transaction.mode).toMatch(getType());
105
+ },
106
+ },
107
+ {
108
+ name: "Send max XLM",
109
+ maxRun: 2,
110
+ transaction: ({ account, siblings, bridge, maxSpendable }) => {
111
+ invariant(maxSpendable.gt(minAmountCutoff), "XLM balance is too low");
112
+
113
+ const transaction = bridge.createTransaction(account);
114
+ const sibling = pickSiblings(siblings, 4);
115
+ const recipient = sibling.freshAddress;
116
+
117
+ const updates: Array<Partial<Transaction>> = [
41
118
  {
42
119
  recipient,
120
+ // Setting higher max fee here to make sure transaction doesn't
121
+ // time out.
122
+ fees: new BigNumber(MAX_FEE),
123
+ },
124
+ {
125
+ useAllAmount: true,
43
126
  },
44
127
  ];
45
128
 
@@ -56,12 +139,148 @@ const stellar: AppSpec<Transaction> = {
56
139
  };
57
140
  },
58
141
  test: ({ account, accountBeforeTransaction, operation, transaction }) => {
59
- expect(account.balance.toString()).toBe(
60
- accountBeforeTransaction.balance.minus(operation.value).toString()
142
+ // We don't know what the final fee will be until after the tx is
143
+ // submitted. Using higher max fee to make sure tx doesn't time out.
144
+ expect(account.balance.toNumber()).toBeLessThanOrEqual(
145
+ accountBeforeTransaction.balance.minus(operation.value).toNumber()
146
+ );
147
+
148
+ if (transaction.memoValue) {
149
+ expect(operation.extra).toMatchObject({
150
+ memo: transaction.memoValue,
151
+ });
152
+ }
153
+
154
+ const getType = () => {
155
+ switch (transaction.mode) {
156
+ case "send":
157
+ return "send";
158
+ case "changeTrust":
159
+ return /change_trust/;
160
+ default:
161
+ return "";
162
+ }
163
+ };
164
+
165
+ expect(transaction.mode).toMatch(getType());
166
+ },
167
+ },
168
+ {
169
+ name: "add USDC asset",
170
+ maxRun: 1,
171
+ transaction: ({ account, bridge, maxSpendable }) => {
172
+ invariant(maxSpendable.gt(reserve), "XLM balance is too low 1");
173
+ invariant(
174
+ account.subAccounts && !findAssetUSDC(account.subAccounts),
175
+ "already have subaccounts"
176
+ );
177
+ const assetUSDC = findAssetUSDC<TokenCurrency>(
178
+ listTokensForCryptoCurrency(account.currency)
179
+ );
180
+ invariant(assetUSDC, "USDC asset not found");
181
+
182
+ const transaction = bridge.createTransaction(account);
183
+
184
+ const updates: Array<Partial<Transaction>> = [
185
+ {
186
+ mode: "changeTrust",
187
+ // Setting higher max fee here to make sure transaction doesn't
188
+ // time out.
189
+ fees: new BigNumber(MAX_FEE),
190
+ },
191
+ {
192
+ assetCode: USDC_CODE,
193
+ assetIssuer: USDC_ISSUER,
194
+ },
195
+ ];
196
+
197
+ return {
198
+ transaction,
199
+ updates,
200
+ };
201
+ },
202
+ test: ({ account }) => {
203
+ const assetId = `${USDC_CODE}:${USDC_ISSUER}`;
204
+ const hasAsset = account.subAccounts?.find((a) =>
205
+ a.id.endsWith(assetId)
206
+ );
207
+ expect(hasAsset).toBeTruthy();
208
+ },
209
+ },
210
+ {
211
+ name: "move ~50% USDC asset",
212
+ maxRun: 1,
213
+ transaction: ({ account, siblings, bridge, maxSpendable }) => {
214
+ invariant(maxSpendable.gt(minAmountCutoff), "XLM balance is too low");
215
+
216
+ const usdcSubAccount = findAssetUSDC<SubAccount>(account?.subAccounts);
217
+
218
+ invariant(usdcSubAccount, "USDC asset not found");
219
+ invariant(
220
+ usdcSubAccount?.balance.gt(MIN_ASSET_BALANCE),
221
+ "USDC balance is too low"
222
+ );
223
+
224
+ const siblingWithAssetUSDC = siblings.find((s) =>
225
+ findAssetUSDC(s.subAccounts)
226
+ );
227
+ invariant(siblingWithAssetUSDC, "No siblings with USDC asset");
228
+
229
+ if (!usdcSubAccount || !siblingWithAssetUSDC) {
230
+ throw new Error("No USDC asset or sibling with USDC asset");
231
+ }
232
+
233
+ const transaction = bridge.createTransaction(account);
234
+ const recipient = siblingWithAssetUSDC.freshAddress;
235
+ const amount = usdcSubAccount.balance
236
+ .div(1.9 + 0.2 * Math.random())
237
+ .integerValue();
238
+
239
+ const updates: Array<Partial<Transaction>> = [
240
+ {
241
+ recipient,
242
+ },
243
+ {
244
+ subAccountId: usdcSubAccount.id,
245
+ },
246
+ {
247
+ amount,
248
+ // Setting higher max fee here to make sure transaction doesn't
249
+ // time out.
250
+ fees: new BigNumber(MAX_FEE),
251
+ },
252
+ ];
253
+
254
+ if (Math.random() < 0.5) {
255
+ updates.push({
256
+ memoType: "MEMO_TEXT",
257
+ memoValue: "Ledger Live",
258
+ });
259
+ }
260
+
261
+ return {
262
+ transaction,
263
+ updates,
264
+ };
265
+ },
266
+ test: ({
267
+ account,
268
+ accountBeforeTransaction,
269
+ operation,
270
+ transaction,
271
+ status,
272
+ }) => {
273
+ const asset = findAssetUSDC<SubAccount>(account?.subAccounts);
274
+ const assetBeforeTx = findAssetUSDC<SubAccount>(
275
+ accountBeforeTransaction?.subAccounts
276
+ );
277
+
278
+ expect(asset?.balance.toString()).toBe(
279
+ assetBeforeTx?.balance.minus(status.amount).toString()
61
280
  );
62
281
 
63
282
  if (transaction.memoValue) {
64
- expect(operation.extra).toEqual({
283
+ expect(operation.extra).toMatchObject({
65
284
  memo: transaction.memoValue,
66
285
  });
67
286
  }