Skip to content

Commit

Permalink
[Coin Fmk] Account export cleaning (#6818)
Browse files Browse the repository at this point in the history
* chore: simplify import for account

Signed-off-by: Stéphane Prohaszka <[email protected]>

* chore: add changelog

Signed-off-by: Stéphane Prohaszka <[email protected]>

* fix: lint issue

Signed-off-by: Stéphane Prohaszka <[email protected]>

* fix: update unimported

Signed-off-by: Stéphane Prohaszka <[email protected]>

---------

Signed-off-by: Stéphane Prohaszka <[email protected]>
  • Loading branch information
sprohaszka-ledger committed May 13, 2024
1 parent 5fd55c9 commit a18c28e
Show file tree
Hide file tree
Showing 31 changed files with 191 additions and 333 deletions.
9 changes: 9 additions & 0 deletions .changeset/kind-dryers-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@ledgerhq/live-common": minor
"@ledgerhq/coin-framework": minor
"@ledgerhq/coin-polkadot": patch
"@ledgerhq/coin-bitcoin": patch
"@ledgerhq/live-cli": patch
---

Split account utils between coin-fmk and LLC
16 changes: 11 additions & 5 deletions apps/cli/src/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@ import { Observable, from, defer, of, throwError, concat } from "rxjs";
import { skip, take, reduce, mergeMap, map, filter, concatMap } from "rxjs/operators";
import type { Account, DerivationMode, SyncConfig } from "@ledgerhq/types-live";
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
import { encodeAccountId, decodeAccountId } from "@ledgerhq/live-common/account/index";
import { emptyHistoryCache } from "@ledgerhq/live-common/account/index";
import { fromAccountRaw } from "@ledgerhq/live-common/account/serialization";
import { asDerivationMode } from "@ledgerhq/coin-framework/derivation";
import {
encodeAccountId,
decodeAccountId,
emptyHistoryCache,
fromAccountRaw,
} from "@ledgerhq/live-common/account/index";
import {
asDerivationMode,
runDerivationScheme,
getDerivationScheme,
} from "@ledgerhq/coin-framework/derivation";
import { getAccountBridge, getCurrencyBridge } from "@ledgerhq/live-common/bridge/index";
import {
findCryptoCurrencyByKeyword,
findCryptoCurrencyById,
getCryptoCurrencyById,
} from "@ledgerhq/live-common/currencies/index";
import { runDerivationScheme, getDerivationScheme } from "@ledgerhq/coin-framework/derivation";
import { makeBridgeCacheSystem } from "@ledgerhq/live-common/bridge/cache";
import getAppAndVersion from "@ledgerhq/live-common/hw/getAppAndVersion";
import { withDevice } from "@ledgerhq/live-common/hw/deviceAccess";
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
TransactionStatusCommon,
} from "@ledgerhq/types-live";
import perFamily from "@ledgerhq/live-common/generated/cli-transaction";
import { getAccountCurrency } from "@ledgerhq/live-common/account/helpers";
import { getAccountCurrency } from "@ledgerhq/live-common/account/index";
import { parseCurrencyUnit } from "@ledgerhq/live-common/currencies/index";
import { getAccountBridge } from "@ledgerhq/live-common/bridge/index";

Expand Down
68 changes: 0 additions & 68 deletions libs/coin-framework/src/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import {
groupAccountsOperationsByDay,
shortAddressPreview,
accountWithMandatoryTokens,
withoutToken,
} from "./account";
import { genAccount } from "./mocks/account";
import { Operation } from "@ledgerhq/types-live";
import { SubAccount } from "@ledgerhq/types-live";
import { getCryptoCurrencyById, getTokenById } from "@ledgerhq/cryptoassets/index";

