Skip to content

feat: add Hat Steward claim guidance#34

Merged
ECWireless merged 2 commits into
mainfrom
feat/hat-stewards-claiming-guide
Jul 7, 2026
Merged

feat: add Hat Steward claim guidance#34
ECWireless merged 2 commits into
mainfrom
feat/hat-stewards-claiming-guide

Conversation

@ECWireless

@ECWireless ECWireless commented Jul 7, 2026

Copy link
Copy Markdown
Member

This pull request enhances the Hat Stewards page by adding real-time display of claimable Superfluid token balances for each steward role, along with a user guide for claiming streams. It introduces backend logic to fetch and calculate unwrappable balances, updates the UI to show these balances, and provides improved formatting for token and USD amounts.

Feature: Claimable Balances and Claiming Guide

  • Added a new section and UI card that displays the current claimable (unwrappable) Superfluid token balances for each role Safe, replacing the previous per-quarter projection. Users can now see exactly how much they can claim at any time. [1] [2] [3]
  • Introduced a step-by-step "ClaimingStreamGuide" component to instruct users on how to claim and unwrap their streamed tokens from the Safe. [1] [2]

Backend: Fetching and Calculating Unwrappable Balances

  • Implemented getUnwrappableBalancesForStreams in streams.ts to fetch and compute live Superfluid balances per receiver/token, returning them in a structured format for the frontend. [1] [2]
  • Added helper logic to calculate the current balance from Superfluid snapshot data, ensuring up-to-date claimable amounts. [1] [2]

UI/UX Improvements

  • Updated the UI to show claimable balances with links to open Superfluid inside the Safe app, and improved the formatting of token and USD values. [1] [2]
  • Enhanced error handling to display messages if Superfluid balance data is unavailable.

Code and Data Flow Updates

  • Updated data fetching logic to retrieve both runway and unwrappable balances in parallel, and passed unwrappable balances to each RoleStreamCard. [1] [2] [3]

References:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]

Summary by CodeRabbit

  • New Features
    • Added a “Claimable now” display for hat steward unwrappable balances, including an external Superfluid link opened via the Safe.
    • Introduced a new claiming guide section on the page.
  • Bug Fixes
    • Improved balance calculations and messaging for zero/unavailable and mixed-token scenarios (including “No accrued balance yet” where applicable).
  • UX Updates
    • Updated stream card metrics to include token symbols (e.g., “/ quarter” and “/ day”) and removed the projected-quarter block.

Copilot AI review requested due to automatic review settings July 7, 2026 17:31
@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
raidguild-accounting Ready Ready Preview, Comment Jul 7, 2026 6:53pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds unwrappable Superfluid balance fetching and summary helpers, refactors runway balance math, and wires a claimable-now Superfluid link plus claiming guide into the Hat Stewards page.

Changes

Unwrappable Balances Feature

Layer / File(s) Summary
Snapshot balance and balance fetching
src/lib/hat-stewards/streams.ts
Adds Superfluid response shapes, token-pair and unwrappable-balance result types, a current snapshot balance helper, runway reuse, and the new Superfluid queries that collect token pairs and account token snapshots.
Claimable balance summary and card UI
src/app/hat-stewards/page.tsx
Adds the Safe Superfluid deep link helper, the unwrappable balance summary formatter, the new claiming guide UI, and the RoleStreamCard claimable-now rendering and unit label updates.
Page data fetching and wiring
src/app/hat-stewards/page.tsx
Reworks page-side fetching to load streams, token pairs, runway, and unwrappable balances concurrently, inserts the claiming guide, adds the unavailable balance panel, and passes per-receiver balances into each RoleStreamCard.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • raid-guild/accounting#33 — Introduced the Hat Stewards Superfluid stream helpers that this PR extends with token-pair and unwrappable-balance fetching.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the PR’s main theme of adding Hat Steward claim guidance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/hat-stewards-claiming-guide

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.

Copilot AI 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.

Pull request overview

