UNPKG

@ledgerhq/coin-hedera

Version:
198 lines 8.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.enrichERC20Transfers = exports.getERC20Operations = void 0; exports.createTransactionId = createTransactionId; exports.parseTransfers = parseTransfers; exports.getERC20BalancesForAccount = getERC20BalancesForAccount; exports.getERC20BalancesForAccountV2 = getERC20BalancesForAccountV2; exports.parseThirdwebTransactionParams = parseThirdwebTransactionParams; const sdk_1 = require("@hashgraph/sdk"); const state_1 = require("@ledgerhq/cryptoassets/state"); const live_env_1 = require("@ledgerhq/live-env"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const constants_1 = require("../constants"); const utils_1 = require("../logic/utils"); const api_1 = require("./api"); const hgraph_1 = require("./hgraph"); async function createTransactionId(accountId, config) { if (!config.useNetworkTimestamp) { return sdk_1.TransactionId.generate(accountId); } try { const lastBlock = await api_1.apiClient.getLatestBlock(); const validStart = (0, utils_1.toTimestamp)(lastBlock.timestamp.to ?? lastBlock.timestamp.from); return sdk_1.TransactionId.withValidStart(sdk_1.AccountId.fromString(accountId), validStart); } catch { return sdk_1.TransactionId.generate(accountId); } } function isValidRecipient(accountId, recipients) { if (accountId.shard.eq(0) && accountId.realm.eq(0)) { // account is a node, only add to list if we have none if (accountId.num.lt(100)) { return recipients.length === 0; } // account is a system account that is not a node, do NOT add if (accountId.num.lt(1000)) { return false; } } return true; } function parseTransfers(mirrorTransfers, address, stakingReward = new bignumber_js_1.default(0)) { let value = new bignumber_js_1.default(0); let type = "NONE"; const senders = []; const recipients = []; const rewardPayerAddress = (0, live_env_1.getEnv)("HEDERA_STAKING_REWARD_ACCOUNT_ID"); for (const transfer of mirrorTransfers) { const amount = new bignumber_js_1.default(transfer.amount); const accountId = sdk_1.AccountId.fromString(transfer.account); // staking reward is included in transfer, so it can be positive even if user sent less HBARs than the reward is const amountWithoutReward = transfer.account === address ? amount.minus(stakingReward) : amount; if (transfer.account === address) { value = amountWithoutReward.abs(); type = amountWithoutReward.isNegative() ? "OUT" : "IN"; } if (amountWithoutReward.isNegative()) { // exclude reward payer from senders list, because rewards are shown as separate operations const shouldIgnoreAddress = transfer.account === rewardPayerAddress && stakingReward.gt(0); if (shouldIgnoreAddress) { continue; } senders.push(transfer.account); } else if (isValidRecipient(accountId, recipients)) { recipients.push(transfer.account); } } // NOTE: earlier addresses are the "fee" addresses senders.reverse(); recipients.reverse(); return { type, value, senders, recipients, }; } // TODO: remove once migration to new API is complete async function getERC20BalancesForAccount(evmAccountId, supportedTokenIds = constants_1.SUPPORTED_ERC20_TOKENS.map(token => token.id)) { const availableTokens = []; for (const erc20TokenId of supportedTokenIds) { const calToken = await (0, state_1.getCryptoAssetsStore)().findTokenById(erc20TokenId); if (calToken) { availableTokens.push(calToken); } } const promises = availableTokens.map(async (erc20token) => { const balance = await api_1.apiClient.getERC20Balance(evmAccountId, erc20token.contractAddress); return { balance, token: erc20token, }; }); const balances = await Promise.all(promises); return balances; } async function getERC20BalancesForAccountV2(address) { const balances = []; const rawBalances = await hgraph_1.hgraphClient.getERC20Balances({ address }); for (const rawBalance of rawBalances) { const rawBalanceTokenId = (0, utils_1.toEntityId)({ num: rawBalance.token_id }); const supportedToken = constants_1.SUPPORTED_ERC20_TOKENS.find(token => { return token.tokenId === rawBalanceTokenId; }); if (!supportedToken) { continue; } const calToken = await (0, state_1.getCryptoAssetsStore)().findTokenById(supportedToken.id); if (!calToken) { continue; } balances.push({ token: calToken, balance: new bignumber_js_1.default(rawBalance.balance), }); } return balances; } // TODO: remove once migration to new API is complete const getERC20Operations = async (latestERC20Transactions) => { const latestERC20Operations = []; for (const thirdwebTransaction of latestERC20Transactions) { const tokenId = thirdwebTransaction.address; const token = await (0, state_1.getCryptoAssetsStore)().findTokenByAddressInCurrency(tokenId, "hedera"); if (!token) continue; const hash = thirdwebTransaction.transactionHash; const contractCallResult = await api_1.apiClient.getContractCallResult(hash); const mirrorTransaction = await api_1.apiClient.findTransactionByContractCall(contractCallResult.timestamp, contractCallResult.contract_id); if (!mirrorTransaction) continue; latestERC20Operations.push({ thirdwebTransaction, mirrorTransaction, contractCallResult, token, }); } return latestERC20Operations; }; exports.getERC20Operations = getERC20Operations; // TODO: remove once migration to new API is complete function parseThirdwebTransactionParams(transaction) { const { from, to, value } = transaction.decoded.params; if (typeof from !== "string" || typeof to !== "string" || typeof value !== "string") { return null; } return { from, to, value }; } /** * Enriches raw ERC20 transfers from Hgraph with additional data needed for operations: * - fetches contract call result containing gas metrics and block hash * - finds the corresponding Mirror Node transaction by consensus timestamp * * @param erc20Transfers - Raw ERC20 transfers from Hgraph API * @returns Array of enriched transfers with complete operation data, filtered to supported tokens only */ const enrichERC20Transfers = async (erc20Transfers) => { const enrichedTransfers = []; // with hgraph we can get two different transfers with the same transaction hash const groupedByTxHash = new Map(); for (const transfer of erc20Transfers) { const group = groupedByTxHash.get(transfer.transaction_hash); if (!group) { groupedByTxHash.set(transfer.transaction_hash, [transfer]); continue; } group.push(transfer); } for (const [txHash, transfers] of groupedByTxHash.entries()) { const payerAddress = (0, utils_1.toEntityId)({ num: transfers[0].payer_account_id }); const inaccurateConsensusTimestampNs = new bignumber_js_1.default(transfers[0].consensus_timestamp); const inaccurateConsensusTimestamp = (0, utils_1.nanosToSeconds)(inaccurateConsensusTimestampNs).toFixed(9); const [contractCallResult, mirrorTransaction] = await Promise.all([ api_1.apiClient.getContractCallResult(txHash), api_1.apiClient.findTransactionByContractCallV2({ payerAddress, timestamp: inaccurateConsensusTimestamp, }), ]); if (!mirrorTransaction) { continue; } enrichedTransfers.push({ transfers, contractCallResult, mirrorTransaction, }); } return enrichedTransfers; }; exports.enrichERC20Transfers = enrichERC20Transfers; //# sourceMappingURL=utils.js.map