setSupportedCurrencies(["ethereum", "ethereum_classic", "tron"]);
Expand Down Expand Up @@ -164,69 +162,3 @@ test("accountWithMandatoryTokens ethereum", () => {
});
expect((enhance.subAccounts || []).map(a => a.id)).toMatchSnapshot();
});
test("withoutToken ethereum", () => {
const isTokenAccount = (account: SubAccount, tokenId: string) =>
account.type === "TokenAccount" && account.token.id === tokenId;

const tokenIds = [
"ethereum/erc20/0x_project",
"ethereum/erc20/leo_token",
"ethereum/erc20/cro",
"ethereum/erc20/huobitoken",
];
const currency = getCryptoCurrencyById("ethereum");
const account = genAccount("", {
currency,
subAccountsCount: 0,
});
//Enhance the account with some tokens
const enhance = accountWithMandatoryTokens(account, tokenIds.map(getTokenById));
//Get a version of that account without all the tokens
let demote = enhance;

for (const tokenId of tokenIds) {
demote = withoutToken(demote, tokenId);
}

const saTokens = enhance.subAccounts || [];
const saNoTokens = demote.subAccounts || [];

//See if we have added/removed them correctly
for (const tokenId of tokenIds) {
expect(saTokens.find(a => isTokenAccount(a, tokenId))).toBeTruthy();
expect(saNoTokens.find(a => isTokenAccount(a, tokenId))).toBeFalsy();
}
});
test("withoutToken tron", () => {
const isTokenAccount = (account: SubAccount, tokenId: string) =>
account.type === "TokenAccount" && account.token.id === tokenId;

const tokenIds = [
"tron/trc10/1002000",
"tron/trc10/1002398",
"tron/trc10/1000226",
"tron/trc20/tla2f6vpqdgre67v1736s7bj8ray5wyju7",
];
const currency = getCryptoCurrencyById("tron");
const account = genAccount("", {
currency,
subAccountsCount: 0,
});
//Enhance the account with some tokens
const enhance = accountWithMandatoryTokens(account, tokenIds.map(getTokenById));
//Get a version of that account without all the tokens
let demote = enhance;

for (const tokenId of tokenIds) {
demote = withoutToken(demote, tokenId);
}

const saTokens = enhance.subAccounts || [];
const saNoTokens = demote.subAccounts || [];

//See if we have added/removed them correctly
for (const tokenId of tokenIds) {
expect(saTokens.find(a => isTokenAccount(a, tokenId))).toBeTruthy();
expect(saNoTokens.find(a => isTokenAccount(a, tokenId))).toBeFalsy();
}
});
36 changes: 5 additions & 31 deletions libs/coin-framework/src/account/accountId.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import memoize from "lodash/memoize";
import invariant from "invariant";
import { asDerivationMode } from "../derivation";
import {
findTokenByAddressInCurrency,
findTokenById,
getCryptoCurrencyById,
} from "@ledgerhq/cryptoassets";
import type { AccountIdParams, DerivationMode } from "@ledgerhq/types-live";
import type { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
import { findTokenByAddressInCurrency, findTokenById } from "@ledgerhq/cryptoassets";
import type { AccountIdParams } from "@ledgerhq/types-live";
import type { TokenCurrency } from "@ledgerhq/types-cryptoassets";

function ensureNoColon(value: string, ctx: string): string {
invariant(!value.includes(":"), "AccountId '%s' component must not use colon", ctx);
Expand Down Expand Up @@ -52,6 +47,7 @@ export function encodeAccountId({
export function encodeTokenAccountId(accountId: string, token: TokenCurrency): string {
return accountId + "+" + safeEncodeTokenId(token.id);
}

export function decodeTokenAccountId(id: string): {
accountId: string;
token: TokenCurrency | null | undefined;
Expand All @@ -68,6 +64,7 @@ export function decodeTokenAccountId(id: string): {
token,
};
}

export function decodeAccountId(accountId: string): AccountIdParams {
invariant(typeof accountId === "string", "accountId is not a string");
const splitted = accountId.split(":");
Expand All @@ -81,26 +78,3 @@ export function decodeAccountId(accountId: string): AccountIdParams {
derivationMode: asDerivationMode(derivationMode),
};
}
// you can pass account because type is shape of Account
// wallet name is a lib-core concept that usually identify a pool of accounts with the same (seed, cointype, derivation scheme) config.
export function getWalletName({
seedIdentifier,
derivationMode,
currency,
}: {
seedIdentifier: string;
derivationMode: DerivationMode;
currency: CryptoCurrency;
}): string {
return `${seedIdentifier}_${currency.id}_${derivationMode}`;
}
export const inferFamilyFromAccountId: (accountId: string) => string | null | undefined = memoize(
accountId => {
try {
const { currencyId } = decodeAccountId(accountId);
return getCryptoCurrencyById(currencyId).family;
} catch (e) {
return null;
}
},
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { startOfHour, startOfDay, startOfWeek } from ".";
import { startOfHour, startOfDay, startOfWeek } from "./balanceHistoryCache";

describe("date utils", () => {
describe("Timezones", () => {
Expand Down
9 changes: 3 additions & 6 deletions libs/coin-framework/src/account/groupOperations.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { flattenAccounts } from "./helpers";
import { flattenOperationWithInternalsAndNfts } from "../operation";
import type {
AccountLike,
AccountLikeArray,
DailyOperations,
Operation,
} from "@ledgerhq/types-live";

function startOfDay(t: Date) {
return new Date(t.getFullYear(), t.getMonth(), t.getDate());
}
import { flattenOperationWithInternalsAndNfts } from "../operation";
import { flattenAccounts } from "./helpers";
import { startOfDay } from "./balanceHistoryCache";

const emptyDailyOperations = {
sections: [],
Expand Down
58 changes: 2 additions & 56 deletions libs/coin-framework/src/account/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import BigNumber from "bignumber.js";
import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
import { Account, Operation, SubAccount, TokenAccount } from "@ledgerhq/types-live";
import { Account, SubAccount, TokenAccount } from "@ledgerhq/types-live";
import { getTokenById } from "@ledgerhq/cryptoassets/tokens";
import {
areAllOperationsLoaded,
emptyHistoryCache,
getAccountCurrency,
getAccountSpendableBalance,
getFeesCurrency,
} from ".";
import { isAccountEmpty, isAccountBalanceSignificant, clearAccount } from "./helpers";
import { isAccountEmpty, clearAccount } from "./helpers";

const mockAccount = {} as Account;
const tokenAccount = {
Expand Down Expand Up @@ -163,59 +162,6 @@ describe(isAccountEmpty.name, () => {
});
});

describe(areAllOperationsLoaded.name, () => {
describe("given an account with subAccounts", () => {
beforeEach(() => {
mockAccount.type = "Account";
mockAccount.operations = [];
mockAccount.operationsCount = 0;
mockAccount.subAccounts = [
{
operations: [],
operationsCount: 0,
},
{
operations: [{} as Operation],
operationsCount: 1,
},
] as SubAccount[];
});
describe("when sub account operation aren't loaded", () => {
beforeEach(() => {
(mockAccount.subAccounts as SubAccount[])[1].operations = [];
});
it("should return false", () => {
expect(areAllOperationsLoaded(mockAccount)).toEqual(false);
});
});

describe("when sub account operation are loaded", () => {
it("should return true", () => {
expect(areAllOperationsLoaded(mockAccount)).toEqual(true);
});
});
});
});

describe(isAccountBalanceSignificant.name, () => {
describe("when balance is low", () => {
beforeEach(() => {
mockAccount.balance = new BigNumber(10);
});
it("should return false", () => {
expect(isAccountBalanceSignificant(mockAccount)).toEqual(false);
});
});
describe("when balance is high", () => {
beforeEach(() => {
mockAccount.balance = new BigNumber(101);
});
it("should return true", () => {
expect(isAccountBalanceSignificant(mockAccount)).toEqual(true);
});
});
});

describe(clearAccount.name, () => {
describe("given a TokenAccount", () => {
const tokenAccount = {
Expand Down
35 changes: 0 additions & 35 deletions libs/coin-framework/src/account/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,6 @@ export const isAccountEmpty = (a: AccountLike): boolean => {
return a.operationsCount === 0 && a.balance.isZero() && !hasSubAccounts;
};

export function areAllOperationsLoaded(account: AccountLike): boolean {
if (account.operationsCount !== account.operations.length) {
return false;
}

if (account.type === "Account" && account.subAccounts) {
return account.subAccounts.every(areAllOperationsLoaded);
}

return true;
}

export const isAccountBalanceSignificant = (a: AccountLike): boolean => a.balance.gt(100);

// in future, could be a per currency thing
// clear account to a bare minimal version that can be restored via sync
// will preserve the balance to avoid user panic
Expand Down Expand Up @@ -241,20 +227,6 @@ export const accountWithMandatoryTokens = (
return { ...account, subAccounts: subAccounts.concat(addition) };
};

/**
* Patch account to enforce the removal of a blacklisted token
*/
export const withoutToken = (account: Account, tokenId: string): Account => {
const { subAccounts } = account;
if (!subAccounts) return account;
const tokenAccount = subAccounts.find(a => a.type === "TokenAccount" && a.token.id === tokenId);
if (!tokenAccount) return account;
return {
...account,
subAccounts: subAccounts.filter(sa => sa.id !== tokenAccount.id),
};
};

/**
* Find matching pair of subAccount/parentAccount for a given token curency
* if no subAccount found will return parentAccount or null if no matches found
Expand Down Expand Up @@ -307,13 +279,6 @@ export function isTokenAccount(account?: AccountLike): account is TokenAccount {
return account?.type === "TokenAccount";
}

/**
* @deprecated use isTokenAccount instead
*/
export function isSubAccount(account?: AccountLike): account is SubAccount {
return isTokenAccount(account);
}

export function getParentAccount(account: AccountLike, accounts: AccountLike[]): Account {
switch (account.type) {
case "Account":
Expand Down
38 changes: 32 additions & 6 deletions libs/coin-framework/src/account/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
export * from "./accountId";
export * from "./balanceHistoryCache";
export * from "./groupOperations";
export * from "./helpers";
export * from "./pending";
export * from "./support";
export {
encodeAccountId,
decodeAccountId,
encodeTokenAccountId,
decodeTokenAccountId,
} from "./accountId";
export { emptyHistoryCache, getAccountHistoryBalances } from "./balanceHistoryCache";
export { groupAccountOperationsByDay, groupAccountsOperationsByDay } from "./groupOperations";
export {
getMainAccount,
getFeesCurrency,
getFeesUnit,
getAccountCurrency,
getAccountSpendableBalance,
isAccountEmpty,
clearAccount,
findSubAccountById,
listSubAccounts,
type FlattenAccountsOptions,
flattenAccounts,
shortAddressPreview,
isAccountBalanceUnconfirmed,
isUpToDateAccount,
makeEmptyTokenAccount,
accountWithMandatoryTokens,
findTokenAccountByCurrency,
isAccount,
isTokenAccount,
getParentAccount,
} from "./helpers";
export { addPendingOperation } from "./pending";
export { getReceiveFlowError, checkAccountSupported } from "./support";
Loading

0 comments on commit a18c28e

Please sign in to comment.