Skip to content

Keep the payout fields, not the whole entries, for the wallet total - #1571

Merged
feruzm merged 2 commits into
developfrom
perf/wallet-pending-payout-projection
Aug 20, 2026
Merged

Keep the payout fields, not the whole entries, for the wallet total#1571
feruzm merged 2 commits into
developfrom
perf/wallet-pending-payout-projection

Conversation

@feruzm

@feruzm feruzm commented Aug 20, 2026

Copy link
Copy Markdown
Member

Closes #1570.

The wallet's pending-earnings figure reads payout_at and pending_payout_value and nothing else, but the bridge answers with whole entries.

Measured across four active accounts, the two calls this component makes retain about 660 KB each time the wallet is opened, roughly 100 KB of post bodies and 500 KB of voter records, to produce one number. Projected on arrival it is 2.8 KB.

account retained today projected
good-karma 705 KB 2.8 KB
ecency 445 KB 2.8 KB
erikah 662 KB 2.8 KB
taskmaster4450 829 KB 2.8 KB

Be clear about what this does and does not buy: the wire cost is unchanged, because the bridge sends what it sends, and only an endpoint of our own or a field on the account could avoid that. What it buys is retained memory, which is what bounds how many renderer replicas fit on a host (#1559).

The projection answers under its own cache key. accountPostsPage is shared with the waves composer and the decks user column, both of which consume whole entries, and handing either a projected row would be the fault behind #1556. A test pins that the key is the shared one plus a marker rather than the shared one itself, alongside the projection and an empty answer.

It is a plain projection rather than withSlimPageEntries: slimming would add per-entry work for a surface that renders no card, and would only remove the 100 KB of bodies while leaving the 500 KB of votes.

pnpm typecheck clean, pnpm test green (3028 tests).

The wallet's pending-earnings figure reads payout_at and pending_payout_value
and nothing else, but the bridge answers with whole entries. Measured across
four active accounts, the two calls this component makes retain about 660 KB
each time the wallet is opened, roughly 100 KB of post bodies and 500 KB of
voter records, to produce one number. Projected on arrival it is 2.8 KB.

The wire cost is unchanged and cannot be fixed here, since the bridge sends
what it sends. What this buys is retained memory, which is what bounds how many
renderer replicas fit on a host.

The projection answers under its own key. accountPostsPage is shared with the
waves composer and the decks user column, both of which consume whole entries,
and handing either a projected row would be the fault behind #1556. A test pins
that the key is the shared one plus a marker rather than the shared one.
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 20, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. RPC entry identity is unchecked ⊘ Outdated 📜 Skill insight ⛨ Security
Description
The new projection accepts every returned entry without confirming that its author matches the
requested username. An unexpected RPC response could therefore contribute another account's payout
to the displayed wallet total.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R40-43]