Adds live “claimable now” (unwrappable) Superfluid balances to the Hat Stewards page and introduces an in-page guide describing how to claim/unwrap streams via Safe + Superfluid.

Changes:

  • Implemented live Superfluid accountTokenSnapshots fetching + “current balance” calculation for unwrappable balances.
  • Updated Hat Stewards UI to display per-role claimable balances and added a step-by-step claiming guide component.
  • Adjusted stream amount formatting and added Safe-internal Superfluid deep links.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/lib/hat-stewards/streams.ts Adds snapshot balance math and a new API to fetch per-receiver unwrappable balances from the Superfluid subgraph.
src/app/hat-stewards/page.tsx Renders claimable balances per role Safe, adds a claiming guide, and links into Superfluid within Safe.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/hat-stewards/streams.ts
Comment thread src/app/hat-stewards/page.tsx
Comment thread src/app/hat-stewards/page.tsx Outdated
Comment thread src/app/hat-stewards/page.tsx Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/app/hat-stewards/page.tsx (1)

82-156: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

getUnwrappableBalanceSummary duplicates getProjectedQuarterSummary's group/format/mixed-token pattern.

Both functions group amounts, handle zero/single/mixed-token cases, and produce a { detail, value } shape. Consider extracting a shared helper (e.g. summarizeTokenAmounts(entries)) parameterized by the amount/symbol extractor.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/hat-stewards/page.tsx` around lines 82 - 156, Refactor the duplicated
token summary logic in getProjectedQuarterSummary and
getUnwrappableBalanceSummary by extracting a shared helper that groups token
amounts, handles zero/single/mixed-token cases, and returns the common { detail,
value } shape. Use a parameterized helper (for example, one that accepts amount
and symbol accessors) so both functions can delegate formatting while preserving
their current behavior.
src/lib/hat-stewards/streams.ts (1)

333-421: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate fetch/parse logic with getStreamRunwayForStreams.

The request body, headers, next.revalidate, !response.ok, and body.errors handling (Lines 352-392) closely mirror the equivalent block in getStreamRunwayForStreams (Lines 245-285). Extracting a shared fetchAccountTokenSnapshots(ids) helper (returning either snapshots or an error) would reduce duplication and the risk of the two implementations drifting apart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/hat-stewards/streams.ts` around lines 333 - 421, The fetch and
response-handling logic in getUnwrappableBalancesForStreams duplicates the same
accountTokenSnapshots request flow used by getStreamRunwayForStreams. Extract
the shared GraphQL call, headers, next.revalidate, response.ok handling, and
body.errors parsing into a common helper such as
fetchAccountTokenSnapshots(ids), and have both getStreamRunwayForStreams and
getUnwrappableBalancesForStreams consume that helper so the implementations stay
aligned.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/hat-stewards/page.tsx`:
- Around line 61-68: The amounts in hat steward streams are raw token
quantities, not USD, so the current USD formatter in formatUsdAmount is
misleading. Update the stream display in hat-stewards/page.tsx to format token
amounts with the token symbol (as before) instead of using Intl.NumberFormat
currency USD, and keep the projectedQuarterAmount and flowRatePerDay labels
consistent with HatStewardStream values from streams.ts. If a generic formatter
is still needed, make it token-aware and pass the symbol through where the
values are rendered.
- Around line 48-52: The deep link in getSafeSuperfluidAppUrl is hardcoding the
Safe network as gno, which will route non-Gnosis role Safes to the wrong chain.
Update getSafeSuperfluidAppUrl to accept and use the Safe’s chainId from the
role Safe data instead of a fixed prefix, and build the Safe parameter from that
value so the Superfluid app opens the correct Safe/network for each case.

In `@src/lib/hat-stewards/streams.ts`:
- Around line 332-421: getUnwrappableBalancesForStreams currently builds its
lookup keys only from the passed-in active streams, so historical receiver/token
pairs disappear after a stream is paused or cancelled. Update the source of
snapshot IDs to use a persistent list of receiver/token pairs that includes
ended streams as well, and keep seeding balancesByReceiver from that broader set
before calling fetch. Make the change in getUnwrappableBalancesForStreams and
any upstream stream collection it depends on so the Superfluid lookup still
includes claimable balances for previously active receivers.

---

Nitpick comments:
In `@src/app/hat-stewards/page.tsx`:
- Around line 82-156: Refactor the duplicated token summary logic in
getProjectedQuarterSummary and getUnwrappableBalanceSummary by extracting a
shared helper that groups token amounts, handles zero/single/mixed-token cases,
and returns the common { detail, value } shape. Use a parameterized helper (for
example, one that accepts amount and symbol accessors) so both functions can
delegate formatting while preserving their current behavior.

In `@src/lib/hat-stewards/streams.ts`:
- Around line 333-421: The fetch and response-handling logic in
getUnwrappableBalancesForStreams duplicates the same accountTokenSnapshots
request flow used by getStreamRunwayForStreams. Extract the shared GraphQL call,
headers, next.revalidate, response.ok handling, and body.errors parsing into a
common helper such as fetchAccountTokenSnapshots(ids), and have both
getStreamRunwayForStreams and getUnwrappableBalancesForStreams consume that
helper so the implementations stay aligned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 94d889f3-72cf-4549-b3bd-7f55c598df81

📥 Commits

Reviewing files that changed from the base of the PR and between 71a0c87 and 6fe1a58.

📒 Files selected for processing (2)
  • src/app/hat-stewards/page.tsx
  • src/lib/hat-stewards/streams.ts

Comment thread src/app/hat-stewards/page.tsx Outdated
Comment thread src/app/hat-stewards/page.tsx Outdated
Comment thread src/lib/hat-stewards/streams.ts

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/app/hat-stewards/page.tsx (1)

110-150: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Empty balances are indistinguishable from a failed fetch.

getUnwrappableBalanceSummary only receives balances (an array), never the fetch status. Per the wiring at Lines 526-530, unwrappableBalances defaults to [] whenever the receiver isn't in the map — which happens both when the balance is genuinely zero and when unwrappableBalancesResult.status === "unavailable". In the outage case, each RoleStreamCard will render "0 SYMBOL" or "No balance" as if there's nothing to claim, contradicting the global unavailable banner shown elsewhere on the page (Lines 503-507) and potentially misleading a steward into thinking they have nothing to claim.

The "Stream Runway" MetricCard already handles this correctly by explicitly showing "Unavailable" (Lines 467-471) — the claimable-now block should follow the same pattern.

🐛 Suggested fix: thread status through
 function getUnwrappableBalanceSummary({
   balances,
   streams,
+  status,
 }: {
   balances: HatStewardUnwrappableBalance[];
   streams: HatStewardStream[];
+  status: "ready" | "unavailable";
 }) {
+  if (status === "unavailable") {
+    return { detail: undefined, value: "Unavailable" };
+  }
+
   if (balances.length === 0) {

And thread unwrappableBalancesResult.status into RoleStreamCard alongside unwrappableBalances at Lines 526-530.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/hat-stewards/page.tsx` around lines 110 - 150, The unwrappable
balance summary in getUnwrappableBalanceSummary currently treats an empty
balances array as a real zero balance, but the caller also uses [] when
unwrappableBalancesResult.status is unavailable. Thread the fetch status through
RoleStreamCard from the wiring that passes unwrappableBalances, then update
getUnwrappableBalanceSummary to return an unavailable/unknown state instead of
“0 SYMBOL” or “No balance” when the data source is unavailable, matching the
handling used by the Stream Runway MetricCard.
src/lib/hat-stewards/streams.ts (1)

