UNPKG

filecoin-pin

Version:

Bridge IPFS content to Filecoin Onchain Cloud using familiar tools

168 lines 7.6 kB
/** * Automatic payment setup flow * * This module provides an automated, non-interactive setup experience for * configuring payment approvals. It uses default values and command-line * options to complete the setup without user interaction. */ import { RPC_URLS, Synapse } from '@filoz/synapse-sdk'; import { ethers } from 'ethers'; import pc from 'picocolors'; import { calculateDepositCapacity, checkAndSetAllowances } from '../synapse/payments.js'; import { cleanupProvider } from '../synapse/service.js'; import { cancel, createSpinner, intro, outro } from '../utils/cli-helpers.js'; import { log } from '../utils/cli-logger.js'; import { checkFILBalance, checkUSDFCBalance, depositUSDFC, displayAccountInfo, displayDepositWarning, formatUSDFC, getPaymentStatus, validatePaymentRequirements, } from './setup.js'; /** * Run automatic payment setup with defaults * * @param options - Options from command line */ export async function runAutoSetup(options) { intro(pc.bold('Filecoin Onchain Cloud Payment Setup')); log.message(pc.gray('Running in auto mode...')); // Parse and validate all arguments upfront // 1. Private key const privateKey = options.privateKey || process.env.PRIVATE_KEY; if (!privateKey) { console.error(pc.red('Error: Private key required via --private-key or PRIVATE_KEY env')); process.exit(1); } // Validate private key format early try { new ethers.Wallet(privateKey); } catch { console.error(pc.red('Error: Invalid private key format')); process.exit(1); } // 2. RPC URL const rpcUrl = options.rpcUrl || RPC_URLS.calibration.websocket; // 3. Deposit amount let targetDeposit; try { targetDeposit = ethers.parseUnits(options.deposit, 18); } catch { console.error(pc.red(`Error: Invalid deposit amount '${options.deposit}'`)); process.exit(1); } const spinner = createSpinner(); spinner.start('Initializing connection...'); // Store provider reference for cleanup if it's a WebSocket provider let provider = null; try { // Initialize Synapse const synapse = await Synapse.create({ privateKey, rpcURL: rpcUrl, }); const network = synapse.getNetwork(); const signer = synapse.getSigner(); const address = await signer.getAddress(); // Store provider reference for cleanup if it's a WebSocket provider if (rpcUrl.match(/^wss?:\/\//)) { provider = synapse.getProvider(); } spinner.stop(`${pc.green('✓')} Connected to ${pc.bold(network)}`); // Check balances spinner.start('Checking balances...'); const filStatus = await checkFILBalance(synapse); const usdfcBalance = await checkUSDFCBalance(synapse); spinner.stop(`${pc.green('✓')} Balance check complete`); // Validate payment requirements const validation = validatePaymentRequirements(filStatus.hasSufficientGas, usdfcBalance, filStatus.isCalibnet); if (!validation.isValid) { log.line(`${pc.red('✗')} ${validation.errorMessage}`); if (validation.helpMessage) { log.line(''); log.line(` ${pc.cyan(validation.helpMessage)}`); } log.flush(); cancel('Please fund your wallet and try again'); process.exit(1); } // Now safe to get payment status since we know account exists const status = await getPaymentStatus(synapse); // Display account and balance info using shared function displayAccountInfo(address, network, filStatus.balance, filStatus.isCalibnet, filStatus.hasSufficientGas, usdfcBalance, status.depositedAmount); // Get storage pricing for capacity calculation const storageInfo = await synapse.storage.getStorageInfo(); const pricePerTiBPerEpoch = storageInfo.pricing.noCDN.perTiBPerEpoch; // Track if any changes were made let actionsTaken = false; let actualDepositAmount = 0n; // Auto-set max allowances for WarmStorage spinner.start('Configuring WarmStorage permissions...'); const allowanceResult = await checkAndSetAllowances(synapse); if (allowanceResult.updated) { spinner.stop(`${pc.green('✓')} WarmStorage permissions configured`); log.line(pc.bold('Transaction:')); log.indent(pc.gray(allowanceResult.transactionHash || 'Unknown')); log.flush(); actionsTaken = true; } else { spinner.stop(`${pc.green('✓')} WarmStorage permissions already configured`); } if (status.depositedAmount < targetDeposit) { const depositAmount = targetDeposit - status.depositedAmount; actualDepositAmount = depositAmount; if (depositAmount > usdfcBalance) { console.error(pc.red(`✗ Insufficient USDFC for deposit (need ${formatUSDFC(depositAmount)} USDFC, have ${formatUSDFC(usdfcBalance)} USDFC)`)); process.exit(1); } spinner.start(`Depositing ${formatUSDFC(depositAmount)} USDFC...`); const { approvalTx, depositTx } = await depositUSDFC(synapse, depositAmount); spinner.stop(`${pc.green('✓')} Deposited ${formatUSDFC(depositAmount)} USDFC`); actionsTaken = true; log.line(pc.bold('Transaction details:')); if (approvalTx) { log.indent(pc.gray(`Approval: ${approvalTx}`)); } log.indent(pc.gray(`Deposit: ${depositTx}`)); log.flush(); } else { // Use a dummy spinner to get consistent formatting spinner.start('Checking deposit...'); spinner.stop(`${pc.green('✓')} Deposit already sufficient (${formatUSDFC(status.depositedAmount)} USDFC)`); } // Calculate capacity for final summary const totalDeposit = status.depositedAmount + actualDepositAmount; const capacity = calculateDepositCapacity(totalDeposit, pricePerTiBPerEpoch); // Final summary spinner.start('Completing setup...'); spinner.stop('━━━ Configuration Summary ━━━'); log.line(`Network: ${pc.bold(network)}`); log.line(`Deposit: ${formatUSDFC(totalDeposit)} USDFC`); if (capacity.gibPerMonth > 0) { const capacityStr = capacity.gibPerMonth >= 1024 ? `${(capacity.gibPerMonth / 1024).toFixed(1)} TiB` : `${capacity.gibPerMonth.toFixed(1)} GiB`; log.line(`Storage: ~${capacityStr} for 1 month`); } log.line(`Status: ${pc.green('Ready to upload')}`); log.flush(); // Show deposit warning if needed displayDepositWarning(totalDeposit, status.currentAllowances.lockupUsed); // Show appropriate outro message based on whether actions were taken if (actionsTaken) { outro('Payment setup completed successfully'); } else { outro('Payment setup already configured - ready to use'); } } catch (error) { spinner.stop(); // Stop spinner without message console.error(pc.red('✗ Setup failed')); console.error(pc.red('Error:'), error instanceof Error ? error.message : error); process.exitCode = 1; } finally { await cleanupProvider(provider); process.exit(); } } //# sourceMappingURL=auto.js.map