FilOzone / FilOzone/filecoin-pay-explorer

fix: reconcile availableFunds and debt in calculateFundedUntil

Open
#332 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
3
Forks
7
Avg merge
5d 14h
Merged PRs (30d)
47

Description

## Description

`FundsSection/utils/calculateFundedUntil.ts` derives `availableFunds` and `debt` under two different assumptions, so both can be non-zero at once.

`fundedUntilEpoch` (line 34) uses BigInt division, which truncates:

```
(funds - lockupCurrent) / lockupRate → (100 - 40) / 7 = 8.571… → 8n
```

`availableFunds` then clamps the lockup simulation at that epoch (line 35), so lockup stops growing when funds run out. `debt` (line 53) uses unclamped `elapsedEpochs` and keeps accruing. With `funds=100, lockupCurrent=40, lockupRate=7`, nine epochs after the last settled epoch:

```
availableFunds: 4n ← the truncation remainder, frozen
debt: 3n ← grows every epoch
fundedUntilTimestamp: 8 epochs ago
```

The account is expired, owes 3, and reports 4 withdrawable. This isn't a corner case: `availableFunds` stays pinned at `(funds - lockupCurrent) % lockupRate` indefinitely, so it affects every account whose remaining funds don't divide evenly by the rate.

Downstream, `FundsOverview.tsx:94` resolves the contradiction by letting debt win — the Available card shows `-3 / Outstanding debt` while`availableFunds` says otherwise.

## Proposed solution

Use the Synapse SDK as the reference implementation rather than patching the arithmetic in place. `AccountSummary` exposes `runwayInEpochs`, `debt`, and `lockupRatePerEpoch` derived against the contract's own model, and the notification service already consumes them (`alert-processor/account.ts`). Aligning on that source removes the divergence at its root and makes the console, the alert emails, and the SDK agree by construction.

Decide explicitly whether the SDK is fetched client-side alongside the subgraph data, or whether the subgraph derivation is corrected to match the SDK's semantics. Either way, the two figures must come from one model.

## Done criteria

- [ ] `availableFunds` and `debt` cannot both be non-zero for the same token and timestamp
- [ ] Behaviour matches the SDK's `AccountSummary` for the same on-chain state
- [ ] The `funds=100, lockupCurrent=40, lockupRate=7` case above is covered by a test
- [ ] `FundsOverview` no longer needs debt to take precedence over available in order to be correct
- [ ] Console tiers agree with the notification service for accounts in debt

## Trade-offs and risks

Fetching from the SDK adds a data source to a component currently fed entirely by the subgraph, and the console renders before a wallet client is guaranteed. Correcting the subgraph derivation avoids that but keeps a second implementation of contract logic in the frontend, which is what produced this divergence.

The truncation remainder is small in absolute terms, so this is a correctness and trust issue rather than a large financial discrepancy, worth fixing properly rather than urgently.

## Notes

Existing coverage is in `calculateFundedUntil.test.ts`. Two tests need revisiting with the fix: the fractional-runway case asserts only `fundedUntilTimestamp`, and "available funds stay clamped at zero" holds only for the divisible example it uses.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with FundsSection/utils/calculateFundedUntil.ts and its calculateFundedUntil.test.ts coverage, then read FundsOverview.tsx and alert-processor/account.ts to compare how the figures are consumed. Use the Synapse SDK AccountSummary as the reference and resolve whether the SDK or subgraph supplies the shared model. Done means the listed remainder case is tested, the figures cannot conflict, and console tiers agree with notifications.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.