@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
JavaScript
;
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