@vechain.energy/gas
Version:
calculate estimated gas usage for transactions
163 lines (162 loc) • 7.33 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = feeMarket;
const bent_1 = __importDefault(require("bent"));
const bignumber_js_1 = __importDefault(require("bignumber.js"));
function feeMarket(nodeOrConnex_1) {
return __awaiter(this, arguments, void 0, function* (nodeOrConnex, options = {}) {
if (typeof nodeOrConnex === "string") {
return yield calculateDynamicFee(nodeOrConnex, options);
}
// For Connex, fall back to legacy calculation
return new bignumber_js_1.default(0);
});
}
function calculateDynamicFee(nodeUrl, options) {
return __awaiter(this, void 0, void 0, function* () {
// Get current base fee from latest block
const baseFee = yield getBaseFeePerGas(nodeUrl);
// Calculate priority fee
let priorityFee;
if (options.maxPriorityFeePerGas !== undefined) {
priorityFee = new bignumber_js_1.default(options.maxPriorityFeePerGas);
}
else {
priorityFee = yield calculateOptimalPriorityFee(nodeUrl, baseFee);
}
const totalFeePerGas = baseFee.plus(priorityFee);
// Apply maxFeePerGas cap if specified
if (options.maxFeePerGas !== undefined) {
const maxFee = new bignumber_js_1.default(options.maxFeePerGas);
return bignumber_js_1.default.min(totalFeePerGas, maxFee);
}
return totalFeePerGas;
});
}
function getBaseFeePerGas(nodeUrl) {
return __awaiter(this, void 0, void 0, function* () {
const getNode = (0, bent_1.default)(nodeUrl, 'GET', 'json', 200);
try {
const response = yield getNode('/blocks/best');
if (response && response.baseFeePerGas) {
return new bignumber_js_1.default(response.baseFeePerGas);
}
// Fallback to legacy base gas price if baseFeePerGas not available
return yield getLegacyBaseGasPrice(nodeUrl);
}
catch (error) {
// Fallback to legacy base gas price
return yield getLegacyBaseGasPrice(nodeUrl);
}
});
}
function getLegacyBaseGasPrice(nodeUrl) {
return __awaiter(this, void 0, void 0, function* () {
const postNode = (0, bent_1.default)(nodeUrl, 'POST', 'json', 200);
const response = yield postNode('/accounts/*', {
clauses: [{
to: '0x0000000000000000000000000000506172616d73',
data: '0x8eaa6ac0000000000000000000000000000000000000626173652d6761732d7072696365',
value: '0'
}]
});
if (!Array.isArray(response)) {
return new bignumber_js_1.default(0);
}
const data = response[0].data;
// Simple hex to decimal conversion for uint256
const decoded = new bignumber_js_1.default(data.slice(2), 16);
return decoded;
});
}
function calculateOptimalPriorityFee(nodeUrl, baseFee) {
return __awaiter(this, void 0, void 0, function* () {
try {
// Get fee history for recent blocks
const feeHistory = yield getFeeHistory(nodeUrl, {
blockCount: 10,
newestBlock: 'best',
rewardPercentiles: [25, 50, 75]
});
let percentile75 = new bignumber_js_1.default(0);
if (feeHistory.reward && feeHistory.reward.length > 0) {
const latestBlockRewards = feeHistory.reward[feeHistory.reward.length - 1];
const equalRewardsOnLastBlock = new Set(latestBlockRewards).size === 3;
if (equalRewardsOnLastBlock) {
// Use 75th percentile from latest block
percentile75 = new bignumber_js_1.default(latestBlockRewards[2], 16);
}
else {
// Calculate average of 75th percentiles across blocks
let sum = new bignumber_js_1.default(0);
let count = 0;
for (const blockRewards of feeHistory.reward) {
if (blockRewards.length > 2 && blockRewards[2]) {
sum = sum.plus(new bignumber_js_1.default(blockRewards[2], 16));
count++;
}
}
percentile75 = count > 0 ? sum.dividedBy(count) : new bignumber_js_1.default(0);
}
}
else {
// Fallback to network-suggested priority fee
percentile75 = yield getMaxPriorityFeePerGas(nodeUrl);
}
// Calculate 4.6% of base fee (HIGH speed threshold)
const baseFeeCap = baseFee.times(46).dividedBy(1000);
// Use minimum of historical vs. cap values
return bignumber_js_1.default.min(baseFeeCap, percentile75);
}
catch (error) {
// Fallback to network-suggested priority fee
return yield getMaxPriorityFeePerGas(nodeUrl);
}
});
}
function getFeeHistory(nodeUrl, options) {
return __awaiter(this, void 0, void 0, function* () {
const getNode = (0, bent_1.default)(nodeUrl, 'GET', 'json', 200);
const queryParams = new URLSearchParams({
blockCount: options.blockCount.toString(),
newestBlock: options.newestBlock
});
if (options.rewardPercentiles) {
queryParams.append('rewardPercentiles', options.rewardPercentiles.join(','));
}
const response = yield getNode(`/fees/history?${queryParams.toString()}`);
if (!response || typeof response !== 'object') {
throw new Error('Invalid fee history response');
}
return response;
});
}
function getMaxPriorityFeePerGas(nodeUrl) {
return __awaiter(this, void 0, void 0, function* () {
const getNode = (0, bent_1.default)(nodeUrl, 'GET', 'json', 200);
try {
const response = yield getNode('/fees/priority');
if (response && response.maxPriorityFeePerGas) {
return new bignumber_js_1.default(response.maxPriorityFeePerGas, 16);
}
// Fallback to a reasonable default
return new bignumber_js_1.default(1000000000); // 1 gwei
}
catch (error) {
// Fallback to a reasonable default
return new bignumber_js_1.default(1000000000); // 1 gwei
}
});
}