Skip to content

fix(perps): Don't see latest funding payments in Activity#28671

Open
abretonc7s wants to merge 8 commits intomainfrom
fix/tat-2057-dont-see-latest-funding-paymen
Open

fix(perps): Don't see latest funding payments in Activity#28671
abretonc7s wants to merge 8 commits intomainfrom
fix/tat-2057-dont-see-latest-funding-paymen

Conversation

@abretonc7s
Copy link
Copy Markdown
Contributor

@abretonc7s abretonc7s commented Apr 10, 2026

Description

Root cause

The Activity page was not showing the most recent funding payments for accounts with many open positions. `HyperLiquidProvider.getFunding()` made a single API call for the full 365-day history window; HyperLiquid's API silently caps responses at 500 records (oldest-first), so the most recent payments were dropped.

Fix

  • Initial load — 1 API call: `getFunding()` now defaults to fetching only the most recent 30-day window instead of 365 days. For windows exceeding the 500-record cap, the provider chunks into 30-day sub-windows in parallel, deduplicating boundary-timestamp records via hash-based filtering.
  • On-demand older history: A cursor-based `loadMoreFunding` in `usePerpsTransactionHistory` loads the next 30-day page when the user scrolls to the bottom of the Funding tab. Max lookback remains 365 days.
  • Rate limit improvement: 1 API call per navigation/refresh vs. a potential 365-day full fetch — ~50 loads/min safe vs. 3.

Changes

File Change
`HyperLiquidProvider.ts` `getFunding()` defaults to 30-day window; hash-based dedup across chunk boundaries
`usePerpsTransactionHistory.ts` Added cursor pagination (`loadMoreFunding`, `hasFundingMore`, `isFetchingMoreFunding`); fixed partial-window guard to use `cursorEndTime` instead of `cursorStartTime`; dedup by id in `loadMoreFunding` state merge
`PerpsTransactionsView.tsx` Wired `onEndReached → loadMoreFunding` on Funding tab; `ActivityIndicator` spinner in `ListFooterComponent`
`PerpsTransactionsView.styles.ts` Added `loadMoreContainer` style
`Perps.testIds.ts` Added `FUNDING_LOAD_MORE_SPINNER` testID
`transactionsHistoryConfig.ts` Added `FUNDING_HISTORY_PAGE_WINDOW_DAYS` and `FLASH_LIST_*` constants
`HyperLiquidProvider.test.ts` Updated pagination tests to use explicit `startTime` (multi-page) vs. default 30-day (single-page)

Changelog

CHANGELOG entry: Fixed missing recent perpetuals funding payments — `getFunding` now fetches the most recent 30-day window by default (1 API call) and loads older history on demand as the user scrolls, replacing the previous 365-day call that silently dropped recent records past the 500-record cap.

Related issues

Fixes: TAT-2057

Manual testing steps

Feature: On-demand funding history pagination

  Scenario: AC1 — Activity shows most recent funding payments (1 API call)
    Given the user has an account with perpetuals positions open for 12+ months
    When the user navigates to Activity > Perps > Funding tab
    Then the most recent funding payments appear under "Today"
    And only 1 getFunding API call is made (visible in Metro logs)

  Scenario: AC2 — Consecutive fetches return consistent results
    Given the user is on the Funding tab
    When getFunding is called twice with the same time window
    Then both calls return identical record counts and latest timestamps

  Scenario: AC3 — Scrolling to bottom loads older history
    Given the user is on the Funding tab with recent data loaded
    When the user scrolls to the bottom of the Funding list
    Then a loading spinner appears at the bottom
    And a second API call fetches the next 30-day window
    And older funding entries appear in the list

  Scenario: AC5 — Refresh resets to fresh recent data
    Given the user pulls to refresh on the Funding tab
    Then the cursor resets and the most recent 30-day window is re-fetched (1 call)

Screenshots/Recordings

Evidence Description
`evidence-ac1-funding-latest.png` Funding tab showing "Today" entries — 1 API call
`evidence-ac2-funding-consistent.png` Two consecutive fetches → same count, same timestamps
`evidence-ac3-scroll-load-more.png` After scrolling to bottom → older 30-day page loaded
`after.mp4` Full flow: navigate → Funding tab → scroll → load more

Recipe proof: `log_watch` confirmed `[PERPS-FUNDING] loadMoreFunding: older records loaded` fired 3× with non-zero count.

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Validation Recipe

recipe.json — automated validation script (19/19 nodes pass)
{
  "pr": "28671",
  "title": "Verify on-demand funding history pagination — TAT-2057 follow-up",
  "jira": "TAT-2057",
  "acceptance_criteria": [
    "AC1: Activity shows funding payments up to today (30-day initial window, 1 API call, isRecent=true)",
    "AC2: Multiple getFunding calls with same window return consistent results (no oscillation)",
    "AC3: Scrolling to bottom of Funding tab triggers a second fetch and loads older entries",
    "AC5: Refresh resets cursor and returns fresh recent data"
  ]
}

Note

Medium Risk
Changes funding history fetching and pagination behavior across provider, hook, and UI; mistakes could lead to missing/duplicated funding rows or extra API calls, but scope is limited to perps activity history.

