UNPKG

radix-utils

Version:

Utility functions for Radix DLT blockchain development

228 lines 9.97 kB
"use strict"; 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