424-465: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Paginate accountTokenSnapshots here
tokenPairs can now exceed 100 historical receiver/token pairs, but this query still uses first: 100 with no paging. Once that happens, the overflowed snapshots are dropped and some receivers will render as having no balance. Chunk snapshotIds (or paginate) instead of relying on orderBy alone.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/hat-stewards/streams.ts` around lines 424 - 465,
getUnwrappableBalancesForStreams is still querying accountTokenSnapshots with a
hardcoded first: 100, so any tokenPairs beyond that range will be missed. Update
the fetch logic in getUnwrappableBalancesForStreams to chunk snapshotIds or
paginate through accountTokenSnapshots until all ids are resolved, then merge
the results into balancesByReceiver so receivers don’t render as empty when
there are more than 100 pairs.
🧹 Nitpick comments (1)
src/lib/hat-stewards/streams.ts (1)

428-437: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Side-effecting .map() mixes concerns.

balancesByReceiver.set(receiver, []) is a side effect performed inside the .map() callback whose return value is used to build snapshotIds. It works, but a plain for...of loop would make the seeding and id-collection intent clearer.

♻️ Optional refactor
   const balancesByReceiver = new Map<string, HatStewardUnwrappableBalance[]>();
-  const snapshotIds = Array.from(
-    new Set(
-      tokenPairs.map((tokenPair) => {
-        const receiver = tokenPair.receiver.toLowerCase();
-
-        balancesByReceiver.set(receiver, []);
-        return `${receiver}-${tokenPair.tokenAddress.toLowerCase()}`;
-      }),
-    ),
-  );
+  const snapshotIdSet = new Set<string>();
+
+  for (const tokenPair of tokenPairs) {
+    const receiver = tokenPair.receiver.toLowerCase();
+
+    balancesByReceiver.set(receiver, []);
+    snapshotIdSet.add(`${receiver}-${tokenPair.tokenAddress.toLowerCase()}`);
+  }
+  const snapshotIds = Array.from(snapshotIdSet);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/hat-stewards/streams.ts` around lines 428 - 437, The snapshotIds
construction in streams.ts is mixing data collection with the side effect of
seeding balancesByReceiver inside the .map callback. Refactor the tokenPairs
iteration into a plain for...of loop (or equivalent) so receiver normalization,
balancesByReceiver.set(receiver, []), and snapshot id collection are done
explicitly and separately while preserving the same snapshotIds values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/app/hat-stewards/page.tsx`:
- Around line 110-150: The unwrappable balance summary in
getUnwrappableBalanceSummary currently treats an empty balances array as a real
zero balance, but the caller also uses [] when unwrappableBalancesResult.status
is unavailable. Thread the fetch status through RoleStreamCard from the wiring
that passes unwrappableBalances, then update getUnwrappableBalanceSummary to
return an unavailable/unknown state instead of “0 SYMBOL” or “No balance” when
the data source is unavailable, matching the handling used by the Stream Runway
MetricCard.

In `@src/lib/hat-stewards/streams.ts`:
- Around line 424-465: getUnwrappableBalancesForStreams is still querying
accountTokenSnapshots with a hardcoded first: 100, so any tokenPairs beyond that
range will be missed. Update the fetch logic in getUnwrappableBalancesForStreams
to chunk snapshotIds or paginate through accountTokenSnapshots until all ids are
resolved, then merge the results into balancesByReceiver so receivers don’t
render as empty when there are more than 100 pairs.

---

Nitpick comments:
In `@src/lib/hat-stewards/streams.ts`:
- Around line 428-437: The snapshotIds construction in streams.ts is mixing data
collection with the side effect of seeding balancesByReceiver inside the .map
callback. Refactor the tokenPairs iteration into a plain for...of loop (or
equivalent) so receiver normalization, balancesByReceiver.set(receiver, []), and
snapshot id collection are done explicitly and separately while preserving the
same snapshotIds values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b3c975e-b4ec-4c74-afc0-31a08a4fccb2

📥 Commits

Reviewing files that changed from the base of the PR and between 6fe1a58 and 208fd33.

📒 Files selected for processing (2)
  • src/app/hat-stewards/page.tsx
  • src/lib/hat-stewards/streams.ts

@ECWireless ECWireless merged commit 1e3f823 into main Jul 7, 2026
3 checks passed
@ECWireless ECWireless deleted the feat/hat-stewards-claiming-guide branch July 7, 2026 18:59
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.

2 participants