UNPKG

@ledgerhq/coin-filecoin

Version:
171 lines 7.85 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateTokenTxnParams = exports.abiEncodeTransferParams = exports.encodeTxnParams = exports.erc20TxnToOperation = void 0; exports.buildTokenAccounts = buildTokenAccounts; const cryptoAssetsStore_1 = require("@ledgerhq/ledger-wallet-framework/cryptoAssetsStore"); const errors_1 = require("@ledgerhq/ledger-wallet-framework/errors"); const index_1 = require("@ledgerhq/ledger-wallet-framework/account/index"); const jsHelpers_1 = require("@ledgerhq/ledger-wallet-framework/bridge/jsHelpers"); const operation_1 = require("@ledgerhq/ledger-wallet-framework/operation"); const logs_1 = require("@ledgerhq/logs"); const cbor_1 = __importDefault(require("@zondax/cbor")); const bignumber_js_1 = __importDefault(require("bignumber.js")); const ethers_1 = require("ethers"); const invariant_1 = __importDefault(require("invariant")); const network_1 = require("../network"); const utils_1 = require("../bridge/utils"); const types_1 = require("../types"); const ERC20_json_1 = __importDefault(require("./ERC20.json")); const erc20TxnToOperation = (tx, address, accountId) => { try { const { to, from, timestamp, tx_hash, tx_cid, amount, height, status } = tx; const txAmount = new bignumber_js_1.default(amount); const isSending = address.toLowerCase() === from.toLowerCase(); const isReceiving = address.toLowerCase() === to.toLowerCase(); const fee = new bignumber_js_1.default(0); const date = new Date(timestamp * 1000); const hash = tx_cid ?? tx_hash; const hasFailed = status !== types_1.TxStatus.Ok; const ops = []; if (isSending) { ops.push({ id: (0, operation_1.encodeOperationId)(accountId, hash, "OUT"), hash, type: "OUT", value: txAmount, fee, blockHeight: height, blockHash: "", accountId, senders: [from], recipients: [to], date, hasFailed, extra: {}, }); } if (isReceiving) { ops.push({ id: (0, operation_1.encodeOperationId)(accountId, hash, "IN"), hash, type: "IN", value: txAmount, fee, blockHeight: height, blockHash: "", accountId, senders: [from], recipients: [to], date, hasFailed, extra: {}, }); } (0, invariant_1.default)(ops, "filecoin operation is not defined"); return ops; } catch (e) { (0, logs_1.log)("error", "filecoin error converting erc20 transaction to operation", e); return []; } }; exports.erc20TxnToOperation = erc20TxnToOperation; async function buildTokenAccounts(filAddr, lastHeight, parentAccountId, initialAccount) { try { const transfers = await (0, network_1.fetchERC20TransactionsWithPages)(filAddr, lastHeight); if (!transfers.length) { return initialAccount?.subAccounts ?? []; } // Group transfers by contract address (normalized to lowercase) const transfersByContract = transfers.reduce((acc, transfer) => { const contractAddr = transfer.contract_address.toLowerCase(); transfer.contract_address = contractAddr; if (!acc[contractAddr]) { acc[contractAddr] = []; } acc[contractAddr].push(transfer); return acc; }, {}); // Create lookup map for existing sub-accounts const existingSubAccounts = new Map(initialAccount?.subAccounts?.map(sa => [sa.token.contractAddress.toLowerCase(), sa]) ?? []); // Track which existing accounts we've processed const processedContracts = new Set(); const tokenAccounts = []; // Process accounts with new transfers for (const [contractAddr, txns] of Object.entries(transfersByContract)) { processedContracts.add(contractAddr); const token = await (0, cryptoAssetsStore_1.getCryptoAssetsStore)().findTokenByAddressInCurrency(contractAddr, "filecoin"); if (!token) { (0, logs_1.log)("error", `filecoin token not found, addr: ${contractAddr}`); continue; } const balance = await (0, network_1.fetchERC20TokenBalance)(filAddr, contractAddr); const bnBalance = new bignumber_js_1.default(balance); const tokenAccountId = (0, index_1.encodeTokenAccountId)(parentAccountId, token); const operations = txns .flatMap(txn => (0, exports.erc20TxnToOperation)(txn, filAddr, tokenAccountId)) .flat() .sort((a, b) => b.date.getTime() - a.date.getTime()); // Skip if no operations and zero balance if (operations.length === 0 && bnBalance.isZero()) { continue; } const existingAccount = existingSubAccounts.get(contractAddr); const tokenAccount = { type: utils_1.AccountType.TokenAccount, id: tokenAccountId, parentId: parentAccountId, token, balance: bnBalance, spendableBalance: bnBalance, operationsCount: txns.length, operations: (0, jsHelpers_1.mergeOps)(existingAccount?.operations ?? [], operations), pendingOperations: existingAccount?.pendingOperations ?? [], creationDate: operations[operations.length - 1]?.date ?? new Date(), swapHistory: existingAccount?.swapHistory ?? [], balanceHistoryCache: index_1.emptyHistoryCache, // calculated in the jsHelpers }; tokenAccounts.push(tokenAccount); } // Add existing accounts that didn't have new transfers for (const [contractAddr, existingAccount] of existingSubAccounts) { if (!processedContracts.has(contractAddr)) { tokenAccounts.push(existingAccount); } } return tokenAccounts; } catch (e) { (0, logs_1.log)("error", "filecoin error building token accounts", e); return []; } } const encodeTxnParams = (abiEncodedParams) => { (0, logs_1.log)("debug", `filecoin/abiEncodedParams: ${abiEncodedParams}`); if (!abiEncodedParams) { throw new Error("Cannot encode empty abi encoded params"); } const buffer = Buffer.from(abiEncodedParams.slice(2), "hex"); // buffer/byte array const dataEncoded = cbor_1.default.encode(buffer); return dataEncoded.toString("base64"); }; exports.encodeTxnParams = encodeTxnParams; const abiEncodeTransferParams = (recipient, amount) => { const contract = new ethers_1.ethers.Interface(ERC20_json_1.default); const data = contract.encodeFunctionData("transfer", [recipient, amount]); return data; }; exports.abiEncodeTransferParams = abiEncodeTransferParams; const generateTokenTxnParams = (recipient, amount) => { (0, logs_1.log)("debug", "generateTokenTxnParams", { recipient, amount: amount.toString() }); if (!recipient) { throw new errors_1.RecipientRequired(); } recipient = (0, network_1.convertAddressFilToEth)(recipient); return (0, exports.abiEncodeTransferParams)(recipient, amount.toString()); }; exports.generateTokenTxnParams = generateTokenTxnParams; //# sourceMappingURL=tokenAccounts.js.map