BitGo Signing Driver
A driver for signing and retrieving Canton transactions using the BitGo TSS MPC custodial wallet API, implementing the SigningDriverInterface from @canton-network/core-signing-lib.
How it works
BitGo signs Canton transactions asynchronously via its MPC TSS protocol:
- Key creation — a BitGo custodial wallet is created per Canton party (
POST /api/v2/{coin}/wallet). The returnedKeycarries the BitGo wallet ID asid(stable routing identifier) and the Ed25519 public key derived from the wallet keychain atm/0aspublicKey(used for Canton party allocation and fingerprint generation). - Sign request — the Canton transaction is submitted as a message signing request (
POST /api/v2/wallet/{walletId}/msgrequests) and returns atxRequestIdimmediately with statuspending. - Polling — the wallet gateway polls
getTransaction(txRequestId)untilstatus === 'signed'. The Ed25519 signature and Canton signer fingerprint are extracted from the signed txRequest response.
Credentials
- Sign in to BitGo (or BitGo Test for testnet).
- Create a Long-Lived Access Token in User Settings → Developer Options → Access Tokens. Select the scopes your use case requires (at minimum: wallet management and transaction signing).
- Note your Enterprise ID from Settings → Enterprise. This is required for wallet creation.
Wallet Gateway configuration
Configure non-secret values in the Wallet Gateway config. Listing bitgo under
signingProviders opts the provider in:
| Gateway config field | Required | Description |
|---|---|---|
signingProviders.bitgo.baseUrl | No | API base URL. Defaults to https://app.bitgo.com (prod). Use https://app.bitgo-test.com for testnet. |
signingProviders.bitgo.enterpriseId | No | BitGo enterprise ID. Required for createKey. Enables restart-safe getTransaction fallback via the enterprise txrequests endpoint. |
signingProviders.bitgo.coin | No | Canton coin identifier. Auto-detected: tcanton for bitgo-test.com URLs, canton for everything else (prod, proxies, custom URLs). |
signingProviders.bitgo.accessTokenEnv | No | Name of the environment variable that holds the access token. Defaults to BITGO_ACCESS_TOKEN. The token value stays in the environment. |
If signingProviders is omitted, the Wallet Gateway uses legacy discovery and
reads BITGO_ACCESS_TOKEN, BITGO_API_URL, BITGO_ENTERPRISE_ID, and BITGO_COIN from the
environment. Non-secret variables are ignored once signingProviders is present.
Transaction state lifecycle
BitGo signing is asynchronous — the MPC TSS protocol requires multiple internal rounds before a signature is produced. The driver maps BitGo states to Canton SigningStatus:
| BitGo state | Canton status | Notes |
|---|---|---|
initialized, pendingApproval, pendingDelivery, pendingUserSignature, pendingUserCommitment, pendingUserRShare, pendingUserGShare, readyToSend | pending | MPC rounds in progress |
messages[0].state === 'signed' | signed | Message-level state takes precedence — signing is complete even if txRequest is still pendingDelivery |
delivered, signed | signed | |
canceled, rejected | rejected | |
failed | failed |
Restart resilience
The driver maintains in-memory caches for fast lookups (txRequestId → walletId, publicKey ↔ walletId). If the process restarts, these caches are empty. Transactions are recovered via the BitGo enterprise txrequests endpoint (requires BITGO_ENTERPRISE_ID). Public keys are resolved on demand per wallet via the BitGo wallet and keychain endpoints when not in cache.
Development
pnpm build # compile
pnpm nx test @canton-network/core-signing-bitgo # run tests
pnpm nx test @canton-network/core-signing-bitgo -c coverage # with coverage report