# Canton reference

This page lists the Canton-specific API fields, identifiers, and errors in Ripple Custody. For task procedures, see [Set up Canton accounts with the API](/products/custody/accounts-and-assets/blockchains/canton/set-up-accounts-api) and [Send and receive Canton assets with the API](/products/custody/accounts-and-assets/blockchains/canton/send-and-receive-api).

## Intents and operations

| Intent | Operation | Purpose |
|  --- | --- | --- |
| `v0_CreateLedger` |  | Registers a Canton network as a ledger. |
| `v0_ValidateTickers` |  | Registers Canton Coin or a CIP-56 token as a ticker. |
| `v0_CreateAccount` |  | Creates an account and derives its party key and party ID. |
| `v0_CreateTransactionOrder` | `CreateParty` | Allocates the account's party on the validator. |
| `v0_CreateTransactionOrder` | `CreateNativeTransferPreapproval` | Creates the account's Canton Coin pre-approval. |
| `v0_CreateTransactionOrder` | `CreateTokenTransferPreapproval` | Creates the account's pre-approval for one CIP-56 token. |
| `v0_CreateTransactionOrder` | `NativeTransfer` | Sends Canton Coin. |
| `v0_CreateTransactionOrder` | `TokenTransfer` | Sends a CIP-56 token. |
| `v0_CreateTransactionOrder` | `Withdraw` | Cancels an outgoing two-step transfer offer. |
| `v0_CreateTransactionOrder` | `Accept` | Accepts an incoming two-step transfer offer. |
| `v0_CreateTransactionOrder` | `Reject` | Rejects an incoming two-step transfer offer. |


## Ledger parameters

`v0_CreateLedger` takes the following `parameters` for a Canton ledger. You must provide every field.

| Field | Type | Description |
|  --- | --- | --- |
| `type` | string | `Canton` |
| `synchronizerId` | string, 5–255 characters | The synchronizer ID of the network. The vault checks every prepared transaction against it. |
| `operatorId` | string, 5–255 characters | Your validator's own primary party ID. |
| `dsoPartyId` | string, 5–255 characters | The Decentralized Synchronizer Operator party ID. It's the instrument administrator for Canton Coin. |
| `daRegistryOperator` | string, 5–255 characters | The DA Registry operator party ID. |


`v0_CreateLedger` also requires `id`, `alias`, and `customProperties`.

## Network identifiers

| Network | `dsoPartyId` |
|  --- | --- |
| MainNet | `DSO::1220b1431ef217342db44d516bb9befde802be7d8899637d290895fa58880f19accc` |
| TestNet | `DSO::1220f22a8b8f2d813c25b9a684dc4dd52b532a0174d8e73a13cdf2baabfff7518337` |
| DevNet | `DSO::1220be58c29e65de40bf273be1dc2b266d43a9a002ea5b18955aeef7aac881bb471a` |


The `daRegistryOperator` is the same on all three networks:

```text
DigitalAsset-UtilityOperator::12202679f2bbe57d8cba9ef3cee847ac8239df0877105ab1f01a77d47477fdce1204
```

Get the `synchronizerId` and `operatorId` from your validator operator. DevNet resets regularly, so check the DevNet values after each reset.

## Ticker ledger details

In `v0_ValidateTickers`, a Canton ticker's `ledgerDetails` has `type` set to `Canton` and a `properties` object of one of the following types.

| `properties.type` | Asset | Other fields |
|  --- | --- | --- |
| `Native` | Canton Coin | None. |
| `Instrument` | A CIP-56 token | `instrumentAdmin` (string, 5–255 characters): the party ID of the token's administrator.`instrumentId` (string, 1–255 characters): the token's instrument ID. |


Set `decimals` to `10` for every Canton ticker.

## Transaction order parameters

