Skip to content

Integrate GoodDollar citizen-sdk for Join to Vote face verification#337

Merged
tnrdd merged 3 commits into
mainfrom
feat/goodwallet-sdk-join-to-vote
Jul 3, 2026
Merged

Integrate GoodDollar citizen-sdk for Join to Vote face verification#337
tnrdd merged 3 commits into
mainfrom
feat/goodwallet-sdk-join-to-vote

Conversation

@tnrdd

@tnrdd tnrdd commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the external goodwallet.xyz link on the "Join to Vote" button with in-app GoodDollar face verification via @goodsdks/citizen-sdk. Users verify with whatever wallet they have connected instead of creating a new GoodWallet. Applies to every round with a GoodDollar voter group (the button already only renders for those).

How

  • New useGoodDollarVerification hook builds the identity SDK with a wallet client pinned to Celo over the connected wallet's own transport. Link generation only needs a message signature, so users connected on Base/OP/Arbitrum never get a network-switch prompt. It also exposes an isWhitelisted read with the same contract and semantics the eligibility API checks.
  • Desktop: pop-up mode. A placeholder window opens synchronously on click (popup blockers would kill a window opened after the async signature), then navigates to the verification link. If the popup is blocked, falls back to redirect.
  • Mobile: redirect mode. The tab navigates to GoodDollar's verification page and returns with a ?gdVerified=1 marker; the page strips it, waits for the wallet to reconnect, and resumes automatically.
  • Both paths converge on a "Verifying..." state that polls Celo whitelist status (4s interval, 60s grace after popup close/redirect return, 10 min cap while the popup is open), then re-runs the existing eligibility check to trigger the bot's addVoter. The eligibility API and bot flow are unchanged.

Notes

  • The lockfile grows ~500 lines because citizen-sdk 1.2.5 ships tsup as a runtime dependency (their packaging bug). Install-time only; the SDK's real runtime dep is lz-string, nothing extra is bundled.
  • The server still checks isWhitelisted(address) directly, so a wallet linked to a whitelisted root identity (IdentityV4 connected accounts) does not pass. Switching to getWhitelistedRoot semantics is a possible follow-up.

Manual QA

  • Desktop popup happy path, popup-blocked fallback, signature rejection
  • Mobile redirect out and back, auto-resume after wallet reconnect
  • Already-verified wallet: "Check Voter Eligibility" still short-circuits without FV

@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
platform Ready Ready Preview, Comment Jul 2, 2026 8:31pm

Request Review

@gaston-review gaston-review 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.

Gaston Review

Verdict: Approved

Score: ███████░░░ 7/10

Pull Request Summary

This PR replaces the old external GoodWallet link with an in-app face verification flow using the @goodsdks/citizen-sdk. On desktop, it opens a popup for face verification and polls the Celo Identity contract for whitelist status; on mobile, it does a full-page redirect and resumes polling on return. A new useGoodDollarVerification hook encapsulates SDK interaction and the on-chain whitelist check.

Review Summary

🔵 Suggestion — handleJoinToVote reconnect triggers checkEligibility, not verification
When a disconnected user clicks 'Join to Vote' (failed state), handleJoinToVote sets pendingCheck=true and opens the connect modal. But pendingCheck triggers checkEligibility — not the verification flow. After reconnecting, the user sees another failure and must click 'Join to Vote' a second time. Consider adding a separate pendingVerification flag that triggers handleJoinToVote after wallet connect, or at least check if this double-click UX is intentional.

🔵 Suggestion — Stale closure risk in watchVerification polling loop
The watchVerification async loop captures checkIsWhitelisted and checkEligibility at call time. If the user switches wallets mid-poll (changing address), the loop continues checking the old address. The watchIdRef only increments on unmount, not on dependency changes. In practice this is rare — users don't switch wallets during face verification — but a defensive bump of watchIdRef.current in a useEffect watching address would close the gap.

Solid integration. The popup/redirect fallback, polling cancellation via ref-based watch IDs, and unmount cleanup are all handled well. I have one UX-level concern about the disconnect-during-failed-state flow requiring an extra click, and a minor note on the stale closure risk in the polling loop, but neither is a blocker. Production-ready.

📌 3 inline comments
🔵 Suggestion: 1 · ⚪ Nitpick: 2


🔍 Reviewed by Gaston

Comment thread src/app/flow-councils/components/EligibilityButton.tsx
Comment thread src/app/flow-councils/components/EligibilityButton.tsx
Comment thread src/app/flow-councils/hooks/useGoodDollarVerification.ts Outdated

@gaston-review gaston-review 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.

Gaston Review

Verdict: Approved

Score: ███████░░░ 7/10

Pull Request Summary

This PR replaces the old external GoodWallet link with an in-app face verification flow using @goodsdks/citizen-sdk. It handles desktop (popup) and mobile (redirect) paths, polls Celo's Identity contract for whitelist status, and auto-claims voter eligibility on success. A shared ABI constant and a new useGoodDollarVerification hook encapsulate the SDK and on-chain interactions.

The latest push cleanly addresses both actionable items from my prior review: the ABI is now a shared constant in constants.ts (eliminating the duplication I flagged), and the reconnect-triggers-checkEligibility behavior has an explanatory comment confirming it's intentional. The core logic — polling cancellation, popup/redirect fallback, timeout handling — remains solid. Nothing new to block on.


🔍 Reviewed by Gaston

@gaston-review gaston-review 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.

Gaston Review

Verdict: Approved

Score: ████████░░ 8/10

Pull Request Summary

This PR replaces the external GoodWallet link with an in-app face verification flow using @goodsdks/citizen-sdk. Desktop users get a popup, mobile users get a full-page redirect. A polling loop watches the Celo Identity contract for whitelist status and auto-claims voter eligibility on success. The latest push adds a notWhitelisted discriminator to the API response so only definitive not-whitelisted results route into face verification — transient errors (RPC hiccups, bot failures) now reset to idle for retry instead of incorrectly pushing users into the verification flow.

All items from prior reviews have been addressed: shared ABI constant, intentional reconnect comment, and now the transient-vs-definitive error distinction. The code is clean, well-structured, and handles edge cases thoughtfully. Production-ready.


🔍 Reviewed by Gaston

@tnrdd tnrdd merged commit 15fce56 into main Jul 3, 2026
3 of 4 checks passed
@tnrdd tnrdd deleted the feat/goodwallet-sdk-join-to-vote branch July 3, 2026 04:46
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.

1 participant