UNPKG

filecoin-pin

Version:

Bridge IPFS content to Filecoin Onchain Cloud using familiar tools

182 lines (154 loc) 6.36 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 { ethers } from 'ethers' import pc from 'picocolors' import { TELEMETRY_CLI_APP_NAME } from '../common/constants.js' import { calculateDepositCapacity, checkAndSetAllowances, checkFILBalance, checkUSDFCBalance, depositUSDFC, getPaymentStatus, validatePaymentRequirements, } from '../core/payments/index.js' import { cleanupSynapseService, initializeSynapse } from '../core/synapse/index.js' import { formatUSDFC } from '../core/utils/format.js' import { getCLILogger, parseCLIAuth } from '../utils/cli-auth.js' import { cancel, createSpinner, intro, outro } from '../utils/cli-helpers.js' import { log } from '../utils/cli-logger.js' import { displayAccountInfo, displayDepositWarning } from './setup.js' import type { PaymentSetupOptions } from './types.js' /** * Run automatic payment setup with defaults * * @param options - Options from command line */ export async function runAutoSetup(options: PaymentSetupOptions): Promise<void> { intro(pc.bold('Filecoin Onchain Cloud Payment Setup')) log.message(pc.gray('Running in auto mode...')) // Parse and validate deposit amount let targetFilecoinPayBalance: bigint try { targetFilecoinPayBalance = 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...') try { // Parse and validate authentication const authConfig = parseCLIAuth(options) const logger = getCLILogger() const synapse = await initializeSynapse( { ...authConfig, telemetry: { sentrySetTags: { appName: TELEMETRY_CLI_APP_NAME } } }, logger ) const network = synapse.getNetwork() const client = synapse.getClient() const address = await client.getAddress() spinner.stop(`${pc.green('✓')} Connected to ${pc.bold(network)}`) // Check balances spinner.start('Checking balances...') const filStatus = await checkFILBalance(synapse) const walletUsdfcBalance = await checkUSDFCBalance(synapse) spinner.stop(`${pc.green('✓')} Balance check complete`) // Validate payment requirements const validation = validatePaymentRequirements(filStatus.hasSufficientGas, walletUsdfcBalance, 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, walletUsdfcBalance, status.filecoinPayBalance ) // 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 actualFilecoinPayTopUp = 0n if (status.filecoinPayBalance < targetFilecoinPayBalance) { const neededFilecoinPayTopUp = targetFilecoinPayBalance - status.filecoinPayBalance actualFilecoinPayTopUp = neededFilecoinPayTopUp if (neededFilecoinPayTopUp > walletUsdfcBalance) { console.error( pc.red( `✗ Insufficient USDFC for deposit (need ${formatUSDFC(neededFilecoinPayTopUp)} USDFC, have ${formatUSDFC(walletUsdfcBalance)} USDFC)` ) ) process.exit(1) } spinner.start(`Depositing ${formatUSDFC(neededFilecoinPayTopUp)} USDFC...`) const { depositTx } = await depositUSDFC(synapse, neededFilecoinPayTopUp) spinner.stop(`${pc.green('✓')} Deposited ${formatUSDFC(neededFilecoinPayTopUp)} USDFC`) actionsTaken = true log.line(pc.bold('Transaction details:')) log.indent(pc.gray(`Deposit: ${depositTx}`)) log.flush() } else { // Use a dummy spinner to get consistent formatting spinner.start('Checking deposit...') const { updated, transactionHash } = await checkAndSetAllowances(synapse) if (updated) { spinner.stop(`${pc.green('✓')} Updated payment allowances, tx: ${transactionHash}`) } else { spinner.stop(`${pc.green('✓')} Deposit already sufficient (${formatUSDFC(status.filecoinPayBalance)} USDFC)`) } } // Calculate capacity for final summary const totalDeposit = status.filecoinPayBalance + actualFilecoinPayTopUp 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 cleanupSynapseService() process.exit() } }