Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,40 @@ Example query to Claude:

> "Buy $20 worth of OpenRouter credits."

### Advanced Contract Tools

The Base MCP server now includes advanced contract interaction capabilities:

#### execute-batch-transactions
Execute multiple transactions in a batch with optimized gas usage.

Example query to Claude:
> "Execute these transactions in a batch: transfer 0.1 ETH to 0x123... and 0.2 ETH to 0x456..."

#### subscribe-contract-events
Subscribe to contract events with filtering capabilities.

Example query to Claude:
> "Subscribe to Transfer events from contract 0x789..."

#### execute-contract-call
Execute a contract call with advanced options and gas optimization.

Example query to Claude:
> "Call the mint function on contract 0xabc... with parameters [1, 2, 3]"

#### optimize-gas
Optimize gas usage based on different strategies (fast, medium, slow).

Example query to Claude:
> "What's the optimal gas price for a fast transaction?"

#### validate-abi
Validate contract ABI with comprehensive checks.

Example query to Claude:
> "Validate this contract ABI for any issues"

## Security Considerations

- The configuration file contains sensitive information (API keys and seed phrases). Ensure it's properly secured and not shared.
Expand Down
89 changes: 89 additions & 0 deletions examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,92 @@ _Claude uses the `buy_openrouter_credits` tool with the following parameters:_
6. **Verify contract interactions** - When interacting with smart contracts, verify the contract address and function parameters before confirming transactions.

7. **Use onramp responsibly** - When onramping funds, be aware of any fees or limits that may apply.

## Advanced Contract Tools Examples

### Batch Transactions

Execute multiple transactions in a single batch to save gas and ensure atomicity:

```typescript
// Example batch transaction
const batchTx = {
transactions: [
{
to: "0x1234567890123456789012345678901234567890",
data: "0x...",
value: "0.1"
},
{
to: "0x0987654321098765432109876543210987654321",
data: "0x...",
value: "0.2"
}
],
gasLimit: "300000",
maxFeePerGas: "50",
maxPriorityFeePerGas: "2"
};
```

### Contract Event Subscription

Subscribe to specific contract events with filtering:

```typescript
// Example event subscription
const subscription = {
contractAddress: "0x1234567890123456789012345678901234567890",
eventName: "Transfer",
fromBlock: 1000000,
toBlock: 2000000,
filter: {
from: "0x123...",
to: "0x456..."
}
};
```

### Advanced Contract Calls

Execute contract calls with optimized gas and advanced options:

```typescript
// Example contract call
const contractCall = {
contractAddress: "0x1234567890123456789012345678901234567890",
abi: [...],
functionName: "mint",
args: [1, 2, 3],
value: "0.1",
gasLimit: "200000"
};
```

### Gas Optimization

Optimize gas usage based on different strategies:

```typescript
// Example gas optimization
const gasOptimization = {
strategy: "fast", // or "medium" or "slow"
maxGasPrice: "100", // in gwei
priorityFee: "2" // in gwei
};
```

### ABI Validation

Validate contract ABIs for potential issues:

