feat: AI Credits Widget — buy AI coding credits with G$ on Celo#63
Conversation
- widgetRuntimeContract.ts: 13 states, all action types, adapter factory pattern - mockBackendClient.ts: interface + mock + production client, createBackendClient factory - adapter.ts: useAiCreditsAdapter hook with full state machine (EIP-712 consent, buyer key gen, Celo payment) - aiCreditsComponents.tsx: all widget-specific components (AiCreditsHero, BuyerKeyPanel, OperatorConsentStep, AmountPicker, CreditsBalance, SetupSnippet, UsageLog, AiCreditsFlowStepper) - AiCreditsWidget.tsx: main widget with GoodWidgetProvider wrapper and state-driven render - integration.ts: manifest with Celo+Base chain IDs and all 13 states - element.ts + register.ts: <ai-credits-widget> web component bridge - index.ts: public exports - Storybook stories: QA + Showcase stories for all 13 states - aiCreditsWidgetStories.tsx: mock state factory and story helpers - Playwright smoke tests: states.spec.ts covering all states - pnpm build passes, pnpm lint passes (0 errors)
|
@copilot can you run the tests and commit the outputted screenshots of the widget? |
Done — all 13 tests now pass and screenshots are committed to Two bugs were fixed along the way:
Screenshots: |
blueogin
left a comment
There was a problem hiding this comment.
Buyer key
now Random bytes truncated to a pseudo-address; private key discarded (adapter.ts ~373–381)
Real generatePrivateKey + privateKeyToAccount; user keeps private key for AntSeed
Operator consent
now Payer walletClient.signTypedData (~405–426)
Calls backend to get signature(eip712 style)
Consent submission
now Signature only updates local state
calls backend with signature
(We can combine Operator Consent and Consent Submission into 1 and also can use 1 backend api endpoint)
Celo payment
now G$.transfer to stub 0x00…01 (~556–565)
approve + CeloGdAntSeedVault.deposit(amount, data) (data ABI-encoded AntSeed buyer address: abi.encode(buyerAddress). Required.)
GoodID
isGoodIdVerified always false in live adapter; never read on-chain
Vault isGoodIDVerified → hero badge + 20% stream bonus in quotes
the backend repository: https://github.com/GoodDollar/antseed-integration
…t, vault deposit, on-chain GoodID
All 5 corrections are in (commit 1. Buyer key — switched from random-bytes pseudo-address to 2 & 3. Operator consent + submission (combined into 1 handler) — removed 4. Celo payment — replaced 5. GoodID — Backend client updated to match the actual Worker API ( |
|
@copilot For buttons, “Generate new key” and “Paste existing key” are the same size and color, giving them equal visual weight. The primary action (generate) should be emphasized.
|
…ss multiple components in AiCreditsWidget
…t changes in UI and functionality
…d consistency and functionality
|
@copilot |
…onChangeText, pay toast, adapter error status) Co-authored-by: blueogin <43612769+blueogin@users.noreply.github.com>
Done in commit
All 11 Playwright tests pass. |
|
@copilot |
Understood. My understanding is now aligned with the current PR description:
No code changes made. |
…s for improved layout consistency
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
… for cleaner code

Closes #62
Summary
Adds
@goodwidget/ai-credits-widget— a GoodWidget for buying AI coding credits with G$ on Celo. Credits settle on Base and are redeemable with AntSeed-compatible tools (Cursor, Cline, etc.).The widget ships with:
AiCreditsWidget), web-component entrypoints (<ai-credits-widget>), and integration manifesttransferAndCalldeposit)WidgetTabsIntentional deviations from #62
Issue #62 is stale in a few places. This PR implements the agreed product direction from review:
statusvalues (connected_empty,has_credits,usage_*,insufficient_ai_credits, …)activeTab: 'buy' | 'manage'executeCeloPayment/v1/auth/nonce+ payer plain-message signPOST /v1/accounts/{buyer}/operator-consentgeneratePrivateKey()buyer keybuyerKeyDerivation.ts)createComponentcard wrappers<Card>panels;createComponentkept only forAiCreditsStatusNotice+BonusBadgeFrameuseShell: false,layout: 'padded', 420pxYStackwrapper (staking/citizen-claim pattern)Status mapping
connected_empty/ early buy setup →purchase_setup+activeTab: 'buy'has_credits/usage_empty/usage_active→activeTab: 'manage'(balance, stream, history, withdraw, API setup)insufficient_ai_credits→ dropped (no dedicated widget status)Buyer key
buildBuyerKeyMessage(address); private key is derived deterministically viaderiveBuyerPrivateKeyFromSignatureOperator consent
operatorConsent.ts)POST /v1/accounts/{buyer}/operator-consentCelo payment (
celoPayment.ts)executeCeloPaymentsubmits separate wallet confirmations (not a single Multicall batch):streamAmountG > 0):G$.increaseAllowance(CFA, …)when needed, thenCFA_V1_FORWARDER.createFloworupdateFlowwithuserData = abi.encode(buyer)depositAmountG > 0):G$.transferAndCall(vault, depositWei, abi.encode(buyer))Celo mainnet addresses (chainId 42220):
0x62B8B11039FcfE5aB0C56E502b1C372A3d2a9c7A0xA4Ff07cF81C02CFD356184879D953970cA9575850xcfA132E353cB4E398080B9700609bb008eceB125GoodID
getWhitelistedRoot(chainClient.ts)quoteMath.ts,AmountPicker.tsx)Navigation & purchase UX
WidgetTabs— Buy Credits | ManageManage tab
setupSnippetrequired in adapter state)compactButtonProps