# Prerequisites for Canton as Source Tutorials
Source: https://docs.chain.link/ccip/tutorials/canton/source/prerequisites
Last Updated: 2026-07-06

> For the complete documentation index, see [llms.txt](/llms.txt).

Before starting the **Canton as Source** (`canton2any`) tutorials, ensure you have the following:

> **NOTE: Testnet only**
>
> These tutorials use **Canton testnet** and **Ethereum Sepolia** only. The starter kit's `helperConfig.ts` and `canton-config.example.json` are pre-configured for the Canton CCIP testnet lane.

## Development Environment

- **Node.js v20 or higher**: You can use the [nvm package](http://nvm.sh/) to install and switch between Node.js versions. Once installed, verify the node version with:

  ```bash filename="Terminal"
  node -v
  ```

  Example output:

  ```text
  $ node -v
  v22.15.0
  ```

- **npm**: For installing and managing dependencies.

- **Git**: For cloning the repository.

## Starter Kit Repository

1. Clone the [CCIP Canton Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-canton):

   ```bash filename="Terminal"
   git clone https://github.com/smartcontractkit/ccip-starter-kit-canton.git
   ```

2. Navigate to the directory:

   ```bash filename="Terminal"
   cd ccip-starter-kit-canton
   ```

3. Install dependencies:

   ```bash filename="Terminal"
   npm install
   ```

The starter kit uses [`@chainlink/ccip-sdk`](https://www.npmjs.com/package/@chainlink/ccip-sdk) v1.10+ for Canton and Ethereum Sepolia interactions.

## Understanding Canton Configuration (`canton-config.json`)

Canton deployment fields live in `config/canton-config.json`. Copy the example file and replace the **two placeholder fields**:

```bash filename="Terminal"
cp config/canton-config.example.json config/canton-config.json
```

Replace **`party`** with your Canton testnet party ID (it must match your Okta application). Replace **`transferInstructionUrl`** with your validator's transfer-instruction API (Amulet fee transfers and token metadata — not EDS). On **CCIP testnet** with Chainlink-hosted parties, contact Chainlink for your assigned validator URL and ledger access. If you run your own validator, use your validator's transfer-instruction API URL instead.

All other fields (`edsUrl`, `indexerUrl`, `ccipParty`, `ccvs`, `senderInstanceId`, etc.) are pre-configured for the Canton CCIP testnet lane in `canton-config.example.json`.

**Example `canton-config.json`:**

```json filename="config/canton-config.json"
{
  "party": "YOUR_LEDGER_PARTY::1220…",
  "ccipParty": "ccipOwner::1220e382f4e57b0815e6be737006e381e6b7de448e06bd033ece6df498017879f551",
  "edsUrl": "https://eds.testnet.ccip.chain.link",
  "transferInstructionUrl": "https://validator.example.com/api/validator",
  "indexerUrl": "https://indexer-1.testnet.ccip.chain.link",
  "chainId": "canton:TestNet",
  "senderInstanceId": "prod-ccipsender",
  "ccvs": ["0xec1e288bcf8bbf034ac2d31b67f9b15a3f1f828d086c5b9d8fc2866129cd02fe"]
}
```

Key fields:

- **`party`**: Your ledger party — the sender for `canton2any` scripts and the default Canton receiver for `any2canton` scripts.
- **`transferInstructionUrl`**: Your validator's transfer-instruction API. Depends on which validator hosts your party (not the Global CCIP EDS at `edsUrl`).
- **`senderInstanceId`**: The `CCIPSender` instance used when sending from Canton.
- **`ccvs`**: CommitteeVerifier contract IDs required for message validation.
- **`edsUrl`** / **`indexerUrl`**: Global CCIP Explicit Disclosure Service and indexer endpoints for proofs and disclosures.

Network selectors, router addresses, token instruments, and the default **`--gasLimit`** (`50000` for destination `ccipReceive`) are defined in [`helperConfig.ts`](https://github.com/smartcontractkit/ccip-starter-kit-canton/blob/main/helperConfig.ts).

## Optional script flags

Send scripts (`canton2any:*` and `any2canton:*`) share these common flags:

| Flag                                 | Default      | Description                                                                                                  |
| :----------------------------------- | :----------- | :----------------------------------------------------------------------------------------------------------- |
| `--gasLimit`                         | `50000`      | Gas limit for `ccipReceive` on the destination chain (Sepolia for `canton2any`, Canton for `any2canton`)     |
| `--feeToken`                         | `native`     | CCIP fee token on the source chain (`native`: Amulet on Canton, ETH on Sepolia; or `link`)                   |
| `--evmReceiver` / `--cantonReceiver` | See config   | Override the default receiver on the destination chain                                                       |
| `--no-exec`                          | auto-exec on | `canton2any` only — skip automatic execution on Sepolia; run `canton2any:manual-exec` after CCV verification |
| `--finality`                         | `finalized`  | `any2canton` only — requested source finality (`finalized` or block depth, e.g. `32`)                        |

## Canton Ledger Access

Canton sends and executes use **JWT authentication + direct ledger submit** (same as `ccip-cli` without `--wallet`). No local signing key is required for participant-hosted parties.

Scripts obtain a JWT automatically via **Okta client credentials** (`CANTON_AUTH_URL`, `CANTON_CLIENT_ID`, and `CANTON_CLIENT_SECRET` in `.env`). Resolution order:

1. `CANTON_JWT` in `.env` (explicit override)
2. Client credentials → fresh token from Okta
3. `jwt` field in `canton-config.json` (fallback only)

> **NOTE: Prod testnet access**
>
> Canton testnet Ledger API access may require Chainlink VPN and an Okta JWT tied to your party. Contact Chainlink for testnet credentials, including `CANTON_LEDGER_URL`, `CANTON_AUTH_URL`, and `transferInstructionUrl`.

## Wallets and Accounts

- **Canton party**: Your on-ledger party ID in `canton-config.json`. Scripts submit commands on behalf of this party via `submit-and-wait-for-transaction`.

- **Ethereum Sepolia account** (optional for source sends): Required only if you run `canton2any:manual-exec` on Sepolia or want to verify token delivery on the destination chain. Set `EVM_PRIVATE_KEY` and `ETHEREUM_SEPOLIA_RPC_URL` in `.env`. For data-only sends with default auto-execution, you only need the **receiver contract address** on Sepolia (the starter kit provides a default).

## Environment Configuration (`.env` file)

Create `.env` from the example and replace every placeholder:

```bash filename="Terminal"
cp .env.example .env
```

| Variable                   | Description                                                           |
| :------------------------- | :-------------------------------------------------------------------- |
| `EVM_PRIVATE_KEY`          | Sepolia signer private key (hex, with or without `0x`)                |
| `ETHEREUM_SEPOLIA_RPC_URL` | Your Sepolia JSON-RPC URL                                             |
| `CANTON_LEDGER_URL`        | Canton participant **JSON Ledger API** URL (not EDS or the validator) |
| `CANTON_CONFIG_PATH`       | Path to `canton-config.json` (default: `./config/canton-config.json`) |
| `CANTON_AUTH_URL`          | Okta authorization server URL for your Canton party                   |
| `CANTON_CLIENT_ID`         | Okta client ID for your Canton party                                  |
| `CANTON_CLIENT_SECRET`     | Okta client secret — scripts fetch a fresh JWT on each run            |

**Example `.env` file:**

```bash filename=".env"
EVM_PRIVATE_KEY=0xYourSepoliaPrivateKey
ETHEREUM_SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY
CANTON_LEDGER_URL=https://your-canton-ledger-api.example.com
CANTON_CONFIG_PATH=./config/canton-config.json
CANTON_AUTH_URL=https://auth.example.com
CANTON_CLIENT_ID=your_okta_client_id
CANTON_CLIENT_SECRET=your_okta_client_secret
```

> **CAUTION: Keep your secrets safe**
>
> Never commit `.env`, Okta client secrets, or private keys to version control.

## Testnet Tokens

### Amulet (default CCIP fee token on Canton)

`canton2any` scripts pay CCIP fees in **Amulet** by default. Fund your Canton party with Amulet holdings before sending.

Check balances:

```bash filename="Terminal"
npm run check-balance
```

Or check Amulet only:

```bash filename="Terminal"
npm run check-balance -- --chain canton --token amulet
```

### LINK on Canton (token transfer tutorials)

Token transfer and programmable token transfer tutorials send **LINK** from Canton to Sepolia. Your party needs LINK (`link-token`) holdings on Canton.

```bash filename="Terminal"
npm run check-balance -- --chain canton --token link
```

Pass `--feeToken link` on send scripts to pay CCIP fees in LINK instead of Amulet.

### Sepolia ETH (optional)

If you run `canton2any:manual-exec` yourself, your Sepolia account needs test ETH for gas. Use the [Chainlink Faucet](https://faucet.chain.link) to obtain test ETH.

## Verify Setup

From the starter kit root, confirm Canton connectivity and balances:

```bash filename="Terminal"
npm run check-balance
```

You should see LINK and Amulet balances for your Canton party. If you configured `EVM_PRIVATE_KEY`, Sepolia LINK and TEST balances are shown as well.

When setup is complete, continue to the [Canton as Source tutorials](/ccip/tutorials/canton/source).

> **CAUTION: Educational Example Disclaimer**
>
> This page includes an educational example to use a Chainlink system, product, or service and is provided to
> demonstrate how to interact with Chainlink's systems, products, and services to integrate them into your own. This
> template is provided "AS IS" and "AS AVAILABLE" without warranties of any kind, it has not been audited, and it may be
> missing key checks or error handling to make the usage of the system, product or service more clear. Do not use the
> code in this example in a production environment without completing your own audits and application of best practices.
> Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for unintended outputs
> that are generated due to errors in code.