UNPKG

filecoin-pin

Version:

Bridge IPFS content to Filecoin Onchain Cloud using familiar tools

155 lines (127 loc) 5.97 kB
import { SIZE_CONSTANTS, TIME_CONSTANTS } from '@filoz/synapse-sdk' import { describe, expect, it } from 'vitest' import { applyFloorPricing, BUFFER_DENOMINATOR, BUFFER_NUMERATOR, calculatePieceUploadRequirements, calculateRequiredAllowances, computeAdjustmentForExactDaysWithPiece, DEFAULT_LOCKUP_DAYS, getFloorAllowances, type PaymentStatus, type ServiceApprovalStatus, } from '../../core/payments/index.js' function makeStatus(params: { filecoinPayBalance: bigint; lockupUsed?: bigint; rateUsed?: bigint }): PaymentStatus { const currentAllowances: ServiceApprovalStatus = { rateAllowance: 0n, lockupAllowance: 0n, lockupUsed: params.lockupUsed ?? 0n, rateUsed: params.rateUsed ?? 0n, maxLockupPeriod: BigInt(DEFAULT_LOCKUP_DAYS) * TIME_CONSTANTS.EPOCHS_PER_DAY, } return { network: 'calibration', address: '0x0000000000000000000000000000000000000000', filBalance: 0n, walletUsdfcBalance: 0n, filecoinPayBalance: params.filecoinPayBalance, currentAllowances, } } function getBufferedFloorDeposit(): bigint { const floor = getFloorAllowances() return (floor.lockupAllowance * BUFFER_NUMERATOR) / BUFFER_DENOMINATOR } describe('applyFloorPricing', () => { const mockPricing = 100_000_000_000_000n // Some arbitrary price per TiB per epoch const smallCases = [ { label: 'tiny file (1 byte)', size: 1 }, { label: 'small file (1 KB)', size: 1024 }, { label: 'medium file (1 MB)', size: 1024 * 1024 }, ] for (const { label, size } of smallCases) { it(`applies floor pricing to ${label}`, () => { const baseAllowances = calculateRequiredAllowances(size, mockPricing) const floorAdjusted = applyFloorPricing(baseAllowances) const floor = getFloorAllowances() expect(floorAdjusted.rateAllowance).toBe(floor.rateAllowance) expect(floorAdjusted.lockupAllowance).toBe(floor.lockupAllowance) }) } it('does not apply floor for large file when base cost exceeds floor', () => { // Use a very high price to ensure base cost exceeds floor const highPrice = 1_000_000_000_000_000_000n // 1 USDFC per TiB per epoch const largeFile = Number(SIZE_CONSTANTS.GiB) * 100 // 100 GiB const baseAllowances = calculateRequiredAllowances(largeFile, highPrice) const floorAdjusted = applyFloorPricing(baseAllowances) const floor = getFloorAllowances() // Base cost should exceed floor, so base is returned expect(floorAdjusted.rateAllowance).toBeGreaterThan(floor.rateAllowance) expect(floorAdjusted.lockupAllowance).toBeGreaterThan(floor.lockupAllowance) expect(floorAdjusted.rateAllowance).toBe(baseAllowances.rateAllowance) }) }) describe('calculatePieceUploadRequirements - Floor Pricing Integration', () => { const mockPricing = 100_000_000_000_000n const floorSizes = [ { label: '0-byte file', size: 0 }, { label: '1 KB file', size: 1024 }, ] for (const { label, size } of floorSizes) { it(`enforces floor price for ${label}`, () => { const status = makeStatus({ filecoinPayBalance: 0n }) const requirements = calculatePieceUploadRequirements(status, size, mockPricing) const floor = getFloorAllowances() expect(requirements.required.rateAllowance).toBe(floor.rateAllowance) expect(requirements.required.lockupAllowance).toBe(floor.lockupAllowance) }) } it('requires deposit with 10% buffer applied', () => { const status = makeStatus({ filecoinPayBalance: 0n }) const requirements = calculatePieceUploadRequirements(status, 0, mockPricing) const bufferedFloor = getBufferedFloorDeposit() expect(requirements.totalDepositNeeded).toBe(bufferedFloor) // User needs to deposit the buffered amount expect(requirements.insufficientDeposit).toBe(bufferedFloor) expect(requirements.canUpload).toBe(false) }) it('allows upload when deposit meets buffered floor price', () => { const bufferedFloor = getBufferedFloorDeposit() const status = makeStatus({ filecoinPayBalance: bufferedFloor }) const requirements = calculatePieceUploadRequirements(status, 0, mockPricing) expect(requirements.canUpload).toBe(true) expect(requirements.insufficientDeposit).toBe(0n) }) it('blocks upload when deposit is below buffered floor price', () => { const bufferedFloor = getBufferedFloorDeposit() const slightlyLess = bufferedFloor - 1n const status = makeStatus({ filecoinPayBalance: slightlyLess }) const requirements = calculatePieceUploadRequirements(status, 0, mockPricing) expect(requirements.canUpload).toBe(false) expect(requirements.insufficientDeposit).toBe(1n) }) }) describe('computeAdjustmentForExactDaysWithPiece - Floor Pricing Integration', () => { const mockPricing = 100_000_000_000_000n it('applies floor pricing for small file in auto-fund calculation', () => { const status = makeStatus({ filecoinPayBalance: 0n, lockupUsed: 0n, rateUsed: 0n }) const adjustment = computeAdjustmentForExactDaysWithPiece(status, 30, 1024, mockPricing) const floor = getFloorAllowances() // Should use floor-adjusted allowances expect(adjustment.newRateUsed).toBe(floor.rateAllowance) expect(adjustment.newLockupUsed).toBe(floor.lockupAllowance) }) it('calculates correct deposit delta with floor pricing', () => { const status = makeStatus({ filecoinPayBalance: 0n, lockupUsed: 0n, rateUsed: 0n }) const adjustment = computeAdjustmentForExactDaysWithPiece(status, 30, 0, mockPricing) const floor = getFloorAllowances() // Target deposit = buffered lockup + runway const bufferedLockup = getBufferedFloorDeposit() const perDay = floor.rateAllowance * TIME_CONSTANTS.EPOCHS_PER_DAY const safety = perDay > 0n ? perDay / 24n : 1n const runwayCost = 30n * perDay + safety expect(adjustment.delta).toBeGreaterThan(0n) expect(adjustment.targetDeposit).toBe(bufferedLockup + runwayCost) }) })