UNPKG

@ledgerhq/coin-hedera

Version:
230 lines 9.25 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.listOperations = listOperations; const state_1 = require("@ledgerhq/cryptoassets/state"); const accountId_1 = require("@ledgerhq/ledger-wallet-framework/account/accountId"); const operation_1 = require("@ledgerhq/ledger-wallet-framework/operation"); const live_env_1 = require("@ledgerhq/live-env"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const constants_1 = require("../constants"); const api_1 = require("../network/api"); const utils_1 = require("../network/utils"); const utils_2 = require("./utils"); const txNameToCustomOperationType = { TOKENASSOCIATE: "ASSOCIATE_TOKEN", CONTRACTCALL: "CONTRACT_CALL", CRYPTOUPDATEACCOUNT: "UPDATE_ACCOUNT", }; function getCommonOperationData(rawTx, useEncodedHash, useSyntheticBlocks) { const timestamp = new Date(Number.parseInt(rawTx.consensus_timestamp.split(".")[0], 10) * 1000); const hash = useEncodedHash ? (0, utils_2.base64ToUrlSafeBase64)(rawTx.transaction_hash) : rawTx.transaction_hash; const fee = new bignumber_js_1.default(rawTx.charged_tx_fee); const hasFailed = rawTx.result !== "SUCCESS"; const syntheticBlock = (0, utils_2.getSyntheticBlock)(rawTx.consensus_timestamp); const memo = (0, utils_2.getMemoFromBase64)(rawTx.memo_base64); const feesPayer = (0, utils_2.extractFeesPayer)(rawTx); const extra = { pagingToken: rawTx.consensus_timestamp, consensusTimestamp: rawTx.consensus_timestamp, transactionId: rawTx.transaction_id, feesPayer, ...(memo && { memo }), }; return { timestamp, hash, fee, hasFailed, blockHeight: useSyntheticBlocks ? syntheticBlock.blockHeight : 10, blockHash: useSyntheticBlocks ? syntheticBlock.blockHash : null, extra, }; } async function processTokenTransfers({ rawTx, address, currency, ledgerAccountId, commonData, skipFeesForTokenOperations, }) { const tokenTransfers = rawTx.token_transfers ?? []; if (tokenTransfers.length === 0) return null; const tokenId = tokenTransfers[0].token_id; const token = await (0, state_1.getCryptoAssetsStore)().findTokenByAddressInCurrency(tokenId, currency.id); if (!token) return null; const encodedTokenId = (0, accountId_1.encodeTokenAccountId)(ledgerAccountId, token); const { type, value, senders, recipients } = (0, utils_1.parseTransfers)(tokenTransfers, address); const { hash, fee, timestamp, blockHeight, blockHash, hasFailed } = commonData; const extra = { ...commonData.extra }; let coinOperation; // Add main FEES coin operation for send token transfer if (type === "OUT" && !skipFeesForTokenOperations) { coinOperation = { id: (0, operation_1.encodeOperationId)(ledgerAccountId, hash, "FEES"), accountId: ledgerAccountId, type: "FEES", value: fee, recipients, senders, hash, fee, date: timestamp, blockHeight, blockHash, hasFailed, extra, }; } const tokenOperation = { id: (0, operation_1.encodeOperationId)(encodedTokenId, hash, type), accountId: encodedTokenId, contract: token.contractAddress, standard: "hts", type, value, recipients, senders, hash, fee, date: timestamp, blockHeight, blockHash, hasFailed, extra, }; return { coinOperation, tokenOperation, }; } function processTransfers({ rawTx, address, ledgerAccountId, commonData, mirrorTokens, stakingAnalysis, }) { const coinOperations = []; const transfers = rawTx.transfers ?? []; if (transfers.length === 0) { return []; } const { type, value, senders, recipients } = (0, utils_1.parseTransfers)(transfers, address); const { hash, fee, timestamp, blockHeight, blockHash, hasFailed } = commonData; const extra = { ...commonData.extra }; let operationType = txNameToCustomOperationType[rawTx.name] ?? type; // update operation type and extra fields if staking analysis is available if (stakingAnalysis) { operationType = stakingAnalysis.operationType; extra.previousStakingNodeId = stakingAnalysis.previousStakingNodeId; extra.targetStakingNodeId = stakingAnalysis.targetStakingNodeId; extra.stakedAmount = new bignumber_js_1.default(stakingAnalysis.stakedAmount.toString()); } // each transfer may trigger staking reward claim const stakingReward = rawTx.staking_reward_transfers.reduce((acc, transfer) => { const transferAmount = new bignumber_js_1.default(transfer.amount); if (transfer.account === address) { acc = acc.plus(transferAmount); } return acc; }, new bignumber_js_1.default(0)); // try to enrich ASSOCIATE_TOKEN operation with extra.associatedTokenId // this value is used by custom OperationDetails components in Hedera family // accounts or contracts must first associate with an HTS token before they can receive or send that token; without association, token transfers fail if (operationType === "ASSOCIATE_TOKEN") { const relatedMirrorToken = mirrorTokens.find(t => { return t.created_timestamp === rawTx.consensus_timestamp; }); if (relatedMirrorToken) { extra.associatedTokenId = relatedMirrorToken.token_id; } } // add REWARD operation representing staking reward transfers if (stakingReward.gt(0)) { const stakingRewardHash = (0, utils_2.createStakingRewardOperationHash)(hash); const stakingRewardType = "REWARD"; // offset timestamp by +1ms to ensure it appears just before the operation that triggered it const stakingRewardTimestamp = new Date(timestamp.getTime() + 1); coinOperations.push({ id: (0, operation_1.encodeOperationId)(ledgerAccountId, stakingRewardHash, stakingRewardType), accountId: ledgerAccountId, type: stakingRewardType, value: stakingReward, recipients: [address], senders: [(0, live_env_1.getEnv)("HEDERA_STAKING_REWARD_ACCOUNT_ID")], hash: stakingRewardHash, fee: new bignumber_js_1.default(0), date: stakingRewardTimestamp, blockHeight, blockHash, extra, }); } coinOperations.push({ id: (0, operation_1.encodeOperationId)(ledgerAccountId, hash, operationType), accountId: ledgerAccountId, type: operationType, value, recipients, senders, hash, fee, date: timestamp, blockHeight, blockHash, hasFailed, extra, }); return coinOperations; } async function listOperations({ currency, address, mirrorTokens, cursor, limit, order, fetchAllPages, skipFeesForTokenOperations, useEncodedHash, useSyntheticBlocks, }) { const coinOperations = []; const tokenOperations = []; const mirrorResult = await api_1.apiClient.getAccountTransactions({ address, pagingToken: cursor ?? null, order: order, limit: limit, fetchAllPages, }); const ledgerAccountId = (0, accountId_1.encodeAccountId)({ type: "js", version: "2", currencyId: currency.id, xpubOrAddress: address, derivationMode: "hederaBip44", }); for (const rawTx of mirrorResult.transactions) { const commonData = getCommonOperationData(rawTx, useEncodedHash, useSyntheticBlocks); // try to distinguish staking operations for CRYPTOUPDATEACCOUNT transactions const stakingAnalysis = rawTx.name === constants_1.HEDERA_TRANSACTION_NAMES.UpdateAccount ? await (0, utils_2.analyzeStakingOperation)(address, rawTx) : null; // process token transfers const tokenResult = await processTokenTransfers({ rawTx, address, currency, ledgerAccountId, commonData, skipFeesForTokenOperations, }); if (tokenResult?.coinOperation) coinOperations.push(tokenResult.coinOperation); if (tokenResult?.tokenOperation) tokenOperations.push(tokenResult.tokenOperation); // process regular transfers only if there were no token transfers if (!tokenResult) { const newCoinOperations = processTransfers({ rawTx, address, ledgerAccountId, commonData, mirrorTokens, stakingAnalysis, }); coinOperations.push(...newCoinOperations); } } return { coinOperations, tokenOperations, nextCursor: mirrorResult.nextCursor, }; } //# sourceMappingURL=listOperations.js.map