Overview
Fixes missing recent perps funding payments by changing HyperLiquidProvider.getFunding() to fetch a default 30-day window and, when given larger ranges, page through 30-day chunks with recursive window-splitting when the 500-record API cap is hit (plus hash-based dedup + sorting).

Adds cursor-based “load more” funding pagination to usePerpsTransactionHistory (new loadMoreFunding/hasFundingMore/isFetchingMoreFunding), resets pagination on refresh, and wires the Funding tab UI to trigger loadMoreFunding on scroll with a footer spinner (PerpsTransactionsViewSelectorsIDs.FUNDING_LOAD_MORE_SPINNER). Tests are updated/extended to cover the new funding pagination and provider chunking/splitting behavior.

Reviewed by Cursor Bugbot for commit 6d59712. Bugbot is set up for automated code reviews on this repo. Configure here.

@abretonc7s abretonc7s added DO-NOT-MERGE Pull requests that should not be merged agentic labels Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbot metamaskbot added the team-perps Perps team label Apr 10, 2026
@abretonc7s
Copy link
Copy Markdown
Contributor Author

Automated fix-bug run — TAT-2057

Metric Value
Run 54ed1019
Duration ?
Model claude/sonnet
Nudges 0
Difficulty medium (-/10)
Grade rationale No structured response
Cost estimate $24.71
Worker report

Fix Report — TAT-2057: Don't see latest funding payments in Activity

Summary

HyperLiquidProvider.getFunding() made a single API call covering the full 365-day history window. The HyperLiquid userFunding API returns records in ascending chronological order capped at 500 per call, so active accounts silently receive only their oldest 500 records. The fix replaces the single call with parallel 30-day window fetches that guarantee the most recent records are always included.

Root Cause

File: app/controllers/perps/providers/HyperLiquidProvider.ts:5416

The original getFunding() called:

const rawFunding = await infoClient.userFunding({
  user: userAddress,
  startTime: Date.now() - 365 * 24 * 60 * 60 * 1000,
  endTime: undefined,
});

HyperLiquid's userFunding API returns records ascending (oldest first), capped at 500 per request. For accounts with >500 funding records in the past year (e.g., 0x316BDE with 1174 records from 2025-05 to 2026-04), only the oldest 500 were returned. The most recent 674 payments were silently dropped.

The oscillation between two cutoff dates on consecutive refreshes was caused by Date.now() being evaluated fresh on each call, producing slightly different window boundaries. Even small shifts changed which 500 records fell within the cap, making the cutoff jump between dates.

Reproduction Commit

SHA: a39d03de17 — "debug(pr-28671): add reproduction marker"

Added DevLogger.log('[PR-28671] BUG_MARKER: getFunding hit 500-record API cap') that fires when rawFunding.length >= 500. For 0x316BDE (1174 records), this marker would fire on every call.

Changes

File Description
app/controllers/perps/constants/transactionsHistoryConfig.ts Added FUNDING_HISTORY_PAGE_WINDOW_DAYS: 30 and FUNDING_HISTORY_API_LIMIT: 500 constants
app/controllers/perps/providers/HyperLiquidProvider.ts Replaced single API call with parallel 30-day window pagination via Promise.all
app/controllers/perps/providers/HyperLiquidProvider.test.ts Added 3 regression tests: parallel windows, recent records in long history, null page handling

Fix Detail

Divided the 365-day range into 30-day windows (~13 chunks), fetched all in parallel via Promise.all, then merged and sorted results. Each 30-day window stays well under the 500-record cap for any realistic user. The most recent window always contains today's records.

const chunks: { start: number; end: number }[] = [];
let chunkEnd = finalEndTime;
while (chunkEnd > finalStartTime) {
  const chunkStart = Math.max(finalStartTime, chunkEnd - pageWindowMs);
  chunks.push({ start: chunkStart, end: chunkEnd });
  chunkEnd = chunkStart;
}
const pages = await Promise.all(
  chunks.map((chunk) => infoClient.userFunding({ user: userAddress, startTime: chunk.start, endTime: chunk.end }))
);
const allRaw = pages.flatMap((page) => page ?? []);
allRaw.sort((a, b) => a.time - b.time);

Test Plan

Unit tests: yarn jest app/controllers/perps/providers/HyperLiquidProvider.test.ts — 4/4 pass (3 new regression tests)

Recipe validation (account 0x316BDE, 1174 records):

  • ac1-fetch-latest: count=1174 > 500 cap, isRecent=true, exceedsCap=true ✅
  • ac2-consistency-check: count1=99, count2=99, latestTs1=latestTs2, consistent=true ✅

CI gates: lint (0 errors), lint:tsc (0 errors), format:check (pass) ✅

Evidence

Artifact Description
before.mp4 Recipe on buggy code (single-call, 500-record cap)
after.mp4 Recipe on fixed code — count=1174, consistent results
before-ac1-funding-latest.png Funding tab before fix
after-ac1-funding-latest.png Funding tab after fix — "Today" entries visible
after-ac2-funding-consistent.png Stable list — no oscillation
recipe-coverage.md Per-AC coverage matrix

