11import chalk from "chalk"
22import { RelayerSummary , CycleResult } from "./types"
33
4- const W = 66
5-
64function formatB3TR ( wei : bigint ) : string {
75 const whole = wei / 10n ** 18n
86 const frac = ( wei % 10n ** 18n ) / 10n ** 16n
@@ -24,86 +22,100 @@ function pct(num: bigint, den: bigint): string {
2422 return ( ( Number ( num ) / Number ( den ) ) * 100 ) . toFixed ( 2 ) + "%"
2523}
2624
27- function pad ( left : string , right : string , width : number = W - 4 ) : string {
28- const gap = width - left . length - right . length
25+ function stripAnsi ( str : string ) : number {
26+ return str . replace ( / \x1b \[ [ 0 - 9 ; ] * m / g, "" ) . length
27+ }
28+
29+ function pad ( left : string , right : string , width : number = 62 ) : string {
30+ const gap = width - stripAnsi ( left ) - stripAnsi ( right )
2931 return left + " " . repeat ( Math . max ( 1 , gap ) ) + right
3032}
3133
32- function line ( content : string ) : string {
33- const inner = content . padEnd ( W - 4 )
34- return `║ ${ inner } ║`
34+ function heading ( text : string ) : string {
35+ return chalk . bold . cyan ( text )
3536}
3637
37- function sep ( ) : string {
38- return "╠" + "═" . repeat ( W - 2 ) + "╣"
38+ function label ( text : string ) : string {
39+ return chalk . dim ( text )
3940}
4041
4142export function renderSummary ( s : RelayerSummary ) : string {
4243 const out : string [ ] = [ ]
43- const top = "╔" + "═" . repeat ( W - 2 ) + "╗"
44- const bot = "╚" + "═" . repeat ( W - 2 ) + "╝"
45-
46- const title = "VeBetterDAO Relayer Node"
47- const titlePad = Math . floor ( ( W - 4 - title . length ) / 2 )
48-
49- out . push ( top )
50- out . push ( line ( " " . repeat ( titlePad ) + chalk . bold . cyan ( title ) ) )
51- out . push ( sep ( ) )
52-
53- const status = s . isRegistered ? chalk . green ( "✓ Registered" ) : chalk . red ( "✗ Not registered" )
54- out . push ( line ( pad ( "Network " + chalk . white ( s . network ) , "Block " + chalk . white ( s . latestBlock . toLocaleString ( ) ) ) ) )
55- out . push ( line ( pad ( "Node " + chalk . gray ( new URL ( s . nodeUrl ) . hostname ) , "" ) ) )
56- out . push ( line ( pad ( "Address " + chalk . yellow ( shortAddr ( s . relayerAddress ) ) , status ) ) )
57-
58- out . push ( sep ( ) )
59-
60- const roundStatus = s . isRoundActive ? chalk . green ( "● Active" ) : chalk . gray ( "○ Ended" )
61- out . push ( line ( chalk . bold ( `ROUND #${ s . currentRoundId } ` ) + " " + roundStatus ) )
62- out . push ( line ( pad ( `Snapshot ${ s . roundSnapshot } ` , `Deadline ${ s . roundDeadline } ` ) ) )
63- out . push ( line ( pad ( `Auto-voters ${ chalk . white ( s . autoVotingUsers . toString ( ) ) } ` , `Relayers ${ chalk . white ( s . registeredRelayers . length . toString ( ) ) } ` ) ) )
64- out . push ( line ( pad ( `Voters ${ chalk . white ( s . totalVoters . toString ( ) ) } ` , `Total VOT3 ${ chalk . white ( formatVOT3 ( s . totalVotes ) ) } ` ) ) )
65-
66- out . push ( sep ( ) )
6744
45+ out . push ( "" )
46+ out . push ( heading ( " VeBetterDAO Relayer Node" ) )
47+ out . push ( chalk . dim ( " " + "─" . repeat ( 60 ) ) )
48+ out . push ( "" )
49+
50+ // Node info
51+ const regStatus = s . isRegistered ? chalk . green ( "Registered" ) : chalk . red ( "Not registered" )
52+ out . push ( " " + pad ( label ( "Network" ) + " " + chalk . white . bold ( s . network ) , label ( "Block" ) + " " + chalk . white ( s . latestBlock . toLocaleString ( ) ) ) )
53+ out . push ( " " + pad ( label ( "Node" ) + " " + chalk . gray ( new URL ( s . nodeUrl ) . hostname ) , "" ) )
54+ out . push ( " " + pad ( label ( "Address" ) + " " + chalk . yellow ( shortAddr ( s . relayerAddress ) ) , regStatus ) )
55+
56+ out . push ( "" )
57+ out . push ( chalk . dim ( " " + "─" . repeat ( 60 ) ) )
58+ out . push ( "" )
59+
60+ // Round info
61+ const roundStatus = s . isRoundActive ? chalk . green ( "Active" ) : chalk . dim ( "Ended" )
62+ out . push ( " " + heading ( `Round #${ s . currentRoundId } ` ) + " " + roundStatus )
63+ out . push ( " " + pad ( label ( "Snapshot" ) + " " + chalk . white ( s . roundSnapshot . toString ( ) ) , label ( "Deadline" ) + " " + chalk . white ( s . roundDeadline . toString ( ) ) ) )
64+ out . push ( " " + pad ( label ( "Auto-voters" ) + " " + chalk . white . bold ( s . autoVotingUsers . toString ( ) ) , label ( "Relayers" ) + " " + chalk . white . bold ( s . registeredRelayers . length . toString ( ) ) ) )
65+ out . push ( " " + pad ( label ( "Voters" ) + " " + chalk . white ( s . totalVoters . toString ( ) ) , label ( "Total" ) + " " + chalk . cyan ( formatVOT3 ( s . totalVotes ) ) ) )
66+
67+ out . push ( "" )
68+ out . push ( chalk . dim ( " " + "─" . repeat ( 60 ) ) )
69+ out . push ( "" )
70+
71+ // Fee config
6872 const feeStr = s . feeDenominator > 0n ? pct ( s . feePercentage , s . feeDenominator ) : "—"
69- out . push ( line ( pad ( ` Vote Wt ${ s . voteWeight } ` , ` Claim Wt ${ s . claimWeight } ` ) ) )
70- out . push ( line ( pad ( ` Fee ${ feeStr } ` , `Cap ${ formatB3TR ( s . feeCap ) } ` ) ) )
71- out . push ( line ( pad ( ` Early Access ${ s . earlyAccessBlocks } blocks` , "" ) ) )
73+ out . push ( " " + pad ( label ( " Vote Weight" ) + " " + chalk . white . bold ( s . voteWeight . toString ( ) ) , label ( " Claim Weight" ) + " " + chalk . white . bold ( s . claimWeight . toString ( ) ) ) )
74+ out . push ( " " + pad ( label ( " Fee" ) + " " + chalk . yellow ( feeStr ) , label ( "Cap" ) + " " + chalk . yellow ( formatB3TR ( s . feeCap ) ) ) )
75+ out . push ( " " + pad ( label ( " Early Access" ) + " " + chalk . white ( s . earlyAccessBlocks . toString ( ) ) + chalk . dim ( " blocks" ) , "" ) )
7276
73- out . push ( sep ( ) )
74- out . push ( line ( chalk . bold ( "THIS ROUND" ) ) )
77+ out . push ( "" )
78+ out . push ( chalk . dim ( " " + "─" . repeat ( 60 ) ) )
79+ out . push ( "" )
7580
81+ // This round stats
82+ out . push ( " " + heading ( "This Round" ) )
7683 const completionPct = s . currentTotalWeighted > 0n
7784 ? pct ( s . currentCompletedWeighted , s . currentTotalWeighted )
7885 : "—"
79- out . push ( line ( pad (
80- `Completion ${ completionPct } ` ,
81- `Missed ${ s . currentMissedUsers } ` ,
82- ) ) )
83- out . push ( line ( pad (
84- `Pool ${ chalk . green ( formatB3TR ( s . currentTotalRewards ) ) } ` ,
85- `Your share ${ chalk . green ( formatB3TR ( s . currentRelayerClaimable ) ) } ` ,
86- ) ) )
87- out . push ( line ( pad (
88- `Actions ${ s . currentRelayerActions } (wt: ${ s . currentRelayerWeighted } )` ,
89- `Total acts ${ s . currentTotalActions } ` ,
90- ) ) )
91-
86+ const completionColor = s . currentTotalWeighted > 0n && s . currentCompletedWeighted >= s . currentTotalWeighted
87+ ? chalk . green : chalk . yellow
88+ out . push ( " " + pad (
89+ label ( "Completion" ) + " " + completionColor ( completionPct ) ,
90+ label ( "Missed" ) + " " + ( s . currentMissedUsers > 0n ? chalk . red ( s . currentMissedUsers . toString ( ) ) : chalk . green ( s . currentMissedUsers . toString ( ) ) ) ,
91+ ) )
92+ out . push ( " " + pad (
93+ label ( "Pool" ) + " " + chalk . green ( formatB3TR ( s . currentTotalRewards ) ) ,
94+ label ( "Your share" ) + " " + chalk . greenBright . bold ( formatB3TR ( s . currentRelayerClaimable ) ) ,
95+ ) )
96+ out . push ( " " + pad (
97+ label ( "Actions" ) + " " + chalk . white ( s . currentRelayerActions . toString ( ) ) + chalk . dim ( " (wt: " ) + chalk . white ( s . currentRelayerWeighted . toString ( ) ) + chalk . dim ( ")" ) ,
98+ label ( "Total" ) + " " + chalk . white ( s . currentTotalActions . toString ( ) ) ,
99+ ) )
100+
101+ // Previous round
92102 if ( s . previousRoundId > 0 ) {
93- out . push ( line ( "" ) )
94- out . push ( line ( chalk . bold ( `PREVIOUS ROUND #${ s . previousRoundId } ` ) ) )
95- const claimStatus = s . previousRewardClaimable ? chalk . green ( "✓ Claimable" ) : chalk . gray ( "✗ Not yet" )
96- out . push ( line ( pad (
97- `Pool ${ chalk . green ( formatB3TR ( s . previousTotalRewards ) ) } ` ,
98- `Your share ${ chalk . green ( formatB3TR ( s . previousRelayerClaimable ) ) } ` ,
99- ) ) )
100- out . push ( line ( pad (
101- `Actions ${ s . previousRelayerActions } ` ,
103+ out . push ( "" )
104+ out . push ( chalk . dim ( " " + "─" . repeat ( 60 ) ) )
105+ out . push ( "" )
106+ const claimStatus = s . previousRewardClaimable ? chalk . green ( "Claimable" ) : chalk . dim ( "Not yet" )
107+ out . push ( " " + heading ( `Previous Round #${ s . previousRoundId } ` ) )
108+ out . push ( " " + pad (
109+ label ( "Pool" ) + " " + chalk . green ( formatB3TR ( s . previousTotalRewards ) ) ,
110+ label ( "Your share" ) + " " + chalk . greenBright . bold ( formatB3TR ( s . previousRelayerClaimable ) ) ,
111+ ) )
112+ out . push ( " " + pad (
113+ label ( "Actions" ) + " " + chalk . white ( s . previousRelayerActions . toString ( ) ) ,
102114 claimStatus ,
103- ) ) )
115+ ) )
104116 }
105117
106- out . push ( bot )
118+ out . push ( "" )
107119 return out . join ( "\n" )
108120}
109121
@@ -113,11 +125,14 @@ export function renderCycleResult(r: CycleResult): string[] {
113125 const dryTag = r . dryRun ? chalk . yellow ( " (DRY RUN)" ) : ""
114126
115127 if ( r . totalUsers === 0 ) {
116- lines . push ( `${ label } round #${ r . roundId } : no users${ dryTag } ` )
128+ lines . push ( `${ label } round #${ r . roundId } : ${ chalk . dim ( " no users" ) } ${ dryTag } ` )
117129 return lines
118130 }
119131
120- lines . push ( `${ label } round #${ r . roundId } : ${ chalk . green ( r . successful . toString ( ) ) } /${ r . totalUsers } successful${ dryTag } ` )
132+ const ratio = r . successful === r . totalUsers
133+ ? chalk . green . bold ( `${ r . successful } /${ r . totalUsers } ` )
134+ : chalk . yellow ( `${ r . successful } /${ r . totalUsers } ` )
135+ lines . push ( `${ label } round #${ r . roundId } : ${ ratio } successful${ dryTag } ` )
121136
122137 if ( r . failed . length > 0 )
123138 lines . push ( chalk . red ( ` ${ r . failed . length } failed` ) + chalk . gray ( ` (${ r . failed . slice ( 0 , 3 ) . map ( ( f ) => shortAddr ( f . user ) ) . join ( ", " ) } ${ r . failed . length > 3 ? "..." : "" } )` ) )
0 commit comments