User story / Problem statement
canton-connect is described as wagmi-style, and at the level that matters most it is: the hook
names and the split of responsibility match, so a developer arriving from wagmi knows which hook to
reach for. Each hook's JSDoc now names its wagmi counterpart (useParty → useAccount,
useExecute → useWriteContract + useWaitForTransactionReceipt, and so on).
The resemblance stops at the return shapes. wagmi's mutation hooks are wrappers around TanStack
Query's useMutation, so they return mutate / mutateAsync / isPending / data / status /
failureCount / reset. Ours resolve plain promises and return domain-named fields:
isSigning, isExecuting, signature, lastTx. Someone with wagmi muscle memory reaches for
isPending and data and doesn't find them.
That is a real papercut, but the fix has two very different sizes, and conflating them is how this
turns into an accidental architecture change.
Proposed solution
Level 1 — align the vocabulary. No new dependency.
Rename the status flags to wagmi's uniform isPending (replacing isSigning and isExecuting),
and keep error and reset, which already match. This is a pure rename of the public surface,
free right now because the package has no consumers.
Deliberately not proposed: renaming the payloads to data. useSignMessage().signature is
clearer than data, and useExecute().lastTx is not wagmi's data at all — see below. Adopting
data everywhere buys canonicality at the cost of saying less. Worth arguing either way, which is
why it is an open question rather than an acceptance criterion.
Level 2 — actually adopt TanStack Query. New peer dependency, consumer-visible.
This is what would make the hooks genuinely wagmi-shaped: useMutation under the covers, so
mutate/mutateAsync/status/failureCount come for free, plus useQuery for reads. It also
requires every consumer to wrap the app in a QueryClientProvider and pulls the package's users
into a specific data-layer choice — a heavy import for a package whose stated value is being thin
and cheap to delete once the SDK ships hooks of its own.
Recommendation: do Level 1 if the papercut is worth a rename; hold Level 2 until the package
grows a read surface. TanStack Query earns its keep on cached, refetchable, deduplicated
queries. Today this package has three write-shaped operations (sign, execute, ledger call) and two
context reads (party, status), and mutations don't cache. The moment useLedger becomes typed
cached reads — ACS queries, balances, listAccounts — the calculus flips and Level 2 becomes the
obvious answer rather than ceremony.
One Canton-specific reason not to force the fit. wagmi's mutations model one request producing
one result. useExecute().lastTx models a wallet-pushed lifecycle — pending → signed → executed
or failed — arriving as txChanged events after the call resolves. wagmi's nearest equivalent
splits that across useWriteContract (submit) and useWaitForTransactionReceipt (one receipt at
the end); it has no concept of a status stream from the wallet. Mapping lastTx onto data would
misrepresent it. Whatever shape this lands on, the tx lifecycle stays a first-class field of its
own.
Acceptance criteria
Out of scope
- Adding
@tanstack/react-query as a dependency in this issue. If Level 2 wins, it gets its own
issue with the QueryClientProvider requirement spelled out for consumers.
- Renaming the hooks themselves. The names already match wagmi and are not in question.
User story / Problem statement
canton-connectis described as wagmi-style, and at the level that matters most it is: the hooknames and the split of responsibility match, so a developer arriving from wagmi knows which hook to
reach for. Each hook's JSDoc now names its wagmi counterpart (
useParty→useAccount,useExecute→useWriteContract+useWaitForTransactionReceipt, and so on).The resemblance stops at the return shapes. wagmi's mutation hooks are wrappers around TanStack
Query's
useMutation, so they returnmutate/mutateAsync/isPending/data/status/failureCount/reset. Ours resolve plain promises and return domain-named fields:isSigning,isExecuting,signature,lastTx. Someone with wagmi muscle memory reaches forisPendinganddataand doesn't find them.That is a real papercut, but the fix has two very different sizes, and conflating them is how this
turns into an accidental architecture change.
Proposed solution
Level 1 — align the vocabulary. No new dependency.
Rename the status flags to wagmi's uniform
isPending(replacingisSigningandisExecuting),and keep
errorandreset, which already match. This is a pure rename of the public surface,free right now because the package has no consumers.
Deliberately not proposed: renaming the payloads to
data.useSignMessage().signatureisclearer than
data, anduseExecute().lastTxis not wagmi'sdataat all — see below. Adoptingdataeverywhere buys canonicality at the cost of saying less. Worth arguing either way, which iswhy it is an open question rather than an acceptance criterion.
Level 2 — actually adopt TanStack Query. New peer dependency, consumer-visible.
This is what would make the hooks genuinely wagmi-shaped:
useMutationunder the covers, somutate/mutateAsync/status/failureCountcome for free, plususeQueryfor reads. It alsorequires every consumer to wrap the app in a
QueryClientProviderand pulls the package's usersinto a specific data-layer choice — a heavy import for a package whose stated value is being thin
and cheap to delete once the SDK ships hooks of its own.
Recommendation: do Level 1 if the papercut is worth a rename; hold Level 2 until the package
grows a read surface. TanStack Query earns its keep on cached, refetchable, deduplicated
queries. Today this package has three write-shaped operations (sign, execute, ledger call) and two
context reads (party, status), and mutations don't cache. The moment
useLedgerbecomes typedcached reads — ACS queries, balances,
listAccounts— the calculus flips and Level 2 becomes theobvious answer rather than ceremony.
One Canton-specific reason not to force the fit. wagmi's mutations model one request producing
one result.
useExecute().lastTxmodels a wallet-pushed lifecycle —pending → signed → executedor
failed— arriving astxChangedevents after the call resolves. wagmi's nearest equivalentsplits that across
useWriteContract(submit) anduseWaitForTransactionReceipt(one receipt atthe end); it has no concept of a status stream from the wallet. Mapping
lastTxontodatawouldmisrepresent it. Whatever shape this lands on, the tx lifecycle stays a first-class field of its
own.
Acceptance criteria
isSigningandisExecutingbecomeisPending, uniformly across the hooks that have apending state
signature,lastTx) orwagmi's generic
dataso the counterpart line can't be read as a promise of drop-in compatibility
README.md's hook table matches whatever is decideddown (the first cached read the package needs)
lastTxkeeps its own field regardless, with the reason recorded: it is an event stream, nota mutation result
Out of scope
@tanstack/react-queryas a dependency in this issue. If Level 2 wins, it gets its ownissue with the
QueryClientProviderrequirement spelled out for consumers.