From 0efc81df5f5e34cbb1dbf683619440e756deac68 Mon Sep 17 00:00:00 2001 From: Gancho Radkov Date: Tue, 24 Mar 2026 13:14:40 +0200 Subject: [PATCH 1/2] docs: add Canton chain support specification Add per-chain specification for Canton Network covering WalletConnect integration with operator-defined networks, Ed25519 signing, and the full RPC method surface (prepareSignExecute, listAccounts, getPrimaryAccount, getActiveNetwork, status, ledgerApi, signMessage). Made-with: Cursor --- docs.json | 3 +- wallet-sdk/chain-support/canton.mdx | 585 ++++++++++++++++++++++++++ wallet-sdk/chain-support/overview.mdx | 1 + 3 files changed, 588 insertions(+), 1 deletion(-) create mode 100644 wallet-sdk/chain-support/canton.mdx diff --git a/docs.json b/docs.json index 8bef0fa..1194dde 100644 --- a/docs.json +++ b/docs.json @@ -60,7 +60,8 @@ "wallet-sdk/chain-support/stacks", "wallet-sdk/chain-support/ton", "wallet-sdk/chain-support/tron", - "wallet-sdk/chain-support/adi" + "wallet-sdk/chain-support/adi", + "wallet-sdk/chain-support/canton" ] }, { diff --git a/wallet-sdk/chain-support/canton.mdx b/wallet-sdk/chain-support/canton.mdx new file mode 100644 index 0000000..80644c4 --- /dev/null +++ b/wallet-sdk/chain-support/canton.mdx @@ -0,0 +1,585 @@ +--- +title: Canton +description: "Overview of the Canton JSON-RPC methods supported by Wallet SDK." +--- + +These are the methods that wallets should implement to handle Canton transactions and messages via WalletConnect. + +## Network / Chain Information + +- **Namespace:** `canton` +- **CAIP-2:** `canton:` (e.g. `canton:devnet`, `canton:production`) +- **CAIP-10 Account:** `canton::` (e.g. `canton:devnet:operator%3A%3A1220abc...`) + +Unlike most chains, Canton does not have fixed mainnet/testnet identifiers. Network IDs are **operator-defined** — each wallet is configured with one or more networks, and the `network-id` used in CAIP-2 identifiers comes from that configuration. + +dApps should **not** hardcode specific chain IDs in the session proposal. Instead, request the `canton` namespace without specifying `chains`, and work with whatever network the wallet provides in the approved session. The network ID and party ID are available directly from the session's `canton.accounts` array as CAIP-10 strings (e.g. `canton:production:operator%3A%3A1220abc...`). For full network details, use [`getActiveNetwork`](#getactivenetwork). + +## Registered Methods & Events + +```typescript theme={null} +const CANTON_WC_METHODS = [ + 'prepareSignExecute', + 'listAccounts', + 'getPrimaryAccount', + 'getActiveNetwork', + 'status', + 'ledgerApi', + 'signMessage', +] + +const CANTON_WC_EVENTS = ['accountsChanged', 'statusChanged', 'chainChanged'] +``` + +### Auto-Approve vs Manual-Approve + +Read-only methods are auto-approved by the wallet. Methods that mutate the ledger or perform sensitive operations require explicit user approval. + +| Method | Approval | +| -------------------- | ------------ | +| `listAccounts` | Auto-approve | +| `getPrimaryAccount` | Auto-approve | +| `getActiveNetwork` | Auto-approve | +| `status` | Auto-approve | +| `ledgerApi` | Auto-approve | +| `prepareSignExecute` | Manual | +| `signMessage` | Manual | + +## Method Name Mapping (dApp SDK) + +The dApp SDK's `WalletConnectTransport` maps SDK method names before sending over WC: + +| SDK method | WC method (on the wire) | +| ------------------------ | ------------------------ | +| `prepareExecute` | `prepareSignExecute` | +| `prepareExecuteAndWait` | `prepareSignExecute` | + +All other methods (`listAccounts`, `status`, `ledgerApi`, etc.) are sent as-is. Both SDK methods resolve with the same response — over WalletConnect, every submission blocks until the transaction completes. + +## RPC Methods + +### prepareSignExecute + +Prepare, sign, and execute a Canton ledger transaction. This is the primary method for submitting commands that mutate ledger state. The wallet performs the full prepare → sign → execute cycle and responds when the transaction is complete. + +#### Request + +```typescript theme={null} +interface PrepareSignExecuteRequest { + method: 'prepareSignExecute'; + params: PrepareParams; +} + +interface PrepareParams { + commandId?: string; // auto-generated (UUIDv4) if omitted + commands?: { [k: string]: unknown }; + actAs?: string[]; // defaults to [primaryWallet.partyId] if omitted + readAs?: string[]; // defaults to [] if omitted + disclosedContracts?: Array<{ + templateId?: string; + contractId?: string; + createdEventBlob: string; + synchronizerId?: string; + }>; + packageIdSelectionPreference?: string[]; +} +``` + +#### Example Request + +```json theme={null} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "prepareSignExecute", + "params": { + "commands": { + "0": { + "ExerciseCommand": { + "templateId": "#:Module:Template", + "contractId": "00abcdef...", + "choice": "Transfer", + "choiceArgument": { + "receiver": "bob::1220..." + } + } + } + }, + "commandId": "d290f1ee-6c54-4b01-90e6-d701748f0851", + "actAs": ["operator::1220abc..."], + "readAs": [], + "disclosedContracts": [ + { + "templateId": "#:Module:Template", + "contractId": "00abcdef...", + "createdEventBlob": "", + "synchronizerId": "wallet::1220e7b..." + } + ], + "packageIdSelectionPreference": [""] + } + } +} +``` + +#### Signing Providers + +Wallets support multiple signing backends. The signing provider determines the Ledger API flow used: + +| Provider | Flow | +| ---------------- | -------------------------------------------------------------------- | +| `participant` | Single call to `POST /v2/commands/submit-and-wait` (participant signs internally) | +| `wallet-kernel` | `POST /v2/interactive-submission/prepare` → local Ed25519 sign → `POST /v2/interactive-submission/execute` | +| `blockdaemon` | `POST /v2/interactive-submission/prepare` → sign via Blockdaemon API → `POST /v2/interactive-submission/execute` | + +#### Success Response + +```json theme={null} +{ + "id": 1234, + "jsonrpc": "2.0", + "result": { + "status": "executed", + "commandId": "d290f1ee-...", + "payload": { + "updateId": "tx-update-id", + "completionOffset": 42 + } + } +} +``` + +#### Error Response + +```json theme={null} +{ + "id": 1234, + "jsonrpc": "2.0", + "error": { + "code": 5001, + "message": "Transaction execution failed: INVALID_ARGUMENT: ..." + } +} +``` + +#### User Rejected Response + +```json theme={null} +{ + "id": 1234, + "jsonrpc": "2.0", + "error": { + "code": 5000, + "message": "User rejected" + } +} +``` + +--- + +### listAccounts + +Retrieve all configured wallet accounts. + +#### Request + +```json theme={null} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "listAccounts", + "params": {} + } +} +``` + +#### Response + +```json theme={null} +{ + "id": 1235, + "jsonrpc": "2.0", + "result": [ + { + "primary": true, + "partyId": "operator::1220abc...", + "status": "allocated", + "hint": "operator", + "publicKey": "", + "namespace": "1220abc...", + "networkId": "canton:production", + "signingProviderId": "participant", + "disabled": false + } + ] +} +``` + +#### Wallet Type + +```typescript theme={null} +interface Wallet { + primary: boolean; + partyId: string; + status: 'initialized' | 'allocated' | 'removed'; + hint: string; + publicKey: string; + namespace: string; + networkId: string; + signingProviderId: string; + externalTxId?: string; + topologyTransactions?: string; + disabled?: boolean; + reason?: string; +} +``` + +--- + +### getPrimaryAccount + +Retrieve the primary wallet account (where `primary === true`). + +#### Request + +```json theme={null} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "getPrimaryAccount", + "params": {} + } +} +``` + +#### Response + +```json theme={null} +{ + "id": 1236, + "jsonrpc": "2.0", + "result": { + "primary": true, + "partyId": "operator::1220abc...", + "status": "allocated", + "hint": "operator", + "publicKey": "", + "namespace": "1220abc...", + "networkId": "canton:production", + "signingProviderId": "participant" + } +} +``` + +--- + +### getActiveNetwork + +Retrieve the currently active network configuration. + +#### Request + +```json theme={null} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "getActiveNetwork", + "params": {} + } +} +``` + +#### Response + +```json theme={null} +{ + "id": 1237, + "jsonrpc": "2.0", + "result": { + "networkId": "canton:production", + "ledgerApi": "http://127.0.0.1:5003" + } +} +``` + +--- + +### status + +Check the wallet's connectivity to the Canton ledger. + +#### Request + +```json theme={null} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "status", + "params": {} + } +} +``` + +#### Response (ledger reachable) + +```json theme={null} +{ + "id": 1238, + "jsonrpc": "2.0", + "result": { + "provider": { + "id": "remote-da", + "version": "3.4.0", + "providerType": "remote" + }, + "connection": { + "isConnected": true, + "isNetworkConnected": true + }, + "network": { + "networkId": "canton:production", + "ledgerApi": "http://127.0.0.1:5003" + } + } +} +``` + +#### Response (ledger unreachable) + +```json theme={null} +{ + "id": 1238, + "jsonrpc": "2.0", + "result": { + "provider": { + "id": "remote-da", + "version": "3.4.0", + "providerType": "remote" + }, + "connection": { + "isConnected": true, + "isNetworkConnected": false, + "reason": "Ledger unreachable" + } + } +} +``` + +--- + +### ledgerApi + +Proxy raw Canton Ledger API requests through the wallet. The wallet authenticates and forwards the request. + +#### Request + +```typescript theme={null} +interface LedgerApiRequest { + method: 'ledgerApi'; + params: LedgerApiParams; +} + +interface LedgerApiParams { + requestMethod: 'GET' | 'POST'; + resource: string; + body?: string | object; +} +``` + +#### Example Request + +```json theme={null} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "ledgerApi", + "params": { + "requestMethod": "POST", + "resource": "/v2/state/active-contracts", + "body": { + "filter": { + "filtersByParty": { + "operator::1220abc...": { + "cumulative": { + "templateFilters": [] + } + } + } + } + } + } + } +} +``` + +#### Response + +```json theme={null} +{ + "id": 1239, + "jsonrpc": "2.0", + "result": { + "response": { + "version": "3.4.0", + "features": {} + } + } +} +``` + + +The `response` field contains the raw Ledger API JSON response as-is. + + +--- + +### signMessage + +Sign an arbitrary message with the wallet's Ed25519 private key. + +#### Request + +```typescript theme={null} +interface SignMessageRequest { + method: 'signMessage'; + params: { + message: string; + }; +} +``` + +#### Example Request + +```json theme={null} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "signMessage", + "params": { + "message": "Please sign this message to verify your identity" + } + } +} +``` + +#### Success Response + +```json theme={null} +{ + "id": 1240, + "jsonrpc": "2.0", + "result": { + "signature": "", + "publicKey": "" + } +} +``` + +## Events + +### accountsChanged + +Emitted when wallet accounts are added, removed, or modified. + +```json theme={null} +{ + "name": "accountsChanged", + "data": [ + { + "primary": true, + "partyId": "operator::1220abc...", + "status": "allocated", + "hint": "operator", + "publicKey": "...", + "namespace": "1220abc...", + "networkId": "canton:production", + "signingProviderId": "participant" + } + ] +} +``` + +### statusChanged + +Emitted when the wallet's connectivity status changes. + +```json theme={null} +{ + "name": "statusChanged", + "data": { + "provider": { "id": "remote-da", "providerType": "remote" }, + "connection": { "isConnected": true, "isNetworkConnected": true }, + "network": { "networkId": "canton:production" } + } +} +``` + +### chainChanged + +Emitted when the wallet switches to a different network. + +```json theme={null} +{ + "name": "chainChanged", + "data": { + "chainId": "canton:production" + } +} +``` + +## Session Lifecycle + +### Pairing + +The dApp creates a pairing URI and delivers it to the wallet: + +```typescript +const { uri, approval } = await signClient.connect({ + optionalNamespaces: { + canton: { + methods: CANTON_WC_METHODS, + events: CANTON_WC_EVENTS, + }, + }, +}) +``` + +### Session Approval + +The wallet builds approved namespaces including the CAIP-10 account with the URL-encoded partyId: + +```json theme={null} +{ + "canton": { + "chains": ["canton:devnet"], + "accounts": ["canton:devnet:operator%3A%3A1220abc..."], + "methods": ["prepareSignExecute", "listAccounts", "getPrimaryAccount", "getActiveNetwork", "status", "ledgerApi", "signMessage"], + "events": ["accountsChanged", "statusChanged", "chainChanged"] + } +} +``` + +## Error Codes + +| Code | Meaning | +| ------ | ----------------------------------------- | +| `5000` | User rejected | +| `5001` | Execution / handler error | +| `5100` | Canton namespace not found in proposal | +| `6000` | Wallet disconnected | + +## Notes & Considerations + +- All requests and responses comply with JSON-RPC structure (`id`, `jsonrpc`, etc.). +- Canton uses Ed25519 signing for transaction authentication. +- The `ledgerApi` method acts as a transparent proxy — the wallet handles authentication with the Canton Ledger API. Only `GET` and `POST` are supported; other HTTP methods will return a `5001` error. +- Party IDs in CAIP-10 accounts are URL-encoded (e.g. `operator::1220abc...` becomes `operator%3A%3A1220abc...`). +- The `prepareSignExecute` method always performs the full prepare → sign → execute cycle synchronously, responding only when the transaction is complete. +- The WC session `chainId` (e.g. `canton:devnet`) may differ from the `networkId` in wallet/network records (e.g. `canton:production`). The `chainId` identifies the chain at pairing time, while `networkId` reflects the wallet's internal network configuration. diff --git a/wallet-sdk/chain-support/overview.mdx b/wallet-sdk/chain-support/overview.mdx index ef05d75..9a46835 100644 --- a/wallet-sdk/chain-support/overview.mdx +++ b/wallet-sdk/chain-support/overview.mdx @@ -14,6 +14,7 @@ The Wallet SDK is built to be **chain-agnostic** — it supports integrations ac - [TON](/wallet-sdk/chain-support/ton) - [Tron](/wallet-sdk/chain-support/tron) - [ADI Chain](/wallet-sdk/chain-support/adi) +- [Canton](/wallet-sdk/chain-support/canton) ## Adding New Chain Support From cc9cd1c6d8097d73e5cef7809de0d48b8e478494 Mon Sep 17 00:00:00 2001 From: Gancho Radkov Date: Tue, 24 Mar 2026 14:10:17 +0200 Subject: [PATCH 2/2] docs: prefix prepareSignExecute with canton_ namespace Made-with: Cursor --- wallet-sdk/chain-support/canton.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/wallet-sdk/chain-support/canton.mdx b/wallet-sdk/chain-support/canton.mdx index 80644c4..1f607cd 100644 --- a/wallet-sdk/chain-support/canton.mdx +++ b/wallet-sdk/chain-support/canton.mdx @@ -19,7 +19,7 @@ dApps should **not** hardcode specific chain IDs in the session proposal. Instea ```typescript theme={null} const CANTON_WC_METHODS = [ - 'prepareSignExecute', + 'canton_prepareSignExecute', 'listAccounts', 'getPrimaryAccount', 'getActiveNetwork', @@ -42,7 +42,7 @@ Read-only methods are auto-approved by the wallet. Methods that mutate the ledge | `getActiveNetwork` | Auto-approve | | `status` | Auto-approve | | `ledgerApi` | Auto-approve | -| `prepareSignExecute` | Manual | +| `canton_prepareSignExecute` | Manual | | `signMessage` | Manual | ## Method Name Mapping (dApp SDK) @@ -51,14 +51,14 @@ The dApp SDK's `WalletConnectTransport` maps SDK method names before sending ove | SDK method | WC method (on the wire) | | ------------------------ | ------------------------ | -| `prepareExecute` | `prepareSignExecute` | -| `prepareExecuteAndWait` | `prepareSignExecute` | +| `prepareExecute` | `canton_prepareSignExecute` | +| `prepareExecuteAndWait` | `canton_prepareSignExecute` | All other methods (`listAccounts`, `status`, `ledgerApi`, etc.) are sent as-is. Both SDK methods resolve with the same response — over WalletConnect, every submission blocks until the transaction completes. ## RPC Methods -### prepareSignExecute +### canton_prepareSignExecute Prepare, sign, and execute a Canton ledger transaction. This is the primary method for submitting commands that mutate ledger state. The wallet performs the full prepare → sign → execute cycle and responds when the transaction is complete. @@ -66,7 +66,7 @@ Prepare, sign, and execute a Canton ledger transaction. This is the primary meth ```typescript theme={null} interface PrepareSignExecuteRequest { - method: 'prepareSignExecute'; + method: 'canton_prepareSignExecute'; params: PrepareParams; } @@ -92,7 +92,7 @@ interface PrepareParams { "topic": "", "chainId": "canton:devnet", "request": { - "method": "prepareSignExecute", + "method": "canton_prepareSignExecute", "params": { "commands": { "0": { @@ -560,7 +560,7 @@ The wallet builds approved namespaces including the CAIP-10 account with the URL "canton": { "chains": ["canton:devnet"], "accounts": ["canton:devnet:operator%3A%3A1220abc..."], - "methods": ["prepareSignExecute", "listAccounts", "getPrimaryAccount", "getActiveNetwork", "status", "ledgerApi", "signMessage"], + "methods": ["canton_prepareSignExecute", "listAccounts", "getPrimaryAccount", "getActiveNetwork", "status", "ledgerApi", "signMessage"], "events": ["accountsChanged", "statusChanged", "chainChanged"] } } @@ -581,5 +581,5 @@ The wallet builds approved namespaces including the CAIP-10 account with the URL - Canton uses Ed25519 signing for transaction authentication. - The `ledgerApi` method acts as a transparent proxy — the wallet handles authentication with the Canton Ledger API. Only `GET` and `POST` are supported; other HTTP methods will return a `5001` error. - Party IDs in CAIP-10 accounts are URL-encoded (e.g. `operator::1220abc...` becomes `operator%3A%3A1220abc...`). -- The `prepareSignExecute` method always performs the full prepare → sign → execute cycle synchronously, responding only when the transaction is complete. +- The `canton_prepareSignExecute` method always performs the full prepare → sign → execute cycle synchronously, responding only when the transaction is complete. - The WC session `chainId` (e.g. `canton:devnet`) may differ from the `networkId` in wallet/network records (e.g. `canton:production`). The `chainId` identifies the chain at pairing time, while `networkId` reflects the wallet's internal network configuration.