@substrate/api-sidecar
Version:
REST service that makes it easy to interact with blockchain nodes built using Substrate's FRAME framework.
141 lines • 6.59 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.calculateVestingDetails = exports.calculateVestedClaimable = exports.calculateVestingTotal = exports.calculateTotalVested = exports.calculateEndBlock = exports.calculateVested = void 0;
const bn_js_1 = __importDefault(require("bn.js"));
/**
* Calculate the amount that has vested for a single vesting schedule.
*
* The calculation follows the formula used in the vesting pallet:
* - If currentBlock <= startingBlock: nothing is vested yet
* - Otherwise: vested = min(blocksPassed * perBlock, locked)
*
* Note: This is the theoretical vested amount based on time. The actual
* claimable amount depends on the on-chain lock state (see calculateVestedClaimable).
*
* @param currentBlock - The block number to calculate vested amount at
* @param schedule - The vesting schedule containing locked, perBlock, and startingBlock
* @returns The amount that has vested at the given block
*/
const calculateVested = (currentBlock, schedule) => {
const { locked, perBlock, startingBlock } = schedule;
// Vesting hasn't started yet
if (currentBlock.lte(startingBlock)) {
return new bn_js_1.default(0);
}
// Calculate how many blocks have passed since vesting started
const blocksPassed = currentBlock.sub(startingBlock);
// Calculate vested amount: blocksPassed * perBlock
const vested = blocksPassed.mul(perBlock);
// Return the minimum of vested and locked (can't vest more than was locked)
return bn_js_1.default.min(vested, locked);
};
exports.calculateVested = calculateVested;
/**
* Calculate the block number when a vesting schedule will be fully vested.
*
* The end block is calculated as: startingBlock + (locked / perBlock)
*
* @param schedule - The vesting schedule
* @returns The block number when vesting completes
*/
const calculateEndBlock = (schedule) => {
const { locked, perBlock, startingBlock } = schedule;
// Handle edge case where perBlock is 0 (would cause division by zero)
if (perBlock.isZero()) {
// If nothing unlocks per block, vesting never ends
// Return a very large number to indicate "never"
return new bn_js_1.default(Number.MAX_SAFE_INTEGER);
}
// endBlock = startingBlock + ceil(locked / perBlock)
// Using div for integer division, then add 1 if there's a remainder
const blocksNeeded = locked.div(perBlock);
const hasRemainder = !locked.mod(perBlock).isZero();
return startingBlock.add(blocksNeeded).add(hasRemainder ? new bn_js_1.default(1) : new bn_js_1.default(0));
};
exports.calculateEndBlock = calculateEndBlock;
/**
* Calculate the total vested amount across multiple vesting schedules.
*
* @param currentBlock - The block number to calculate vested amounts at
* @param schedules - Array of vesting schedules
* @returns The total amount that has vested across all schedules (vestedBalance)
*/
const calculateTotalVested = (currentBlock, schedules) => {
return schedules.reduce((total, schedule) => {
return total.add((0, exports.calculateVested)(currentBlock, schedule));
}, new bn_js_1.default(0));
};
exports.calculateTotalVested = calculateTotalVested;
/**
* Calculate the total locked amount across multiple vesting schedules.
*
* @param schedules - Array of vesting schedules
* @returns The total locked amount across all schedules (vestingTotal)
*/
const calculateVestingTotal = (schedules) => {
return schedules.reduce((total, schedule) => {
return total.add(schedule.locked);
}, new bn_js_1.default(0));
};
exports.calculateVestingTotal = calculateVestingTotal;
/**
* Calculate the actual claimable amount that can be unlocked.
*
* This follows the polkadot-js formula:
* vestedClaimable = vestingLocked - (vestingTotal - vestedBalance)
*
* Where:
* - vestingLocked: actual on-chain lock amount from balances.locks
* - vestingTotal: sum of all locked amounts from vesting schedules
* - vestedBalance: sum of all vested amounts from vesting schedules
*
* The difference accounts for previous claims that reduced the on-chain lock.
*
* @param vestingLocked - The actual on-chain vesting lock amount
* @param vestingTotal - Total locked across all vesting schedules
* @param vestedBalance - Total vested across all vesting schedules
* @returns The amount that can actually be claimed right now
*/
const calculateVestedClaimable = (vestingLocked, vestingTotal, vestedBalance) => {
// stillLocked = vestingTotal - vestedBalance (what should remain locked according to schedules)
const stillLocked = vestingTotal.sub(vestedBalance);
// vestedClaimable = vestingLocked - stillLocked
// This is the difference between what's actually locked on-chain and what should be locked
const claimable = vestingLocked.sub(stillLocked);
// Ensure we don't return negative values
return bn_js_1.default.max(claimable, new bn_js_1.default(0));
};
exports.calculateVestedClaimable = calculateVestedClaimable;
/**
* Calculate vested amounts for multiple schedules, returning per-schedule results.
*
* @param currentBlock - The block number to calculate vested amounts at
* @param schedules - Array of vesting schedules
* @returns Array of calculation results with vested amount and end block for each schedule
*/
const calculateVestingDetails = (currentBlock, schedules) => {
return schedules.map((schedule) => ({
vested: (0, exports.calculateVested)(currentBlock, schedule),
endBlock: (0, exports.calculateEndBlock)(schedule),
}));
};
exports.calculateVestingDetails = calculateVestingDetails;
//# sourceMappingURL=vestingCalculations.js.map