| name | cairo-deploy |
| description | Deployment guidance for Cairo contracts on Starknet covering sncast commands, account setup, declare/deploy workflow, network configuration, and contract verification. |
| license | Apache-2.0 |
| metadata | {"author":"starknet-agentic","version":"1.0.0","org":"keep-starknet-strange"} |
| keywords | ["cairo","deploy","sncast","starknet","devnet","sepolia","mainnet","declare","verification"] |
| allowed-tools | ["Bash","Read","Write","Glob","Grep","Task"] |
| user-invocable | true |
Cairo Deploy
Reference for deploying Cairo smart contracts to Starknet using sncast (Starknet Foundry).
When to Use
- Deploying contracts to Starknet devnet, Sepolia, or mainnet
- Declaring contract classes
- Setting up deployer accounts
- Configuring network endpoints
- Verifying deployed contracts
- Invoking/calling deployed contracts
When NOT to Use
- Writing or refactoring contract logic (
cairo-contract-authoring).
- Unit, integration, or fuzz testing (
cairo-testing).
- Gas or performance optimization (
cairo-optimization).
- Security review of existing code (
cairo-auditor).
Quick Start
- Build with
scarb build, then declare and deploy with sncast.
- Use skills catalog when the task moves back into authoring, testing, or auditing.
Setup
Install Starknet Foundry
asdf plugin add starknet-foundry
asdf install starknet-foundry 0.56.0
asdf global starknet-foundry 0.56.0
curl -L https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/install.sh | sh
snfoundryup
.tool-versions
Pin versions for reproducible builds:
scarb 2.15.1
starknet-foundry 0.56.0
Note: snforge 0.56.0 requires Scarb >= 2.12.0. Check github.com/foundry-rs/starknet-foundry/releases for the latest.
Build
scarb build
Output goes to target/dev/:
myproject_MyContract.contract_class.json (Sierra)
myproject_MyContract.compiled_contract_class.json (CASM)
Account Setup
Create a New Account
sncast account create \
--url https://starknet-sepolia.g.alchemy.com/v2/YOUR_KEY \
--name my-deployer
sncast account deploy \
--url https://starknet-sepolia.g.alchemy.com/v2/YOUR_KEY \
--name my-deployer
Import Existing Account
sncast account import \
--url https://starknet-sepolia.g.alchemy.com/v2/YOUR_KEY \
--name my-deployer \
--address 0x123... \
--private-key 0xabc... \
--type oz
Account types: oz (OpenZeppelin), argent, braavos
sncast.toml
Configure defaults to avoid repeating flags:
[default]
url = "https://starknet-sepolia.g.alchemy.com/v2/YOUR_KEY"
account = "my-deployer"
accounts-file = "~/.starknet_accounts/starknet_open_zeppelin_accounts.json"
wait = true
[mainnet]
url = "https://starknet-mainnet.g.alchemy.com/v2/YOUR_KEY"
account = "mainnet-deployer"
Use profiles: sncast --profile mainnet declare ...
Declare (Register Class)
Before deploying, declare the contract class on-chain:
sncast declare \
--contract-name MyContract
If the class is already declared, sncast will tell you โ that's fine, use the existing class hash.
Deploy (Create Instance)
sncast deploy \
--class-hash 0x1234... \
--constructor-calldata 0xOWNER_ADDRESS
sncast deploy \
--class-hash 0x1234... \
--constructor-calldata 0xOWNER 0xTOKEN_ADDRESS 1000
Constructor Calldata Encoding
Arguments are passed as felt252 values:
ContractAddress โ pass as hex 0x123...
u256 โ pass as TWO felts: low high (e.g., 1000 0 for 1000)
felt252 โ pass directly
bool โ 1 for true, 0 for false
ByteArray (strings) โ use sncast's string encoding or pass raw
Invoke (Write)
sncast invoke \
--contract-address 0xCONTRACT \
--function "transfer" \
--calldata 0xRECIPIENT 1000 0
Call (Read)
sncast call \
--contract-address 0xCONTRACT \
--function "get_balance" \
--calldata 0xACCOUNT
Programmatic Deployment (starknet.js)
import { Account, CallData, Contract, RpcProvider } from "starknet";
const provider = new RpcProvider({ nodeUrl: process.env.STARKNET_RPC! });
const account = new Account(provider, process.env.ACCOUNT_ADDRESS!, process.env.PRIVATE_KEY!);
const declareTx = await account.declare({ contract: compiledSierra, casm: compiledCasm });
await provider.waitForTransaction(declareTx.transaction_hash);
const deployTx = await account.deploy({
classHash: declareTx.class_hash,
constructorCalldata: CallData.compile({ owner: process.env.OWNER! }),
});
await provider.waitForTransaction(deployTx.transaction_hash);
const contract = new Contract(abi, deployTx.contract_address[0], provider).connect(account);
await contract.invoke("set_fee", [10]);
const fee = await contract.call("get_fee", []);
console.log({ fee });
Multicall
Execute multiple calls in a single transaction:
cat > multicall.toml << 'EOF'
[[call]]
call_type = "deploy"
class_hash = "0x1234..."
inputs = ["0xOWNER"]
[[call]]
call_type = "invoke"
contract_address = "0xTOKEN"
function = "approve"
inputs = ["0xSPENDER", "1000", "0"]
EOF
sncast multicall run --path multicall.toml
Deploy Script Pattern
For complex deployments, use a script:
#!/bin/bash
set -euo pipefail
RPC_URL="https://starknet-sepolia.g.alchemy.com/v2/YOUR_KEY"
ACCOUNT="my-deployer"
echo "Building..."
scarb build
echo "Declaring MyToken..."
TOKEN_CLASS=$(sncast --json declare --contract-name MyToken --url $RPC_URL --account $ACCOUNT | jq -r '.class_hash')
echo "Token class: $TOKEN_CLASS"
echo "Deploying MyToken..."
TOKEN_ADDR=$(sncast --json deploy --class-hash $TOKEN_CLASS --constructor-calldata 0xOWNER --url $RPC_URL --account $ACCOUNT | jq -r '.contract_address')
echo "Token deployed at: $TOKEN_ADDR"
echo "Declaring AMM..."
AMM_CLASS=$(sncast --json declare --contract-name AMM --url $RPC_URL --account $ACCOUNT | jq -r '.class_hash')
echo "Deploying AMM..."
AMM_ADDR=$(sncast --json deploy --class-hash $AMM_CLASS --constructor-calldata $TOKEN_ADDR --url $RPC_URL --account $ACCOUNT | jq -r '.contract_address')
echo "AMM deployed at: $AMM_ADDR"
echo "Done. Addresses:"
echo " Token: $TOKEN_ADDR"
echo " AMM: $AMM_ADDR"
Network Endpoints
| Network | RPC URL |
|---|
| Devnet (local) | http://localhost:5050 |
| Sepolia (testnet) | https://starknet-sepolia.g.alchemy.com/v2/KEY |
| Mainnet | https://starknet-mainnet.g.alchemy.com/v2/KEY |
Alternative providers: Infura, Blast, Nethermind (free tier available).
Local Devnet
cargo install starknet-devnet
starknet-devnet --seed 42
Contract Verification
Verify source code on Voyager or Starkscan:
Note: sncast verify supports both Walnut and Voyager backends. Use --verifier walnut or --verifier voyager explicitly.
Upgradeable Contracts
For contracts using OZ UpgradeableComponent:
sncast declare --contract-name MyContractV2
sncast invoke \
--contract-address 0xEXISTING_CONTRACT \
--function "upgrade" \
--calldata 0xNEW_CLASS_HASH
Common Errors
| Error | Cause | Fix |
|---|
Contract not found | Account not deployed | Run sncast account deploy |
Insufficient max fee | Not enough ETH/STRK for gas | Fund the deployer account |
Class already declared | Same class hash exists | Use the existing class hash for deploy |
Entry point not found | Wrong function name | Check the contract ABI |
Invalid calldata | Wrong number/type of args | Check constructor signature, remember u256 = 2 felts |