PartyLayerDocs
Try Demo

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

WalletNetworksOpt-inAdapter
Console Walletdevnet, testnet, mainnetNo@partylayer/adapter-console
5N Loopdevnet, testnet, mainnetNo@partylayer/adapter-loop
Cantor8devnet, testnet, mainnetNo@partylayer/adapter-cantor8
Brondevnet, testnet, mainnetYes@partylayer/adapter-bron
Nightlydevnet, testnet, mainnetNo@partylayer/adapter-nightly
SendmainnetNo@partylayer/adapter-send
Walleymainnet, testnet, devnetNo@k2flabs/walley-dapp-sdk
WalletConnectdevnet, testnet, mainnetYes@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.

WalletconnectsignMessagesignTransactionsubmitTransactionledgerApirestore
Consolesupportedsupportedsupportedsupportedfullsupported
5N Loopsupportedsupportednonesupportedlimitedsupported
Cantor8supportedsupportedsupportednonenonesupported
Nightlysupportedsupportednonesupportedfullsupported
Bronsupportedsupportedsupportednonefullsupported
Sendsupportedsupportednonesupportedfullsupported
Walleysupportedsupportednonesupportednonesupported
WalletConnectsupportedsupportednonesupportedfullsupported

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)

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({
      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.

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 JSNextSend (Beta)