@@ -18,14 +18,59 @@ import {
1818 getPaymentStatus ,
1919} from '../core/payments/index.js'
2020import { cleanupSynapseService , initializeSynapse } from '../core/synapse/index.js'
21- import { formatUSDFC } from '../core/utils/format.js'
21+ import { formatFIL , formatUSDFC } from '../core/utils/format.js'
2222import { formatRunwaySummary } from '../core/utils/index.js'
2323import { type CLIAuthOptions , getCLILogger , parseCLIAuth } from '../utils/cli-auth.js'
2424import { cancel , createSpinner , intro , outro } from '../utils/cli-helpers.js'
2525import { log } from '../utils/cli-logger.js'
2626import { displayDepositWarning } from './setup.js'
2727
28- interface StatusOptions extends CLIAuthOptions { }
28+ interface StatusOptions extends CLIAuthOptions {
29+ includeRails ?: boolean
30+ }
31+
32+ const STORAGE_DISPLAY_PRECISION_DIGITS = 6
33+ const STORAGE_DISPLAY_PRECISION = 10n ** BigInt ( STORAGE_DISPLAY_PRECISION_DIGITS )
34+
35+ /**
36+ * Derives the current warm storage size from the Filecoin Pay spend rate.
37+ *
38+ * rateUsed represents the USDFC burn per epoch while pricePerTiBPerEpoch
39+ * represents the quoted USDFC price for storing 1 TiB for the same duration.
40+ * Dividing the two gives the actively billed TiB, which we convert to GiB for
41+ * display with a small fixed precision.
42+ */
43+ function formatStorageGiB ( rateUsed : bigint , pricePerTiBPerEpoch : bigint ) : string {
44+ if ( rateUsed === 0n || pricePerTiBPerEpoch === 0n ) {
45+ return pc . gray ( 'Stored: no active usage' )
46+ }
47+
48+ if ( pricePerTiBPerEpoch < 0n ) {
49+ // pricePerTiBPerEpoch should always be positive
50+ return pc . gray ( 'Stored: unknown' )
51+ }
52+
53+ const storedTiBScaled = ( rateUsed * STORAGE_DISPLAY_PRECISION ) / pricePerTiBPerEpoch
54+ if ( storedTiBScaled <= 0n ) {
55+ return pc . gray ( 'Stored: no active usage' )
56+ }
57+
58+ const storedGiB = Number ( ethers . formatUnits ( storedTiBScaled * 1024n , STORAGE_DISPLAY_PRECISION_DIGITS ) )
59+
60+ if ( storedGiB < 0.1 ) {
61+ return 'Stored: < 0.1 GiB'
62+ }
63+
64+ let digits = 1
65+ if ( storedGiB < 10 ) {
66+ digits = 2
67+ }
68+ const formatted = storedGiB . toLocaleString ( undefined , {
69+ maximumFractionDigits : digits ,
70+ minimumFractionDigits : digits ,
71+ } )
72+ return `Stored: ~${ formatted } GiB`
73+ }
2974
3075/**
3176 * Display current payment status
@@ -104,52 +149,72 @@ export async function showPaymentStatus(options: StatusOptions): Promise<void> {
104149 const storageInfo = await synapse . storage . getStorageInfo ( )
105150 const pricePerTiBPerEpoch = storageInfo . pricing . noCDN . perTiBPerEpoch
106151
107- const paymentRailsData = await fetchPaymentRailsData ( synapse )
152+ let paymentRailsData : PaymentRailsData | null = null
153+ if ( options . includeRails === true ) {
154+ paymentRailsData = await fetchPaymentRailsData ( synapse )
155+ }
108156 spinner . stop ( `${ pc . green ( '✓' ) } Configuration loaded` )
109157
110158 // Display all status information
111159 log . line ( '━━━ Current Status ━━━' )
112160
113- log . line ( `Address: ${ address } ` )
114- log . line ( `Network: ${ pc . bold ( network ) } ` )
115- log . line ( '' )
116-
117161 // Show wallet balances
118162 log . line ( pc . bold ( 'Wallet' ) )
119- const filUnit = filStatus . isCalibnet ? 'tFIL' : 'FIL'
120- log . indent ( `${ ethers . formatEther ( filStatus . balance ) } ${ filUnit } ` )
121- log . indent ( `${ formatUSDFC ( walletUsdfcBalance ) } USDFC` )
163+ log . indent ( `Owner address: ${ address } ` )
164+ log . indent ( `Network: ${ network } ` )
165+ log . indent ( `FIL: ${ formatFIL ( filStatus . balance , filStatus . isCalibnet ) } ` )
166+ log . indent ( `USDFC: ${ formatUSDFC ( walletUsdfcBalance ) } USDFC` )
122167 log . line ( '' )
123168
124169 // Show deposit and capacity
170+ const lockupUsed = status . currentAllowances . lockupUsed ?? 0n
171+ const rateUsed = status . currentAllowances . rateUsed ?? 0n
172+ const availableDeposit = status . filecoinPayBalance > lockupUsed ? status . filecoinPayBalance - lockupUsed : 0n
125173 const capacity = calculateDepositCapacity ( status . filecoinPayBalance , pricePerTiBPerEpoch )
126- log . line ( pc . bold ( 'Storage Deposit' ) )
127- log . indent ( `${ formatUSDFC ( status . filecoinPayBalance ) } USDFC deposited` )
128- if ( capacity . gibPerMonth > 0 ) {
129- const asTiB = capacity . tibPerMonth
130- const tibStr = asTiB >= 100 ? Math . round ( asTiB ) . toLocaleString ( ) : asTiB . toFixed ( 1 )
131- log . indent ( `Capacity: ~${ tibStr } TiB/month ${ pc . gray ( '(includes 10-day safety reserve)' ) } ` )
132- } else if ( status . filecoinPayBalance > 0n ) {
133- log . indent ( pc . gray ( '(insufficient for storage)' ) )
134- }
135- log . flush ( )
136-
137- // Show payment rails summary
138- displayPaymentRailsSummary ( paymentRailsData , log )
139-
140- // Show spend summaries (rateUsed, runway)
141174 const runway = calculateStorageRunway ( status )
142175 const runwayDisplay = formatRunwaySummary ( runway )
143- const maxLockup = status . currentAllowances . maxLockupPeriod
144- const lockupDays = maxLockup != null ? Number ( maxLockup / TIME_CONSTANTS . EPOCHS_PER_DAY ) : 10
176+ const dailyCost = runway . perDay
177+ const monthlyCost = dailyCost * TIME_CONSTANTS . DAYS_PER_MONTH
178+
179+ log . line ( pc . bold ( 'Filecoin Pay' ) )
180+ log . indent ( `Balance: ${ formatUSDFC ( status . filecoinPayBalance ) } USDFC` )
181+ log . indent ( `Locked: ${ formatUSDFC ( lockupUsed ) } USDFC (30-day reserve)` )
182+ log . indent ( `Available: ${ formatUSDFC ( availableDeposit ) } USDFC` )
183+ if ( rateUsed > 0n ) {
184+ log . indent ( `Epoch cost: ${ formatUSDFC ( rateUsed ) } USDFC` )
185+ log . indent ( `Daily cost: ${ formatUSDFC ( dailyCost ) } USDFC` )
186+ log . indent ( `Monthly cost: ${ formatUSDFC ( monthlyCost ) } USDFC` )
187+ } else {
188+ log . indent ( `Epoch cost: ${ pc . gray ( '0 USDFC' ) } ` )
189+ log . indent ( `Daily cost: ${ pc . gray ( '0 USDFC' ) } ` )
190+ log . indent ( `Monthly cost: ${ pc . gray ( '0 USDFC' ) } ` )
191+ }
192+ if ( paymentRailsData != null ) {
193+ displayPaymentRailsSummary ( paymentRailsData , 1 )
194+ }
195+ log . line ( '' )
145196
197+ // Show storage usage details
146198 log . line ( pc . bold ( 'WarmStorage Usage' ) )
199+ if ( rateUsed > 0n ) {
200+ log . indent ( formatStorageGiB ( rateUsed , pricePerTiBPerEpoch ) )
201+ } else if ( status . filecoinPayBalance > 0n ) {
202+ log . indent ( pc . gray ( 'Stored: no active usage' ) )
203+ } else {
204+ log . indent ( pc . gray ( 'Stored: none' ) )
205+ }
147206 if ( runway . state === 'active' ) {
148- log . indent ( `Spend rate: ${ formatUSDFC ( runway . rateUsed ) } USDFC/epoch` )
149- log . indent ( `Locked: ${ formatUSDFC ( runway . lockupUsed ) } USDFC (~${ lockupDays } -day reserve)` )
150207 log . indent ( `Runway: ~${ runwayDisplay } ` )
151208 } else {
152- log . indent ( pc . gray ( runwayDisplay ) )
209+ log . indent ( pc . gray ( `Runway: ${ runwayDisplay } ` ) )
210+ }
211+ const capacityTiB =
212+ capacity . tibPerMonth >= 100 ? Math . round ( capacity . tibPerMonth ) . toLocaleString ( ) : capacity . tibPerMonth . toFixed ( 1 )
213+ const capacityLine = `Funding could cover ~${ capacityTiB } TiB for one month`
214+ if ( capacity . gibPerMonth > 0 ) {
215+ log . indent ( capacityLine )
216+ } else {
217+ log . indent ( pc . gray ( capacityLine ) )
153218 }
154219 log . flush ( )
155220
@@ -251,38 +316,26 @@ async function fetchPaymentRailsData(synapse: Synapse): Promise<PaymentRailsData
251316/**
252317 * Display payment rails summary
253318 */
254- function displayPaymentRailsSummary ( data : PaymentRailsData , log : any ) : void {
255- log . line ( pc . bold ( 'Payment Rails' ) )
319+ function displayPaymentRailsSummary ( data : PaymentRailsData , indentLevel : number = 1 ) : void {
320+ log . indent ( pc . bold ( 'Payment Rails' ) , indentLevel )
256321
257322 if ( data . error ) {
258- log . indent ( pc . gray ( data . error ) )
259- log . flush ( )
323+ log . indent ( pc . gray ( data . error ) , indentLevel + 1 )
260324 return
261325 }
262326
263327 if ( data . activeRails === 0 && data . terminatedRails === 0 ) {
264- log . indent ( pc . gray ( 'No active payment rails' ) )
265- log . flush ( )
328+ log . indent ( pc . gray ( 'No active payment rails' ) , indentLevel + 1 )
266329 return
267330 }
268331
269- log . indent ( `${ data . activeRails } active, ${ data . terminatedRails } terminated` )
270-
271- if ( data . activeRails > 0 ) {
272- const dailyCost = data . totalActiveRate * 2880n // 2880 epochs per day
273- const monthlyCost = dailyCost * 30n
274-
275- log . indent ( `Daily cost: ${ formatUSDFC ( dailyCost ) } USDFC` )
276- log . indent ( `Monthly cost: ${ formatUSDFC ( monthlyCost ) } USDFC` )
277- }
332+ log . indent ( `${ data . activeRails } active, ${ data . terminatedRails } terminated` , indentLevel + 1 )
278333
279334 if ( data . totalPendingSettlements > 0n ) {
280- log . indent ( `Pending settlement: ${ formatUSDFC ( data . totalPendingSettlements ) } USDFC` )
335+ log . indent ( `Pending settlement: ${ formatUSDFC ( data . totalPendingSettlements ) } USDFC` , indentLevel + 1 )
281336 }
282337
283338 if ( data . railsNeedingSettlement > 0 ) {
284- log . indent ( `${ data . railsNeedingSettlement } rail(s) need settlement` )
339+ log . indent ( `${ data . railsNeedingSettlement } rail(s) need settlement` , indentLevel + 1 )
285340 }
286-
287- log . flush ( )
288341}
0 commit comments