import BN from 'bn.js' ; import { newtonRaphson , NewtonRaphsonResult } from './newton-raphson' ; import { solveQuadraticEquation } from './solveQuadraticEquation' ; import { FailingThresholdResult , PassingThresholdResult , VoteThreshold , VoteThresholdEnum } from './types' ; interface ThresholdBase { totalIssuance : BN ; threshold : VoteThreshold ; } interface getPassingThresholdParamsType extends ThresholdBase { nays : BN ; naysWithoutConviction : BN ; } interface getFailingThresholdParamsType extends ThresholdBase { ayes : BN ; ayesWithoutConviction : BN ; } const ONE = new BN ( 1 ) ; const TWO = new BN ( 2 ) ; const THREE = new BN ( 3 ) ; const TEN = new BN ( 10 ) ; const MAX_ITERATIONS = 20 ; interface FAndFpParamsTypes { totalIssuance : BN ; votes : BN ; votesWithoutConviction : BN ; } type PolynomialFunction = ( x : BN ) => BN ; interface FandFpType { f : PolynomialFunction ; fp : PolynomialFunction ; } const getFAndFp = function ( { totalIssuance , votes , votesWithoutConviction } ) : FandFpType { return { f : ( x ) => { return x . pow ( THREE ) . add ( votesWithoutConviction . mul ( x . pow ( TWO ) ) ) . sub ( votes . pow ( TWO ) . mul ( totalIssuance ) ) ; } , fp : ( x ) => { return THREE . mul ( x . pow ( TWO ) ) . add ( TWO . mul ( votesWithoutConviction ) . mul ( x ) ) ; } } ; } ; const raphsonIterations = function ( f , fp ) : NewtonRaphsonResult { const initialGuess = ONE ; let result = { foundRoot : false } ; let i = 1 ; while ( ! result . foundRoot && i < MAX_ITERATIONS ) { result = newtonRaphson ( f , fp , initialGuess . mul ( TEN ) . pow ( new BN ( i ) ) ) ; i ++ ; } return result ; } ; name summary export function getFailingThreshold ( { ayes , ayesWithoutConviction , totalIssuance , threshold } ) { if ( ayes . isZero ( ) || ayesWithoutConviction . isZero ( ) ) { return { failingThreshold : ONE , isValid : true } ; } if ( ayesWithoutConviction . gt ( totalIssuance . divn ( 2 ) ) ) { return { isValid : false } ; } if ( threshold === VoteThresholdEnum . Simplemajority ) { return { failingThreshold : ayes , isValid : true } ; } if ( threshold === VoteThresholdEnum . Supermajorityrejection ) { const { f , fp } = getFAndFp ( { totalIssuance , votes : ayes , votesWithoutConviction : ayesWithoutConviction } ) ; const result = raphsonIterations ( f , fp ) ; return result . foundRoot ? { failingThreshold : result . result as BN , isValid : true } : { isValid : false } ; } else { const res = solveQuadraticEquation ( totalIssuance . neg ( ) , ayes . pow ( TWO ) , ayes . pow ( TWO ) . mul ( ayesWithoutConviction ) ) ; return { failingThreshold : BN . max ( res [ 0 ] , res [ 1 ] ) , isValid : true } ; } } name summary export function getPassingThreshold ( { nays , naysWithoutConviction , totalIssuance , threshold } ) { if ( nays . isZero ( ) || naysWithoutConviction . isZero ( ) ) { return { isValid : true , passingThreshold : ONE } ; } if ( naysWithoutConviction . gt ( totalIssuance . divn ( 2 ) ) ) { return { isValid : false } ; } if ( threshold === VoteThresholdEnum . Simplemajority ) { return { isValid : true , passingThreshold : nays } ; } if ( threshold === VoteThresholdEnum . Supermajorityapproval ) { const { f , fp } = getFAndFp ( { totalIssuance , votes : nays , votesWithoutConviction : naysWithoutConviction } ) ; const result = raphsonIterations ( f , fp ) ; return result . foundRoot ? { isValid : true , passingThreshold : result . result as BN } : { isValid : false } ; } else { const res = solveQuadraticEquation ( totalIssuance . neg ( ) , nays . pow ( TWO ) , nays . pow ( TWO ) . mul ( naysWithoutConviction ) ) ; return { isValid : true , passingThreshold : BN . max ( res [ 0 ] , res [ 1 ] ) } ; } }