linea-mcp
Version:
A Model Context Protocol server for interacting with the Linea blockchain
135 lines (134 loc) • 4.56 kB
JavaScript
import { createPublicClient, http, formatEther, getContract, } from 'viem';
import { linea, lineaSepolia, mainnet } from 'viem/chains';
import config from '../config/index.js';
/**
* Service for interacting with blockchains using viem's PublicClient
*/
class BlockchainService {
_client;
network;
chain;
/**
* Create a new BlockchainService instance
* @param network The network to connect to (mainnet, testnet, or ethereum)
*/
constructor(network = 'mainnet') {
this.network = network;
let rpcUrl;
let chain;
switch (network) {
case 'ethereum':
rpcUrl = config.rpc.ethereum;
chain = mainnet;
break;
case 'testnet':
rpcUrl = config.rpc.testnet;
chain = lineaSepolia;
break;
case 'mainnet':
default:
// Ensure mainnet RPC is defined in config, fallback if necessary
rpcUrl = config.rpc.mainnet || 'https://rpc.linea.build';
chain = linea;
break;
}
if (!rpcUrl) {
throw new Error(`RPC URL for network "${network}" is not configured.`);
}
this.chain = chain;
this._client = createPublicClient({
chain: this.chain,
transport: http(rpcUrl),
});
}
/**
* Get the current viem PublicClient instance
* @returns The PublicClient instance
*/
get client() {
return this._client;
}
/**
* Get the current viem Chain object
* @returns The Chain object
*/
get currentChain() {
return this.chain;
}
/**
* Get the current network name
* @returns The network name (mainnet, testnet, or ethereum)
*/
get currentNetwork() {
return this.network;
}
/**
* Get the current block number
* @returns A promise that resolves to the current block number (bigint)
*/
async getBlockNumber() {
return this._client.getBlockNumber();
}
/**
* Get the balance of an address
* @param address The address (0x...) to check the balance of
* @returns A promise that resolves to the balance formatted as an Ether string
*/
async getBalance(address) {
const balance = await this._client.getBalance({ address });
return formatEther(balance);
}
/**
* Get a transaction by its hash
* @param txHash The transaction hash (0x...)
* @returns A promise that resolves to the transaction details or null if not found
*/
async getTransaction(txHash) {
return this._client.getTransaction({ hash: txHash });
}
/**
* Get a transaction receipt by its hash
* @param txHash The transaction hash (0x...)
* @returns A promise that resolves to the transaction receipt or null if not found/mined
*/
async getTransactionReceipt(txHash) {
return this._client.getTransactionReceipt({ hash: txHash });
}
/**
* Create a read-only contract instance
* @param address The contract address (0x...)
* @param abi The contract ABI
* @returns A viem Contract instance for read operations
*/
createContract(address, abi // Explicitly require Abi type
) {
return getContract({
address,
abi: abi, // Pass the Abi directly
client: { public: this._client }, // Use client property name expected by getContract
}); // Cast to any to bypass the persistent type error
}
/**
* Estimate gas for a transaction
* @param transaction The transaction parameters (matching viem's EstimateGasParameters)
* @returns A promise that resolves to the gas estimate (bigint)
*/
async estimateGas(transaction) {
// Account needs to be passed for estimateGas
if (!transaction.account) {
// If no account is provided, we might need to fetch the default one
// or throw an error, depending on expected usage.
// For now, let's throw, as estimating gas usually requires a sender context.
throw new Error("Account is required for gas estimation.");
}
return this._client.estimateGas(transaction);
}
/**
* Get the current gas price
* @returns A promise that resolves to the current gas price (bigint)
*/
async getGasPrice() {
return this._client.getGasPrice();
}
}
export default BlockchainService;