CoW Protocol on Solana
CoW Protocol is an open-source trading protocol that settles user intents in batch auctions. It supports direct matching between users (Coincidence of Wants) as well as on-chain liquidity sources.
This repository hosts the Solana implementation, currently in early development. The protocol is already live on Ethereum and other EVM chains; the Solidity contracts are at cowprotocol/contracts.
[!CAUTION] These Solana programs are a work in progress and are not ready for production use. The code is unaudited, subject to change, and may contain significant vulnerabilities that could lead to loss of funds. Please do not rely on it with real assets. Any deployment to mainnet is for testing purposes only. We're sharing it openly to develop in the open and welcome your feedback.
Design
The design of the program is documented in DESIGN.md. It contains a high-level technical description of what the program does and points out meaningful differences from the Ethereum implementation.
The security design is documented in SECURITY_DESIGN.md. It lays out the security properties the program aims to guarantee, the trust scope, the privileged roles, and what is explicitly not guaranteed.
Development
Install the Solana toolchain (Rust, Solana CLI, and friends) by following the Solana quick setup.
A Node.JS runtime is also necessary for generating client libraries from the IDL.
Common dev tasks are exposed via just recipes (see Justfile).
Most package managers provide this package, see list of available Just packages.
Run just --list to see what's available.
Repository layout
The repository is a Cargo workspace following the program / client / interface split:
interface/: shared types and theInstructionbuilders. Depends only on the lightweight crates so it can be consumed from both on-chain and off-chain code.programs/settlement/: the on-chain settlement program.programs/settlement/idl/: the IDL of the settlement program and its generated associated client libraries.programs/settlement/idl/client/js/: A Typescript client generated from the IDL
client/: off-chain client helpers that re-export the builders frominterfaceand add small convenience wrappers.
How to build
Build the on-chain program (produces target/deploy/cow_settlement.so):
Build everything except IDL client libraries (workspace crates plus the on-chain program):
The IDL client libraries can be generated with:
How to test
The IDL client libraries can be tested with:
Benchmarks
just bench runs the test suite and regenerates bench-report.json:
How to build a verified (reproducible) build
Requires Docker.
The program bakes in the address it will be deployed at, chosen at build time, so pass the target deployment as $ENV (either prod for production or barn for staging):
Verified builds are only available for the committed prod and barn addresses, since one can't pass build flags to a verified build process.
How to deploy
Requires Docker (for the verified build step).
You will need a deployer keypair — a Solana wallet funded with enough SOL to cover program storage rent and transaction fees. This wallet becomes the upgrade authority for the deployed program.
Do not fund the program address itself. Only the deployer wallet needs SOL.
There are two environments, each pinned to its own fixed address: barn (staging, used for testing) and prod (production, the actual contract).
Deploy either with just deploy "$ENV" …, where $ENV is barn or prod.
The recipe refuses to run unless the keypair/address you pass resolves to that environment's committed address.
It covers both a first-time deploy and an upgrade, and finishes by running the initialize script (a no-op if the program is already initialized).
For a one-off build at an arbitrary address use just deploy-at.
In the case of a first deployment, the deployer must be B6acm3swJK9pJ7fe4i4GQgP7x5A3RndvsdV2bKhcA1i5, otherwise initialization fails.
Initial deployment
The program only works at the specified hardcoded addresses (either PROD_ID or BARN_ID from the interface), which its pinned state PDA is derived from. initialize rejects a deployment anywhere else.
initialize also only accepts the hardcoded INITIALIZER (cow_settlement_interface::instruction::initialize::INITIALIZER) as its payer, so the deployer keypair must be that key.
Pass the program keypair file as the first argument. Solana derives the program address from it and registers the deployer as the upgrade authority:
Upgrading an existing program
[!IMPORTANT] Before upgrading, if the storage format has been changed for any existing PDAs, ensure that the MINOR (aka v0.x.0) version in the Cargo.toml is bumped. This will relocate all program storage, preventing unintended collisions with incompatible data.
Pass the program's public key (address) as the first argument. The deployer wallet must already be the upgrade authority:
Verifying the deployment on-chain
Since we use verified builds, we can have our program marked as verified on various block explorers. This is only possible on mainnet, not on testnets.
To do so, run this once the program is deployed and the matching source is pushed to the public repository:
$ENV: the environment it was built for (prodorbarn).$PROGRAM_ID: the program address.$DEPLOYER_KEYFILE: the upgrade authority keypair.<optional-commit-hash>: the commit that will be verified; currentHEADotherwise.
Publishing the cargo packages
Authenticate your cargo cli with cargo login. Ensure you have permission to publish cow-settlement-interface, cow-settlement-client, and cow-test-cli.
Then, all packages can published in one go:
One-off example
$PROGRAM_KEYFILE may be an address in the case of an upgrade.
Alpha releases
There are two possible release flows while the project is in alpha:
- Program breaking change: the logic of the program changes meaningfully or there's some change on the layout of any PDA.
- Patch update: it's mostly a hotfix for the program that doesn't meaningfully change the program state. The old client/interface should still work with the new program code.
You can use the settle CLI for a smoke test of the programs after a release. See cargo run -p cow-test-cli -- sell --help and cargo run -p cow-test-cli -- settle --help.
Breaking change
- Create a new branch based off the
mainbranch, pull the latest changes. Make sure there are no local changes (git status --porcelainis empty). - Follow the instruction to bump the versions in the project by at least a minor version.
- Generate new accounts (
for env in prod barn; do solana-keygen new --no-bip39-passphrase -o "../deploy-v$VERSION-$env.json"; done). These will be the addresses of the new deployment. - Store the newly generated accounts in 1password (under "Settlement account by version").
- Update the production address in
solana_pubkey::declare_id!andBARN_IDininterface/src/lib.rsto the newly generated addresses (search and replace). - Update the state PDA bytes in the IDL (use the test to see the bytes to change).
- Commit the code changes resulting from the steps above (excluding the key of the generated account).
- Switch your network to devnet (
solana config set --url devnet). - Deploy on
ENV=barn.- Deploy the programs:
PROGRAM_KEYFILE="../deploy-v$VERSION-$ENV.json". The deployer keypair is in 1password (under "Solana Deployer"). The program keypair file is the key that was generated before. - Switch your network to mainnet (
solana config set --url mainnet-beta). - Authorize all currently existing solvers using the solver CLI (
cow solver add --keypair "$DEPLOYER_KEYFILE" --program-id "$ENV" <solver-address>).
- Deploy the programs:
- Deploy on
ENV=prodas before once confirming that barn is healthy. - Make sure the package installs without errors: run
cargo install --path ./test-cli --locked(it depends on all other packages). - Create a PR with the changes and wait for approval.
- (Only for
ENV=prod!) Publish the IDL. - Once the PR is merged to
main, check out that commit and verify the deployment on-chain for both barn and prod. - Publish the cargo packages.
- Create a new GitHub release; in doing so, create a new tag like
v0.42; title "Alpha release, v0.42".
Patch update
- Create a new branch based off the
mainbranch. Make sure there are no local changes (git status --porcelainis empty). - Follow the instruction to bump the versions in the project by a patch version.
- Commit the code changes resulting from the changes above.
- Make sure the package installs without errors: run
cargo install --path ./test-cli --locked(it depends on all other packages). - Create a PR with the changes and wait for approval, then merge on
main; check out main and pull the changes. - Upgrade
ENV=barn. - Verify the deployment on-chain.
- Repeat the previous two steps with
ENV=prod. - Only for
prod, publish the IDL, if changed. - Publish the cargo packages.
- Create a new GitHub release; in doing so, create a new tag like
v0.42.1337; title "Alpha release, v0.42.1337".
Bump the versions in the project
You need to update Cargo's toml and lock file. Here is a list of commands to help bumping all relevant strings:
# Rust workspace crate
# IDL metadata
Publishing the IDL
Use the following command to publish the IDL. Block explorers will then use the IDL to show decoded transactions to the user.
PROGRAM_ID="the IDL will be registered for the program at the address specified in this string"
The IDL is only available for the prod environment.
License
The core settlement program rust crate is licensed under the terms of the GNU Lesser General Public License v3.0.
All other crates are dual licensed under the terms of the MIT or Apache 2.0 license.
Copyright (c) 2026 CoW Foundation