Skip to content

Commit

Permalink
Gas page (#401)
Browse files Browse the repository at this point in the history
* 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é <[email protected]>
  • Loading branch information
antoinedc and Antoine de Chevigné authored Feb 18, 2025
1 parent 61f988a commit 548bc1f
Show file tree
Hide file tree
Showing 31 changed files with 2,605 additions and 37 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"ace-mode-solidity": "^0.1.1",
"axios": "^0.21.1",
"axios-cache-interceptor": "1",
"chart.js": "3.9.1",
"chart.js": "^4.4.7",
"chartjs-plugin-zoom": "^2.0.1",
"chokidar": "^3.5.0",
"codemirror": "^6.0.1",
Expand All @@ -67,7 +67,7 @@
"util": "^0.12.5",
"v-stripe-elements": "^1.2.0",
"vue": "^3.5.1",
"vue-chartjs": "4.1.2",
"vue-chartjs": "^5.3.2",
"vue-json-pretty": "^2.2.4",
"vue-moment": "^4.1.0",
"vue-router": "4",
Expand Down
127 changes: 127 additions & 0 deletions run/api/gas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
const express = require('express');
const router = express.Router();
const db = require('../lib/firebase');
const workspaceAuthMiddleware = require('../middlewares/workspaceAuth');
const { unmanagedError } = require('../lib/errors');

/*
Return gas stats for the explorer
@returns {object} - The gas stats
- blockNumber: The block number it was calculated on
- averageBlockSize: The average block size in transactions
- averageUtilization: The average quantity of gas used per block
- averageBlockTime: The average block time in seconds
- latestBlockNumber: The number of the latest block used for this calculation
- baseFeePerGas: The base fee per gas for the latest block
- priorityFeePerGas: The three levels of priority fee per gas for the latest block (slow, average, fast)
*/
router.get('/stats', workspaceAuthMiddleware, async (req, res, next) => {
const data = req.query;

try {
const result = await db.getLatestGasStats(data.workspace.id, data.intervalInMinutes);

res.status(200).json(result);
} catch(error) {
unmanagedError(error, req, next);
}
});

/*
Return gas price history for the explorer
@returns {array} - The gas price history
*/
router.get('/priceHistory', workspaceAuthMiddleware, async (req, res, next) => {
const data = req.query;

try {
const result = await db.getGasPriceHistory(data.workspace.id, data.from, data.to);

res.status(200).json(result);
} catch(error) {
unmanagedError(error, req, next);
}
});

/*
Return gas limit history for the explorer
@returns {array} - The gas limit history
- day: The day of the gas limit history
- gasLimit: The average gas limit for the day
*/
router.get('/limitHistory', workspaceAuthMiddleware, async (req, res, next) => {
const data = req.query;

try {
const result = await db.getGasLimitHistory(data.workspace.id, data.from, data.to);

res.status(200).json(result);
} catch(error) {
unmanagedError(error, req, next);
}
});

/*
Return gas utilization ratio history for the explorer
@returns {array} - The gas utilization ratio history
- day: The day of the gas utilization ratio history
- gasUtilizationRatio: The average gas utilization ratio for the day
*/
router.get('/utilizationRatioHistory', workspaceAuthMiddleware, async (req, res, next) => {
const data = req.query;

try {
const result = await db.getGasUtilizationRatioHistory(data.workspace.id, data.from, data.to);

res.status(200).json(result);
} catch(error) {
unmanagedError(error, req, next);
}
});

/*
Return the latest biggest gas consumers for the explorer:
@returns {array} - The latest biggest gas consumers
- to: The address of the gas consumer
- gasUsed: The total gas used by the gas consumer
- gasCost: Cost of total gas used
*/
router.get('/consumers', workspaceAuthMiddleware, async (req, res, next) => {
const data = req.query;

try {
const result = await db.getLatestGasConsumers(data.workspace.id, data.intervalInHours, data.limit);

res.status(200).json(result);
} catch(error) {
unmanagedError(error, req, next);
}
});

/*
Return the latest biggest gas spenders for the explorer:
@returns {array} - The latest biggest gas spenders
- from: The address of the gas spender
- gasUsed: The total gas used by the gas spender
- gasCost: Cost of total gas used
- percentUsed: The percentage of total gas used by the gas spender
*/
router.get('/spenders', workspaceAuthMiddleware, async (req, res, next) => {
const data = req.query;

try {
const result = await db.getLatestGasSpenders(data.workspace.id, data.intervalInHours, data.limit);

res.status(200).json(result);
} catch(error) {
unmanagedError(error, req, next);
}
});

module.exports = router;
2 changes: 2 additions & 0 deletions run/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const external = require('./external');
const domains = require('./domains');
const faucets = require('./faucets');
const v2Dexes = require('./v2Dexes');
const gas = require('./gas');

router.use('/blocks', blocks);
router.use('/contracts', contracts);
Expand All @@ -41,6 +42,7 @@ router.use('/external', external);
router.use('/domains', domains);
router.use('/faucets', faucets);
router.use('/v2_dexes', v2Dexes);
router.use('/gas', gas);

if (isDemoEnabled()) {
const demo = require('./demo');
Expand Down
158 changes: 157 additions & 1 deletion run/lib/firebase.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/*
This file contains all the methods to interact with
models from the API. They check for parameters,
make sure the required resources exist and can
be accessed by the user.
APIs should use methods from this file and not interact
with the models directly.
Background jobs do not need to use methods here and
can interact with the models directly, as they are not
exposed to the user.
*/
const Sequelize = require('sequelize');
const { getDemoUserId, getMaxBlockForSyncReset } = require('./env');
const models = require('../models');
Expand All @@ -21,6 +32,145 @@ const ExplorerFaucet = models.ExplorerFaucet;
const ExplorerV2Dex = models.ExplorerV2Dex;
const V2DexPair = models.V2DexPair;

/*
This method is used to get the latest biggest gas spenders for a workspace
for a given interval (now - intervalInHours).
@param {number} workspaceId - The ID of the workspace
@param {number} intervalInHours - The interval in hours to get the gas spenders for
@param {number} limit - The limit of gas spenders to return
@returns {array} - The gas spenders
- from: The address of the gas spender
- gasUsed: The total gas used by the gas spender
- gasCost: Cost of total gas used
- percentUsed: The percentage of total gas used by the gas spender
*/
const getLatestGasSpenders = async (workspaceId, intervalInHours = 24, limit = 50) => {
if (!workspaceId)
throw new Error('Missing parameter');

const workspace = await Workspace.findByPk(workspaceId);
if (!workspace)
throw new Error('Could not find workspace');

return workspace.getLatestGasSpenders(intervalInHours, limit);
};

/*
This method is used to get the latest biggest gas consumers for a workspace
for a given interval (now - intervalInHours).
@param {number} workspaceId - The ID of the workspace
@param {number} intervalInHours - The interval in hours to get the gas consumers for
@param {number} limit - The limit of gas consumers to return
@returns {array} - The gas consumers
- to: The address of the gas consumer
- gasUsed: The total gas used by the gas consumer
- gasCost: Cost of total gas used
*/
const getLatestGasConsumers = async (workspaceId, intervalInHours = 24, limit = 50) => {
if (!workspaceId)
throw new Error('Missing parameter');

const workspace = await Workspace.findByPk(workspaceId);
if (!workspace)
throw new Error('Could not find workspace');

return workspace.getLatestGasConsumers(intervalInHours, limit);
};

/*
This method is used to get the gas utilization ratio history for a workspace.
@param {number} workspaceId - The ID of the workspace
@param {string} from - The start date of the gas utilization ratio history
@param {string} to - The end date of the gas utilization ratio history
@returns {array} - The gas utilization ratio history
- day: The day of the gas utilization ratio history
- gasUtilizationRatio: The average gas utilization ratio for the day
*/
const getGasUtilizationRatioHistory = async (workspaceId, from, to) => {
if (!workspaceId || !from || !to)
throw new Error('Missing parameter');

const workspace = await Workspace.findByPk(workspaceId);
if (!workspace)
throw new Error('Could not find workspace');

return workspace.getGasUtilizationRatioHistory(from, to);
};

/*
This method is used to get the gas limit history for a workspace.
@param {number} workspaceId - The ID of the workspace
@param {string} from - The start date of the gas limit history
@param {string} to - The end date of the gas limit history
@returns {array} - The gas limit history
- day: The day of the gas limit history
- gasLimit: The average gas limit for the day
*/
const getGasLimitHistory = async (workspaceId, from, to) => {
if (!workspaceId || !from || !to)
throw new Error('Missing parameter');

const workspace = await Workspace.findByPk(workspaceId);
if (!workspace)
throw new Error('Could not find workspace');

return workspace.getGasLimitHistory(from, to);
};

/*
This method is used to get the gas price history for a workspace.
@param {number} workspaceId - The ID of the workspace
@param {string} from - The start date of the gas price history
@param {string} to - The end date of the gas price history
@returns {array} - The gas price history
- day: The day of the gas price history
- minSlow: The minimum slow gas price
- slow: The average slow gas price
- maxSlow: The maximum slow gas price
- minAverage: The minimum average gas price
- average: The average average gas price
- maxAverage: The maximum average gas price
- minFast: The minimum fast gas price
- fast: The average fast gas price
- maxFast: The maximum fast gas price
*/
const getGasPriceHistory = async (workspaceId, from, to) => {
if (!workspaceId || !from || !to)
throw new Error('Missing parameter');

const workspace = await Workspace.findByPk(workspaceId);
if (!workspace)
throw new Error('Could not find workspace');

return workspace.getGasPriceHistory(from, to);
};

/*
This method is used to get the latest gas stats for a workspace.
@param {number} workspaceId - The ID of the workspace
@param {number} intervalInMinutes - The interval in minutes to get the gas stats for
@returns {object} - The gas stats object
- averageBlockSize: The average block size in transactions
- averageUtilization: The average quantity of gas used per block
- averageBlockTime: The average block time in seconds
- latestBlockNumber: The number of the latest block used for this calculation
- baseFeePerGas: The base fee per gas for the latest block
- priorityFeePerGas: The three levels of priority fee per gas for the latest block (slow, average, fast)
*/
const getLatestGasStats = async (workspaceId, intervalInMinutes = 1) => {
const workspace = await Workspace.findByPk(workspaceId);
if (!workspace)
throw new Error('Could not find workspace');

return workspace.getLatestGasStats(intervalInMinutes);
};

const createUserStripeSubscription = (userId, stripeSubscription, stripePlan) => {
if (!userId || !stripeSubscription || !stripePlan)
throw new Error('Missing parameter');
Expand Down Expand Up @@ -2495,5 +2645,11 @@ module.exports = {
deleteV2Dex: deleteV2Dex,
getV2DexPairCount: getV2DexPairCount,
getUserStripeSubscription: getUserStripeSubscription,
createUserStripeSubscription: createUserStripeSubscription
createUserStripeSubscription: createUserStripeSubscription,
getLatestGasStats: getLatestGasStats,
getGasPriceHistory: getGasPriceHistory,
getGasLimitHistory: getGasLimitHistory,
getGasUtilizationRatioHistory: getGasUtilizationRatioHistory,
getLatestGasConsumers: getLatestGasConsumers,
getLatestGasSpenders: getLatestGasSpenders
};
9 changes: 8 additions & 1 deletion run/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ const ethers = require('ethers');

const DEFAULT_PROMISE_TIMEOUT = 10 * 1000;

const avg = (arr) => {
const sum = arr.reduce((a, v) => a + v);
return Math.round(sum/arr.length);
};


const sleep = (ms) => {
return new Promise((r) => setTimeout(r, ms))
};
Expand Down Expand Up @@ -181,5 +187,6 @@ module.exports = {
processRawRpcObject,
formatErc721Metadata,
validateBNString,
sleep
sleep,
avg
};
Loading

0 comments on commit 548bc1f

Please sign in to comment.