Open
feat: multi-buyer identity support in AI Credits widget#136
Conversation
24 tasks
Co-authored-by: blueogin <43612769+blueogin@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Add multi-buyer support in AI Credits widget
feat: multi-buyer identity support in AI Credits widget
Jul 30, 2026
blueogin
approved these changes
Jul 31, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR expands the AI Credits widget runtime/session model from a single deterministic buyer identity to a multi-buyer identity system per payer wallet, adding UI affordances to create/import/watch buyers, switching active buyer context, and filtering history by buyer.
Changes:
- Introduces
BuyerRecord[]+activeBuyerAddressinto runtime state/session (with legacy session migration) and adds new adapter actions for buyer lifecycle (create/select/import/watch/deep-link). - Updates Manage + History UI to support buyer selection and buyer-based history filtering.
- Adds Storybook QA fixtures and Playwright smoke coverage for multi-buyer flows.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/widgets/ai-credits-widget/states.spec.ts | Adds Playwright smoke tests + screenshots for multi-buyer UI states. |
| packages/ai-credits-widget/src/widgetRuntimeContract.ts | Extends adapter state/actions for multi-buyer support and re-exports BuyerRecord. |
| packages/ai-credits-widget/src/useAiCreditsHistory.ts | Adds buyer filter state/action and client-side filtering by entry.buyerAddress. |
| packages/ai-credits-widget/src/payerSession.ts | Replaces legacy single-buyer session fields with buyers[] + activeBuyerAddress and migration logic. |
| packages/ai-credits-widget/src/index.ts | Exports the new buyer-related types for consumers. |
| packages/ai-credits-widget/src/components/manage/BuyerOperatorCard.tsx | Adds buyer selector UI and import/watch panel; disables signing for address-only buyers. |
| packages/ai-credits-widget/src/components/history/HistoryTab.tsx | Adds buyer filter dropdown fed by known buyers. |
| packages/ai-credits-widget/src/buyerKeyDerivation.ts | Makes derivation message index-aware while preserving index 0 legacy message. |
| packages/ai-credits-widget/src/AiCreditsWidget.tsx | Wires knownBuyers to History UI and defaults history filter to active buyer. |
| packages/ai-credits-widget/src/adapter.ts | Implements multi-buyer session wiring + new actions (create/select/import/watch/deep-link) and URL param parsing. |
| examples/storybook/src/stories/helpers/aiCreditsWidgetStories.tsx | Adds multi-buyer QA fixture stories. |
| examples/storybook/src/stories/ai-credits-widget/AiCreditsWidgetQA.stories.tsx | Exposes new QA fixtures in Storybook. |
Suppressed comments (3)
packages/ai-credits-widget/src/adapter.ts:894
- Deep-link signature validation currently accepts any non-empty hex string (
/^0x[0-9a-fA-F]{2,}$/), so malformed/too-short signatures will be treated as “valid”. At minimum, require a 64- or 65-byte signature (EIP-2098 compact or standard) before accepting URL params.
if (!/^0x[0-9a-fA-F]{2,}$/.test(trimmedSignature)) {
setState((prev) =>
withDerivedStatus(prev, { error: 'Deep-link buyer signature is invalid' }, true),
)
return
packages/ai-credits-widget/src/adapter.ts:1457
- Deep-link parsing runs before a payer wallet is connected. When
buyerAddress/buyerSignatureare present butaddressis still null,handleApplyDeepLinkBuyersets an error (“Connect your wallet…”) on mount. Since the effect re-runs after connect anyway, defer applying deep-link params untiladdressis available to avoid a spurious error flash.
useEffect(() => {
if (typeof window === 'undefined') return
const params = new URLSearchParams(window.location.search)
const urlBuyerAddress = params.get('buyerAddress')
const urlBuyerSignature = params.get('buyerSignature')
packages/ai-credits-widget/src/components/history/HistoryTab.tsx:524
- Options in
BuyerFilterSelecthaverole="option"but don’t indicate selection viaaria-selected, which is part of the listbox pattern. This makes it harder for screen readers to announce which buyer filter is currently selected.
tag="button"
role="option"
paddingHorizontal="$3"
paddingVertical="$2"
cursor="pointer"
…ts widget - Added support for deep link parameters, including buyer address and operator signature, to facilitate buyer registration via NCDI deep links. - Introduced validation functions for buyer address and operator signature. - Enhanced state management to handle operator consent and session updates based on deep link data. - Updated relevant components and types to accommodate new deep link functionality, ensuring a seamless user experience when applying deep links.
- Introduced new functions for managing deep link parameters, including storage and retrieval from local storage. - Updated error handling to provide clearer messages when deep link parameters are invalid or missing. - Improved state management to reflect deep link status and errors in the UI, ensuring a better user experience. - Added a fallback mechanism for deep link processing, enhancing robustness in various scenarios.
- Removed the previous implementation of handleGenerateBuyerKey, consolidating its functionality into handleCreateBuyer for clarity and to preserve backward compatibility. - Updated comments to reflect changes in buyer key generation logic, ensuring better understanding of the derivation index handling. - Enhanced the interface documentation for generateBuyerKey to clarify its behavior regarding existing buyers and derivation indices.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A payer wallet was limited to a single deterministic buyer identity. This adds full multi-buyer identity management: create additional derived buyers, import via private key, register address-only (view/consent) buyers, switch between them, and deep-link assign a partner buyer via URL params.
Runtime contract & session model
BuyerRecordtype withtype: 'derived' | 'imported' | 'address-only',derivationIndex, andlabelpayerSessionnow storesbuyers: BuyerRecord[]+activeBuyerAddress; old single-buyer sessions auto-migrate on first readbuyersandactiveBuyerAddress;buyerPubKey/buyerPrvKeyremain as computed fields from the active buyerDerivation
buildBuyerKeyMessage(address, index)— index0preserves the exact legacy message for backward compatibility; index> 0appends(buyer N)New adapter actions
createBuyerselectBuyer(address)importBuyerFromPrivateKey(key)0x…64hex, derives address, registers asimportedselectBuyerByAddress(address)applyDeepLinkBuyer(address, sig)?buyerAddress=&buyerSignature=URL params on mount, registers partner buyerUI
BuyerOperatorCard: buyer selector list (visible when >1 buyer), "New Buyer" button, collapsible "Import Key" / "Watch Address" panel; address-only buyers show a "view only" badge and Sign Consent is disabledHistoryTab:BuyerFilterSelectdropdown fed via newknownBuyersprop; defaults to active buyer on history tab openuseAiCreditsHistory:buyerAddressFilterstate +setBuyerAddressFilteraction; client-side filtering onentry.buyerAddressStories & tests
Three new QA fixtures (
MultiBuyerManage,AddressOnlyBuyer,MultiBuyerHistory) and four Playwright smoke tests covering the new flows.