Titandocs

Wallets

Canton CIP-0103, 5N Loop, EVM and Auth0 — the connect paths, and the SDK quirks each one forced.

Auth is exclusive: exactly one of Canton, EVM, or Auth0 is live at a time. Connecting one disconnects the others. On rehydrate, priority is canton > evm > auth0.

Only the Canton paths produce a trading party.

Canton — CIP-0103

lib/auth/canton-wallet.ts, lazy-loaded. Three target kinds:

TargetHow
{kind:'extension', id}A wallet detected via canton:announceProvider window events
{kind:'remote', url}Gateway URL → SDK RemoteAdapter (supported by the store, not exposed in the modal)
{kind:'walletconnect'}SDK WalletConnectAdapter — mobile and hardware wallets

Connection goes through DiscoveryClient.connect(providerId), not DappSDK.connect().

DappSDK.connect()'s failure path calls waitForWalletPickerRetrySelection(), which reads a window opened by the SDK's own picker popup — one we never open. So every failed connect rejected with "Wallet picker is not open" while the real cause went to notifyWalletPickerError(), which posts into that same absent window and drops it.

Verified: a dead gateway reports Failed to fetch through DiscoveryClient, and Wallet picker is not open through DappSDK.connect(). One of those is debuggable.

Two Canton extensions can answer each other's requests. Extensions share the window.canton slot. WindowTransport routes by target but matches the response on the JSON-RPC id alone — so an untargeted adapter can be answered by the wrong wallet. The RPC client is therefore built from the active session, carrying the announced target.

After connect

The wallet signs the canonical Session-Auth blob once, opening an engine trading session (sessionService.authorizePOST /v1/session-auth). Failure is non-fatal — the user is connected but cannot trade, and the order form shows "Enable trading" to retry.

connectCanton(target);        // connect + session-auth
cancelCantonConnect();        // abort in-flight — the SDK never rejects an abandoned prompt
enableCantonTrading();        // re-run session-auth after a refused signature
disconnectCanton();           // drops session and wallet link

WalletConnect specifics

  • The adapter is registered once via sdk.init({ additionalAdapters }) inside createInstance(); later connects reuse configuredAdapters.
  • chainId is canton:${config.networkId} — the SDK defaults to canton:devnet, which is wrong on mainnet builds.
  • The pairing URI reaches the modal through setPairingUriHandlercantonPairingUri in the store. The adapter's built-in showUriInPopup writes into a window only the SDK's picker opens, so the QR would otherwise be dropped — and it creates a stray about:blank tab via window.open("", "wallet-popup"), which withoutSdkQrPopup() neuters for the duration of the connect.

Reown AppKit and the Canton adapter both initialise WalletConnect Core with the same project id, so WC logs Core is already initialized… called 2 times. Harmless today; the fix if it ever misbehaves is a separate project id for the Canton adapter.

5N Loop

Not CIP-0103. @fivenorth/loop-sdk runs its own popup, QR dialog and WebSocket session, so it bypasses DiscoveryClient entirely — connectCantonWallet routes {kind:'loop'} to lib/auth/loop-wallet.ts.

It signs Ed25519, so fingerprint binding and engine session-auth verification are unchanged.

Three quirks worth knowing:

loop.connect() resolves void; the provider only arrives through the onAccept callback passed to init(). Hence the callback→promise bridge, and init() must run exactly once.

The RPC is SIGN_RAW_MESSAGE — the canonical blob goes over as literal text, never base64, whatever VITE_SIGN_MESSAGE_ENCODING says.

Its QR is a full-screen <dialog open> opened by attribute, not showModal() — so Escape is inert — with no close control, sitting on top of our own Cancel. installEscapeHatch() adds Escape + backdrop-click, rejecting with LoopCancelledError and calling loop.logout(), whose clearLocalSession() tears the dialog down.

The wallet catalog

lib/auth/wallet-catalog.ts is a hand-maintained list of the 10 known Canton wallets. The Canton tab is a single searchable card grid — no accordion, no per-wallet rows.

connectivityCard behaviour
cip0103Connect when installed, otherwise an Install link
walletconnectConnect via the WC adapter
vendorSdkDisabled — "Coming soon"
unsupportedDisabled, but shown

Unsupported wallets are shown disabled rather than hidden. The user's real question is "is my wallet supported?", and an absent entry answers that ambiguously.

findCatalogWallet resolves an announcement by longest matching token — Console Wallet announces as loop, so a first-match scan would steal a "5N Loop" announcement for Console.

EVM and Mail

MethodStackRole
EVMReown AppKit + wagmi + viem, mirrored by <ReownWalletSync>Identity / SIWE proof
MailAuth0 email + passkey, mirrored by <Auth0WalletSync>Identity

Both are tenant-gated: with VITE_AUTH0_DOMAIN and VITE_AUTH0_CLIENT_ID empty, the Mail tab renders Unavailable and no Auth0Provider mounts at all.

On this page