UNPKG

@pushchain/core

Version:

Push Chain is a true universal L1 that is 100% EVM compatible. It allows developers to deploy once and make their apps instantly compatible with users from all other L1s (Ethereum, Solana, etc) with zero on-chain code change.

322 lines 12.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EvmClient = void 0; const tslib_1 = require("tslib"); const viem_1 = require("viem"); /** * EVM client for reading and writing to Ethereum-compatible chains * * @example * // Initialize with an RPC URL * const evmClient = new EvmClient({ * rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/your-api-key' * }); */ class EvmClient { constructor({ rpcUrls }) { const transports = rpcUrls.map((rpcUrl) => (0, viem_1.http)(rpcUrl)); this.publicClient = (0, viem_1.createPublicClient)({ transport: (0, viem_1.fallback)(transports), }); } /** * Returns the balance (in wei) of an EVM address. * * @param address - The EVM address to check balance for * @returns Balance as a bigint in wei * * @example * // Get balance of an address * const balance = await evmClient.getBalance('0x123...'); * console.log(`Balance: ${balance} wei`); * * @example * // Check if an address has zero balance * const newAddress = privateKeyToAccount(generatePrivateKey()).address; * const balance = await evmClient.getBalance(newAddress); * if (balance === BigInt(0)) { * console.log('Address has no funds'); * } */ getBalance(address) { return tslib_1.__awaiter(this, void 0, void 0, function* () { return this.publicClient.getBalance({ address }); }); } /** * Performs a read-only call to a smart contract. * * @param params - Parameters including ABI, contract address, function name, and args * @returns The result of the contract call with the specified type * * @example * // Read a greeting value from a contract * const greeting = await evmClient.readContract<string>({ * abi: parseAbi(['function greet() view returns (string)']), * address: '0x2ba5873eF818BEE57645B7d674149041C44F42c6', * functionName: 'greet', * }); * console.log(`Current greeting: ${greeting}`); * * @example * // Reading with arguments * const balance = await evmClient.readContract<bigint>({ * abi: parseAbi(['function balanceOf(address) view returns (uint256)']), * address: '0xTokenAddress', * functionName: 'balanceOf', * args: ['0xUserAddress'], * }); */ readContract(_a) { return tslib_1.__awaiter(this, arguments, void 0, function* ({ abi, address, functionName, args = [], }) { return this.publicClient.readContract({ abi: abi, address: address, functionName, args, }); }); } /** * Returns the ERC-20 token balance of an owner address. * * This is a convenience wrapper around readContract using the minimal * ERC-20 ABI: balanceOf(address) -> uint256. */ getErc20Balance(_a) { return tslib_1.__awaiter(this, arguments, void 0, function* ({ tokenAddress, ownerAddress, }) { const { parseAbi } = yield Promise.resolve().then(() => tslib_1.__importStar(require('viem'))); const erc20Abi = parseAbi([ 'function balanceOf(address) view returns (uint256)', ]); return this.readContract({ abi: erc20Abi, address: tokenAddress, functionName: 'balanceOf', args: [ownerAddress], }); }); } /** * Writes a transaction to a smart contract using a UniversalSigner. * This function handles contract interaction by encoding function data * and sending the transaction through sendTransaction. * * @param params - Parameters including ABI, contract address, function name, args, value and signer * @returns Transaction hash as a hex string * * @example * // Set a new greeting on a contract * const txHash = await evmClient.writeContract({ * abi: parseAbi(['function setGreeting(string _greeting)']), * address: '0x2ba5873eF818BEE57645B7d674149041C44F42c6', * functionName: 'setGreeting', * args: ['Hello from Push SDK!'], * signer: universalSigner, * }); * console.log(`Transaction sent: ${txHash}`); * * @example * // Sending ether with a contract interaction * const txHash = await evmClient.writeContract({ * abi: parseAbi(['function deposit() payable']), * address: '0xContractAddress', * functionName: 'deposit', * value: parseEther('0.1'), // Send 0.1 ETH * signer: universalSigner, * }); */ writeContract(_a) { return tslib_1.__awaiter(this, arguments, void 0, function* ({ abi, address, functionName, args = [], value = (0, viem_1.parseEther)('0'), signer, }) { const data = (0, viem_1.encodeFunctionData)({ abi: abi, functionName, args, }); return this.sendTransaction({ to: address, data, value, signer, }); }); } /** * Sends a raw EVM transaction using a UniversalSigner. * This handles the full transaction flow: * 1. Gets nonce, estimates gas, and gets current fee data * 2. Serializes and signs the transaction * 3. Broadcasts the signed transaction to the network * * @param params - Transaction parameters including destination, data, value and signer * @returns Transaction hash as a hex string * * @example * // Send a simple ETH transfer * const txHash = await evmClient.sendTransaction({ * to: '0xRecipientAddress', * data: '0x', // empty data for a simple transfer * value: parseEther('0.01'), * signer: universalSigner, * }); * console.log(`ETH transfer sent: ${txHash}`); */ sendTransaction(_a) { return tslib_1.__awaiter(this, arguments, void 0, function* ({ to, data, value = (0, viem_1.parseEther)('0'), signer, }) { const [nonce, gas, feePerGas] = yield Promise.all([ this.publicClient.getTransactionCount({ address: signer.account.address, }), // Use fixed gas for simple transfers, estimate for contract interactions data === '0x' ? Promise.resolve(BigInt(21000)) : this.publicClient.estimateGas({ account: signer.account.address, to, data, value, }), this.publicClient.estimateFeesPerGas(), ]); const chainId = yield this.publicClient.getChainId(); const unsignedTx = (0, viem_1.serializeTransaction)({ chainId, type: 'eip1559', to, data, gas, nonce, maxFeePerGas: feePerGas.maxFeePerGas, maxPriorityFeePerGas: feePerGas.maxPriorityFeePerGas, value, }); if (!signer.signAndSendTransaction) { throw new Error('signer.signAndSendTransaction is undefined'); } const txHashBytes = yield signer.signAndSendTransaction((0, viem_1.hexToBytes)(unsignedTx)); return (0, viem_1.bytesToHex)(txHashBytes); }); } /** * Estimates the gas required for a transaction. * * @param params - Parameters including from/to addresses, value and optional data * @returns Estimated gas as a bigint * * @example * // Estimate gas for a simple transfer * const gasEstimate = await evmClient.estimateGas({ * from: '0xSenderAddress', * to: '0xRecipientAddress', * value: parseEther('0.01'), * }); * console.log(`Estimated gas: ${gasEstimate}`); * * @example * // Estimate gas for a contract interaction * const data = encodeFunctionData({ * abi: parseAbi(['function setGreeting(string)']), * functionName: 'setGreeting', * args: ['New greeting'], * }); * * const gasEstimate = await evmClient.estimateGas({ * from: universalSigner.account.address as `0x${string}`, * to: '0xContractAddress', * data, * value: BigInt(0), * }); */ estimateGas(_a) { return tslib_1.__awaiter(this, arguments, void 0, function* ({ from, to, value, data, }) { return this.publicClient.estimateGas({ account: from || undefined, to, value, data, }); }); } /** * Gets the current gas price on the network. * This is primarily used for legacy transactions, but can be useful * for gas cost estimation in EIP-1559 transactions as well. * * @returns Current gas price in wei as a bigint * * @example * // Get current gas price for cost estimation * const gasPrice = await evmClient.getGasPrice(); * console.log(`Current gas price: ${gasPrice} wei`); * * @example * // Calculate total cost of a transaction * const gasPrice = await evmClient.getGasPrice(); * const gasEstimate = await evmClient.estimateGas({...}); * const totalCost = gasPrice * gasEstimate; * console.log(`Estimated transaction cost: ${totalCost} wei`); */ getGasPrice() { return tslib_1.__awaiter(this, void 0, void 0, function* () { return this.publicClient.getGasPrice(); }); } /** * Fetches the full transaction response by hash. * * @param txHash - The transaction hash to query * @returns The transaction object or null if not found * * @example * const tx = await evmClient.getTransaction('0xabc...'); * console.log(tx?.from, tx?.to, tx?.value); */ getTransaction(txHash) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const tx = yield this.publicClient.getTransaction({ hash: txHash }); if (!tx) throw new Error('No transaction found!'); const wait = (...args_1) => tslib_1.__awaiter(this, [...args_1], void 0, function* (confirmations = 1) { const receipt = yield this.publicClient.waitForTransactionReceipt({ hash: txHash, confirmations, }); return receipt; }); return Object.assign(Object.assign({}, tx), { wait }); }); } /** * Waits for a transaction to achieve the desired number of confirmations. * * @param txHash - Transaction hash * @param confirmations - Number of confirmations to wait for (default: 3) * @param pollIntervalMs - How often to check (default: 1000 ms) * @param timeoutMs - Maximum time to wait before error (default: 60000 ms) */ waitForConfirmations(_a) { return tslib_1.__awaiter(this, arguments, void 0, function* ({ txHash, confirmations = 3, pollIntervalMs = 1000, timeoutMs = 30000, }) { // first, wait for the tx to land in a block const receipt = yield this.publicClient.waitForTransactionReceipt({ hash: txHash, }); const targetBlock = receipt.blockNumber + BigInt(confirmations); const startTime = Date.now(); // poll until we hit the target block or timeout // eslint-disable-next-line no-constant-condition while (true) { const currentBlock = yield this.publicClient.getBlockNumber(); if (currentBlock >= targetBlock) { return; } if (Date.now() - startTime > timeoutMs) { throw new Error(`Timeout: transaction ${txHash} not confirmed with ${confirmations} confirmations ` + `within ${timeoutMs} ms`); } yield new Promise((r) => setTimeout(r, pollIntervalMs)); } }); } } exports.EvmClient = EvmClient; //# sourceMappingURL=evm-client.js.map