mirror of
https://github.com/lux-tel/websites.git
synced 2026-10-30 20:31:36 +00:00
175 lines
5.2 KiB
TypeScript
175 lines
5.2 KiB
TypeScript
import type {
|
|
ContributionContract,
|
|
ContributionContractNotReady,
|
|
Stake,
|
|
VestingContract,
|
|
} from '@session/staking-api-js/schema';
|
|
import { bigIntSortDesc } from '@session/util-crypto/maths';
|
|
import { areHexesEqual } from '@session/util-crypto/string';
|
|
import { safeTrySyncWithFallback } from '@session/util-js/try';
|
|
import type { Address } from 'viem';
|
|
import {
|
|
STAKE_EVENT_STATE,
|
|
STAKE_STATE,
|
|
isStakeRequestingExit,
|
|
parseStakeEventState,
|
|
parseStakeState,
|
|
} from '../components/StakedNode/state';
|
|
import { getTotalStakedAmountForAddress } from '../components/getTotalStakedAmountForAddress';
|
|
import logger from '../lib/logger';
|
|
import { getReadyContracts, parseContracts } from './parseContracts';
|
|
|
|
/**
|
|
* Sorts stakes by total staked amount descending.
|
|
* @param a - The first stake to compare.
|
|
* @param b - The second stake to compare.
|
|
* @param address - The address to filter by.
|
|
* @returns The sorted stakes.
|
|
*/
|
|
export const sortingTotalStakedDesc = (
|
|
a: Stake | ContributionContract,
|
|
b: Stake | ContributionContract,
|
|
address?: Address
|
|
) => {
|
|
const stakedA = address ? getTotalStakedAmountForAddress(a.contributors, address) : 0n;
|
|
const stakedB = address ? getTotalStakedAmountForAddress(b.contributors, address) : 0n;
|
|
return bigIntSortDesc(stakedB, stakedA);
|
|
};
|
|
|
|
/**
|
|
* Sorts reserved contracts by total staked amount descending.
|
|
* @param a - The first contract to compare.
|
|
* @param b - The second contract to compare.
|
|
* @param connectedAddress - The connected address to filter by.
|
|
* @returns The sorted contracts.
|
|
*/
|
|
export const sortingReservedContractsDesc = (
|
|
a: ContributionContract,
|
|
b: ContributionContract,
|
|
connectedAddress?: Address
|
|
) => {
|
|
const reservedA = connectedAddress
|
|
? (a.contributors.find(({ address }) => areHexesEqual(address, connectedAddress))?.reserved ??
|
|
0n)
|
|
: 0n;
|
|
|
|
const reservedB = connectedAddress
|
|
? (b.contributors.find(({ address }) => areHexesEqual(address, connectedAddress))?.reserved ??
|
|
0n)
|
|
: 0n;
|
|
|
|
return bigIntSortDesc(reservedB, reservedA);
|
|
};
|
|
|
|
const stakeStateSortOrder = {
|
|
[STAKE_STATE.DECOMMISSIONED]: 1,
|
|
[STAKE_STATE.AWAITING_EXIT]: 2,
|
|
[STAKE_STATE.RUNNING]: 3,
|
|
[STAKE_STATE.DEREGISTERED]: 4,
|
|
[STAKE_STATE.EXITED]: 5,
|
|
[STAKE_STATE.UNKNOWN]: 6,
|
|
} as const;
|
|
|
|
/**
|
|
* Stakes are sorted by {@link stakeStateSortOrder} ascending
|
|
* then by the amount staked by the connected wallet descending
|
|
* then by the operator fee ascending
|
|
*
|
|
* NOTE: If both stakes are {@link STAKE_STATE.DECOMMISSIONED} then they are sorted by earned_downtime_blocks ascending
|
|
* NOTE: If both stakes are {@link STAKE_STATE.AWAITING_EXIT} then they are sorted by requested_unlock_height ascending
|
|
*/
|
|
export function sortStakes(a: Stake, b: Stake, address?: Address, blockHeight = 0) {
|
|
const stateA = parseStakeState(a, blockHeight);
|
|
const stateB = parseStakeState(b, blockHeight);
|
|
|
|
let priorityA = stakeStateSortOrder[stateA] ?? 999;
|
|
let priorityB = stakeStateSortOrder[stateB] ?? 999;
|
|
|
|
if (isStakeRequestingExit(a) && stateA !== STAKE_STATE.DEREGISTERED) {
|
|
priorityA = stakeStateSortOrder[STAKE_STATE.AWAITING_EXIT] + 0.1;
|
|
}
|
|
|
|
if (isStakeRequestingExit(b) && stateB !== STAKE_STATE.DEREGISTERED) {
|
|
priorityB = stakeStateSortOrder[STAKE_STATE.AWAITING_EXIT] + 0.1;
|
|
}
|
|
|
|
if (priorityA !== priorityB) {
|
|
// Priority ascending
|
|
return priorityA - priorityB;
|
|
}
|
|
|
|
// NOTE: By definition, if the priorities are the same the state is the same
|
|
|
|
if (stateA === STAKE_STATE.DECOMMISSIONED || stateB === STAKE_STATE.DECOMMISSIONED) {
|
|
// earned_downtime_blocks ascending
|
|
return (
|
|
(a.earned_downtime_blocks ?? Number.POSITIVE_INFINITY) -
|
|
(b.earned_downtime_blocks ?? Number.POSITIVE_INFINITY)
|
|
);
|
|
}
|
|
|
|
if (isStakeRequestingExit(a) || isStakeRequestingExit(b)) {
|
|
// requested_unlock_height ascending
|
|
return (
|
|
(a.requested_unlock_height ?? Number.POSITIVE_INFINITY) -
|
|
(b.requested_unlock_height ?? Number.POSITIVE_INFINITY)
|
|
);
|
|
}
|
|
|
|
const stakeSort = sortingTotalStakedDesc(a, b, address);
|
|
if (stakeSort) return stakeSort;
|
|
|
|
// operator_fee ascending
|
|
return (a.operator_fee ?? 0) - (b.operator_fee ?? 0);
|
|
}
|
|
|
|
export type ParseStakesParams = {
|
|
address?: Address;
|
|
blockHeight: number;
|
|
stakes: Array<Stake>;
|
|
contracts: Array<ContributionContractNotReady>;
|
|
vesting: Array<VestingContract>;
|
|
addedBlsKeys: Record<string, number>;
|
|
nodeMinLifespanArbBlocks: number;
|
|
};
|
|
|
|
export function parseStakes({
|
|
stakes,
|
|
vesting,
|
|
address,
|
|
blockHeight,
|
|
contracts,
|
|
addedBlsKeys,
|
|
nodeMinLifespanArbBlocks,
|
|
}: ParseStakesParams) {
|
|
stakes.sort((a, b) => sortStakes(a, b, address, blockHeight));
|
|
|
|
const runningStakesBlsKeysSet = new Set(
|
|
stakes
|
|
.filter((stake) => parseStakeEventState(stake) === STAKE_EVENT_STATE.ACTIVE)
|
|
.map(({ pubkey_bls }) => pubkey_bls)
|
|
);
|
|
|
|
vesting.sort((a, b) => bigIntSortDesc(a.initial_amount, b.initial_amount));
|
|
|
|
const [contractsErr, readyContracts] = safeTrySyncWithFallback(
|
|
() => getReadyContracts(contracts),
|
|
[]
|
|
);
|
|
if (contractsErr) logger.error(contractsErr);
|
|
|
|
return {
|
|
...parseContracts({
|
|
contracts: readyContracts,
|
|
address,
|
|
blockHeight,
|
|
addedBlsKeys,
|
|
nodeMinLifespanArbBlocks,
|
|
runningStakesBlsKeysSet,
|
|
}),
|
|
stakes,
|
|
vesting,
|
|
blockHeight,
|
|
};
|
|
}
|