@substrate/api-sidecar
Version:
REST service that makes it easy to interact with blockchain nodes built using Substrate's FRAME framework.
167 lines • 7.37 kB
JavaScript
;
// Copyright 2017-2025 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeTransactionPoolService = void 0;
const bn_js_1 = __importDefault(require("bn.js"));
const AbstractService_1 = require("../AbstractService");
class NodeTransactionPoolService extends AbstractService_1.AbstractService {
/**
* Fetch the transaction pool, and provide relevant extrinsic information.
*
* @param includeFee Whether or not to include the fee's and priority of a extrinsic
* in the transaction pool.
*/
async fetchTransactionPool(includeFee) {
const { api } = this;
const extrinsics = await api.rpc.author.pendingExtrinsics();
if (includeFee) {
const pool = await Promise.all(extrinsics.map((ext) => this.extractExtrinsicInfo(ext)));
return {
pool,
};
}
else {
return {
pool: extrinsics.map((ext) => {
return {
hash: ext.hash.toHex(),
encodedExtrinsic: ext.toHex(),
};
}),
};
}
}
/**
* Extract all information related to the extrinsic, and compute it's
* priority in the transaction pool.
*
* @param ext Extrinsic we want to provide all the information for.
*/
async extractExtrinsicInfo(ext) {
const { api } = this;
const { hash, tip } = ext;
const u8a = ext.toU8a();
const { class: c, partialFee, weight } = await api.call.transactionPaymentApi.queryInfo(u8a, u8a.length);
const priority = await this.computeExtPriority(ext, c, weight);
return {
hash: hash.toHex(),
encodedExtrinsic: ext.toHex(),
tip: tip.toString(),
priority: priority,
partialFee: partialFee,
};
}
/**
* We calculate the priority of an extrinsic in the transaction pool depending
* on its dispatch class, ie. 'normal', 'operational', 'mandatory'.
*
* The following formula can summarize the below logic.
* tip * (max_block_{weight|length} / bounded_{weight|length})
*
* Please reference this link for more information
* ref: https://github.com/paritytech/substrate/blob/fe5bf49290d166b9552f65e751d46ec592173ebd/frame/transaction-payment/src/lib.rs#L610
*
* @param ext
* @param c
* @param weight
*/
async computeExtPriority(ext, dispatchClass, weight) {
const { api } = this;
const { tip, encodedLength: len } = ext;
const BN_ONE = new bn_js_1.default(1);
const sanitizedClass = this.defineDispatchClassType(dispatchClass);
// Check which versions of Weight we are using by checking to see if refTime exists.
const versionedWeight = (weight === null || weight === void 0 ? void 0 : weight.refTime) != undefined
? weight.refTime.unwrap().toBn()
: weight.toBn();
const maxBlockWeight = api.consts.system.blockWeights.maxBlock.refTime
? api.consts.system.blockWeights.maxBlock.refTime.unwrap()
: api.consts.system.blockWeights.maxBlock;
const maxLength = new bn_js_1.default(api.consts.system.blockLength.max[sanitizedClass]);
const boundedWeight = bn_js_1.default.min(bn_js_1.default.max(versionedWeight, BN_ONE), new bn_js_1.default(maxBlockWeight));
const boundedLength = bn_js_1.default.min(bn_js_1.default.max(new bn_js_1.default(len), BN_ONE), maxLength);
const maxTxPerBlockWeight = maxBlockWeight.toBn().div(boundedWeight);
const maxTxPerBlockLength = maxLength.div(boundedLength);
const maxTxPerBlock = bn_js_1.default.min(maxTxPerBlockWeight, maxTxPerBlockLength);
const saturatedTip = tip.toBn().add(BN_ONE);
const scaledTip = this.maxReward(saturatedTip, maxTxPerBlock);
let priority;
switch (sanitizedClass) {
case 'normal': {
priority = scaledTip.toString();
break;
}
case 'mandatory': {
priority = scaledTip.toString();
break;
}
case 'operational': {
const u8a = ext.toU8a();
const { inclusionFee } = await api.call.transactionPaymentApi.queryFeeDetails(u8a, u8a.length);
const { operationalFeeMultiplier } = api.consts.transactionPayment;
if (inclusionFee.isNone) {
// This is an unsigned_extrinsic, and does not have priority
priority = '0';
break;
}
const { baseFee, lenFee, adjustedWeightFee } = inclusionFee.unwrap();
const computedInclusionFee = baseFee.toBn().add(lenFee).add(adjustedWeightFee);
const finalFee = computedInclusionFee.add(tip.toBn());
const virtualTip = finalFee.mul(operationalFeeMultiplier);
const scaledVirtualTip = this.maxReward(virtualTip, maxTxPerBlock);
priority = scaledTip.add(scaledVirtualTip).toString();
break;
}
default: {
priority = '0';
break;
}
}
return priority;
}
/**
* Explicitly define the type of class an extrinsic is.
*
* @param c DispatchClass of an extrinsic
*/
defineDispatchClassType(c) {
const cString = c.type.toLowerCase();
if (cString === 'normal')
return 'normal';
if (cString === 'mandatory')
return 'mandatory';
if (cString === 'operational')
return 'operational';
// This will never be reached, but is here to satisfy the TS compiler.
return 'normal';
}
/**
* Multiply a value (tip) by its maxTxPerBlock multiplier.
* ref: https://github.com/paritytech/substrate/blob/fe5bf49290d166b9552f65e751d46ec592173ebd/frame/transaction-payment/src/lib.rs#L633
*
* @param val Value to be multiplied by the maxTxPerBlock. Usually a tip.
* @param maxTxPerBlock The minimum value between maxTxPerBlockWeight and maxTxPerBlockLength
*/
maxReward(val, maxTxPerBlock) {
return val.mul(maxTxPerBlock);
}
}
exports.NodeTransactionPoolService = NodeTransactionPoolService;
//# sourceMappingURL=NodeTransactionPoolService.js.map