Skip to content

Multi-buyer support in AI Credits widget #123

Description

@sirpy

Context

Parent issue: #120

Today a payer wallet maps to a single deterministic buyer in the AI Credits widget. We need to let users create and manage multiple buyer identities from one payer wallet, while keeping all widget operations scoped to the selected buyer.

Relevant code

  • packages/ai-credits-widget/src/adapter.ts

    • handleGenerateBuyerKey (around line 528) derives a single buyer private key from a payer wallet signature.
    • AiCreditsWidgetAdapterState (see widgetRuntimeContract.ts) stores only one buyerPubKey / buyerPrvKey pair at a time.
  • packages/ai-credits-widget/src/buyerKeyDerivation.ts

    • buildBuyerKeyMessage and deriveBuyerPrivateKeyFromSignature currently produce one deterministic key from a single signature / counter loop.
  • packages/ai-credits-widget/src/payerSession.ts

    • PayerWalletSession currently stores only one buyerPubKey / buyerPrvKey per payer address.
  • packages/ai-credits-widget/src/widgetRuntimeContract.ts

    • AiCreditsWidgetAdapterActions exposes a single generateBuyerKey action.
    • There is no action for selecting, switching, or importing a buyer.
  • packages/ai-credits-widget/src/components/manage/CreditsManagementCard.tsx

    • Management UI shows total credit, withdraw, and close channel scoped to the currently loaded buyer.
  • packages/ai-credits-widget/src/components/manage/UsageLog.tsx

    • History currently has no buyer filter; entries contain buyerAddress.
  • packages/ai-credits-widget/src/backendTypes.ts

    • GdCreditEntry includes a buyerAddress field, supporting per-buyer filtering on the client.

Requirements

  • Create and switch between multiple buyer identities from one connected payer wallet.
  • Paste a private key to deterministically derive / import a buyer identity.
  • Paste a buyer address (without private key) to treat that address as the active buyer for view-only / consent-pairing flows.
  • Recover a known buyer after refresh via deterministic derivation per buyer index/label.
  • Scope all Manage actions to the selected buyer: balance, consent, withdraw, and API setup.
  • Filter History by buyer.
  • Accept a buyer address plus a buyer signature for operator assignment via URL GET parameters, so a partner can pass in a pre-generated buyer identity and authorization in a deep link.

Acceptance Criteria

  • A payer wallet can create more than one buyer identity and switch between them without disconnecting.
  • Users can paste a private key to generate/import a buyer identity, with format validation and secure handling.
  • Users can paste a buyer address to select that buyer for view/consent flows even when the private key is not available in this session.
  • Buyers can be recovered deterministically by index/label after refresh or reconnect.
  • Balance, consent, withdraw, and API setup use the selected buyer's key/address.
  • History can be filtered by buyer, with an option to show all buyers' activity.
  • Existing single-buyer behavior keeps working for users with only one buyer.
  • URL GET parameters can carry a buyer address and buyer signature for operator assignment; the widget validates and applies them to the selected buyer.

Plan (added by @copilot)

Required states, flows, and behaviors

  1. Multi-buyer identity model per payer
    • Keep a buyer list scoped by payer wallet address.
    • Track activeBuyer and whether it is:
      • deterministic (derived index/label),
      • imported private-key buyer,
      • address-only buyer (no private key, view/consent pairing only).
  2. Buyer lifecycle flows
    • Create buyer from deterministic derivation by index/label.
    • Switch active buyer without disconnecting payer wallet.
    • Import buyer from pasted private key with strict format validation.
    • Select buyer from pasted address without private key.
    • Restore known deterministic buyers after reconnect/refresh.
  3. Scoped runtime behavior
    • All actions (balance, consent, withdraw, close channel, API setup) must read/write only the active buyer context.
    • Flows requiring signing must fail gracefully for address-only buyers.
  4. History behavior
    • Add buyer filter in history (All buyers + specific buyer options).
    • Default to active buyer when selected; keep option to view all buyer entries.
  5. Deep-link behavior
    • Parse URL params for buyer address + buyer signature.
    • Validate signature payload shape and associate only with selected/active buyer.
    • Reject malformed/invalid params with non-blocking user feedback.

