filecoin-pin
Version:
Bridge IPFS content to Filecoin Onchain Cloud using familiar tools
193 lines • 7.94 kB
JavaScript
import { formatUSDFC } from '../utils/index.js';
import { calculatePieceUploadRequirements, computeAdjustmentForExactDaysWithPiece, computeTopUpForDuration, depositUSDFC, getPaymentStatus, } from './index.js';
/**
* Format a human-readable message for a top-up calculation
*
* @param calc - Top-up calculation result
* @returns Human-readable message explaining the top-up requirement
*/
export function formatTopUpReason(calc) {
if (calc.requiredTopUp === 0n) {
return 'No top-up required';
}
switch (calc.reasonCode) {
case 'piece-upload':
return 'Required top-up for file upload (lockup requirement)';
case 'required-runway':
return `Required top-up for ${calc.calculation.minStorageDays} days of storage`;
case 'required-runway-plus-upload':
return `Required top-up for ${calc.calculation.minStorageDays} days of storage (including upcoming upload)`;
default:
return 'Required top-up';
}
}
/**
* Calculate required top-up for a specific storage scenario
*
* This function determines how much USDFC needs to be deposited to meet
* the specified storage requirements, taking into account current usage
* and the size of any upcoming upload.
*
* @param status - Current payment status
* @param options - Storage requirements
* @returns Top-up calculation with reasoning
*/
export function calculateRequiredTopUp(status, options) {
const { minStorageDays = 0, pieceSizeBytes, pricePerTiBPerEpoch } = options;
const rateUsed = status.currentAllowances.rateUsed ?? 0n;
const lockupUsed = status.currentAllowances.lockupUsed ?? 0n;
let requiredTopUp = 0n;
let reasonCode = 'none';
// Calculate piece upload requirements if we have piece info
let pieceUploadTopUp = 0n;
if (pieceSizeBytes != null && pieceSizeBytes > 0 && pricePerTiBPerEpoch != null) {
const uploadRequirements = calculatePieceUploadRequirements(status, pieceSizeBytes, pricePerTiBPerEpoch);
if (uploadRequirements.insufficientDeposit > 0n) {
pieceUploadTopUp = uploadRequirements.insufficientDeposit;
}
}
// Calculate runway requirements if specified
let runwayTopUp = 0n;
let isRunwayWithUpload = false;
if (minStorageDays > 0) {
if (rateUsed === 0n && pieceSizeBytes != null && pieceSizeBytes > 0 && pricePerTiBPerEpoch != null) {
// New piece upload with runway requirement
const { delta } = computeAdjustmentForExactDaysWithPiece(status, minStorageDays, pieceSizeBytes, pricePerTiBPerEpoch);
runwayTopUp = delta;
isRunwayWithUpload = true;
}
else {
// Existing usage with runway requirement
const { topUp } = computeTopUpForDuration(status, minStorageDays);
runwayTopUp = topUp;
}
}
// Determine the final top-up requirement (take the maximum of file upload and runway needs)
if (runwayTopUp > pieceUploadTopUp) {
requiredTopUp = runwayTopUp;
reasonCode = isRunwayWithUpload ? 'required-runway-plus-upload' : 'required-runway';
}
else if (pieceUploadTopUp > 0n) {
requiredTopUp = pieceUploadTopUp;
reasonCode = 'piece-upload';
}
return {
requiredTopUp,
reasonCode,
calculation: {
minStorageDays,
pieceSizeBytes,
pricePerTiBPerEpoch,
currentRateUsed: rateUsed,
currentLockupUsed: lockupUsed,
currentDeposited: status.filecoinPayBalance,
},
};
}
/**
* Execute a top-up operation with balance limit checking
*
* This function handles the complete top-up process including:
* - Checking if top-up is needed
* - Validating against balance limits
* - Executing the deposit transaction
* - Providing detailed feedback
*
* @param synapse - Initialized Synapse instance
* @param topUpAmount - Amount of USDFC to deposit
* @param options - Options for top-up execution
* @returns Top-up execution result
*/
export async function executeTopUp(synapse, topUpAmount, options = {}) {
const { balanceLimit, logger } = options;
const warnings = [];
if (topUpAmount <= 0n) {
return {
success: true,
deposited: 0n,
message: 'No deposit required - sufficient balance available',
warnings,
};
}
// Get current status for limit checking
const currentStatus = await getPaymentStatus(synapse);
// Check if deposit would exceed maximum balance if specified
if (balanceLimit != null && balanceLimit >= 0n) {
// Check if current balance already equals or exceeds limit
if (currentStatus.filecoinPayBalance >= balanceLimit) {
const message = `Current balance (${formatUSDFC(currentStatus.filecoinPayBalance)}) already equals or exceeds the configured balance limit (${formatUSDFC(balanceLimit)}). No additional deposits will be made.`;
logger?.warn(`${message}`);
return {
success: true,
deposited: 0n,
message,
warnings,
};
}
else {
// Check if required top-up would exceed the limit
const projectedBalance = currentStatus.filecoinPayBalance + topUpAmount;
if (projectedBalance > balanceLimit) {
// Calculate the maximum allowed top-up that won't exceed the limit
const maxAllowedTopUp = balanceLimit - currentStatus.filecoinPayBalance;
if (maxAllowedTopUp > 0n) {
const warning = `Required top-up (${formatUSDFC(topUpAmount)}) would exceed the configured balance limit (${formatUSDFC(balanceLimit)}). Reducing to ${formatUSDFC(maxAllowedTopUp)}.`;
logger?.warn(`${warning}`);
warnings.push(warning);
topUpAmount = maxAllowedTopUp;
}
else {
return {
success: true,
deposited: 0n,
message: 'Cannot deposit - would exceed balance limit',
warnings,
};
}
}
}
}
// Ensure wallet has sufficient USDFC for the deposit
if (currentStatus.walletUsdfcBalance < topUpAmount) {
const message = `Insufficient USDFC in wallet for deposit. Needed ${formatUSDFC(topUpAmount)}, available ${formatUSDFC(currentStatus.walletUsdfcBalance)}.`;
logger?.warn(`${message}`);
return {
success: false,
deposited: 0n,
message,
warnings,
};
}
try {
// Execute the deposit
const result = await depositUSDFC(synapse, topUpAmount);
// Verify the deposit was successful
const newStatus = await getPaymentStatus(synapse);
const depositDifference = newStatus.filecoinPayBalance - currentStatus.filecoinPayBalance;
let message = '';
if (depositDifference > 0n) {
message = `Deposit verified: ${formatUSDFC(depositDifference)} USDFC added to Filecoin Pay`;
}
else {
message = 'Deposit transaction submitted but not yet reflected in balance';
warnings.push('Transaction may take a moment to process');
}
return {
success: true,
deposited: depositDifference,
transactionHash: result.depositTx,
message,
warnings,
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
success: false,
deposited: 0n,
message: `Deposit failed: ${errorMessage}`,
warnings,
};
}
}
//# sourceMappingURL=top-up.js.map