@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
@@ -12,15 +12,25 @@ import type { NetworkInfo } from "../types";
12
12
  import {
13
13
  getAccountSpendableBalance,
14
14
  rawOperationsToOperations,
15
+ getReservedBalance,
15
16
  } from "../logic";
16
17
  import { NetworkDown, LedgerAPI4xx, LedgerAPI5xx } from "@ledgerhq/errors";
17
18
  import { requestInterceptor, responseInterceptor } from "../../../network";
19
+ import type { BalanceAsset } from "../types";
20
+ import { NetworkCongestionLevel, Signer } from "../types";
18
21
 
19
22
  const LIMIT = getEnv("API_STELLAR_HORIZON_FETCH_LIMIT");
20
23
  const FALLBACK_BASE_FEE = 100;
24
+ const TRESHOLD_LOW = 0.5;
25
+ const TRESHOLD_MEDIUM = 0.75;
21
26
  const currency = getCryptoCurrencyById("stellar");
22
27
  const server = new StellarSdk.Server(getEnv("API_STELLAR_HORIZON"));
23
28
 
29
+ // Constants
30
+ export const BASE_RESERVE = 0.5;
31
+ export const BASE_RESERVE_MIN_COUNT = 2;
32
+ export const MIN_BALANCE = 1;
33
+
24
34
  StellarSdk.HorizonAxiosClient.interceptors.request.use(requestInterceptor);
25
35
 