Execution plan

  1. Runtime contract/state expansion
    • Update packages/ai-credits-widget/src/widgetRuntimeContract.ts:
      • replace single buyer key fields with structured buyer identities + active buyer reference,
      • add actions for create/select/import/address-only selection,
      • include URL-assignment application action/state where needed.
  2. Deterministic derivation enhancements
    • Update packages/ai-credits-widget/src/buyerKeyDerivation.ts:
      • make derivation deterministic by buyer index/label,
      • keep backward-compatible behavior for index 0 (single-buyer users).
  3. Session persistence model
    • Update packages/ai-credits-widget/src/payerSession.ts:
      • persist per-payer buyer list metadata,
      • persist active buyer pointer,
      • preserve old single-buyer session shape migration on read.
  4. Adapter flow updates
    • Update packages/ai-credits-widget/src/adapter.ts:
      • wire new actions/state transitions,
      • scope existing manage/purchase operations to activeBuyer,
      • add URL GET param parsing/validation for buyer address + signature,
      • apply operator assignment to selected buyer only.
  5. Manage & buy UI updates
    • Update buyer management UI for create/switch/import/select-address flows in:
      • packages/ai-credits-widget/src/components/manage/BuyerOperatorCard.tsx
      • packages/ai-credits-widget/src/components/buy/BuyerKeyPanel.tsx
    • Ensure action enablement/disablement reflects buyer capability (has private key vs address-only).
  6. History filtering
    • Update packages/ai-credits-widget/src/components/manage/UsageLog.tsx to filter by buyerAddress with All buyers option.
    • Reuse packages/ai-credits-widget/src/backendTypes.ts buyerAddress field as filter key.
  7. Manage card scoping checks
    • Verify packages/ai-credits-widget/src/components/manage/CreditsManagementCard.tsx reads the selected buyer context and enforces key-required actions only when private key is available.
  8. QA/stories/tests
    • Update/add AI Credits stories in:
      • examples/storybook/src/stories/ai-credits-widget/AiCreditsWidgetQA.stories.tsx
      • helper fixtures under examples/storybook/src/stories/helpers/ (as needed)
    • Update/add Playwright smoke coverage in:
      • tests/widgets/ai-credits-widget/states.spec.ts
    • Add scenarios for: multi-buyer switch, private-key import, address-only selection, history filtering, deep-link assignment.

Acceptance criteria (implementation-ready)

  • More than one buyer can be created and switched per payer session.
  • Private key import validates and maps deterministically to buyer identity.
  • Address-only buyer selection works for view/consent pairing flows; sign-required actions are guarded.
  • Deterministic buyers are recoverable after refresh/reconnect using index/label mapping.
  • Manage actions execute strictly for selected buyer context.
  • History supports buyer-specific filtering and All buyers view.
  • Single-buyer legacy sessions still function without migration breakage.
  • URL buyer address + signature params are validated and applied to selected buyer assignment flow.

Human-reviewer checklist

  • Confirm runtime contract updates do not break existing widget integration API unexpectedly.
  • Confirm deterministic derivation remains stable for existing buyer index 0 users.
  • Confirm no private key is logged, surfaced unintentionally, or persisted beyond intended session scope.
  • Confirm address-only buyer mode cannot trigger signing-only operations.
  • Confirm operator consent/assignment is bound to selected buyer and not leaked across buyers.
  • Confirm history filter correctness with mixed-buyer entries.
  • Confirm deep-link malformed params are safely rejected.
  • Confirm Storybook and Playwright evidence covers the new flows and edge cases.

Metadata

Metadata

Labels

enhancementNew feature or request

Projects

Status
Prepare AI Task

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions