@ledgerhq/live-common 24.0.0 → 24.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__/range.js +8 -0
- package/lib/__tests__/range.js.map +1 -1
- package/lib/api/Ethereum.d.ts +7 -0
- package/lib/api/Ethereum.d.ts.map +1 -1
- package/lib/api/Ethereum.js +29 -1
- package/lib/api/Ethereum.js.map +1 -1
- package/lib/bridge/jsHelpers.d.ts +2 -1
- package/lib/bridge/jsHelpers.d.ts.map +1 -1
- package/lib/bridge/jsHelpers.js +4 -2
- package/lib/bridge/jsHelpers.js.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.js +2 -173
- package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
- package/lib/families/bitcoin/logic.d.ts +6 -1
- package/lib/families/bitcoin/logic.d.ts.map +1 -1
- package/lib/families/bitcoin/logic.js +186 -1
- package/lib/families/bitcoin/logic.js.map +1 -1
- package/lib/families/bitcoin/xpub.txs.txNoOutput.unit.test.d.ts +2 -0
- package/lib/families/bitcoin/xpub.txs.txNoOutput.unit.test.d.ts.map +1 -0
- package/lib/families/bitcoin/xpub.txs.txNoOutput.unit.test.js +72 -0
- package/lib/families/bitcoin/xpub.txs.txNoOutput.unit.test.js.map +1 -0
- package/lib/families/cosmos/api/Cosmos.d.ts.map +1 -1
- package/lib/families/cosmos/api/Cosmos.js +1 -0
- package/lib/families/cosmos/api/Cosmos.js.map +1 -1
- package/lib/families/cosmos/react.test.js +5 -4
- package/lib/families/cosmos/react.test.js.map +1 -1
- package/lib/families/ethereum/bridge/js.d.ts.map +1 -1
- package/lib/families/ethereum/bridge/js.js +5 -1
- package/lib/families/ethereum/bridge/js.js.map +1 -1
- package/lib/families/ethereum/datasets/ethereum2.d.ts +3 -0
- package/lib/families/ethereum/datasets/ethereum2.d.ts.map +1 -0
- package/lib/families/ethereum/datasets/ethereum2.js +22 -0
- package/lib/families/ethereum/datasets/ethereum2.js.map +1 -0
- package/lib/families/ethereum/synchronisation.d.ts.map +1 -1
- package/lib/families/ethereum/synchronisation.integration.test.js +68 -0
- package/lib/families/ethereum/synchronisation.integration.test.js.map +1 -1
- package/lib/families/ethereum/synchronisation.js +17 -11
- package/lib/families/ethereum/synchronisation.js.map +1 -1
- package/lib/hw/extractOnboardingState.d.ts +26 -0
- package/lib/hw/extractOnboardingState.d.ts.map +1 -0
- package/lib/hw/extractOnboardingState.js +69 -0
- package/lib/hw/extractOnboardingState.js.map +1 -0
- package/lib/hw/extractOnboardingState.test.d.ts +2 -0
- package/lib/hw/extractOnboardingState.test.d.ts.map +1 -0
- package/lib/hw/extractOnboardingState.test.js +182 -0
- package/lib/hw/extractOnboardingState.test.js.map +1 -0
- package/lib/hw/getOnboardingStatePolling.d.ts +20 -0
- package/lib/hw/getOnboardingStatePolling.d.ts.map +1 -0
- package/lib/hw/getOnboardingStatePolling.js +117 -0
- package/lib/hw/getOnboardingStatePolling.js.map +1 -0
- package/lib/hw/getOnboardingStatePolling.test.d.ts +2 -0
- package/lib/hw/getOnboardingStatePolling.test.d.ts.map +1 -0
- package/lib/hw/getOnboardingStatePolling.test.js +224 -0
- package/lib/hw/getOnboardingStatePolling.test.js.map +1 -0
- package/lib/onboarding/hooks/useOnboardingStatePolling.d.ts +22 -0
- package/lib/onboarding/hooks/useOnboardingStatePolling.d.ts.map +1 -0
- package/lib/onboarding/hooks/useOnboardingStatePolling.js +81 -0
- package/lib/onboarding/hooks/useOnboardingStatePolling.js.map +1 -0
- package/lib/onboarding/hooks/useOnboardingStatePolling.test.d.ts +2 -0
- package/lib/onboarding/hooks/useOnboardingStatePolling.test.d.ts.map +1 -0
- package/lib/onboarding/hooks/useOnboardingStatePolling.test.js +374 -0
- package/lib/onboarding/hooks/useOnboardingStatePolling.test.js.map +1 -0
- package/lib/operation.d.ts +7 -0
- package/lib/operation.d.ts.map +1 -1
- package/lib/operation.js +15 -1
- package/lib/operation.js.map +1 -1
- package/lib/types/manager.d.ts +5 -0
- package/lib/types/manager.d.ts.map +1 -1
- package/lib/types/manager.js +7 -1
- package/lib/types/manager.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/range.ts +13 -0
- package/src/api/Ethereum.ts +31 -0
- package/src/bridge/jsHelpers.ts +7 -1
- package/src/families/bitcoin/js-synchronisation.ts +3 -172
- package/src/families/bitcoin/logic.ts +169 -0
- package/src/families/bitcoin/xpub.txs.txNoOutput.unit.test.ts +37 -0
- package/src/families/cosmos/api/Cosmos.ts +4 -0
- package/src/families/cosmos/react.test.ts +3 -0
- package/src/families/ethereum/bridge/js.ts +5 -1
- package/src/families/ethereum/datasets/ethereum2.ts +20 -0
- package/src/families/ethereum/synchronisation.integration.test.ts +63 -1
- package/src/families/ethereum/synchronisation.ts +15 -7
- package/src/hw/extractOnboardingState.test.ts +251 -0
- package/src/hw/extractOnboardingState.ts +97 -0
- package/src/hw/getOnboardingStatePolling.test.ts +247 -0
- package/src/hw/getOnboardingStatePolling.ts +153 -0
- package/src/onboarding/hooks/useOnboardingStatePolling.test.ts +304 -0
- package/src/onboarding/hooks/useOnboardingStatePolling.ts +86 -0
- package/src/operation.ts +24 -0
- package/src/types/manager.ts +5 -0
package/src/api/Ethereum.ts
CHANGED
|
@@ -72,26 +72,38 @@ export type Tx = {
|
|
|
72
72
|
time: string;
|
|
73
73
|
};
|
|
74
74
|
};
|
|
75
|
+
|
|
75
76
|
export type ERC20BalancesInput = Array<{
|
|
76
77
|
address: string;
|
|
77
78
|
contract: string;
|
|
78
79
|
}>;
|
|
80
|
+
|
|
79
81
|
export type ERC20BalanceOutput = Array<{
|
|
80
82
|
address: string;
|
|
81
83
|
contract: string;
|
|
82
84
|
balance: BigNumber;
|
|
83
85
|
}>;
|
|
86
|
+
|
|
84
87
|
export type NFTMetadataInput = Readonly<
|
|
85
88
|
Array<{
|
|
86
89
|
contract: string;
|
|
87
90
|
tokenId: string;
|
|
88
91
|
}>
|
|
89
92
|
>;
|
|
93
|
+
|
|
90
94
|
export type NFTCollectionMetadataInput = Readonly<
|
|
91
95
|
Array<{
|
|
92
96
|
contract: string;
|
|
93
97
|
}>
|
|
94
98
|
>;
|
|
99
|
+
|
|
100
|
+
export type BlockByHashOutput = {
|
|
101
|
+
hash: string;
|
|
102
|
+
height: number;
|
|
103
|
+
time: string;
|
|
104
|
+
txs: string[];
|
|
105
|
+
};
|
|
106
|
+
|
|
95
107
|
export type API = {
|
|
96
108
|
getTransactions: (
|
|
97
109
|
address: string,
|
|
@@ -133,6 +145,7 @@ export type API = {
|
|
|
133
145
|
medium: BigNumber;
|
|
134
146
|
high: BigNumber;
|
|
135
147
|
}>;
|
|
148
|
+
getBlockByHash: (blockHash: string) => Promise<BlockByHashOutput | undefined>;
|
|
136
149
|
};
|
|
137
150
|
export const apiForCurrency = (currency: CryptoCurrency): API => {
|
|
138
151
|
const baseURL = blockchainBaseURL(currency);
|
|
@@ -328,5 +341,23 @@ export const apiForCurrency = (currency: CryptoCurrency): API => {
|
|
|
328
341
|
maxAge: 30 * 1000,
|
|
329
342
|
}
|
|
330
343
|
),
|
|
344
|
+
|
|
345
|
+
async getBlockByHash(blockHash) {
|
|
346
|
+
if (!blockHash) {
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const { data }: { data: BlockByHashOutput[] } = await network({
|
|
352
|
+
method: "GET",
|
|
353
|
+
url: `${baseURL}/blocks/${blockHash}`,
|
|
354
|
+
transformResponse: JSONBigNumber.parse,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
return data[0];
|
|
358
|
+
} catch (e) {
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
},
|
|
331
362
|
};
|
|
332
363
|
};
|
package/src/bridge/jsHelpers.ts
CHANGED
|
@@ -172,9 +172,11 @@ export const makeSync =
|
|
|
172
172
|
({
|
|
173
173
|
getAccountShape,
|
|
174
174
|
postSync = (_, a) => a,
|
|
175
|
+
shouldMergeOps = true,
|
|
175
176
|
}: {
|
|
176
177
|
getAccountShape: GetAccountShape;
|
|
177
178
|
postSync?: (initial: Account, synced: Account) => Account;
|
|
179
|
+
shouldMergeOps?: boolean;
|
|
178
180
|
}): AccountBridge<any>["sync"] =>
|
|
179
181
|
(initial, syncConfig): Observable<AccountUpdater> =>
|
|
180
182
|
Observable.create((o) => {
|
|
@@ -205,10 +207,14 @@ export const makeSync =
|
|
|
205
207
|
},
|
|
206
208
|
syncConfig
|
|
207
209
|
);
|
|
210
|
+
|
|
208
211
|
o.next((acc) => {
|
|
209
212
|
const a = needClear ? clearAccount(acc) : acc;
|
|
210
213
|
// FIXME reconsider doing mergeOps here. work is redundant for impl like eth
|
|
211
|
-
const operations =
|
|
214
|
+
const operations = shouldMergeOps
|
|
215
|
+
? mergeOps(a.operations, shape.operations || [])
|
|
216
|
+
: shape.operations || [];
|
|
217
|
+
|
|
212
218
|
return recalculateAccountBalanceHistories(
|
|
213
219
|
postSync(a, {
|
|
214
220
|
...a,
|
|
@@ -1,26 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
TX,
|
|
4
|
-
Currency,
|
|
5
|
-
Input as WalletInput,
|
|
6
|
-
Output as WalletOutput,
|
|
7
|
-
} from "./wallet-btc";
|
|
1
|
+
import type { Currency, Output as WalletOutput } from "./wallet-btc";
|
|
8
2
|
import { DerivationModes as WalletDerivationModes } from "./wallet-btc";
|
|
9
3
|
import { BigNumber } from "bignumber.js";
|
|
10
4
|
import Btc from "@ledgerhq/hw-app-btc";
|
|
11
5
|
import { log } from "@ledgerhq/logs";
|
|
12
6
|
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets";
|
|
13
|
-
import type {
|
|
14
|
-
Account,
|
|
15
|
-
Operation,
|
|
16
|
-
OperationType,
|
|
17
|
-
DerivationMode,
|
|
18
|
-
} from "../../types";
|
|
7
|
+
import type { Account, Operation, DerivationMode } from "../../types";
|
|
19
8
|
import type { GetAccountShape } from "../../bridge/jsHelpers";
|
|
20
9
|
import { makeSync, makeScanAccounts, mergeOps } from "../../bridge/jsHelpers";
|
|
21
10
|
import { findCurrencyExplorer } from "../../api/Ledger";
|
|
22
11
|
import { encodeAccountId } from "../../account";
|
|
23
|
-
import { encodeOperationId } from "../../operation";
|
|
24
12
|
import {
|
|
25
13
|
isSegwitDerivationMode,
|
|
26
14
|
isNativeSegwitDerivationMode,
|
|
@@ -30,6 +18,7 @@ import { BitcoinOutput } from "./types";
|
|
|
30
18
|
import { perCoinLogic } from "./logic";
|
|
31
19
|
import wallet from "./wallet-btc";
|
|
32
20
|
import { getAddressWithBtcInstance } from "./hw-getAddress";
|
|
21
|
+
import { mapTxToOperations } from "./logic";
|
|
33
22
|
|
|
34
23
|
// Map LL's DerivationMode to wallet-btc's
|
|
35
24
|
const toWalletDerivationMode = (
|
|
@@ -89,164 +78,6 @@ const deduplicateOperations = (
|
|
|
89
78
|
return out;
|
|
90
79
|
};
|
|
91
80
|
|
|
92
|
-
const mapTxToOperations = (
|
|
93
|
-
tx: TX,
|
|
94
|
-
currencyId: string,
|
|
95
|
-
accountId: string,
|
|
96
|
-
accountAddresses: Set<string>,
|
|
97
|
-
changeAddresses: Set<string>
|
|
98
|
-
): $Shape<Operation[]> => {
|
|
99
|
-
const operations: Operation[] = [];
|
|
100
|
-
const txId = tx.id;
|
|
101
|
-
const fee = new BigNumber(tx.fees);
|
|
102
|
-
const blockHeight = tx.block?.height;
|
|
103
|
-
const blockHash = tx.block?.hash;
|
|
104
|
-
const date = new Date(tx.block?.time || tx.received_at);
|
|
105
|
-
const senders = new Set<string>();
|
|
106
|
-
const recipients: string[] = [];
|
|
107
|
-
let type: OperationType = "OUT";
|
|
108
|
-
let value = new BigNumber(0);
|
|
109
|
-
const hasFailed = false;
|
|
110
|
-
const accountInputs: WalletInput[] = [];
|
|
111
|
-
const accountOutputs: WalletOutput[] = [];
|
|
112
|
-
const syncReplaceAddress = perCoinLogic[currencyId]?.syncReplaceAddress;
|
|
113
|
-
|
|
114
|
-
for (const input of tx.inputs) {
|
|
115
|
-
if (input.address) {
|
|
116
|
-
senders.add(
|
|
117
|
-
syncReplaceAddress ? syncReplaceAddress(input.address) : input.address
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
if (input.value) {
|
|
121
|
-
if (accountAddresses.has(input.address)) {
|
|
122
|
-
// This address is part of the account
|
|
123
|
-
value = value.plus(input.value);
|
|
124
|
-
accountInputs.push(input);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// All inputs of a same transaction have the same sequence
|
|
131
|
-
const transactionSequenceNumber =
|
|
132
|
-
(accountInputs.length > 0 && accountInputs[0].sequence) || undefined;
|
|
133
|
-
|
|
134
|
-
const hasSpentNothing = value.eq(0);
|
|
135
|
-
// Change output is always the last one
|
|
136
|
-
const changeOutputIndex = tx.outputs
|
|
137
|
-
.map((o) => o.output_index)
|
|
138
|
-
.reduce((p, c) => (p > c ? p : c));
|
|
139
|
-
|
|
140
|
-
for (const output of tx.outputs) {
|
|
141
|
-
if (output.address) {
|
|
142
|
-
if (!accountAddresses.has(output.address)) {
|
|
143
|
-
// The output doesn't belong to this account
|
|
144
|
-
if (
|
|
145
|
-
accountInputs.length > 0 && // It's a SEND operation
|
|
146
|
-
(tx.outputs.length === 1 || // The transaction has only 1 output
|
|
147
|
-
output.output_index < changeOutputIndex) // The output isn't the change output
|
|
148
|
-
) {
|
|
149
|
-
recipients.push(
|
|
150
|
-
syncReplaceAddress
|
|
151
|
-
? syncReplaceAddress(output.address)
|
|
152
|
-
: output.address
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
} else {
|
|
156
|
-
// The output belongs to this account
|
|
157
|
-
accountOutputs.push(output);
|
|
158
|
-
|
|
159
|
-
if (!changeAddresses.has(output.address)) {
|
|
160
|
-
// The output isn't a change output of this account
|
|
161
|
-
recipients.push(
|
|
162
|
-
syncReplaceAddress
|
|
163
|
-
? syncReplaceAddress(output.address)
|
|
164
|
-
: output.address
|
|
165
|
-
);
|
|
166
|
-
} else {
|
|
167
|
-
// The output is a change output of this account,
|
|
168
|
-
// we count it as a recipient only in some special cases
|
|
169
|
-
if (
|
|
170
|
-
(recipients.length === 0 &&
|
|
171
|
-
output.output_index >= changeOutputIndex) ||
|
|
172
|
-
hasSpentNothing
|
|
173
|
-
) {
|
|
174
|
-
recipients.push(
|
|
175
|
-
syncReplaceAddress
|
|
176
|
-
? syncReplaceAddress(output.address)
|
|
177
|
-
: output.address
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (accountInputs.length > 0) {
|
|
186
|
-
// It's a SEND operation
|
|
187
|
-
for (const output of accountOutputs) {
|
|
188
|
-
if (changeAddresses.has(output.address)) {
|
|
189
|
-
value = value.minus(output.value);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
type = "OUT";
|
|
194
|
-
operations.push({
|
|
195
|
-
id: encodeOperationId(accountId, txId, type),
|
|
196
|
-
hash: txId,
|
|
197
|
-
type,
|
|
198
|
-
value,
|
|
199
|
-
fee,
|
|
200
|
-
senders: Array.from(senders),
|
|
201
|
-
recipients,
|
|
202
|
-
blockHeight,
|
|
203
|
-
blockHash,
|
|
204
|
-
transactionSequenceNumber,
|
|
205
|
-
accountId,
|
|
206
|
-
date,
|
|
207
|
-
hasFailed,
|
|
208
|
-
extra: {},
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (accountOutputs.length > 0) {
|
|
213
|
-
// It's a RECEIVE operation
|
|
214
|
-
const filterChangeAddresses = !!accountInputs.length;
|
|
215
|
-
let accountOutputCount = 0;
|
|
216
|
-
let finalAmount = new BigNumber(0);
|
|
217
|
-
|
|
218
|
-
for (const output of accountOutputs) {
|
|
219
|
-
if (!filterChangeAddresses || !changeAddresses.has(output.address)) {
|
|
220
|
-
finalAmount = finalAmount.plus(output.value);
|
|
221
|
-
accountOutputCount += 1;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (accountOutputCount > 0) {
|
|
226
|
-
value = finalAmount;
|
|
227
|
-
type = "IN";
|
|
228
|
-
operations.push({
|
|
229
|
-
id: encodeOperationId(accountId, txId, type),
|
|
230
|
-
hash: txId,
|
|
231
|
-
type,
|
|
232
|
-
value,
|
|
233
|
-
fee,
|
|
234
|
-
senders: Array.from(senders),
|
|
235
|
-
recipients,
|
|
236
|
-
blockHeight,
|
|
237
|
-
blockHash,
|
|
238
|
-
transactionSequenceNumber,
|
|
239
|
-
accountId,
|
|
240
|
-
date,
|
|
241
|
-
hasFailed,
|
|
242
|
-
extra: {},
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return operations;
|
|
248
|
-
};
|
|
249
|
-
|
|
250
81
|
const getAccountShape: GetAccountShape = async (info) => {
|
|
251
82
|
const {
|
|
252
83
|
transport,
|
|
@@ -9,6 +9,15 @@ import type {
|
|
|
9
9
|
NetworkInfo,
|
|
10
10
|
UtxoStrategy,
|
|
11
11
|
} from "./types";
|
|
12
|
+
import { $Shape } from "utility-types";
|
|
13
|
+
import type {
|
|
14
|
+
TX,
|
|
15
|
+
Input as WalletInput,
|
|
16
|
+
Output as WalletOutput,
|
|
17
|
+
} from "./wallet-btc";
|
|
18
|
+
import { BigNumber } from "bignumber.js";
|
|
19
|
+
import { encodeOperationId } from "../../operation";
|
|
20
|
+
import type { Operation, OperationType } from "../../types";
|
|
12
21
|
|
|
13
22
|
// correspond ~ to min relay fees but determined empirically for a tx to be accepted by network
|
|
14
23
|
const minFees = {
|
|
@@ -176,3 +185,163 @@ export const perCoinLogic: Record<
|
|
|
176
185
|
}),
|
|
177
186
|
},
|
|
178
187
|
};
|
|
188
|
+
|
|
189
|
+
export const mapTxToOperations = (
|
|
190
|
+
tx: TX,
|
|
191
|
+
currencyId: string,
|
|
192
|
+
accountId: string,
|
|
193
|
+
accountAddresses: Set<string>,
|
|
194
|
+
changeAddresses: Set<string>
|
|
195
|
+
): $Shape<Operation[]> => {
|
|
196
|
+
const operations: Operation[] = [];
|
|
197
|
+
const txId = tx.id;
|
|
198
|
+
const fee = new BigNumber(tx.fees);
|
|
199
|
+
const blockHeight = tx.block?.height;
|
|
200
|
+
const blockHash = tx.block?.hash;
|
|
201
|
+
const date = new Date(tx.block?.time || tx.received_at);
|
|
202
|
+
const senders = new Set<string>();
|
|
203
|
+
const recipients: string[] = [];
|
|
204
|
+
let type: OperationType = "OUT";
|
|
205
|
+
let value = new BigNumber(0);
|
|
206
|
+
const hasFailed = false;
|
|
207
|
+
const accountInputs: WalletInput[] = [];
|
|
208
|
+
const accountOutputs: WalletOutput[] = [];
|
|
209
|
+
const syncReplaceAddress = perCoinLogic[currencyId]?.syncReplaceAddress;
|
|
210
|
+
|
|
211
|
+
for (const input of tx.inputs) {
|
|
212
|
+
if (input.address) {
|
|
213
|
+
senders.add(
|
|
214
|
+
syncReplaceAddress ? syncReplaceAddress(input.address) : input.address
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
if (input.value) {
|
|
218
|
+
if (accountAddresses.has(input.address)) {
|
|
219
|
+
// This address is part of the account
|
|
220
|
+
value = value.plus(input.value);
|
|
221
|
+
accountInputs.push(input);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// All inputs of a same transaction have the same sequence
|
|
228
|
+
const transactionSequenceNumber =
|
|
229
|
+
(accountInputs.length > 0 && accountInputs[0].sequence) || undefined;
|
|
230
|
+
|
|
231
|
+
const hasSpentNothing = value.eq(0);
|
|
232
|
+
|
|
233
|
+
// Change output is always the last one
|
|
234
|
+
const changeOutputIndex =
|
|
235
|
+
tx.outputs.length === 0
|
|
236
|
+
? 0
|
|
237
|
+
: tx.outputs.map((o) => o.output_index).reduce((p, c) => (p > c ? p : c));
|
|
238
|
+
|
|
239
|
+
for (const output of tx.outputs) {
|
|
240
|
+
if (output.address) {
|
|
241
|
+
if (!accountAddresses.has(output.address)) {
|
|
242
|
+
// The output doesn't belong to this account
|
|
243
|
+
if (
|
|
244
|
+
accountInputs.length > 0 && // It's a SEND operation
|
|
245
|
+
(tx.outputs.length === 1 || // The transaction has only 1 output
|
|
246
|
+
output.output_index < changeOutputIndex) // The output isn't the change output
|
|
247
|
+
) {
|
|
248
|
+
recipients.push(
|
|
249
|
+
syncReplaceAddress
|
|
250
|
+
? syncReplaceAddress(output.address)
|
|
251
|
+
: output.address
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
// The output belongs to this account
|
|
256
|
+
accountOutputs.push(output);
|
|
257
|
+
|
|
258
|
+
if (!changeAddresses.has(output.address)) {
|
|
259
|
+
// The output isn't a change output of this account
|
|
260
|
+
recipients.push(
|
|
261
|
+
syncReplaceAddress
|
|
262
|
+
? syncReplaceAddress(output.address)
|
|
263
|
+
: output.address
|
|
264
|
+
);
|
|
265
|
+
} else {
|
|
266
|
+
// The output is a change output of this account,
|
|
267
|
+
// we count it as a recipient only in some special cases
|
|
268
|
+
if (
|
|
269
|
+
(recipients.length === 0 &&
|
|
270
|
+
output.output_index >= changeOutputIndex) ||
|
|
271
|
+
hasSpentNothing
|
|
272
|
+
) {
|
|
273
|
+
recipients.push(
|
|
274
|
+
syncReplaceAddress
|
|
275
|
+
? syncReplaceAddress(output.address)
|
|
276
|
+
: output.address
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (accountInputs.length > 0) {
|
|
285
|
+
// It's a SEND operation
|
|
286
|
+
for (const output of accountOutputs) {
|
|
287
|
+
if (changeAddresses.has(output.address)) {
|
|
288
|
+
value = value.minus(output.value);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
type = "OUT";
|
|
293
|
+
operations.push({
|
|
294
|
+
id: encodeOperationId(accountId, txId, type),
|
|
295
|
+
hash: txId,
|
|
296
|
+
type,
|
|
297
|
+
value,
|
|
298
|
+
fee,
|
|
299
|
+
senders: Array.from(senders),
|
|
300
|
+
recipients,
|
|
301
|
+
blockHeight,
|
|
302
|
+
blockHash,
|
|
303
|
+
transactionSequenceNumber,
|
|
304
|
+
accountId,
|
|
305
|
+
date,
|
|
306
|
+
hasFailed,
|
|
307
|
+
extra: {},
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (accountOutputs.length > 0) {
|
|
312
|
+
// It's a RECEIVE operation
|
|
313
|
+
const filterChangeAddresses = !!accountInputs.length;
|
|
314
|
+
let accountOutputCount = 0;
|
|
315
|
+
let finalAmount = new BigNumber(0);
|
|
316
|
+
|
|
317
|
+
for (const output of accountOutputs) {
|
|
318
|
+
if (!filterChangeAddresses || !changeAddresses.has(output.address)) {
|
|
319
|
+
finalAmount = finalAmount.plus(output.value);
|
|
320
|
+
accountOutputCount += 1;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (accountOutputCount > 0) {
|
|
325
|
+
value = finalAmount;
|
|
326
|
+
type = "IN";
|
|
327
|
+
operations.push({
|
|
328
|
+
id: encodeOperationId(accountId, txId, type),
|
|
329
|
+
hash: txId,
|
|
330
|
+
type,
|
|
331
|
+
value,
|
|
332
|
+
fee,
|
|
333
|
+
senders: Array.from(senders),
|
|
334
|
+
recipients,
|
|
335
|
+
blockHeight,
|
|
336
|
+
blockHash,
|
|
337
|
+
transactionSequenceNumber,
|
|
338
|
+
accountId,
|
|
339
|
+
date,
|
|
340
|
+
hasFailed,
|
|
341
|
+
extra: {},
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return operations;
|
|
347
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { mapTxToOperations } from "./logic";
|
|
2
|
+
|
|
3
|
+
describe("testing transaction without output", () => {
|
|
4
|
+
it("Parse zcash transaction without output", async () => {
|
|
5
|
+
const tx = {
|
|
6
|
+
id: "0b9f98d07eb418fa20573112d3cba6b871d429a06c724a7888ff0886be5213d1",
|
|
7
|
+
inputs: [
|
|
8
|
+
{
|
|
9
|
+
output_hash:
|
|
10
|
+
"2772f3963856f3eb38cb706ec8c2b62fcdeb2ce10f32cf7160afb3873be6f60d",
|
|
11
|
+
output_index: 0,
|
|
12
|
+
value: "5000000000",
|
|
13
|
+
address: "2NCDBM9DAuMrD1T8XDHMxvbTmLutP7at4AB",
|
|
14
|
+
sequence: 4294967294,
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
outputs: [],
|
|
18
|
+
block: {
|
|
19
|
+
hash: "305d4b8d4a6d6ecca0a3dd0216f8ecd090978ed346d1845883c8aa4529d72fc8",
|
|
20
|
+
height: 120,
|
|
21
|
+
time: "2021-07-28T13:34:38Z",
|
|
22
|
+
},
|
|
23
|
+
account: 0,
|
|
24
|
+
index: 0,
|
|
25
|
+
address: "mwXTtHo8Yy3aNKUUZLkBDrTcKT9qG9TqLb",
|
|
26
|
+
received_at: "2021-07-28T13:34:38Z",
|
|
27
|
+
};
|
|
28
|
+
const out = mapTxToOperations(
|
|
29
|
+
tx,
|
|
30
|
+
"zcash",
|
|
31
|
+
"zcash 1",
|
|
32
|
+
new Set<string>(),
|
|
33
|
+
new Set<string>()
|
|
34
|
+
);
|
|
35
|
+
expect(out.length).toEqual(0);
|
|
36
|
+
}, 30000);
|
|
37
|
+
});
|
|
@@ -128,6 +128,10 @@ const getDelegations = async (
|
|
|
128
128
|
url: `${defaultEndpoint}/cosmos/staking/v1beta1/delegations/${address}`,
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
+
data1.delegation_responses = data1.delegation_responses.filter(
|
|
132
|
+
(d) => d.balance.amount !== "0"
|
|
133
|
+
);
|
|
134
|
+
|
|
131
135
|
let status = "unbonded";
|
|
132
136
|
const statusMap = {
|
|
133
137
|
BOND_STATUS_UNBONDED: "unbonded",
|
|
@@ -49,6 +49,9 @@ describe("cosmos/react", () => {
|
|
|
49
49
|
);
|
|
50
50
|
const delegations = account.cosmosResources?.delegations;
|
|
51
51
|
invariant(delegations, "cosmos: delegations is required");
|
|
52
|
+
expect(
|
|
53
|
+
account.cosmosResources?.delegations?.some((d) => d.amount[0] === 0)
|
|
54
|
+
).toBe(false);
|
|
52
55
|
expect(Array.isArray(result.current)).toBe(true);
|
|
53
56
|
expect(result.current.length).toBe(
|
|
54
57
|
(delegations as CosmosDelegation[]).length
|
|
@@ -47,7 +47,11 @@ const broadcast = async ({
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const scanAccounts = makeScanAccounts({ getAccountShape });
|
|
50
|
-
const sync = makeSync({
|
|
50
|
+
const sync = makeSync({
|
|
51
|
+
getAccountShape,
|
|
52
|
+
postSync: postSyncPatch,
|
|
53
|
+
shouldMergeOps: false,
|
|
54
|
+
});
|
|
51
55
|
|
|
52
56
|
const createTransaction = (): Transaction => ({
|
|
53
57
|
family: "ethereum",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AccountRaw } from "../../../types";
|
|
2
|
+
|
|
3
|
+
export const ethereum2: AccountRaw = {
|
|
4
|
+
id: "js:2:ethereum:0x789d2f10826BF8f3a56Ec524359bBA4e738Af5bF:",
|
|
5
|
+
seedIdentifier: "0x789d2f10826BF8f3a56Ec524359bBA4e738Af5bF",
|
|
6
|
+
name: "Ethereum legacy xpub6Bem...JyAdpYZy",
|
|
7
|
+
derivationMode: "",
|
|
8
|
+
index: 0,
|
|
9
|
+
freshAddress: "0x789d2f10826BF8f3a56Ec524359bBA4e738Af5bF",
|
|
10
|
+
freshAddressPath: "44'/60'/0'/0/0",
|
|
11
|
+
freshAddresses: [],
|
|
12
|
+
pendingOperations: [],
|
|
13
|
+
operations: [],
|
|
14
|
+
currencyId: "ethereum",
|
|
15
|
+
unitMagnitude: 18,
|
|
16
|
+
balance: "",
|
|
17
|
+
blockHeight: 0,
|
|
18
|
+
lastSyncDate: "",
|
|
19
|
+
xpub: "",
|
|
20
|
+
};
|
|
@@ -2,10 +2,16 @@ import "../../__tests__/test-helpers/setup";
|
|
|
2
2
|
import { reduce } from "rxjs/operators";
|
|
3
3
|
import { fromAccountRaw } from "../../account";
|
|
4
4
|
import { getAccountCurrency } from "../../account/helpers";
|
|
5
|
-
import type { Account, SubAccount } from "../../types";
|
|
5
|
+
import type { Account, OperationRaw, SubAccount } from "../../types";
|
|
6
6
|
import { getAccountBridge } from "../../bridge";
|
|
7
7
|
import { makeBridgeCacheSystem } from "../../bridge/cache";
|
|
8
8
|
import { ethereum1 } from "./datasets/ethereum1";
|
|
9
|
+
import { ethereum2 } from "./datasets/ethereum2";
|
|
10
|
+
import { encodeOperationId } from "../../operation";
|
|
11
|
+
import {
|
|
12
|
+
getCryptoCurrencyById,
|
|
13
|
+
listTokensForCryptoCurrency,
|
|
14
|
+
} from "@ledgerhq/cryptoassets";
|
|
9
15
|
|
|
10
16
|
describe("blacklistedTokenIds functionality", () => {
|
|
11
17
|
const account = fromAccountRaw(ethereum1);
|
|
@@ -83,3 +89,59 @@ describe("blacklistedTokenIds functionality", () => {
|
|
|
83
89
|
).toBeTruthy();
|
|
84
90
|
});
|
|
85
91
|
});
|
|
92
|
+
|
|
93
|
+
describe("sync on reorg", () => {
|
|
94
|
+
test("should sync from scratch if stableoperations latest blockhash is not on chain", async () => {
|
|
95
|
+
const opHash = "NotImportant";
|
|
96
|
+
const accountId = ethereum2.id;
|
|
97
|
+
const opType = "OUT";
|
|
98
|
+
const opId = encodeOperationId(accountId, opHash, opType);
|
|
99
|
+
const blockHeight = 10042069;
|
|
100
|
+
const currency = getCryptoCurrencyById(ethereum2.currencyId);
|
|
101
|
+
const syncHash =
|
|
102
|
+
JSON.stringify([]) +
|
|
103
|
+
"_" +
|
|
104
|
+
listTokensForCryptoCurrency(currency, {
|
|
105
|
+
withDelisted: true,
|
|
106
|
+
}).length;
|
|
107
|
+
|
|
108
|
+
const operation: OperationRaw = {
|
|
109
|
+
id: opId,
|
|
110
|
+
hash: opHash,
|
|
111
|
+
type: opType,
|
|
112
|
+
value: "1",
|
|
113
|
+
fee: "string",
|
|
114
|
+
senders: ["0x789d2f10826BF8f3a56Ec524359bBA4e738Af5bF"],
|
|
115
|
+
recipients: ["0xdA9EDcC3CF66bc18050dB55D376407Cf85e0617B"],
|
|
116
|
+
blockHeight: blockHeight - 81, // 80 is defined today as the threshold number of confirmation to be safe for a reorg. @see SAFE_REORG_THRESHOLD
|
|
117
|
+
blockHash:
|
|
118
|
+
"0x00000000000000000000000000000000000Th1sH4shSh0u1dN0t3x1sTOnCh4iN",
|
|
119
|
+
transactionSequenceNumber: 1,
|
|
120
|
+
date: "May-11-2020 01:50:10 UTC",
|
|
121
|
+
extra: {},
|
|
122
|
+
accountId,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const account = fromAccountRaw({
|
|
126
|
+
...ethereum2,
|
|
127
|
+
blockHeight,
|
|
128
|
+
syncHash,
|
|
129
|
+
operations: [operation],
|
|
130
|
+
operationsCount: 1,
|
|
131
|
+
});
|
|
132
|
+
const bridge = getAccountBridge(account);
|
|
133
|
+
const syncPromise = await bridge
|
|
134
|
+
.sync(account, {
|
|
135
|
+
paginationConfig: {},
|
|
136
|
+
blacklistedTokenIds: [],
|
|
137
|
+
})
|
|
138
|
+
.pipe(reduce((a, f: (arg0: Account) => Account) => f(a), account))
|
|
139
|
+
.toPromise();
|
|
140
|
+
// expect not to throw ok if you reach this point
|
|
141
|
+
// + expect operations to be from full sync instead of incremental
|
|
142
|
+
// so the fake op should be removed
|
|
143
|
+
expect(syncPromise.operations.map((o) => o.id)).not.toContain(opId);
|
|
144
|
+
// To this date (June 20th 2022) this account has 160 ops, we should at least get them all
|
|
145
|
+
expect(syncPromise.operations.length >= 160).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
});
|