radix-utils
Version:
Utility functions for Radix DLT blockchain development
228 lines • 9.97 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchValidatorInfo = exports.checkResourceInUsersFungibleAssets = exports.computeValidatorFeeFactor = void 0;
const decimal_1 = require("../utils/decimal");
const date_1 = require("../utils/date");
/**
* Extract metadata from EntityMetadataCollection
* @param metadata - The metadata collection to extract from
* @returns Extracted metadata as key-value pairs
*/
const extractMetadata = (metadata) => {
const extractedMetadata = {};
metadata.items.forEach((item) => {
const valueType = item.value.typed.type;
if (valueType === 'String' ||
valueType === 'Url' ||
valueType === 'GlobalAddress' ||
valueType === 'NonFungibleLocalId') {
extractedMetadata[item.key] = item.value.typed.value;
}
});
return extractedMetadata;
};
/**
* Extract vault addresses from validator state
* @param state - The validator state object
* @returns Validator vault addresses
*/
const extractVaultsAddresses = (state) => {
let NODE_CURRENTLY_EARNED_LSU_VAULT_ADDRESS = '';
let NODE_OWNER_UNLOCKING_LSU_VAULT_ADDRESS = '';
let NODE_TOTAL_STAKED_XRD_VAULT_ADDRESS = '';
let NODE_UNSTAKING_XRD_VAULT_ADDRESS = '';
if ('stake_xrd_vault' in state) {
NODE_TOTAL_STAKED_XRD_VAULT_ADDRESS = state.stake_xrd_vault.entity_address;
}
if ('pending_xrd_withdraw_vault' in state) {
NODE_UNSTAKING_XRD_VAULT_ADDRESS = state.pending_xrd_withdraw_vault.entity_address;
}
if ('locked_owner_stake_unit_vault' in state) {
NODE_CURRENTLY_EARNED_LSU_VAULT_ADDRESS = state.locked_owner_stake_unit_vault.entity_address;
}
if ('pending_owner_stake_unit_unlock_vault' in state) {
NODE_OWNER_UNLOCKING_LSU_VAULT_ADDRESS = state.pending_owner_stake_unit_unlock_vault.entity_address;
}
return {
NODE_CURRENTLY_EARNED_LSU_VAULT_ADDRESS,
NODE_OWNER_UNLOCKING_LSU_VAULT_ADDRESS,
NODE_TOTAL_STAKED_XRD_VAULT_ADDRESS,
NODE_UNSTAKING_XRD_VAULT_ADDRESS,
};
};
/**
* Filter pending withdrawals and separate unlocked LSUs
* @param pendingWithdrawals - Array of pending withdrawals
* @param currentEpoch - Current epoch number
* @returns Filtered withdrawals and unlocked amounts
*/
const filterPendingWithdrawalsFromUnlockedLSUs = (pendingWithdrawals, currentEpoch) => {
let unlockedLSUsAmount = (0, decimal_1.BN)(0);
let lsuInUnlockingProcess = (0, decimal_1.BN)(0);
const filteredWithdrawals = pendingWithdrawals.filter((withdrawal) => {
const isUnlocked = withdrawal.epoch_unlocked <= currentEpoch;
if (isUnlocked) {
unlockedLSUsAmount = unlockedLSUsAmount.add(withdrawal.stake_unit_amount);
}
else {
lsuInUnlockingProcess = lsuInUnlockingProcess.add(withdrawal.stake_unit_amount);
}
return !isUnlocked;
});
return {
filteredWithdrawals,
unlockedLSUsAmount,
lsuInUnlockingProcess,
};
};
/**
* Compute validator fee factor information
* @param currentFeeFactor - Current fee factor as string
* @param newFeeFactor - New fee factor configuration (if any)
* @param currentEpoch - Current epoch number
* @returns Fee factor information
*/
const computeValidatorFeeFactor = (currentFeeFactor, newFeeFactor, currentEpoch) => {
const feeFactor = {
aboutToChange: null,
current: (+currentFeeFactor * 100).toFixed(2) + '%',
alert: '',
};
if (newFeeFactor) {
const newFactorPercentage = (+newFeeFactor.new_fee_factor * 100).toFixed(2) + '%';
if (newFeeFactor.epoch_effective <= currentEpoch) {
feeFactor.current = newFactorPercentage;
feeFactor.aboutToChange = null;
}
else {
feeFactor.aboutToChange = {
new_fee_factor: newFactorPercentage,
epoch_effective: newFeeFactor.epoch_effective,
};
feeFactor.alert = `Fee will be changed to ${newFactorPercentage} on ${(0, date_1.calculateEstimatedUnlockDate)(newFeeFactor.epoch_effective, currentEpoch)}`;
}
}
return feeFactor;
};
exports.computeValidatorFeeFactor = computeValidatorFeeFactor;
/**
* Check if a resource exists in users' fungible assets
* @param usersAddresses - Array of user addresses to check
* @param fungibleResourceToCheck - Resource address to check for
* @param gatewayApi - Gateway API client instance
* @param ledgerState - Optional ledger state selector
* @returns Resource check result with users and total amount
*/
const checkResourceInUsersFungibleAssets = async (usersAddresses, fungibleResourceToCheck, gatewayApi, ledgerState) => {
try {
const allPromises = usersAddresses.map((address) => gatewayApi.state.innerClient.entityFungibleResourceVaultPage({
stateEntityFungibleResourceVaultsPageRequest: {
address,
resource_address: fungibleResourceToCheck,
at_ledger_state: ledgerState,
},
}));
const allResponses = (await (0, decimal_1.retryPromiseAll)(allPromises)).flat();
let totalAmount = (0, decimal_1.BN)(0);
const usersWithResourceAmount = {};
allResponses.forEach((res) => {
res.items.forEach((vault) => {
if ((0, decimal_1.BN)(vault.amount).greaterThan(0)) {
usersWithResourceAmount[res.address] = vault.amount;
totalAmount = totalAmount.plus(vault.amount);
}
});
});
return {
usersWithResourceAmount,
totalAmount: totalAmount.toString(),
};
}
catch (error) {
console.error('Error in checkResourceInUsersFungibleAssets', error);
throw error;
}
};
exports.checkResourceInUsersFungibleAssets = checkResourceInUsersFungibleAssets;
/**
* Fetch comprehensive validator information
* @param gatewayApi - Gateway API client instance
* @param validatorAddress - Validator address to fetch info for
* @returns Validator information or undefined if not found/invalid
*/
const fetchValidatorInfo = async (gatewayApi, validatorAddress) => {
if (validatorAddress === '' || !validatorAddress.startsWith('validator_')) {
return undefined;
}
try {
const res = await gatewayApi.state.innerClient.stateEntityDetails({
stateEntityDetailsRequest: {
addresses: [validatorAddress],
aggregation_level: 'Vault',
},
});
const validatorInfo = res.items[0];
const vaultsBalance = {};
let rewardsInUnlockingProcess = [];
const epoch = res.ledger_state.epoch;
let unlockedLSUs = (0, decimal_1.BN)(0);
let ownerLSUsInUnlockingProcess = (0, decimal_1.BN)(0);
let stakeUnitAddress = '';
let fees = { alert: '', current: '', aboutToChange: null };
if (validatorInfo?.details?.type === 'Component' &&
validatorInfo?.details?.state) {
const metadata = extractMetadata(res.items[0].metadata);
const validatorState = validatorInfo.details.state;
const vaults = extractVaultsAddresses(validatorState);
// Extract vault balances
validatorInfo?.fungible_resources?.items.forEach((resource) => {
if (resource.aggregation_level === 'Vault') {
resource.vaults.items.forEach((vault) => {
vaultsBalance[vault.vault_address] = vault.amount;
});
}
});
// Process pending withdrawals
if ('pending_owner_stake_unit_withdrawals' in validatorState) {
const { filteredWithdrawals, unlockedLSUsAmount, lsuInUnlockingProcess, } = filterPendingWithdrawalsFromUnlockedLSUs(validatorState.pending_owner_stake_unit_withdrawals, epoch);
rewardsInUnlockingProcess = filteredWithdrawals;
unlockedLSUs = unlockedLSUs.add(unlockedLSUsAmount);
ownerLSUsInUnlockingProcess = ownerLSUsInUnlockingProcess.add(lsuInUnlockingProcess);
}
// Add already unlocked amounts
if ('already_unlocked_owner_stake_unit_amount' in validatorState) {
unlockedLSUs = unlockedLSUs.add(validatorState.already_unlocked_owner_stake_unit_amount);
}
// Get stake unit address
if ('stake_unit_resource_address' in validatorState) {
stakeUnitAddress = validatorState.stake_unit_resource_address;
}
// Compute fees
if ('validator_fee_factor' in validatorState &&
'validator_fee_change_request' in validatorState) {
fees = (0, exports.computeValidatorFeeFactor)(validatorState.validator_fee_factor, validatorState.validator_fee_change_request, epoch);
}
const info = {
currentlyEarnedLockedLSUs: vaultsBalance[vaults.NODE_CURRENTLY_EARNED_LSU_VAULT_ADDRESS] || '0',
ownerLSUsInUnlockingProcess: ownerLSUsInUnlockingProcess.toString(),
totalStakedXrds: vaultsBalance[vaults.NODE_TOTAL_STAKED_XRD_VAULT_ADDRESS] || '0',
totalXrdsLeavingOurNode: vaultsBalance[vaults.NODE_UNSTAKING_XRD_VAULT_ADDRESS] || '0',
unlockingLSUsBreakdown: rewardsInUnlockingProcess,
epoch,
unlockedLSUs: unlockedLSUs.toString(),
metadata,
stakeUnitAddress,
vaults,
validatorAddress,
fees,
};
return info;
}
}
catch (error) {
console.error('Error in fetchValidatorInfo', error);
}
return undefined;
};
exports.fetchValidatorInfo = fetchValidatorInfo;
//# sourceMappingURL=index.js.map
;