> For the complete documentation index, see [llms.txt](https://docs.squidrouter.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.squidrouter.com/api-and-sdk-integration/chain-integration-guides/canton-integration.md).

# Canton Integration

Integrate cross-chain swaps from Canton to EVM chains using Squid. Canton support is powered by [**Squid Intents**](/api-and-sdk-integration/coral-intent-swaps.md) — Squid's intent-based execution protocol for fast, solver-driven cross-chain settlement.

> **Squid Intents must be enabled on your integrator ID.** Reach out to the Squid team to request Squid Intents access before integrating with Canton.

***

## Canton Parameters

| Parameter    | Value    |
| ------------ | -------- |
| **Chain ID** | `canton` |

### Supported Tokens

| Token                               | Address                                                                     |
| ----------------------------------- | --------------------------------------------------------------------------- |
| **Canton Coin (CC)** — native asset | `DSO::1220b1431ef217342db44d516bb9befde802be7d8899637d290895fa58880f19accc` |

Canon Coin (CC) is the native asset on Canton. Additional tokens are supported — query the [Supported Tokens endpoint](/chains-and-tokens/get-supported-tokens-and-chains.md) with `chainId=canton` to get the full list of available tokens.

> Token addresses on Canton follow the format `admin::fingerprint` for the native Canton Coin, or `instrumentId::admin::fingerprint` for CIP-056 tokens (e.g., `USDCx::decentralized-usdc-admin::12208...`).

***

## Key Concepts

### Transaction Type: `DEPOSIT_ADDRESS_DIRECT_TRANSFER`

Canton routes use the `DEPOSIT_ADDRESS_DIRECT_TRANSFER` transaction type. Unlike standard EVM routes, this type:

* **Does not require ERC-20 approval** — Canton is not EVM-compatible
* **Returns a deposit address** — the route response includes a `target` field containing the deposit address for funds
* **Uses the order hash as a memo** — the `data` field in the transaction request contains the order hash, which must be included as a transfer memo when sending funds

Canton transactions can be executed in two ways:

1. **Connected wallet (browser)**: Using a CIP-103-compatible Canton wallet, the transfer is built using the Splice token-standard `TransferFactory` and submitted via `prepareExecuteAndWait`
2. **Deposit address**: Send funds directly to the deposit address with the order hash as a memo from any Canton wallet

> **Why can't Canton swaps be fully automated from a script?**
>
> Unlike EVM chains where a private key can be imported to sign transactions programmatically, Canton wallets use the **CIP-103 protocol** — a browser-based wallet discovery and signing standard. Wallet keys are managed entirely within browser extensions (Send, C8, Nightly) and communicate via `window.postMessage`. There is no exportable private key or Node.js-compatible signing SDK for Canton.
>
> The API and SDK examples handle everything that can be done programmatically: requesting a route, displaying deposit details, and polling transaction status. The only manual step is sending funds to the deposit address from your Canton wallet with the order hash as the memo.
>
> For fully automated execution in a browser environment, use the [Squid Widget](/widget-integration/add-a-widget/widget.md), which connects to Canton wallets via CIP-103.

### Canton Address Format

Canton addresses use the format `name::fingerprint`, where `name` is an identifier and `fingerprint` is a `1220`-prefixed SHA-256 hash (68 hex characters total). Example: `DSO::1220b1431ef217342db44d516bb9befde802be7d8899637d290895fa58880f19accc`

### Token Pre-Approval (EVM → Canton)

Canton uses the **CIP-56 token standard**, which follows an **offer-and-acceptance model** for token transfers. Unlike EVM's push-only model (where tokens are sent directly to a recipient), Canton requires recipients to explicitly agree to receive a token before it can be credited to their wallet.

To streamline this, Canton supports **`TransferPreapproval`** — a smart contract that pre-authorizes incoming transfers for a specific token. Without a preapproval in place, incoming tokens arrive as a pending "transfer offer" that the recipient must manually accept in their wallet.

**How this affects integrations:**

* **Squid Widget**: The widget handles `TransferPreapproval` checks automatically. If a user's Canton wallet does not have a preapproval for the receiving token, the widget will prompt them to set one up before executing the swap.
* **API / SDK**: The `TransferPreapproval` is not exposed as an API endpoint. When building EVM → Canton swaps via the API or SDK, the route is a standard EVM transaction (`DEPOSIT_ADDRESS_CALLDATA`). The Canton-side token delivery is handled by Squid's backend. However, the user's Canton wallet must have a `TransferPreapproval` for the destination token, or they will need to manually accept the transfer offer in their wallet.

***

## API Integration

### Canton → EVM Swap

The following example demonstrates a full Canton-to-EVM swap using the Squid API directly. It sends Canton Coin (CC) to USDC on Base using the deposit-address pattern.

```typescript
// Canton to EVM Swap Using Squid API (Squid Intents)
import axios from "axios";
import * as dotenv from "dotenv";
dotenv.config();

// Load environment variables
const cantonAddress: string = process.env.CANTON_ADDRESS!;
const integratorId: string = process.env.INTEGRATOR_ID!;

// Define swap parameters
const fromChainId = "canton";
const fromToken =
  "DSO::1220b1431ef217342db44d516bb9befde802be7d8899637d290895fa58880f19accc"; // Canton Coin (CC)
const fromAmount = "10000000000"; // 1 CC (Canton Coin uses 10 decimals)

const toChainId = "8453"; // Base
const toToken = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // USDC on Base

// Get the optimal route for the swap
const getRoute = async (params: any) => {
  try {
    const result = await axios.post(
      "https://v2.api.squidrouter.com/v2/route",
      params,
      {
        headers: {
          "x-integrator-id": integratorId,
          "Content-Type": "application/json",
        },
      }
    );
    const requestId = result.headers["x-request-id"];
    return { data: result.data, requestId: requestId };
  } catch (error: any) {
    if (error.response) {
      console.error("API error:", error.response.data);
    }
    throw error;
  }
};

// Get the status of the transaction
const getStatus = async (params: any) => {
  try {
    const result = await axios.get("https://v2.api.squidrouter.com/v2/status", {
      params,
      headers: {
        "x-integrator-id": integratorId,
      },
    });
    return result.data;
  } catch (error: any) {
    if (error.response) {
      console.error("API error:", error.response.data);
    }
    throw error;
  }
};

// Poll transaction status until completion
const updateTransactionStatus = async (
  requestId: string,
  quoteId: string
) => {
  const getStatusParams: any = {
    requestId: requestId,
    fromChainId: fromChainId,
    toChainId: toChainId,
    quoteId: quoteId,
  };

  let status;
  const completedStatuses = ["success", "partial_success", "needs_gas", "not_found"];
  const maxRetries = 30;
  let retryCount = 0;

  do {
    try {
      status = await getStatus(getStatusParams);
      console.log(`Route status: ${status.squidTransactionStatus}`);
    } catch (error: any) {
      if (error.response && error.response.status === 404) {
        retryCount++;
        if (retryCount >= maxRetries) {
          console.error("Max retries reached. Transaction not found.");
          break;
        }
        await new Promise((resolve) => setTimeout(resolve, 5000));
        continue;
      } else {
        throw error;
      }
    }

    if (status && !completedStatuses.includes(status.squidTransactionStatus)) {
      await new Promise((resolve) => setTimeout(resolve, 5000));
    }
  } while (!(status && completedStatuses.includes(status.squidTransactionStatus)));

  console.log("Swap fully confirmed!");
};

// Main function
(async () => {
  console.log(`Canton address: ${cantonAddress}`);

  const params = {
    fromAddress: cantonAddress,
    fromChain: fromChainId,
    fromToken: fromToken,
    fromAmount: fromAmount,
    toChain: toChainId,
    toToken: toToken,
    toAddress: "0xYourEVMAddress", // Replace with your destination EVM wallet
    quoteOnly: false,
  };

  console.log("Parameters:", params);

  // Get the swap route
  const routeResult = await getRoute(params);
  const route = routeResult.data.route;
  const requestId = routeResult.requestId;

  // Extract quoteId for Squid Intents transactions
  const quoteId =
    route?.estimate?.actions?.[0]?.coralV2Order?.quoteId ||
    route?.estimate?.quoteId ||
    route?.quoteId ||
    routeResult.data?.quoteId;

  console.log("Calculated route. Target output:", route.estimate.toAmount);
  console.log("requestId:", requestId);
  console.log("quoteId:", quoteId);

  const transactionRequest = route.transactionRequest;

  // Canton routes return a deposit address and order hash
  // The order hash is used as a transfer memo when sending funds
  const depositAddress = transactionRequest.target;
  const orderHash = transactionRequest.data;

  console.log("\n=== Canton Deposit Details ===");
  console.log(`Deposit Address: ${depositAddress}`);
  console.log(`Order Hash (Memo): ${orderHash}`);
  console.log(`Amount: ${fromAmount} (in smallest unit)`);
  console.log("\nSend the specified amount to the deposit address above");
  console.log("using your Canton wallet. Include the order hash as the");
  console.log("transfer memo (reason field).");
  console.log("==============================\n");

  // Track transaction status
  console.log("Waiting for deposit and tracking swap status...");
  await updateTransactionStatus(requestId as string, quoteId);

  console.log(`Track on Squid Scanner: https://scan.squidrouter.com`);
})();
```

### API Code Examples

| Route        | Example                                                                                 |
| ------------ | --------------------------------------------------------------------------------------- |
| Canton → EVM | [cantonToEVMSwap](https://github.com/0xsquid/examples/tree/main/V2/api/cantonToEVMSwap) |

***

## SDK Integration

The Squid SDK provides the same Canton routing capabilities. After initializing the SDK, use `squid.getRoute()` with the same parameters, then execute the transaction natively on the Canton network.

> **Note:** Because Canton is not EVM-compatible, `squid.executeRoute()` cannot be used directly. Canton transactions must be executed natively — either through a connected Canton wallet or by sending funds to the deposit address.

```typescript
// Canton to EVM Swap Using Squid SDK (Squid Intents)
import { Squid } from "@0xsquid/sdk";
import * as dotenv from "dotenv";
dotenv.config();