For a Canton transaction order, `parameters` has the following fields.

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `type` | string | Yes | `Canton` |
| `operation` | object | Yes | One of the operations in [Operation fields](#operation-fields). |


Canton orders don't take a `feeStrategy`, and they don't support `maximumFee`. Ripple Custody rejects a Canton order that sets `maximumFee`.

### Operation fields

| Operation | Field | Type | Required | Description |
|  --- | --- | --- | --- | --- |
| `CreateParty` | `type` | string | Yes | `CreateParty` |
| `CreateNativeTransferPreapproval` | `type` | string | Yes | `CreateNativeTransferPreapproval` |
| `CreateTokenTransferPreapproval` | `type` | string | Yes | `CreateTokenTransferPreapproval` |
|  | `tickerId` | UUID | Yes | The ticker ID of the CIP-56 token. |
| `NativeTransfer` | `type` | string | Yes | `NativeTransfer` |
|  | `destination` | object | Yes | The receiver: an `Address` destination with the receiver's party ID, an `Endpoint` destination, or an `Account` destination. |
|  | `amount` | string | Yes | The amount in the smallest unit, as an integer string. The amount must be greater than zero. |
|  | `executeBefore` | date-time | No | The acceptance deadline for a two-step transfer offer. Defaults to 30 days after submission. |
|  | `memo` | string, 1–255 characters | No | An on-ledger reference, visible to the sender and receiver. |
| `TokenTransfer` | `type` | string | Yes | `TokenTransfer` |
|  | `destination` | object | Yes | Same as `NativeTransfer`. |
|  | `amount` | string | Yes | Same as `NativeTransfer`. |
|  | `tickerId` | UUID | Yes | The ticker ID of the CIP-56 token. |
|  | `executeBefore` | date-time | No | Same as `NativeTransfer`. |
|  | `memo` | string, 1–255 characters | No | Same as `NativeTransfer`. |
| `Withdraw` | `type` | string | Yes | `Withdraw` |
|  | `contractId` | string, 1–255 characters | Yes | The contract ID of your outgoing transfer offer. |
| `Accept` | `type` | string | Yes | `Accept` |
|  | `contractId` | string, 1–255 characters | Yes | The contract ID of the incoming transfer offer. |
| `Reject` | `type` | string | Yes | `Reject` |
|  | `contractId` | string, 1–255 characters | Yes | The contract ID of the incoming transfer offer. |


## Transaction estimate

A dry run of a Canton order returns an estimate with `type` set to `Canton` and a `fee` field. The fee is always `0`, because your validator operator party pays network fees as traffic.

## Transaction ledger data

For a Canton transaction, `ledgerTransactionData.ledgerData` has `type` set to `Canton` and can contain the following fields. Ripple Custody fills them for two-step transfer offers.

| Field | Type | Description |
|  --- | --- | --- |
| `contractId` | string | The contract ID of the transfer offer. Use it in `Accept`, `Reject`, or `Withdraw`. |
| `sender` | string | The sender's party ID. |
| `receiver` | string | The receiver's party ID. |
| `amount` | string | The amount in the smallest unit. |
| `instrumentId` | string | The instrument ID of the asset. |
| `executeBefore` | date-time | The deadline for the receiver to accept the offer. |


The `ledgerTransactionId` of a Canton transaction is the Canton update ID. Ripple Custody sets it after the network sequences the transaction.

## Account keys

A Canton account's party key is an Ed25519 key that Ripple Custody derives in your vault. The vault derives it from the account's `ED25519_CUSTODY_1` key on a reserved derivation path, which only the vault's Canton support can use. You can't request a key on this path.

In the `keys` array of the account's provider details, the party key has the key ID `ED25519_CANTON_PARTY_1` and the type `VaultReserved`:

```json
{
  "id": "ED25519_CANTON_PARTY_1",
  "publicKey": {
    "type": "PublicKey",
    "value": "..."
  },
  "type": "VaultReserved"
}
```

A `VaultReserved` key has a public key only, never an extended public key. Accounts without a Canton ledger don't have `VaultReserved` keys. If your integration reads the `keys` array, make sure it accepts this type before you create Canton accounts.

The party key appears in the `keys` array when you first add your Canton ledger to the account, with a `v0_CreateAccount` intent that includes the Canton ledger ID in `ledgerIds`, or with a `v0_AddAccountLedgers` intent.

The party key doesn't need a separate backup. The vault re-derives it from the account's `ED25519_CUSTODY_1` key whenever it needs it, so backing up the vault covers it. After a restore, the key reappears in the `keys` array when the notary replays the request that first added the Canton ledger to the account.

## Party ID format

```text
c::<fingerprint>
```

The fingerprint identifies the account's Ed25519 public key, so you can reproduce the party ID from the public key alone. For more information, see [Parties and party IDs](/products/custody/accounts-and-assets/blockchains/canton/concepts#parties-and-party-ids).

## Limits

| Limit | Value |
|  --- | --- |
| Decimal places for Canton assets | 10 |
| Maximum Holdings spent by one transfer | 100, as an upper bound. The network enforces it for Canton Coin, and Ripple Custody applies it to other CIP-56 tokens. The effective limit can be lower. |
| Time from preparing a transaction to executing it | 24 hours |
| Default deadline for a two-step transfer offer | 30 days |
| Minimum amount settled through a pre-approval | None |
| Maximum `memo` length | 255 characters |


## Errors and troubleshooting

| Symptom | Cause | Resolution |
|  --- | --- | --- |
| A transfer fails with an invalid destination error. | The receiver party doesn't exist on the network. This happens when the receiving Ripple Custody account hasn't completed `CreateParty`. | Check the party ID. If the receiver is your own account, complete [Step 2: Create the party](/products/custody/accounts-and-assets/blockchains/canton/set-up-accounts-api#step-2-create-the-party). |
| An incoming transfer appears as a pending offer instead of a settled Holding. | The receiving account has no pre-approval for the asset. | Accept or reject the offer. To avoid offers in the future, create the missing pre-approval. |
| A transfer fails even though the balance covers the amount. | The transfer needs more Holdings than one transfer can spend, or another transfer in progress has reserved the account's Holdings. | Merge or split Holdings with a self-transfer. For more information, see [Manage Holdings](/products/custody/accounts-and-assets/blockchains/canton/send-and-receive-api#manage-holdings). |
| A transaction fails after a long signing delay. | More than 24 hours passed between preparing the transaction and executing it. The network rejects the prepared transaction. | Submit the order again, so that it's prepared and signed again. For cold vaults, see [Set up cold vault accounts](/products/custody/accounts-and-assets/blockchains/canton/set-up-accounts-api#set-up-cold-vault-accounts). |
| A transaction stays in the `Broadcasting` status without a ledger transaction ID. | Ripple Custody hasn't observed the committed update yet. | Wait for the indexer to observe the update. If the status doesn't change, check the validator's health and connectivity. |
| The vault refuses to sign. | The prepared transaction doesn't match the approved intent, or its synchronizer ID doesn't match the ledger's `synchronizerId`. | Check the ledger parameters and the intent. Contact your Ripple liaison if the problem continues. |
| Ripple Custody can't connect to the validator. | An endpoint uses plaintext `http`, a firewall blocks the indexer, the Ledger API is behind a forward proxy, or the credentials are missing or out of date. | Check the [validator prerequisites](/products/custody/accounts-and-assets/blockchains/canton/connect-your-validator#validator-prerequisites). |
| A balance looks out of date. | The indexed balance drifted from the validator's Holdings. | [Force an account balance update](/products/custody/accounts-and-assets/accounts/manage-accounts-api#force-an-account-balance-update). |


To confirm on-ledger state independently of Ripple Custody, look up the party in a Canton block explorer, such as [Cantonscan](https://cantonscan.com) for MainNet or [CCView](https://devnet.ccview.io) for DevNet.