@ledgerhq/coin-framework 0.3.5 → 0.3.6-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +8 -46
- package/CHANGELOG.md +13 -0
- package/package.json +9 -18
- package/src/account/accountId.ts +20 -30
- package/src/account/accountName.ts +1 -4
- package/src/account/balanceHistoryCache.ts +18 -37
- package/src/account/groupOperations.ts +4 -6
- package/src/account/helpers.test.ts +6 -26
- package/src/account/helpers.ts +33 -66
- package/src/account/ordering.ts +10 -18
- package/src/account/pending.ts +6 -15
- package/src/account/serialization.ts +8 -19
- package/src/account/support.ts +8 -18
- package/src/account.test.ts +25 -40
- package/src/bot/specs.ts +24 -43
- package/src/bot/types.ts +1 -1
- package/src/bridge/getAddressWrapper.ts +5 -13
- package/src/bridge/jsHelpers.ts +215 -259
- package/src/cache.ts +4 -4
- package/src/currencies/BigNumberToLocaleString.test.ts +25 -33
- package/src/currencies/BigNumberToLocaleString.ts +3 -8
- package/src/currencies/CurrencyURIScheme.ts +1 -3
- package/src/currencies/chopCurrencyUnitDecimals.ts +1 -4
- package/src/currencies/formatCurrencyUnit.ts +11 -21
- package/src/currencies/index.ts +1 -4
- package/src/currencies/localeUtility.ts +2 -4
- package/src/currencies/parseCurrencyUnit.ts +1 -4
- package/src/currencies/sanitizeValueString.ts +1 -1
- package/src/currencies/support.ts +3 -9
- package/src/derivation.test.ts +1 -4
- package/src/derivation.ts +45 -95
- package/src/errors.test.ts +1 -1
- package/src/errors.ts +2 -6
- package/src/mocks/account.ts +27 -80
- package/src/mocks/fixtures/nfts.test.ts +2 -6
- package/src/mocks/fixtures/nfts.ts +12 -38
- package/src/mocks/helpers.ts +2 -8
- package/src/nft/nftId.ts +2 -2
- package/src/operation.test.ts +6 -31
- package/src/operation.ts +12 -26
- package/src/transaction/common.ts +9 -22
package/.eslintrc.js
CHANGED
|
@@ -1,57 +1,19 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
-
parser: "@typescript-eslint/parser",
|
|
3
2
|
env: {
|
|
4
3
|
browser: true,
|
|
5
4
|
es6: true,
|
|
6
|
-
node: true,
|
|
7
|
-
jest: true,
|
|
8
5
|
},
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
overrides: [
|
|
7
|
+
{
|
|
8
|
+
files: ["src/**/*.test.{ts,tsx}"],
|
|
9
|
+
env: {
|
|
10
|
+
"jest/globals": true,
|
|
11
|
+
},
|
|
12
|
+
plugins: ["jest"],
|
|
13
|
+
},
|
|
14
14
|
],
|
|
15
|
-
globals: {
|
|
16
|
-
Atomics: "readonly",
|
|
17
|
-
SharedArrayBuffer: "readonly",
|
|
18
|
-
},
|
|
19
|
-
plugins: ["@typescript-eslint", "prettier"],
|
|
20
15
|
rules: {
|
|
21
16
|
"no-console": ["error", { allow: ["warn", "error"] }],
|
|
22
|
-
"linebreak-style": ["error", "unix"],
|
|
23
|
-
semi: ["error", "always"],
|
|
24
|
-
"no-unused-vars": "off",
|
|
25
|
-
"import/prefer-default-export": 0,
|
|
26
|
-
"no-plusplus": 0,
|
|
27
|
-
"no-underscore-dangle": 0,
|
|
28
|
-
"prefer-template": 0,
|
|
29
|
-
"no-await-in-loop": 0,
|
|
30
|
-
"no-restricted-syntax": 0,
|
|
31
|
-
"consistent-return": 0,
|
|
32
|
-
"no-lonely-if": 0,
|
|
33
|
-
"no-use-before-define": 0,
|
|
34
|
-
"no-nested-ternary": 0,
|
|
35
|
-
"import/no-cycle": 0,
|
|
36
|
-
"no-multi-assign": 0,
|
|
37
|
-
"guard-for-in": 0,
|
|
38
|
-
"no-continue": 0,
|
|
39
|
-
"lines-between-class-members": 0,
|
|
40
|
-
"prefer-destructuring": 0,
|
|
41
|
-
"prettier/prettier": "error",
|
|
42
|
-
"@typescript-eslint/no-use-before-define": "off",
|
|
43
|
-
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
|
44
17
|
"@typescript-eslint/no-empty-function": "off",
|
|
45
|
-
"@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }],
|
|
46
|
-
"@typescript-eslint/no-explicit-any": 0,
|
|
47
|
-
"@typescript-eslint/ban-types": [
|
|
48
|
-
"error",
|
|
49
|
-
{
|
|
50
|
-
extendDefaults: true,
|
|
51
|
-
types: {
|
|
52
|
-
"{}": false,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
18
|
},
|
|
57
19
|
};
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @ledgerhq/coin-framework
|
|
2
2
|
|
|
3
|
+
## 0.3.6-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`9adc1862dd`](https://github.com/LedgerHQ/ledger-live/commit/9adc1862dda605a722d19f3b6895bd324834c914), [`4a1454beb3`](https://github.com/LedgerHQ/ledger-live/commit/4a1454beb3f86405ba2686e07879c12a7d35ad8e), [`809065c571`](https://github.com/LedgerHQ/ledger-live/commit/809065c57198646a49adea112b9d799e35a57d25), [`d1aa522db7`](https://github.com/LedgerHQ/ledger-live/commit/d1aa522db75f7ea850efe412abaa4dc7d37af6b7), [`ebe5b07afe`](https://github.com/LedgerHQ/ledger-live/commit/ebe5b07afec441ea3e2d9103da9e1175972add47)]:
|
|
8
|
+
- @ledgerhq/errors@6.12.7-next.0
|
|
9
|
+
- @ledgerhq/cryptoassets@9.8.0-next.0
|
|
10
|
+
- @ledgerhq/types-cryptoassets@7.2.1-next.0
|
|
11
|
+
- @ledgerhq/types-live@6.35.1-next.0
|
|
12
|
+
- @ledgerhq/devices@8.0.4-next.0
|
|
13
|
+
- @ledgerhq/hw-transport@6.28.5-next.0
|
|
14
|
+
- @ledgerhq/live-portfolio@0.0.8-next.0
|
|
15
|
+
|
|
3
16
|
## 0.3.5
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/coin-framework",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6-next.0",
|
|
4
4
|
"description": "Ledger framework for Coin integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -61,33 +61,24 @@
|
|
|
61
61
|
"prando": "^6.0.1",
|
|
62
62
|
"rxjs": "^6.6.7",
|
|
63
63
|
"rxjs-compat": "^6.6.7",
|
|
64
|
-
"@ledgerhq/cryptoassets": "^9.
|
|
65
|
-
"@ledgerhq/devices": "^8.0.
|
|
66
|
-
"@ledgerhq/errors": "^6.12.
|
|
67
|
-
"@ledgerhq/hw-transport": "^6.28.
|
|
64
|
+
"@ledgerhq/cryptoassets": "^9.8.0-next.0",
|
|
65
|
+
"@ledgerhq/devices": "^8.0.4-next.0",
|
|
66
|
+
"@ledgerhq/errors": "^6.12.7-next.0",
|
|
67
|
+
"@ledgerhq/hw-transport": "^6.28.5-next.0",
|
|
68
68
|
"@ledgerhq/live-env": "^0.3.0",
|
|
69
|
-
"@ledgerhq/live-portfolio": "^0.0.
|
|
69
|
+
"@ledgerhq/live-portfolio": "^0.0.8-next.0",
|
|
70
70
|
"@ledgerhq/live-promise": "^0.0.1",
|
|
71
71
|
"@ledgerhq/logs": "^6.10.1",
|
|
72
|
-
"@ledgerhq/types-cryptoassets": "^7.2.0",
|
|
73
|
-
"@ledgerhq/types-live": "^6.35.0"
|
|
72
|
+
"@ledgerhq/types-cryptoassets": "^7.2.1-next.0",
|
|
73
|
+
"@ledgerhq/types-live": "^6.35.1-next.0"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@types/invariant": "^2.2.2",
|
|
77
77
|
"@types/jest": "^29.2.4",
|
|
78
78
|
"@types/lodash": "^4.14.191",
|
|
79
79
|
"@types/numeral": "^2.0.2",
|
|
80
|
-
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
|
81
|
-
"@typescript-eslint/parser": "^5.46.1",
|
|
82
80
|
"cross-env": "^7.0.3",
|
|
83
|
-
"eslint": "^7.32.0",
|
|
84
|
-
"eslint-config-prettier": "^8.3.0",
|
|
85
|
-
"eslint-config-typescript": "^3.0.0",
|
|
86
|
-
"eslint-formatter-pretty": "^3.0.1",
|
|
87
|
-
"eslint-plugin-prettier": "^3.4.0",
|
|
88
|
-
"eslint-plugin-typescript": "^0.14.0",
|
|
89
81
|
"jest": "^28.1.1",
|
|
90
|
-
"prettier": "^2.8.1",
|
|
91
82
|
"timemachine": "^0.3.2",
|
|
92
83
|
"ts-jest": "^28.0.5"
|
|
93
84
|
},
|
|
@@ -97,7 +88,7 @@
|
|
|
97
88
|
"prewatch": "pnpm build",
|
|
98
89
|
"watch": "tsc --watch",
|
|
99
90
|
"doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
|
|
100
|
-
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx",
|
|
91
|
+
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
|
|
101
92
|
"lint:fix": "pnpm lint --fix",
|
|
102
93
|
"test": "cross-env TZ=America/New_York jest"
|
|
103
94
|
}
|
package/src/account/accountId.ts
CHANGED
|
@@ -4,17 +4,10 @@ import { asDerivationMode } from "../derivation";
|
|
|
4
4
|
import { getCryptoCurrencyById, findTokenById } from "../currencies";
|
|
5
5
|
import { findTokenByAddressInCurrency } from "@ledgerhq/cryptoassets";
|
|
6
6
|
import type { AccountIdParams, DerivationMode } from "@ledgerhq/types-live";
|
|
7
|
-
import type {
|
|
8
|
-
CryptoCurrency,
|
|
9
|
-
TokenCurrency,
|
|
10
|
-
} from "@ledgerhq/types-cryptoassets";
|
|
7
|
+
import type { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
|
|
11
8
|
|
|
12
9
|
function ensureNoColon(value: string, ctx: string): string {
|
|
13
|
-
invariant(
|
|
14
|
-
!value.includes(":"),
|
|
15
|
-
"AccountId '%s' component must not use colon",
|
|
16
|
-
ctx
|
|
17
|
-
);
|
|
10
|
+
invariant(!value.includes(":"), "AccountId '%s' component must not use colon", ctx);
|
|
18
11
|
return value;
|
|
19
12
|
}
|
|
20
13
|
|
|
@@ -25,18 +18,15 @@ export function encodeAccountId({
|
|
|
25
18
|
xpubOrAddress,
|
|
26
19
|
derivationMode,
|
|
27
20
|
}: AccountIdParams): string {
|
|
28
|
-
return `${ensureNoColon(type, "type")}:${ensureNoColon(
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
)}:${ensureNoColon(
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
)}
|
|
21
|
+
return `${ensureNoColon(type, "type")}:${ensureNoColon(version, "version")}:${ensureNoColon(
|
|
22
|
+
currencyId,
|
|
23
|
+
"currencyId",
|
|
24
|
+
)}:${ensureNoColon(xpubOrAddress, "xpubOrAddress")}:${ensureNoColon(
|
|
25
|
+
derivationMode,
|
|
26
|
+
"derivationMode",
|
|
27
|
+
)}`;
|
|
35
28
|
}
|
|
36
|
-
export function encodeTokenAccountId(
|
|
37
|
-
accountId: string,
|
|
38
|
-
token: TokenCurrency
|
|
39
|
-
): string {
|
|
29
|
+
export function encodeTokenAccountId(accountId: string, token: TokenCurrency): string {
|
|
40
30
|
return accountId + "+" + encodeURIComponent(token.id);
|
|
41
31
|
}
|
|
42
32
|
export function decodeTokenAccountId(id: string): {
|
|
@@ -81,13 +71,13 @@ export function getWalletName({
|
|
|
81
71
|
}): string {
|
|
82
72
|
return `${seedIdentifier}_${currency.id}_${derivationMode}`;
|
|
83
73
|
}
|
|
84
|
-
export const inferFamilyFromAccountId: (
|
|
85
|
-
accountId
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
74
|
+
export const inferFamilyFromAccountId: (accountId: string) => string | null | undefined = memoize(
|
|
75
|
+
accountId => {
|
|
76
|
+
try {
|
|
77
|
+
const { currencyId } = decodeAccountId(accountId);
|
|
78
|
+
return getCryptoCurrencyById(currencyId).family;
|
|
79
|
+
} catch (e) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
);
|
|
@@ -14,10 +14,7 @@ export const getAccountPlaceholderName = ({
|
|
|
14
14
|
};
|
|
15
15
|
export const getNewAccountPlaceholderName = getAccountPlaceholderName; // same naming
|
|
16
16
|
|
|
17
|
-
export const validateNameEdition = (
|
|
18
|
-
account: Account,
|
|
19
|
-
name?: string | null | undefined
|
|
20
|
-
): string =>
|
|
17
|
+
export const validateNameEdition = (account: Account, name?: string | null | undefined): string =>
|
|
21
18
|
(
|
|
22
19
|
(name || account.name || "").replace(/\s+/g, " ").trim() ||
|
|
23
20
|
account.name ||
|
|
@@ -27,7 +27,7 @@ function generateHistoryFromOperationsG(
|
|
|
27
27
|
account: AccountLike,
|
|
28
28
|
g: GranularityId, // partial=true allows a faster implementation that only recompose the last part of the history
|
|
29
29
|
// to only use when we do not recalculate the history but we just want to access it
|
|
30
|
-
partial = false
|
|
30
|
+
partial = false,
|
|
31
31
|
): BalanceHistoryDataCache {
|
|
32
32
|
const { increment, startOf, maxDatapoints } = granularities[g];
|
|
33
33
|
const latestDate = startOf(new Date()).getTime();
|
|
@@ -51,9 +51,7 @@ function generateHistoryFromOperationsG(
|
|
|
51
51
|
// FIXME: added valueOf here to make typescript happy
|
|
52
52
|
account.operations[i].date.valueOf() > date
|
|
53
53
|
) {
|
|
54
|
-
balance = balance.minus(
|
|
55
|
-
getOperationAmountNumberWithInternals(account.operations[i])
|
|
56
|
-
);
|
|
54
|
+
balance = balance.minus(getOperationAmountNumberWithInternals(account.operations[i]));
|
|
57
55
|
i++;
|
|
58
56
|
}
|
|
59
57
|
|
|
@@ -71,9 +69,7 @@ function generateHistoryFromOperationsG(
|
|
|
71
69
|
};
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
export function generateHistoryFromOperations(
|
|
75
|
-
account: AccountLike
|
|
76
|
-
): BalanceHistoryCache {
|
|
72
|
+
export function generateHistoryFromOperations(account: AccountLike): BalanceHistoryCache {
|
|
77
73
|
return {
|
|
78
74
|
HOUR: generateHistoryFromOperationsG(account, "HOUR"),
|
|
79
75
|
DAY: generateHistoryFromOperationsG(account, "DAY"),
|
|
@@ -84,10 +80,7 @@ export function generateHistoryFromOperations(
|
|
|
84
80
|
/**
|
|
85
81
|
* get the current balance history of the account. if possible from the cache.
|
|
86
82
|
*/
|
|
87
|
-
export function getAccountHistoryBalances(
|
|
88
|
-
account: AccountLike,
|
|
89
|
-
g: GranularityId
|
|
90
|
-
): number[] {
|
|
83
|
+
export function getAccountHistoryBalances(account: AccountLike, g: GranularityId): number[] {
|
|
91
84
|
const { balances, latestDate } = account.balanceHistoryCache[g];
|
|
92
85
|
const { startOf } = granularities[g];
|
|
93
86
|
const now = startOf(new Date()).getTime();
|
|
@@ -103,10 +96,7 @@ export function getAccountHistoryBalances(
|
|
|
103
96
|
/**
|
|
104
97
|
* utility used at the end of an account synchronisation to recalculate the balance history if necessary
|
|
105
98
|
*/
|
|
106
|
-
export function recalculateAccountBalanceHistories(
|
|
107
|
-
res: Account,
|
|
108
|
-
prev: Account
|
|
109
|
-
): Account {
|
|
99
|
+
export function recalculateAccountBalanceHistories(res: Account, prev: Account): Account {
|
|
110
100
|
// recalculate balance history cache
|
|
111
101
|
if (prev.balanceHistoryCache === res.balanceHistoryCache) {
|
|
112
102
|
// we only regenerate if it was not overriden by the implemenetation.
|
|
@@ -116,30 +106,21 @@ export function recalculateAccountBalanceHistories(
|
|
|
116
106
|
const prevSubAccounts = prev.subAccounts;
|
|
117
107
|
const nextSubAccounts = res.subAccounts;
|
|
118
108
|
|
|
119
|
-
if (
|
|
120
|
-
nextSubAccounts &&
|
|
121
|
-
prevSubAccounts &&
|
|
122
|
-
prevSubAccounts !== nextSubAccounts
|
|
123
|
-
) {
|
|
109
|
+
if (nextSubAccounts && prevSubAccounts && prevSubAccounts !== nextSubAccounts) {
|
|
124
110
|
// when sub accounts changes, we need to recalculate
|
|
125
|
-
res.subAccounts = nextSubAccounts.map(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
subAccount = {
|
|
135
|
-
...subAccount,
|
|
136
|
-
balanceHistoryCache: generateHistoryFromOperations(subAccount),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return subAccount;
|
|
111
|
+
res.subAccounts = nextSubAccounts.map((subAccount: SubAccount): SubAccount => {
|
|
112
|
+
const old = prevSubAccounts.find(a => a.id === subAccount.id);
|
|
113
|
+
|
|
114
|
+
if (!old || old.balanceHistoryCache === subAccount.balanceHistoryCache) {
|
|
115
|
+
// we only regenerate if it was not overriden by the implemenetation.
|
|
116
|
+
subAccount = {
|
|
117
|
+
...subAccount,
|
|
118
|
+
balanceHistoryCache: generateHistoryFromOperations(subAccount),
|
|
119
|
+
};
|
|
141
120
|
}
|
|
142
|
-
|
|
121
|
+
|
|
122
|
+
return subAccount;
|
|
123
|
+
});
|
|
143
124
|
}
|
|
144
125
|
|
|
145
126
|
return res;
|
|
@@ -22,18 +22,16 @@ type GroupOpsByDayOpts = {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const hasStableOperation = (account: AccountLike, hash: string) =>
|
|
25
|
-
account.operations.some(
|
|
25
|
+
account.operations.some(op => op.hash === hash);
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* @memberof account
|
|
29
29
|
*/
|
|
30
30
|
export function groupAccountsOperationsByDay(
|
|
31
31
|
inputAccounts: AccountLikeArray,
|
|
32
|
-
{ count, withSubAccounts, filterOperation }: GroupOpsByDayOpts
|
|
32
|
+
{ count, withSubAccounts, filterOperation }: GroupOpsByDayOpts,
|
|
33
33
|
): DailyOperations {
|
|
34
|
-
const accounts = withSubAccounts
|
|
35
|
-
? flattenAccounts(inputAccounts)
|
|
36
|
-
: inputAccounts;
|
|
34
|
+
const accounts = withSubAccounts ? flattenAccounts(inputAccounts) : inputAccounts;
|
|
37
35
|
// Track indexes of account.operations[] for each account
|
|
38
36
|
const indexes: number[] = Array(accounts.length).fill(0);
|
|
39
37
|
// Track indexes of account.pendingOperations[] for each account
|
|
@@ -157,7 +155,7 @@ export function groupAccountsOperationsByDay(
|
|
|
157
155
|
*/
|
|
158
156
|
export function groupAccountOperationsByDay(
|
|
159
157
|
account: AccountLike,
|
|
160
|
-
arg: GroupOpsByDayOpts
|
|
158
|
+
arg: GroupOpsByDayOpts,
|
|
161
159
|
): DailyOperations {
|
|
162
160
|
const accounts: AccountLike[] = [account];
|
|
163
161
|
return groupAccountsOperationsByDay(accounts, arg);
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
TokenCurrency,
|
|
4
|
-
Unit,
|
|
5
|
-
} from "@ledgerhq/types-cryptoassets";
|
|
6
|
-
import {
|
|
7
|
-
Account,
|
|
8
|
-
ChildAccount,
|
|
9
|
-
Operation,
|
|
10
|
-
SubAccount,
|
|
11
|
-
TokenAccount,
|
|
12
|
-
} from "@ledgerhq/types-live";
|
|
1
|
+
import { CryptoCurrency, TokenCurrency, Unit } from "@ledgerhq/types-cryptoassets";
|
|
2
|
+
import { Account, ChildAccount, Operation, SubAccount, TokenAccount } from "@ledgerhq/types-live";
|
|
13
3
|
import BigNumber from "bignumber.js";
|
|
14
4
|
import {
|
|
15
5
|
areAllOperationsLoaded,
|
|
@@ -20,11 +10,7 @@ import {
|
|
|
20
10
|
getAccountUnit,
|
|
21
11
|
getFeesCurrency,
|
|
22
12
|
} from ".";
|
|
23
|
-
import {
|
|
24
|
-
isAccountEmpty,
|
|
25
|
-
isAccountBalanceSignificant,
|
|
26
|
-
clearAccount,
|
|
27
|
-
} from "./helpers";
|
|
13
|
+
import { isAccountEmpty, isAccountBalanceSignificant, clearAccount } from "./helpers";
|
|
28
14
|
|
|
29
15
|
const mockAccount = {} as Account;
|
|
30
16
|
const childAccount = {
|
|
@@ -251,9 +237,7 @@ describe(getAccountSpendableBalance.name, () => {
|
|
|
251
237
|
it("should return the account spendable balance", () => {
|
|
252
238
|
const sampleAccountBalance = new BigNumber(10);
|
|
253
239
|
mockAccount.spendableBalance = sampleAccountBalance;
|
|
254
|
-
expect(getAccountSpendableBalance(mockAccount)).toEqual(
|
|
255
|
-
sampleAccountBalance
|
|
256
|
-
);
|
|
240
|
+
expect(getAccountSpendableBalance(mockAccount)).toEqual(sampleAccountBalance);
|
|
257
241
|
});
|
|
258
242
|
});
|
|
259
243
|
|
|
@@ -261,9 +245,7 @@ describe(getAccountSpendableBalance.name, () => {
|
|
|
261
245
|
it("should return the account balance", () => {
|
|
262
246
|
const sampleAccountBalance = new BigNumber(10);
|
|
263
247
|
childAccount.balance = sampleAccountBalance;
|
|
264
|
-
expect(getAccountSpendableBalance(childAccount)).toEqual(
|
|
265
|
-
sampleAccountBalance
|
|
266
|
-
);
|
|
248
|
+
expect(getAccountSpendableBalance(childAccount)).toEqual(sampleAccountBalance);
|
|
267
249
|
});
|
|
268
250
|
});
|
|
269
251
|
|
|
@@ -271,9 +253,7 @@ describe(getAccountSpendableBalance.name, () => {
|
|
|
271
253
|
it("should return the token account spendable balance", () => {
|
|
272
254
|
const sampleAccountBalance = new BigNumber(10);
|
|
273
255
|
tokenAccount.spendableBalance = sampleAccountBalance;
|
|
274
|
-
expect(getAccountSpendableBalance(tokenAccount)).toEqual(
|
|
275
|
-
sampleAccountBalance
|
|
276
|
-
);
|
|
256
|
+
expect(getAccountSpendableBalance(tokenAccount)).toEqual(sampleAccountBalance);
|
|
277
257
|
});
|
|
278
258
|
});
|
|
279
259
|
|
package/src/account/helpers.ts
CHANGED
|
@@ -11,28 +11,22 @@ import type {
|
|
|
11
11
|
TokenAccount,
|
|
12
12
|
ChildAccount,
|
|
13
13
|
} from "@ledgerhq/types-live";
|
|
14
|
-
import {
|
|
15
|
-
CryptoCurrency,
|
|
16
|
-
TokenCurrency,
|
|
17
|
-
Unit,
|
|
18
|
-
} from "@ledgerhq/types-cryptoassets";
|
|
14
|
+
import { CryptoCurrency, TokenCurrency, Unit } from "@ledgerhq/types-cryptoassets";
|
|
19
15
|
|
|
20
16
|
// By convention, a main account is the top level account
|
|
21
17
|
// - in case of an Account is the account itself
|
|
22
18
|
// - in case of a SubAccount it's the parentAccount
|
|
23
|
-
export const getMainAccount = (
|
|
24
|
-
account:
|
|
25
|
-
parentAccount?:
|
|
26
|
-
):
|
|
19
|
+
export const getMainAccount = <A extends Account>(
|
|
20
|
+
account: A | SubAccount,
|
|
21
|
+
parentAccount?: A | null | undefined,
|
|
22
|
+
): A => {
|
|
27
23
|
const mainAccount = account.type === "Account" ? account : parentAccount;
|
|
28
24
|
invariant(mainAccount, "an account is expected");
|
|
29
|
-
return mainAccount as
|
|
25
|
+
return mainAccount as A;
|
|
30
26
|
};
|
|
31
27
|
|
|
32
28
|
// Return the currency in which fees are paid for this account
|
|
33
|
-
export const getFeesCurrency = (
|
|
34
|
-
account?: AccountLike
|
|
35
|
-
): TokenCurrency | CryptoCurrency => {
|
|
29
|
+
export const getFeesCurrency = (account?: AccountLike): TokenCurrency | CryptoCurrency => {
|
|
36
30
|
switch (account?.type) {
|
|
37
31
|
case "Account":
|
|
38
32
|
return account.feesCurrency || account.currency;
|
|
@@ -44,9 +38,7 @@ export const getFeesCurrency = (
|
|
|
44
38
|
return account.token;
|
|
45
39
|
|
|
46
40
|
default:
|
|
47
|
-
throw new Error(
|
|
48
|
-
"invalid account.type=" + (account as unknown as { type: string })?.type
|
|
49
|
-
);
|
|
41
|
+
throw new Error("invalid account.type=" + (account as unknown as { type: string })?.type);
|
|
50
42
|
}
|
|
51
43
|
};
|
|
52
44
|
|
|
@@ -55,9 +47,7 @@ export const getFeesUnit = (currency: TokenCurrency | CryptoCurrency): Unit => {
|
|
|
55
47
|
return currency.units[0];
|
|
56
48
|
};
|
|
57
49
|
|
|
58
|
-
export const getAccountCurrency = (
|
|
59
|
-
account?: AccountLike
|
|
60
|
-
): TokenCurrency | CryptoCurrency => {
|
|
50
|
+
export const getAccountCurrency = (account?: AccountLike): TokenCurrency | CryptoCurrency => {
|
|
61
51
|
switch (account?.type) {
|
|
62
52
|
case "Account":
|
|
63
53
|
case "ChildAccount":
|
|
@@ -67,9 +57,7 @@ export const getAccountCurrency = (
|
|
|
67
57
|
return account.token;
|
|
68
58
|
|
|
69
59
|
default:
|
|
70
|
-
throw new Error(
|
|
71
|
-
"invalid account.type=" + (account as unknown as { type: string })?.type
|
|
72
|
-
);
|
|
60
|
+
throw new Error("invalid account.type=" + (account as unknown as { type: string })?.type);
|
|
73
61
|
}
|
|
74
62
|
};
|
|
75
63
|
|
|
@@ -118,8 +106,7 @@ export const getAccountSpendableBalance = (account: AccountLike): BigNumber => {
|
|
|
118
106
|
};
|
|
119
107
|
|
|
120
108
|
export const isAccountEmpty = (a: AccountLike): boolean => {
|
|
121
|
-
const hasSubAccounts =
|
|
122
|
-
a.type === "Account" && a.subAccounts && a.subAccounts.length;
|
|
109
|
+
const hasSubAccounts = a.type === "Account" && a.subAccounts && a.subAccounts.length;
|
|
123
110
|
return a.operationsCount === 0 && a.balance.isZero() && !hasSubAccounts;
|
|
124
111
|
};
|
|
125
112
|
|
|
@@ -135,15 +122,14 @@ export function areAllOperationsLoaded(account: AccountLike): boolean {
|
|
|
135
122
|
return true;
|
|
136
123
|
}
|
|
137
124
|
|
|
138
|
-
export const isAccountBalanceSignificant = (a: AccountLike): boolean =>
|
|
139
|
-
a.balance.gt(100);
|
|
125
|
+
export const isAccountBalanceSignificant = (a: AccountLike): boolean => a.balance.gt(100);
|
|
140
126
|
|
|
141
127
|
// in future, could be a per currency thing
|
|
142
128
|
// clear account to a bare minimal version that can be restored via sync
|
|
143
129
|
// will preserve the balance to avoid user panic
|
|
144
130
|
export function clearAccount<T extends AccountLike>(
|
|
145
131
|
account: T,
|
|
146
|
-
familyClean?: (account: Account) => void
|
|
132
|
+
familyClean?: (account: Account) => void,
|
|
147
133
|
): T {
|
|
148
134
|
if (account.type === "TokenAccount") {
|
|
149
135
|
return {
|
|
@@ -171,9 +157,7 @@ export function clearAccount<T extends AccountLike>(
|
|
|
171
157
|
pendingOperations: [],
|
|
172
158
|
subAccounts:
|
|
173
159
|
(account as Account).subAccounts &&
|
|
174
|
-
(account as Account).subAccounts?.map((acc)
|
|
175
|
-
clearAccount(acc, familyClean)
|
|
176
|
-
),
|
|
160
|
+
(account as Account).subAccounts?.map(acc => clearAccount(acc, familyClean)),
|
|
177
161
|
};
|
|
178
162
|
|
|
179
163
|
familyClean?.(copy);
|
|
@@ -182,11 +166,8 @@ export function clearAccount<T extends AccountLike>(
|
|
|
182
166
|
return copy as T;
|
|
183
167
|
}
|
|
184
168
|
|
|
185
|
-
export function findSubAccountById(
|
|
186
|
-
account
|
|
187
|
-
id: string
|
|
188
|
-
): SubAccount | null | undefined {
|
|
189
|
-
return (account.subAccounts || []).find((a) => a.id === id);
|
|
169
|
+
export function findSubAccountById(account: Account, id: string): SubAccount | null | undefined {
|
|
170
|
+
return (account.subAccounts || []).find(a => a.id === id);
|
|
190
171
|
}
|
|
191
172
|
|
|
192
173
|
// get the token accounts of an account, ignoring those that are zero IF user don't want them
|
|
@@ -194,7 +175,7 @@ export function listSubAccounts(account: Account): SubAccount[] {
|
|
|
194
175
|
const accounts = account.subAccounts || [];
|
|
195
176
|
|
|
196
177
|
if (getEnv("HIDE_EMPTY_TOKEN_ACCOUNTS")) {
|
|
197
|
-
return accounts.filter(
|
|
178
|
+
return accounts.filter(a => !a.balance.isZero());
|
|
198
179
|
}
|
|
199
180
|
|
|
200
181
|
return accounts;
|
|
@@ -206,7 +187,7 @@ export type FlattenAccountsOptions = {
|
|
|
206
187
|
|
|
207
188
|
export function flattenAccounts(
|
|
208
189
|
topAccounts: AccountLikeArray,
|
|
209
|
-
o: FlattenAccountsOptions = {}
|
|
190
|
+
o: FlattenAccountsOptions = {},
|
|
210
191
|
): AccountLike[] {
|
|
211
192
|
const accounts: AccountLike[] = [];
|
|
212
193
|
|
|
@@ -236,9 +217,8 @@ export const shortAddressPreview = (addr: string, target = 20): string => {
|
|
|
236
217
|
};
|
|
237
218
|
|
|
238
219
|
export const isAccountBalanceUnconfirmed = (account: AccountLike): boolean =>
|
|
239
|
-
account.pendingOperations.some(
|
|
240
|
-
|
|
241
|
-
) || account.operations.some((op) => !op.blockHeight);
|
|
220
|
+
account.pendingOperations.some(op => !account.operations.find(o => o.hash === op.hash)) ||
|
|
221
|
+
account.operations.some(op => !op.blockHeight);
|
|
242
222
|
|
|
243
223
|
export const isUpToDateAccount = (account: Account | null | undefined) => {
|
|
244
224
|
if (!account) return true;
|
|
@@ -246,16 +226,12 @@ export const isUpToDateAccount = (account: Account | null | undefined) => {
|
|
|
246
226
|
const { blockAvgTime } = currency;
|
|
247
227
|
if (!blockAvgTime) return true;
|
|
248
228
|
const outdated =
|
|
249
|
-
|
|
250
|
-
Date.now().valueOf() - (lastSyncDate.valueOf() || 0) >
|
|
229
|
+
Date.now() - lastSyncDate.getTime() >
|
|
251
230
|
blockAvgTime * 1000 + getEnv("SYNC_OUTDATED_CONSIDERED_DELAY");
|
|
252
231
|
return !outdated;
|
|
253
232
|
};
|
|
254
233
|
|
|
255
|
-
export const makeEmptyTokenAccount = (
|
|
256
|
-
account: Account,
|
|
257
|
-
token: TokenCurrency
|
|
258
|
-
): TokenAccount => ({
|
|
234
|
+
export const makeEmptyTokenAccount = (account: Account, token: TokenCurrency): TokenAccount => ({
|
|
259
235
|
type: "TokenAccount",
|
|
260
236
|
id: account.id + "+" + token.contractAddress,
|
|
261
237
|
parentId: account.id,
|
|
@@ -276,20 +252,18 @@ export const makeEmptyTokenAccount = (
|
|
|
276
252
|
*/
|
|
277
253
|
export const accountWithMandatoryTokens = (
|
|
278
254
|
account: Account,
|
|
279
|
-
tokenCurrencies: TokenCurrency[]
|
|
255
|
+
tokenCurrencies: TokenCurrency[],
|
|
280
256
|
): Account => {
|
|
281
257
|
const { subAccounts } = account;
|
|
282
258
|
if (!subAccounts) return account;
|
|
283
|
-
const existingTokens = subAccounts
|
|
284
|
-
.map((a) => a.type === "TokenAccount" && a.token)
|
|
285
|
-
.filter(Boolean);
|
|
259
|
+
const existingTokens = subAccounts.map(a => a.type === "TokenAccount" && a.token).filter(Boolean);
|
|
286
260
|
const addition = tokenCurrencies
|
|
287
261
|
.filter(
|
|
288
262
|
(
|
|
289
|
-
t // token of the same currency
|
|
290
|
-
) => t.parentCurrency === account.currency && !existingTokens.includes(t) // not yet in the sub accounts
|
|
263
|
+
t, // token of the same currency
|
|
264
|
+
) => t.parentCurrency === account.currency && !existingTokens.includes(t), // not yet in the sub accounts
|
|
291
265
|
)
|
|
292
|
-
.map<TokenAccount>(
|
|
266
|
+
.map<TokenAccount>(token => ({
|
|
293
267
|
type: "TokenAccount",
|
|
294
268
|
id: encodeTokenAccountId(account.id, token),
|
|
295
269
|
parentId: account.id,
|
|
@@ -314,13 +288,11 @@ export const accountWithMandatoryTokens = (
|
|
|
314
288
|
export const withoutToken = (account: Account, tokenId: string): Account => {
|
|
315
289
|
const { subAccounts } = account;
|
|
316
290
|
if (!subAccounts) return account;
|
|
317
|
-
const tokenAccount = subAccounts.find(
|
|
318
|
-
(a) => a.type === "TokenAccount" && a.token.id === tokenId
|
|
319
|
-
);
|
|
291
|
+
const tokenAccount = subAccounts.find(a => a.type === "TokenAccount" && a.token.id === tokenId);
|
|
320
292
|
if (!tokenAccount) return account;
|
|
321
293
|
return {
|
|
322
294
|
...account,
|
|
323
|
-
subAccounts: subAccounts.filter(
|
|
295
|
+
subAccounts: subAccounts.filter(sa => sa.id !== tokenAccount.id),
|
|
324
296
|
};
|
|
325
297
|
};
|
|
326
298
|
|
|
@@ -330,7 +302,7 @@ export const withoutToken = (account: Account, tokenId: string): Account => {
|
|
|
330
302
|
*/
|
|
331
303
|
export const findTokenAccountByCurrency = (
|
|
332
304
|
tokenCurrency: TokenCurrency,
|
|
333
|
-
accounts: Account[]
|
|
305
|
+
accounts: Account[],
|
|
334
306
|
):
|
|
335
307
|
| {
|
|
336
308
|
account?: SubAccount;
|
|
@@ -384,20 +356,15 @@ export function isSubAccount(account?: AccountLike): account is SubAccount {
|
|
|
384
356
|
return isTokenAccount(account) || isChildAccount(account);
|
|
385
357
|
}
|
|
386
358
|
|
|
387
|
-
export function getParentAccount(
|
|
388
|
-
account: AccountLike,
|
|
389
|
-
accounts: AccountLike[]
|
|
390
|
-
): Account {
|
|
359
|
+
export function getParentAccount(account: AccountLike, accounts: AccountLike[]): Account {
|
|
391
360
|
switch (account.type) {
|
|
392
361
|
case "Account":
|
|
393
362
|
return account;
|
|
394
363
|
case "TokenAccount":
|
|
395
364
|
case "ChildAccount": {
|
|
396
|
-
const parentAccount = accounts.find(
|
|
365
|
+
const parentAccount = accounts.find(a => a.id == account.parentId);
|
|
397
366
|
if (!parentAccount) {
|
|
398
|
-
throw new Error(
|
|
399
|
-
"No 'parentAccount' account provided for token account"
|
|
400
|
-
);
|
|
367
|
+
throw new Error("No 'parentAccount' account provided for token account");
|
|
401
368
|
}
|
|
402
369
|
|
|
403
370
|
return parentAccount as Account;
|