diff --git a/package.json b/package.json index 6cbc4cd..04d6a8d 100644 --- a/package.json +++ b/package.json @@ -97,10 +97,11 @@ "homepage": "https://github.com/filecoin-project/filecoin-pin#readme", "dependencies": { "@clack/prompts": "^0.11.0", - "@filoz/synapse-sdk": "^0.34.0", + "@filoz/synapse-sdk": "^0.35.0", "@helia/unixfs": "^6.0.1", "@ipld/car": "^5.4.2", "commander": "^14.0.1", + "ethers": "^6.15.0", "fastify": "^5.6.0", "helia": "^6.0.1", "it-to-buffer": "^4.0.10", diff --git a/src/add/add.ts b/src/add/add.ts index 22f5736..f67435b 100644 --- a/src/add/add.ts +++ b/src/add/add.ts @@ -164,9 +164,6 @@ export async function runAdd(options: AddOptions): Promise { onProviderSelected: (provider) => { spinner.message(`Connecting to storage provider: ${provider.name || provider.serviceProvider}...`) }, - onDataSetCreationStarted: (transaction) => { - spinner.message(`Creating data set (tx: ${transaction.hash.slice(0, 10)}...)`) - }, onDataSetResolved: (info) => { if (info.isExisting) { spinner.message(`Using existing data set #${info.dataSetId}`) diff --git a/src/add/types.ts b/src/add/types.ts index 4a7e1de..0a5c5d0 100644 --- a/src/add/types.ts +++ b/src/add/types.ts @@ -4,7 +4,7 @@ import type { CLIAuthOptions } from '../utils/cli-auth.js' export interface AddOptions extends CLIAuthOptions { filePath: string bare?: boolean - /** Auto-fund: automatically ensure minimum 10 days of runway */ + /** Auto-fund: automatically ensure minimum 30 days of runway */ autoFund?: boolean } diff --git a/src/common/constants.ts b/src/common/constants.ts index 7366452..2132fd4 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -1,4 +1,4 @@ /** * Minimum runway in days to ensure WarmStorage can cover costs. Used when `--auto-fund` is passed to import or add commands */ -export const MIN_RUNWAY_DAYS = 10 +export const MIN_RUNWAY_DAYS = 30 diff --git a/src/common/upload-flow.ts b/src/common/upload-flow.ts index ed38cff..979a744 100644 --- a/src/common/upload-flow.ts +++ b/src/common/upload-flow.ts @@ -9,7 +9,7 @@ import type { Synapse } from '@filoz/synapse-sdk' import type { CID } from 'multiformats/cid' import pc from 'picocolors' import type { Logger } from 'pino' -import type { PaymentCapacityCheck } from '../core/payments/index.js' +import { DEFAULT_LOCKUP_DAYS, type PaymentCapacityCheck } from '../core/payments/index.js' import { cleanupSynapseService, type SynapseService } from '../core/synapse/index.js' import { checkUploadReadiness, executeUpload, getDownloadURL, type SynapseUploadResult } from '../core/upload/index.js' import { formatUSDFC } from '../core/utils/format.js' @@ -48,7 +48,7 @@ export interface UploadFlowResult extends SynapseUploadResult { /** * Perform auto-funding if requested - * Automatically ensures a minimum of 10 days of runway based on current usage + new file requirements + * Automatically ensures a minimum of 30 days of runway based on current usage + new file requirements * * @param synapse - Initialized Synapse instance * @param fileSize - Size of file being uploaded (in bytes) @@ -207,7 +207,7 @@ function displayPaymentIssues(capacityCheck: PaymentCapacityCheck, fileSize: num log.indent( `Required deposit: ${formatUSDFC(capacityCheck.required.lockupAllowance + capacityCheck.required.lockupAllowance / 10n)} USDFC` ) - log.indent(pc.gray('(includes 10-day safety reserve)')) + log.indent(pc.gray(`(includes ${DEFAULT_LOCKUP_DAYS}-day safety reserve)`)) log.line('') log.line(pc.bold('Suggested actions:')) diff --git a/src/core/payments/index.ts b/src/core/payments/index.ts index 35ab265..4cf6ca7 100644 --- a/src/core/payments/index.ts +++ b/src/core/payments/index.ts @@ -22,7 +22,7 @@ import { isSessionKeyMode } from '../synapse/index.js' // Constants export const USDFC_DECIMALS = 18 const MIN_FIL_FOR_GAS = ethers.parseEther('0.1') // Minimum FIL padding for gas -export const DEFAULT_LOCKUP_DAYS = 10 // WarmStorage requires 10 days lockup +export const DEFAULT_LOCKUP_DAYS = 30 // WarmStorage requires 30 days lockup // Maximum allowances for trusted WarmStorage service // Using MaxUint256 which MetaMask displays as "Unlimited" @@ -381,7 +381,7 @@ export async function setServiceApprovals( ): Promise { const warmStorageAddress = synapse.getWarmStorageAddress() - // Max lockup period is always 10 days worth of epochs for WarmStorage + // Max lockup period is always 30 days worth of epochs for WarmStorage const maxLockupPeriod = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY // Set the service approval @@ -415,9 +415,11 @@ export async function checkAllowances(synapse: Synapse): Promise<{ // Get current allowances const currentAllowances = await synapse.payments.serviceApproval(warmStorageAddress, TOKENS.USDFC) - // Check if we need to update (not at max) + // Check if we need to update (not at max or max lockup period is not enough) const needsUpdate = - currentAllowances.rateAllowance < MAX_RATE_ALLOWANCE || currentAllowances.lockupAllowance < MAX_LOCKUP_ALLOWANCE + currentAllowances.rateAllowance < MAX_RATE_ALLOWANCE || + currentAllowances.lockupAllowance < MAX_LOCKUP_ALLOWANCE || + currentAllowances.maxLockupPeriod < BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY return { needsUpdate, @@ -537,9 +539,9 @@ export function calculateStorageAllowances(storageTiB: number, pricePerTiBPerEpo // Calculate rate allowance (per epoch payment) const rateAllowance = (pricePerTiBPerEpoch * BigInt(scaledStorage)) / BigInt(scale) - // Calculate lockup allowance (10 days worth) - const epochsIn10Days = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY - const lockupAllowance = rateAllowance * epochsIn10Days + // Calculate lockup allowance + const epochsInLockupDays = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY + const lockupAllowance = rateAllowance * epochsInLockupDays return { rateAllowance, @@ -580,7 +582,7 @@ export function calculateActualCapacity(rateAllowance: bigint, pricePerTiBPerEpo * Calculate storage capacity from USDFC amount * * Determines how much storage can be purchased with a given USDFC amount, - * accounting for the 10-day lockup period. + * accounting for the 30-day lockup period. * * @param usdfcAmount - Amount of USDFC in its smallest unit * @param pricePerTiBPerEpoch - Current pricing from storage service @@ -589,9 +591,9 @@ export function calculateActualCapacity(rateAllowance: bigint, pricePerTiBPerEpo export function calculateStorageFromUSDFC(usdfcAmount: bigint, pricePerTiBPerEpoch: bigint): number { if (pricePerTiBPerEpoch === 0n) return 0 - // Calculate how much this covers for 10 days - const epochsIn10Days = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY - const ratePerEpoch = usdfcAmount / epochsIn10Days + // Calculate how much this covers for lockup + const epochsInLockupDays = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY + const ratePerEpoch = usdfcAmount / epochsInLockupDays return calculateActualCapacity(ratePerEpoch, pricePerTiBPerEpoch) } @@ -599,7 +601,7 @@ export function calculateStorageFromUSDFC(usdfcAmount: bigint, pricePerTiBPerEpo /** * Compute the additional deposit required to fund current usage for a duration. * - * The WarmStorage service maintains ~10 days of lockup (lockupUsed) and draws future + * The WarmStorage service maintains ~30 days of lockup (lockupUsed) and draws future * lockups from the available deposit (deposited - lockupUsed). To keep the current * rails alive for N days, ensure available >= N days of spend at the current rateUsed. * @@ -808,7 +810,7 @@ export function computeAdjustmentForExactDaysWithPiece( * treating WarmStorage as fully trusted with max allowances, i.e. not * accounting for allowance limits. If usage limits need to be accounted for * then the capacity can be capped by either deposit or allowances. - * This function accounts for the 10-day lockup requirement. + * This function accounts for the 30-day lockup requirement. * * @param depositAmount - Amount deposited in USDFC * @param pricePerTiBPerEpoch - Current pricing from storage service @@ -837,14 +839,14 @@ export function calculateDepositCapacity( } // With infinite allowances, deposit is the only limiting factor - // Deposit needs to cover: lockup (10 days) + at least some buffer - const epochsIn10Days = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY + // Deposit needs to cover: lockup (30 days) + at least some buffer + const epochsInLockupDays = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY const epochsPerMonth = TIME_CONSTANTS.EPOCHS_PER_MONTH // Maximum storage we can support with this deposit // Reserve 10% for buffer beyond the lockup // Calculate max rate per epoch we can afford with deposit - const maxRatePerEpoch = (depositAmount * BUFFER_DENOMINATOR) / (epochsIn10Days * BUFFER_NUMERATOR) + const maxRatePerEpoch = (depositAmount * BUFFER_DENOMINATOR) / (epochsInLockupDays * BUFFER_NUMERATOR) // Convert to storage capacity const tibPerMonth = calculateActualCapacity(maxRatePerEpoch, pricePerTiBPerEpoch) @@ -852,7 +854,7 @@ export function calculateDepositCapacity( // Calculate the actual costs for this capacity const monthlyPayment = maxRatePerEpoch * epochsPerMonth - const requiredLockup = maxRatePerEpoch * epochsIn10Days + const requiredLockup = maxRatePerEpoch * epochsInLockupDays const totalRequired = withBuffer(requiredLockup) return { diff --git a/src/core/synapse/index.ts b/src/core/synapse/index.ts index ba259b7..45ef572 100644 --- a/src/core/synapse/index.ts +++ b/src/core/synapse/index.ts @@ -5,7 +5,7 @@ import { type ProviderInfo, RPC_URLS, type StorageContext, - type StorageCreationCallbacks, + type StorageContextCallbacks, type StorageServiceOptions, Synapse, type SynapseOptions, @@ -144,11 +144,6 @@ export interface DatasetOptions { metadata?: Record } -/** - * Progress callbacks for tracking dataset and provider selection. - */ -export type StorageProgressCallbacks = Omit - /** * Options for creating a storage context. */ @@ -161,7 +156,7 @@ export interface CreateStorageContextOptions { /** * Progress callbacks for tracking creation. */ - callbacks?: StorageProgressCallbacks + callbacks?: StorageContextCallbacks /** * Override provider selection by address. @@ -440,7 +435,7 @@ export async function createStorageContext( * Callbacks provide visibility into the storage lifecycle * These are crucial for debugging and monitoring in production */ - const callbacks: StorageCreationCallbacks = { + const callbacks: StorageContextCallbacks = { onProviderSelected: (provider) => { currentProviderInfo = provider @@ -471,29 +466,6 @@ export async function createStorageContext( options?.callbacks?.onDataSetResolved?.(info) }, - onDataSetCreationStarted: (transaction, statusUrl) => { - logger.info( - { - event: 'synapse.storage.data_set_creation_started', - txHash: transaction.hash, - statusUrl, - }, - 'Data set creation transaction submitted' - ) - - options?.callbacks?.onDataSetCreationStarted?.(transaction) - }, - onDataSetCreationProgress: (status) => { - logger.info( - { - event: 'synapse.storage.data_set_creation_progress', - transactionMined: status.transactionMined, - dataSetLive: status.dataSetLive, - elapsedMs: status.elapsedMs, - }, - 'Data set creation progress' - ) - }, } sdkOptions.callbacks = callbacks diff --git a/src/core/upload/index.ts b/src/core/upload/index.ts index 7c36028..9aabc19 100644 --- a/src/core/upload/index.ts +++ b/src/core/upload/index.ts @@ -205,11 +205,11 @@ export async function executeUpload( onUploadComplete: (pieceCid) => { callbacks?.onUploadComplete?.(pieceCid) }, - onPieceAdded: (transaction) => { - if (transaction?.hash) { - transactionHash = transaction.hash + onPieceAdded: (txHash) => { + if (txHash) { + transactionHash = txHash } - callbacks?.onPieceAdded?.(transaction) + callbacks?.onPieceAdded?.(txHash) }, onPieceConfirmed: (pieceIds) => { callbacks?.onPieceConfirmed?.(pieceIds) diff --git a/src/core/upload/synapse.ts b/src/core/upload/synapse.ts index ea10774..0b413c2 100644 --- a/src/core/upload/synapse.ts +++ b/src/core/upload/synapse.ts @@ -87,13 +87,13 @@ export async function uploadToSynapse( callbacks?.onUploadComplete?.(pieceCid) }, - onPieceAdded: (transaction) => { - if (transaction != null) { + onPieceAdded: (txHash) => { + if (txHash != null) { logger.info( { event: 'synapse.upload.piece_added', contextId, - txHash: transaction.hash, + txHash: txHash, }, 'Piece addition transaction submitted' ) @@ -106,7 +106,7 @@ export async function uploadToSynapse( 'Piece added to data set' ) } - callbacks?.onPieceAdded?.(transaction) + callbacks?.onPieceAdded?.(txHash) }, onPieceConfirmed: (pieceIds) => { diff --git a/src/data-set/inspect.ts b/src/data-set/inspect.ts index 98d26fc..bf3dba6 100644 --- a/src/data-set/inspect.ts +++ b/src/data-set/inspect.ts @@ -62,22 +62,22 @@ function formatPaymentToken(tokenAddress: string): string { * Format storage price in USDFC per TiB per month * Always shows TiB/month for consistency, with appropriate precision */ -function formatStoragePrice(pricePerTiBPerMonth: bigint): string { +function formatStoragePrice(pricePerTiBPerDay: bigint): string { try { - const priceInUSDFC = parseFloat(ethers.formatUnits(pricePerTiBPerMonth, 18)) + const priceInUSDFC = parseFloat(ethers.formatUnits(pricePerTiBPerDay, 18)) // Handle very small prices that would show as 0.0000 if (priceInUSDFC < 0.0001) { - return '< 0.0001 USDFC/TiB/month' + return '< 0.0001 USDFC/TiB/day' } // For prices >= 0.0001, show with appropriate precision if (priceInUSDFC >= 1) { - return `${priceInUSDFC.toFixed(2)} USDFC/TiB/month` + return `${priceInUSDFC.toFixed(2)} USDFC/TiB/day` } else if (priceInUSDFC >= 0.01) { - return `${priceInUSDFC.toFixed(4)} USDFC/TiB/month` + return `${priceInUSDFC.toFixed(4)} USDFC/TiB/day` } else { - return `${priceInUSDFC.toFixed(6)} USDFC/TiB/month` + return `${priceInUSDFC.toFixed(6)} USDFC/TiB/day` } } catch { return pc.red('invalid price') @@ -235,7 +235,7 @@ export function displayDataSetStatus(ctx: DataSetInspectionContext, dataSetId: n log.indent(`Service URL: ${pdpData.serviceURL}`) log.indent(`Min piece size: ${formatBytes(BigInt(pdpData.minPieceSizeInBytes))}`) log.indent(`Max piece size: ${formatBytes(BigInt(pdpData.maxPieceSizeInBytes))}`) - log.indent(`Storage price: ${formatStoragePrice(pdpData.storagePricePerTibPerMonth)}`) + log.indent(`Storage price: ${formatStoragePrice(pdpData.storagePricePerTibPerDay)}`) log.indent(`Min proving period: ${pdpData.minProvingPeriodInEpochs} epochs`) log.indent(`Location: ${pdpData.location}`) log.indent(`Payment token: ${formatPaymentToken(pdpData.paymentTokenAddress)}`) @@ -244,9 +244,6 @@ export function displayDataSetStatus(ctx: DataSetInspectionContext, dataSetId: n if (base.pdpEndEpoch > 0) { log.indent(pc.yellow(`PDP payments ended @ epoch ${base.pdpEndEpoch}`)) } - if (base.cdnEndEpoch > 0) { - log.indent(pc.yellow(`CDN payments ended @ epoch ${base.cdnEndEpoch}`)) - } log.line('') log.line(pc.bold('Metadata')) diff --git a/src/import/import.ts b/src/import/import.ts index 573f423..c3b6aaa 100644 --- a/src/import/import.ts +++ b/src/import/import.ts @@ -208,9 +208,6 @@ export async function runCarImport(options: ImportOptions): Promise { spinner.message(`Connecting to storage provider: ${provider.name || provider.serviceProvider}...`) }, - onDataSetCreationStarted: (transaction) => { - spinner.message(`Creating data set (tx: ${transaction.hash.slice(0, 10)}...)`) - }, onDataSetResolved: (info) => { if (info.isExisting) { spinner.message(`Using existing data set #${info.dataSetId}`) diff --git a/src/import/types.ts b/src/import/types.ts index 267e2e1..09762f3 100644 --- a/src/import/types.ts +++ b/src/import/types.ts @@ -3,7 +3,7 @@ import type { CLIAuthOptions } from '../utils/cli-auth.js' export interface ImportOptions extends CLIAuthOptions { filePath: string - /** Auto-fund: automatically ensure minimum 10 days of runway */ + /** Auto-fund: automatically ensure minimum 30 days of runway */ autoFund?: boolean } diff --git a/src/payments/fund.ts b/src/payments/fund.ts index 0879b20..68e5fb1 100644 --- a/src/payments/fund.ts +++ b/src/payments/fund.ts @@ -17,6 +17,7 @@ import { computeAdjustmentForExactDays, computeAdjustmentForExactDaysWithPiece, computeAdjustmentForExactDeposit, + DEFAULT_LOCKUP_DAYS, depositUSDFC, getPaymentStatus, validatePaymentRequirements, @@ -31,8 +32,8 @@ import { cancel, createSpinner, intro, isInteractive, outro } from '../utils/cli import { isTTY, log } from '../utils/cli-logger.js' import type { AutoFundOptions, FundingAdjustmentResult, FundOptions } from './types.js' -// Helper: confirm/warn or bail when target implies < 10-day runway -async function ensureBelowTenDaysAllowed(opts: { +// Helper: confirm/warn or bail when target implies < lockup-days runway +async function ensureBelowThirtyDaysAllowed(opts: { spinner: Spinner warningLine1: string warningLine2: string @@ -43,7 +44,7 @@ async function ensureBelowTenDaysAllowed(opts: { console.error(pc.red(warningLine1)) console.error(pc.red(warningLine2)) cancel('Fund adjustment aborted') - throw new Error('Unsafe target below 10-day baseline') + throw new Error(`Unsafe target below ${DEFAULT_LOCKUP_DAYS}-day baseline`) } log.line(pc.yellow('⚠ Warning')) @@ -52,7 +53,7 @@ async function ensureBelowTenDaysAllowed(opts: { log.flush() const proceed = await confirm({ - message: 'Proceed with reducing runway below 10 days?', + message: 'Proceed with reducing runway below 30 days?', initialValue: false, }) if (!proceed) { @@ -347,14 +348,14 @@ export async function runFund(options: FundOptions): Promise { } delta = 0n } - } else if (runwayCheckDays != null && runwayCheckDays < 10) { + } else if (runwayCheckDays != null && runwayCheckDays < Number(TIME_CONSTANTS.DEFAULT_LOCKUP_DAYS)) { const line1 = hasDays - ? 'Requested runway below 10-day safety baseline.' - : 'Target deposit implies less than 10 days of runway at current spend.' + ? 'Requested runway below 30-day safety baseline.' + : 'Target deposit implies less than 30 days of runway at current spend.' const line2 = hasDays - ? 'WarmStorage reserves 10 days of costs; a shorter runway risks termination.' + ? 'WarmStorage reserves 30 days of costs; a shorter runway risks termination.' : 'Increase target or accept risk: shorter runway may cause termination.' - await ensureBelowTenDaysAllowed({ + await ensureBelowThirtyDaysAllowed({ spinner, warningLine1: line1, warningLine2: line2, diff --git a/src/payments/interactive.ts b/src/payments/interactive.ts index f0a3372..c29659d 100644 --- a/src/payments/interactive.ts +++ b/src/payments/interactive.ts @@ -15,6 +15,7 @@ import { checkAllowances, checkFILBalance, checkUSDFCBalance, + DEFAULT_LOCKUP_DAYS, depositUSDFC, getPaymentStatus, setMaxAllowances, @@ -93,6 +94,7 @@ export async function runInteractiveSetup(options: PaymentSetupOptions): Promise privateKey, rpcURL: rpcUrl, withIpni: true, // Always filter for IPNI-enabled providers + ...(options.warmStorageAddress && { warmStorageAddress: options.warmStorageAddress }), }) const network = synapse.getNetwork() const client = synapse.getClient() @@ -226,7 +228,7 @@ export async function runInteractiveSetup(options: PaymentSetupOptions): Promise log.indent(`100 GiB capacity: ~${formatUSDFC((pricePerGiBPerMonth * 100n * 11n) / 10n)} USDFC`) log.indent(`1 TiB capacity: ~${formatUSDFC((pricePerTiBPerMonth * 11n) / 10n)} USDFC`) log.indent(`10 TiB capacity: ~${formatUSDFC((pricePerTiBPerMonth * 10n * 11n) / 10n)} USDFC`) - log.indent(pc.gray('(deposit covers 1 month + 10-day safety reserve)')) + log.indent(pc.gray(`(deposit covers 1 month + ${DEFAULT_LOCKUP_DAYS}-day safety reserve)`)) log.flush() const amountStr = await text({ @@ -298,7 +300,7 @@ export async function runInteractiveSetup(options: PaymentSetupOptions): Promise ? `${(finalCapacity.gibPerMonth / 1024).toFixed(1)} TiB` : `${finalCapacity.gibPerMonth.toFixed(1)} GiB` log.indent(`Capacity: ~${capacityStr} for 1 month`) - log.indent(pc.gray('(includes 10-day safety reserve)')) + log.indent(pc.gray(`(includes ${DEFAULT_LOCKUP_DAYS}-day safety reserve)`)) } log.flush() diff --git a/src/payments/setup.ts b/src/payments/setup.ts index d90c007..8614d31 100644 --- a/src/payments/setup.ts +++ b/src/payments/setup.ts @@ -299,7 +299,7 @@ export function displayPricing(pricePerGiBPerMonth: bigint, 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 10 days of costs as security)')) + log.indent(pc.gray('(for each upload, WarmStorage service will reserve 30 days of costs as security)')) log.flush() } diff --git a/src/test/unit/import.test.ts b/src/test/unit/import.test.ts index 24599b6..42ecefc 100644 --- a/src/test/unit/import.test.ts +++ b/src/test/unit/import.test.ts @@ -356,7 +356,6 @@ describe('CAR Import', () => { expect.objectContaining({ callbacks: expect.objectContaining({ onProviderSelected: expect.any(Function), - onDataSetCreationStarted: expect.any(Function), onDataSetResolved: expect.any(Function), }), }) diff --git a/src/test/unit/payments-setup.test.ts b/src/test/unit/payments-setup.test.ts index 9330454..b56461f 100644 --- a/src/test/unit/payments-setup.test.ts +++ b/src/test/unit/payments-setup.test.ts @@ -110,9 +110,9 @@ describe('Payment Setup Tests', () => { getStorageInfo: vi.fn().mockResolvedValue({ pricing: { noCDN: { - perTiBPerEpoch: ethers.parseUnits('0.0000565', 18), - perTiBPerDay: ethers.parseUnits('0.16272', 18), - perTiBPerMonth: ethers.parseUnits('4.8816', 18), + perTiBPerEpoch: ethers.parseUnits('0.00002893519', 18), // 2.5 USDFC/TiB/month + perTiBPerDay: ethers.parseUnits('0.08333333', 18), + perTiBPerMonth: ethers.parseUnits('2.5', 18), }, }, }), @@ -196,7 +196,7 @@ describe('Payment Setup Tests', () => { '0xwarmstorage', rateAllowance, lockupAllowance, - 28800n, // 10 days * 2880 epochs/day + 86400n, // 30 days * 2880 epochs/day (bigint) 'USDFC' ) }) @@ -210,7 +210,7 @@ describe('Payment Setup Tests', () => { expect(allowances.storageCapacityTiB).toBe(1) expect(allowances.rateAllowance).toBe(ethers.parseUnits('0.0000565', 18)) expect(allowances.lockupAllowance).toBe( - ethers.parseUnits('0.0000565', 18) * 2880n * 10n // rate * epochs/day * 10 days + ethers.parseUnits('0.0000565', 18) * 2880n * 30n // rate * epochs/day * 30 days ) }) @@ -231,7 +231,7 @@ describe('Payment Setup Tests', () => { // 1.5 TiB expect(allowances.rateAllowance).toBe(ethers.parseUnits('0.00008475', 18)) expect(allowances.lockupAllowance).toBe( - ethers.parseUnits('0.00008475', 18) * 2880n * 10n // rate * epochs/day * 10 days + ethers.parseUnits('0.00008475', 18) * 2880n * 30n // rate * epochs/day * 30 days ) }) @@ -355,8 +355,8 @@ describe('Payment Setup Tests', () => { describe('calculateStorageFromUSDFC', () => { it('should calculate storage capacity from USDFC amount with high precision', () => { const pricePerTiBPerEpoch = ethers.parseUnits('0.0000565', 18) - // 10 days worth of 1GiB/month = 0.0015881472 USDFC - const usdfcAmount = ethers.parseUnits('0.0015881472', 18) + // 30 days worth of 1GiB/month = 0.0047644416 USDFC + const usdfcAmount = ethers.parseUnits('0.0047644416', 18) const capacityTiB = calculateStorageFromUSDFC(usdfcAmount, pricePerTiBPerEpoch) @@ -387,11 +387,12 @@ describe('Payment Setup Tests', () => { expect(capacityTiB).toBe(0) }) - // simple testcase to show what the pricePerTibPerEpoch would need to be to get 1TiB/month with 1USDFC - // feel free to skip/delete this testcase if it becomes irrelevant + // Verify pricePerTibPerEpoch needed to get 1TiB/month with 1USDFC given 30-day lockup + // With 30-day lockup: 1 USDFC / (30 days * 2880 epochs/day) = 1 USDFC / 86400 epochs + // For 1 TiB capacity: pricePerTiBPerEpoch = 1 / 86400 = 0.000011574074 USDFC it('should return capacity of 1 when pricePerTibPerEpoch is low', () => { const usdfcAmount = ethers.parseUnits('1', 18) - const pricePerTiBPerEpoch = ethers.parseUnits('0.000034722219', 18) + const pricePerTiBPerEpoch = ethers.parseUnits('0.000011574074', 18) const capacityTiB = calculateStorageFromUSDFC(usdfcAmount, pricePerTiBPerEpoch) // within 10 decimal places accuracy of 1 expect(capacityTiB).toBeCloseTo(1, 10) diff --git a/src/test/unit/payments.compute.test.ts b/src/test/unit/payments.compute.test.ts index 6330177..3da2061 100644 --- a/src/test/unit/payments.compute.test.ts +++ b/src/test/unit/payments.compute.test.ts @@ -85,14 +85,14 @@ describe('computeAdjustmentForExactDays', () => { it('returns positive delta when more deposit needed (includes 1-hour safety)', () => { const rateUsed = 1_000_000_000_000_000_000n // 1 USDFC/epoch const perDay = rateUsed * TIME_CONSTANTS.EPOCHS_PER_DAY - const days = 10 - const available = perDay * 10n // exactly 10 days + const days = 30 + const available = perDay * 30n // exactly 30 days const status = makeStatus({ filecoinPayBalance: available, lockupUsed: 0n, rateUsed }) const res = computeAdjustmentForExactDays(status, days) const perHour = perDay / 24n const safety = perHour > 0n ? perHour : 1n expect(res.delta).toBe(safety) - expect(res.targetAvailable).toBe(perDay * 10n + safety) + expect(res.targetAvailable).toBe(perDay * 30n + safety) }) it('returns negative delta when withdrawal possible', () => { @@ -163,7 +163,7 @@ describe('computeAdjustmentForExactDaysWithPiece', () => { it('adds file requirements to existing usage', () => { // Scenario: Existing storage, adding another file const rateUsed = 1_000_000_000_000_000_000n // 1 USDFC/epoch - const lockupUsed = rateUsed * BigInt(10) * TIME_CONSTANTS.EPOCHS_PER_DAY // 10 days worth + const lockupUsed = rateUsed * BigInt(30) * TIME_CONSTANTS.EPOCHS_PER_DAY // 30 days worth const filecoinPayBalance = (lockupUsed * 12n) / 10n // 20% buffer const status = makeStatus({ filecoinPayBalance, lockupUsed, rateUsed }) diff --git a/upload-action/package.json b/upload-action/package.json index 93ff8a0..046d9a0 100644 --- a/upload-action/package.json +++ b/upload-action/package.json @@ -6,7 +6,7 @@ "description": "Helper runner for Filecoin Pin Upload GitHub Action", "dependencies": { "@actions/artifact": "^2.3.2", - "@filoz/synapse-sdk": "^0.33.0", + "@filoz/synapse-sdk": "^0.35.0", "@octokit/rest": "^22.0.0", "ethers": "^6.15.0", "filecoin-pin": "../", diff --git a/upload-action/src/filecoin.js b/upload-action/src/filecoin.js index e9baf83..220d5ea 100644 --- a/upload-action/src/filecoin.js +++ b/upload-action/src/filecoin.js @@ -290,10 +290,10 @@ export async function uploadCarToFilecoin(synapse, carPath, ipfsRootCid, options console.log(`Piece CID: ${pieceCid}`) console.log('\n⏳ Registering piece in data set...') }, - onPieceAdded: (transaction) => { - if (transaction?.hash) { + onPieceAdded: (txHash) => { + if (txHash) { console.log('✓ Piece registration transaction submitted') - console.log(`Transaction hash: ${transaction.hash}`) + console.log(`Transaction hash: ${txHash}`) console.log('\n⏳ Waiting for on-chain confirmation...') } else { console.log('✓ Piece added to data set (no transaction needed)')