Skip to content

Commit 1064d78

Browse files
authored
feat: make WarmStorage approvals infinite, focus only on deposit (#47)
Remove all of the complexity of dealing with approvals and rate limits by just setting them to be infinite by default for WarmStorage (i.e. it's a trusted service and your risk is managed by deposit). This lets us trim down the UI for dealing with payments and simplifies the messaging.
1 parent 743b5b8 commit 1064d78

File tree

8 files changed

+437
-452
lines changed

8 files changed

+437
-452
lines changed

src/add/add.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ export async function runAdd(options: AddOptions): Promise<AddResult> {
118118

119119
spinner.stop(`${pc.green('✓')} Connected to ${pc.bold(network)}`)
120120

121-
// Validate basic payment setup (actual CAR size will be checked later)
122-
spinner.start('Validating payment setup (basic requirements)...')
121+
// Check payment setup (may configure permissions if needed)
122+
// Actual CAR size will be checked later
123+
spinner.start('Checking payment setup...')
123124
await validatePaymentSetup(synapse, 0, spinner)
124125

125126
// Create CAR from file or directory
@@ -144,8 +145,8 @@ export async function runAdd(options: AddOptions): Promise<AddResult> {
144145
const carSize = carData.length
145146
spinner.stop(`${pc.green('✓')} IPFS content loaded (${formatFileSize(carSize)})`)
146147

147-
// Validate payment setup with actual CAR size
148-
spinner.start('Validating payment capacity for CAR size...')
148+
// Validate payment capacity for actual file size
149+
spinner.start('Checking payment capacity...')
149150
await validatePaymentSetup(synapse, carSize, spinner)
150151

151152
// Create storage context

src/common/upload-flow.ts

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import type { CID } from 'multiformats/cid'
1010
import pc from 'picocolors'
1111
import type { Logger } from 'pino'
1212
import { formatUSDFC, validatePaymentRequirements } from '../payments/setup.js'
13-
import { checkFILBalance, checkUSDFCBalance, validatePaymentCapacity } from '../synapse/payments.js'
13+
import {
14+
checkAllowances,
15+
checkFILBalance,
16+
checkUSDFCBalance,
17+
setMaxAllowances,
18+
validatePaymentCapacity,
19+
} from '../synapse/payments.js'
1420
import { cleanupSynapseService, type SynapseService } from '../synapse/service.js'
1521
import { getDownloadURL, type SynapseUploadResult, uploadToSynapse } from '../synapse/upload.js'
1622
import { cancel, formatFileSize } from '../utils/cli-helpers.js'
@@ -87,7 +93,22 @@ export async function validatePaymentSetup(
8793
process.exit(1)
8894
}
8995

90-
// Check capacity for this specific file
96+
// Check WarmStorage permissions and configure if needed
97+
spinner?.message('Checking WarmStorage permissions...')
98+
const checkResult = await checkAllowances(synapse)
99+
100+
if (checkResult.needsUpdate) {
101+
spinner?.message('Configuring WarmStorage permissions (one-time setup)...')
102+
const setResult = await setMaxAllowances(synapse)
103+
spinner?.stop(`${pc.green('✓')} WarmStorage permissions configured`)
104+
log.indent(pc.gray(`Transaction: ${setResult.transactionHash}`))
105+
log.flush()
106+
spinner?.start('Validating payment capacity...')
107+
} else {
108+
spinner?.message('Validating payment capacity...')
109+
}
110+
111+
// Check capacity for this specific file (this also calls checkAndSetAllowances but will be a no-op now)
91112
const capacityCheck = await validatePaymentCapacity(synapse, fileSize)
92113

93114
if (!capacityCheck.canUpload) {
@@ -119,30 +140,15 @@ function displayPaymentIssues(
119140
fileSize: number,
120141
spinner?: ReturnType<typeof import('../utils/cli-helpers.js').createSpinner>
121142
): void {
122-
spinner?.stop(`${pc.red('✗')} Insufficient payment capacity for this file`)
143+
spinner?.stop(`${pc.red('✗')} Insufficient deposit for this file`)
123144
log.line('')
124145
log.line(pc.bold('File Requirements:'))
125146
log.indent(`File size: ${formatFileSize(fileSize)} (${capacityCheck.storageTiB.toFixed(4)} TiB)`)
126147
log.indent(`Storage cost: ${formatUSDFC(capacityCheck.required.rateAllowance)} USDFC/epoch`)
127-
log.indent(`10-day lockup: ${formatUSDFC(capacityCheck.required.lockupAllowance)} USDFC`)
128-
log.line('')
129-
130-
log.line(pc.bold(`${pc.red('Issues found:')}`))
131-
if (capacityCheck.issues.insufficientDeposit) {
132-
log.indent(
133-
`${pc.red('✗')} Insufficient deposit (need ${formatUSDFC(capacityCheck.issues.insufficientDeposit)} more)`
134-
)
135-
}
136-
if (capacityCheck.issues.insufficientRateAllowance) {
137-
log.indent(
138-
`${pc.red('✗')} Rate allowance too low (need ${formatUSDFC(capacityCheck.issues.insufficientRateAllowance)} more per epoch)`
139-
)
140-
}
141-
if (capacityCheck.issues.insufficientLockupAllowance) {
142-
log.indent(
143-
`${pc.red('✗')} Lockup allowance too low (need ${formatUSDFC(capacityCheck.issues.insufficientLockupAllowance)} more)`
144-
)
145-
}
148+
log.indent(
149+
`Required deposit: ${formatUSDFC(capacityCheck.required.lockupAllowance + capacityCheck.required.lockupAllowance / 10n)} USDFC`
150+
)
151+
log.indent(pc.gray('(includes 10-day safety reserve)'))
146152
log.line('')
147153

148154
log.line(pc.bold('Suggested actions:'))
@@ -151,20 +157,13 @@ function displayPaymentIssues(
151157
})
152158
log.line('')
153159

154-
// Calculate suggested parameters for payment setup
160+
// Calculate suggested deposit
155161
const suggestedDeposit = capacityCheck.issues.insufficientDeposit
156162
? formatUSDFC(capacityCheck.issues.insufficientDeposit)
157163
: '0'
158-
const suggestedStorage = `${Math.ceil(capacityCheck.storageTiB * 10) / 10}TiB/month`
159164

160-
log.line(`${pc.yellow('⚠')} To fix these issues, run:`)
161-
if (capacityCheck.issues.insufficientDeposit) {
162-
log.indent(
163-
pc.cyan(`filecoin-pin payments setup --deposit ${suggestedDeposit} --storage ${suggestedStorage} --auto`)
164-
)
165-
} else {
166-
log.indent(pc.cyan(`filecoin-pin payments setup --storage ${suggestedStorage} --auto`))
167-
}
165+
log.line(`${pc.yellow('⚠')} To fix this, run:`)
166+
log.indent(pc.cyan(`filecoin-pin payments setup --deposit ${suggestedDeposit} --auto`))
168167
log.flush()
169168
}
170169

src/import/import.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ export async function runCarImport(options: ImportOptions): Promise<ImportResult
189189

190190
spinner.stop(`${pc.green('✓')} Connected to ${pc.bold(network)}`)
191191

192-
// Step 5: Validate payment setup
193-
spinner.start('Validating payment setup...')
192+
// Step 5: Validate payment setup (may configure permissions if needed)
193+
spinner.start('Checking payment setup...')
194194
await validatePaymentSetup(synapse, fileStat.size, spinner)
195195

196196
// Step 6: Create storage context now that payments are validated

src/payments/auto.ts

Lines changed: 33 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,24 @@
66
* options to complete the setup without user interaction.
77
*/
88

9-
import { RPC_URLS, Synapse, TIME_CONSTANTS } from '@filoz/synapse-sdk'
9+
import { RPC_URLS, Synapse } from '@filoz/synapse-sdk'
1010
import { ethers } from 'ethers'
1111
import pc from 'picocolors'
12+
import { calculateDepositCapacity, checkAndSetAllowances } from '../synapse/payments.js'
1213
import { cleanupProvider } from '../synapse/service.js'
1314
import { cancel, createSpinner, intro, outro } from '../utils/cli-helpers.js'
1415
import { log } from '../utils/cli-logger.js'
1516
import {
16-
calculateStorageAllowances,
17-
calculateStorageFromUSDFC,
1817
checkFILBalance,
1918
checkUSDFCBalance,
2019
depositUSDFC,
2120
displayAccountInfo,
2221
displayDepositWarning,
23-
displayPaymentSummary,
24-
displayServicePermissions,
2522
formatUSDFC,
2623
getPaymentStatus,
27-
parseStorageAllowance,
28-
setServiceApprovals,
2924
validatePaymentRequirements,
3025
} from './setup.js'
31-
import type { PaymentSetupOptions, StorageAllowances } from './types.js'
26+
import type { PaymentSetupOptions } from './types.js'
3227

3328
/**
3429
* Run automatic payment setup with defaults
@@ -67,27 +62,6 @@ export async function runAutoSetup(options: PaymentSetupOptions): Promise<void>
6762
process.exit(1)
6863
}
6964

70-
// 4. Parse storage allowance early to validate format
71-
if (!options.rateAllowance) {
72-
console.error(pc.red('Error: Storage allowance is required'))
73-
process.exit(1)
74-
}
75-
76-
let parsedTiB: number | null
77-
let rawUsdfcPerEpoch: string | null = null
78-
try {
79-
parsedTiB = parseStorageAllowance(options.rateAllowance)
80-
if (parsedTiB === null) {
81-
// parseStorageAllowance already validated it's a valid USDFC amount
82-
// (it would have thrown otherwise), so we can safely save it
83-
rawUsdfcPerEpoch = options.rateAllowance
84-
}
85-
} catch (error) {
86-
console.error(pc.red(`Error: Invalid storage allowance '${options.rateAllowance}'`))
87-
console.error(pc.red(error instanceof Error ? error.message : String(error)))
88-
process.exit(1)
89-
}
90-
9165
const spinner = createSpinner()
9266
spinner.start('Initializing connection...')
9367

@@ -150,25 +124,23 @@ export async function runAutoSetup(options: PaymentSetupOptions): Promise<void>
150124
const storageInfo = await synapse.storage.getStorageInfo()
151125
const pricePerTiBPerEpoch = storageInfo.pricing.noCDN.perTiBPerEpoch
152126

153-
// Calculate storage allowances now that we have synapse
154-
let allowances: StorageAllowances
155-
if (parsedTiB !== null) {
156-
// User specified TiB/month, calculate allowances
157-
allowances = calculateStorageAllowances(parsedTiB, pricePerTiBPerEpoch)
158-
} else if (rawUsdfcPerEpoch !== null) {
159-
// User specified USDFC per epoch directly
160-
const usdfcPerEpochBigint = ethers.parseUnits(rawUsdfcPerEpoch, 18)
161-
const capacityTiB = calculateStorageFromUSDFC(usdfcPerEpochBigint, pricePerTiBPerEpoch)
162-
allowances = calculateStorageAllowances(capacityTiB, pricePerTiBPerEpoch)
127+
// Track if any changes were made
128+
let actionsTaken = false
129+
let actualDepositAmount = 0n
130+
131+
// Auto-set max allowances for WarmStorage
132+
spinner.start('Configuring WarmStorage permissions...')
133+
const allowanceResult = await checkAndSetAllowances(synapse)
134+
if (allowanceResult.updated) {
135+
spinner.stop(`${pc.green('✓')} WarmStorage permissions configured`)
136+
log.line(pc.bold('Transaction:'))
137+
log.indent(pc.gray(allowanceResult.transactionHash || 'Unknown'))
138+
log.flush()
139+
actionsTaken = true
163140
} else {
164-
// This shouldn't happen due to earlier validation
165-
throw new Error('Invalid storage allowance state')
141+
spinner.stop(`${pc.green('✓')} WarmStorage permissions already configured`)
166142
}
167143

168-
// Handle deposits
169-
let actualDepositAmount = 0n
170-
let actionsTaken = false // Track if any changes were made
171-
172144
if (status.depositedAmount < targetDeposit) {
173145
const depositAmount = targetDeposit - status.depositedAmount
174146
actualDepositAmount = depositAmount
@@ -199,83 +171,27 @@ export async function runAutoSetup(options: PaymentSetupOptions): Promise<void>
199171
spinner.stop(`${pc.green('✓')} Deposit already sufficient (${formatUSDFC(status.depositedAmount)} USDFC)`)
200172
}
201173

202-
// Set storage allowances
203-
spinner.start(`Checking WarmStorage service allowances (${options.rateAllowance})...`)
204-
205-
// Check if we need to update allowances
206-
const currentAllowances = status.currentAllowances
207-
let needsUpdate = false
208-
209-
if (currentAllowances.rateAllowance < allowances.rateAllowance) {
210-
needsUpdate = true
211-
}
212-
213-
if (currentAllowances.lockupAllowance < allowances.lockupAllowance) {
214-
needsUpdate = true
215-
}
216-
217-
// Calculate total deposit for capacity display
174+
// Calculate capacity for final summary
218175
const totalDeposit = status.depositedAmount + actualDepositAmount
176+
const capacity = calculateDepositCapacity(totalDeposit, pricePerTiBPerEpoch)
219177

220-
if (needsUpdate) {
221-
spinner.message('Setting WarmStorage service approvals...')
222-
const approvalTx = await setServiceApprovals(synapse, allowances.rateAllowance, allowances.lockupAllowance)
223-
spinner.stop(`${pc.green('✓')} WarmStorage service approvals updated`)
224-
actionsTaken = true
225-
226-
log.line(pc.bold('Transaction:'))
227-
log.indent(pc.gray(approvalTx))
228-
log.flush()
229-
230-
// Display new permissions with capacity info
231-
const monthlyRate = allowances.rateAllowance * TIME_CONSTANTS.EPOCHS_PER_MONTH
232-
displayServicePermissions(
233-
'New WarmStorage Service Limits:',
234-
monthlyRate,
235-
allowances.lockupAllowance,
236-
totalDeposit,
237-
pricePerTiBPerEpoch
238-
)
239-
} else {
240-
spinner.stop(`${pc.green('✓')} WarmStorage service permissions already sufficient`)
241-
242-
// Display current permissions with capacity info
243-
const monthlyRate = currentAllowances.rateAllowance * TIME_CONSTANTS.EPOCHS_PER_MONTH
244-
displayServicePermissions(
245-
'Your Current WarmStorage Service Limits:',
246-
monthlyRate,
247-
currentAllowances.lockupAllowance,
248-
totalDeposit,
249-
pricePerTiBPerEpoch
250-
)
251-
}
178+
// Final summary
179+
spinner.start('Completing setup...')
180+
spinner.stop('━━━ Configuration Summary ━━━')
252181

253-
// Get final values
254-
let finalRateAllowance: bigint
255-
let finalLockupAllowance: bigint
182+
log.line(`Network: ${pc.bold(network)}`)
183+
log.line(`Deposit: ${formatUSDFC(totalDeposit)} USDFC`)
256184

257-
if (needsUpdate) {
258-
finalRateAllowance = allowances.rateAllowance
259-
finalLockupAllowance = allowances.lockupAllowance
260-
} else {
261-
finalRateAllowance = currentAllowances.rateAllowance
262-
finalLockupAllowance = currentAllowances.lockupAllowance
185+
if (capacity.gibPerMonth > 0) {
186+
const capacityStr =
187+
capacity.gibPerMonth >= 1024
188+
? `${(capacity.gibPerMonth / 1024).toFixed(1)} TiB`
189+
: `${capacity.gibPerMonth.toFixed(1)} GiB`
190+
log.line(`Storage: ~${capacityStr} for 1 month`)
263191
}
264192

265-
// Final summary
266-
spinner.start('Completing setup...')
267-
spinner.stop('━━━ Setup Complete ━━━')
268-
269-
displayPaymentSummary(
270-
network,
271-
filStatus.balance,
272-
filStatus.isCalibnet,
273-
usdfcBalance,
274-
totalDeposit,
275-
finalRateAllowance,
276-
finalLockupAllowance,
277-
pricePerTiBPerEpoch
278-
)
193+
log.line(`Status: ${pc.green('Ready to upload')}`)
194+
log.flush()
279195

280196
// Show deposit warning if needed
281197
displayDepositWarning(totalDeposit, status.currentAllowances.lockupUsed)
@@ -284,7 +200,7 @@ export async function runAutoSetup(options: PaymentSetupOptions): Promise<void>
284200
if (actionsTaken) {
285201
outro('Payment setup completed successfully')
286202
} else {
287-
outro('Payment setup already configured - no changes needed')
203+
outro('Payment setup already configured - ready to use')
288204
}
289205
} catch (error) {
290206
spinner.stop() // Stop spinner without message

0 commit comments

Comments
 (0)