Repository navigation
[PLAN] Add Connect-a-wallet widget to GoodWidget #114
Description
[PLAN] Add Connect-a-wallet widget to GoodWidget
Parent issue: #113
Required states, flows, and behaviors
(Carried over from #113 — not re-derived.)
- No wallet connected: only show a "Connect a wallet" prompt.
- Connecting: handled via the primary verified identity flow; show a loading state while the connection is pending.
- Connected (primary verified identity): unblocks a connect/disconnect address input field, where the user can paste an external address to link.
- Pasting an address triggers a loading state for that address while its connection status is checked per chain against the primary verified address.
- Each chain field then shows a "Connect" or "Disconnect" button, respective to that chain's current connection status.
- Error states: surfaced via toast notification or an error block at the top of the widget.
- Connect/disconnect transaction failure → red error block.
- Connected to an unsupported network → orange warning block.
- Chains in scope, per
citizen-sdk'sSupportedChains: Celo (42220), Fuse (122), XDC (50).
Execution plan
Reference files mapped (across repos)
GoodDollar/GoodSDKs (citizen-sdk — reference logic, not modified):
packages/citizen-sdk/README.md— wallet-link contract:identitySDK.connectAccount(),disconnectAccount(),checkConnectedStatus(),SupportedChainsenum,WalletLinkOptions(onSecurityMessage,onHash,skipSecurityMessage).
GoodDollar/GoodWidget (target repo):
packages/citizen-claim-widget/— closest precedent package; also built on@goodsdks/citizen-sdk. Use its scaffold as the structural template (widgetRuntimeContract.ts,adapter.ts,CitizenClaimWidget.tsx,integration.ts,element.ts,register.ts,index.ts).packages/streaming-widget/src/— structural precedent for component decomposition (not wallet logic): top-levelStreamingWidget.tsxstays a thin contract/provider-wiring entry point;components/holds the actual composed view (StreamingWidgetView.tsx), per-state/per-tab files (BalancesTab.tsx,HistoryTab.tsx, etc.), a per-item row component (PoolCard.tsx/StreamCard.tsx), a gating component (WalletGate.tsx), and shared helpers (format.ts,shared.tsx). Mirror this shape forconnect-a-wallet-widget— see the updated package-placement assessment below.packages/core/src/(provider.tsx,hooks.ts,types.ts,eip1193.ts) —GoodWidgetProvider,useWallet()/useHost(),EIP1193Providertype. This is the only source of wallet/chain state; no new provider abstraction is needed. Confirmed: every existing widget package (ai-credits-widget,citizen-claim-widget,goodreserve-widget,staking-migration-widget,streaming-widget) consumes this sameuseWallet()and calls its GoodDollar SDK directly — none use Wagmi or@goodsdks/react-hooks.packages/ui/src/components/andcomponents-test/— existing primitives to reuse (below).docs/widget-author-instructions.md,docs/architecture/theming-contract.md— styling hierarchy andcreateComponent()rules to follow; also documents the "Practical Component Workflow" this plan's decomposition follows.docs/demo-environment.md— Storybook/Playwright conventions for the new widget's stories and tests.AGENTS.md— Always/Ask-first/Never operating rules (see flags below).
Existing @GoodDollar packages to import
@goodwidget/core(workspace) —GoodWidgetProvider,useWallet,EIP1193Provider.@goodwidget/ui(workspace) — see component reuse list below.@goodwidget/embed(workspace) —createMiniAppElementfor the Custom Element wrapper (element.ts), same ascitizen-claim-widget.@goodsdks/citizen-sdk—IdentitySDK,SupportedChains,WalletLinkOptionstypes. Pin the same version rangecitizen-claim-widgetuses unless a wallet-link fix requires a bump.
Confirmed decision (approved by Bounty Lead): do not add @goodsdks/react-hooks or wagmi as dependencies. react-hooks's wallet-link hooks require a Wagmi v2 provider tree, which conflicts with GoodWidget's provider-first, raw-EIP-1193 architecture — and matches how every other widget package in the repo is built. Call IdentitySDK methods directly from a local adapter, built on the viem client GoodWidget's own useWallet() already exposes — same pattern as citizen-claim-widget/adapter.ts.
New components — package placement assessment
New package: packages/connect-a-wallet-widget (@goodwidget/connect-a-wallet-widget), scaffolded like citizen-claim-widget, with the component tree following streaming-widget's decomposition pattern rather than a single file:
widgetRuntimeContract.ts— status union (loading | connecting | not_connected | connected_no_input | checking_address | ready | error), per-chain link state, props contract.adapter.ts—useConnectAWalletAdapterhook: wrapsIdentitySDK.connectAccount/disconnectAccount/checkConnectedStatus, tracks per-chain pending/connected/error state, surfaces theonSecurityMessageconfirmation andonHashcallback from the SDK'sWalletLinkOptions.ConnectAWalletWidget.tsx— top-level widget contract entry only (provider wiring), mirroringstreaming-widget/src/StreamingWidget.tsx. No per-state UI logic here.components/ConnectAWalletWidgetView.tsx— the composed view; switches on the adapter's status union and renders the components below, mirroringstreaming-widget/src/components/StreamingWidgetView.tsx.components/ConnectPrompt.tsx— "no wallet connected" prompt state.components/WalletGate.tsx— connected/not-connected gating, mirroringstreaming-widget/src/components/WalletGate.tsx.components/AddressLinkForm.tsx— address paste input + per-address checking-loading state.components/ChainLinkRow.tsx— one row per chain (badge + connect/disconnect + status), mirroringstreaming-widget/src/components/PoolCard.tsx/StreamCard.tsx's per-item row pattern.components/format.ts— pure formatting/status-mapping helpers (address display, chain name/status text), mirroringstreaming-widget/src/components/format.ts.components/shared.tsx— small shared UI bits reused across the above (e.g. banner-to-Alertmapping), mirroringstreaming-widget/src/components/shared.tsx.integration.ts,element.ts(taggw-connect-a-wallet),register.ts,index.ts— same shape ascitizen-claim-widget.
Reused as-is from packages/ui (no new component needed):
Alert,Toast/ToastContainer/createToast,Input,AddressDisplay,ChainBadge,TransactionButton,Spinner,Card,Heading,Text,YStack/XStack.
Net new to packages/ui (only if design confirms cross-widget reuse — do not build ahead of it):
- A composite "per-chain link row" (chain badge + status + connect/disconnect button) is the one candidate worth assessing for
packages/uivs. widget-local. Default to widget-local (components/ChainLinkRow.tsx) for the first implementation; only promote if a second widget needs the same composite.
Build steps
- Scaffold
packages/connect-a-wallet-widgetmirroringcitizen-claim-widget's. - Implement
widgetRuntimeContract.tstypes for the state machine above. - Implement
adapter.tson top ofIdentitySDK(direct viem client fromuseWallet()). - Implement the widget as the focused
components/tree above (view composition, per-state, per-chain row, shared helpers) rather than one monolithic file —ConnectAWalletWidget.tsxstays a thin contract/provider-wiring entry point. See package-placement assessment above. - Add
integration.ts,element.ts,register.ts,index.ts. - Add Storybook stories mirroring
citizen-claim-widget's Showcase/QA/mdx split. - Add Playwright state coverage (
states.spec.ts) covering: no-wallet, connecting, connected-empty-input, per-chain checking, per-chain connected, per-chain disconnected, tx-error, unsupported-network. - Regenerate curated screenshot evidence per
docs/demo-environment.mdconventions. - Wire into demo entry points the same way
citizen-claim-widgetis wired, if applicable.
Acceptance criteria
- New
@goodwidget/connect-a-wallet-widgetpackage builds, lints, and exports a Custom Element (gw-connect-a-wallet) and a plain React component, consistent withcitizen-claim-widget's package shape. - "No wallet connected" state renders only the "Connect a wallet" prompt.
- "Connecting" state shows a loading indicator while the primary verified identity flow resolves.
- Once connected, the address input is enabled; pasting an address triggers a per-address loading state while per-chain status is checked against the primary verified address, for Celo, Fuse, and XDC.
- Each chain row shows a "Connect" or "Disconnect" action matching that chain's live connection status, and pressing it calls
IdentitySDK.connectAccount/disconnectAccountand updates state on success. - Connect/disconnect transaction failures surface as a red error block (or toast, per final design).
- Being connected to an unsupported network surfaces an orange warning block.
- No new wallet/provider abstraction (e.g. Wagmi) is introduced; the widget consumes
@goodwidget/core's existinguseWallet()EIP-1193 context, consistent with every other widget package in the repo. - All new components either reuse existing
packages/uiprimitives or are added to the new widget package; nothing is added topackages/uiwithout a stated cross-widget reuse justification. -
ConnectAWalletWidget.tsxis a thin entry point (provider wiring only); per-state/per-section UI lives in focused files undercomponents/, mirroringstreaming-widget's decomposition — no single monolithic component/hook implements the whole widget. - Storybook stories + curated screenshots and a Playwright
states.spec.tsexist for the new widget, followingcitizen-claim-widget's conventions. -
pnpm run build,pnpm run lint,pnpm test:storybook, andpnpm test:demopass for the new package.
Human-reviewer checklist
- Confirm whether the security-confirmation prompt (
onSecurityMessagefromWalletLinkOptions) should be a blocking modal/dialog (packages/ui'sDialog) or an inline confirmation — not specified in [Feature]: Add Connect-a-wallet widget #113.
-- it should be a modal that gets confirmed before executing the transaction. That is also how its demoed. - Confirm error-block vs. toast placement for tx failures ([Feature]: Add Connect-a-wallet widget #113 allows either).
-- Toast - Verify per-chain Connect/Disconnect buttons correctly disable/hide based on live on-chain status, not just optimistic local state.
-- it should show either connect/disconnect, never hide. if status is loading it should have a loading indicator - Verify Storybook screenshots are committed following the pattern as has been comitted so far in
tests - Verify no widget-local styling bypasses
packages/uitokens/themes perdocs/widget-author-instructions.md. - Verify the
components/tree matches the decomposition above (no monolithicConnectAWalletWidget.tsx) before merge.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsPrepare AI Task
Activity
[-][DRAFT][PLAN] Add Connect-a-wallet widget to GoodWidget[/-][+][PLAN] Add Connect-a-wallet widget to GoodWidget[/+]