Skip to content

chore: update flow council factory and subgraph to v0.4.1 on Celo#268

Open
tnrdd wants to merge 4 commits intomainfrom
chore/upgrade-subgraph
Open

chore: update flow council factory and subgraph to v0.4.1 on Celo#268
tnrdd wants to merge 4 commits intomainfrom
chore/upgrade-subgraph

Conversation

@tnrdd
Copy link
Copy Markdown
Contributor

@tnrdd tnrdd commented Apr 2, 2026

No description provided.

@tnrdd tnrdd requested a review from gravenp April 2, 2026 13:11
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 2, 2026

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

Project Deployment Actions Updated (UTC)
platform Ready Ready Preview, Comment Apr 3, 2026 7:32pm

Request Review

@gaston-review
Copy link
Copy Markdown

gaston-review bot commented Apr 2, 2026

⚠️ Gaston review skipped — your organization has reached its daily review limit. Reviews will resume tomorrow (resets at midnight UTC).

Copy link
Copy Markdown
Member

@gravenp gravenp left a comment

Choose a reason for hiding this comment

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

@tnrdd, to functionally test the new contracts & subgraph, I think we need to make a couple of additional changes in this PR:

  • Revert #249
  • Update the way that a ballot with a removed grantee is displayed & resubmitted

When testing a local branch with PR249 reverted, my ballot looked like the image below (on /flow-councils/42220/0x30fc88832401dfd6203cb13fa48bff872c3bf0b6):

Image

On that ballot UI, there's no way to successfully "submit 0 votes" for that removed grantee. Any attempt to vote the full allocation for the remaining grantees fails. Based on our past conversations, I don't think we can return all voters' votes at the time of graduation/removal. But when they return to the UI for their next ballot, we want it to appear and function like that:

  • "Stale" votes should be excluded from their voting power used numerator and their ballot display, but remain in the denominator as available
  • When they submit their next ballot, we should remove any votes for graduated/removed grantees so they can be added to others

Filter removed recipients in council query using subgraph v0.4.1
soft-delete field. For GoodDollar council, keep reducing voting
power by stale amount. For other councils, full voting power
remains available.
Copy link
Copy Markdown

@gaston-review gaston-review bot left a comment

Choose a reason for hiding this comment

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

🔄 Changes Requested

Score: █████░░░░░ 5/10


The PR replaces contract-based stale vote detection with subgraph-based detection (leveraging the new removed field in v0.4.1) and updates the factory/subgraph addresses. The approach is sound — moving stale vote detection to the subgraph is cleaner and avoids multiple contract calls. However, there's one significant issue:

Hardcoded council address: GOODDOLLAR_COUNCIL_ADDRESS is hardcoded inline as a string literal in FlowCouncil.tsx, but the exact same address already exists in src/app/flow-councils/lib/constants.ts as GOODBUILDERS_COUNCIL_ADDRESSES[1]. This creates a maintenance risk — if the address changes in one place but not the other, the stale vote logic silently breaks.

Stale vote filtering is council-specific but shouldn't be: The staleVotesList and filteredCurrentBallot computations apply to all councils (good), but the voting power adjustment only applies to the GoodDollar council. The commit message says "detect stale votes from subgraph instead of contract ABI" — but the old code applied to all councils, not just GoodDollar. This is a behavior change that should be intentional and documented.

The subgraph/factory address updates and the recipients(where: { removed: false }) filter look correct.


📌 2 inline comments


🔍 Reviewed by Gaston · Model: claude-opus-4-6

FlowCouncilContextProvider wraps the entire app but councilId is only
present on flow-council routes. The stale votes refactor called
councilId.toLowerCase() unconditionally, crashing every other page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@gaston-review gaston-review bot left a comment

Choose a reason for hiding this comment

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

🔄 Changes Requested

Score: ██████░░░░ 6/10


