UNPKG

@ledgerhq/coin-filecoin

Version:
142 lines 5.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.listOperations = listOperations; const api_1 = require("../../network/api"); const addresses_1 = require("../../network/addresses"); const types_1 = require("../../types"); const PAGE_LIMIT = 50; function parseCursor(cursor, minHeight) { if (!cursor) { return { nativeOffset: 0, tokenOffset: 0, fromHeight: minHeight }; } try { return JSON.parse(cursor); } catch { return { nativeOffset: 0, tokenOffset: 0, fromHeight: minHeight }; } } // Maps native FIL transactions into operations. A single tx can yield both an // OUT (or FEES) and an IN operation when sender === recipient. function mapNativeOperations(address, txs, startIndex) { const items = []; let opIndex = startIndex; for (const tx of txs) { const { to, from, hash, timestamp, amount, fee_data, status } = tx; const date = new Date(timestamp * 1000); const value = BigInt(amount); const fee = BigInt(fee_data?.TotalCost ?? "0"); const failed = status !== types_1.TxStatus.Ok; const block = { height: tx.height, hash: "", time: date }; if (address === from) { const opType = value === 0n ? "FEES" : "OUT"; items.push({ id: `${address}-${hash}-${opType}-${opIndex}`, type: opType, senders: [from], recipients: [to], // OUT: value = amount + fees (bridge convention) value: value + fee, asset: { type: "native" }, tx: { hash, block, fees: fee, date, failed }, }); opIndex++; } if (address === to) { const opType = value === 0n ? "FEES" : "IN"; items.push({ id: `${address}-${hash}-${opType}-${opIndex}`, type: opType, senders: [from], recipients: [to], value, asset: { type: "native" }, tx: { hash, block, fees: fee, date, failed }, }); opIndex++; } } return items; } // Maps ERC-20 transactions into operations. ERC-20 records use ETH-format (0x) // addresses, so we compare against ethAddr (the address used to query the endpoint). function mapTokenOperations(address, ethAddr, txs, startIndex) { const items = []; let opIndex = startIndex; const ethAddrLower = ethAddr.toLowerCase(); for (const tx of txs) { const { to, from, tx_hash, tx_cid, amount, height, timestamp, status, contract_address } = tx; const hash = tx_cid ?? tx_hash; const date = new Date(timestamp * 1000); const value = BigInt(amount); const failed = status !== types_1.TxStatus.Ok; const asset = { type: "erc20", assetReference: contract_address.toLowerCase() }; const block = { height, hash: "", time: date }; if (ethAddrLower === from.toLowerCase()) { items.push({ id: `${address}-${hash}-OUT-${opIndex}`, type: "OUT", senders: [from], recipients: [to], value, asset, tx: { hash, block, fees: 0n, date, failed }, }); opIndex++; } if (ethAddrLower === to.toLowerCase()) { items.push({ id: `${address}-${hash}-IN-${opIndex}`, type: "IN", senders: [from], recipients: [to], value, asset, tx: { hash, block, fees: 0n, date, failed }, }); opIndex++; } } return items; } async function listOperations(address, options) { const limit = options.limit ?? PAGE_LIMIT; const cur = parseCursor(options.cursor, options.minHeight ?? 0); // Fetch native FIL transactions const nativeResp = await (0, api_1.fetchTxs)(address, cur.fromHeight, cur.nativeOffset, limit); const nativeTxs = nativeResp.txs ?? []; // Fetch ERC-20 transactions — convert FIL address to Ethereum-compatible address first. // address may not be convertible (e.g. f1/f3 secp256k1 without delegated counterpart). let ethAddr = null; try { ethAddr = (0, addresses_1.convertAddressFilToEth)(address); } catch { ethAddr = null; } const tokenTxs = ethAddr ? ((await (0, api_1.fetchERC20Transactions)(ethAddr, cur.fromHeight, cur.tokenOffset, limit)).txs ?? []) : []; // tokenOpIndex continues from the native op count to avoid ID collisions const nativeItems = mapNativeOperations(address, nativeTxs, 0); const tokenItems = ethAddr ? mapTokenOperations(address, ethAddr, tokenTxs, nativeItems.length) : []; // Merge and sort descending by date const items = [...nativeItems, ...tokenItems]; items.sort((a, b) => b.tx.date.getTime() - a.tx.date.getTime()); const hasMoreNative = nativeTxs.length >= limit; const hasMoreToken = tokenTxs.length >= limit; // OR rule: if either stream has more pages, propagate a next cursor let next; if (hasMoreNative || hasMoreToken) { const nextCursor = { nativeOffset: cur.nativeOffset + (hasMoreNative ? limit : nativeTxs.length), tokenOffset: cur.tokenOffset + (hasMoreToken ? limit : tokenTxs.length), fromHeight: cur.fromHeight, }; next = JSON.stringify(nextCursor); } return { items, next }; } //# sourceMappingURL=listOperations.js.map