-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
154 lines (131 loc) · 3.7 KB
/
Copy pathserver.js
File metadata and controls
154 lines (131 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const express = require('express');
const cors = require('cors');
const dotenv = require('dotenv');
const { compareEstimates } = require('./lib/compare');
const { simulateOnFork } = require('./lib/simulate');
const { ethers } = require('ethers'); // 新增
dotenv.config();
const app = express();
app.use(cors());
app.use(express.json({ limit: '1mb' }));
const PORT = process.env.PORT || 4000;
const DEFAULT_FROM = process.env.DEFAULT_FROM;
const CHAIN_CONFIG = [
{
name: 'Ethereum',
chainId: 1,
nativeSymbol: 'ETH',
rpcEnvKey: 'MAINNET_RPC',
},
{
name: 'Optimism',
chainId: 10,
nativeSymbol: 'ETH',
rpcEnvKey: 'OPTIMISM_RPC',
},
{
name: 'Arbitrum',
chainId: 42161,
nativeSymbol: 'ETH',
rpcEnvKey: 'ARBITRUM_RPC',
},
{
name: 'Base',
chainId: 8453,
nativeSymbol: 'ETH',
rpcEnvKey: 'BASE_RPC',
},
{
name: 'Polygon',
chainId: 137,
nativeSymbol: 'MATIC',
rpcEnvKey: 'POLYGON_RPC',
},
];
// 构建启用的链(有 RPC 才会启用)
function buildActiveChains() {
return CHAIN_CONFIG.map((chain) => {
const rpcUrl = process.env[chain.rpcEnvKey];
return rpcUrl
? {
...chain,
rpcUrl,
}
: null;
}).filter(Boolean);
}
// 健康检查
app.get('/health', (req, res) => {
res.json({ ok: true, chains: buildActiveChains().map((c) => c.name) });
});
/**
* 新增路由:查询单条链的 gas price
* 示例:
* http://localhost:4000/gas?chain=ethereum
*/
app.get('/gas', async (req, res) => {
try {
const { chain } = req.query;
if (!chain) {
return res.status(400).json({ error: '缺少 chain 参数,例如 ?chain=ethereum' });
}
const chains = buildActiveChains();
const target = chains.find(c => c.name.toLowerCase() === chain.toLowerCase());
if (!target) {
return res.status(404).json({ error: `链 ${chain} 未配置或不受支持` });
}
const provider = new ethers.JsonRpcProvider(target.rpcUrl);
const gasPrice = await provider.getGasPrice();
res.json({
chain: target.name,
gasPrice: gasPrice.toString(), // Wei
gasPriceGwei: ethers.formatUnits(gasPrice, 'gwei'), // Gwei
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
/**
* 比较不同链的 estimateGas
*/
app.post('/estimate', async (req, res) => {
try {
const { to, data, value = '0x0', from = DEFAULT_FROM, gasLimit } = req.body || {};
if (!to || !data) {
return res.status(400).json({ error: '`to` 和 `data` 为必填字段' });
}
const chains = buildActiveChains();
if (!chains.length) {
return res.status(400).json({ error: '未配置任何 RPC,请检查 .env' });
}
const tx = { to, data, value };
if (from) tx.from = from;
if (gasLimit) tx.gasLimit = gasLimit;
const estimates = await compareEstimates({ tx, chains });
res.json({ estimates });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
/**
* 本地 Hardhat fork 模拟
*/
app.post('/simulate', async (req, res) => {
try {
const { to, data, value = '0x0', from = DEFAULT_FROM, gasLimit } = req.body || {};
if (!to || !data) {
return res.status(400).json({ error: '`to` 和 `data` 为必填字段' });
}
const rpcUrl = process.env.HARDHAT_FORK_RPC || 'http://127.0.0.1:8545';
const tx = { to, data, value };
if (from) tx.from = from;
if (gasLimit) tx.gasLimit = gasLimit;
const result = await simulateOnFork({ tx, rpcUrl });
res.json(result);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.listen(PORT, () => {
console.log(`🚀 CrossChain Gas Optimizer API running on http://localhost:${PORT}`);
});