UNPKG

@ledgerhq/coin-tron

Version:
195 lines 9.22 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.sync = exports.getAccountShape = void 0; const account_1 = require("@ledgerhq/coin-framework/account"); const jsHelpers_1 = require("@ledgerhq/coin-framework/bridge/jsHelpers"); const operation_1 = require("@ledgerhq/coin-framework/operation"); const index_1 = require("@ledgerhq/cryptoassets/index"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const compact_1 = __importDefault(require("lodash/compact")); const get_1 = __importDefault(require("lodash/get")); const logic_1 = require("../logic"); const pagination_1 = require("../logic/pagination"); const network_1 = require("../network"); const utils_1 = require("./utils"); // the balance does not update straightaway so we should ignore recent operations if they are in pending for a bit const PREFER_PENDING_OPERATIONS_UNTIL_BLOCK_VALIDATION = 35; const getAccountShape = async ({ initialAccount, currency, address, derivationMode }, syncConfig) => { const { height: blockHeight } = await (0, network_1.getLastBlock)(); const tronAcc = await (0, network_1.fetchTronAccount)(address); const accountId = (0, account_1.encodeAccountId)({ type: "js", version: "2", currencyId: currency.id, xpubOrAddress: address, derivationMode: derivationMode, }); if (tronAcc.length === 0) { return { id: accountId, blockHeight, balance: new bignumber_js_1.default(0), tronResources: utils_1.defaultTronResources, }; } const acc = tronAcc[0]; const cacheTransactionInfoById = initialAccount ? { ...(initialAccount?.tronResources?.cacheTransactionInfoById || {}), } : {}; const operationsPageSize = Math.min(1000, (0, pagination_1.getOperationsPageSize)(initialAccount && initialAccount.id, syncConfig)); // FIXME: this is not optional especially that we might already have initialAccount // use minimalOperationsBuilderSync to reconciliate and KEEP REF const txs = await (0, network_1.fetchTronAccountTxs)(address, txs => txs.length < operationsPageSize, cacheTransactionInfoById); const tronResources = await (0, utils_1.getTronResources)(acc, txs, cacheTransactionInfoById); // const tronResources = await getTronResources(acc); const spendableBalance = acc.balance ? new bignumber_js_1.default(acc.balance) : new bignumber_js_1.default(0); const balance = (0, logic_1.computeBalanceBridge)(acc); const parentTxs = txs.filter(utils_1.isParentTx); const parentOperations = (0, compact_1.default)(parentTxs.map(tx => (0, utils_1.txInfoToOperation)(accountId, address, tx))); const trc10Tokens = (0, get_1.default)(acc, "assetV2", []).reduce((accumulator, { key, value }) => { const tokenInfo = (0, index_1.findTokenById)(`tron/trc10/${key}`); if (tokenInfo) { accumulator.push({ key, type: "trc10", tokenId: tokenInfo.id, balance: value.toString(), }); } return accumulator; }, []); const trc20Tokens = (0, get_1.default)(acc, "trc20", []).reduce((accumulator, trc20) => { const [[contractAddress, balance]] = Object.entries(trc20); const tokenInfo = (0, index_1.findTokenByAddressInCurrency)(contractAddress, currency.id); if (tokenInfo) { accumulator.push({ key: contractAddress, type: "trc20", tokenId: tokenInfo.id, balance, }); } return accumulator; }, []); const { blacklistedTokenIds = [] } = syncConfig; const subAccounts = (0, compact_1.default)(trc10Tokens.concat(trc20Tokens).map(({ key, tokenId, balance }) => { const { blacklistedTokenIds = [] } = syncConfig; const token = (0, index_1.findTokenById)(tokenId); if (!token || blacklistedTokenIds.includes(tokenId)) return; const id = (0, account_1.encodeTokenAccountId)(accountId, token); const tokenTxs = txs.filter(tx => tx.tokenId === key); const operations = (0, compact_1.default)(tokenTxs.map(tx => (0, utils_1.txInfoToOperation)(id, address, tx))); const maybeExistingSubAccount = initialAccount?.subAccounts?.find(a => a.id === id); const bnBalance = new bignumber_js_1.default(balance); const sub = { type: "TokenAccount", id, parentId: accountId, token, balance: bnBalance, spendableBalance: bnBalance, operationsCount: operations.length, operations, pendingOperations: maybeExistingSubAccount ? maybeExistingSubAccount.pendingOperations : [], creationDate: operations.length > 0 ? operations[operations.length - 1].date : new Date(), swapHistory: maybeExistingSubAccount ? maybeExistingSubAccount.swapHistory : [], balanceHistoryCache: account_1.emptyHistoryCache, // calculated in the jsHelpers }; return sub; })); // Filter blacklisted tokens from the initial account's subAccounts // Could be use to filter out tokens that got their CAL id changed const filteredInitialSubAccounts = (initialAccount?.subAccounts || []).filter(subAccount => !blacklistedTokenIds.includes(subAccount.token.id)); // keep old account with emptyBalance and a history not returned by the BE fixes LIVE-12797 const mergedSubAccounts = mergeSubAccounts(subAccounts, filteredInitialSubAccounts); // get 'OUT' token operations with fee const subOutOperationsWithFee = subAccounts .flatMap(s => s.operations) .filter(o => o.type === "OUT" && o.fee.isGreaterThan(0)) .map((o) => ({ ...o, accountId, value: o.fee, id: (0, operation_1.encodeOperationId)(accountId, o.hash, "OUT"), extra: o.extra, })); // add them to the parent operations and sort by date desc /** * FIXME * * We have a problem here as we're just concatenating ops without ever really linking them. * It means no operation can be "FEES" of a subOp by example. It leads to our issues with TRC10/TRC20 * optimistic operation never really existing in the end. */ const parentOpsAndSubOutOpsWithFee = parentOperations .concat(subOutOperationsWithFee) .sort((a, b) => b.date.valueOf() - a.date.valueOf()); return { id: accountId, balance, spendableBalance, operationsCount: parentOpsAndSubOutOpsWithFee.length, operations: parentOpsAndSubOutOpsWithFee, subAccounts: mergedSubAccounts, tronResources, blockHeight, }; }; exports.getAccountShape = getAccountShape; const postSync = (initial, parent) => { function evictRecentOpsIfPending(a) { a.pendingOperations.forEach(pending => { const i = a.operations.findIndex(o => o.id === pending.id); if (i !== -1) { const diff = parent.blockHeight - (a.operations[i].blockHeight || 0); if (diff < PREFER_PENDING_OPERATIONS_UNTIL_BLOCK_VALIDATION) { a.operations.splice(i, 1); } } }); } evictRecentOpsIfPending(parent); parent.subAccounts && parent.subAccounts.forEach(evictRecentOpsIfPending); return parent; }; /** * Merges two arrays of subAccounts according to specific rules: * - The first array (subAccounts1) is up-to-date and should not be modified. * - Old duplicates from the second array (subAccounts2) should be filtered out. * - Only new subAccounts with a unique ID from the second array should be included. * - The balance and spendableBalance fields of the second array's subAccounts should be set to 0. * * @param {Array} subAccounts1 - The first array of subAccounts, which is up-to-date and should not be modified. * @param {Array} subAccounts2 - The second array of subAccounts, from which only new unique subAccounts should be included. * @returns {Array} - The merged array of subAccounts. */ const mergeSubAccounts = (subAccounts1, subAccounts2) => { const existingIds = new Set(subAccounts1.map(subAccount => subAccount.id)); const filteredSubAccounts2 = subAccounts2 .map(subAccount => { if (existingIds.has(subAccount.id)) { return null; } else { // Set balance and spendableBalance to 0 has if they are not here it means balance is 0 return { ...subAccount, balance: new bignumber_js_1.default(0), spendableBalance: new bignumber_js_1.default(0), }; } }) .filter((elt) => elt !== null); return subAccounts1.concat(filteredSubAccounts2); }; exports.sync = (0, jsHelpers_1.makeSync)({ getAccountShape: exports.getAccountShape, postSync, }); //# sourceMappingURL=synchronization.js.map