PartyLayerDocs
Try Demo

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

WalletTransportDetectionAuto-registered
Console WalletPostMessageBrowser extensionYes
5N LoopQR Code / PopupMobile / Web appYes
Cantor8 (C8)Deep LinkBrowser extensionYes
NightlyInjectedwindow.nightly.cantonYes
BronOAuthEnterprise configNo
ℹ️ 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)

tsx
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.

typescript
// 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):

typescript
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:

typescript
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:

typescript
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

PropTypeDefaultDescription
walletIdreadonly WalletIdUnique wallet identifier.
namereadonly stringHuman-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>optionalRestore a persisted session.
signMessage?()(ctx, session, params) => Promise<SignedMessage>optionalSign arbitrary message.
signTransaction?()(ctx, session, params) => Promise<SignedTransaction>optionalSign transaction.
submitTransaction?()(ctx, session, params) => Promise<TxReceipt>optionalSign and submit transaction.
ledgerApi?()(ctx, session, params) => Promise<LedgerApiResult>optionalProxy a JSON Ledger API request.
PreviousVanilla JSNextCIP-0103 Provider