mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-07-07 10:47:26 +00:00
971df8edba
- Remove all 3rd party parachain configurations from endpoints: - productionRelayPolkadot.ts: Keep only system parachains - productionRelayDicle.ts: Keep only system parachains - testingRelayZagros.ts: Keep only system parachains - testingRelayTeyrChain.ts: Keep only system parachains - Update domain references: - polkadot.js.org → pezkuwichain.app - wiki.polkadot.network → wiki.pezkuwichain.io - dotapps.io → pezkuwichain.app - statement.polkadot.network → docs.pezkuwichain.io/statement - support.polkadot.network → docs.pezkuwichain.io - Update repository references: - github.com/pezkuwi-js/apps → github.com/pezkuwichain/pwap - Rename system parachains to Pezkuwi ecosystem: - PolkadotAssetHub → PezkuwiAssetHub - polkadotBridgeHub → pezkuwiBridgeHub - polkadotCollectives → pezkuwiCollectives - polkadotCoretime → pezkuwiCoretime - polkadotPeople → pezkuwiPeople - Update network name in claims utility: - Polkadot → Pezkuwi
258 lines
7.9 KiB
TypeScript
258 lines
7.9 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/app-referenda authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { ApiPromise } from '@pezkuwi/api';
|
|
import type { PezpalletConvictionVotingTally, PezpalletRankedCollectiveTally, PezpalletReferendaCurve, PezpalletReferendaReferendumInfoConvictionVotingTally, PezpalletReferendaReferendumInfoRankedCollectiveTally, PezpalletReferendaTrackDetails } from '@pezkuwi/types/lookup';
|
|
import type { CurveGraph, TrackDescription, TrackInfoExt } from './types.js';
|
|
|
|
import { getGovernanceTracks } from '@pezkuwi/apps-config';
|
|
import { BN, BN_BILLION, BN_ONE, BN_ZERO, bnMax, bnMin, formatNumber, objectSpread, stringPascalCase } from '@pezkuwi/util';
|
|
|
|
const CURVE_LENGTH = 500;
|
|
|
|
export function getTrackName (trackId: BN, { name }: PezpalletReferendaTrackDetails): string {
|
|
return `${
|
|
formatNumber(trackId)
|
|
} / ${
|
|
name
|
|
.replace(/_/g, ' ')
|
|
.split(' ')
|
|
.map(stringPascalCase)
|
|
.join(' ')
|
|
}`;
|
|
}
|
|
|
|
export function getTrackInfo (api: ApiPromise, specName: string, palletReferenda: string, tracks: TrackDescription[], trackId?: number): TrackInfoExt | undefined {
|
|
let info: TrackInfoExt | undefined;
|
|
|
|
if (tracks && trackId !== undefined && trackId !== -1) {
|
|
const originMap = getGovernanceTracks(api, specName, palletReferenda);
|
|
const track = tracks.find(({ id }) => id.eqn(trackId));
|
|
|
|
if (track && originMap) {
|
|
const trackName = track.info.name.toString();
|
|
const base = originMap.find(({ id, name }) =>
|
|
id === trackId &&
|
|
name === trackName
|
|
);
|
|
|
|
if (base) {
|
|
info = objectSpread<TrackInfoExt>({
|
|
track,
|
|
trackName: getTrackName(track.id, track.info)
|
|
}, base);
|
|
}
|
|
}
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
export function isConvictionTally (tally: PezpalletRankedCollectiveTally | PezpalletConvictionVotingTally): tally is PezpalletConvictionVotingTally {
|
|
return !!(tally as PezpalletConvictionVotingTally).support && !(tally as PezpalletRankedCollectiveTally).bareAyes;
|
|
}
|
|
|
|
export function isConvictionVote (info: PezpalletReferendaReferendumInfoConvictionVotingTally | PezpalletReferendaReferendumInfoRankedCollectiveTally): info is PezpalletReferendaReferendumInfoConvictionVotingTally {
|
|
return info.isOngoing && isConvictionTally(info.asOngoing.tally);
|
|
}
|
|
|
|
export function curveThreshold (curve: PezpalletReferendaCurve, input: BN, div: BN): BN {
|
|
// if divisor is zero, we return the max
|
|
if (div.isZero()) {
|
|
return BN_BILLION;
|
|
}
|
|
|
|
const x = input.mul(BN_BILLION).div(div);
|
|
|
|
if (curve.isLinearDecreasing) {
|
|
const { ceil, floor, length } = curve.asLinearDecreasing;
|
|
|
|
// if divisor is zero, we return the max
|
|
if (length.isZero()) {
|
|
return BN_BILLION;
|
|
}
|
|
|
|
// *ceil - (x.min(*length).saturating_div(*length, Down) * (*ceil - *floor))
|
|
// NOTE: We first multiply, then divide (since we work with fractions)
|
|
return ceil.sub(
|
|
bnMin(x, length)
|
|
.mul(ceil.sub(floor))
|
|
.div(length)
|
|
);
|
|
} else if (curve.isSteppedDecreasing) {
|
|
const { begin, end, period, step } = curve.asSteppedDecreasing;
|
|
|
|
// (*begin - (step.int_mul(x.int_div(*period))).min(*begin)).max(*end)
|
|
return bnMax(
|
|
end,
|
|
begin.sub(
|
|
bnMin(
|
|
begin,
|
|
step
|
|
.mul(x)
|
|
.div(period)
|
|
)
|
|
)
|
|
);
|
|
} else if (curve.isReciprocal) {
|
|
const { factor, xOffset, yOffset } = curve.asReciprocal;
|
|
const div = x.add(xOffset);
|
|
|
|
if (div.isZero()) {
|
|
return BN_BILLION;
|
|
}
|
|
|
|
// factor
|
|
// .checked_rounding_div(FixedI64::from(x) + *x_offset, Low)
|
|
// .map(|yp| (yp + *y_offset).into_clamped_perthing())
|
|
// .unwrap_or_else(Perbill::one)
|
|
return bnMin(
|
|
BN_BILLION,
|
|
factor
|
|
.mul(BN_BILLION)
|
|
.div(div)
|
|
.add(yOffset)
|
|
);
|
|
}
|
|
|
|
throw new Error(`Unknown curve found ${curve.type}`);
|
|
}
|
|
|
|
export function curveDelay (curve: PezpalletReferendaCurve, input: BN, div: BN): BN {
|
|
try {
|
|
// if divisor is zero, we return the max
|
|
if (div.isZero()) {
|
|
return BN_BILLION;
|
|
}
|
|
|
|
const y = input.mul(BN_BILLION).div(div);
|
|
|
|
if (curve.isLinearDecreasing) {
|
|
const { ceil, floor, length } = curve.asLinearDecreasing;
|
|
|
|
// if y < *floor {
|
|
// Perbill::one()
|
|
// } else if y > *ceil {
|
|
// Perbill::zero()
|
|
// } else {
|
|
// (*ceil - y).saturating_div(*ceil - *floor, Up).saturating_mul(*length)
|
|
// }
|
|
return y.lt(floor)
|
|
? BN_BILLION
|
|
: y.gt(ceil)
|
|
? BN_ZERO
|
|
: bnMin(
|
|
BN_BILLION,
|
|
bnMax(
|
|
BN_ZERO,
|
|
ceil
|
|
.sub(y)
|
|
.mul(length)
|
|
.div(ceil.sub(floor))
|
|
)
|
|
);
|
|
} else if (curve.isSteppedDecreasing) {
|
|
const { begin, end, period, step } = curve.asSteppedDecreasing;
|
|
|
|
// if y < *end {
|
|
// Perbill::one()
|
|
// } else {
|
|
// period.int_mul((*begin - y.min(*begin) + step.less_epsilon()).int_div(*step))
|
|
// }
|
|
return y.lt(end)
|
|
? BN_BILLION
|
|
: bnMin(
|
|
BN_BILLION,
|
|
bnMax(
|
|
BN_ZERO,
|
|
period
|
|
.mul(
|
|
begin
|
|
.sub(bnMin(y, begin))
|
|
.add(
|
|
step.isZero()
|
|
? step
|
|
: step.sub(BN_ONE)
|
|
)
|
|
)
|
|
.div(step)
|
|
)
|
|
);
|
|
} else if (curve.asReciprocal) {
|
|
const { factor, xOffset, yOffset } = curve.asReciprocal;
|
|
const div = y.sub(yOffset);
|
|
|
|
if (div.isZero()) {
|
|
return BN_BILLION;
|
|
}
|
|
|
|
// let y = FixedI64::from(y);
|
|
// let maybe_term = factor.checked_rounding_div(y - *y_offset, High);
|
|
// maybe_term
|
|
// .and_then(|term| (term - *x_offset).try_into_perthing().ok())
|
|
// .unwrap_or_else(Perbill::one)
|
|
return bnMin(
|
|
BN_BILLION,
|
|
bnMax(
|
|
BN_ZERO,
|
|
factor
|
|
.mul(BN_BILLION)
|
|
.div(div)
|
|
.sub(xOffset)
|
|
)
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error(`Failed on curve ${curve.type}:`, curve.inner.toHuman());
|
|
|
|
throw error;
|
|
}
|
|
|
|
throw new Error(`Unknown curve found ${curve.type}`);
|
|
}
|
|
|
|
export function calcDecidingEnd (totalEligible: BN, tally: PezpalletRankedCollectiveTally | PezpalletConvictionVotingTally, { decisionPeriod, minApproval, minSupport }: PezpalletReferendaTrackDetails, since: BN): BN | undefined {
|
|
const support = isConvictionTally(tally)
|
|
? tally.support
|
|
: tally.bareAyes;
|
|
|
|
return since.add(
|
|
decisionPeriod
|
|
.mul(
|
|
bnMax(
|
|
curveDelay(minApproval, tally.ayes, tally.ayes.add(tally.nays)),
|
|
curveDelay(minSupport, support, totalEligible)
|
|
)
|
|
)
|
|
.div(BN_BILLION)
|
|
);
|
|
}
|
|
|
|
export function calcCurves ({ decisionPeriod, minApproval, minSupport }: PezpalletReferendaTrackDetails): CurveGraph {
|
|
const approval = new Array<BN>(CURVE_LENGTH);
|
|
const support = new Array<BN>(CURVE_LENGTH);
|
|
const x = new Array<BN>(CURVE_LENGTH);
|
|
const last = CURVE_LENGTH - 1;
|
|
// Bringing it to a higher precision before dividing by curve length.
|
|
// Otherwise, graphs with short periods (on dev chains) are invalid.
|
|
const stepWithPrecision = decisionPeriod.muln(100).divn(CURVE_LENGTH);
|
|
let currentWithPrecision = new BN(0);
|
|
|
|
for (let i = 0; i < last; i++) {
|
|
const current = currentWithPrecision.divn(100);
|
|
|
|
approval[i] = curveThreshold(minApproval, current, decisionPeriod);
|
|
support[i] = curveThreshold(minSupport, current, decisionPeriod);
|
|
x[i] = current;
|
|
|
|
currentWithPrecision = currentWithPrecision.add(stepWithPrecision);
|
|
}
|
|
|
|
// since we may be lossy with the step, we explicitly calc the final point at 100%
|
|
approval[last] = curveThreshold(minApproval, decisionPeriod, decisionPeriod);
|
|
support[last] = curveThreshold(minSupport, decisionPeriod, decisionPeriod);
|
|
x[last] = decisionPeriod;
|
|
|
|
return { approval, support, x };
|
|
}
|