Skip to content

feat(connect): add useParties() for every party the wallet holds - #66

Open
fernandomg wants to merge 3 commits into
mainfrom
feat/51-parties
Open

feat(connect): add useParties() for every party the wallet holds#66
fernandomg wants to merge 3 commits into
mainfrom
feat/51-parties

Conversation

@fernandomg

Copy link
Copy Markdown
Member

Summary

Part of #51; the issue closes at the top of this stack (#63).

#51's two halves (the party read model and the wallet list) plus the #57 lifecycle fixes grew into
one 12-commit draft. Split so each concern reviews on its own: this slice is the party read model,
cut from the same commits. Merge order is #47, #48, this, then the rest of the stack.

Changes

  • useParties(): every usable party the wallet holds, primary first, updating on
    accountsChanged. party is always parties[0].
  • Party mapping filters on the wallet's own status: only allocated parties are offered.
    initialized means the party is not on the ledger yet, so it cannot act. disabled is never
    filtered on; that flag only records that no signing provider matched the namespace, and such a
    party still signs through the participant.
  • The fake wallet and the mock adapter accept a per-account status, so tests can drive the filter.

Acceptance criteria

From #51, the criteria this slice owns:

  • useParties() exists, returns every usable account as Party, and updates on accountsChanged
  • selectPrimaryAccount and toParty stay internal
  • JSDoc on the new exports

The remaining #51 criteria (wallet entries readable from the public surface, #50's picker buildable
against them) are the selection half and land with #63, as does the useParties README hook-table
row (task 11 of the plan, one doc pass for the whole stack).

Test plan

Automated tests

pnpm -C canton-connect test: 38 to 50 tests. New coverage: the mapping filter cases in
walletAccount.test.ts, push/read parity for useParties() in CantonConnectProvider.test.tsx,
and the doubles' status support in mockAdapter.test.ts. Verified at this commit: 50 passed,
tsc and biome check clean.

Manual verification

No manual steps required; the harness walkthrough happens at the stack top (#63, task 12).

Breaking changes

None.

Checklist

Screenshots

None.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a useParties() hook to canton-connect, exposing every usable party the connected wallet holds (primary first), updating on accountsChanged. It is the party read-model slice of issue #51, cut from a larger stack whose selection half lands in #63. The account-mapping helper gains a toParties function that filters on the wallet's status (only allocated parties are offered; disabled is deliberately never filtered on), and the provider now tracks a parties array alongside party, keeping party === parties[0] through one shared applyAccounts mapping. The test doubles (fake wallet, mock adapter) accept a per-account status so the filter can be driven.

Changes:

  • New useParties() hook + UsePartiesResult, exported from the barrel with JSDoc; selectPrimaryAccount/toParty stay internal.
  • toParties centralizes account→Party mapping (status filter + primary-first ordering); provider wires it into restore/connect/disconnect via a single applyAccounts callback and adds parties to the context value.
  • Fake wallet and mock adapter accept a per-account status, defaulting to allocated in the mock.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
canton-connect/src/walletAccount.ts Adds toParties (status filter via isUsable, primary-first order) and status/disabled fields on the raw account shape.
canton-connect/src/walletAccount.test.ts Covers toParties filtering, ordering, per-account networkId fallback, and empty cases.
canton-connect/src/hooks/useParties.ts New hook returning { parties } from context, with JSDoc.
canton-connect/src/CantonConnectProvider.tsx Adds parties state and applyAccounts, maintaining party === parties[0] across all lifecycle paths.
canton-connect/src/CantonConnectProvider.test.tsx Adds push/read/disconnect parity tests for useParties().
canton-connect/src/testing/fakeWallet.ts Adds optional per-account status.
canton-connect/src/mock/mockAdapter.ts Adds per-account status (defaults to allocated); renames MOCK_WALLET_STATUSDEFAULT_WALLET_STATUS.
canton-connect/src/mock/mockAdapter.test.ts Verifies status pass-through and the allocated default.
canton-connect/src/index.ts Exports useParties and UsePartiesResult.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Base automatically changed from docs/42-jsdoc-public-api to main August 3, 2026 13:05
The SDK returns one record per account, each carrying its own required status.
Both doubles now model that per-account rather than wallet-wide: the fake
wallet passes a declared status straight through, and the mock defaults to
allocated when an account declares none.
- only `allocated` parties are offered: `initialized` never finished signing, `removed` is gone
- an account reporting no status is kept, so older wallets and test doubles still work
- `disabled` is never filtered on — such a party signs through the participant and still works
- the primary account leads; the rest keep the wallet's order
- provider tracks parties beside party; one applyAccounts callback maps both
- initial listAccounts read and the accountsChanged push share that mapping
- parties empties wherever party goes undefined: lock, disconnect, dead probe
- party is always parties[0]; a test states the invariant

/**
* Every party the connected wallet holds that can actually act, primary
* first. Empty while disconnected and while the wallet is locked; parties the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Promises "Empty while disconnected and while the wallet is locked;", but statusChanged handler at CantonConnectProvider.tsx sets only isLocked and never clears parties.

@gabitoesmiapodo gabitoesmiapodo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

.

(accounts: AccountsChangedEvent): void => {
const mapped = toParties(accounts, networkId)
setParties(mapped)
setParty(mapped[0])

@gabitoesmiapodo gabitoesmiapodo Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

(CC feedback)

connected + unlocked + zero parties is now reachable, undocumented, untested

The status filter at walletAccount.ts:30-31 can empty the list for a live wallet. Previously selectPrimaryAccount fell back to the first entry regardless of status (walletAccount.ts:18-19), so party was always defined once connected.

Verified: a wallet holding one initialized account connects successfully and yields { parties: [], party: undefined, status: 'connected', isConnected: true }.

isConnected no longer implies party. useParty.ts:11 still says party is undefined only "until connect() succeeds", and no test covers this state. A consumer doing if (isConnected) party.partyId now throws.

Comment on lines +29 to +31
// Only an allocated party exists on the ledger; a missing status is trusted (older wallets).
const isUsable = (account: RawWalletAccount): boolean =>
account.status === undefined || account.status === 'allocated'

@gabitoesmiapodo gabitoesmiapodo Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

(CC feedback)

the initialized exclusion rests on an undocumented inference

walletAccount.ts:29 asserts "Only an allocated party exists on the ledger". The SDK documents WalletStatus as nothing more than "The status of the wallet" (@canton-network/dapp-sdk/dist/dapp-api/rpc-gen/typings.d.ts:322-327). Nothing in the SDK, this repo, or wallet-service (which returns [] from listAccounts) corroborates the lifecycle reading.

The disabled half of the PR's reasoning does check out: the SDK explicitly documents it as "no signing provider matches the party's namespace ... Disabled wallets use participant as the default signing provider" (typings.d.ts:364-369), so never filtering on it is correct.

The risk is one-directional. If a real wallet reports initialized for a party the user can act as, parties disappear with no signal, and per the test plan no live-wallet check happens until #63. A console.warn on a dropped account, or verifying against one real wallet before the stack merges, would close it.

@gabitoesmiapodo
gabitoesmiapodo self-requested a review August 3, 2026 17:19

@gabitoesmiapodo gabitoesmiapodo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Left a few comments, all low priority.

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.

3 participants