+      const entries = (await fetchEntries()) ?? [];
+      return entries.map((entry) => ({
+        payout_at: entry.payout_at,
+        pending_payout_value: entry.pending_payout_value
Relevance

●●● Strong

Recent security precedents accept validating caller/account identity rather than trusting client- or
response-provided identity.

PR-#1459
PR-#578
PR-#1106

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668433 requires account/post RPC identity fields to be checked against requested
values before use. The projection maps every returned entry directly and never compares
entry.author with username.

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[40-44]
Skill: code-review: Skill: code-review: Skill: code-review: Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Validate account-post RPC results against the requested username before using their payout fields.
## Issue Context
The wrapper consumes all entries returned by the underlying Hive account-post query. Reject or exclude entries whose identity fields do not match the requested account while preserving null-response handling and the projected cache shape.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[39-44]
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[45-57]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. RPC entry identity is unchecked ⊘ Outdated 📜 Skill insight ⛨ Security
Description
The new projection accepts every returned entry without confirming that its author matches the
requested username. An unexpected RPC response could therefore contribute another account's payout
to the displayed wallet total.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R40-43]

+      const entries = (await fetchEntries()) ?? [];
+      return entries.map((entry) => ({
+        payout_at: entry.payout_at,
+        pending_payout_value: entry.pending_payout_value
Relevance

●●● Strong

Recent security precedents accept validating caller/account identity rather than trusting client- or
response-provided identity.

PR-#1459
PR-#578
PR-#1106

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668433 requires account/post RPC identity fields to be checked against requested
values before use. The projection maps every returned entry directly and never compares
entry.author with username.

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[40-44]
Skill: code-review: Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Validate account-post RPC results against the requested username before using their payout fields.
## Issue Context
The wrapper consumes all entries returned by the underlying Hive account-post query. Reject or exclude entries whose identity fields do not match the requested account while preserving null-response handling and the projected cache shape.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[39-44]
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[45-57]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Exported builder lacks return type ⊘ Outdated 📘 Rule violation ⚙ Maintainability
Description
The exported pendingPayoutsQueryOptions function has no explicit return type annotation. The new
public TypeScript API therefore does not satisfy the checklist's explicit-typing requirement.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31]

+export function pendingPayoutsQueryOptions(username: string, sort: "posts" | "comments") {
Relevance

●●● Strong

Recent reviews consistently require explicit return types for new exported TypeScript functions.

PR-#1535
PR-#1521
PR-#1563

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668119 requires explicit return annotations for new or modified public functions.
The exported function declaration specifies parameter types but relies on an inferred return type.

Rule 2668119: Disallow implicit and any types in new TypeScript code
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Add an explicit strongly typed return annotation to the exported query-options builder.
## Issue Context
The type must preserve `PendingPayout[]` as the query result and must not introduce `any`.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-39]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Entry factory duplicates shared helper ⊘ Outdated 📘 Rule violation ▣ Testability
Description
The test defines a local full Entry factory even though mockEntry is exported by the shared
src/specs/test-utils.tsx utilities. This duplicates domain mock construction and bypasses the
required shared factory.
Code

apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[R28-31]

+function entry(overrides: Partial<Entry> = {}): Entry {
+  return {
+    author: "alice",
+    permlink: "p",
Relevance

●●● Strong

Recent test reviews accepted replacing local domain fixtures with shared mock factories, matching
this exact duplication concern.

PR-#1545
PR-#1535
PR-#1565

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668103 requires domain-shaped test data covered by test-utils.tsx factories to
use those shared helpers. The new test constructs an Entry locally, while the repository's shared
test utility exports mockEntry(overrides?: Partial): Entry.

Rule 2668103: Use shared factory helpers for mock data in tests
apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[28-37]
apps/web/src/specs/test-utils.tsx[225-235]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Replace the local `entry` domain-object factory with the shared `mockEntry` test helper.
## Issue Context
Pass the body, vote records, payout timestamp, and payout value as overrides to `mockEntry`; retain only a small local wrapper if it adds scenario-specific defaults without rebuilding the full domain shape.
## Fix Focus Areas
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[28-38]
- apps/web/src/specs/test-utils.tsx[225-235]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Query cancellation is discarded ⊘ Outdated 🐞 Bug ☼ Reliability
Description
The projected query invokes the SDK query function without React Query's context, so its abort
signal never reaches the bridge request or post-resolution work. Navigating away or superseding the
query therefore leaves the full-entry request and processing running despite cancellation.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R39-40]

+    queryFn: async (): Promise<PendingPayout[]> => {
+      const entries = (await fetchEntries()) ?? [];
Relevance

●●● Strong

Recent SDK precedent explicitly requires rethrowing on aborted signals so cancellation propagates
through query wrappers.

PR-#1427
PR-#747

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new wrapper calls fetchEntries() with no context. The underlying SDK query destructures
signal from that context and forwards it to getAccountPosts, which in turn passes it to both
bridgeApiCall and resolvePosts; consequently this change removes cancellation propagation that
the original direct query usage provided.

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-44]
packages/sdk/src/modules/posts/queries/get-account-posts-query-options.ts[80-98]
packages/sdk/src/modules/bridge/requests.ts[85-108]
PR-#1427

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pending-payout projection calls the underlying SDK query function without forwarding React Query's query context. This discards the abort signal, preventing cancellation of the bridge request and subsequent post-resolution work.
## Issue Context
Update the wrapper query function to accept React Query's context and forward the relevant signal/context to the captured SDK query function. Add a test proving that the supplied abort signal reaches the SDK mock.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-45]
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[44-73]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (7)
6. Query cancellation is discarded ⊘ Outdated 🐞 Bug ☼ Reliability
Description
The projected query invokes the SDK query function without React Query's context, so its abort
signal never reaches the bridge request or post-resolution work. Navigating away or superseding the
query therefore leaves the full-entry request and processing running despite cancellation.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R39-40]

+    queryFn: async (): Promise<PendingPayout[]> => {
+      const entries = (await fetchEntries()) ?? [];
Relevance

●●● Strong

Recent SDK precedent explicitly requires rethrowing on aborted signals so cancellation propagates
through query wrappers.

PR-#1427
PR-#747

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new wrapper calls fetchEntries() with no context. The underlying SDK query destructures
signal from that context and forwards it to getAccountPosts, which in turn passes it to both
bridgeApiCall and resolvePosts; consequently this change removes cancellation propagation that
the original direct query usage provided.

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-44]
packages/sdk/src/modules/posts/queries/get-account-posts-query-options.ts[80-98]
packages/sdk/src/modules/bridge/requests.ts[85-108]
PR-#1427

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pending-payout projection calls the underlying SDK query function without forwarding React Query's query context. This discards the abort signal, preventing cancellation of the bridge request and subsequent post-resolution work.
## Issue Context
Update the wrapper query function to accept React Query's context and forward the relevant signal/context to the captured SDK query function. Add a test proving that the supplied abort signal reaches the SDK mock.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-45]
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[44-73]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Entry factory duplicates shared helper ⊘ Outdated 📘 Rule violation ▣ Testability
Description
The test defines a local full Entry factory even though mockEntry is exported by the shared
src/specs/test-utils.tsx utilities. This duplicates domain mock construction and bypasses the
required shared factory.
Code

apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[R28-31]

+function entry(overrides: Partial<Entry> = {}): Entry {
+  return {
+    author: "alice",
+    permlink: "p",
Relevance

●●● Strong

Recent test reviews accepted replacing local domain fixtures with shared mock factories, matching
this exact duplication concern.

PR-#1545
PR-#1535
PR-#1565

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668103 requires domain-shaped test data covered by test-utils.tsx factories to
use those shared helpers. The new test constructs an Entry locally, while the repository's shared
test utility exports mockEntry(overrides?: Partial): Entry.

Rule 2668103: Use shared factory helpers for mock data in tests
apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[28-37]
apps/web/src/specs/test-utils.tsx[225-235]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Replace the local `entry` domain-object factory with the shared `mockEntry` test helper.
## Issue Context
Pass the body, vote records, payout timestamp, and payout value as overrides to `mockEntry`; retain only a small local wrapper if it adds scenario-specific defaults without rebuilding the full domain shape.
## Fix Focus Areas
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[28-38]
- apps/web/src/specs/test-utils.tsx[225-235]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Exported builder lacks return type ⊘ Outdated 📘 Rule violation ⚙ Maintainability
Description
The exported pendingPayoutsQueryOptions function has no explicit return type annotation. The new
public TypeScript API therefore does not satisfy the checklist's explicit-typing requirement.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31]

+export function pendingPayoutsQueryOptions(username: string, sort: "posts" | "comments") {
Relevance

●●● Strong

Recent reviews consistently require explicit return types for new exported TypeScript functions.

PR-#1535
PR-#1521
PR-#1563

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668119 requires explicit return annotations for new or modified public functions.
The exported function declaration specifies parameter types but relies on an inferred return type.

Rule 2668119: Disallow implicit and any types in new TypeScript code
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Add an explicit strongly typed return annotation to the exported query-options builder.
## Issue Context
The type must preserve `PendingPayout[]` as the query result and must not introduce `any`.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-39]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Pending payout key is hardcoded ⊘ Outdated 📘 Rule violation ⚙ Maintainability
Description
The projected query manually appends "pending-payouts" instead of obtaining the complete key from
QueryKeys. This duplicates query-key construction outside the shared SDK registry.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[38]

+    queryKey: [...base.queryKey, "pending-payouts"],
Relevance

●● Moderate

Production query-key centralization has been accepted recently, but comparable mock-key findings
were also rejected; context evidence is mixed.

PR-#1516
PR-#1439
PR-#1565

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2667922 requires every React Query key to come from QueryKeys and prohibits
manually written array keys. The changed code creates a new key by spreading another key and
appending a hardcoded literal.

Rule 2667922: Use QueryKeys constants for react-query keys instead of hardcoded literals
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[38-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pending-payout query manually constructs part of its React Query key instead of using `QueryKeys` from `@ecency/sdk`.
## Issue Context
Keep the projection under a distinct cache key, but define the complete key through the shared query-key registry so whole-entry and projected results remain isolated.
## Fix Focus Areas
- packages/sdk/src/modules/core/query-keys.ts[28-63]
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[37-39]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Wallet logic bypasses feature directory ✓ Resolved 📜 Skill insight ⌂ Architecture
Description
The new wallet-specific query builder is placed under a route component directory rather than
apps/web/src/features//api/. This violates both feature-location requirements for newly introduced
application logic.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R31-33]

+export function pendingPayoutsQueryOptions(username: string, sort: "posts" | "comments") {
+  const base = getAccountPostsQueryOptions(username, sort, "", "", RECENT_LIMIT, "");
+  const fetchEntries = base.queryFn as (ctx?: unknown) => Promise<Entry[] | null | undefined>;
Relevance

●● Moderate

Feature placement rules support the finding, but no close accepted/rejected precedent establishes
this exact route-to-feature relocation pattern.

PR-#1545

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance IDs 2668016 and 2668151 require new application feature logic to reside under
apps/web/src/features// with an appropriate subdirectory. This newly added wallet query
implementation instead resides under src/app/.../_components/.

Rule 2668016: Place new feature code under src/features using feature-based directories
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-47]
Skill: add-feature: Skill: add-feature: Skill: add-feature: Skill: add-feature

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Move the new wallet-specific query logic into the standard feature directory structure.
## Issue Context
The route component may consume the builder, but its implementation should live in a wallet feature `api/` directory and be exposed through that feature's public exports.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-47]
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-pending-earnings.tsx[9-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Pending payout key is hardcoded ✗ Dismissed 📘 Rule violation ⚙ Maintainability
Description
The projected query manually appends "pending-payouts" instead of obtaining the complete key from
QueryKeys. This duplicates query-key construction outside the shared SDK registry.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[38]

+    queryKey: [...base.queryKey, "pending-payouts"],
Relevance

●● Moderate

Production query-key centralization has been accepted recently, but comparable mock-key findings
were also rejected; context evidence is mixed.

PR-#1516
PR-#1439
PR-#1565

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2667922 requires every React Query key to come from QueryKeys and prohibits
manually written array keys. The changed code creates a new key by spreading another key and
appending a hardcoded literal.

Rule 2667922: Use QueryKeys constants for react-query keys instead of hardcoded literals
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[38-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pending-payout query manually constructs part of its React Query key instead of using `QueryKeys` from `@ecency/sdk`.
## Issue Context
Keep the projection under a distinct cache key, but define the complete key through the shared query-key registry so whole-entry and projected results remain isolated.
## Fix Focus Areas
- packages/sdk/src/modules/core/query-keys.ts[28-63]
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[37-39]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Wallet logic bypasses feature directory ✓ Resolved 📜 Skill insight ⌂ Architecture
Description
The new wallet-specific query builder is placed under a route component directory rather than
apps/web/src/features//api/. This violates both feature-location requirements for newly introduced
application logic.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R31-33]

+export function pendingPayoutsQueryOptions(username: string, sort: "posts" | "comments") {
+  const base = getAccountPostsQueryOptions(username, sort, "", "", RECENT_LIMIT, "");
+  const fetchEntries = base.queryFn as (ctx?: unknown) => Promise<Entry[] | null | undefined>;
Relevance

●● Moderate

Feature placement rules support the finding, but no close accepted/rejected precedent establishes
this exact route-to-feature relocation pattern.

PR-#1545

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance IDs 2668016 and 2668151 require new application feature logic to reside under
apps/web/src/features// with an appropriate subdirectory. This newly added wallet query
implementation instead resides under src/app/.../_components/.

Rule 2668016: Place new feature code under src/features using feature-based directories
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-47]
Skill: add-feature: Skill: add-feature

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Move the new wallet-specific query logic into the standard feature directory structure.
## Issue Context
The route component may consume the builder, but its implementation should live in a wallet feature `api/` directory and be exposed through that feature's public exports.
## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-47]
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-pending-earnings.tsx[9-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@feruzm, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d215a72-0823-4577-93c9-4df356f66e7e

📥 Commits

Reviewing files that changed from the base of the PR and between bc6a1d2 and 8994fdf.

📒 Files selected for processing (4)
  • apps/web/src/api/queries/index.ts
  • apps/web/src/api/queries/pending-payouts-query.ts
  • apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-pending-earnings.tsx
  • apps/web/src/specs/api/pending-payouts-query.spec.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 20, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Project wallet pending payouts to reduce retained memory

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Project wallet content to payout fields, reducing retained data from ~660 KB to 2.8 KB.
• Isolate projected results under a cache key that preserves full-entry consumers.
• Cover field projection, cache isolation, and absent bridge responses with tests.
Diagram

sequenceDiagram
    actor U as Wallet User
    participant W as Wallet UI
    participant C as Query Cache
    participant P as Payout Query
    participant S as SDK Query
    participant B as Hive Bridge
    U->>W: Open wallet
    W->>C: Request distinct key
    C->>P: Resolve cache miss
    P->>S: Fetch recent entries
    S->>B: Request content
    B-->>S: Whole entries
    S-->>P: Whole entries
    P-->>C: Payout fields only
    C-->>W: Projected rows
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Dedicated payout endpoint
  • ➕ Reduces both network transfer and retained renderer memory
  • ➕ Could return an aggregate instead of per-entry rows
  • ➖ Requires backend or bridge API changes
  • ➖ Adds deployment and compatibility work beyond this wallet optimization
2. React Query select projection
  • ➕ Keeps transformation close to the consuming component
  • ➕ Requires less custom query wiring
  • ➖ Leaves complete entries retained in the shared query cache
  • ➖ Does not address the renderer-memory objective
3. Use withSlimPageEntries
  • ➕ Reuses an existing entry-slimming abstraction
  • ➕ Preserves more fields for potential future consumers
  • ➖ Performs unnecessary work for a surface rendering no entry cards
  • ➖ Retains large voter data and saves substantially less memory

Recommendation: Use the PR's arrival-time projection with a distinct cache key as the best immediate frontend solution. It minimizes retained data without corrupting shared full-entry caches; a dedicated payout endpoint remains the preferred future option if wire cost must also be reduced.

Files changed (3) +127 / -4

Enhancement (1) +6 / -4
profile-wallet-pending-earnings.tsxConsume projected payout rows in the wallet +6/-4

Consume projected payout rows in the wallet

• Replaces full-entry post and comment queries with the payout-only query options. Pending-earnings calculation and rendered behavior remain unchanged while retained query data is reduced.

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-pending-earnings.tsx

Tests (1) +74 / -0
pending-payouts-query.spec.tsVerify payout projection and cache isolation +74/-0

Verify payout projection and cache isolation

• Tests that large entry fields are discarded, projected queries extend rather than reuse the shared full-entry key, and null bridge responses produce an empty result.

apps/web/src/specs/app/profile/pending-payouts-query.spec.ts

Other (1) +47 / -0
pending-payouts-query.tsAdd a payout-only account-content query +47/-0

Add a payout-only account-content query

• Introduces typed query options that map recent posts or comments to only 'payout_at' and 'pending_payout_value'. The projected response uses a marked derivative of the shared key and normalizes missing responses to an empty array.

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80cae0cc3c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return queryOptions({
queryKey: [...base.queryKey, "pending-payouts"],
queryFn: async (): Promise<PendingPayout[]> => {
const entries = (await fetchEntries()) ?? [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Forward the query context to preserve request cancellation

When these wallet queries are cancelled because the user navigates away, changes profiles, or code calls cancelQueries, this wrapper discards React Query's context and invokes fetchEntries() without its abort signal. The wrapped SDK query explicitly forwards that signal to getAccountPosts (packages/sdk/src/modules/posts/queries/get-account-posts-query-options.ts, lines 83–97), so the newly wrapped requests now continue downloading and retaining the full post/vote payload after their consumer is gone, undermining the memory reduction this change targets. Accept the query context here and pass it through to the SDK query function.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Correct, and fixed in 8994fdf. The wrapper now takes React Query's context and passes it straight through, so signal reaches getAccountPosts again. Covered by a test that fails if the argument is dropped.

@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown

Greptile Summary

The PR introduces a wallet-specific React Query projection that retains only payout timestamps and values while preserving request cancellation and isolating the projected rows under a distinct cache key.

  • Adds pendingPayoutsQueryOptions with author filtering, null-response handling, and cancellation-context forwarding.
  • Updates the pending-earnings component to use projected post and comment results.
  • Adds coverage for projection, cache-key isolation, empty responses, cancellation forwarding, and account scoping.

Confidence Score: 5/5

The PR appears safe to merge.

The previously reported cancellation failure is fixed because the wrapper forwards React Query’s context to the SDK query function, preserving the abort signal, and no blocking failure remains.

Important Files Changed

Filename Overview
apps/web/src/api/queries/pending-payouts-query.ts Adds a cache-isolated projection over the SDK account-post query and correctly forwards React Query’s cancellation context.
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-pending-earnings.tsx Switches pending-earnings post and comment queries to the lightweight payout projection.
apps/web/src/specs/api/pending-payouts-query.spec.ts Covers projected shape, distinct cache identity, empty responses, cancellation forwarding, and account filtering.
apps/web/src/api/queries/index.ts Exports the new pending-payout query helper from the web query barrel.

Sequence Diagram

sequenceDiagram
  participant Wallet as Pending earnings component
  participant RQ as React Query
  participant Projection as pendingPayoutsQueryOptions
  participant SDK as SDK account-posts query
  participant Bridge as Bridge API
  Wallet->>RQ: Query posts/comments with projected cache key
  RQ->>Projection: queryFn(context with abort signal)
  Projection->>SDK: Forward complete query context
  SDK->>Bridge: Fetch account entries with signal
  Bridge-->>SDK: Full entries
  SDK-->>Projection: Entry array
  Projection->>Projection: Filter author and retain payout fields
  Projection-->>RQ: PendingPayout array
  RQ-->>Wallet: Lightweight cached rows
Loading

Reviews (2): Last reviewed commit: "Forward the query context and filter the..." | Re-trigger Greptile

Comment on lines +39 to +40
queryFn: async (): Promise<PendingPayout[]> => {
const entries = (await fetchEntries()) ?? [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Query cancellation context is discarded

When the wallet unmounts during either request, React Query aborts its signal, but this wrapper calls fetchEntries without forwarding that context, so the bridge RPC and nested post-resolution work continue until completion or timeout.

Suggested change
queryFn: async (): Promise<PendingPayout[]> => {
const entries = (await fetchEntries()) ?? [];
queryFn: async (ctx): Promise<PendingPayout[]> => {
const entries = (await fetchEntries(ctx)) ?? [];

Fix in Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 8994fdf, along the lines of the suggestion. Added a test asserting the exact context object reaches the SDK query function and that its signal is the one the caller supplied.

@qodo-code-review

qodo-code-review Bot commented Aug 20, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. RPC entry identity is unchecked ⊘ Outdated 📜 Skill insight ⛨ Security
Description
The new projection accepts every returned entry without confirming that its author matches the
requested username. An unexpected RPC response could therefore contribute another account's payout
to the displayed wallet total.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R40-43]

+      const entries = (await fetchEntries()) ?? [];
+      return entries.map((entry) => ({
+        payout_at: entry.payout_at,
+        pending_payout_value: entry.pending_payout_value
Relevance

●●● Strong

Recent security precedents accept validating caller/account identity rather than trusting client- or
response-provided identity.

PR-#1459
PR-#578
PR-#1106

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668433 requires account/post RPC identity fields to be checked against requested
values before use. The projection maps every returned entry directly and never compares
entry.author with username.

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[40-44]
Skill: code-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Validate account-post RPC results against the requested username before using their payout fields.

## Issue Context
The wrapper consumes all entries returned by the underlying Hive account-post query. Reject or exclude entries whose identity fields do not match the requested account while preserving null-response handling and the projected cache shape.

## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[39-44]
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[45-57]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Query cancellation is discarded ⊘ Outdated 🐞 Bug ☼ Reliability
Description
The projected query invokes the SDK query function without React Query's context, so its abort
signal never reaches the bridge request or post-resolution work. Navigating away or superseding the
query therefore leaves the full-entry request and processing running despite cancellation.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R39-40]

+    queryFn: async (): Promise<PendingPayout[]> => {
+      const entries = (await fetchEntries()) ?? [];
Relevance

●●● Strong

Recent SDK precedent explicitly requires rethrowing on aborted signals so cancellation propagates
through query wrappers.

PR-#1427
PR-#747

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new wrapper calls fetchEntries() with no context. The underlying SDK query destructures
signal from that context and forwards it to getAccountPosts, which in turn passes it to both
bridgeApiCall and resolvePosts; consequently this change removes cancellation propagation that
the original direct query usage provided.

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-44]
packages/sdk/src/modules/posts/queries/get-account-posts-query-options.ts[80-98]
packages/sdk/src/modules/bridge/requests.ts[85-108]
PR-#1427

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pending-payout projection calls the underlying SDK query function without forwarding React Query's query context. This discards the abort signal, preventing cancellation of the bridge request and subsequent post-resolution work.

## Issue Context
Update the wrapper query function to accept React Query's context and forward the relevant signal/context to the captured SDK query function. Add a test proving that the supplied abort signal reaches the SDK mock.

## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-45]
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[44-73]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Entry factory duplicates shared helper ⊘ Outdated 📘 Rule violation ▣ Testability
Description
The test defines a local full Entry factory even though mockEntry is exported by the shared
src/specs/test-utils.tsx utilities. This duplicates domain mock construction and bypasses the
required shared factory.
Code

apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[R28-31]

+function entry(overrides: Partial<Entry> = {}): Entry {
+  return {
+    author: "alice",
+    permlink: "p",
Relevance

●●● Strong

Recent test reviews accepted replacing local domain fixtures with shared mock factories, matching
this exact duplication concern.

PR-#1545
PR-#1535
PR-#1565

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668103 requires domain-shaped test data covered by test-utils.tsx factories to
use those shared helpers. The new test constructs an Entry locally, while the repository's shared
test utility exports mockEntry(overrides?: Partial<Entry>): Entry.

Rule 2668103: Use shared factory helpers for mock data in tests
apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[28-37]
apps/web/src/specs/test-utils.tsx[225-235]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Replace the local `entry` domain-object factory with the shared `mockEntry` test helper.

## Issue Context
Pass the body, vote records, payout timestamp, and payout value as overrides to `mockEntry`; retain only a small local wrapper if it adds scenario-specific defaults without rebuilding the full domain shape.

## Fix Focus Areas
- apps/web/src/specs/app/profile/pending-payouts-query.spec.ts[28-38]
- apps/web/src/specs/test-utils.tsx[225-235]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Exported builder lacks return type ⊘ Outdated 📘 Rule violation ⚙ Maintainability
Description
The exported pendingPayoutsQueryOptions function has no explicit return type annotation. The new
public TypeScript API therefore does not satisfy the checklist's explicit-typing requirement.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31]

+export function pendingPayoutsQueryOptions(username: string, sort: "posts" | "comments") {
Relevance

●●● Strong

Recent reviews consistently require explicit return types for new exported TypeScript functions.

PR-#1535
PR-#1521
PR-#1563

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2668119 requires explicit return annotations for new or modified public functions.
The exported function declaration specifies parameter types but relies on an inferred return type.

Rule 2668119: Disallow implicit and any types in new TypeScript code
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Add an explicit strongly typed return annotation to the exported query-options builder.

## Issue Context
The type must preserve `PendingPayout[]` as the query result and must not introduce `any`.

## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-39]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (2)
5. Pending payout key is hardcoded ✗ Dismissed 📘 Rule violation ⚙ Maintainability
Description
The projected query manually appends "pending-payouts" instead of obtaining the complete key from
QueryKeys. This duplicates query-key construction outside the shared SDK registry.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[38]

+    queryKey: [...base.queryKey, "pending-payouts"],
Relevance

●● Moderate

Production query-key centralization has been accepted recently, but comparable mock-key findings
were also rejected; context evidence is mixed.

PR-#1516
PR-#1439
PR-#1565

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2667922 requires every React Query key to come from QueryKeys and prohibits
manually written array keys. The changed code creates a new key by spreading another key and
appending a hardcoded literal.

Rule 2667922: Use QueryKeys constants for react-query keys instead of hardcoded literals
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[38-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pending-payout query manually constructs part of its React Query key instead of using `QueryKeys` from `@ecency/sdk`.

## Issue Context
Keep the projection under a distinct cache key, but define the complete key through the shared query-key registry so whole-entry and projected results remain isolated.

## Fix Focus Areas
- packages/sdk/src/modules/core/query-keys.ts[28-63]
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[37-39]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Wallet logic bypasses feature directory ✓ Resolved 📜 Skill insight ⌂ Architecture
Description
The new wallet-specific query builder is placed under a route component directory rather than
apps/web/src/features/<feature>/api/. This violates both feature-location requirements for newly
introduced application logic.
Code

apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[R31-33]

+export function pendingPayoutsQueryOptions(username: string, sort: "posts" | "comments") {
+  const base = getAccountPostsQueryOptions(username, sort, "", "", RECENT_LIMIT, "");
+  const fetchEntries = base.queryFn as (ctx?: unknown) => Promise<Entry[] | null | undefined>;
Relevance

●● Moderate

Feature placement rules support the finding, but no close accepted/rejected precedent establishes
this exact route-to-feature relocation pattern.

PR-#1545

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance IDs 2668016 and 2668151 require new application feature logic to reside under
apps/web/src/features/<feature-name>/ with an appropriate subdirectory. This newly added wallet
query implementation instead resides under src/app/.../_components/.

Rule 2668016: Place new feature code under src/features using feature-based directories
apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-47]
Skill: add-feature

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Move the new wallet-specific query logic into the standard feature directory structure.

## Issue Context
The route component may consume the builder, but its implementation should live in a wallet feature `api/` directory and be exposed through that feature's public exports.

## Fix Focus Areas
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/pending-payouts-query.ts[31-47]
- apps/web/src/app/(dynamicPages)/profile/[username]/wallet/_components/profile-wallet-pending-earnings.tsx[9-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 82 rules
✅ Skills: 6 invoked
  add-feature
  add-query
  add-sdk-mutation
  add-test
  code-review
  debug
Review mode: ⚖️ Balanced: This is a localized runtime/query-cache behavior change with meaningful correctness risk around shared React Query keys and data projection, but not dense or broad enough to warrant redundant extended review.

Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread apps/web/src/specs/app/profile/pending-payouts-query.spec.ts Outdated
The wrapper called the SDK query function with no arguments, so the
abort signal React Query hands it never reached the bridge call: a
wallet the reader had navigated away from kept downloading and
resolving entries, which is what this projection exists to avoid.

Also drops entries whose author is not the account whose wallet is
open, so a node answering with anything else cannot move the total,
and moves the builder to api/queries where app-specific queries live.
@feruzm
feruzm merged commit 16904f3 into develop Aug 20, 2026
9 checks passed
@feruzm
feruzm deleted the perf/wallet-pending-payout-projection branch August 20, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The profile wallet keeps 660 KB of entries to compute one number

1 participant