Second pass — both prior concerns remain unaddressed with no author response:

  1. Hardcoded address: GOODDOLLAR_COUNCIL_ADDRESS on line 218 duplicates GOODBUILDERS_COUNCIL_ADDRESSES[1] from constants.ts. This is already imported in three other files. Duplicating it inline is a maintenance hazard.

  2. Voting power adjustment scoped to GoodDollar only: The old code (deleted staleVotesQuery.ts) adjusted voting power for all councils. The new code gates it behind isGoodDollarCouncil. If this is intentional, a comment explaining why is warranted. If not, it's a regression.

The core approach — replacing contract reads with subgraph filtering via removed: false and client-side stale vote detection — is solid. The staleVotesList / filteredCurrentBallot / councilMember memos are well-structured. Factory and subgraph URL updates look correct.


📌 2 inline comments


🔍 Reviewed by Gaston · Model: claude-opus-4-6

[councilMemberRaw, staleVotes, isLoadingStaleVotes],
);
const GOODDOLLAR_COUNCIL_ADDRESS =
"0xfabef1abae4998146e8a8422813eb787caa26ec2";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Re-flagging from prior review (unaddressed, code unchanged): this address is already defined as GOODBUILDERS_COUNCIL_ADDRESSES[1] in src/app/flow-councils/lib/constants.ts and imported in three other files (EligibilityButton.tsx, s2StreamMigration.ts, eligibility/route.ts). Import and reuse it:

import { GOODBUILDERS_COUNCIL_ADDRESSES } from "@/app/flow-councils/lib/constants";
// ...
const isGoodDollarCouncil =
  councilId?.toLowerCase() === GOODBUILDERS_COUNCIL_ADDRESSES[1];

Duplicate magic strings rot silently — if the constant is updated but this literal isn't, stale vote deduction breaks without any compile-time signal.

Wrap useBallotQuery's filter/map in useMemo to stabilize object
references. Without this, every provider render created new arrays,
cascading through staleVotesList → councilMember → newBallot dispatch,
causing a render loop that broke the VoteBubble pie chart and click
handler.

Also replace inline GoodDollar address with GOODBUILDERS_COUNCIL_ADDRESSES
constant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@gaston-review gaston-review bot left a comment

Choose a reason for hiding this comment

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

🔄 Changes Requested

Score: ██████░░░░ 6/10


Third pass. The hardcoded address concern from prior reviews has been addressed — now using the shared GOODBUILDERS_COUNCIL_ADDRESSES[1] constant. The useMemo addition to ballotQuery.ts is correct and well-motivated. The subgraph/factory updates and removed: false filter look good.

One concern remains unaddressed after two prior reviews with no author response: the voting power stale-vote deduction is now scoped to GoodDollar only (line 250), whereas the old code applied it universally. This is either a silent regression for other councils or an intentional change that needs documentation.


📌 1 inline comment


🔍 Reviewed by Gaston · Model: claude-opus-4-6

@gravenp
Copy link
Copy Markdown
Member

gravenp commented Apr 3, 2026

Stale vote deduction on GoodBuilders

The switch from contract read (useStaleVotesQuery) to subgraph-based stale vote detection in 7bc5594 undercounts stale votes for some GoodBuilders voters.

Root cause

The contract's vote() function merges per-recipient — old vote entries persist unless explicitly zeroed. When a voter resubmits a ballot without including a previously-voted recipient, the contract keeps the old entry but the subgraph ballot only reflects the latest submission's payload. These orphaned vote entries are invisible to the subgraph but still count against voting power on-chain.

Example — voter 0xA488...:

  • Subgraph ballot: 7 votes, all for active recipients → stale = 0 → shows 6817
  • Contract (getVoter): 8 votes, including recipientId=3 with 32 amount pointing to zero address → stale = 32 → should show 6785

Both v0.3.7 and v0.4.1 subgraphs have this same gap. The old useStaleVotesQuery compensated by reading the contract directly.

Options

  1. Restore contract read for GoodBuilders only — bring back useStaleVotesQuery gated behind isGoodDollarCouncil. Quick fix, scoped to the one council that needs it.

  2. Update the v0.4.1 subgraph — accumulate per-recipient vote entries across all vote() events so the ballot entity matches the full on-chain state. Cleaner long-term fix, but requires a subgraph change + redeploy.

@tnrdd what's the preferred approach?

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