Ticket

TAT-2057 — https://consensyssoftware.atlassian.net/browse/TAT-2057

@abretonc7s abretonc7s marked this pull request as ready for review April 10, 2026 15:19
@abretonc7s abretonc7s requested a review from a team as a code owner April 10, 2026 15:19
@github-actions github-actions bot added the risk-medium Moderate testing recommended · Possible bug introduction risk label Apr 11, 2026
…issing latest payments

HyperLiquid userFunding API returns records ascending (oldest first) and
caps each call at 500 records. A single 365-day window silently drops the
most recent payments for accounts with >500 funding events.

Replace the single call with parallel 30-day window fetches via Promise.all.
Each window stays well under the cap, guaranteeing the most recent records
are always present regardless of history length.

Fixes TAT-2057
- Merge duplicate perps-controller imports (no-duplicate-imports)
- Move PAGE_WINDOW_MS/MAX_LOOKBACK_MS to module level (exhaustive-deps)
- Remove dead startTime/endTime from useCallback dep array
- Apply prettier formatting to all changed files
@abretonc7s abretonc7s force-pushed the fix/tat-2057-dont-see-latest-funding-paymen branch from 7609ee6 to eac26df Compare April 11, 2026 06:13
@github-actions github-actions bot added size-L risk-medium Moderate testing recommended · Possible bug introduction risk and removed size-M risk-medium Moderate testing recommended · Possible bug introduction risk labels Apr 11, 2026
…nding

- Fix early return guard: check cursorEndTime instead of cursorStartTime
  so the last partial funding window is fetched rather than silently dropped
  (365d / 30d = 12.17 pages; ~5 days were previously skipped)
- Deduplicate transactions by id when merging in loadMoreFunding to prevent
  boundary-timestamp duplicates from HyperLiquid's inclusive API ranges
@github-actions github-actions bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Apr 11, 2026
- Add try/catch so API rejections don't propagate as unhandled promise
  rejections through FlashList's fire-and-forget onEndReached callback
- Add fetchGenerationRef counter bumped on every fetchAllTransactions;
  loadMoreFunding captures it before the await and discards results if a
  concurrent refresh has reset the cursor, preventing stale cursor writes
  and stale data being appended after pull-to-refresh
@github-actions github-actions bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Apr 11, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePerps, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: @PerformancePreps
  • Risk Level: medium
  • AI Confidence: 88%
click to see 🤖 AI reasoning details

E2E Test Selection:
All 9 changed files are within the Perps feature domain:

  1. HyperLiquidProvider.ts: Significant refactor of getFunding() - changed from a single 365-day API call to paginated 30-day windows with recursive auto-splitting when API limits (500 records) are hit. This is a behavioral change in the data layer that could affect funding history completeness and correctness.

  2. transactionsHistoryConfig.ts: Added FUNDING_HISTORY_PAGE_WINDOW_DAYS (30) and FUNDING_HISTORY_API_LIMIT (500) constants supporting the new pagination strategy.

  3. usePerpsTransactionHistory.ts: Added loadMoreFunding, hasFundingMore, isFetchingMoreFunding to the hook interface, enabling infinite scroll for funding history. Removed startTime/endTime params from the hook.

  4. PerpsTransactionsView.tsx: Updated to consume the new pagination API, added load-more spinner for the Funding tab, uses onEndReached with loadMoreFunding callback.

  5. PerpsTransactionsView.styles.ts: Added loadMoreContainer style for the spinner.

  6. Perps.testIds.ts: Added FUNDING_LOAD_MORE_SPINNER test ID.

  7. Test files (3): Updated mocks to include new pagination fields.

Tag selection:

  • SmokePerps: Primary tag - directly tests Perps functionality including Add Funds flow, balance verification, and the Perps interface. The transaction history changes are core to the Perps experience.
  • SmokeWalletPlatform: Required per SmokePerps description - "Perps is also a section inside the Trending tab (SmokeWalletPlatform); changes to Perps views affect Trending."
  • SmokeConfirmations: Required per SmokePerps description - "When selecting SmokePerps, also select SmokeConfirmations (Add Funds deposits are on-chain transactions)."

No other tags are affected - changes are isolated to the Perps feature with no impact on accounts, network, swaps, identity, or other wallet features.

Performance Test Selection:
The PerpsTransactionsView now uses paginated funding history loading with FlashList and a load-more spinner. The HyperLiquidProvider's getFunding() method changed from a single large API call to multiple parallel 30-day window fetches with recursive splitting. These changes could impact the rendering performance of the Perps transactions view and the data loading performance of the funding history. The @PerformancePreps tag covers perps market loading, position management, add funds flow, and order execution - which includes the transactions view that was modified.

View GitHub Actions results

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6d59712. Configure here.

@github-actions
Copy link
Copy Markdown
Contributor

E2E Fixture Validation — Schema is up to date
11 value mismatches detected (expected — fixture represents an existing user).
View details

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
47.6% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agentic DO-NOT-MERGE Pull requests that should not be merged risk-medium Moderate testing recommended · Possible bug introduction risk size-L team-perps Perps team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants