From 7a7ff3f236427046a1f9aa8b9a5e04543a9d23cb Mon Sep 17 00:00:00 2001 From: zenground0 Date: Tue, 21 Oct 2025 14:32:48 -0600 Subject: [PATCH 01/11] Explicitly declare direct dep rather than rely on synapse --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 6cbc4cdb..543290a1 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "@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", From 14e760b1bfe05a7e1fbb4ae4e732051603d6143d Mon Sep 17 00:00:00 2001 From: zenground0 Date: Tue, 21 Oct 2025 14:44:23 -0600 Subject: [PATCH 02/11] cdn endEpoch removed from ds info --- src/data-set/inspect.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/data-set/inspect.ts b/src/data-set/inspect.ts index 98d26fc0..12a4ab11 100644 --- a/src/data-set/inspect.ts +++ b/src/data-set/inspect.ts @@ -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')) From 2f004841e188bdc486cd17994ad7698cf219245c Mon Sep 17 00:00:00 2001 From: zenground0 Date: Tue, 21 Oct 2025 16:05:20 -0600 Subject: [PATCH 03/11] 10 day grace period => 30 day grace period --- src/add/types.ts | 2 +- src/common/constants.ts | 2 +- src/common/upload-flow.ts | 2 +- src/core/payments/index.ts | 12 ++++++------ src/import/types.ts | 2 +- src/payments/fund.ts | 14 +++++++------- src/payments/setup.ts | 2 +- src/test/unit/payments-setup.test.ts | 10 +++++----- src/test/unit/payments.compute.test.ts | 8 +++++++- 9 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/add/types.ts b/src/add/types.ts index 4a7e1dee..0a5c5d02 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 73664522..2132fd45 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 ed38cffe..1c914f75 100644 --- a/src/common/upload-flow.ts +++ b/src/common/upload-flow.ts @@ -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) diff --git a/src/core/payments/index.ts b/src/core/payments/index.ts index 35ab2651..63c5d9ea 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 @@ -537,7 +537,7 @@ 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) + // Calculate lockup allowance (30 days worth) const epochsIn10Days = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY const lockupAllowance = rateAllowance * epochsIn10Days @@ -589,7 +589,7 @@ 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 + // Calculate how much this covers for 30 days const epochsIn10Days = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY const ratePerEpoch = usdfcAmount / epochsIn10Days @@ -599,7 +599,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. * @@ -837,7 +837,7 @@ export function calculateDepositCapacity( } // With infinite allowances, deposit is the only limiting factor - // Deposit needs to cover: lockup (10 days) + at least some buffer + // Deposit needs to cover: lockup (30 days) + at least some buffer const epochsIn10Days = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY const epochsPerMonth = TIME_CONSTANTS.EPOCHS_PER_MONTH diff --git a/src/import/types.ts b/src/import/types.ts index 267e2e11..09762f35 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 0879b204..4159446e 100644 --- a/src/payments/fund.ts +++ b/src/payments/fund.ts @@ -32,7 +32,7 @@ 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: { +async function ensureBelowThirtyDaysAllowed(opts: { spinner: Spinner warningLine1: string warningLine2: string @@ -52,7 +52,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 +347,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/setup.ts b/src/payments/setup.ts index d90c007a..8614d31a 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/payments-setup.test.ts b/src/test/unit/payments-setup.test.ts index 93304546..794f888c 100644 --- a/src/test/unit/payments-setup.test.ts +++ b/src/test/unit/payments-setup.test.ts @@ -196,7 +196,7 @@ describe('Payment Setup Tests', () => { '0xwarmstorage', rateAllowance, lockupAllowance, - 28800n, // 10 days * 2880 epochs/day + 86400, // 30 days * 2880 epochs/day '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) diff --git a/src/test/unit/payments.compute.test.ts b/src/test/unit/payments.compute.test.ts index 63301774..143baf7b 100644 --- a/src/test/unit/payments.compute.test.ts +++ b/src/test/unit/payments.compute.test.ts @@ -86,7 +86,7 @@ describe('computeAdjustmentForExactDays', () => { 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 available = perDay * 30n // exactly 30 days const status = makeStatus({ filecoinPayBalance: available, lockupUsed: 0n, rateUsed }) const res = computeAdjustmentForExactDays(status, days) const perHour = perDay / 24n @@ -163,9 +163,15 @@ 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 +<<<<<<< HEAD const lockupUsed = rateUsed * BigInt(10) * TIME_CONSTANTS.EPOCHS_PER_DAY // 10 days worth const filecoinPayBalance = (lockupUsed * 12n) / 10n // 20% buffer const status = makeStatus({ filecoinPayBalance, lockupUsed, rateUsed }) +======= + const lockupUsed = rateUsed * BigInt(30) * TIME_CONSTANTS.EPOCHS_PER_DAY // 30 days worth + const depositedAmount = (lockupUsed * 12n) / 10n // 20% buffer + const status = makeStatus({ depositedAmount, lockupUsed, rateUsed }) +>>>>>>> 1b0898e (10 day grace period => 30 day grace period) const pieceSizeBytes = 1024 * 1024 * 1024 // 1 GiB const pricePerTiBPerEpoch = 1_000_000_000_000_000n // 0.001 USDFC per TiB per epoch From 4d87bb3e467a10d7dcb9061147ef5e3a89ac68a9 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Wed, 22 Oct 2025 22:08:47 +1100 Subject: [PATCH 04/11] fix: plumb --warm-storage-address through `payments setup` --- src/payments/interactive.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/payments/interactive.ts b/src/payments/interactive.ts index f0a33720..fa007589 100644 --- a/src/payments/interactive.ts +++ b/src/payments/interactive.ts @@ -93,6 +93,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() From 607a9928b884b6f8eddea08351c92f080e0ee1d9 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 23 Oct 2025 22:18:25 +1100 Subject: [PATCH 05/11] fix: check lockup period during allowance check --- src/core/payments/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/payments/index.ts b/src/core/payments/index.ts index 63c5d9ea..0f5b568c 100644 --- a/src/core/payments/index.ts +++ b/src/core/payments/index.ts @@ -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, From 0e7750ccc88643feed01f110de89d59b24dc7b68 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 24 Oct 2025 18:31:15 +1100 Subject: [PATCH 06/11] chore: update to synapse@next tag, fix test failures & update more lockup mismatches --- package.json | 3 +-- src/common/upload-flow.ts | 4 ++-- src/core/payments/index.ts | 22 +++++++++++----------- src/payments/fund.ts | 5 +++-- src/payments/interactive.ts | 5 +++-- src/test/unit/payments-setup.test.ts | 15 ++++++++------- src/test/unit/payments.compute.test.ts | 4 ++-- 7 files changed, 30 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 543290a1..4b2edc24 100644 --- a/package.json +++ b/package.json @@ -97,11 +97,10 @@ "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-dev.1", "@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/common/upload-flow.ts b/src/common/upload-flow.ts index 1c914f75..979a744b 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' @@ -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 0f5b568c..4cf6ca7c 100644 --- a/src/core/payments/index.ts +++ b/src/core/payments/index.ts @@ -539,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 (30 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, @@ -582,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 @@ -591,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 30 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) } @@ -810,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 @@ -840,13 +840,13 @@ export function calculateDepositCapacity( // With infinite allowances, deposit is the only limiting factor // Deposit needs to cover: lockup (30 days) + at least some buffer - const epochsIn10Days = BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY + 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) @@ -854,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/payments/fund.ts b/src/payments/fund.ts index 4159446e..68e5fb1f 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,7 +32,7 @@ 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 +// Helper: confirm/warn or bail when target implies < lockup-days runway async function ensureBelowThirtyDaysAllowed(opts: { spinner: Spinner warningLine1: string @@ -43,7 +44,7 @@ async function ensureBelowThirtyDaysAllowed(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')) diff --git a/src/payments/interactive.ts b/src/payments/interactive.ts index fa007589..c29659d9 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, @@ -227,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({ @@ -299,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/test/unit/payments-setup.test.ts b/src/test/unit/payments-setup.test.ts index 794f888c..b56461f8 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, - 86400, // 30 days * 2880 epochs/day + 86400n, // 30 days * 2880 epochs/day (bigint) 'USDFC' ) }) @@ -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 143baf7b..563d895b 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 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', () => { From 9ebc05deea13e6a0e0069f3a41f3f688c8e03949 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Thu, 30 Oct 2025 08:32:47 -0600 Subject: [PATCH 07/11] post rebase wip --- src/test/unit/payments.compute.test.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/test/unit/payments.compute.test.ts b/src/test/unit/payments.compute.test.ts index 563d895b..3da20614 100644 --- a/src/test/unit/payments.compute.test.ts +++ b/src/test/unit/payments.compute.test.ts @@ -163,15 +163,9 @@ 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 -<<<<<<< HEAD - 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 }) -======= - const lockupUsed = rateUsed * BigInt(30) * TIME_CONSTANTS.EPOCHS_PER_DAY // 30 days worth - const depositedAmount = (lockupUsed * 12n) / 10n // 20% buffer - const status = makeStatus({ depositedAmount, lockupUsed, rateUsed }) ->>>>>>> 1b0898e (10 day grace period => 30 day grace period) const pieceSizeBytes = 1024 * 1024 * 1024 // 1 GiB const pricePerTiBPerEpoch = 1_000_000_000_000_000n // 0.001 USDFC per TiB per epoch From 7f608e0f172ff366125c3d95bfde3afb96f09920 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Thu, 30 Oct 2025 08:40:02 -0600 Subject: [PATCH 08/11] Update synapse to v0.35.0 --- package.json | 2 +- upload-action/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4b2edc24..849059a1 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "homepage": "https://github.com/filecoin-project/filecoin-pin#readme", "dependencies": { "@clack/prompts": "^0.11.0", - "@filoz/synapse-sdk": "^0.35.0-dev.1", + "@filoz/synapse-sdk": "^0.35.0", "@helia/unixfs": "^6.0.1", "@ipld/car": "^5.4.2", "commander": "^14.0.1", diff --git a/upload-action/package.json b/upload-action/package.json index 93ff8a08..046d9a0b 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": "../", From d9be05935db02dd96ea1577ad26f8d65aa640802 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Thu, 30 Oct 2025 11:52:58 -0600 Subject: [PATCH 09/11] Make things compile with add piece and create all in one --- package.json | 1 + src/add/add.ts | 3 --- src/core/synapse/index.ts | 34 +++------------------------------- src/core/upload/index.ts | 8 ++++---- src/core/upload/synapse.ts | 8 ++++---- src/data-set/inspect.ts | 14 +++++++------- src/import/import.ts | 3 --- 7 files changed, 19 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 849059a1..04d6a8de 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "@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 22f57363..f67435b9 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/core/synapse/index.ts b/src/core/synapse/index.ts index ba259b7a..45ef572e 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 7c360283..9aabc19e 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 ea107741..0b413c23 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 12a4ab11..bf3dba6d 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)}`) diff --git a/src/import/import.ts b/src/import/import.ts index 573f4238..c3b6aaab 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}`) From 7bdb7221ec8ca1b6bddbda60ae2f9d2bf7b0eef5 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:54:34 -0400 Subject: [PATCH 10/11] chore: fix onPieceAdded txHash --- upload-action/src/filecoin.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/upload-action/src/filecoin.js b/upload-action/src/filecoin.js index e9baf83e..220d5ea6 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)') From 8c16732c3ff6eca690d836bcf382922c41645be5 Mon Sep 17 00:00:00 2001 From: zenground0 Date: Thu, 30 Oct 2025 11:59:22 -0600 Subject: [PATCH 11/11] fix test --- src/test/unit/import.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/unit/import.test.ts b/src/test/unit/import.test.ts index 24599b67..42ecefc2 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), }), })