Skip to content

Commit b1eab32

Browse files
authored
feat: show new MND adoption related stats (#31)
* feat: show new MND adoption related stats * fixes * fix: add awbBalance to LicenseListItem * fixes
1 parent e600e47 commit b1eab32

File tree

13 files changed

+245
-98
lines changed

13 files changed

+245
-98
lines changed

app/account/[ownerEthAddr]/page.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ export default async function NodeOperatorPage({ params }) {
102102
return <NotFound />;
103103
}
104104

105+
const totalCurveReleased = licenses.reduce((acc, license) => acc + BigInt(license.totalClaimedAmount), 0n);
106+
const totalAwbBalance = licenses.reduce((acc, license) => acc + BigInt(license.awbBalance ?? '0'), 0n);
107+
const totalWalletClaimed = totalCurveReleased > totalAwbBalance ? totalCurveReleased - totalAwbBalance : 0n;
108+
const totalAssigned = licenses.reduce((acc, license) => acc + BigInt(license.totalAssignedAmount), 0n);
109+
105110
return (
106111
<div className="responsive-col">
107112
<BorderedCard>
@@ -131,32 +136,30 @@ export default async function NodeOperatorPage({ params }) {
131136

132137
<CardHorizontal
133138
label="Total $R1 Claimed"
134-
value={
135-
<div className="text-primary">
136-
{fBI(
137-
licenses.reduce((acc, license) => acc + BigInt(license.totalClaimedAmount), 0n),
138-
18,
139-
)}
140-
</div>
141-
}
139+
value={<div className="text-primary">{fBI(totalWalletClaimed, 18)}</div>}
142140
isSmall
143141
isFlexible
144142
widthClasses="min-w-[268px]"
145143
/>
146144

145+
{totalAwbBalance > 0n && (
146+
<CardHorizontal
147+
label="Total in AWB"
148+
value={<div className="text-orange-500">{fBI(totalAwbBalance, 18)}</div>}
149+
isSmall
150+
isFlexible
151+
widthClasses="min-w-[268px]"
152+
/>
153+
)}
154+
147155
<CardHorizontal
148156
label="Licenses Usage (Total)"
149157
value={
150158
<div className="w-full min-w-52 xs:min-w-56 md:min-w-60">
151159
<UsageStats
152-
totalClaimedAmount={licenses.reduce(
153-
(acc, license) => acc + BigInt(license.totalClaimedAmount),
154-
0n,
155-
)}
156-
totalAssignedAmount={licenses.reduce(
157-
(acc, license) => acc + BigInt(license.totalAssignedAmount),
158-
0n,
159-
)}
160+
totalClaimedAmount={totalCurveReleased}
161+
totalAssignedAmount={totalAssigned}
162+
awbBalance={totalAwbBalance}
160163
/>
161164
</div>
162165
}

app/node/[nodeAddr]/page.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,10 @@ const fetchLicenseDetailsAndNodeAvailability = async (
8686
nodeResponse: types.OraclesAvailabilityResult & types.OraclesDefaultResult;
8787
}> => {
8888
let nodeAddress: types.EthAddress,
89-
totalAssignedAmount: bigint,
90-
totalClaimedAmount: bigint,
91-
firstMiningEpoch: bigint | undefined,
89+
totalAssignedAmount: bigint,
90+
totalClaimedAmount: bigint,
91+
awbBalance: bigint,
92+
firstMiningEpoch: bigint | undefined,
9293
lastClaimEpoch: bigint,
9394
assignTimestamp: bigint,
9495
lastClaimOracle: types.EthAddress,
@@ -102,9 +103,10 @@ const fetchLicenseDetailsAndNodeAvailability = async (
102103
try {
103104
({
104105
nodeAddress,
105-
totalAssignedAmount,
106-
totalClaimedAmount,
107-
firstMiningEpoch,
106+
totalAssignedAmount,
107+
totalClaimedAmount,
108+
awbBalance,
109+
firstMiningEpoch,
108110
lastClaimEpoch,
109111
assignTimestamp,
110112
lastClaimOracle,
@@ -127,9 +129,10 @@ const fetchLicenseDetailsAndNodeAvailability = async (
127129

128130
const license: types.License = {
129131
nodeAddress,
130-
totalAssignedAmount,
131-
totalClaimedAmount,
132-
firstMiningEpoch,
132+
totalAssignedAmount,
133+
totalClaimedAmount,
134+
awbBalance,
135+
firstMiningEpoch,
133136
lastClaimEpoch,
134137
assignTimestamp,
135138
lastClaimOracle,

app/server-components/Licenses/License.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,17 @@ interface Props {
1717
}
1818

1919
export default function License({ license }: Props) {
20-
const { licenseType, licenseId, owner, nodeAddress, totalAssignedAmount, totalClaimedAmount, assignTimestamp, isBanned } =
21-
license;
20+
const {
21+
licenseType,
22+
licenseId,
23+
owner,
24+
nodeAddress,
25+
totalAssignedAmount,
26+
totalClaimedAmount,
27+
assignTimestamp,
28+
awbBalance,
29+
isBanned,
30+
} = license;
2231

2332
return (
2433
<BorderedCard useCustomWrapper useFixedWidthLarge>
@@ -29,6 +38,7 @@ export default function License({ license }: Props) {
2938
licenseType={licenseType}
3039
totalAssignedAmount={BigInt(totalAssignedAmount)}
3140
totalClaimedAmount={BigInt(totalClaimedAmount)}
41+
awbBalance={BigInt(awbBalance || '0')}
3242
isBanned={isBanned}
3343
isLink
3444
hideType

app/server-components/Licenses/LicenseRewardsPoA.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getLicenseFirstCheckEpoch } from '@/config';
2-
import { getLicenseRewards } from '@/lib/api/blockchain';
2+
import { getLicenseRewardsBreakdown } from '@/lib/api/blockchain';
33
import * as types from '@/typedefs/blockchain';
44
import { formatUnits } from 'viem';
55
import { CardHorizontal } from '../shared/cards/CardHorizontal';
@@ -17,8 +17,6 @@ export default async function LicenseRewardsPoA({
1717
getNodeAvailability: () => Promise<(types.OraclesAvailabilityResult & types.OraclesDefaultResult) | undefined>;
1818
}) {
1919
try {
20-
let rewards: bigint | undefined = 0n;
21-
2220
const nodeResponse: (types.OraclesAvailabilityResult & types.OraclesDefaultResult) | undefined =
2321
await getNodeAvailability();
2422

@@ -29,23 +27,41 @@ export default async function LicenseRewardsPoA({
2927
const firstCheckEpoch: number = getLicenseFirstCheckEpoch(license.assignTimestamp);
3028
const lastClaimEpoch: number = Number(license.lastClaimEpoch);
3129

32-
rewards = await getLicenseRewards(
30+
const rewardsBreakdown = await getLicenseRewardsBreakdown(
3331
license,
3432
licenseType,
3533
BigInt(licenseId),
3634
nodeResponse.epochs.slice(lastClaimEpoch - firstCheckEpoch),
3735
nodeResponse.epochs_vals.slice(lastClaimEpoch - firstCheckEpoch),
3836
);
3937

38+
const rewards = rewardsBreakdown.claimableAmount;
39+
const showMndBreakdown =
40+
licenseType !== 'ND' &&
41+
rewardsBreakdown.claimableAmount !== undefined &&
42+
(rewardsBreakdown.carryoverAmount ?? 0n) > 0n;
43+
4044
return (
4145
<CardHorizontal
42-
label="Claimable PoA rewards:"
46+
label="Rewards (PoA)"
4347
value={
44-
<div className="text-primary">
45-
{rewards === undefined
46-
? '...'
47-
: parseFloat(Number(formatUnits(rewards ?? 0n, 18)).toFixed(2)).toLocaleString()}
48-
{!!rewards ? ' $R1' : ''}
48+
<div className="col items-end gap-1.5">
49+
<div className="text-primary">
50+
{rewards === undefined
51+
? '...'
52+
: parseFloat(Number(formatUnits(rewards ?? 0n, 18)).toFixed(2)).toLocaleString()}
53+
{!!rewards ? ' $R1' : ''}
54+
</div>
55+
56+
{showMndBreakdown && (
57+
<div className="text-xs font-medium text-slate-500">
58+
includes{' '}
59+
{parseFloat(
60+
Number(formatUnits(rewardsBreakdown.carryoverAmount ?? 0n, 18)).toFixed(2),
61+
).toLocaleString()}{' '}
62+
carryover
63+
</div>
64+
)}
4965
</div>
5066
}
5167
widthClasses="min-w-[280px]"

app/server-components/main-cards/CompactLicenseCard.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CardHorizontal } from '@/app/server-components/shared/cards/CardHorizon
33
import ClientWrapper from '@/components/shared/ClientWrapper';
44
import { CopyableAddress } from '@/components/shared/CopyableValue';
55
import { routePath } from '@/lib/routes';
6-
import { isZeroAddress } from '@/lib/utils';
6+
import { fBI, isZeroAddress } from '@/lib/utils';
77
import * as types from '@/typedefs/blockchain';
88
import Link from 'next/link';
99
import PoA from '../Licenses/PoA';
@@ -20,6 +20,7 @@ interface Props {
2020
export default async function CompactLicenseCard({ license, licenseType, licenseId, nodeEthAddress }: Props) {
2121
const totalAssignedAmount = BigInt(license.totalAssignedAmount);
2222
const totalClaimedAmount = BigInt(license.totalClaimedAmount);
23+
const awbBalance = BigInt(license.awbBalance ?? '0');
2324

2425
return (
2526
<BorderedCard>
@@ -45,7 +46,7 @@ export default async function CompactLicenseCard({ license, licenseType, license
4546

4647
{!!license.lastClaimEpoch && (
4748
<CardHorizontal
48-
label="Last claimed epoch"
49+
label="Last claim epoch"
4950
value={license.lastClaimEpoch.toString()}
5051
isSmall
5152
isFlexible
@@ -57,12 +58,28 @@ export default async function CompactLicenseCard({ license, licenseType, license
5758
label="Usage"
5859
value={
5960
<div className="w-full min-w-52 xs:min-w-56 md:min-w-60">
60-
<UsageStats totalClaimedAmount={totalClaimedAmount} totalAssignedAmount={totalAssignedAmount} />
61+
<UsageStats
62+
totalClaimedAmount={totalClaimedAmount}
63+
totalAssignedAmount={totalAssignedAmount}
64+
awbBalance={awbBalance}
65+
/>
6166
</div>
6267
}
6368
isSmall
6469
/>
6570

71+
{licenseType !== 'ND' && (
72+
<>
73+
<CardHorizontal
74+
label="Adoption Withheld Buffer"
75+
value={<div className="text-orange-500">{fBI(awbBalance, 18)} $R1</div>}
76+
isSmall
77+
isFlexible
78+
widthClasses="min-w-[360px]"
79+
/>
80+
</>
81+
)}
82+
6683
<PoA totalAssignedAmount={totalAssignedAmount} totalClaimedAmount={totalClaimedAmount} />
6784

6885
{!isZeroAddress(nodeEthAddress) && (

app/server-components/main-cards/LicenseCard.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ClientWrapper from '@/components/shared/ClientWrapper';
55
import { CopyableAddress } from '@/components/shared/CopyableValue';
66
import config from '@/config';
77
import { routePath } from '@/lib/routes';
8+
import { fBI } from '@/lib/utils';
89
import * as types from '@/typedefs/blockchain';
910
import { Skeleton } from '@heroui/skeleton';
1011
import clsx from 'clsx';
@@ -28,6 +29,7 @@ interface Props {
2829

2930
export default async function LicenseCard({ license, licenseType, licenseId, owner, getNodeAvailability, hasLink }: Props) {
3031
const environment = config.environment;
32+
const awbBalance = license.awbBalance;
3133

3234
const getTitle = () => <CardTitle hasLink={hasLink}>License #{licenseId}</CardTitle>;
3335

@@ -90,7 +92,7 @@ export default async function LicenseCard({ license, licenseType, licenseId, own
9092

9193
{!!license.lastClaimEpoch && (
9294
<CardHorizontal
93-
label="Last claimed epoch"
95+
label="Last claim epoch"
9496
value={license.lastClaimEpoch.toString()}
9597
isSmall
9698
isFlexible
@@ -105,6 +107,7 @@ export default async function LicenseCard({ license, licenseType, licenseId, own
105107
<UsageStats
106108
totalClaimedAmount={license.totalClaimedAmount}
107109
totalAssignedAmount={license.totalAssignedAmount}
110+
awbBalance={license.awbBalance}
108111
/>
109112
</div>
110113
}
@@ -114,6 +117,16 @@ export default async function LicenseCard({ license, licenseType, licenseId, own
114117

115118
<PoA totalAssignedAmount={license.totalAssignedAmount} totalClaimedAmount={license.totalClaimedAmount} />
116119

120+
{licenseType !== 'ND' && (
121+
<CardHorizontal
122+
label="Adoption Withheld Buffer"
123+
value={<div className="text-orange-500">{fBI(awbBalance, 18)} $R1</div>}
124+
isSmall
125+
isFlexible
126+
widthClasses="min-w-[360px]"
127+
/>
128+
)}
129+
117130
<Suspense fallback={<Skeleton className="min-h-[76px] w-full rounded-xl md:max-w-[258px]" />}>
118131
<LicenseRewardsPoA
119132
license={license}
@@ -125,7 +138,7 @@ export default async function LicenseCard({ license, licenseType, licenseId, own
125138

126139
{licenseType === 'ND' && (
127140
<CardHorizontal
128-
label="Claimable PoAI rewards:"
141+
label="Rewards (PoAI)"
129142
value={
130143
<div className="text-primary">
131144
{license.r1PoaiRewards === undefined

app/server-components/shared/Licenses/LicenseSmallCard.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface Props {
1010
licenseType: 'ND' | 'MND' | 'GND' | undefined;
1111
totalAssignedAmount: bigint | undefined;
1212
totalClaimedAmount: bigint;
13+
awbBalance?: bigint;
1314
isBanned: boolean;
1415
isLink?: boolean;
1516
hideType?: boolean;
@@ -20,6 +21,7 @@ export default async function LicenseSmallCard({
2021
licenseType,
2122
totalClaimedAmount,
2223
totalAssignedAmount,
24+
awbBalance,
2325
isBanned,
2426
isLink,
2527
hideType,
@@ -43,7 +45,11 @@ export default async function LicenseSmallCard({
4345
</div>
4446

4547
<div className="w-52">
46-
<UsageStats totalClaimedAmount={totalClaimedAmount} totalAssignedAmount={totalAssignedAmount} />
48+
<UsageStats
49+
totalClaimedAmount={totalClaimedAmount}
50+
totalAssignedAmount={totalAssignedAmount}
51+
awbBalance={awbBalance}
52+
/>
4753
</div>
4854
</div>
4955
</SmallCard>

app/server-components/shared/Licenses/UsageStats.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,33 @@ import { fBI } from '@/lib/utils';
44
export default async function UsageStats({
55
totalClaimedAmount,
66
totalAssignedAmount,
7+
awbBalance = 0n,
78
}: {
89
totalClaimedAmount: bigint;
910
totalAssignedAmount?: bigint;
11+
awbBalance?: bigint;
1012
}) {
1113
if (!totalAssignedAmount) {
1214
return null;
1315
}
1416

17+
const denominator = totalAssignedAmount || config.ndLicenseCap;
18+
const walletClaimedAmount = totalClaimedAmount > awbBalance ? totalClaimedAmount - awbBalance : 0n;
19+
const walletClaimedPercentage = Number((walletClaimedAmount * 100n) / denominator);
20+
const awbPercentage = Number((awbBalance * 100n) / denominator);
21+
const walletClaimedPercentageScaled = (walletClaimedAmount * 10_000n) / denominator; // percentage with 2 decimal places, scaled by 100
22+
const walletClaimedPercentageLabel = Number(walletClaimedPercentageScaled) / 100;
23+
1524
return (
1625
<div className="row w-full gap-2.5 text-sm font-medium leading-none">
17-
<div>
18-
{fBI(totalClaimedAmount, 18)}/{fBI(totalAssignedAmount || config.ndLicenseCap, 18)}
19-
</div>
26+
<div>{`${fBI(walletClaimedAmount, 18)}/${fBI(denominator, 18)}`}</div>
2027

2128
<div className="flex h-1 w-full overflow-hidden rounded-full bg-gray-300">
22-
<div
23-
className="rounded-full bg-primary transition-all"
24-
style={{ width: `${Number((totalClaimedAmount * 100n) / (totalAssignedAmount || config.ndLicenseCap))}%` }}
25-
></div>
29+
<div className="rounded-full bg-primary transition-all" style={{ width: `${walletClaimedPercentage}%` }}></div>
30+
{awbBalance > 0n && <div className="bg-orange-500 transition-all" style={{ width: `${awbPercentage}%` }}></div>}
2631
</div>
2732

28-
<div>
29-
{parseFloat(
30-
((Number(totalClaimedAmount) / Number(totalAssignedAmount || config.ndLicenseCap)) * 100).toFixed(2),
31-
)}
32-
%
33-
</div>
33+
<div>{walletClaimedPercentageLabel}%</div>
3434
</div>
3535
);
3636
}

blockchain/Reader.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,11 @@ export const ReaderAbi = [
365365
name: 'assignTimestamp',
366366
type: 'uint256',
367367
},
368+
{
369+
internalType: 'uint256',
370+
name: 'awbBalance',
371+
type: 'uint256',
372+
},
368373
{
369374
internalType: 'bool',
370375
name: 'isBanned',

0 commit comments

Comments
 (0)