Wallets & Adapters
PartyLayer includes 5 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 | Transport | Detection | Auto-registered |
|---|
| Console Wallet | PostMessage | Browser extension | Yes |
| 5N Loop | QR Code / Popup | Mobile / Web app | Yes |
| Cantor8 (C8) | Deep Link | Browser extension | Yes |
| Nightly | Injected | window.nightly.canton | Yes |
| Bron | OAuth | Enterprise config | No |
ℹ️ Note
All wallets except
Bron are auto-registered when using
PartyLayerKit. Bron requires explicit configuration because it needs an OAuth client ID.
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({
clientId: 'your-oauth-client-id',
// Optional: custom OAuth redirect URI
// redirectUri: 'https://my-app.com/callback',
}),
]}
>
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. |