// Load environment variables
const cantonAddress: string = process.env.CANTON_ADDRESS!;
const integratorId: string = process.env.INTEGRATOR_ID!;

// Define swap parameters
const fromChainId = "canton";
const fromToken =
  "DSO::1220b1431ef217342db44d516bb9befde802be7d8899637d290895fa58880f19accc"; // Canton Coin (CC)
const fromAmount = "10000000000"; // 1 CC (Canton Coin uses 10 decimals)

const toChainId = "8453"; // Base
const toToken = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // USDC on Base

// Initialize the Squid SDK
const getSDK = (): Squid => {
  const squid = new Squid({
    baseUrl: "https://v2.api.squidrouter.com",
    integratorId: integratorId,
  });
  return squid;
};

(async () => {
  const squid = getSDK();
  await squid.init();
  console.log("Initialized Squid SDK");
  console.log(`Canton address: ${cantonAddress}`);

  const params = {
    fromAddress: cantonAddress,
    fromChain: fromChainId,
    fromToken: fromToken,
    fromAmount: fromAmount,
    toChain: toChainId,
    toToken: toToken,
    toAddress: "0xYourEVMAddress", // Replace with your destination EVM wallet
    quoteOnly: false,
  };

  console.log("Parameters:", params);

  // Get the swap route
  const { route, requestId } = await squid.getRoute(params);

  // Extract quoteId for Squid Intents transactions
  const quoteId =
    (route as any).estimate?.actions?.[0]?.coralV2Order?.quoteId ||
    (route as any).estimate?.quoteId ||
    (route as any).quoteId;

  console.log("Calculated route. Target output:", route.estimate.toAmount);
  console.log("requestId:", requestId);
  console.log("quoteId:", quoteId);

  const transactionRequest = route.transactionRequest as any;

  // Canton routes return a deposit address and order hash
  const depositAddress = transactionRequest.target;
  const orderHash = transactionRequest.data;

  console.log("\n=== Canton Deposit Details ===");
  console.log(`Deposit Address: ${depositAddress}`);
  console.log(`Order Hash (Memo): ${orderHash}`);
  console.log(`Amount: ${fromAmount} (in smallest unit)`);
  console.log("\nSend the specified amount to the deposit address above");
  console.log("using your Canton wallet. Include the order hash as the");
  console.log("transfer memo (reason field).");
  console.log("==============================\n");

  // Track status using the SDK
  const getStatusParams: any = {
    requestId: requestId,
    integratorId: integratorId,
    fromChainId: fromChainId,
    toChainId: toChainId,
    quoteId: quoteId,
  };

  const completedStatuses = ["success", "partial_success", "needs_gas", "not_found"];
  const maxRetries = 30;
  let retryCount = 0;

  console.log("Waiting for deposit and tracking swap status...");

  let status;
  do {
    try {
      await new Promise((resolve) => setTimeout(resolve, 5000));
      status = await squid.getStatus(getStatusParams);
      console.log(`Route status: ${status.squidTransactionStatus}`);
    } catch (error: unknown) {
      if (error instanceof Error && (error as any).response?.status === 404) {
        retryCount++;
        if (retryCount >= maxRetries) {
          console.error("Max retries reached.");
          break;
        }
        continue;
      } else {
        throw error;
      }
    }
  } while (
    status &&
    !completedStatuses.includes(status.squidTransactionStatus as string)
  );

  console.log("Swap fully confirmed!");
  console.log(`Track on Squid Scanner: https://scan.squidrouter.com`);
})();
```

### SDK Code Examples

| Route        | Example                                                                                 |
| ------------ | --------------------------------------------------------------------------------------- |
| Canton → EVM | [cantonToEVMSwap](https://github.com/0xsquid/examples/tree/main/V2/sdk/cantonToEVMSwap) |

***

## Transaction Handling

### Deposit Address Flow

The Squid route response for Canton returns a `transactionRequest` with:

| Field    | Description                                        |
| -------- | -------------------------------------------------- |
| `target` | The deposit address — send funds here              |
| `data`   | The order hash — include this as the transfer memo |
| `type`   | `DEPOSIT_ADDRESS_DIRECT_TRANSFER`                  |

```typescript
const depositAddress = transactionRequest.target;
const orderHash = transactionRequest.data; // Use as transfer memo
```

### Connected Wallet Flow (Widget)

For browser-based integrations using the Squid Widget, Canton swaps are executed through the connected Canton wallet:

1. The route's order hash is used as the transfer memo
2. A Splice token-standard transfer is built via the `TransferFactory`
3. The transfer is submitted through the wallet's `prepareExecuteAndWait` method
4. The memo field key is `splice.lfdecentralizedtrust.org/reason`

> For widget integration, Canton wallet connection and transaction execution are handled automatically. See the [Widget Integration](/widget-integration/add-a-widget/widget.md) guide.

***

## Status Tracking

Canton routes are powered by Squid Intents, which **requires** the `quoteId` for status polling.

```typescript
const statusParams = {
  fromChainId: "canton",
  toChainId: "8453",
  transactionId: transactionId,
  quoteId: quoteId, // Required for Squid Intents
};

