Wallets & Adapters
PartyLayer includes built-in wallet adapters and supports custom adapters for any Canton wallet. Wallets are discovered through the registry and CIP-0103 native provider detection.
Built-in Wallets
| Wallet | Networks | Opt-in | Adapter |
|---|
| Console Wallet | devnet, testnet, mainnet | No | @partylayer/adapter-console |
| 5N Loop | devnet, testnet, mainnet | No | @partylayer/adapter-loop |
| Cantor8 | devnet, testnet, mainnet | No | @partylayer/adapter-cantor8 |
| Bron | devnet, testnet, mainnet | Yes | @partylayer/adapter-bron |
| Nightly | devnet, testnet, mainnet | No | @partylayer/adapter-nightly |
| Send | mainnet | No | @partylayer/adapter-send |
| Walley | mainnet, testnet, devnet | No | @k2flabs/walley-dapp-sdk |
| WalletConnect | devnet, testnet, mainnet | Yes | @partylayer/adapter-walletconnect |
ℹ️ Note
Wallets marked
Opt-in (
Bron,
WalletConnect) require explicit configuration — register them via
config.adapters (Bron needs an OAuth client ID; WalletConnect needs the optional
@walletconnect/* peers). The rest are auto-registered when using
PartyLayerKit.
Capability Matrix
Not every wallet supports every operation. Check this matrix before building features that depend on specific capabilities.
| Wallet | connect | signMessage | signTransaction | submitTransaction | ledgerApi | restore |
|---|
| Console | supported | supported | supported | supported | full | supported |
| 5N Loop | supported | supported | none | supported | limited | supported |
| Cantor8 | supported | supported | supported | none | none | supported |
| Nightly | supported | supported | none | supported | full | supported |
| Bron | supported | supported | supported | none | full | supported |
| Send | supported | supported | none | supported | full | supported |
| Walley | supported | supported | none | supported | none | supported |
| WalletConnect | supported | supported | none | supported | full | supported |
Capability Notes
- Loop / Nightly / Send — signTransaction: these wallets combine signing and submission into a single step. Use
submitTransaction directly instead of the separate sign-then-submit pattern. Calling signTransaction on any of them throws CapabilityNotSupportedError pointing you at this fix. - Send — passkey signing & namespace guard: Send injects at the bare
window.canton slot (the same slot any splice-wallet-kernel-compatible extension would use). The Send adapter verifies the running provider's kernel.id before forwarding any RPC, so installing Send next to a Console-class wallet never claims the wrong provider. Send is currently mainnet-only and signs every transaction via WebAuthn-PRF (Touch ID / Face ID), so the user sees a passkey prompt rather than an extension popup. - Loop — ledgerApi (limited): Supports
POST /v2/state/acs, GET /v2/state/acs/active-contracts, POST /v2/commands/submit, and POST /v2/commands/submit-and-wait. Other endpoints are not available — for full Ledger API access, use Console, Nightly, or Bron. - Cantor8: Mobile-only deep link transport. Supports
signMessage and signTransaction via the deep link flow, but does not expose submitTransaction or ledgerApi. - Bron — submitTransaction: Bron is a remote signer (OAuth) — it signs commands but does not submit them directly to the ledger. Pair
signTransaction with your own participant submission, or call ledgerApi against /v2/commands/submit-and-wait and let Bron sign the pre-built command. Requires explicit OAuth configuration via BronAdapter with auth and api config. - Session restore (all five wallets): every adapter declares the
restore capability and implements a matching restore() method. On page reload, the SDK decrypts the persisted session and hands it to the adapter, which re-establishes the provider if it can. See Advanced → Session Persistence for per-wallet behavior and edge cases.
Adding Bron (Enterprise)
import { PartyLayerKit } from '@partylayer/react';
import { getBuiltinAdapters } from '@partylayer/sdk';
import { BronAdapter } from '@partylayer/adapter-bron';
<PartyLayerKit
network="mainnet"
appName="My dApp"
adapters={[
...getBuiltinAdapters(),
new BronAdapter({
auth: {
clientId: 'your-oauth-client-id',
redirectUri: 'https://my-app.com/callback',
authorizationUrl: 'https://auth.bron.example/authorize',
tokenUrl: 'https://auth.bron.example/token',
},
api: {
baseUrl: 'https://api.bron.example',
getAccessToken: async () => getStoredAccessToken(),
},
}),
]}
>
Wallet Discovery
PartyLayer discovers wallets through two mechanisms:
1. Registry Discovery
On initialization, the SDK fetches the wallet registry from registry.partylayer.xyz. The registry contains verified wallet metadata — names, icons, capabilities, install hints, and supported networks. Registry entries are cryptographically signed.
If the registry is unreachable, the SDK gracefully falls back to adapter-only discovery, ensuring your dApp still works offline.
2. CIP-0103 Native Detection
The SDK scans window.canton.* for CIP-0103 compliant providers injected by wallet extensions. Native wallets appear first in the wallet list with a "Native" badge.
// What the SDK does internally:
import { discoverInjectedProviders } from '@partylayer/provider';
const providers = discoverInjectedProviders();
// → [{ id: 'console', provider: CIP0103Provider }, ...]
getBuiltinAdapters
Returns all auto-registered adapters (Console, Loop, Cantor8, Nightly):
import { getBuiltinAdapters } from '@partylayer/sdk';
const adapters = getBuiltinAdapters();
// → [ConsoleAdapter, LoopAdapter, Cantor8Adapter, NightlyAdapter]
Creating a Custom Adapter
To support a new wallet, implement the WalletAdapter interface:
import type {
WalletAdapter, AdapterContext, AdapterDetectResult,
AdapterConnectResult, Session, SignMessageParams,
SignTransactionParams, SubmitTransactionParams,
} from '@partylayer/core';
class MyWalletAdapter implements WalletAdapter {
readonly walletId = 'my-wallet' as WalletId;
readonly name = 'My Custom Wallet';
getCapabilities() {
return ['connect', 'disconnect', 'signMessage', 'signTransaction'];
}
async detectInstalled(): Promise<AdapterDetectResult> {
const installed = typeof window !== 'undefined' && !!window.myWallet;
return { installed, reason: installed ? undefined : 'Extension not found' };
}
async connect(ctx: AdapterContext, opts?: { timeoutMs?: number }): Promise<AdapterConnectResult> {
const result = await window.myWallet.connect({
appName: ctx.appName,
network: ctx.network,
});
return {
partyId: result.partyId,
session: { metadata: result.metadata },
capabilities: this.getCapabilities(),
};
}
async disconnect(ctx: AdapterContext, session: Session): Promise<void> {
await window.myWallet.disconnect();
}
async signMessage(ctx: AdapterContext, session: Session, params: SignMessageParams) {
const sig = await window.myWallet.sign(params.message);
return {
signature: sig,
partyId: session.partyId,
message: params.message,
};
}
async signTransaction(ctx: AdapterContext, session: Session, params: SignTransactionParams) {
const result = await window.myWallet.signTx(params.tx);
return {
signedTx: result.signedPayload,
transactionHash: result.txHash,
partyId: session.partyId,
};
}
}
Registering at Runtime
Register adapters at runtime using the client's registerAdapter method:
import { createPartyLayer } from '@partylayer/sdk';
const client = createPartyLayer({ network: 'mainnet', app: { name: 'My dApp' } });
// Register your custom adapter
client.registerAdapter(new MyWalletAdapter());
// Now it appears in listWallets()
const wallets = await client.listWallets();
WalletAdapter Interface
| Prop | Type | Default | Description |
|---|
| walletId | readonly WalletId | — | Unique wallet identifier. |
| name | readonly string | — | Human-readable wallet name. |
| getCapabilities() | () => CapabilityKey[] | — | Return supported capabilities. |
| detectInstalled() | () => Promise<AdapterDetectResult> | — | Check if wallet is installed. Returns { installed, reason? }. |
| connect() | (ctx, opts?) => Promise<AdapterConnectResult> | — | Establish wallet connection. Returns { partyId, session, capabilities }. |
| disconnect() | (ctx, session) => Promise<void> | — | Close connection. Required. |
| restore?() | (ctx, persisted) => Promise<Session | null> | optional | Restore a persisted session. |
| signMessage?() | (ctx, session, params) => Promise<SignedMessage> | optional | Sign arbitrary message. |
| signTransaction?() | (ctx, session, params) => Promise<SignedTransaction> | optional | Sign transaction. |
| submitTransaction?() | (ctx, session, params) => Promise<TxReceipt> | optional | Sign and submit transaction. |
| ledgerApi?() | (ctx, session, params) => Promise<LedgerApiResult> | optional | Proxy a JSON Ledger API request. |