26
36
  StellarSdk.HorizonAxiosClient.interceptors.response.use((response) => {
@@ -46,16 +56,48 @@ const getFormattedAmount = (amount: BigNumber) => {
46
56
  .toString(10);
47
57
  };
48
58
 
49
- export const fetchBaseFee = async (): Promise<number> => {
50
- let baseFee;
59
+ export const fetchBaseFee = async (): Promise<{
60
+ baseFee: number;
61
+ recommendedFee: number;
62
+ networkCongestionLevel: NetworkCongestionLevel;
63
+ }> => {
64
+ // For tests
65
+ if (getEnv("API_STELLAR_HORIZON_STATIC_FEE")) {
66
+ return {
67
+ baseFee: 100,
68
+ recommendedFee: 100,
69
+ networkCongestionLevel: NetworkCongestionLevel.LOW,
70
+ };
71
+ }
72
+
73
+ const baseFee = StellarSdk.BASE_FEE || FALLBACK_BASE_FEE;
74
+ let recommendedFee = baseFee;
75
+ let networkCongestionLevel = NetworkCongestionLevel.MEDIUM;
51
76
 
52
77
  try {
53
- baseFee = await server.fetchBaseFee();
78
+ const feeStats = await server.feeStats();
79
+ const ledgerCapacityUsage = feeStats.ledger_capacity_usage;
80
+ recommendedFee = Number(feeStats.fee_charged.mode);
81
+
82
+ if (
83
+ ledgerCapacityUsage > TRESHOLD_LOW &&
84
+ ledgerCapacityUsage <= TRESHOLD_MEDIUM
85
+ ) {
86
+ networkCongestionLevel = NetworkCongestionLevel.MEDIUM;
87
+ } else if (ledgerCapacityUsage > TRESHOLD_MEDIUM) {
88
+ networkCongestionLevel = NetworkCongestionLevel.HIGH;
89
+ } else {
90
+ networkCongestionLevel = NetworkCongestionLevel.LOW;
91
+ }
54
92
  } catch (e) {
55
- baseFee = FALLBACK_BASE_FEE;
93
+ // do nothing, will use defaults
56
94
  }
57
95
 
58
- return baseFee;
96
+ return {
97
+ baseFee,
98
+ recommendedFee,
99
+ networkCongestionLevel,
100
+ };
59
101
  };
60
102
 
61
103
  /**
@@ -70,15 +112,21 @@ export const fetchAccount = async (
70
112
  blockHeight?: number;
71
113
  balance: BigNumber;
72
114
  spendableBalance: BigNumber;
115
+ assets: BalanceAsset[];
73
116
  }> => {
74
117
  let account: typeof AccountRecord = {};
75
118
  let balance: Record<string, any> = {};
119
+ let assets: BalanceAsset[] = [];
76
120
 
77
121
  try {
78
122
  account = await server.accounts().accountId(addr).call();
79
- balance = account.balances.find((balance) => {
123
+ balance = account.balances?.find((balance) => {
80
124
  return balance.asset_type === "native";
81
125
  });
126
+ // Getting all non-native (XLM) assets on the account
127
+ assets = account.balances?.filter((balance) => {
128
+ return balance.asset_type !== "native";
129
+ });
82
130
  } catch (e) {
83
131
  balance.balance = "0";
84
132
  }
@@ -97,6 +145,7 @@ export const fetchAccount = async (
97
145
  blockHeight: account.sequence ? Number(account.sequence) : undefined,
98
146
  balance: formattedBalance,
99
147
  spendableBalance,
148
+ assets,
100
149
  };
101
150
  };
102
151
 
@@ -105,16 +154,23 @@ export const fetchAccount = async (
105
154
  *
106
155
  * @param {string} accountId
107
156
  * @param {string} addr
108
- * @param {number} startAt - blockHeight after which you fetch this op (included)
157
+ * @param {string} order - "desc" or "asc" order of returned records
158
+ * @param {string} cursor - point to start fetching records
109
159
  *
110
160
  * @return {Operation[]}
111
161
  */
112
- export const fetchOperations = async (
113
- accountId: string,
114
- addr: string,
115
- startAt = 0
116
- ): Promise<Operation[]> => {
117
- if (!addr || !addr.length) {
162
+ export const fetchOperations = async ({
163
+ accountId,
164
+ addr,
165
+ order,
166
+ cursor,
167
+ }: {
168
+ accountId: string;
169
+ addr: string;
170
+ order: "asc" | "desc";
171
+ cursor: string;
172
+ }): Promise<Operation[]> => {
173
+ if (!addr) {
118
174
  return [];
119
175
  }
120
176
 
@@ -125,10 +181,11 @@ export const fetchOperations = async (
125
181
  rawOperations = await server
126
182
  .operations()
127
183
  .forAccount(addr)
128
- .join("transactions")
129
- .includeFailed(true)
130
184
  .limit(LIMIT)
131
- .cursor(startAt)
185
+ .order(order)
186
+ .cursor(cursor)
187
+ .includeFailed(true)
188
+ .join("transactions")
132
189
  .call();
133
190
  } catch (e: any) {
134
191
  // FIXME: terrible hacks, because Stellar SDK fails to cast network failures to typed errors in react-native...
@@ -175,6 +232,7 @@ export const fetchOperations = async (
175
232
 
176
233
  return operations;
177
234
  };
235
+
178
236
  export const fetchAccountNetworkInfo = async (
179
237
  account: Account
180
238
  ): Promise<NetworkInfo> => {
@@ -183,25 +241,24 @@ export const fetchAccountNetworkInfo = async (
183
241
  .accounts()
184
242
  .accountId(account.freshAddress)
185
243
  .call();
186
- const numberOfEntries = extendedAccount.subentry_count;
187
- const ledger = await server
188
- .ledgers()
189
- .ledger(extendedAccount.last_modified_ledger)
190
- .call();
191
- const baseReserve = new BigNumber(
192
- (ledger.base_reserve_in_stroops * (2 + numberOfEntries)).toString()
193
- );
194
- const fees = new BigNumber(ledger.base_fee_in_stroops.toString());
244
+ const baseReserve = getReservedBalance(extendedAccount);
245
+ const { recommendedFee, networkCongestionLevel, baseFee } =
246
+ await fetchBaseFee();
247
+
195
248
  return {
196
249
  family: "stellar",
197
- fees,
250
+ fees: new BigNumber(recommendedFee.toString()),
251
+ baseFee: new BigNumber(baseFee.toString()),
198
252
  baseReserve,
253
+ networkCongestionLevel,
199
254
  };
200
255
  } catch (error) {
201
256
  return {
202
257
  family: "stellar",
203
258
  fees: new BigNumber(0),
259
+ baseFee: new BigNumber(0),
204
260
  baseReserve: new BigNumber(0),
261
+ networkCongestionLevel: undefined,
205
262
  };
206
263
  }
207
264
  };
@@ -213,7 +270,7 @@ export const fetchSequence = async (a: Account): Promise<BigNumber> => {
213
270
  : new BigNumber(0);
214
271
  };
215
272
 
216
- export const fetchSigners = async (a: Account) => {
273
+ export const fetchSigners = async (a: Account): Promise<Signer[]> => {
217
274
  try {
218
275
  const extendedAccount = await server
219
276
  .accounts()
@@ -238,15 +295,29 @@ export const broadcastTransaction = async (
238
295
  return res.hash;
239
296
  };
240
297
 
241
- export const buildPaymentOperation = (
242
- destination: string,
243
- amount: BigNumber
244
- ): any => {
298
+ export const buildPaymentOperation = ({
299
+ destination,
300
+ amount,
301
+ assetCode,
302
+ assetIssuer,
303
+ }: {
304
+ destination: string;
305
+ amount: BigNumber;
306
+ assetCode: string | undefined;
307
+ assetIssuer: string | undefined;
308
+ }): any => {
245
309
  const formattedAmount = getFormattedAmount(amount);
310
+ // Non-native assets should always have asset code and asset issuer. If an
311
+ // asset doesn't have both, we assume it is native asset.
312
+ const asset =
313
+ assetCode && assetIssuer
314
+ ? new StellarSdk.Asset(assetCode, assetIssuer)
315
+ : StellarSdk.Asset.native();
246
316
  return StellarSdk.Operation.payment({
247
317
  destination: destination,
248
318
  amount: formattedAmount,
249
- asset: StellarSdk.Asset.native(),
319
+ asset,
320
+ withMuxing: true,
250
321
  });
251
322
  };
252
323
 
@@ -261,6 +332,15 @@ export const buildCreateAccountOperation = (
261
332
  });
262
333
  };
263
334
 
335
+ export const buildChangeTrustOperation = (
336
+ assetCode: string,
337
+ assetIssuer: string
338
+ ): any => {
339
+ return StellarSdk.Operation.changeTrust({
340
+ asset: new StellarSdk.Asset(assetCode, assetIssuer),
341
+ });
342
+ };
343
+
264
344
  export const buildTransactionBuilder = (
265
345
  source: typeof StellarSdk.Account,
266
346
  fee: BigNumber
@@ -272,7 +352,9 @@ export const buildTransactionBuilder = (
272
352
  });
273
353
  };
274
354
 
275
- export const loadAccount = async (addr: string): Promise<any> => {
355
+ export const loadAccount = async (
356
+ addr: string
357
+ ): Promise<AccountRecord | null> => {
276
358
  if (!addr || !addr.length) {
277
359
  return null;
278
360
  }
@@ -8,6 +8,10 @@ export {
8
8
  broadcastTransaction,
9
9
  buildPaymentOperation,
10
10
  buildCreateAccountOperation,
11
+ buildChangeTrustOperation,
11
12
  buildTransactionBuilder,
12
13
  loadAccount,
14
+ BASE_RESERVE,
15
+ BASE_RESERVE_MIN_COUNT,
16
+ MIN_BALANCE,
13
17
  } from "./horizon";
@@ -43,6 +43,9 @@ const createTransaction = (): Transaction => ({
43
43
  memoValue: null,
44
44
  memoType: null,
45
45
  useAllAmount: false,
46
+ mode: "send",
47
+ assetCode: "",
48
+ assetIssuer: "",
46
49
  });
47
50
 
48
51
  const updateTransaction = (t, patch) => {
@@ -111,11 +111,15 @@ const dataset: DatasetTest<Transaction> = {
111
111
  networkInfo: {
112
112
  family: "stellar",
113
113
  fees: "100",
114
+ baseFee: "100",
114
115
  baseReserve: "1500000",
115
116
  },
116
117
  fees: "100",
117
118
  memoType: null,
118
119
  memoValue: null,
120
+ mode: "send",
121
+ assetCode: "",
122
+ assetIssuer: "",
119
123
  }),
120
124
  expectedStatus: {
121
125
  errors: {
@@ -166,18 +170,22 @@ const dataset: DatasetTest<Transaction> = {
166
170
  networkInfo: {
167
171
  family: "stellar",
168
172
  fees: "100",
173
+ baseFee: "100",
169
174
  baseReserve: "1500000",
170
175
  },
171
176
  fees: "100",
172
177
  memoType: null,
173
178
  memoValue: null,
179
+ mode: "send",
180
+ assetCode: "",
181
+ assetIssuer: "",
174
182
  }),
175
183
  expectedStatus: (account) => ({
176
184
  errors: {},
177
185
  warnings: {},
178
186
  estimatedFees: new BigNumber("100"),
179
- amount: account.balance.minus("1500000").minus("100"),
180
- totalSpent: account.balance.minus("1500000"),
187
+ amount: account.spendableBalance.minus("100"),
188
+ totalSpent: account.spendableBalance.minus("100"),
181
189
  }),
182
190
  },
183
191
  {
@@ -1,5 +1,13 @@
1
1
  import invariant from "invariant";
2
- import type { Transaction, AccountLike } from "../../types";
2
+ import { getAccountCurrency } from "../../account";
3
+ import type {
4
+ Transaction,
5
+ AccountLike,
6
+ Account,
7
+ AccountLikeArray,
8
+ } from "../../types";
9
+ import { getAssetIdFromTokenId } from "./tokens";
10
+
3
11
  const options = [
4
12
  {
5
13
  name: "fee",
@@ -16,6 +24,21 @@ const options = [
16
24
  type: String,
17
25
  desc: "stellar memo value",
18
26
  },
27
+ {
28
+ name: "mode",
29
+ type: String,
30
+ desc: "change operation type",
31
+ },
32
+ {
33
+ name: "assetIssuer",
34
+ type: String,
35
+ desc: "Asset issuer",
36
+ },
37
+ {
38
+ name: "assetCode",
39
+ type: String,
40
+ desc: "Same as token",
41
+ },
19
42
  ];
20
43
 
21
44
  function inferTransactions(
@@ -25,17 +48,67 @@ function inferTransactions(
25
48
  }>,
26
49
  opts: Record<string, any>
27
50
  ): Transaction[] {
28
- return transactions.map(({ transaction }) => {
51
+ return transactions.map(({ transaction, account }) => {
29
52
  invariant(transaction.family === "stellar", "stellar family");
53
+
30
54
  return {
31
55
  ...transaction,
56
+ subAccountId: account.type === "TokenAccount" ? account.id : null,
32
57
  memoType: opts.memoType,
33
58
  memoValue: opts.memoValue,
59
+ mode: opts.mode ?? "send",
60
+ assetCode: opts.token,
61
+ assetIssuer: opts.assetIssuer,
34
62
  };
35
63
  });
36
64
  }
37
65
 
66
+ function inferAccounts(
67
+ account: Account,
68
+ opts: Record<string, any>
69
+ ): AccountLikeArray {
70
+ invariant(account.currency.family === "stellar", "stellar family");
71
+
72
+ if (opts.subAccountId) {
73
+ const assetSubAccount = account.subAccounts?.find(
74
+ (a) => a.id === opts.subAccountId
75
+ );
76
+
77
+ if (!assetSubAccount) {
78
+ throw new Error(`${opts.subAccountId} asset not found`);
79
+ }
80
+
81
+ return [assetSubAccount];
82
+ }
83
+
84
+ if (opts.assetCode) {
85
+ const subAccounts = account.subAccounts || [];
86
+
87
+ const subAccount = subAccounts.find((sa) => {
88
+ const currency = getAccountCurrency(sa);
89
+ return (
90
+ opts.assetCode.toLowerCase() === currency.ticker.toLowerCase() ||
91
+ opts.assetCode.toLowerCase() === getAssetIdFromTokenId(currency.id)
92
+ );
93
+ });
94
+
95
+ if (!subAccount) {
96
+ throw new Error(
97
+ "token account '" +
98
+ opts.assetCode +
99
+ "' not found. Available: " +
100
+ subAccounts.map((t) => getAccountCurrency(t).ticker).join(", ")
101
+ );
102
+ }
103
+
104
+ return [subAccount];
105
+ }
106
+
107
+ return [account];
108
+ }
109
+
38
110
  export default {
39
111
  options,
112
+ inferAccounts,
40
113
  inferTransactions,
41
114
  };
@@ -10,16 +10,27 @@ export type ExtraDeviceTransactionField =
10
10
  | {
11
11
  type: "stellar.network";
12
12
  label: string;
13
+ }
14
+ | {
15
+ type: "stellar.assetCode";
16
+ label: string;
17
+ }
18
+ | {
19
+ type: "stellar.assetIssuer";
20
+ label: string;
13
21
  };
14
22
 
15
23
  function getDeviceTransactionConfig({
16
24
  status: { amount, estimatedFees },
25
+ transaction,
17
26
  }: {
18
27
  account: AccountLike;
19
28
  parentAccount: Account | null | undefined;
20
29
  transaction: Transaction;
21
30
  status: TransactionStatus;
22
31
  }): Array<DeviceTransactionField | ExtraDeviceTransactionField> {
32
+ const { assetCode, assetIssuer } = transaction;
33
+
23
34
  const fields: Array<DeviceTransactionField | ExtraDeviceTransactionField> = [
24
35
  {
25
36
  type: "stellar.network",
@@ -34,6 +45,17 @@ function getDeviceTransactionConfig({
34
45
  });
35
46
  }
36
47
 
48
+ if (assetCode && assetIssuer) {
49
+ fields.push({
50
+ type: "stellar.assetCode",
51
+ label: "Asset",
52
+ });
53
+ fields.push({
54
+ type: "stellar.assetIssuer",
55
+ label: "Asset issuer",
56
+ });
57
+ }
58
+
37
59
  fields.push({
38
60
  type: "stellar.memo",
39
61
  label: "Memo",
@@ -0,0 +1,34 @@
1
+ import { createCustomErrorClass } from "@ledgerhq/errors";
2
+
3
+ export const StellarMemoRecommended = createCustomErrorClass(
4
+ "StellarMemoRecommended"
5
+ );
6
+ export const StellarWrongMemoFormat = createCustomErrorClass(
7
+ "StellarWrongMemoFormat"
8
+ );
9
+ export const StellarAssetRequired = createCustomErrorClass(
10
+ "StellarAssetRequired"
11
+ );
12
+ export const StellarAssetNotAccepted = createCustomErrorClass(
13
+ "StellarAssetNotAccepted"
14
+ );
15
+ export const StellarAssetNotFound = createCustomErrorClass(
16
+ "StellarAssetNotFound"
17
+ );
18
+ export const StellarNotEnoughNativeBalance = createCustomErrorClass(
19
+ "StellarNotEnoughNativeBalance"
20
+ );
21
+ export const StellarFeeSmallerThanRecommended = createCustomErrorClass(
22
+ "StellarFeeSmallerThanRecommended"
23
+ );
24
+ export const StellarFeeSmallerThanBase = createCustomErrorClass(
25
+ "StellarFeeSmallerThanBase"
26
+ );
27
+ export const StellarNotEnoughNativeBalanceToAddTrustline =
28
+ createCustomErrorClass("StellarNotEnoughNativeBalanceToAddTrustline");
29
+ export const StellarMuxedAccountNotExist = createCustomErrorClass(
30
+ "StellarMuxedAccountNotExist"
31
+ );
32
+ export const StellarSourceHasMultiSign = createCustomErrorClass(
33
+ "StellarSourceHasMultiSign"
34
+ );
@@ -1,4 +1,4 @@
1
- import { bip32asBuffer } from "@ledgerhq/hw-app-btc/lib/bip32";
1
+ import { bip32asBuffer } from "@ledgerhq/hw-app-btc/bip32";
2
2
 
3
3
  const getSerializedAddressParameters = (
4
4
  path: string
@@ -1,5 +1,4 @@
1
1
  import invariant from "invariant";
2
- import { BigNumber } from "bignumber.js";
3
2
  import StellarSdk from "stellar-sdk";
4
3
  import { AmountRequired, FeeNotLoaded, NetworkDown } from "@ledgerhq/errors";
5
4
  import type { Account } from "../../types";
@@ -8,9 +7,14 @@ import {
8
7
  buildPaymentOperation,
9
8
  buildCreateAccountOperation,
10
9
  buildTransactionBuilder,
10
+ buildChangeTrustOperation,
11
11
  loadAccount,
12
12
  } from "./api";
13
- import { addressExists } from "./logic";
13
+ import { getRecipientAccount, getAmountValue } from "./logic";
14
+ import {
15
+ StellarAssetRequired,
16
+ StellarMuxedAccountNotExist,
17
+ } from "../../errors";
14
18
 
15
19
  /**
16
20
  * @param {Account} a
@@ -20,33 +24,69 @@ export const buildTransaction = async (
20
24
  account: Account,
21
25
  transaction: Transaction
22
26
  ): Promise<any> => {
23
- const { recipient, useAllAmount, networkInfo, fees, memoType, memoValue } =
24
- transaction;
27
+ const {
28
+ recipient,
29
+ networkInfo,
30
+ fees,
31
+ memoType,
32
+ memoValue,
33
+ mode,
34
+ assetCode,
35
+ assetIssuer,
36
+ } = transaction;
25
37
 
26
38
  if (!fees) {
27
39
  throw new FeeNotLoaded();
28
40
  }
29
41
 
30
- invariant(networkInfo && networkInfo.family === "stellar", "stellar family");
31
- let amount = new BigNumber(0);
32
- amount =
33
- useAllAmount && networkInfo
34
- ? account.balance.minus(networkInfo.baseReserve).minus(fees)
35
- : transaction.amount;
36
- if (!amount) throw new AmountRequired();
37
42
  const source = await loadAccount(account.freshAddress);
38
- if (!source) throw new NetworkDown();
43
+
44
+ if (!source) {
45
+ throw new NetworkDown();
46
+ }
47
+
48
+ invariant(networkInfo && networkInfo.family === "stellar", "stellar family");
49
+
39
50
  const transactionBuilder = buildTransactionBuilder(source, fees);
40
51
  let operation = null;
41
- const recipientExists = await addressExists(transaction.recipient); // TODO: use cache with checkRecipientExist instead?
42
52
 
43
- if (recipientExists) {
44
- operation = buildPaymentOperation(recipient, amount);
53
+ if (mode === "changeTrust") {
54
+ if (!assetCode || !assetIssuer) {
55
+ throw new StellarAssetRequired("");
56
+ }
57
+
58
+ operation = buildChangeTrustOperation(assetCode, assetIssuer);
45
59
  } else {
46
- operation = buildCreateAccountOperation(recipient, amount);
60
+ // Payment
61
+ const amount = getAmountValue(account, transaction, fees);
62
+
63
+ if (!amount) {
64
+ throw new AmountRequired();
65
+ }
66
+
67
+ const recipientAccount = await getRecipientAccount({
68
+ account,
69
+ recipient: transaction.recipient,
70
+ });
71
+
72
+ if (recipientAccount?.id) {
73
+ operation = buildPaymentOperation({
74
+ destination: recipient,
75
+ amount,
76
+ assetCode,
77
+ assetIssuer,
78
+ });
79
+ } else {
80
+ if (recipientAccount?.isMuxedAccount) {
81
+ throw new StellarMuxedAccountNotExist("");
82
+ }
83
+
84
+ operation = buildCreateAccountOperation(recipient, amount);
85
+ }
47
86
  }
48
87
 
49
88
  transactionBuilder.addOperation(operation);
89
+
50
90
  let memo = null;
51
91
 
52
92
  if (memoType && memoValue) {
@@ -16,6 +16,9 @@ const createTransaction = (): Transaction => ({
16
16
  memoValue: null,
17
17
  memoType: null,
18
18
  useAllAmount: false,
19
+ mode: "send",
20
+ assetCode: "",
21
+ assetIssuer: "",
19
22
  });
20
23
 
21
24
  export default createTransaction;
@@ -27,7 +27,7 @@ const estimateMaxSpendable = async ({
27
27
  useAllAmount: true,
28
28
  });
29
29
  const s = await getTransactionStatus(mainAccount, t);
30
- return s.amount;
30
+ return s.amount.gte(0) ? s.amount : new BigNumber(0);
31
31
  };
32
32
 
33
33
  export default estimateMaxSpendable;