UNPKG

filecoin-pin

Version:

Bridge IPFS content to Filecoin Onchain Cloud using familiar tools

154 lines 7.99 kB
import { TIME_CONSTANTS } from '@filoz/synapse-sdk'; import { describe, expect, it } from 'vitest'; import { computeAdjustmentForExactDays, computeAdjustmentForExactDaysWithPiece, computeAdjustmentForExactDeposit, computeTopUpForDuration, DEFAULT_LOCKUP_DAYS, } from '../../core/payments/index.js'; function makeStatus(params) { const currentAllowances = { 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, }; } describe('computeTopUpForDuration', () => { it('returns 0 topUp when days <= 0', () => { const rateUsed = 1000000000000000000n; // 1 USDFC/epoch const status = makeStatus({ filecoinPayBalance: 0n, rateUsed }); const res = computeTopUpForDuration(status, 0); expect(res.topUp).toBe(0n); expect(res.perDay).toBe(rateUsed * TIME_CONSTANTS.EPOCHS_PER_DAY); }); it('returns 0 topUp when rateUsed = 0', () => { const status = makeStatus({ filecoinPayBalance: 1000n, rateUsed: 0n }); const res = computeTopUpForDuration(status, 10); expect(res.topUp).toBe(0n); expect(res.perDay).toBe(0n); }); it('returns 0 topUp when available already covers the period', () => { const rateUsed = 1000000000000000000n; // 1 USDFC/epoch const perDay = rateUsed * TIME_CONSTANTS.EPOCHS_PER_DAY; const days = 10; const filecoinPayBalance = perDay * BigInt(days); const status = makeStatus({ filecoinPayBalance, lockupUsed: 0n, rateUsed }); const res = computeTopUpForDuration(status, days); expect(res.topUp).toBe(0n); expect(res.available).toBe(filecoinPayBalance); }); it('returns required topUp when available is insufficient', () => { const rateUsed = 1000000000000000000n; // 1 USDFC/epoch const perDay = rateUsed * TIME_CONSTANTS.EPOCHS_PER_DAY; const days = 10; const available = perDay * 5n; // only 5 days funded const lockupUsed = 0n; const filecoinPayBalance = available + lockupUsed; const status = makeStatus({ filecoinPayBalance, lockupUsed, rateUsed }); const res = computeTopUpForDuration(status, days); expect(res.topUp).toBe(perDay * 5n); expect(res.available).toBe(available); }); }); describe('computeAdjustmentForExactDays', () => { it('throws on negative days', () => { const status = makeStatus({ filecoinPayBalance: 0n, lockupUsed: 0n, rateUsed: 1n }); expect(() => computeAdjustmentForExactDays(status, -1)).toThrow('days must be non-negative'); }); it('returns zeros when rateUsed is 0', () => { const status = makeStatus({ filecoinPayBalance: 1000n, lockupUsed: 100n, rateUsed: 0n }); const res = computeAdjustmentForExactDays(status, 10); expect(res.delta).toBe(0n); expect(res.targetAvailable).toBe(0n); expect(res.available).toBe(900n); }); it('returns positive delta when more deposit needed (includes 1-hour safety)', () => { const rateUsed = 1000000000000000000n; // 1 USDFC/epoch const perDay = rateUsed * TIME_CONSTANTS.EPOCHS_PER_DAY; 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 * 30n + safety); }); it('returns negative delta when withdrawal possible', () => { const rateUsed = 1000000000000000000n; // 1 USDFC/epoch const perDay = rateUsed * TIME_CONSTANTS.EPOCHS_PER_DAY; const days = 5; const perHour = perDay / 24n; const safety = perHour > 0n ? perHour : 1n; const targetAvailable = perDay * BigInt(days) + safety; const available = targetAvailable + 1000n; // a bit more than target const status = makeStatus({ filecoinPayBalance: available, lockupUsed: 0n, rateUsed }); const res = computeAdjustmentForExactDays(status, days); expect(res.delta).toBe(-1000n); }); }); describe('computeAdjustmentForExactDeposit', () => { it('throws on negative target', () => { const status = makeStatus({ filecoinPayBalance: 0n, lockupUsed: 0n, rateUsed: 0n }); expect(() => computeAdjustmentForExactDeposit(status, -1n)).toThrow('target deposit cannot be negative'); }); it('clamps target to lockup when target below locked funds', () => { const filecoinPayBalance = 1000n; const lockupUsed = 800n; const status = makeStatus({ filecoinPayBalance, lockupUsed, rateUsed: 1n }); const res = computeAdjustmentForExactDeposit(status, 500n); expect(res.clampedTarget).toBe(lockupUsed); // Need to withdraw down to locked amount expect(res.delta).toBe(lockupUsed - filecoinPayBalance); // negative }); it('returns zero delta when already at target', () => { const filecoinPayBalance = 2000n; const lockupUsed = 500n; const status = makeStatus({ filecoinPayBalance, lockupUsed, rateUsed: 1n }); const res = computeAdjustmentForExactDeposit(status, filecoinPayBalance); expect(res.delta).toBe(0n); expect(res.clampedTarget).toBe(filecoinPayBalance); }); it('returns positive delta when more deposit needed', () => { const filecoinPayBalance = 1000n; const lockupUsed = 100n; const status = makeStatus({ filecoinPayBalance, lockupUsed, rateUsed: 1n }); const res = computeAdjustmentForExactDeposit(status, 1500n); expect(res.delta).toBe(500n); expect(res.clampedTarget).toBe(1500n); }); }); describe('computeAdjustmentForExactDaysWithPiece', () => { it('calculates deposit for new file when rateUsed is 0', () => { // Scenario: No existing storage, uploading first file const status = makeStatus({ filecoinPayBalance: 0n, lockupUsed: 0n, rateUsed: 0n }); const pieceSizeBytes = 1024 * 1024 * 1024; // 1 GiB const pricePerTiBPerEpoch = 1000000000000000n; // 0.001 USDFC per TiB per epoch const days = 30; const res = computeAdjustmentForExactDaysWithPiece(status, days, pieceSizeBytes, pricePerTiBPerEpoch); // Should require deposit for both lockup and runway expect(res.delta).toBeGreaterThan(0n); expect(res.newRateUsed).toBeGreaterThan(0n); expect(res.newLockupUsed).toBeGreaterThan(0n); }); it('adds file requirements to existing usage', () => { // Scenario: Existing storage, adding another file const rateUsed = 1000000000000000000n; // 1 USDFC/epoch 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 pieceSizeBytes = 1024 * 1024 * 1024; // 1 GiB const pricePerTiBPerEpoch = 1000000000000000n; // 0.001 USDFC per TiB per epoch const days = 30; const res = computeAdjustmentForExactDaysWithPiece(status, days, pieceSizeBytes, pricePerTiBPerEpoch); // New rate should be higher than existing expect(res.newRateUsed).toBeGreaterThan(rateUsed); expect(res.newLockupUsed).toBeGreaterThan(lockupUsed); }); }); //# sourceMappingURL=payments.compute.test.js.map