Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

0.1.0 - 2026-10-09

First release of the ckSOL minter, which mints ckSOL on the ckSOL ledger for SOL deposited on Solana and sends SOL back when ckSOL is burned. It is deployed on staging against Solana Devnet as ckDevnetSOL.

Added

  • Deposit addresses: get_deposit_address derives a Solana deposit address for any ICRC-1 account, as an update call so that it can later depend on state without a breaking change. The minter’s own main address is derived separately from the master key, so no account can obtain it as a deposit address (#3, #183, #227)
  • Deposits: deposit_sol reads the finalized balance of the given account’s deposit address and queues it for a sweep, charging the attached cycles for the RPC call plus the deposit_sol_fee. It rejects balances below minimum_deposit_amount, and a second call while a deposit for the same account is in flight. deposit_sol_status follows a deposit through Queued, Swept, Finalized and Minted, or Dropped and Quarantined on the unhappy paths (#5, #25, #208, #210, #211, #212)
  • Sweeps: a timer sweeps up to ten queued deposits into the main address per transaction. Once the transaction is finalized, the minter checks its outcome against the planned transfers, credits what the main address received and mints ckSOL for each deposit, with the sweep signature and deposit id in the mint memo. Sweeps that fail or expire are dropped and can be retried with deposit_sol; sweeps whose outcome does not match the plan are quarantined and nothing is minted (#213, #214, #218, #220, #222, #230, #232)
  • Withdrawals: withdraw_sol burns ckSOL through an ICRC-2 approval and queues a transfer of the amount minus withdrawal_fee; withdraw_sol_status reports Pending, TxSent and TxFinalized. Amounts below minimum_withdrawal_amount are rejected, and so are destinations that could never receive the transfer or would corrupt the minter’s accounting: reserved account keys such as sysvars and builtin programs, the minter’s main address, and its nonce accounts (#9, #13, #18, #29, #46, #109, #243, #262)
  • Durable-nonce withdrawals: queued withdrawals are batched up to ten per transaction, each built on a durable nonce account from a pool configured through the init and upgrade arguments, so a withdrawal transaction never expires. Nonce accounts can only be added, and each is verified on read. A transaction that does not reach the chain is re-broadcast unchanged, and a withdrawal is finalized from the state of its nonce account, which unlike the transaction history stays readable indefinitely. Only withdrawals the main address can pay for, including the transaction fee, are submitted (#84, #115, #202, #241, #244, #248, #252, #255, #257, #258, #260)
  • Event log: every state change is recorded as an event and replayed on upgrade, and get_events exposes the log. The minter’s public key is recorded on first fetch, so it is available immediately after an upgrade (#8, #20, #26, #65, #67, #70, #235)
  • Configuration: init and upgrade arguments for the SOL RPC canister, the ledger, the threshold key, the Solana network, fees, minimum amounts and nonce accounts, and get_minter_info to read them together with the minter’s balance and main address (#7, #10, #83, #86)
  • Observability: structured canister logs, Prometheus metrics at /metrics, including the age of the oldest in-flight deposit and withdrawal for alerting, and a dashboard at /dashboard with the minter’s metadata, minted and quarantined deposits, and withdrawals (#10, #64, #72, #90, #114, #127, #140, #152, #237)
  • Reproducible build: the release wasm is built in Docker and its SHA-256 can be checked against the published one (#80)

Fixed

  • Trust RPC responses only for what was asked: a getTransaction response must contain the queried transaction, and getSignatureStatuses searches the transaction history, so a landed transaction is not reported as unknown once it leaves the recent status cache (#236, #239)
  • Tolerate concurrent first fetches of the master public key, which used to trap the second caller (#226)
  • Fixes from a full deposit and withdrawal round trip and a deposit stress test on staging (#263)