11'use server' ;
22
33import { ERC20Abi } from '@/blockchain/ERC20' ;
4+ import { MNDContractAbi } from '@/blockchain/MNDContract' ;
45import { NDContractAbi } from '@/blockchain/NDContract' ;
56import { ReaderAbi } from '@/blockchain/Reader' ;
67import config , { getCurrentEpoch , getEpochStartTimestamp , getNextEpochTimestamp } from '@/config' ;
@@ -66,10 +67,26 @@ export async function getNodeLicenseDetails(nodeAddress: types.EthAddress): Prom
6667 functionName : 'getNodeLicenseDetails' ,
6768 args : [ nodeAddress ] ,
6869 } )
69- . then ( ( result ) => ( {
70- ...result ,
71- licenseType : [ undefined , 'ND' , 'MND' , 'GND' ] [ result . licenseType ] as 'ND' | 'MND' | 'GND' | undefined ,
72- } ) ) ;
70+ . then ( async ( result ) => {
71+ const licenseType = [ undefined , 'ND' , 'MND' , 'GND' ] [ result . licenseType ] as 'ND' | 'MND' | 'GND' | undefined ;
72+ let firstMiningEpoch : bigint | undefined ;
73+ if ( licenseType === 'MND' ) {
74+ firstMiningEpoch = (
75+ await publicClient . readContract ( {
76+ address : config . mndContractAddress ,
77+ abi : MNDContractAbi ,
78+ functionName : 'licenses' ,
79+ args : [ result . licenseId ] ,
80+ } )
81+ ) [ 3 ] ;
82+ }
83+
84+ return {
85+ ...result ,
86+ licenseType,
87+ firstMiningEpoch,
88+ } ;
89+ } ) ;
7390}
7491
7592export async function getLicense ( licenseType : 'ND' | 'MND' | 'GND' , licenseId : number | string ) : Promise < types . License > {
@@ -109,7 +126,7 @@ export async function getLicense(licenseType: 'ND' | 'MND' | 'GND', licenseId: n
109126 functionName : 'getMndLicenseDetails' ,
110127 args : [ BigInt ( licenseId ) ] ,
111128 } )
112- . then ( ( license ) => {
129+ . then ( async ( license ) => {
113130 const isLinked = ! isZeroAddress ( license . nodeAddress ) ;
114131 const licenseType = [ undefined , 'ND' , 'MND' , 'GND' ] [ license . licenseType ] as 'ND' | 'MND' | 'GND' | undefined ;
115132 if ( licenseType === undefined ) {
@@ -118,11 +135,20 @@ export async function getLicense(licenseType: 'ND' | 'MND' | 'GND', licenseId: n
118135 if ( licenseType !== 'MND' && licenseType !== 'GND' ) {
119136 throw new Error ( 'Invalid license type' ) ;
120137 }
138+ const firstMiningEpoch = (
139+ await publicClient . readContract ( {
140+ address : config . mndContractAddress ,
141+ abi : MNDContractAbi ,
142+ functionName : 'licenses' ,
143+ args : [ BigInt ( licenseId ) ] ,
144+ } )
145+ ) [ 3 ] ;
121146
122147 return {
123148 ...license ,
124149 licenseType,
125150 isLinked,
151+ firstMiningEpoch,
126152 } ;
127153 } ) ;
128154 }
@@ -333,7 +359,7 @@ const getNdLicenseRewards = async (license: types.License, epochs: number[], epo
333359} ;
334360
335361const getMndLicenseRewards = async ( license : types . License , epochs : number [ ] , epochs_vals : number [ ] ) : Promise < bigint > => {
336- return calculateLicenseRewards ( license , epochs , epochs_vals , config . mndVestingEpochs , config . mndCliffEpochs ) ;
362+ return calculateMndLicenseRewards ( license , epochs , epochs_vals ) ;
337363} ;
338364
339365const getGndLicenseRewards = async ( license : types . License , epochs : number [ ] , epochs_vals : number [ ] ) : Promise < bigint > => {
@@ -387,3 +413,62 @@ const calculateLicenseRewards = async (
387413 const maxRemainingClaimAmount = license . totalAssignedAmount - license . totalClaimedAmount ;
388414 return rewards_amount < maxRemainingClaimAmount ? rewards_amount : maxRemainingClaimAmount ;
389415} ;
416+
417+ const calculateMndLicenseRewards = async ( license : types . License , epochs : number [ ] , epochs_vals : number [ ] ) : Promise < bigint > => {
418+ const currentEpoch = getCurrentEpoch ( ) ;
419+ const firstMiningEpoch = license . firstMiningEpoch ;
420+
421+ if ( firstMiningEpoch === undefined ) {
422+ throw new Error ( 'First mining epoch is undefined for MND license' ) ;
423+ }
424+
425+ const firstEpochToClaim =
426+ Number ( license . lastClaimEpoch ) >= Number ( firstMiningEpoch ) ? Number ( license . lastClaimEpoch ) : Number ( firstMiningEpoch ) ;
427+ const epochsToClaim = currentEpoch - firstEpochToClaim ;
428+
429+ if ( currentEpoch < Number ( firstMiningEpoch ) || ! epochsToClaim ) {
430+ return 0n ;
431+ }
432+
433+ if ( epochs . length && epochs [ 0 ] < firstEpochToClaim ) {
434+ const start = firstEpochToClaim - epochs [ 0 ] ;
435+ epochs = epochs . slice ( start ) ;
436+ epochs_vals = epochs_vals . slice ( start ) ;
437+ }
438+
439+ if ( epochsToClaim !== epochs . length || epochsToClaim !== epochs_vals . length ) {
440+ console . error (
441+ `Invalid epochs array length. Received ${ epochs . length } epochs, but there are ${ epochsToClaim } epochs to claim.` ,
442+ ) ;
443+ return 0n ;
444+ }
445+
446+ let rewards_amount = 0n ;
447+ const logisticPlateau = 300_505239501691000000n ; // 300.50
448+ const licensePlateau = ( license . totalAssignedAmount * BigInt ( 1e18 ) ) / logisticPlateau ;
449+
450+ for ( let i = 0 ; i < epochsToClaim ; i ++ ) {
451+ const maxRewardsPerEpoch = calculateMndMaxEpochRelease ( epochs [ i ] , firstMiningEpoch , licensePlateau ) ;
452+ rewards_amount += ( maxRewardsPerEpoch * BigInt ( epochs_vals [ i ] ) ) / 255n ;
453+ }
454+
455+ const maxRemainingClaimAmount = license . totalAssignedAmount - license . totalClaimedAmount ;
456+ return rewards_amount > maxRemainingClaimAmount ? maxRemainingClaimAmount : rewards_amount ;
457+ } ;
458+
459+ const calculateMndMaxEpochRelease = ( epoch : number , firstMiningEpoch : bigint , licensePlateau : bigint ) : bigint => {
460+ let x = epoch - Number ( firstMiningEpoch ) ;
461+ if ( x > config . mndVestingEpochs ) {
462+ x = config . mndVestingEpochs ;
463+ }
464+ const frac = logisticFraction ( x ) ;
465+ return ( licensePlateau * BigInt ( frac * 1e18 ) ) / BigInt ( 1e18 ) ;
466+ } ;
467+
468+ const logisticFraction = ( x : number ) : number => {
469+ const length = config . mndVestingEpochs ;
470+ const k = 5.0 ;
471+ const midPrc = 0.7 ;
472+ const midpoint = length * midPrc ;
473+ return 1.0 / ( 1.0 + Math . exp ( ( - k * ( x - midpoint ) ) / length ) ) ;
474+ } ;
0 commit comments