How It Works
The ckSOL minter canister is the core component of the system. It manages the conversion between SOL and ckSOL, maintains custody of the SOL backing all outstanding ckSOL tokens, and interacts with the Solana blockchain via the SOL RPC canister.
The ckSOL token itself is implemented as an ICRC-1/ICRC-2 ledger canister.
The minter controls one or more Solana addresses derived from a threshold Schnorr over Ed25519 public key and a per-account derivation path. No private key ever exists in plaintext — Solana transactions are signed via the IC management canister’s sign_with_schnorr API (SchnorrAlgorithm::Ed25519).
Deposit: SOL → ckSOL
-
Get a deposit address. Call
get_deposit_addresson the minter with your ICP principal (and an optional subaccount). The minter returns a Solana address derived specifically for your account. -
Send SOL. Transfer SOL to that deposit address from any Solana wallet, in one or several transfers. No memo or transaction signature is needed, so deposits can come straight from an exchange.
-
Notify the minter. Call
deposit_solon the minter with the same owner/subaccount used in step 1. This call requires attaching cycles (seedeposit_sol_required_cyclesinget_minter_info; unused cycles are refunded). The minter reads the balance of your deposit address, queues it for a sweep, and returns a deposit id. -
Sweep and mint. On timers, the minter sweeps the deposit address to its main Solana account and, once the sweep transaction is finalized, mints the swept amount of ckSOL (minus the deposit’s share of the sweep transaction fee) to your ICRC-1 ledger account.
-
Track the deposit. Call
deposit_sol_statuswith the deposit id to follow the progress:Queued→Swept→Finalized→Minted.
sequenceDiagram
actor User
participant Minter as ckSOL Minter
participant Ledger as ckSOL Ledger
participant Solana
User->>Minter: get_deposit_address(owner, subaccount)
Minter-->>User: deposit_address
User->>Solana: transfer SOL to deposit_address
User->>Minter: deposit_sol(owner, subaccount) + cycles
Minter->>Solana: read deposit_address balance
Minter-->>User: deposit_id
Note over Minter,Solana: (processed asynchronously by the minter)
Minter->>Solana: sweep deposit_address to main account
Minter->>Ledger: mint with icrc1_transfer(to=user, swept amount - fee share)
User->>Minter: deposit_sol_status(deposit_id)
Minter-->>User: Minted { block_index, minted_amount }
Withdrawal: ckSOL → SOL
-
Approve the minter. Grant the minter an ICRC-2 allowance on your ckSOL ledger account.
-
Submit a withdrawal request. Call
withdraw_solon the minter with the destination Solana address and the amount in lamports. The minter:- Burns the requested ckSOL from your ledger account via icrc2_transfer_from.
- Queues the corresponding SOL transfer.
-
Transaction submission. The minter constructs a Solana transaction, signs it using chain-key Ed25519, and submits it via the SOL RPC canister.
-
Monitor status. Call
withdraw_sol_statuswith the ledger burn index returned bywithdraw_solto track the status of your withdrawal request (Pending→TxSent→TxFinalized).
sequenceDiagram
actor User
participant Minter as ckSOL Minter
participant Ledger as ckSOL Ledger
participant Solana
User->>Ledger: icrc2_approve(spender=minter, amount)
Ledger-->>User: ok
User->>Minter: withdraw_sol(destination_address, amount)
Minter->>Ledger: burn with icrc2_transfer_from(from=user, to=burn, amount)
Ledger-->>Minter: burn_block_index
Minter-->>User: burn_block_index
Note over Minter,Solana: (processed asynchronously by the minter)
Minter->>Solana: submit SOL transfer to destination_address
User->>Minter: withdraw_sol_status(burn_block_index)
Minter-->>User: TxFinalized(Success)
The full design rationale, including fees and flows, is in the Design chapter.