feat: add Hat Steward claim guidance#34
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds 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. ChangesUnwrappable Balances Feature
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
accountTokenSnapshotsfetching + “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.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/app/hat-stewards/page.tsx (1)
82-156: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
getUnwrappableBalanceSummaryduplicatesgetProjectedQuarterSummary'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 winDuplicate fetch/parse logic with
getStreamRunwayForStreams.The request body, headers,
next.revalidate,!response.ok, andbody.errorshandling (Lines 352-392) closely mirror the equivalent block ingetStreamRunwayForStreams(Lines 245-285). Extracting a sharedfetchAccountTokenSnapshots(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
📒 Files selected for processing (2)
src/app/hat-stewards/page.tsxsrc/lib/hat-stewards/streams.ts
There was a problem hiding this comment.
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 winEmpty balances are indistinguishable from a failed fetch.
getUnwrappableBalanceSummaryonly receivesbalances(an array), never the fetchstatus. Per the wiring at Lines 526-530,unwrappableBalancesdefaults to[]whenever the receiver isn't in the map — which happens both when the balance is genuinely zero and whenunwrappableBalancesResult.status === "unavailable". In the outage case, eachRoleStreamCardwill 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"
MetricCardalready 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.statusintoRoleStreamCardalongsideunwrappableBalancesat 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 winPaginate
accountTokenSnapshotshere
tokenPairscan now exceed 100 historical receiver/token pairs, but this query still usesfirst: 100with no paging. Once that happens, the overflowed snapshots are dropped and some receivers will render as having no balance. ChunksnapshotIds(or paginate) instead of relying onorderByalone.🤖 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 valueSide-effecting
.map()mixes concerns.
balancesByReceiver.set(receiver, [])is a side effect performed inside the.map()callback whose return value is used to buildsnapshotIds. It works, but a plainfor...ofloop 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
📒 Files selected for processing (2)
src/app/hat-stewards/page.tsxsrc/lib/hat-stewards/streams.ts
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
Backend: Fetching and Calculating Unwrappable Balances
getUnwrappableBalancesForStreamsinstreams.tsto fetch and compute live Superfluid balances per receiver/token, returning them in a structured format for the frontend. [1] [2]UI/UX Improvements
Code and Data Flow Updates
RoleStreamCard. [1] [2] [3]References:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]
Summary by CodeRabbit