diff --git a/agents/gas-estimation-agent/README.md b/agents/gas-estimation-agent/README.md new file mode 100644 index 0000000..3ea9632 --- /dev/null +++ b/agents/gas-estimation-agent/README.md @@ -0,0 +1,93 @@ +# Gas Estimation Agent + +Multi-chain gas estimation agent for the PayPol protocol. Provides real-time gas price data for Ethereum, Polygon, Base, and Arbitrum networks. + +## Features + +- **Multi-chain support**: Ethereum, Polygon, Base, Arbitrum +- **Smart caching**: 30-second cache to reduce RPC calls +- **Three speed tiers**: slow, standard, fast estimates +- **Cost calculation**: Automatic gas cost calculation in ETH + +## API Endpoints + +### Health Check +``` +GET /health +``` + +### Agent Manifest +``` +GET /manifest +``` + +### Execute Gas Estimation +``` +POST /execute +Content-Type: application/json + +{ + "chain": "ethereum", + "gasLimit": 21000 +} +``` + +Response: +```json +{ + "chain": "ethereum", + "gasLimit": 21000, + "estimates": { + "slow": { + "gasPriceGwei": 25.5, + "gwei": 535500, + "eth": 0.0005355 + }, + "standard": { + "gasPriceGwei": 31.88, + "gwei": 669480, + "eth": 0.00066948 + }, + "fast": { + "gasPriceGwei": 38.25, + "gwei": 803250, + "eth": 0.00080325 + } + }, + "timestamp": "2026-02-27T07:30:00.000Z" +} +``` + +### Get Current Gas Price +``` +GET /gas/:chain +``` + +## Setup + +```bash +npm install +npm run build +npm start +``` + +## Development + +```bash +npm run dev +``` + +## Configuration + +Set the `PORT` environment variable to change the server port (default: 3000). + +## Supported Chains + +- ethereum (Ethereum Mainnet) +- polygon (Polygon PoS) +- base (Base L2) +- arbitrum (Arbitrum One) + +## License + +MIT diff --git a/agents/gas-estimation-agent/manifest.json b/agents/gas-estimation-agent/manifest.json new file mode 100644 index 0000000..8a9fee7 --- /dev/null +++ b/agents/gas-estimation-agent/manifest.json @@ -0,0 +1,17 @@ +{ + "name": "gas-estimation-agent", + "version": "1.0.0", + "description": "Multi-chain gas estimation for Ethereum, Polygon, Base, and Arbitrum", + "author": "KimberleyOCaseyfv", + "license": "MIT", + "entry": "dist/index.js", + "endpoints": { + "health": "/health", + "manifest": "/manifest", + "execute": "/execute" + }, + "capabilities": ["gas-estimation", "multi-chain", "analytics"], + "supportedChains": ["ethereum", "polygon", "base", "arbitrum"], + "category": "analytics", + "tags": ["gas", "fees", "ethereum", "polygon", "base", "arbitrum"] +} diff --git a/agents/gas-estimation-agent/package.json b/agents/gas-estimation-agent/package.json new file mode 100644 index 0000000..b3c5786 --- /dev/null +++ b/agents/gas-estimation-agent/package.json @@ -0,0 +1,23 @@ +{ + "name": "gas-estimation-agent", + "version": "1.0.0", + "description": "Multi-chain gas estimation agent for PayPol protocol", + "main": "dist/index.js", + "scripts": { + "build": "tsc", + "start": "node dist/index.js", + "dev": "ts-node src/index.ts", + "register": "paypol-sdk register --manifest manifest.json" + }, + "dependencies": { + "axios": "^1.6.0", + "express": "^4.18.2", + "ethers": "^6.9.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.10.0", + "typescript": "^5.3.0", + "ts-node": "^10.9.0" + } +} diff --git a/agents/gas-estimation-agent/src/index.ts b/agents/gas-estimation-agent/src/index.ts new file mode 100644 index 0000000..27f904c --- /dev/null +++ b/agents/gas-estimation-agent/src/index.ts @@ -0,0 +1,189 @@ +import express from 'express'; +import axios from 'axios'; + +const app = express(); +app.use(express.json()); + +// Chain configurations +const CHAINS: Record = { + ethereum: { + rpc: 'https://eth-mainnet.public.blastapi.io', + explorer: 'https://etherscan.io' + }, + polygon: { + rpc: 'https://polygon-rpc.com', + explorer: 'https://polygonscan.com' + }, + base: { + rpc: 'https://mainnet.base.org', + explorer: 'https://basescan.org' + }, + arbitrum: { + rpc: 'https://arb1.arbitrum.io/rpc', + explorer: 'https://arbiscan.io' + } +}; + +// Cache for gas prices +const gasCache: Record = {}; +const CACHE_TTL = 30000; // 30 seconds + +/** + * Fetch gas price from RPC + */ +async function fetchGasPrice(chain: string): Promise { + const config = CHAINS[chain.toLowerCase()]; + if (!config) { + throw new Error(`Unsupported chain: ${chain}`); + } + + try { + const response = await axios.post(config.rpc, { + jsonrpc: '2.0', + method: 'eth_gasPrice', + params: [], + id: 1 + }, { + timeout: 10000, + headers: { 'Content-Type': 'application/json' } + }); + + const gasPriceHex = response.data.result; + const gasPriceWei = parseInt(gasPriceHex, 16); + const gasPriceGwei = gasPriceWei / 1e9; + + return Math.round(gasPriceGwei * 100) / 100; + } catch (error) { + console.error(`Failed to fetch gas price for ${chain}:`, error); + throw new Error(`Gas price fetch failed for ${chain}`); + } +} + +/** + * Get gas price with caching + */ +async function getGasPrice(chain: string): Promise { + const cacheKey = chain.toLowerCase(); + const cached = gasCache[cacheKey]; + + if (cached && Date.now() - cached.timestamp < CACHE_TTL) { + return cached.price; + } + + const price = await fetchGasPrice(chain); + gasCache[cacheKey] = { price, timestamp: Date.now() }; + return price; +} + +/** + * Estimate gas cost for a transaction + */ +function estimateGasCost(gasPriceGwei: number, gasLimit: number): { + gwei: number; + eth: number; + usd: number | null; +} { + const costGwei = gasPriceGwei * gasLimit; + const costEth = costGwei / 1e9; + + // ETH price would be fetched from an oracle in production + // Using placeholder for now + const ethPriceUsd = null; + + return { + gwei: costGwei, + eth: Math.round(costEth * 1e6) / 1e6, + usd: ethPriceUsd ? Math.round(costEth * ethPriceUsd * 100) / 100 : null + }; +} + +// Health endpoint +app.get('/health', (req, res) => { + res.json({ + status: 'healthy', + timestamp: new Date().toISOString(), + supportedChains: Object.keys(CHAINS) + }); +}); + +// Manifest endpoint +app.get('/manifest', (req, res) => { + res.json({ + name: 'gas-estimation-agent', + version: '1.0.0', + description: 'Multi-chain gas estimation for Ethereum, Polygon, Base, and Arbitrum', + endpoints: { + health: '/health', + manifest: '/manifest', + execute: '/execute' + }, + capabilities: ['gas-estimation', 'multi-chain'], + supportedChains: Object.keys(CHAINS) + }); +}); + +// Execute endpoint +app.post('/execute', async (req, res) => { + try { + const { chain, gasLimit = 21000 } = req.body; + + if (!chain) { + return res.status(400).json({ + error: 'Missing required parameter: chain' + }); + } + + const gasPrice = await getGasPrice(chain); + const estimates = { + slow: { gasPrice: Math.round(gasPrice * 0.8 * 100) / 100 }, + standard: { gasPrice }, + fast: { gasPrice: Math.round(gasPrice * 1.2 * 100) / 100 } + }; + + const results: Record = {}; + + for (const [speed, data] of Object.entries(estimates)) { + results[speed] = { + gasPriceGwei: data.gasPrice, + ...estimateGasCost(data.gasPrice, gasLimit) + }; + } + + res.json({ + chain: chain.toLowerCase(), + gasLimit, + estimates: results, + timestamp: new Date().toISOString() + }); + } catch (error) { + console.error('Execute error:', error); + res.status(500).json({ + error: error instanceof Error ? error.message : 'Unknown error' + }); + } +}); + +// Get gas price for specific chain +app.get('/gas/:chain', async (req, res) => { + try { + const { chain } = req.params; + const gasPrice = await getGasPrice(chain); + + res.json({ + chain: chain.toLowerCase(), + gasPriceGwei: gasPrice, + timestamp: new Date().toISOString() + }); + } catch (error) { + res.status(400).json({ + error: error instanceof Error ? error.message : 'Unknown error' + }); + } +}); + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => { + console.log(`Gas Estimation Agent running on port ${PORT}`); + console.log(`Supported chains: ${Object.keys(CHAINS).join(', ')}`); +}); diff --git a/agents/gas-estimation-agent/tsconfig.json b/agents/gas-estimation-agent/tsconfig.json new file mode 100644 index 0000000..fa8ee32 --- /dev/null +++ b/agents/gas-estimation-agent/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +}