UNPKG

filecoin-pin

Version:

Bridge IPFS content to Filecoin Onchain Cloud using familiar tools

260 lines 11.2 kB
/** * Payment setup utilities and display functions * * This module provides UI utilities and display functions for payment setup, * building on the shared payment operations exposed from `src/core/payments`. */ import { TIME_CONSTANTS } from '@filoz/synapse-sdk'; import { ethers } from 'ethers'; import pc from 'picocolors'; import { calculateActualCapacity, DEFAULT_LOCKUP_DAYS, getStorageScale, USDFC_DECIMALS, } from '../core/payments/index.js'; import { formatFIL, formatUSDFC } from '../core/utils/format.js'; import { log } from '../utils/cli-logger.js'; /** * Parse storage allowance string * * Parses different storage allowance formats: * - "1TiB/month" or "500GiB/month" - Human-friendly storage units * - "0.0000565" - Direct USDFC per epoch (returns null, needs price lookup) * * @param input - Storage allowance string * @returns Parsed TiB per month or null if it's a direct USDFC amount */ export function parseStorageAllowance(input) { // Check if input is a storage unit (e.g., "1TiB/month", "500GiB/month") const storageMatch = input.match(/^(\d+(?:\.\d+)?)\s*(TiB|GiB|MiB)\/month$/i); if (storageMatch?.[1] && storageMatch[2]) { const amount = parseFloat(storageMatch[1]); const unit = storageMatch[2].toUpperCase(); // Convert to TiB let tibPerMonth; switch (unit) { case 'TIB': tibPerMonth = amount; break; case 'GIB': tibPerMonth = amount / 1024; break; case 'MIB': tibPerMonth = amount / (1024 * 1024); break; default: throw new Error(`Unknown storage unit: ${unit}`); } return tibPerMonth; } // Validate that it's a valid number for USDFC per epoch try { ethers.parseUnits(input, USDFC_DECIMALS); return null; // Valid USDFC amount, but need pricing to convert to TiB } catch { throw new Error(`Invalid storage allowance format: ${input}. Use "1TiB/month", "500GiB/month", or a decimal number for USDFC per epoch (e.g., "0.0000565")`); } } /** * Display the payment status summary * * Shows three sections: Wallet, Filecoin Pay Deposit, and WarmStorage Service Permissions * * @param network - Network name * @param filBalance - FIL balance in wei * @param isCalibnet - Whether this is calibnet testnet * @param walletUsdfcBalance - USDFC balance in wei * @param filecoinPayBalance - Amount deposited in Filecoin Pay * @param rateAllowance - Maximum rate per epoch * @param lockupAllowance - Maximum lockup amount * @param pricePerTiBPerEpoch - Current storage price */ export function displayPaymentSummary(network, filBalance, isCalibnet, walletUsdfcBalance, filecoinPayBalance, rateAllowance, lockupAllowance, pricePerTiBPerEpoch) { // Start the summary section (Setup Complete is shown by spinner in auto mode) log.line(`Network: ${pc.bold(network)}`); log.line(''); // Section 1: Wallet log.line(pc.bold('Wallet')); log.indent(formatFIL(filBalance, isCalibnet)); log.indent(`${formatUSDFC(walletUsdfcBalance)} USDFC`); log.line(''); // Section 2: Filecoin Pay deposit log.line(pc.bold('Filecoin Pay Deposit')); log.indent(`${formatUSDFC(filecoinPayBalance)} USDFC`); log.indent(pc.gray('(spendable on any service)')); // Section 3: WarmStorage service permissions log.line(''); if (rateAllowance > 0n) { const monthlyRate = rateAllowance * TIME_CONSTANTS.EPOCHS_PER_MONTH; displayServicePermissions('Your WarmStorage Service Limits', monthlyRate, lockupAllowance, filecoinPayBalance, pricePerTiBPerEpoch, false); } else { log.line(pc.bold('Your WarmStorage Service Limits')); log.indent(pc.gray('No limits set')); } log.flush(); // Flush everything at the end } /** * Display account and balance information * * @param address - Wallet address * @param network - Network name (mainnet/calibration) * @param filBalance - FIL balance in wei * @param isCalibnet - Whether on calibration testnet * @param hasSufficientGas - Whether wallet has enough FIL for gas * @param walletUsdfcBalance - USDFC balance in wei * @param filecoinPayBalance - Amount deposited to Filecoin Pay */ export function displayAccountInfo(address, network, filBalance, isCalibnet, _hasSufficientGas, walletUsdfcBalance, filecoinPayBalance) { log.line(pc.bold('Account:')); log.indent(pc.gray(`Wallet: ${address}`)); log.indent(pc.gray(`Network: ${network}`)); log.line(pc.bold('Balances:')); log.indent(pc.gray(`FIL: ${formatFIL(filBalance, isCalibnet)}`)); log.indent(pc.gray(`USDFC wallet: ${formatUSDFC(walletUsdfcBalance)} USDFC`)); log.indent(pc.gray(`USDFC deposited: ${formatUSDFC(filecoinPayBalance)} USDFC`)); log.flush(); } /** * Display deposit warning if balance is too low for active storage * * Warns when the available deposit (total deposit minus locked amount) * is insufficient to maintain active storage operations. * * @param filecoinPayBalance - Current deposit balance * @param lockupUsed - Amount currently locked for active storage */ export function displayDepositWarning(filecoinPayBalance, lockupUsed) { if (lockupUsed > 0n) { // Calculate available deposit after accounting for locked funds const availableDeposit = filecoinPayBalance - lockupUsed; // Warn if available deposit is too low (less than 10% of lockup as safety margin) const safetyMargin = lockupUsed / 10n; // 10% safety margin if (availableDeposit < safetyMargin) { const needed = lockupUsed + safetyMargin - filecoinPayBalance; log.newline(); log.message(pc.yellow(`⚠ Warning: Low deposit balance`)); log.indent(pc.yellow(`Your deposit: ${formatUSDFC(filecoinPayBalance)} USDFC`)); log.indent(pc.yellow(`Amount locked: ${formatUSDFC(lockupUsed)} USDFC`)); log.indent(pc.yellow(`Available: ${formatUSDFC(availableDeposit > 0n ? availableDeposit : 0n)} USDFC`)); log.indent(pc.yellow(`Deposit at least ${formatUSDFC(needed)} more USDFC to maintain safety margin`)); log.indent(pc.gray(`Without sufficient deposit, storage may be terminated`)); } } } /** * Format storage capacity with smart unit selection * * @param gib - Capacity in GiB * @returns Formatted string with appropriate unit */ function formatStorageCapacity(gib) { if (gib >= 1024) { const tib = gib / 1024; // Use 1 decimal place if under 100 TiB if (tib < 100) { return `${tib.toFixed(1)} TiB/month`; } return `${Math.round(tib).toLocaleString()} TiB/month`; } // Use GiB between 1 GiB and 1 TiB if (gib >= 0.9) { if (gib < 100) { return `${gib.toFixed(1)} GiB/month`; } return `${Math.round(gib).toLocaleString()} GiB/month`; } // For sub‑1 GiB, show MiB to avoid "0.0 GiB" const mib = gib * 1024; if (mib < 10) { return `${mib.toFixed(1)} MiB/month`; } return `${Math.round(mib).toLocaleString()} MiB/month`; } /** * Helper to calculate storage capacity in GiB from allowances */ function calculateStorageCapacity(rateAllowance, lockupAllowance, pricePerTiBPerEpoch) { const tibFromRate = calculateActualCapacity(rateAllowance, pricePerTiBPerEpoch); const defaultLockupEpochs = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY; const maxRateFromLockup = lockupAllowance / defaultLockupEpochs; const tibFromLockup = calculateActualCapacity(maxRateFromLockup, pricePerTiBPerEpoch); const tibPerMonth = Math.min(tibFromRate, tibFromLockup * 3); const gibPerMonth = tibPerMonth * 1024; return gibPerMonth; } /** * Calculate actual capacity with deposit limitations */ function calculateActualCapacityWithDeposit(filecoinPayBalance, rateAllowance, lockupAllowance, pricePerTiBPerEpoch) { const potentialGiB = calculateStorageCapacity(rateAllowance, lockupAllowance, pricePerTiBPerEpoch); const requiredLockup = lockupAllowance; const monthlyPayment = rateAllowance * TIME_CONSTANTS.EPOCHS_PER_MONTH; const requiredDeposit = requiredLockup + monthlyPayment; let actualGiB; let isDepositLimited = false; let additionalDepositNeeded = 0n; if (filecoinPayBalance >= requiredDeposit) { actualGiB = potentialGiB; } else { isDepositLimited = true; additionalDepositNeeded = requiredDeposit - filecoinPayBalance; const scale = getStorageScale(potentialGiB); const scaleFactor = Number((filecoinPayBalance * BigInt(scale)) / requiredDeposit) / scale; actualGiB = potentialGiB * scaleFactor; } return { actualGiB, potentialGiB, isDepositLimited, additionalDepositNeeded, }; } /** * Display capacity information based on deposit and limits * * @param capacity - Calculated capacity information */ export function displayCapacity(capacity) { if (capacity.isDepositLimited) { log.indent(`→ Current capacity: ~${formatStorageCapacity(capacity.actualGiB)} ${pc.yellow('(deposit-limited)')}`); log.indent(`→ Potential: ~${formatStorageCapacity(capacity.potentialGiB)} (deposit ${formatUSDFC(capacity.additionalDepositNeeded)} more)`); } else { log.indent(`→ Estimated capacity: ~${formatStorageCapacity(capacity.actualGiB)}`); log.indent(pc.gray(' (excludes data set creation fee and optional CDN add-on rates)')); } } /** * Display current pricing information * * @param pricePerGiBPerMonth - Price per GiB per month * @param pricePerTiBPerMonth - Price per TiB per month */ export function displayPricing(pricePerGiBPerMonth, pricePerTiBPerMonth) { log.line(pc.bold('Current Pricing:')); log.indent(`1 GiB/month: ${formatUSDFC(pricePerGiBPerMonth)} USDFC`); log.indent(`1 TiB/month: ${formatUSDFC(pricePerTiBPerMonth)} USDFC`); log.indent(pc.gray('(for each upload, WarmStorage service will reserve 30 days of costs as security)')); log.flush(); } /** * Display WarmStorage service permissions with capacity information * * @param title - Section title to display * @param monthlyRate - Rate allowance in USDFC per month * @param lockupAmount - Lockup allowance amount * @param filecoinPayBalance - Total deposited amount * @param pricePerTiBPerEpoch - Current pricing per TiB per epoch */ export function displayServicePermissions(title, monthlyRate, lockupAmount, filecoinPayBalance, pricePerTiBPerEpoch, shouldFlush = true) { // Calculate capacity const ratePerEpoch = monthlyRate / TIME_CONSTANTS.EPOCHS_PER_MONTH; const capacity = calculateActualCapacityWithDeposit(filecoinPayBalance, ratePerEpoch, lockupAmount, pricePerTiBPerEpoch); log.line(pc.bold(title)); log.indent(`Max payment: ${formatUSDFC(monthlyRate)} USDFC/month`); log.indent(`Max reserve: ${formatUSDFC(lockupAmount)} USDFC (10-day lockup)`); displayCapacity(capacity); if (shouldFlush) { log.flush(); } } //# sourceMappingURL=setup.js.map