UNPKG

chaingate

Version:

Multi-chain cryptocurrency SDK for TypeScript — unified API for Bitcoin, Ethereum, Litecoin, Dogecoin, Bitcoin Cash, Polygon, Arbitrum, and any EVM-compatible chain. Create wallets, query balances, send transactions, and manage tokens and NFTs across UTXO

187 lines (186 loc) 7.62 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EvmTransaction = void 0; const decimal_js_1 = __importDefault(require("decimal.js")); const evmTx_1 = require("../../utils/evmTx"); const evmGasFallback_1 = require("../../utils/evmGasFallback"); const networks_1 = require("../../ChainGate/networks"); const errors_1 = require("../../errors"); const BaseEvmTransaction_1 = require("./BaseEvmTransaction"); const BroadcastedEvmTransaction_1 = require("./BroadcastedEvmTransaction"); /** Converts Gwei string to wei bigint. */ function gweiToWei(gwei) { const d = new decimal_js_1.default(gwei).mul(new decimal_js_1.default(10).pow(9)); return BigInt(d.toFixed(0)); } /** Converts an API fee grade to our EvmRecommendedFee type, computing enoughFunds. */ function gradeToFee(grade, gasLimit, valueWei, balanceWei, gasEstimationFailed) { const maxFeeGwei = grade.maxFeePerGasGwei ?? grade.gasPriceGwei ?? '0'; const tipGwei = grade.maxPriorityFeePerGasGwei ?? '0'; const maxFeePerGas = gweiToWei(maxFeeGwei); const maxPriorityFeePerGas = gweiToWei(tipGwei); const totalCost = valueWei + maxFeePerGas * gasLimit; const enoughFunds = balanceWei >= totalCost; const fee = { maxFeePerGas, maxPriorityFeePerGas, estimatedConfirmationSecs: grade.confirmationTimeSecs, enoughFunds, }; if (!enoughFunds) { fee.missingFunds = totalCost - balanceWei; } if (gasEstimationFailed) { fee.gasEstimationFailed = true; } return fee; } /** * An unsigned EVM transaction prepared by {@link EvmConnector.transfer}. * * @example * ```ts * const amount = cg.networks.ethereum.amount('0.1'); * const tx = await eth.transfer(amount, '0xRecipient...'); * * // Inspect recommended fees * const fees = tx.recommendedFees(); * console.log(fees.normal.maxFeePerGas); * * // Override with a specific tier * tx.setFee(fees.high); * * // Or set a manual fee with optional gas limit override * tx.setFee({ maxFeePerGas: 30_000_000_000n, maxPriorityFeePerGas: 2_000_000_000n, gasLimit: 50_000n }); * * // Sign and broadcast * const broadcasted = await tx.signAndBroadcast(); * console.log(broadcasted.transactionId); * * // Wait for confirmation * const cancel = broadcasted.onConfirmed((details) => { * console.log('Confirmed in block', details.blockHeight); * }); * * // Stop waiting at any time: * cancel(); * ``` */ class EvmTransaction extends BaseEvmTransaction_1.BaseEvmTransaction { /** @internal */ constructor(params) { super({ fromAddress: params.fromAddress, toAddress: params.toAddress, valueWei: params.valueWei, data: params.data, nonce: params.nonce, gasLimit: params.gasLimit, chainId: params.chainId, balanceWei: params.balanceWei, getPrivateKey: params.getPrivateKey, initialFee: { maxFeePerGas: params.feeRates.normal.maxFeePerGas, maxPriorityFeePerGas: params.feeRates.normal.maxPriorityFeePerGas, }, }); this.explorer = params.explorer; this.feeRates = params.feeRates; } /** * Returns all recommended fee tiers (low, normal, high, maximum). * * Each tier includes `maxFeePerGas`, `maxPriorityFeePerGas` (both in wei), * and `estimatedConfirmationSecs`. */ recommendedFees() { return this.feeRates; } /** * Sets the fee for this transaction. * * Pass a tier object from {@link recommendedFees} or an object with manual * `maxFeePerGas`, `maxPriorityFeePerGas` (in wei), and an optional * `gasLimit` override. * * @throws {@link TransactionAlreadySentError} if the transaction has already been sent. */ setFee(fee) { this.applyFee(fee); } signTransaction(privateKey, params) { return (0, evmTx_1.signEip1559Transaction)(params, privateKey); } async broadcast(signedRaw) { const { transactionId } = await this.explorer.broadcastTransaction(signedRaw); return transactionId; } recordNonceUsed() { this.explorer.global.evmNonceCache.recordUsed(Number(this.chainId), this.fromAddress, this.nonce); } buildBroadcasted(transactionId) { return new BroadcastedEvmTransaction_1.BroadcastedEvmTransaction(transactionId, this.explorer); } /** @internal — used by EvmConnector.transfer to build the transaction. */ static async create(params) { const { explorer, fromAddress, toAddress, valueWei, data = '0x', getPrivateKey } = params; // Fetch the nonce first so we can pass it to estimateGas (the nonce can // affect the estimate for contracts whose execution depends on account // state). Gas estimation may fail when the sender lacks sufficient funds; // in that case we fall back to the intrinsic gas for a simple transfer // and flag every fee tier as insufficient. const networkNonce = BigInt((await explorer.getNonce(fromAddress)).nonce); const networkInfo = networks_1.NETWORKS_INFO[explorer.network]; if (!networkInfo.chainId) { throw new errors_1.UnsupportedOperationError(`Network '${explorer.network}' does not have a configured chain ID.`); } const chainId = BigInt(networkInfo.chainId); const cachedNonce = explorer.global.evmNonceCache.get(networkInfo.chainId, fromAddress); const nonce = cachedNonce !== undefined && cachedNonce > networkNonce ? cachedNonce : networkNonce; const [gasEstimateResult, feeRateResult, balanceResult] = await Promise.all([ explorer .estimateGas({ addressFrom: fromAddress, addressTo: toAddress, nonce: nonce.toString(), amount: valueWei.toString(), data: data !== '0x' ? data : undefined, }) .catch(() => null), explorer.getFeeRate(), explorer.getAddressBalance(fromAddress), ]); const gasEstimationFailed = gasEstimateResult === null; const gasLimit = gasEstimationFailed ? (0, evmGasFallback_1.fallbackGasFromCalldata)(data) : BigInt(gasEstimateResult.estimatedGas); const balanceWei = balanceResult.confirmed.min(); const feeRates = parseApiFeeTiers(feeRateResult, gasLimit, valueWei, balanceWei, gasEstimationFailed); return new EvmTransaction({ explorer, fromAddress, toAddress, valueWei, data, nonce, gasLimit, chainId, balanceWei, feeRates, getPrivateKey, }); } } exports.EvmTransaction = EvmTransaction; /** Converts the fee rate response to our EvmRecommendedFees type. */ function parseApiFeeTiers(apiResponse, gasLimit, valueWei, balanceWei, gasEstimationFailed) { return { low: gradeToFee(apiResponse.low, gasLimit, valueWei, balanceWei, gasEstimationFailed), normal: gradeToFee(apiResponse.normal, gasLimit, valueWei, balanceWei, gasEstimationFailed), high: gradeToFee(apiResponse.high, gasLimit, valueWei, balanceWei, gasEstimationFailed), maximum: gradeToFee(apiResponse.maximum, gasLimit, valueWei, balanceWei, gasEstimationFailed), }; }