linea-mcp
Version:
A Model Context Protocol server for interacting with the Linea blockchain
136 lines (135 loc) • 5.19 kB
JavaScript
import { ethers } from 'ethers';
import BlockchainService from '../../services/blockchain.js';
import KeyManagementService from '../../services/keyManagement.js';
/**
* Call a contract function
* @param params The parameters for calling a contract function
* @returns The result of the function call
*/
export async function callContract(params) {
try {
const { contractAddress, abi, functionName, params: functionParams = [], value } = params;
// Initialize services
const blockchain = new BlockchainService('mainnet');
const keyService = new KeyManagementService();
// Normalize ABI format
let normalizedAbi = abi;
if (typeof abi === 'string') {
// If ABI is a single function string, convert it to an array
normalizedAbi = [abi];
}
// Create contract instance
const contract = blockchain.createContract(contractAddress, normalizedAbi);
// Check if the function is read-only or requires a transaction
const fragment = contract.interface.getFunction(functionName);
const isReadOnly = fragment.constant || fragment.stateMutability === 'view' || fragment.stateMutability === 'pure';
let result;
if (isReadOnly) {
// For read-only functions, just call the function
result = await contract[functionName](...functionParams);
}
else {
// For state-changing functions, we need a signer
const wallet = keyService.getDefaultWallet();
const connectedWallet = wallet.connect(blockchain.provider);
const contractWithSigner = blockchain.createContractWithSigner(contractAddress, normalizedAbi, connectedWallet);
// Prepare transaction options
const options = {};
if (value) {
options.value = ethers.utils.parseEther(value);
}
// Execute the transaction
const tx = await contractWithSigner[functionName](...functionParams, options);
await tx.wait();
result = {
transactionHash: tx.hash,
from: wallet.address,
};
}
return {
success: true,
contractAddress,
functionName,
result: formatResult(result),
};
}
catch (error) {
console.error('Error in callContract:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
throw new Error(`Failed to call contract: ${errorMessage}`);
}
}
/**
* Deploy a contract
* @param params The parameters for deploying a contract
* @returns The deployed contract details
*/
export async function deployContract(params) {
try {
const { bytecode, abi, constructorArgs = [], value } = params;
// Initialize services
const blockchain = new BlockchainService('mainnet');
const keyService = new KeyManagementService();
// Normalize ABI format
let normalizedAbi = abi;
if (typeof abi === 'string') {
// If ABI is a single function string, convert it to an array
normalizedAbi = [abi];
}
// Get a wallet for deployment
const wallet = keyService.getDefaultWallet();
const connectedWallet = wallet.connect(blockchain.provider);
// Create contract factory
const factory = new ethers.ContractFactory(normalizedAbi, bytecode, connectedWallet);
// Prepare deployment options
const options = {};
if (value) {
options.value = ethers.utils.parseEther(value);
}
// Deploy the contract
const contract = await factory.deploy(...constructorArgs, options);
await contract.deployed();
return {
success: true,
contractAddress: contract.address,
transactionHash: contract.deployTransaction.hash,
deployer: wallet.address,
abi,
};
}
catch (error) {
console.error('Error in deployContract:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
throw new Error(`Failed to deploy contract: ${errorMessage}`);
}
}
/**
* Format the result of a contract call to be JSON-serializable
* @param result The result to format
* @returns The formatted result
*/
function formatResult(result) {
if (result === null || result === undefined) {
return null;
}
if (typeof result === 'object') {
if (ethers.BigNumber.isBigNumber(result)) {
return result.toString();
}
if (Array.isArray(result)) {
return result.map(formatResult);
}
if (result._isBigNumber) {
return result.toString();
}
// Handle objects
const formatted = {};
for (const key in result) {
if (Object.prototype.hasOwnProperty.call(result, key) && isNaN(Number(key))) {
formatted[key] = formatResult(result[key]);
}
}
return formatted;
}
return result;
}