UNPKG

@ledgerhq/live-common

Version:
178 lines 7.2 kB
import { getAccountCurrency } from "@ledgerhq/ledger-wallet-framework/account/index"; import { getTagDerivationMode } from "@ledgerhq/ledger-wallet-framework/derivation"; import { getOperationAmountNumber, getOperationAmountNumberWithInternals, } from "@ledgerhq/ledger-wallet-framework/operation"; import { getDefaultAccountName } from "@ledgerhq/live-wallet/accountName"; import { BigNumber } from "bignumber.js"; import invariant from "invariant"; import { formatCurrencyUnit } from "../currencies"; import { toAccountRaw } from "./serialization"; import { getAccountBridge } from "../bridge"; const isSignificantAccount = acc => acc.balance.gt(10 ** (getAccountCurrency(acc).units[0].magnitude - 6)); const formatOp = (unitByAccountId, familySpecific) => { const format = (op, level = 0) => { const unit = unitByAccountId(op.accountId); const amountBN = getOperationAmountNumber(op); const amount = unit ? formatCurrencyUnit(unit, amountBN, { showCode: true, alwaysShowSign: true, disableRounding: true, }) : "? " + amountBN.toString(); const spaces = Array((level + 1) * 2) .fill(" ") .join(""); let extra = level > 0 ? "" : `${op.hash} ${op.date.toISOString().split(":").slice(0, 2).join(":")}`; extra += familySpecific(op, unit); const head = `${(spaces + amount).padEnd(20)} ${((op.hasFailed ? "❌" : "") + op.type + " ").padEnd(11)}${extra}`; const sub = (op.subOperations || []) .concat(op.internalOperations || []) .map(subop => format(subop, level + 1)) .join(""); return `\n${head}${sub}`; }; return (op) => format(op, 0); }; function maybeDisplaySumOfOpsIssue(ops, balance, unit) { const sumOfOps = ops.reduce((sum, op) => sum.plus(getOperationAmountNumber(op)), new BigNumber(0)); if (sumOfOps.eq(balance)) return ""; return (" (! sum of ops " + formatCurrencyUnit(unit, sumOfOps, { showCode: true, disableRounding: true, }) + ")"); } const cliFormat = (account, level) => { const { id, name, freshAddress, freshAddressPath, derivationMode, index, operations } = account; const tag = getTagDerivationMode(account.currency, derivationMode); const balance = formatCurrencyUnit(getAccountCurrency(account).units[0], account.balance, { showCode: true, }); const opsCount = `${operations.length}ops`; const freshInfo = `${freshAddress} on ${freshAddressPath}`; const derivationInfo = `${derivationMode}#${index}`; let str = `${name}${tag ? " [" + tag + "]" : ""}: ${balance} (${opsCount}) (${freshInfo}) ${derivationInfo} ${id || ""}`; if (level === "head") return str; str += maybeDisplaySumOfOpsIssue(operations, account.balance, getAccountCurrency(account).units[0]); const formatAccountSpecifics = getAccountBridge(account).formatAccountSpecifics; if (formatAccountSpecifics) { str += formatAccountSpecifics(account); str += "\n"; } const subAccounts = account.subAccounts || []; str += subAccounts .map(ta => " " + ta.type + " " + getDefaultAccountName(ta) + ": " + formatCurrencyUnit(getAccountCurrency(ta).units[0], ta.balance, { showCode: true, disableRounding: true, }) + " (" + ta.operations.length + " ops)" + maybeDisplaySumOfOpsIssue(ta.operations, ta.balance, getAccountCurrency(ta).units[0])) .join("\n"); if (level === "basic") return str; str += "\nOPERATIONS (" + operations.length + ")"; str += operations .map(formatOp(id => { if (account.id === id) return getAccountCurrency(account).units[0]; const ta = subAccounts.find(a => a.id === id); if (ta) return getAccountCurrency(ta).units[0]; console.error("unexpected missing token account " + id); }, (operation, unit) => { return getAccountBridge(account).formatOperationSpecifics?.(operation, unit) ?? ""; })) .join(""); return str; }; const stats = account => { const { subAccounts, operations } = account; const sumOfAllOpsNumber = operations.reduce((sum, op) => sum.plus(getOperationAmountNumberWithInternals(op)), new BigNumber(0)); const sumOfAllOps = formatCurrencyUnit(getAccountCurrency(account).units[0], sumOfAllOpsNumber, { showCode: true, }); const balance = formatCurrencyUnit(getAccountCurrency(account).units[0], account.balance, { showCode: true, }); return { balance, sumOfAllOps, opsCount: operations.length, subAccountsCount: (subAccounts || []).length, }; }; // this is developped for debug purpose in order to compare with balance APIs const operationBalanceHistoryBackwards = account => { let acc = account.balance; return JSON.stringify(account.operations .map(op => { const { blockHeight } = op; const date = op.date.toISOString(); const balance = acc.toNumber(); acc = acc.minus(getOperationAmountNumberWithInternals(op)); return { blockHeight, date, balance }; }) .reverse()); }; const operationBalanceHistory = account => { let acc = new BigNumber(0); return JSON.stringify(account.operations .slice(0) .reverse() .map(op => { acc = acc.plus(getOperationAmountNumberWithInternals(op)); const { blockHeight } = op; const date = op.date.toISOString(); const balance = acc.toNumber(); return { blockHeight, date, balance }; })); }; export const accountFormatters = { operationBalanceHistoryBackwards, operationBalanceHistory, json: account => JSON.stringify(toAccountRaw(account)), head: account => cliFormat(account, "head"), default: account => cliFormat(account), basic: account => cliFormat(account, "basic"), full: account => cliFormat(account, "full"), stats: account => stats(account), significantTokenTickers: account => (account.subAccounts || []) .filter(isSignificantAccount) .map(ta => getAccountCurrency(ta).ticker) .join("\n"), }; export function formatAccount(account, format = "full") { const f = accountFormatters[format]; invariant(f, "missing account formatter=" + format); return f(account); } export function formatOperation(account) { const unitByAccountId = (id) => { if (!account) return; if (account.id === id) return getAccountCurrency(account).units[0]; const ta = (account.subAccounts || []).find(a => a.id === id); if (ta) return getAccountCurrency(ta).units[0]; }; const familyExtra = (operation, unit) => { if (!account) return ""; return getAccountBridge(account).formatOperationSpecifics?.(operation, unit) ?? ""; }; return formatOp(unitByAccountId, familyExtra); } //# sourceMappingURL=formatters.js.map