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
93 changes: 93 additions & 0 deletions agents/gas-estimation-agent/README.md
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions agents/gas-estimation-agent/manifest.json
Original file line number Diff line number Diff line change
@@ -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"]
}
23 changes: 23 additions & 0 deletions agents/gas-estimation-agent/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
189 changes: 189 additions & 0 deletions agents/gas-estimation-agent/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import express from 'express';
import axios from 'axios';

const app = express();
app.use(express.json());

// Chain configurations
const CHAINS: Record<string, { rpc: string; explorer: string }> = {
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<string, { price: number; timestamp: number }> = {};
const CACHE_TTL = 30000; // 30 seconds

/**
* Fetch gas price from RPC
*/
async function fetchGasPrice(chain: string): Promise<number> {
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<number> {
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<string, any> = {};

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(', ')}`);
});
16 changes: 16 additions & 0 deletions agents/gas-estimation-agent/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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"]
}