filecoin-pin
Version:
Bridge IPFS content to Filecoin Onchain Cloud using familiar tools
155 lines (127 loc) • 5.97 kB
text/typescript
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)
})
})