Files

108 lines
3.2 KiB
TypeScript

import type { ReservedContributorStruct } from '@/hooks/useCreateOpenNodeRegistration';
import {
type ContributionContractContributor,
type StakeContributor,
isContributionContractContributor,
} from '@session/staking-api-js/schema';
import { areHexesEqual } from '@session/util-crypto/string';
import type { Address } from 'viem';
const SESSION_NODE_FULL_STAKE_AMOUNT = 25_000_000000000n;
export const parseContributorDetails = (contributors: Array<ReservedContributorStruct> = []) => {
let totalStaked = 0n;
for (const contributor of contributors) {
const amount = contributor.amount;
totalStaked += amount;
}
if (totalStaked > SESSION_NODE_FULL_STAKE_AMOUNT) {
throw new Error(
`Total staked amount must be less than or equal to the full stake amount: ${totalStaked}`
);
}
const remainingStake = SESSION_NODE_FULL_STAKE_AMOUNT - totalStaked;
const min = calcMinimumContribution(
remainingStake,
BigInt(contributors.length),
// If no contributors the first has to be the operator
contributors.length ? 10n : 1n
);
return {
maxStake: remainingStake,
minStake: min,
totalStaked: totalStaked,
};
};
function calcMinimumContribution(
contributionRemaining: bigint,
numContributors: bigint,
maxNumContributors: bigint
): bigint {
if (maxNumContributors < numContributors) {
throw new Error(
`Number of contributors must be less than or equal to the max number of contributors. Max: ${maxNumContributors}, Current: ${numContributors}`
);
}
if (contributionRemaining < 0n) {
throw new Error(`contributionRemaining must be non-negative: ${contributionRemaining}`);
}
if (numContributors < 0n) {
throw new Error(`numContributors must be non-negative: ${numContributors}`);
}
if (maxNumContributors < 0n) {
throw new Error(`maxNumContributors must be non-negative, ${maxNumContributors}`);
}
let result: bigint;
if (numContributors === 0n) {
// Equivalent to Math.ceil(contributionRemaining / 4)
result = contributionRemaining / 4n;
} else {
const slotsRemaining = maxNumContributors - numContributors;
// Equivalent to Math.ceil(contributionRemaining / slotsRemaining)
if (slotsRemaining) {
result = (contributionRemaining + slotsRemaining - 1n) / slotsRemaining;
} else {
return 0n;
}
}
return result;
}
export const getContributionRangeFromContributors = (
contributors: Array<StakeContributor | ContributionContractContributor> = []
) =>
parseContributorDetails(
contributors.map((contributor) => {
return {
amount:
contributor.amount ||
(isContributionContractContributor(contributor) ? contributor.reserved : 0n),
addr: contributor.address,
};
})
);
export const getContributionRangeFromContributorsIgnoreAddress = (
contributors: Array<StakeContributor | ContributionContractContributor> = [],
address?: Address
) =>
getContributionRangeFromContributors(
contributors.filter(
({ address: contributorAddress }) => !areHexesEqual(contributorAddress, address)
)
);
export const getTotalStaked = (
contributors: Array<StakeContributor | ContributionContractContributor> = []
) => contributors.reduce((acc, { amount }) => acc + amount, 0n);