```typescript
// Example ABI validation
const abiValidation = {
abi: [...],
bytecode: "0x...",
validateConstructor: true,
validateFunctions: true,
validateEvents: true
};
```
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"name": "base-mcp",
"version": "1.0.11",
"description": "A Model Context Protocol (MCP) server that provides onchain tools for Claude AI, allowing it to interact with the Base blockchain and Coinbase API",
"bin": {
"base-mcp": "build/index.js"
},
"bin": "build/index.js",
"type": "module",
"scripts": {
"run": "tsx src/index.ts",
Expand Down Expand Up @@ -47,11 +45,14 @@
"@coinbase/agentkit-model-context-protocol": "^0.2.0",
"@coinbase/coinbase-sdk": "^0.21.0",
"@coinbase/onchainkit": "^0.37.6",
"@ethersproject/contracts": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@inquirer/prompts": "^7.4.0",
"@modelcontextprotocol/sdk": "^1.6.1",
"@privy-io/public-api": "^2.20.5",
"chalk": "^5.4.1",
"dotenv": "^16.4.7",
"ethers": "^5.7.2",
"graphql": "^16.10.0",
"graphql-request": "^7.1.2",
"reflect-metadata": "^0.2.2",
Expand Down
178 changes: 178 additions & 0 deletions src/tools/advanced-contracts/handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { ethers } from 'ethers';
import { BaseProvider } from '@ethersproject/providers';
import { Contract } from '@ethersproject/contracts';
import {
BatchTransactionSchema,
ContractEventSubscriptionSchema,
ContractCallSchema,
GasOptimizationSchema,
ABIValidationSchema
} from './schemas.js';

export class AdvancedContractHandler {
private provider: BaseProvider;
private wallet: ethers.Wallet;

constructor(provider: BaseProvider, wallet: ethers.Wallet) {
this.provider = provider;
this.wallet = wallet;
}

async executeBatchTransactions(params: typeof BatchTransactionSchema._type) {
const { transactions, gasLimit, maxFeePerGas, maxPriorityFeePerGas } = params;

// Prepare batch transaction
const batchTx = await Promise.all(
transactions.map(async (tx) => {
const nonce = await this.wallet.getTransactionCount();
return {
...tx,
nonce,
gasLimit: gasLimit || await this.estimateGas(tx),
maxFeePerGas,
maxPriorityFeePerGas,
};
})
);

// Execute batch transaction
const batchReceipt = await this.wallet.sendTransaction(batchTx[0]);
return batchReceipt;
}

async subscribeToContractEvents(params: typeof ContractEventSubscriptionSchema._type) {
const { contractAddress, eventName } = params;

const contract = new Contract(
contractAddress,
['event ' + eventName],
this.provider
);

return new Promise((resolve, reject) => {
contract.on(eventName, (...args: unknown[]) => {
resolve({ event: eventName, args });
});

// Set up error handling
contract.on('error', (error: Error) => {
reject(error);
});
});
}

async executeContractCall(params: typeof ContractCallSchema._type) {
const { contractAddress, abi, functionName, args, value, gasLimit } = params;

const contract = new Contract(contractAddress, abi, this.wallet);

// Prepare transaction options
const txOptions: Record<string, unknown> = {};
if (value) txOptions.value = ethers.utils.parseEther(value);
if (gasLimit) txOptions.gasLimit = ethers.BigNumber.from(gasLimit);

// Execute contract call
const tx = await contract[functionName](...(args || []), txOptions);
const receipt = await tx.wait();

return {
transactionHash: receipt.transactionHash,
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed.toString(),
};
}

async optimizeGas(params: typeof GasOptimizationSchema._type) {
const { strategy, maxGasPrice } = params;

const feeData = await this.provider.getFeeData();

let gasPrice: ethers.BigNumber;
switch (strategy) {
case 'fast':
gasPrice = feeData.maxFeePerGas || feeData.gasPrice || ethers.BigNumber.from(0);
break;
case 'medium':
gasPrice = feeData.maxPriorityFeePerGas || feeData.gasPrice || ethers.BigNumber.from(0);
break;
case 'slow':
gasPrice = feeData.gasPrice || ethers.BigNumber.from(0);
break;
}

if (maxGasPrice) {
const maxGas = ethers.utils.parseUnits(maxGasPrice, 'gwei');
if (gasPrice.gt(maxGas)) {
gasPrice = maxGas;
}
}

return {
gasPrice: gasPrice.toString(),
strategy,
};
}

async validateABI(params: typeof ABIValidationSchema._type) {
const { abi, bytecode, validateConstructor, validateFunctions, validateEvents } = params;

const validationResults = {
isValid: true,
errors: [] as string[],
warnings: [] as string[],
};

try {
// Validate ABI structure
if (!Array.isArray(abi)) {
validationResults.isValid = false;
validationResults.errors.push('ABI must be an array');
return validationResults;
}

// Validate constructor if bytecode is provided
if (validateConstructor && bytecode) {
const constructorAbi = abi.find(item => item.type === 'constructor');
if (!constructorAbi) {
validationResults.warnings.push('No constructor found in ABI');
}
}

// Validate functions
if (validateFunctions) {
const functions = abi.filter(item => item.type === 'function');
for (const func of functions) {
if (!func.name || !func.inputs || !func.outputs) {
validationResults.errors.push(`Invalid function definition: ${func.name || 'unnamed'}`);
}
}
}

// Validate events
if (validateEvents) {
const events = abi.filter(item => item.type === 'event');
for (const event of events) {
if (!event.name || !event.inputs) {
validationResults.errors.push(`Invalid event definition: ${event.name || 'unnamed'}`);
}
}
}

validationResults.isValid = validationResults.errors.length === 0;
return validationResults;
} catch (error: unknown) {
validationResults.isValid = false;
validationResults.errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
return validationResults;
}
}

private async estimateGas(tx: Record<string, unknown>): Promise<string> {
try {
const gasEstimate = await this.provider.estimateGas(tx);
return gasEstimate.toString();
} catch (error: unknown) {
throw new Error(`Gas estimation failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
57 changes: 57 additions & 0 deletions src/tools/advanced-contracts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Tool } from '../types';
import { AdvancedContractHandler } from './handlers';
import {
BatchTransactionSchema,
ContractEventSubscriptionSchema,
ContractCallSchema,
GasOptimizationSchema,
ABIValidationSchema
} from './schemas';

export const advancedContractTools: Tool[] = [
{
name: 'execute-batch-transactions',
description: 'Execute multiple transactions in a batch with optimized gas usage',
parameters: BatchTransactionSchema,
handler: async (params, context) => {
const handler = new AdvancedContractHandler(context.provider, context.wallet);
return await handler.executeBatchTransactions(params);
}
},
{
name: 'subscribe-contract-events',
description: 'Subscribe to contract events with filtering capabilities',
parameters: ContractEventSubscriptionSchema,
handler: async (params, context) => {
const handler = new AdvancedContractHandler(context.provider, context.wallet);
return await handler.subscribeToContractEvents(params);
}
},
{
name: 'execute-contract-call',
description: 'Execute a contract call with advanced options and gas optimization',
parameters: ContractCallSchema,
handler: async (params, context) => {
const handler = new AdvancedContractHandler(context.provider, context.wallet);
return await handler.executeContractCall(params);
}
},
{
name: 'optimize-gas',
description: 'Optimize gas usage based on different strategies',
parameters: GasOptimizationSchema,
handler: async (params, context) => {
const handler = new AdvancedContractHandler(context.provider, context.wallet);
return await handler.optimizeGas(params);
}
},
{
name: 'validate-abi',
description: 'Validate contract ABI with comprehensive checks',
parameters: ABIValidationSchema,
handler: async (params, context) => {
const handler = new AdvancedContractHandler(context.provider, context.wallet);
return await handler.validateABI(params);
}
}
];
Loading