import { RadixAtom , RadixFixedSupplyTokenDefinitionParticle , RadixMutableSupplyTokenDefinitionParticle , RadixParticle , RadixSpin } from '../atommodel' ; import BN from 'bn.js' ; const bnZERO = new BN ( 0 ) ; const assertFeeDoesNotOverflow = ( fee ) => { const uint256Max = new BN ( 2 ) . pow ( new BN ( 256 ) ) . sub ( new BN ( 1 ) ) ; if ( fee . gt ( uint256Max ) ) { throw new Error ( `Fee cannot be larger than UInt256 max value` ) ; } } ; interface FeeEntry { feeForAtom ( atom , feeSize , outputs ) : BN ; } const perBytesFeeEntry = ( units , threshold , fee ) : FeeEntry => { if ( units === undefined || units === null ) { throw new Error ( `units must not be null` ) ; } if ( ! Number . isInteger ( units ) ) { throw new Error ( `units must be an integer, but non-int value ${ units } was passed in.` ) ; } if ( threshold === undefined || threshold === null ) { throw new Error ( `threshold must not be null` ) ; } if ( fee === undefined || fee === null ) { throw new Error ( `Fee must not be null` ) ; } if ( units <= 0 ) { throw new Error ( `Units must be positive, but got: ${ units }` ) ; } if ( threshold < 0 ) { throw new Error ( `Threshold must be non-negative, but got: ${ threshold }` ) ; } return { feeForAtom : ( atom , feeSize , outputs ) : BN => { if ( ! Number . isInteger ( feeSize ) ) { throw new Error ( `feeSize must be an integer, but non-int value ${ feeSize } was passed in.` ) ; } const numberOfUnits = feeSize / units ; if ( numberOfUnits <= threshold ) { return bnZERO ; } const overThresholdUnits = numberOfUnits - threshold ; const feeTotal = fee . mul ( new BN ( overThresholdUnits ) ) ; assertFeeDoesNotOverflow ( feeTotal ) ; return feeTotal ; } , } ; } ; export type Class < T > = new ( ... args ) => T ; export const singleOutParticlesOfTypeFromSet = ( particles , particleType ) : RadixParticle [ ] => { return singleOutParticlesOfTypeFromArray ( Array . from ( particles ) , particleType ) ; } ; export const singleOutParticlesOfTypeFromArray = ( particles , particleType ) : RadixParticle [ ] => { return particles . filter ( p => p instanceof particleType ) ; } ; const perParticleFeeEntry = ( particleType , threshold , fee ) : FeeEntry => { if ( threshold === undefined || threshold === null ) { throw new Error ( `threshold must not be null` ) ; } if ( threshold < 0 ) { throw new Error ( `Threshold must be non-negative, but got: ${ threshold }` ) ; } if ( fee === undefined || fee === null ) { throw new Error ( `Fee must not be null` ) ; } if ( particleType === undefined || particleType === null ) { throw new Error ( `particleType must not be null` ) ; } return { feeForAtom : ( atom , feeSize , outputs ) : BN => { const particleCount = singleOutParticlesOfTypeFromSet ( outputs , particleType ) . length ; if ( particleCount <= threshold ) { return bnZERO ; } const overThresholdParticles = particleCount - threshold ; const feeTotal = fee . mul ( new BN ( overThresholdParticles ) ) ; assertFeeDoesNotOverflow ( feeTotal ) ; return feeTotal ; } , } ; } ; interface FeeTable { minimumFee : BN ; feeEntries : FeeEntry [ ] ; } export const milliRads = ( count ) : BN => { const milliRadAmount = new BN ( 10 ) . pow ( new BN ( 15 ) ) . mul ( count instanceof BN ? count : new BN ( count ) ) ; return milliRadAmount ; } ; const makeFeeTable = ( ) : FeeTable => { return { minimumFee : milliRads ( 40 ) , feeEntries : [ perBytesFeeEntry ( 1 , 3072 , milliRads ( 1 ) ) , perParticleFeeEntry ( RadixFixedSupplyTokenDefinitionParticle , 0 , milliRads ( 1000 ) ) , perParticleFeeEntry ( RadixMutableSupplyTokenDefinitionParticle , 0 , milliRads ( 1000 ) ) , ] , } ; } ; const feeForAtom = ( atom , feeSize , outputs ) : BN => { const feeTable = makeFeeTable ( ) ; const feeEntries = feeTable . feeEntries ; const minimumFee = feeTable . minimumFee ; let incrementalFees = new BN ( 0 ) ; for ( const entry of feeEntries ) { incrementalFees = incrementalFees . add ( entry . feeForAtom ( atom , feeSize , outputs ) ) ; } assertFeeDoesNotOverflow ( incrementalFees ) ; if ( incrementalFees . lt ( minimumFee ) ) { return minimumFee ; } else { return incrementalFees ; } } ; export const calculateFeeForAtom = ( atom ) : BN => { const outputs = new Set ( atom . getParticlesOfSpin ( RadixSpin . UP ) ) ; const feeSize = atom . toDSON ( ) . length ; return feeForAtom ( atom , feeSize , outputs ) ; } ;