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

167 lines (166 loc) 6.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EvmRpcExplorer = void 0; const errors_1 = require("../../errors"); const EvmNonceCache_1 = require("../../utils/EvmNonceCache"); /** * Lightweight EVM explorer that communicates directly with a JSON-RPC 2.0 * endpoint. No dependency on the ChainGate API. * * Only exposes the subset of RPC methods needed by {@link EvmRpcConnector} and * {@link EvmRpcTransaction}. */ class EvmRpcExplorer { constructor(rpcUrl, chainId, nonceCache) { this.nextId = 1; this.rpcUrl = rpcUrl; this.chainId = chainId; this.nonceCache = nonceCache ?? new EvmNonceCache_1.EvmNonceCache(); } // --------------------------------------------------------------------------- // Public RPC helpers // --------------------------------------------------------------------------- /** * Returns the wei balance for an address (`eth_getBalance` at `"latest"`). */ async getBalance(address) { const hex = await this.call('eth_getBalance', [address, 'latest']); return BigInt(hex); } /** * Returns the nonce / transaction count for an address * (`eth_getTransactionCount` at `"latest"`). */ async getTransactionCount(address) { const hex = await this.call('eth_getTransactionCount', [address, 'latest']); return BigInt(hex); } /** * Returns the next nonce to use when sending a transaction from an address * (`eth_getTransactionCount` at `"pending"`). */ async getNonce(address) { const hex = await this.call('eth_getTransactionCount', [address, 'pending']); return BigInt(hex); } /** * Estimates gas for a transaction (`eth_estimateGas`). */ async estimateGas(params) { const txObj = { from: params.from, to: params.to, value: '0x' + params.value.toString(16), }; if (params.data && params.data !== '0x') { txObj.data = params.data; } if (params.nonce !== undefined) { txObj.nonce = '0x' + params.nonce.toString(16); } const hex = await this.call('eth_estimateGas', [txObj]); return BigInt(hex); } /** * Returns fee data for the current block. * * Tries EIP-1559 (`eth_maxPriorityFeePerGas` + `baseFeePerGas` from the * latest block). If the chain does not support EIP-1559, falls back to * `eth_gasPrice`. */ async getFeeData() { const gasPrice = await this.getGasPrice(); try { const [priorityFeeHex, block] = await Promise.all([ this.call('eth_maxPriorityFeePerGas', []), this.call('eth_getBlockByNumber', ['latest', false]), ]); if (block.baseFeePerGas) { const baseFee = BigInt(block.baseFeePerGas); const maxPriorityFeePerGas = BigInt(priorityFeeHex); // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (same heuristic as ethers.js) const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas; return { supportsEip1559: true, maxFeePerGas, maxPriorityFeePerGas, gasPrice, }; } } catch { // Chain does not support EIP-1559 — fall through to legacy. } return { supportsEip1559: false, gasPrice }; } /** * Broadcasts a signed raw transaction (`eth_sendRawTransaction`). * * @returns The transaction hash. */ async sendRawTransaction(signedTxHex) { return this.call('eth_sendRawTransaction', [signedTxHex]); } /** * Returns the transaction receipt, or `null` if the transaction is still * pending (`eth_getTransactionReceipt`). */ async getTransactionReceipt(txHash) { const result = await this.call('eth_getTransactionReceipt', [txHash]); if (!result) return null; return { blockNumber: Number(BigInt(result.blockNumber)), status: Number(BigInt(result.status)), }; } /** * Returns the number of decimals for an ERC-20 token contract. */ async getTokenDecimals(contractAddress) { // decimals() selector: 0x313ce567 const result = await this.call('eth_call', [ { to: contractAddress, data: '0x313ce567' }, 'latest', ]); return Number(BigInt(result)); } // --------------------------------------------------------------------------- // Internal // --------------------------------------------------------------------------- /** Returns the current gas price via `eth_gasPrice`. */ async getGasPrice() { const hex = await this.call('eth_gasPrice', []); return BigInt(hex); } /** * Sends a JSON-RPC 2.0 request to the configured endpoint. * * @throws {RpcError} if the response contains an `error` field or the HTTP * request fails. */ async call(method, params) { const id = this.nextId++; const body = JSON.stringify({ jsonrpc: '2.0', method, params, id }); let res; try { res = await fetch(this.rpcUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body, }); } catch (err) { throw new errors_1.RpcError(`RPC request to ${this.rpcUrl} failed: ${err instanceof Error ? err.message : String(err)}`); } if (!res.ok) { throw new errors_1.RpcError(`RPC HTTP error ${res.status}: ${res.statusText}`); } const json = (await res.json()); if (json.error) { throw new errors_1.RpcError(json.error.message, json.error.code); } return json.result; } } exports.EvmRpcExplorer = EvmRpcExplorer;