@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
178 lines • 7.2 kB
JavaScript
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