PartyLayerDocs
Try Demo

CIP-0103 Provider

CIP-0103 is the Canton dApp Standard — the specification for how wallets and dApps communicate on the Canton Network. PartyLayer fully implements CIP-0103 with 10 methods, 4 events, and a typed error model.

Two Integration Paths

PartyLayer supports two ways to integrate:

  • Adapter SDK (recommended) — Use PartyLayerKit and React hooks. The SDK abstracts CIP-0103 behind a higher-level API.
  • Native CIP-0103 Provider — Work directly with the CIP-0103 provider interface. Useful for non-React apps or when you need raw CIP-0103 compliance.

Provider API

The CIP-0103 provider uses a JSON-RPC–style request() method:

typescript
interface CIP0103Provider {
  request<T>(args: { method: string; params?: unknown }): Promise<T>;
  on<T>(event: string, listener: (data: T) => void): CIP0103Provider;
  emit<T>(event: string, ...args: T[]): boolean;
  removeListener<T>(event: string, listener: (data: T) => void): CIP0103Provider;
}

10 Mandatory Methods

connect

Establish a connection to the wallet.

typescript
const result = await provider.request<CIP0103ConnectResult>({
  method: 'connect',
});
// → { isConnected: true, isNetworkConnected: true }

disconnect

typescript
await provider.request({ method: 'disconnect' });

isConnected

typescript
const status = await provider.request<CIP0103ConnectResult>({
  method: 'isConnected',
});
// → { isConnected: true/false }

status

Get full provider status including connection, provider info, network, and session.

typescript
const status = await provider.request<CIP0103StatusEvent>({
  method: 'status',
});
// → { connection: {...}, provider: { id, version, providerType }, network?: {...}, session?: {...} }

getActiveNetwork

Get the active network in CAIP-2 format.

typescript
const network = await provider.request<CIP0103Network>({
  method: 'getActiveNetwork',
});
// → { networkId: 'canton:da-mainnet', ledgerApi: '...', accessToken: '...' }

listAccounts

typescript
const accounts = await provider.request<CIP0103Account[]>({
  method: 'listAccounts',
});
// → [{ primary: true, partyId: '...', status: 'allocated', ... }]

getPrimaryAccount

typescript
const account = await provider.request<CIP0103Account>({
  method: 'getPrimaryAccount',
});
// → { primary: true, partyId: '...', publicKey: '...', status: 'allocated' }

signMessage

typescript
const result = await provider.request<{ signature: string }>({
  method: 'signMessage',
  params: { message: 'Hello Canton!' },
});
// → { signature: '0x...' }

prepareExecute

Prepare and submit a Daml command for execution.

typescript
await provider.request({
  method: 'prepareExecute',
  params: {
    commands: [{ templateId: '...', choiceId: '...', argument: {...} }],
  },
});

ledgerApi

Proxy requests to the Canton Ledger API through the wallet.

typescript
const result = await provider.request<CIP0103LedgerApiResponse>({
  method: 'ledgerApi',
  params: {
    requestMethod: 'GET',
    resource: '/v1/parties',
  },
});

4 Events

statusChanged

Emitted when the provider status changes.

typescript
provider.on('statusChanged', (status: CIP0103StatusEvent) => {
  console.log('Connection:', status.connection.isConnected);
  console.log('Provider:', status.provider.id);
});

accountsChanged

typescript
provider.on('accountsChanged', (accounts: CIP0103Account[]) => {
  console.log('Accounts:', accounts.map(a => a.partyId));
});

txChanged

Transaction lifecycle events (pending → signed → executed or failed).

typescript
provider.on('txChanged', (event: CIP0103TxChangedEvent) => {
  switch (event.status) {
    case 'pending':
      console.log('TX pending:', event.commandId);
      break;
    case 'signed':
      console.log('TX signed:', event.payload.signature);
      break;
    case 'executed':
      console.log('TX executed:', event.payload.updateId);
      break;
    case 'failed':
      console.log('TX failed:', event.commandId);
      break;
  }
});

connected

Emitted when an async connect completes.

typescript
provider.on('connected', (result: CIP0103ConnectResult) => {
  console.log('Async connect completed:', result.isConnected);
});

Provider Bridge

Wrap your PartyLayerClient as a CIP-0103 provider using asProvider():

typescript
import { createPartyLayer } from '@partylayer/sdk';

const client = createPartyLayer({
  network: 'mainnet',
  app: { name: 'My dApp' },
});

// Bridge to CIP-0103
const provider = client.asProvider();

// Now use standard CIP-0103 methods
const result = await provider.request({ method: 'connect' });
const accounts = await provider.request({ method: 'listAccounts' });
💡 Tip
Use the bridge when you need to expose a CIP-0103 compliant interface to third-party libraries or tools that expect a raw CIP-0103 provider.

Provider Discovery

typescript
import {
  discoverInjectedProviders,
  waitForProvider,
  isCIP0103Provider,
} from '@partylayer/provider';

// Scan window.canton.* for all injected providers
const providers = discoverInjectedProviders();
// → [{ id: 'console', provider: CIP0103Provider }, ...]

// Wait for a specific provider to appear (returns null if not found)
const discovered = await waitForProvider('nightly', 5000);
if (discovered) {
  console.log('Found:', discovered.id, discovered.provider);
}

// Duck-type check
if (isCIP0103Provider(window.canton?.console)) {
  console.log('Console wallet is CIP-0103 compliant');
}

Network Utilities (CAIP-2)

Convert between PartyLayer network IDs and CAIP-2 format:

typescript
import { toCAIP2Network, fromCAIP2Network, isValidCAIP2 } from '@partylayer/provider';

toCAIP2Network('mainnet');           // → { networkId: 'canton:da-mainnet' }
fromCAIP2Network('canton:da-testnet'); // → 'testnet'
isValidCAIP2('canton:da-mainnet');  // → true
isValidCAIP2('not-a-network');      // → false (no colon separator)

Error Model

CIP-0103 uses ProviderRpcError with EIP-1193 and EIP-1474 numeric error codes:

EIP-1193 Codes

typescript
// 4001 — User Rejected
// 4100 — Unauthorized
// 4200 — Unsupported Method
// 4900 — Disconnected
// 4901 — Chain Disconnected

EIP-1474 Codes

typescript
// -32700 — Parse Error
// -32600 — Invalid Request
// -32601 — Method Not Found
// -32602 — Invalid Params
// -32603 — Internal Error
// -32000 — Invalid Input
// -32003 — Transaction Rejected
// -32005 — Limit Exceeded

Error Mapping

Convert between PartyLayer errors and CIP-0103 RPC errors:

typescript
import { toProviderRpcError, toPartyLayerError } from '@partylayer/provider';

// PartyLayer → CIP-0103
const rpcError = toProviderRpcError(new UserRejectedError('connect'));
// → ProviderRpcError { code: 4001, message: 'User Rejected' }

// CIP-0103 → PartyLayer
const plError = toPartyLayerError(rpcError);
// → UserRejectedError { code: 'USER_REJECTED' }
PreviousWallets & AdaptersNextError Handling