Skip to content

Commit 548bc1f

Browse files
antoinedcAntoine de Chevigné
and
Antoine de Chevigné
authored
Gas page (#401)
* gas page * gas page * add viem * gas page * improve tracker * fix gas page * backend tests * add tests * more tests * fix tests --------- Co-authored-by: Antoine de Chevigné <antoine@tryethernal.com>
1 parent 61f988a commit 548bc1f

31 files changed

+2605
-37
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"ace-mode-solidity": "^0.1.1",
4646
"axios": "^0.21.1",
4747
"axios-cache-interceptor": "1",
48-
"chart.js": "3.9.1",
48+
"chart.js": "^4.4.7",
4949
"chartjs-plugin-zoom": "^2.0.1",
5050
"chokidar": "^3.5.0",
5151
"codemirror": "^6.0.1",
@@ -67,7 +67,7 @@
6767
"util": "^0.12.5",
6868
"v-stripe-elements": "^1.2.0",
6969
"vue": "^3.5.1",
70-
"vue-chartjs": "4.1.2",
70+
"vue-chartjs": "^5.3.2",
7171
"vue-json-pretty": "^2.2.4",
7272
"vue-moment": "^4.1.0",
7373
"vue-router": "4",

run/api/gas.js

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
const express = require('express');
2+
const router = express.Router();
3+
const db = require('../lib/firebase');
4+
const workspaceAuthMiddleware = require('../middlewares/workspaceAuth');
5+
const { unmanagedError } = require('../lib/errors');
6+
7+
/*
8+
Return gas stats for the explorer
9+
10+
@returns {object} - The gas stats
11+
- blockNumber: The block number it was calculated on
12+
- averageBlockSize: The average block size in transactions
13+
- averageUtilization: The average quantity of gas used per block
14+
- averageBlockTime: The average block time in seconds
15+
- latestBlockNumber: The number of the latest block used for this calculation
16+
- baseFeePerGas: The base fee per gas for the latest block
17+
- priorityFeePerGas: The three levels of priority fee per gas for the latest block (slow, average, fast)
18+
*/
19+
router.get('/stats', workspaceAuthMiddleware, async (req, res, next) => {
20+
const data = req.query;
21+
22+
try {
23+
const result = await db.getLatestGasStats(data.workspace.id, data.intervalInMinutes);
24+
25+
res.status(200).json(result);
26+
} catch(error) {
27+
unmanagedError(error, req, next);
28+
}
29+
});
30+
31+
/*
32+
Return gas price history for the explorer
33+
34+
@returns {array} - The gas price history
35+
*/
36+
router.get('/priceHistory', workspaceAuthMiddleware, async (req, res, next) => {
37+
const data = req.query;
38+
39+
try {
40+
const result = await db.getGasPriceHistory(data.workspace.id, data.from, data.to);
41+
42+
res.status(200).json(result);
43+
} catch(error) {
44+
unmanagedError(error, req, next);
45+
}
46+
});
47+
48+
/*
49+
Return gas limit history for the explorer
50+
51+
@returns {array} - The gas limit history
52+
- day: The day of the gas limit history
53+
- gasLimit: The average gas limit for the day
54+
*/
55+
router.get('/limitHistory', workspaceAuthMiddleware, async (req, res, next) => {
56+
const data = req.query;
57+
58+
try {
59+
const result = await db.getGasLimitHistory(data.workspace.id, data.from, data.to);
60+
61+
res.status(200).json(result);
62+
} catch(error) {
63+
unmanagedError(error, req, next);
64+
}
65+
});
66+
67+
/*
68+
Return gas utilization ratio history for the explorer
69+
70+
@returns {array} - The gas utilization ratio history
71+
- day: The day of the gas utilization ratio history
72+
- gasUtilizationRatio: The average gas utilization ratio for the day
73+
*/
74+
router.get('/utilizationRatioHistory', workspaceAuthMiddleware, async (req, res, next) => {
75+
const data = req.query;
76+
77+
try {
78+
const result = await db.getGasUtilizationRatioHistory(data.workspace.id, data.from, data.to);
79+
80+
res.status(200).json(result);
81+
} catch(error) {
82+
unmanagedError(error, req, next);
83+
}
84+
});
85+
86+
/*
87+
Return the latest biggest gas consumers for the explorer:
88+
89+
@returns {array} - The latest biggest gas consumers
90+
- to: The address of the gas consumer
91+
- gasUsed: The total gas used by the gas consumer
92+
- gasCost: Cost of total gas used
93+
*/
94+
router.get('/consumers', workspaceAuthMiddleware, async (req, res, next) => {
95+
const data = req.query;
96+
97+
try {
98+
const result = await db.getLatestGasConsumers(data.workspace.id, data.intervalInHours, data.limit);
99+
100+
res.status(200).json(result);
101+
} catch(error) {
102+
unmanagedError(error, req, next);
103+
}
104+
});
105+
106+
/*
107+
Return the latest biggest gas spenders for the explorer:
108+
109+
@returns {array} - The latest biggest gas spenders
110+
- from: The address of the gas spender
111+
- gasUsed: The total gas used by the gas spender
112+
- gasCost: Cost of total gas used
113+
- percentUsed: The percentage of total gas used by the gas spender
114+
*/
115+
router.get('/spenders', workspaceAuthMiddleware, async (req, res, next) => {
116+
const data = req.query;
117+
118+
try {
119+
const result = await db.getLatestGasSpenders(data.workspace.id, data.intervalInHours, data.limit);
120+
121+
res.status(200).json(result);
122+
} catch(error) {
123+
unmanagedError(error, req, next);
124+
}
125+
});
126+
127+
module.exports = router;

run/api/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const external = require('./external');
2121
const domains = require('./domains');
2222
const faucets = require('./faucets');
2323
const v2Dexes = require('./v2Dexes');
24+
const gas = require('./gas');
2425

2526
router.use('/blocks', blocks);
2627
router.use('/contracts', contracts);
@@ -41,6 +42,7 @@ router.use('/external', external);
4142
router.use('/domains', domains);
4243
router.use('/faucets', faucets);
4344
router.use('/v2_dexes', v2Dexes);
45+
router.use('/gas', gas);
4446

4547
if (isDemoEnabled()) {
4648
const demo = require('./demo');

run/lib/firebase.js

+157-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
/*
2+
This file contains all the methods to interact with
3+
models from the API. They check for parameters,
4+
make sure the required resources exist and can
5+
be accessed by the user.
6+
APIs should use methods from this file and not interact
7+
with the models directly.
8+
Background jobs do not need to use methods here and
9+
can interact with the models directly, as they are not
10+
exposed to the user.
11+
*/
112
const Sequelize = require('sequelize');
213
const { getDemoUserId, getMaxBlockForSyncReset } = require('./env');
314
const models = require('../models');
@@ -21,6 +32,145 @@ const ExplorerFaucet = models.ExplorerFaucet;
2132
const ExplorerV2Dex = models.ExplorerV2Dex;
2233
const V2DexPair = models.V2DexPair;
2334

35+
/*
36+
This method is used to get the latest biggest gas spenders for a workspace
37+
for a given interval (now - intervalInHours).
38+
39+
@param {number} workspaceId - The ID of the workspace
40+
@param {number} intervalInHours - The interval in hours to get the gas spenders for
41+
@param {number} limit - The limit of gas spenders to return
42+
@returns {array} - The gas spenders
43+
- from: The address of the gas spender
44+
- gasUsed: The total gas used by the gas spender
45+
- gasCost: Cost of total gas used
46+
- percentUsed: The percentage of total gas used by the gas spender
47+
*/
48+
const getLatestGasSpenders = async (workspaceId, intervalInHours = 24, limit = 50) => {
49+
if (!workspaceId)
50+
throw new Error('Missing parameter');
51+
52+
const workspace = await Workspace.findByPk(workspaceId);
53+
if (!workspace)
54+
throw new Error('Could not find workspace');
55+
56+
return workspace.getLatestGasSpenders(intervalInHours, limit);
57+
};
58+
59+
/*
60+
This method is used to get the latest biggest gas consumers for a workspace
61+
for a given interval (now - intervalInHours).
62+
63+
@param {number} workspaceId - The ID of the workspace
64+
@param {number} intervalInHours - The interval in hours to get the gas consumers for
65+
@param {number} limit - The limit of gas consumers to return
66+
@returns {array} - The gas consumers
67+
- to: The address of the gas consumer
68+
- gasUsed: The total gas used by the gas consumer
69+
- gasCost: Cost of total gas used
70+
*/
71+
const getLatestGasConsumers = async (workspaceId, intervalInHours = 24, limit = 50) => {
72+
if (!workspaceId)
73+
throw new Error('Missing parameter');
74+
75+
const workspace = await Workspace.findByPk(workspaceId);
76+
if (!workspace)
77+
throw new Error('Could not find workspace');
78+
79+
return workspace.getLatestGasConsumers(intervalInHours, limit);
80+
};
81+
82+
/*
83+
This method is used to get the gas utilization ratio history for a workspace.
84+
85+
@param {number} workspaceId - The ID of the workspace
86+
@param {string} from - The start date of the gas utilization ratio history
87+
@param {string} to - The end date of the gas utilization ratio history
88+
@returns {array} - The gas utilization ratio history
89+
- day: The day of the gas utilization ratio history
90+
- gasUtilizationRatio: The average gas utilization ratio for the day
91+
*/
92+
const getGasUtilizationRatioHistory = async (workspaceId, from, to) => {
93+
if (!workspaceId || !from || !to)
94+
throw new Error('Missing parameter');
95+
96+
const workspace = await Workspace.findByPk(workspaceId);
97+
if (!workspace)
98+
throw new Error('Could not find workspace');
99+
100+
return workspace.getGasUtilizationRatioHistory(from, to);
101+
};
102+
103+
/*
104+
This method is used to get the gas limit history for a workspace.
105+
106+
@param {number} workspaceId - The ID of the workspace
107+
@param {string} from - The start date of the gas limit history
108+
@param {string} to - The end date of the gas limit history
109+
@returns {array} - The gas limit history
110+
- day: The day of the gas limit history
111+
- gasLimit: The average gas limit for the day
112+
*/
113+
const getGasLimitHistory = async (workspaceId, from, to) => {
114+
if (!workspaceId || !from || !to)
115+
throw new Error('Missing parameter');
116+
117+
const workspace = await Workspace.findByPk(workspaceId);
118+
if (!workspace)
119+
throw new Error('Could not find workspace');
120+
121+
return workspace.getGasLimitHistory(from, to);
122+
};
123+
124+
/*
125+
This method is used to get the gas price history for a workspace.
126+
127+
@param {number} workspaceId - The ID of the workspace
128+
@param {string} from - The start date of the gas price history
129+
@param {string} to - The end date of the gas price history
130+
@returns {array} - The gas price history
131+
- day: The day of the gas price history
132+
- minSlow: The minimum slow gas price
133+
- slow: The average slow gas price
134+
- maxSlow: The maximum slow gas price
135+
- minAverage: The minimum average gas price
136+
- average: The average average gas price
137+
- maxAverage: The maximum average gas price
138+
- minFast: The minimum fast gas price
139+
- fast: The average fast gas price
140+
- maxFast: The maximum fast gas price
141+
*/
142+
const getGasPriceHistory = async (workspaceId, from, to) => {
143+
if (!workspaceId || !from || !to)
144+
throw new Error('Missing parameter');
145+
146+
const workspace = await Workspace.findByPk(workspaceId);
147+
if (!workspace)
148+
throw new Error('Could not find workspace');
149+
150+
return workspace.getGasPriceHistory(from, to);
151+
};
152+
153+
/*
154+
This method is used to get the latest gas stats for a workspace.
155+
156+
@param {number} workspaceId - The ID of the workspace
157+
@param {number} intervalInMinutes - The interval in minutes to get the gas stats for
158+
@returns {object} - The gas stats object
159+
- averageBlockSize: The average block size in transactions
160+
- averageUtilization: The average quantity of gas used per block
161+
- averageBlockTime: The average block time in seconds
162+
- latestBlockNumber: The number of the latest block used for this calculation
163+
- baseFeePerGas: The base fee per gas for the latest block
164+
- priorityFeePerGas: The three levels of priority fee per gas for the latest block (slow, average, fast)
165+
*/
166+
const getLatestGasStats = async (workspaceId, intervalInMinutes = 1) => {
167+
const workspace = await Workspace.findByPk(workspaceId);
168+
if (!workspace)
169+
throw new Error('Could not find workspace');
170+
171+
return workspace.getLatestGasStats(intervalInMinutes);
172+
};
173+
24174
const createUserStripeSubscription = (userId, stripeSubscription, stripePlan) => {
25175
if (!userId || !stripeSubscription || !stripePlan)
26176
throw new Error('Missing parameter');
@@ -2495,5 +2645,11 @@ module.exports = {
24952645
deleteV2Dex: deleteV2Dex,
24962646
getV2DexPairCount: getV2DexPairCount,
24972647
getUserStripeSubscription: getUserStripeSubscription,
2498-
createUserStripeSubscription: createUserStripeSubscription
2648+
createUserStripeSubscription: createUserStripeSubscription,
2649+
getLatestGasStats: getLatestGasStats,
2650+
getGasPriceHistory: getGasPriceHistory,
2651+
getGasLimitHistory: getGasLimitHistory,
2652+
getGasUtilizationRatioHistory: getGasUtilizationRatioHistory,
2653+
getLatestGasConsumers: getLatestGasConsumers,
2654+
getLatestGasSpenders: getLatestGasSpenders
24992655
};

run/lib/utils.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ const ethers = require('ethers');
22

33
const DEFAULT_PROMISE_TIMEOUT = 10 * 1000;
44

5+
const avg = (arr) => {
6+
const sum = arr.reduce((a, v) => a + v);
7+
return Math.round(sum/arr.length);
8+
};
9+
10+
511
const sleep = (ms) => {
612
return new Promise((r) => setTimeout(r, ms))
713
};
@@ -181,5 +187,6 @@ module.exports = {
181187
processRawRpcObject,
182188
formatErc721Metadata,
183189
validateBNString,
184-
sleep
190+
sleep,
191+
avg
185192
};

0 commit comments

Comments
 (0)