Skip to content

feat: key deployments by chain id and address - #67

Merged
kyscott18 merged 1 commit into
mainfrom
feat/deployment-key
Oct 14, 2026
Merged

kyscott18 merged 1 commit into
mainfrom
feat/deployment-key

Conversation

@kyscott18

@kyscott18 kyscott18 commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Add deploymentKey(chainId, address) to typewriter/client. The runtime uses it for the Postgres schema name. The token and order-book frontends use it as the browser storage key. Before this change, order-book used the address only, so deployments on different chains with the same address shared browser accounts.
  • deploymentSchemaName throws when the name is longer than 63 characters, because Postgres truncates longer identifiers without an error.
  • Token and order-book now use the same account management:
    • lib/account-store.ts stores { accountId, keyPair } as one IndexedDB record.
    • hooks/useManifest and hooks/useStoredAccount thinly wrap useQuery. Mutations update the stored account with setQueryData.
    • Each session signs on a random nonce lane, so the nonce is not stored.
    • Signing keys are not extractable.
    • lib/api.ts is shared, and reads plain-text error bodies.
  • Order-book drops its manifest and account contexts and the unused sign-in hook. It signs with credential ID 1 and gets a sign-out mutation.

Notes

  • Users with saved accounts must sign in again.
  • Known problem, not fixed here: a restarted anvil deploys to the same address again, so an old Postgres schema and old IndexedDB records can conflict with the new deployment.

Test plan

  • bun run typecheck in each app and package
  • bunx biome check
  • packages/typewriter: client, migrate and db-query tests pass
  • Manual browser test of token and order-book sign-up, transfer, and order flows

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 2/5

Fix the sign-out race and failed token setup recovery before merging.

Fix All in Claude CodeFindings

  1. P1 Security Sign-out gets undone ▶
  2. P1 Failed setup cannot recover ▶
Fix with agent prompt
### Issue 1
apps/order-book/frontend/hooks/useMarketOrderMutation.ts:49-50
If a user signs out while an order is pending, this callback restores the captured account, including its signing keys, when the order succeeds. The sign-out button stays available during the request. Unlike the old null-preserving update, this replaces the whole account, so the page can sign more orders without another sign-in.

Update the cache only if it still holds the same session. Apply the same guard in `useDepositMutation`.

**How this was verified:** Sign-out clears the cached account, but a pending order’s success callback restores the captured keys that subsequent orders use to sign.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

### Issue 2
apps/token/frontend/App.tsx:229-230
The token app now saves the account before submitting `Mint`. If that request fails before acceptance, reloading loads the saved account with no tokens and hides the only sign-in button. The signed-in page has no mint retry or sign-out action, so the user cannot finish setup without clearing browser storage.

Save the account after `Mint` succeeds, or keep a recoverable setup state with a retry action.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This PR shares deployment keys across Postgres and browser storage, and moves both frontends to account records in IndexedDB.

  • Token and order-book accounts stay tied to their deployment.
  • Postgres schemas use a validated chain and contract address.
  • API errors show the server's plain-text message.

Acknowledged by kyscott18: existing saved accounts must sign in again because storage changes. Also explicitly deferred by kyscott18: restarting anvil can reuse an address and conflict with old Postgres schemas and IndexedDB records.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  M[Manifest: chain ID and address] --> K[deploymentKey]
  K --> P[Postgres schema name]
  K --> I[IndexedDB account record]
  I --> Q[React Query account cache]
  Q --> S[Sign order]
  S --> R[Request pending]
  Q --> O[Sign out]
  O --> C[Delete record and clear cache]
  R --> F[Order succeeds]
  F --> B[Captured account replaces cache]
  B --> Q
Loading

Reviews (1) · Last reviewed commit: "feat: key deployments by chain id and ad..." · Reviewed by Greptile

Add deploymentKey(chainId, address) to typewriter/client. The runtime
uses it for the Postgres schema name, and the token and order-book
frontends use it as the browser storage key. Before this change, the
order-book frontend used the address only, so deployments on different
chains with the same address shared browser accounts.
deploymentSchemaName throws when the name exceeds 63 characters,
because Postgres truncates longer identifiers without an error.

Give the token and order-book frontends the same account management:

- lib/account-store.ts stores { accountId, keyPair } as one IndexedDB
  record, so a write cannot leave part of an account behind.
- hooks/useManifest and hooks/useStoredAccount thinly wrap useQuery.
  Mutations update the stored account with setQueryData.
- Each session signs on a random nonce lane, so the nonce is not
  stored and two tabs cannot use the same nonce.
- Signing keys are not extractable.
- lib/api.ts is shared, and reads plain-text error bodies.

Order book drops its manifest and account contexts and the unused
sign-in hook, signs with the session credential (ID 1), and gets a
sign-out mutation that deletes the stored account.
@kyscott18
kyscott18 merged commit c9ed91c into main Oct 14, 2026
8 checks passed
@kyscott18
kyscott18 deleted the feat/deployment-key branch October 14, 2026 21:27
Comment on lines +49 to +50
onSuccess: async (result) => {
queryClient.setQueryData(["storedAccount", storageKey], result.account);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Sign-out gets undone

If a user signs out while an order is pending, this callback restores the captured account, including its signing keys, when the order succeeds. The sign-out button stays available during the request. Unlike the old null-preserving update, this replaces the whole account, so the page can sign more orders without another sign-in.

Update the cache only if it still holds the same session. Apply the same guard in useDepositMutation.

How this was verified: Sign-out clears the cached account, but a pending order’s success callback restores the captured keys that subsequent orders use to sign.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/order-book/frontend/hooks/useMarketOrderMutation.ts
Line: 49-50

Comment:
**Sign-out gets undone**

If a user signs out while an order is pending, this callback restores the captured account, including its signing keys, when the order succeeds. The sign-out button stays available during the request. Unlike the old null-preserving update, this replaces the whole account, so the page can sign more orders without another sign-in.

Update the cache only if it still holds the same session. Apply the same guard in `useDepositMutation`.

**How this was verified:** Sign-out clears the cached account, but a pending order’s success callback restores the captured keys that subsequent orders use to sign.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

Comment on lines +229 to +230
const stored = { accountId, keyPair };
await saveStoredAccount(storageKey, stored);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Failed setup cannot recover

The token app now saves the account before submitting Mint. If that request fails before acceptance, reloading loads the saved account with no tokens and hides the only sign-in button. The signed-in page has no mint retry or sign-out action, so the user cannot finish setup without clearing browser storage.

Save the account after Mint succeeds, or keep a recoverable setup state with a retry action.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/token/frontend/App.tsx
Line: 229-230

Comment:
**Failed setup cannot recover**

The token app now saves the account before submitting `Mint`. If that request fails before acceptance, reloading loads the saved account with no tokens and hides the only sign-in button. The signed-in page has no mint retry or sign-out action, so the user cannot finish setup without clearing browser storage.

Save the account after `Mint` succeeds, or keep a recoverable setup state with a retry action.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

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