Skip to content

Commit 121e342

Browse files
redpanda-fSgtPooki
andauthored
feat: deposit with permit integration (#202)
* feat: remove approval tx separate * fix: docstring * fix: use depositWithPermitAndApproveOperator * fix: approved operator * fix: else case * fix: await tx.wait() * fix: lint * Update src/core/payments/index.ts --------- Co-authored-by: Russell Dempsey <[email protected]>
1 parent 39a03c0 commit 121e342

File tree

8 files changed

+44
-109
lines changed

8 files changed

+44
-109
lines changed

src/common/upload-flow.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,6 @@ export async function performAutoFunding(synapse: Synapse, fileSize: number, spi
8383
log.indent(
8484
`Runway: ~${result.newRunwayDays} day(s)${result.newRunwayHours > 0 ? ` ${result.newRunwayHours} hour(s)` : ''}`
8585
)
86-
if (result.approvalTx) {
87-
log.indent(pc.gray(`Approval tx: ${result.approvalTx}`))
88-
}
8986
if (result.transactionHash) {
9087
log.indent(pc.gray(`Transaction: ${result.transactionHash}`))
9188
}

src/core/payments/index.ts

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -238,14 +238,14 @@ export function validatePaymentRequirements(
238238
/**
239239
* Deposit USDFC into the Payments contract
240240
*
241-
* This demonstrates the two-step process required for depositing ERC20 tokens:
242-
* 1. Approve the Payments contract to spend USDFC (standard ERC20 approval)
243-
* 2. Call deposit to move funds into the Payments contract
241+
* This demonstrates the single-step process required for depositing ERC20 tokens:
242+
* 1. If approval is insufficient, use permit to approve and deposit in one transaction
243+
* 2. If approval is sufficient, directly call deposit
244244
*
245245
* Example usage:
246246
* ```typescript
247247
* const amountToDeposit = ethers.parseUnits('100', 18) // 100 USDFC
248-
* const { approvalTx, depositTx } = await depositUSDFC(synapse, amountToDeposit)
248+
* const { depositTx } = await depositUSDFC(synapse, amountToDeposit)
249249
* console.log(`Deposit transaction: ${depositTx}`)
250250
* ```
251251
*
@@ -257,36 +257,29 @@ export async function depositUSDFC(
257257
synapse: Synapse,
258258
amount: bigint
259259
): Promise<{
260-
approvalTx?: string
261260
depositTx: string
262261
}> {
263-
const paymentsAddress = synapse.getPaymentsAddress()
262+
const needsAllowanceUpdate = (await checkAllowances(synapse)).needsUpdate
263+
const amountMoreThanCurrentAllowance =
264+
(await synapse.payments.allowance(synapse.getPaymentsAddress(), TOKENS.USDFC)) < amount
264265

265-
// Step 1: Check current allowance
266-
const currentAllowance = await synapse.payments.allowance(paymentsAddress, TOKENS.USDFC)
266+
let tx: ethers.TransactionResponse
267267

268-
let approvalTx: string | undefined
269-
270-
// Step 2: Approve if needed (skip if already approved)
271-
if (currentAllowance < amount) {
272-
const approveTx = await synapse.payments.approve(paymentsAddress, amount, TOKENS.USDFC)
273-
await approveTx.wait()
274-
approvalTx = approveTx.hash
275-
}
276-
277-
// Step 3: Make the deposit
278-
const depositTransaction = await synapse.payments.deposit(amount, TOKENS.USDFC)
279-
await depositTransaction.wait()
280-
281-
const result: { approvalTx?: string; depositTx: string } = {
282-
depositTx: depositTransaction.hash,
268+
if (amountMoreThanCurrentAllowance || needsAllowanceUpdate) {
269+
tx = await synapse.payments.depositWithPermitAndApproveOperator(
270+
amount,
271+
synapse.getWarmStorageAddress(),
272+
MAX_RATE_ALLOWANCE,
273+
MAX_LOCKUP_ALLOWANCE,
274+
BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY
275+
)
276+
} else {
277+
tx = await synapse.payments.deposit(amount, TOKENS.USDFC)
283278
}
284279

285-
if (approvalTx) {
286-
result.approvalTx = approvalTx
287-
}
280+
await tx.wait()
288281

289-
return result
282+
return { depositTx: tx.hash }
290283
}
291284

292285
/**

src/payments/auto.ts

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,6 @@ export async function runAutoSetup(options: PaymentSetupOptions): Promise<void>
110110
let actionsTaken = false
111111
let actualFilecoinPayTopUp = 0n
112112

113-
// Auto-set max allowances for WarmStorage
114-
spinner.start('Configuring WarmStorage permissions...')
115-
const allowanceResult = await checkAndSetAllowances(synapse)
116-
if (allowanceResult.updated) {
117-
spinner.stop(`${pc.green('✓')} WarmStorage permissions configured`)
118-
log.line(pc.bold('Transaction:'))
119-
log.indent(pc.gray(allowanceResult.transactionHash || 'Unknown'))
120-
log.flush()
121-
actionsTaken = true
122-
} else {
123-
spinner.stop(`${pc.green('✓')} WarmStorage permissions already configured`)
124-
}
125-
126113
if (status.filecoinPayBalance < targetFilecoinPayBalance) {
127114
const neededFilecoinPayTopUp = targetFilecoinPayBalance - status.filecoinPayBalance
128115
actualFilecoinPayTopUp = neededFilecoinPayTopUp
@@ -137,20 +124,22 @@ export async function runAutoSetup(options: PaymentSetupOptions): Promise<void>
137124
}
138125

139126
spinner.start(`Depositing ${formatUSDFC(neededFilecoinPayTopUp)} USDFC...`)
140-
const { approvalTx, depositTx } = await depositUSDFC(synapse, neededFilecoinPayTopUp)
127+
const { depositTx } = await depositUSDFC(synapse, neededFilecoinPayTopUp)
141128
spinner.stop(`${pc.green('✓')} Deposited ${formatUSDFC(neededFilecoinPayTopUp)} USDFC`)
142129
actionsTaken = true
143130

144131
log.line(pc.bold('Transaction details:'))
145-
if (approvalTx) {
146-
log.indent(pc.gray(`Approval: ${approvalTx}`))
147-
}
148132
log.indent(pc.gray(`Deposit: ${depositTx}`))
149133
log.flush()
150134
} else {
151135
// Use a dummy spinner to get consistent formatting
152136
spinner.start('Checking deposit...')
153-
spinner.stop(`${pc.green('✓')} Deposit already sufficient (${formatUSDFC(status.filecoinPayBalance)} USDFC)`)
137+
const { updated, transactionHash } = await checkAndSetAllowances(synapse)
138+
if (updated) {
139+
spinner.stop(`${pc.green('✓')} Updated payment allowances, tx: ${transactionHash}`)
140+
} else {
141+
spinner.stop(`${pc.green('✓')} Deposit already sufficient (${formatUSDFC(status.filecoinPayBalance)} USDFC)`)
142+
}
154143
}
155144

156145
// Calculate capacity for final summary

src/payments/deposit.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,10 @@ export async function runDeposit(options: DepositOptions): Promise<void> {
135135
}
136136

137137
spinner.start(`Depositing ${formatUSDFC(depositAmount)} USDFC...`)
138-
const { approvalTx, depositTx } = await depositUSDFC(synapse, depositAmount)
138+
const { depositTx } = await depositUSDFC(synapse, depositAmount)
139139
spinner.stop(`${pc.green('✓')} Deposit complete`)
140140

141141
log.line(pc.bold('Transaction details:'))
142-
if (approvalTx) {
143-
log.indent(pc.gray(`Approval: ${approvalTx}`))
144-
}
145142
log.indent(pc.gray(`Deposit: ${depositTx}`))
146143
log.flush()
147144

src/payments/fund.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,9 @@ async function performAdjustment(params: {
9292
}
9393
}
9494
spinner.start(depositMsg)
95-
const { approvalTx, depositTx } = await depositUSDFC(synapse, needed)
95+
const { depositTx } = await depositUSDFC(synapse, needed)
9696
spinner.stop(`${pc.green('✓')} Deposit complete`)
9797
log.line(pc.bold('Transaction details:'))
98-
if (approvalTx) log.indent(pc.gray(`Approval: ${approvalTx}`))
9998
log.indent(pc.gray(`Deposit: ${depositTx}`))
10099
log.flush()
101100
} else if (delta < 0n) {
@@ -197,7 +196,6 @@ export async function autoFund(options: AutoFundOptions): Promise<FundingAdjustm
197196
const depositMsg = `Depositing ${formatUSDFC(delta)} USDFC to ensure ${MIN_RUNWAY_DAYS} day(s) runway...`
198197
spinner?.message(depositMsg)
199198
const depositResult = await depositUSDFC(synapse, delta)
200-
const approvalTx = depositResult.approvalTx
201199
const transactionHash = depositResult.depositTx
202200
spinner?.message(`${pc.green('✓')} Deposit complete`)
203201

@@ -211,7 +209,6 @@ export async function autoFund(options: AutoFundOptions): Promise<FundingAdjustm
211209
return {
212210
adjusted: true,
213211
delta,
214-
approvalTx,
215212
transactionHash,
216213
newDepositedAmount: updated.filecoinPayBalance,
217214
newRunwayDays: newRunway,

src/payments/interactive.ts

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ import { ethers } from 'ethers'
1212
import pc from 'picocolors'
1313
import {
1414
calculateDepositCapacity,
15-
checkAllowances,
15+
checkAndSetAllowances,
1616
checkFILBalance,
1717
checkUSDFCBalance,
1818
DEFAULT_LOCKUP_DAYS,
1919
depositUSDFC,
2020
getPaymentStatus,
21-
setMaxAllowances,
2221
validatePaymentRequirements,
2322
} from '../core/payments/index.js'
2423
import { cleanupProvider, cleanupSynapseService } from '../core/synapse/index.js'
@@ -155,41 +154,6 @@ export async function runInteractiveSetup(options: PaymentSetupOptions): Promise
155154
let depositAmount = 0n
156155
let actionsTaken = false // Track if any changes were made
157156

158-
// Check and optionally set max allowances for WarmStorage
159-
s.start('Checking WarmStorage permissions...')
160-
const allowanceCheck = await checkAllowances(synapse)
161-
162-
if (allowanceCheck.needsUpdate) {
163-
s.stop(`${pc.yellow('⚠')} WarmStorage authorization required`)
164-
log.line('')
165-
log.line(pc.bold('WarmStorage Service Authorization'))
166-
log.line('WarmStorage needs permissions to manage storage payments on your behalf.')
167-
log.line('This is a one-time setup.')
168-
log.line('')
169-
170-
const shouldSetAllowances = await confirm({
171-
message: 'Authorize WarmStorage?',
172-
initialValue: true,
173-
})
174-
175-
if (isCancel(shouldSetAllowances)) {
176-
cancel('Setup cancelled')
177-
process.exit(1)
178-
}
179-
180-
if (shouldSetAllowances) {
181-
s.start('Setting WarmStorage permissions...')
182-
const setResult = await setMaxAllowances(synapse)
183-
s.stop(`${pc.green('✓')} WarmStorage permissions configured`)
184-
log.indent(pc.gray(`Transaction: ${setResult.transactionHash}`))
185-
actionsTaken = true
186-
} else {
187-
log.line(pc.yellow('⚠ Skipping WarmStorage authorization. You may need to set this before using storage.'))
188-
}
189-
} else {
190-
s.stop(`${pc.green('✓')} WarmStorage permissions already configured`)
191-
}
192-
193157
// Show current deposit capacity
194158
const currentCapacity = calculateDepositCapacity(status.filecoinPayBalance, pricePerTiBPerEpoch)
195159
log.line(pc.bold('Current Storage Capacity:'))
@@ -258,12 +222,9 @@ export async function runInteractiveSetup(options: PaymentSetupOptions): Promise
258222
depositAmount = ethers.parseUnits(amountStr, 18)
259223

260224
s.start('Depositing USDFC...')
261-
const { approvalTx, depositTx } = await depositUSDFC(synapse, depositAmount)
225+
const { depositTx } = await depositUSDFC(synapse, depositAmount)
262226
s.stop(`${pc.green('✓')} Deposit complete`)
263227

264-
if (approvalTx) {
265-
log.indent(pc.gray(`Approval tx: ${approvalTx}`))
266-
}
267228
log.indent(pc.gray(`Deposit tx: ${depositTx}`))
268229
actionsTaken = true
269230

@@ -278,6 +239,13 @@ export async function runInteractiveSetup(options: PaymentSetupOptions): Promise
278239
log.indent(`Total deposit: ${formatUSDFC(status.filecoinPayBalance + depositAmount)} USDFC`)
279240
log.indent(`Capacity: ~${newCapacityStr} for 1 month`)
280241
log.flush()
242+
} else {
243+
const { updated, transactionHash } = await checkAndSetAllowances(synapse)
244+
if (updated) {
245+
log.indent(`${pc.green('✓')} Updated payment allowances, tx: ${transactionHash}`)
246+
} else {
247+
log.indent(`${pc.green('✓')} Deposit already sufficient (${formatUSDFC(status.filecoinPayBalance)} USDFC)`)
248+
}
281249
}
282250

283251
// Final summary

src/payments/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ export interface FundingAdjustmentResult {
2525
adjusted: boolean
2626
/** Amount deposited (positive) or withdrawn (negative) */
2727
delta: bigint
28-
/** Approval transaction hash (if deposit required approval) */
29-
approvalTx?: string | undefined
3028
/** Deposit or withdraw transaction hash */
3129
transactionHash?: string | undefined
3230
/** Updated deposited amount after adjustment */

src/test/unit/payments-setup.test.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ describe('Payment Setup Tests', () => {
9393
lockupUsed: 0n,
9494
}),
9595
allowance: vi.fn().mockResolvedValue(ethers.parseUnits('0', 18)),
96-
approve: vi.fn().mockResolvedValue({
96+
depositWithPermitAndApproveOperator: vi.fn().mockResolvedValue({
9797
wait: vi.fn(),
98-
hash: '0xapproval',
98+
hash: '0xdepositWithPermitAndApproveOperator',
9999
}),
100100
deposit: vi.fn().mockResolvedValue({
101101
wait: vi.fn(),
@@ -166,21 +166,17 @@ describe('Payment Setup Tests', () => {
166166

167167
const result = await depositUSDFC(mockSynapse, ethers.parseUnits('5', 18))
168168

169-
expect(result.approvalTx).toBeUndefined()
170-
expect(result.depositTx).toBe('0xdeposit')
171-
expect(mockSynapse.payments.approve).not.toHaveBeenCalled()
172-
expect(mockSynapse.payments.deposit).toHaveBeenCalled()
169+
expect(result.depositTx).toBe('0xdepositWithPermitAndApproveOperator')
170+
expect(mockSynapse.payments.depositWithPermitAndApproveOperator).toHaveBeenCalled()
173171
})
174172

175173
it('should approve and deposit when allowance insufficient', async () => {
176174
mockSynapse.payments.allowance.mockResolvedValue(ethers.parseUnits('0', 18))
177175

178176
const result = await depositUSDFC(mockSynapse, ethers.parseUnits('5', 18))
179177

180-
expect(result.approvalTx).toBe('0xapproval')
181-
expect(result.depositTx).toBe('0xdeposit')
182-
expect(mockSynapse.payments.approve).toHaveBeenCalled()
183-
expect(mockSynapse.payments.deposit).toHaveBeenCalled()
178+
expect(result.depositTx).toBe('0xdepositWithPermitAndApproveOperator')
179+
expect(mockSynapse.payments.depositWithPermitAndApproveOperator).toHaveBeenCalled()
184180
})
185181
})
186182

0 commit comments

Comments
 (0)