@vechain.energy/gas
Version:
calculate estimated gas usage for transactions
217 lines (216 loc) • 10.6 kB
JavaScript
"use strict";
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 });
const getNodeMock = jest.fn();
const postNodeMock = jest.fn();
const bentMock = jest.fn().mockImplementation((url, method) => {
if (method === 'GET') {
return getNodeMock;
}
return postNodeMock;
});
jest.mock('bent', () => bentMock, { virtual: true });
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const feeMarket_1 = __importDefault(require("./feeMarket"));
describe('feeMarket', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('with dynamic fee calculation', () => {
it('returns a BigNumber', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock successful base fee response
getNodeMock.mockResolvedValueOnce({
baseFeePerGas: '0x2540be400' // 10 gwei in hex
});
// Mock successful priority fee response
getNodeMock.mockResolvedValueOnce({
maxPriorityFeePerGas: '0x3b9aca00' // 1 gwei in hex
});
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy');
expect(fee).toBeInstanceOf(bignumber_js_1.default);
}));
it('calculates total fee as base fee + priority fee', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock base fee: 10 gwei
getNodeMock.mockResolvedValueOnce({
baseFeePerGas: '0x2540be400'
});
// Mock priority fee response (will be used for optimal calculation)
getNodeMock.mockResolvedValueOnce({
maxPriorityFeePerGas: '0x3b9aca00' // 1 gwei
});
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy');
// Base fee: 10 gwei = 10000000000 wei
// Optimal priority fee: 4.6% of 10 gwei = 0.46 gwei = 460000000 wei
// Total: 10000000000 + 460000000 = 10460000000 wei
expect(fee.toString()).toEqual('10460000000');
}));
it('uses custom priority fee when provided', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock base fee: 10 gwei
getNodeMock.mockResolvedValueOnce({
baseFeePerGas: '0x2540be400'
});
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy', {
maxPriorityFeePerGas: '2000000000' // 2 gwei
});
// Expected: 10 + 2 = 12 gwei = 12000000000 wei
expect(fee.toString()).toEqual('12000000000');
}));
it('applies maxFeePerGas cap when provided', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock base fee: 10 gwei
getNodeMock.mockResolvedValueOnce({
baseFeePerGas: '0x2540be400'
});
// Mock priority fee: 1 gwei
getNodeMock.mockResolvedValueOnce({
maxPriorityFeePerGas: '0x3b9aca00'
});
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy', {
maxFeePerGas: '8000000000' // 8 gwei cap
});
// Expected: min(10 + 1, 8) = 8 gwei = 8000000000 wei
expect(fee.toString()).toEqual('8000000000');
}));
it('falls back to legacy base gas price when baseFeePerGas not available', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock block response without baseFeePerGas
getNodeMock.mockResolvedValueOnce({
// No baseFeePerGas field
});
// Mock legacy base gas price response
postNodeMock.mockResolvedValueOnce([
{
data: '0x000000000000000000000000000000000000000000000000000009184e72a000', // 10 gwei
events: [],
transfers: [],
gasUsed: 591,
reverted: false,
vmError: ''
}
]);
// Mock priority fee response
getNodeMock.mockResolvedValueOnce({
maxPriorityFeePerGas: '0x3b9aca00' // 1 gwei
});
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy');
// Legacy base price: 0x000000000000000000000000000000000000000000000000000009184e72a000
// = 10000000000000 (10 gwei)
// Priority fee: 0x3b9aca00 = 1000000000 (1 gwei)
// Total: 10000000000000 + 1000000000 = 10001000000000
expect(fee.toString()).toEqual('10001000000000');
// Verify legacy call was made
expect(postNodeMock).toHaveBeenCalledWith('/accounts/*', {
clauses: [{
to: '0x0000000000000000000000000000506172616d73',
data: '0x8eaa6ac0000000000000000000000000000000000000626173652d6761732d7072696365',
value: '0'
}]
});
}));
it('calculates optimal priority fee using fee history', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock base fee: 1000 gwei
getNodeMock.mockResolvedValueOnce({
baseFeePerGas: '0xe8d4a51000' // 1000 gwei
});
// Mock fee history with equal rewards
getNodeMock.mockResolvedValueOnce({
oldestBlock: '0x1',
baseFeePerGas: ['0x1', '0x2', '0x3'],
gasUsedRatio: ['0.5', '0.6', '0.7'],
reward: [
['0x20', '0x20', '0x20'] // Equal rewards: 32 wei each
]
});
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy');
// 4.6% of 1000 gwei = 46 gwei
// 75th percentile = 32 wei
// Should use min(46, 32) = 32 wei
// Total: 1000 + 0.000000032 = 1000.000000032 gwei
expect(fee.toString()).toEqual('1000000000032');
}));
it('calculates optimal priority fee using average of 75th percentiles', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock base fee: 1000 gwei
getNodeMock.mockResolvedValueOnce({
baseFeePerGas: '0xe8d4a51000' // 1000 gwei
});
// Mock fee history with varying rewards
getNodeMock.mockResolvedValueOnce({
oldestBlock: '0x1',
baseFeePerGas: ['0x1', '0x2'],
gasUsedRatio: ['0.5', '0.6'],
reward: [
['0x10', '0x20', '0x30'], // Block 1: 75th percentile = 48 wei
['0x15', '0x25', '0x35'] // Block 2: 75th percentile = 53 wei
]
});
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy');
// Average of 75th percentiles: (48 + 53) / 2 = 50.5 wei
// 4.6% of 1000 gwei = 46 gwei
// Should use min(46, 50.5) = 46 gwei
// Total: 1000 + 0.000000046 = 1000.000000046 gwei
expect(fee.toString()).toEqual('1000000000053');
}));
it('falls back to suggested priority fee when fee history is empty', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock base fee: 1000 gwei
getNodeMock.mockResolvedValueOnce({
baseFeePerGas: '0xe8d4a51000' // 1000 gwei
});
// Mock empty fee history
getNodeMock.mockResolvedValueOnce({
oldestBlock: '0x1',
baseFeePerGas: [],
gasUsedRatio: [],
reward: []
});
// Mock suggested priority fee
getNodeMock.mockResolvedValueOnce({
maxPriorityFeePerGas: '0x14' // 20 wei
});
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy');
// 4.6% of 1000 gwei = 46 gwei
// Suggested priority fee = 20 wei
// Should use min(46, 20) = 20 wei
// Total: 1000 + 0.000000020 = 1000.000000020 gwei
expect(fee.toString()).toEqual('1000000000020');
}));
it('handles network errors gracefully with fallbacks', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock base fee error
getNodeMock.mockRejectedValueOnce(new Error('Network error'));
// Mock legacy base gas price response
postNodeMock.mockResolvedValueOnce([
{
data: '0x000000000000000000000000000000000000000000000000000009184e72a000', // 10 gwei
events: [],
transfers: [],
gasUsed: 591,
reverted: false,
vmError: ''
}
]);
// Mock priority fee error
getNodeMock.mockRejectedValueOnce(new Error('Network error'));
const fee = yield (0, feeMarket_1.default)('https://node.vechain.energy');
// Should use legacy base price + default priority fee (1 gwei)
// Legacy base price: 10000000000000 (10 gwei)
// Default priority fee: 1000000000 (1 gwei)
// Total: 10000000000000 + 1000000000 = 10001000000000
expect(fee.toString()).toEqual('10001000000000');
}));
});
describe('with Connex object', () => {
it('returns zero for Connex objects', () => __awaiter(void 0, void 0, void 0, function* () {
const mockConnex = {};
const fee = yield (0, feeMarket_1.default)(mockConnex);
expect(fee.toString()).toEqual('0');
}));
});
});