@ledgerhq/live-common 22.0.3 → 22.1.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.
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.dogecoin.test.d.ts +2 -0
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.dogecoin.test.d.ts.map +1 -0
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.dogecoin.test.js +147 -0
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.dogecoin.test.js.map +1 -0
- package/lib/api/Ripple.d.ts +17 -21
- package/lib/api/Ripple.d.ts.map +1 -1
- package/lib/api/Ripple.js +117 -176
- package/lib/api/Ripple.js.map +1 -1
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +1 -5
- package/lib/api/index.js.map +1 -1
- package/lib/env.d.ts +6 -6
- package/lib/env.d.ts.map +1 -1
- package/lib/env.js +3 -3
- package/lib/env.js.map +1 -1
- package/lib/env.react.d.ts +1 -1
- package/lib/env.react.d.ts.map +1 -1
- package/lib/errors.d.ts +3 -0
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +2 -1
- package/lib/errors.js.map +1 -1
- package/lib/families/bitcoin/getAccountNetworkInfo.d.ts.map +1 -1
- package/lib/families/bitcoin/getAccountNetworkInfo.js +4 -2
- package/lib/families/bitcoin/getAccountNetworkInfo.js.map +1 -1
- package/lib/families/bitcoin/specs.d.ts.map +1 -1
- package/lib/families/bitcoin/specs.js +5 -2
- package/lib/families/bitcoin/specs.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/utils.d.ts +2 -0
- package/lib/families/bitcoin/wallet-btc/utils.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/utils.js +9 -1
- package/lib/families/bitcoin/wallet-btc/utils.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.js +11 -11
- package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
- package/lib/families/cosmos/js-prepareTransaction.d.ts.map +1 -1
- package/lib/families/cosmos/js-prepareTransaction.js +29 -5
- package/lib/families/cosmos/js-prepareTransaction.js.map +1 -1
- package/lib/families/cosmos/js-signOperation.d.ts.map +1 -1
- package/lib/families/cosmos/js-signOperation.js +76 -39
- package/lib/families/cosmos/js-signOperation.js.map +1 -1
- package/lib/families/cosmos/validators.js +1 -1
- package/lib/families/ripple/bridge/js.d.ts +2 -1
- package/lib/families/ripple/bridge/js.d.ts.map +1 -1
- package/lib/families/ripple/bridge/js.js +96 -466
- package/lib/families/ripple/bridge/js.js.map +1 -1
- package/lib/families/ripple/bridge.test.js +0 -48
- package/lib/families/ripple/bridge.test.js.map +1 -1
- package/lib/families/ripple/js-synchronization.d.ts +10 -0
- package/lib/families/ripple/js-synchronization.d.ts.map +1 -0
- package/lib/families/ripple/js-synchronization.js +156 -0
- package/lib/families/ripple/js-synchronization.js.map +1 -0
- package/lib/families/ripple/test-dataset.d.ts.map +1 -1
- package/lib/families/ripple/test-dataset.js +1 -1
- package/lib/families/ripple/test-dataset.js.map +1 -1
- package/lib/families/ripple/types.api.d.ts +24 -0
- package/lib/families/ripple/types.api.d.ts.map +1 -0
- package/lib/families/ripple/types.api.js +3 -0
- package/lib/families/ripple/types.api.js.map +1 -0
- package/lib/families/solana/api/chain/instruction/token/types.d.ts +3 -3
- package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
- package/lib/featureFlags/defaultFeatures.js +46 -0
- package/lib/featureFlags/defaultFeatures.js.map +1 -1
- package/lib/featureFlags/types.d.ts +2 -1
- package/lib/featureFlags/types.d.ts.map +1 -1
- package/lib/platform/providers/GlobalCatalogProvider/api/mock.json +0 -26
- package/lib/platform/providers/RemoteLiveAppProvider/api/mock.json +0 -63
- package/lib/types/nft.d.ts +1 -1
- package/lib/types/nft.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.dogecoin.test.ts +96 -0
- package/src/api/Ripple.ts +95 -160
- package/src/api/index.ts +0 -2
- package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +47 -3
- package/src/env.ts +3 -3
- package/src/errors.ts +4 -0
- package/src/families/bitcoin/getAccountNetworkInfo.ts +4 -2
- package/src/families/bitcoin/specs.ts +4 -5
- package/src/families/bitcoin/wallet-btc/utils.ts +8 -0
- package/src/families/bitcoin/wallet-btc/wallet.ts +24 -12
- package/src/families/cosmos/__snapshots__/bridge.test.ts.snap +128 -7
- package/src/families/cosmos/js-prepareTransaction.ts +7 -5
- package/src/families/cosmos/js-signOperation.ts +54 -36
- package/src/families/cosmos/validators.ts +1 -1
- package/src/families/ripple/bridge/js.ts +89 -494
- package/src/families/ripple/bridge.test.ts +0 -6
- package/src/families/ripple/js-synchronization.ts +153 -0
- package/src/families/ripple/test-dataset.ts +2 -0
- package/src/families/ripple/types.api.ts +23 -0
- package/src/featureFlags/defaultFeatures.ts +47 -0
- package/src/featureFlags/types.ts +7 -1
- package/src/platform/providers/GlobalCatalogProvider/api/mock.json +0 -26
- package/src/platform/providers/RemoteLiveAppProvider/api/mock.json +0 -63
- package/src/types/nft.ts +1 -1
- package/src/.DS_Store +0 -0
- package/src/__tests__/.DS_Store +0 -0
|
@@ -1,77 +1,42 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
|
-
import invariant from "invariant";
|
|
3
|
-
import { BigNumber } from "bignumber.js";
|
|
4
|
-
import { Observable } from "rxjs";
|
|
5
|
-
import bs58check from "ripple-bs58check";
|
|
6
2
|
import {
|
|
7
3
|
AmountRequired,
|
|
8
|
-
NotEnoughBalanceBecauseDestinationNotCreated,
|
|
9
|
-
NotEnoughSpendableBalance,
|
|
10
|
-
InvalidAddress,
|
|
11
4
|
FeeNotLoaded,
|
|
5
|
+
FeeRequired,
|
|
12
6
|
FeeTooHigh,
|
|
13
|
-
|
|
7
|
+
InvalidAddress,
|
|
14
8
|
InvalidAddressBecauseDestinationIsAlsoSource,
|
|
15
|
-
|
|
9
|
+
NetworkDown,
|
|
10
|
+
NotEnoughBalanceBecauseDestinationNotCreated,
|
|
11
|
+
NotEnoughSpendableBalance,
|
|
16
12
|
RecipientRequired,
|
|
17
13
|
} from "@ledgerhq/errors";
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
runDerivationScheme,
|
|
23
|
-
isIterableDerivationMode,
|
|
24
|
-
derivationModeSupportsIndex,
|
|
25
|
-
} from "../../../derivation";
|
|
26
|
-
import { formatCurrencyUnit } from "../../../currencies";
|
|
27
|
-
import { patchOperationWithHash } from "../../../operation";
|
|
14
|
+
import { BigNumber } from "bignumber.js";
|
|
15
|
+
import invariant from "invariant";
|
|
16
|
+
import bs58check from "ripple-bs58check";
|
|
17
|
+
import { Observable } from "rxjs";
|
|
28
18
|
import { getMainAccount } from "../../../account";
|
|
29
|
-
import {
|
|
30
|
-
getAccountPlaceholderName,
|
|
31
|
-
getNewAccountPlaceholderName,
|
|
32
|
-
emptyHistoryCache,
|
|
33
|
-
} from "../../../account";
|
|
34
|
-
import getAddress from "../../../hw/getAddress";
|
|
35
|
-
import { withDevice } from "../../../hw/deviceAccess";
|
|
36
|
-
import {
|
|
37
|
-
parseAPIValue,
|
|
38
|
-
parseAPICurrencyObject,
|
|
39
|
-
formatAPICurrencyXRP,
|
|
40
|
-
} from "../../../api/Ripple";
|
|
41
|
-
import type { CurrencyBridge, AccountBridge } from "../../../types/bridge";
|
|
42
|
-
import signTransaction from "../../../hw/signTransaction";
|
|
43
|
-
import type { Transaction, NetworkInfo } from "../types";
|
|
44
|
-
import { makeAccountBridgeReceive, mergeOps } from "../../../bridge/jsHelpers";
|
|
45
|
-
import {
|
|
46
|
-
preparePayment,
|
|
47
|
-
submit,
|
|
19
|
+
import getLedgerIndex, {
|
|
48
20
|
getAccountInfo,
|
|
49
21
|
getServerInfo,
|
|
50
|
-
|
|
22
|
+
parseAPIValue,
|
|
23
|
+
submit,
|
|
51
24
|
} from "../../../api/Ripple";
|
|
25
|
+
import { makeAccountBridgeReceive } from "../../../bridge/jsHelpers";
|
|
26
|
+
import { formatCurrencyUnit } from "../../../currencies";
|
|
27
|
+
import signTransaction from "../../../hw/signTransaction";
|
|
28
|
+
import { withDevice } from "../../../hw/deviceAccess";
|
|
29
|
+
import { patchOperationWithHash } from "../../../operation";
|
|
30
|
+
import type { Account, Operation, SignOperationEvent } from "../../../types";
|
|
31
|
+
import type { AccountBridge, CurrencyBridge } from "../../../types/bridge";
|
|
32
|
+
import { scanAccounts, sync } from "../js-synchronization";
|
|
33
|
+
import type { NetworkInfo, Transaction } from "../types";
|
|
52
34
|
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
return (
|
|
56
|
-
!e.data || (e.message !== "actNotFound" && e.data.error !== "actNotFound")
|
|
57
|
-
);
|
|
58
|
-
};
|
|
35
|
+
export const NEW_ACCOUNT_ERROR_MESSAGE = "actNotFound";
|
|
36
|
+
const LEDGER_OFFSET = 20;
|
|
59
37
|
|
|
60
38
|
const receive = makeAccountBridgeReceive();
|
|
61
39
|
|
|
62
|
-
const getSequenceNumber = async (account) => {
|
|
63
|
-
const lastOp = account.operations.find((op) => op.type === "OUT");
|
|
64
|
-
|
|
65
|
-
if (lastOp && lastOp.transactionSequenceNumber) {
|
|
66
|
-
return (
|
|
67
|
-
lastOp.transactionSequenceNumber + account.pendingOperations.length + 1
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const info = await getAccountInfo(account.freshAddress);
|
|
72
|
-
return info.sequence + account.pendingOperations.length;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
40
|
const uint32maxPlus1 = new BigNumber(2).pow(32);
|
|
76
41
|
|
|
77
42
|
const validateTag = (tag) => {
|
|
@@ -84,6 +49,11 @@ const validateTag = (tag) => {
|
|
|
84
49
|
);
|
|
85
50
|
};
|
|
86
51
|
|
|
52
|
+
const getNextValidSequence = async (account: Account) => {
|
|
53
|
+
const accInfo = await getAccountInfo(account.freshAddress, true);
|
|
54
|
+
return accInfo.account_data.Sequence;
|
|
55
|
+
};
|
|
56
|
+
|
|
87
57
|
const signOperation = ({
|
|
88
58
|
account,
|
|
89
59
|
transaction,
|
|
@@ -97,46 +67,36 @@ const signOperation = ({
|
|
|
97
67
|
if (!fee) throw new FeeNotLoaded();
|
|
98
68
|
|
|
99
69
|
async function main() {
|
|
100
|
-
const amount = formatAPICurrencyXRP(transaction.amount);
|
|
101
|
-
const tag = transaction.tag ? transaction.tag : undefined;
|
|
102
|
-
const payment = {
|
|
103
|
-
source: {
|
|
104
|
-
address: account.freshAddress,
|
|
105
|
-
amount,
|
|
106
|
-
},
|
|
107
|
-
destination: {
|
|
108
|
-
address: transaction.recipient,
|
|
109
|
-
minAmount: amount,
|
|
110
|
-
tag,
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
const instruction = {
|
|
114
|
-
fee: formatAPICurrencyXRP(fee).value,
|
|
115
|
-
maxLedgerVersionOffset: 12,
|
|
116
|
-
};
|
|
117
|
-
if (tag)
|
|
118
|
-
invariant(
|
|
119
|
-
validateTag(new BigNumber(tag)),
|
|
120
|
-
`tag is set but is not in a valid format, should be between [0 - ${uint32maxPlus1
|
|
121
|
-
.minus(1)
|
|
122
|
-
.toString()}]`
|
|
123
|
-
);
|
|
124
|
-
const prepared = await preparePayment(
|
|
125
|
-
account.freshAddress,
|
|
126
|
-
payment,
|
|
127
|
-
instruction
|
|
128
|
-
);
|
|
129
|
-
let signature;
|
|
130
|
-
|
|
131
70
|
try {
|
|
71
|
+
const tag = transaction.tag ? transaction.tag : undefined;
|
|
72
|
+
const nextSequenceNumber = await getNextValidSequence(account);
|
|
73
|
+
const payment = {
|
|
74
|
+
TransactionType: "Payment",
|
|
75
|
+
Account: account.freshAddress,
|
|
76
|
+
Amount: transaction.amount.toString(),
|
|
77
|
+
Destination: transaction.recipient,
|
|
78
|
+
DestinationTag: tag,
|
|
79
|
+
Fee: fee.toString(),
|
|
80
|
+
Flags: 2147483648,
|
|
81
|
+
Sequence: nextSequenceNumber,
|
|
82
|
+
LastLedgerSequence: (await getLedgerIndex()) + LEDGER_OFFSET,
|
|
83
|
+
};
|
|
84
|
+
if (tag)
|
|
85
|
+
invariant(
|
|
86
|
+
validateTag(new BigNumber(tag)),
|
|
87
|
+
`tag is set but is not in a valid format, should be between [0 - ${uint32maxPlus1
|
|
88
|
+
.minus(1)
|
|
89
|
+
.toString()}]`
|
|
90
|
+
);
|
|
91
|
+
|
|
132
92
|
o.next({
|
|
133
93
|
type: "device-signature-requested",
|
|
134
94
|
});
|
|
135
|
-
signature = await signTransaction(
|
|
95
|
+
const signature = await signTransaction(
|
|
136
96
|
account.currency,
|
|
137
97
|
transport,
|
|
138
98
|
account.freshAddressPath,
|
|
139
|
-
|
|
99
|
+
payment
|
|
140
100
|
);
|
|
141
101
|
o.next({
|
|
142
102
|
type: "device-signature-granted",
|
|
@@ -155,15 +115,10 @@ const signOperation = ({
|
|
|
155
115
|
senders: [account.freshAddress],
|
|
156
116
|
recipients: [transaction.recipient],
|
|
157
117
|
date: new Date(),
|
|
158
|
-
|
|
159
|
-
transactionSequenceNumber: await getSequenceNumber(account),
|
|
118
|
+
transactionSequenceNumber: nextSequenceNumber,
|
|
160
119
|
extra: {} as any,
|
|
161
120
|
};
|
|
162
121
|
|
|
163
|
-
if (transaction.tag) {
|
|
164
|
-
operation.extra.tag = transaction.tag;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
122
|
o.next({
|
|
168
123
|
type: "signed",
|
|
169
124
|
signedOperation: {
|
|
@@ -188,7 +143,9 @@ const signOperation = ({
|
|
|
188
143
|
})
|
|
189
144
|
);
|
|
190
145
|
|
|
191
|
-
const broadcast = async ({
|
|
146
|
+
const broadcast = async ({
|
|
147
|
+
signedOperation: { signature, operation },
|
|
148
|
+
}): Promise<Operation> => {
|
|
192
149
|
const submittedPayment = await submit(signature);
|
|
193
150
|
|
|
194
151
|
if (
|
|
@@ -202,7 +159,7 @@ const broadcast = async ({ signedOperation: { signature, operation } }) => {
|
|
|
202
159
|
return patchOperationWithHash(operation, hash);
|
|
203
160
|
};
|
|
204
161
|
|
|
205
|
-
function isRecipientValid(recipient) {
|
|
162
|
+
function isRecipientValid(recipient: string): boolean {
|
|
206
163
|
try {
|
|
207
164
|
bs58check.decode(recipient);
|
|
208
165
|
return true;
|
|
@@ -211,125 +168,14 @@ function isRecipientValid(recipient) {
|
|
|
211
168
|
}
|
|
212
169
|
}
|
|
213
170
|
|
|
214
|
-
|
|
215
|
-
type: string;
|
|
216
|
-
address: string;
|
|
217
|
-
sequence: number;
|
|
218
|
-
id: string;
|
|
219
|
-
specification: {
|
|
220
|
-
source: {
|
|
221
|
-
address: string;
|
|
222
|
-
maxAmount: {
|
|
223
|
-
currency: string;
|
|
224
|
-
value: string;
|
|
225
|
-
};
|
|
226
|
-
};
|
|
227
|
-
destination: {
|
|
228
|
-
address: string;
|
|
229
|
-
amount: {
|
|
230
|
-
currency: string;
|
|
231
|
-
value: string;
|
|
232
|
-
};
|
|
233
|
-
tag?: string;
|
|
234
|
-
};
|
|
235
|
-
paths: string;
|
|
236
|
-
};
|
|
237
|
-
outcome: {
|
|
238
|
-
result: string;
|
|
239
|
-
fee: string;
|
|
240
|
-
timestamp: string;
|
|
241
|
-
deliveredAmount?: {
|
|
242
|
-
currency: string;
|
|
243
|
-
value: string;
|
|
244
|
-
counterparty: string;
|
|
245
|
-
};
|
|
246
|
-
balanceChanges: Record<
|
|
247
|
-
string,
|
|
248
|
-
Array<{
|
|
249
|
-
counterparty: string;
|
|
250
|
-
currency: string;
|
|
251
|
-
value: string;
|
|
252
|
-
}>
|
|
253
|
-
>;
|
|
254
|
-
orderbookChanges: Record<
|
|
255
|
-
string,
|
|
256
|
-
Array<{
|
|
257
|
-
direction: string;
|
|
258
|
-
quantity: {
|
|
259
|
-
currency: string;
|
|
260
|
-
value: string;
|
|
261
|
-
};
|
|
262
|
-
totalPrice: {
|
|
263
|
-
currency: string;
|
|
264
|
-
counterparty: string;
|
|
265
|
-
value: string;
|
|
266
|
-
};
|
|
267
|
-
makeExchangeRate: string;
|
|
268
|
-
sequence: number;
|
|
269
|
-
status: string;
|
|
270
|
-
}>
|
|
271
|
-
>;
|
|
272
|
-
ledgerVersion: number;
|
|
273
|
-
indexInLedger: number;
|
|
274
|
-
};
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const txToOperation =
|
|
278
|
-
(account: Account) =>
|
|
279
|
-
({
|
|
280
|
-
id,
|
|
281
|
-
sequence,
|
|
282
|
-
outcome: { fee, deliveredAmount, ledgerVersion, timestamp },
|
|
283
|
-
specification: { source, destination },
|
|
284
|
-
}: Tx): Operation | null | undefined => {
|
|
285
|
-
const type = source.address === account.freshAddress ? "OUT" : "IN";
|
|
286
|
-
let value = deliveredAmount
|
|
287
|
-
? parseAPICurrencyObject(deliveredAmount)
|
|
288
|
-
: new BigNumber(0);
|
|
289
|
-
const feeValue = parseAPIValue(fee);
|
|
290
|
-
|
|
291
|
-
if (type === "OUT") {
|
|
292
|
-
if (!Number.isNaN(feeValue)) {
|
|
293
|
-
value = value.plus(feeValue);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const op: Operation = {
|
|
298
|
-
id: `${account.id}-${id}-${type}`,
|
|
299
|
-
hash: id,
|
|
300
|
-
accountId: account.id,
|
|
301
|
-
type,
|
|
302
|
-
value,
|
|
303
|
-
fee: feeValue,
|
|
304
|
-
blockHash: null,
|
|
305
|
-
blockHeight: ledgerVersion,
|
|
306
|
-
senders: [source.address],
|
|
307
|
-
recipients: [destination.address],
|
|
308
|
-
date: new Date(timestamp),
|
|
309
|
-
transactionSequenceNumber: sequence,
|
|
310
|
-
extra: {},
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
if (destination.tag) {
|
|
314
|
-
op.extra.tag = destination.tag;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return op;
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const recipientIsNew = async (endpointConfig, recipient) => {
|
|
171
|
+
const recipientIsNew = async (recipient: string): Promise<boolean> => {
|
|
321
172
|
if (!isRecipientValid(recipient)) return false;
|
|
322
173
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
return false;
|
|
326
|
-
} catch (e) {
|
|
327
|
-
if (checkAccountNotFound(e)) {
|
|
328
|
-
throw e;
|
|
329
|
-
}
|
|
330
|
-
|
|
174
|
+
const info = await getAccountInfo(recipient);
|
|
175
|
+
if (info.error === NEW_ACCOUNT_ERROR_MESSAGE) {
|
|
331
176
|
return true;
|
|
332
177
|
}
|
|
178
|
+
return false;
|
|
333
179
|
};
|
|
334
180
|
|
|
335
181
|
// FIXME this could be cleaner
|
|
@@ -348,284 +194,18 @@ const remapError = (error) => {
|
|
|
348
194
|
|
|
349
195
|
const cacheRecipientsNew = {};
|
|
350
196
|
|
|
351
|
-
const cachedRecipientIsNew = (
|
|
197
|
+
const cachedRecipientIsNew = (recipient: string) => {
|
|
352
198
|
if (recipient in cacheRecipientsNew) return cacheRecipientsNew[recipient];
|
|
353
|
-
cacheRecipientsNew[recipient] = recipientIsNew(
|
|
199
|
+
cacheRecipientsNew[recipient] = recipientIsNew(recipient);
|
|
354
200
|
return cacheRecipientsNew[recipient];
|
|
355
201
|
};
|
|
356
202
|
|
|
357
203
|
const currencyBridge: CurrencyBridge = {
|
|
358
204
|
preload: () => Promise.resolve({}),
|
|
359
205
|
hydrate: () => {},
|
|
360
|
-
scanAccounts
|
|
361
|
-
withDevice(deviceId)(
|
|
362
|
-
(transport) =>
|
|
363
|
-
new Observable((o) => {
|
|
364
|
-
let finished = false;
|
|
365
|
-
|
|
366
|
-
const unsubscribe = () => {
|
|
367
|
-
finished = true;
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
async function main() {
|
|
371
|
-
try {
|
|
372
|
-
const serverInfo = await getServerInfo();
|
|
373
|
-
const ledgers = serverInfo.completeLedgers.split("-");
|
|
374
|
-
const minLedgerVersion = Number(ledgers[0]);
|
|
375
|
-
const maxLedgerVersion = Number(ledgers[1]);
|
|
376
|
-
const derivationModes = getDerivationModesForCurrency(currency);
|
|
377
|
-
|
|
378
|
-
for (const derivationMode of derivationModes) {
|
|
379
|
-
const derivationScheme = getDerivationScheme({
|
|
380
|
-
derivationMode,
|
|
381
|
-
currency,
|
|
382
|
-
});
|
|
383
|
-
const stopAt = isIterableDerivationMode(derivationMode)
|
|
384
|
-
? 255
|
|
385
|
-
: 1;
|
|
386
|
-
|
|
387
|
-
for (let index = 0; index < stopAt; index++) {
|
|
388
|
-
if (!derivationModeSupportsIndex(derivationMode, index))
|
|
389
|
-
continue;
|
|
390
|
-
const freshAddressPath = runDerivationScheme(
|
|
391
|
-
derivationScheme,
|
|
392
|
-
currency,
|
|
393
|
-
{
|
|
394
|
-
account: index,
|
|
395
|
-
}
|
|
396
|
-
);
|
|
397
|
-
const { address } = await getAddress(transport, {
|
|
398
|
-
currency,
|
|
399
|
-
path: freshAddressPath,
|
|
400
|
-
derivationMode,
|
|
401
|
-
});
|
|
402
|
-
if (finished) return;
|
|
403
|
-
const accountId = `ripplejs:2:${currency.id}:${address}:${derivationMode}`;
|
|
404
|
-
let info;
|
|
405
|
-
|
|
406
|
-
try {
|
|
407
|
-
info = await getAccountInfo(address);
|
|
408
|
-
} catch (e) {
|
|
409
|
-
if (checkAccountNotFound(e)) {
|
|
410
|
-
throw e;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// fresh address is address. ripple never changes.
|
|
415
|
-
const freshAddress = address;
|
|
416
|
-
|
|
417
|
-
if (!info) {
|
|
418
|
-
// account does not exist in Ripple server
|
|
419
|
-
// we are generating a new account locally
|
|
420
|
-
if (derivationMode === "") {
|
|
421
|
-
o.next({
|
|
422
|
-
type: "discovered",
|
|
423
|
-
account: {
|
|
424
|
-
type: "Account",
|
|
425
|
-
id: accountId,
|
|
426
|
-
seedIdentifier: freshAddress,
|
|
427
|
-
derivationMode,
|
|
428
|
-
name: getNewAccountPlaceholderName({
|
|
429
|
-
currency,
|
|
430
|
-
index,
|
|
431
|
-
derivationMode,
|
|
432
|
-
}),
|
|
433
|
-
starred: false,
|
|
434
|
-
used: false,
|
|
435
|
-
freshAddress,
|
|
436
|
-
freshAddressPath,
|
|
437
|
-
freshAddresses: [
|
|
438
|
-
{
|
|
439
|
-
address: freshAddress,
|
|
440
|
-
derivationPath: freshAddressPath,
|
|
441
|
-
},
|
|
442
|
-
],
|
|
443
|
-
balance: new BigNumber(0),
|
|
444
|
-
spendableBalance: new BigNumber(0),
|
|
445
|
-
blockHeight: maxLedgerVersion,
|
|
446
|
-
index,
|
|
447
|
-
currency,
|
|
448
|
-
operationsCount: 0,
|
|
449
|
-
operations: [],
|
|
450
|
-
pendingOperations: [],
|
|
451
|
-
unit: currency.units[0],
|
|
452
|
-
// @ts-expect-error archived does not exists on type Account
|
|
453
|
-
archived: false,
|
|
454
|
-
lastSyncDate: new Date(),
|
|
455
|
-
creationDate: new Date(),
|
|
456
|
-
swapHistory: [],
|
|
457
|
-
balanceHistoryCache: emptyHistoryCache, // calculated in the jsHelpers
|
|
458
|
-
},
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
if (finished) return;
|
|
466
|
-
const balance = parseAPIValue(info.xrpBalance);
|
|
467
|
-
invariant(
|
|
468
|
-
!balance.isNaN() && balance.isFinite(),
|
|
469
|
-
`Ripple: invalid balance=${balance.toString()} for address ${address}`
|
|
470
|
-
);
|
|
471
|
-
const transactions = await getTransactions(address, {
|
|
472
|
-
minLedgerVersion,
|
|
473
|
-
maxLedgerVersion,
|
|
474
|
-
types: ["payment"],
|
|
475
|
-
});
|
|
476
|
-
if (finished) return;
|
|
477
|
-
const account: Account = {
|
|
478
|
-
type: "Account",
|
|
479
|
-
id: accountId,
|
|
480
|
-
seedIdentifier: freshAddress,
|
|
481
|
-
derivationMode,
|
|
482
|
-
name: getAccountPlaceholderName({
|
|
483
|
-
currency,
|
|
484
|
-
index,
|
|
485
|
-
derivationMode,
|
|
486
|
-
}),
|
|
487
|
-
starred: false,
|
|
488
|
-
used: true,
|
|
489
|
-
freshAddress,
|
|
490
|
-
freshAddressPath,
|
|
491
|
-
freshAddresses: [
|
|
492
|
-
{
|
|
493
|
-
address: freshAddress,
|
|
494
|
-
derivationPath: freshAddressPath,
|
|
495
|
-
},
|
|
496
|
-
],
|
|
497
|
-
balance,
|
|
498
|
-
spendableBalance: balance,
|
|
499
|
-
// TODO calc with base reserve
|
|
500
|
-
blockHeight: maxLedgerVersion,
|
|
501
|
-
index,
|
|
502
|
-
currency,
|
|
503
|
-
operationsCount: 0,
|
|
504
|
-
operations: [],
|
|
505
|
-
pendingOperations: [],
|
|
506
|
-
unit: currency.units[0],
|
|
507
|
-
lastSyncDate: new Date(),
|
|
508
|
-
creationDate: new Date(),
|
|
509
|
-
swapHistory: [],
|
|
510
|
-
balanceHistoryCache: emptyHistoryCache, // calculated in the jsHelpers
|
|
511
|
-
};
|
|
512
|
-
account.operations = transactions
|
|
513
|
-
.map(txToOperation(account))
|
|
514
|
-
.filter(Boolean);
|
|
515
|
-
account.operationsCount = account.operations.length;
|
|
516
|
-
|
|
517
|
-
if (account.operations.length > 0) {
|
|
518
|
-
account.creationDate =
|
|
519
|
-
account.operations[account.operations.length - 1].date;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
o.next({
|
|
523
|
-
type: "discovered",
|
|
524
|
-
account,
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
o.complete();
|
|
530
|
-
} catch (e) {
|
|
531
|
-
o.error(e);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
main();
|
|
536
|
-
return unsubscribe;
|
|
537
|
-
})
|
|
538
|
-
),
|
|
206
|
+
scanAccounts,
|
|
539
207
|
};
|
|
540
208
|
|
|
541
|
-
const sync = ({
|
|
542
|
-
endpointConfig,
|
|
543
|
-
freshAddress,
|
|
544
|
-
blockHeight,
|
|
545
|
-
operations,
|
|
546
|
-
}: any): Observable<(arg0: Account) => Account> =>
|
|
547
|
-
new Observable((o) => {
|
|
548
|
-
let finished = false;
|
|
549
|
-
const currentOpsLength = operations ? operations.length : 0;
|
|
550
|
-
|
|
551
|
-
const unsubscribe = () => {
|
|
552
|
-
finished = true;
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
async function main() {
|
|
556
|
-
try {
|
|
557
|
-
if (finished) return;
|
|
558
|
-
const serverInfo = await getServerInfo(endpointConfig);
|
|
559
|
-
if (finished) return;
|
|
560
|
-
const ledgers = serverInfo.completeLedgers.split("-");
|
|
561
|
-
const minLedgerVersion = Number(ledgers[0]);
|
|
562
|
-
const maxLedgerVersion = Number(ledgers[1]);
|
|
563
|
-
let info;
|
|
564
|
-
|
|
565
|
-
try {
|
|
566
|
-
info = await getAccountInfo(freshAddress);
|
|
567
|
-
} catch (e) {
|
|
568
|
-
if (checkAccountNotFound(e)) {
|
|
569
|
-
throw e;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (finished) return;
|
|
574
|
-
|
|
575
|
-
if (!info) {
|
|
576
|
-
// account does not exist, we have nothing to sync but to update the last sync date
|
|
577
|
-
o.next((a) => ({ ...a, lastSyncDate: new Date() }));
|
|
578
|
-
o.complete();
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const balance = parseAPIValue(info.xrpBalance);
|
|
583
|
-
invariant(
|
|
584
|
-
!balance.isNaN() && balance.isFinite(),
|
|
585
|
-
`Ripple: invalid balance=${balance.toString()} for address ${freshAddress}`
|
|
586
|
-
);
|
|
587
|
-
const transactions = await getTransactions(freshAddress, {
|
|
588
|
-
minLedgerVersion: Math.max(
|
|
589
|
-
currentOpsLength === 0 ? 0 : blockHeight, // if there is no ops, it might be after a clear and we prefer to pull from the oldest possible history
|
|
590
|
-
minLedgerVersion
|
|
591
|
-
),
|
|
592
|
-
maxLedgerVersion,
|
|
593
|
-
types: ["payment"],
|
|
594
|
-
});
|
|
595
|
-
if (finished) return;
|
|
596
|
-
o.next((a) => {
|
|
597
|
-
const newOps = transactions.map(txToOperation(a));
|
|
598
|
-
const operations = mergeOps(a.operations, newOps);
|
|
599
|
-
const [last] = operations;
|
|
600
|
-
const pendingOperations = a.pendingOperations.filter(
|
|
601
|
-
(oo) =>
|
|
602
|
-
!operations.some((op) => oo.hash === op.hash) &&
|
|
603
|
-
last &&
|
|
604
|
-
last.transactionSequenceNumber &&
|
|
605
|
-
oo.transactionSequenceNumber &&
|
|
606
|
-
oo.transactionSequenceNumber > last.transactionSequenceNumber
|
|
607
|
-
);
|
|
608
|
-
return {
|
|
609
|
-
...a,
|
|
610
|
-
balance,
|
|
611
|
-
spendableBalance: balance,
|
|
612
|
-
// TODO use reserve
|
|
613
|
-
operations,
|
|
614
|
-
pendingOperations,
|
|
615
|
-
blockHeight: maxLedgerVersion,
|
|
616
|
-
lastSyncDate: new Date(),
|
|
617
|
-
};
|
|
618
|
-
});
|
|
619
|
-
o.complete();
|
|
620
|
-
} catch (e) {
|
|
621
|
-
o.error(remapError(e));
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
main();
|
|
626
|
-
return unsubscribe;
|
|
627
|
-
});
|
|
628
|
-
|
|
629
209
|
const createTransaction = (): Transaction => ({
|
|
630
210
|
family: "ripple",
|
|
631
211
|
amount: new BigNumber(0),
|
|
@@ -636,15 +216,23 @@ const createTransaction = (): Transaction => ({
|
|
|
636
216
|
feeCustomUnit: null,
|
|
637
217
|
});
|
|
638
218
|
|
|
639
|
-
const updateTransaction = (
|
|
219
|
+
const updateTransaction = (
|
|
220
|
+
t: Transaction,
|
|
221
|
+
patch: Transaction
|
|
222
|
+
): Transaction => ({ ...t, ...patch });
|
|
640
223
|
|
|
641
|
-
const prepareTransaction = async (
|
|
224
|
+
const prepareTransaction = async (
|
|
225
|
+
a: Account,
|
|
226
|
+
t: Transaction
|
|
227
|
+
): Promise<Transaction> => {
|
|
642
228
|
let networkInfo: NetworkInfo | null | undefined = t.networkInfo;
|
|
643
229
|
|
|
644
230
|
if (!networkInfo) {
|
|
645
231
|
try {
|
|
646
232
|
const info = await getServerInfo(a.endpointConfig);
|
|
647
|
-
const serverFee = parseAPIValue(
|
|
233
|
+
const serverFee = parseAPIValue(
|
|
234
|
+
info.info.validated_ledger.base_fee_xrp.toString()
|
|
235
|
+
);
|
|
648
236
|
networkInfo = {
|
|
649
237
|
family: "ripple",
|
|
650
238
|
serverFee,
|
|
@@ -664,7 +252,7 @@ const prepareTransaction = async (a: Account, t: Transaction) => {
|
|
|
664
252
|
return t;
|
|
665
253
|
};
|
|
666
254
|
|
|
667
|
-
const getTransactionStatus = async (a, t) => {
|
|
255
|
+
const getTransactionStatus = async (a: Account, t: Transaction) => {
|
|
668
256
|
const errors: {
|
|
669
257
|
fee?: Error;
|
|
670
258
|
amount?: Error;
|
|
@@ -674,7 +262,9 @@ const getTransactionStatus = async (a, t) => {
|
|
|
674
262
|
feeTooHigh?: Error;
|
|
675
263
|
} = {};
|
|
676
264
|
const r = await getServerInfo(a.endpointConfig);
|
|
677
|
-
const reserveBaseXRP = parseAPIValue(
|
|
265
|
+
const reserveBaseXRP = parseAPIValue(
|
|
266
|
+
r.info.validated_ledger.reserve_base_xrp.toString()
|
|
267
|
+
);
|
|
678
268
|
const estimatedFees = new BigNumber(t.fee || 0);
|
|
679
269
|
const totalSpent = new BigNumber(t.amount).plus(estimatedFees);
|
|
680
270
|
const amount = new BigNumber(t.amount);
|
|
@@ -697,12 +287,15 @@ const getTransactionStatus = async (a, t) => {
|
|
|
697
287
|
});
|
|
698
288
|
} else if (
|
|
699
289
|
t.recipient &&
|
|
700
|
-
(await cachedRecipientIsNew(
|
|
290
|
+
(await cachedRecipientIsNew(t.recipient)) &&
|
|
701
291
|
t.amount.lt(reserveBaseXRP)
|
|
702
292
|
) {
|
|
703
|
-
const f = formatAPICurrencyXRP(reserveBaseXRP);
|
|
704
293
|
errors.amount = new NotEnoughBalanceBecauseDestinationNotCreated("", {
|
|
705
|
-
minimalAmount:
|
|
294
|
+
minimalAmount: formatCurrencyUnit(a.currency.units[0], reserveBaseXRP, {
|
|
295
|
+
disableRounding: true,
|
|
296
|
+
useGrouping: false,
|
|
297
|
+
showCode: true,
|
|
298
|
+
}),
|
|
706
299
|
});
|
|
707
300
|
}
|
|
708
301
|
|
|
@@ -737,10 +330,12 @@ const estimateMaxSpendable = async ({
|
|
|
737
330
|
account,
|
|
738
331
|
parentAccount,
|
|
739
332
|
transaction,
|
|
740
|
-
}) => {
|
|
333
|
+
}): Promise<BigNumber> => {
|
|
741
334
|
const mainAccount = getMainAccount(account, parentAccount);
|
|
742
335
|
const r = await getServerInfo(mainAccount.endpointConfig);
|
|
743
|
-
const reserveBaseXRP = parseAPIValue(
|
|
336
|
+
const reserveBaseXRP = parseAPIValue(
|
|
337
|
+
r.info.validated_ledger.reserve_base_xrp.toString()
|
|
338
|
+
);
|
|
744
339
|
const t = await prepareTransaction(mainAccount, {
|
|
745
340
|
...createTransaction(),
|
|
746
341
|
...transaction,
|
|
@@ -761,7 +356,7 @@ const accountBridge: AccountBridge<Transaction> = {
|
|
|
761
356
|
prepareTransaction,
|
|
762
357
|
getTransactionStatus,
|
|
763
358
|
estimateMaxSpendable,
|
|
764
|
-
sync,
|
|
359
|
+
sync: sync,
|
|
765
360
|
receive,
|
|
766
361
|
signOperation,
|
|
767
362
|
broadcast,
|