const result = await axios.get("https://v2.api.squidrouter.com/v2/status", {
  params: statusParams,
  headers: {
    "x-integrator-id": "<your-integrator-id>",
  },
});
```

### Extracting the quoteId

The `quoteId` may appear in several locations within the route response. Use a fallback chain to extract it reliably:

```typescript
const quoteId = route?.estimate?.actions?.[0]?.coralV2Order?.quoteId
             || route?.estimate?.quoteId
             || route?.quoteId;
```

> **Important:** A Squid Intents transaction will not be tracked correctly unless status is polled with the `quoteId`. See the [Integrating Squid Intents](/api-and-sdk-integration/coral-intent-swaps/integrating-squid-intents.md) guide for full status polling details.

***

## Important Notes

1. **Canton is not EVM-compatible** — Canton uses Daml smart contracts, not Solidity. Standard EVM tooling (`ethers.js`, MetaMask) does not work with Canton. Use a CIP-103-compatible Canton wallet for browser interactions.
2. **Deposit address with memo** — Canton routes return a deposit address and order hash. The order hash must be included as the transfer memo when sending funds. Omitting the memo will cause the swap to fail.
3. **Balances are private** — Canton balances can only be read through a connected wallet's ledger API. Address-only balance lookups are not supported.
4. **Canton wallet required** — Compatible browser wallets include Cantor8 (C8), Send Wallet, and Nightly. Wallets connect via the CIP-103 discovery protocol.
5. **`quoteId` is required for status polling** — All Squid Intents transactions require the `quoteId` parameter when calling the `/v2/status` endpoint.

For a full reference of all transaction types across all chains, see the [Transaction Types](/api-and-sdk-integration/key-concepts/transaction-types.md) page.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.squidrouter.com/api-and-sdk-integration/chain-integration-guides/canton-integration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
