# Starknet Cairo Development Guide ## Why Starknet? - **ZK Rollup**: All transactions are verified by STARK proofs — security comes from math, not game theory - **Native AA**: All accounts are contract accounts (no EOA concept) - **Cairo VM**: Designed specifically for verifiable computation, independent of the EVM - **Low gas**: Compute-intensive tasks are 10-100x cheaper than Ethereum --- ## Cairo Language Basics ```rust // Cairo 2.x syntax (Rust-like) use starknet::ContractAddress; #[starknet::contract] mod ERC20 { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess, Map}; #[storage] struct Storage { name: ByteArray, total_supply: u256, balances: Map, allowances: Map<(ContractAddress, ContractAddress), u256>, } #[event] #[derive(Drop, starknet::Event)] enum Event { Transfer: Transfer, } #[derive(Drop, starknet::Event)] struct Transfer { #[key] from: ContractAddress, #[key] to: ContractAddress, value: u256, } #[abi(embed_v0)] impl ERC20Impl of super::IERC20 { fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { self.balances.read(account) } fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { let sender = starknet::get_caller_address(); let sender_balance = self.balances.read(sender); assert(sender_balance >= amount, 'Insufficient balance'); self.balances.write(sender, sender_balance - amount); self.balances.write(recipient, self.balances.read(recipient) + amount); self.emit(Transfer { from: sender, to: recipient, value: amount }); true } } } ``` --- ## Key Differences from the EVM | Concept | EVM/Solidity | Starknet/Cairo | |------|-------------|----------------| | Base type | uint256 | felt252 (252-bit field element) | | Storage | mapping | Map | | Accounts | EOA + contracts | **All contracts** | | Events | event | #[event] + #[derive] | | Constructor | constructor | #[constructor] | | Inheritance | is | Composition + interfaces | | msg.sender | msg.sender | get_caller_address() | | address | address | ContractAddress | --- ## OpenZeppelin Cairo Components ```toml # Scarb.toml [dependencies] openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.15.0" } ``` ```rust #[starknet::contract] mod MyToken { use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; use starknet::ContractAddress; component!(path: ERC20Component, storage: erc20, event: ERC20Event); #[abi(embed_v0)] impl ERC20Impl = ERC20Component::ERC20Impl; #[abi(embed_v0)] impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; impl ERC20InternalImpl = ERC20Component::InternalImpl; #[storage] struct Storage { #[substorage(v0)] erc20: ERC20Component::Storage, } #[constructor] fn constructor(ref self: ContractState, recipient: ContractAddress) { self.erc20.initializer("MyToken", "MTK"); self.erc20.mint(recipient, 1000000000000000000000000); // 1M tokens } } ``` --- ## Deployment Workflow ```bash # Install tooling curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | sh pip install starknet-devnet # Create a new project scarb new my_contract cd my_contract && scarb build # Local testnet starknet-devnet --seed 0 # Deploy with sncast sncast --account my_account declare --contract-name MyContract sncast --account my_account deploy --class-hash 0x... ``` --- ## Starknet.js Frontend Integration ```bash npm install starknet ``` ```typescript import { RpcProvider, Account, Contract, cairo } from "starknet" // Connect provider const provider = new RpcProvider({ nodeUrl: "https://starknet-mainnet.public.blastapi.io" }) // Connect wallet (ArgentX / Braavos) const { account } = await window.starknet.enable() // Interact with contract const contract = new Contract(ABI, contractAddress, account) // Read const balance = await contract.balanceOf(address) // Write (invoke) const tx = await contract.transfer(recipient, cairo.uint256(1000n)) await provider.waitForTransaction(tx.transaction_hash) ``` --- ## Account Abstraction (Native Support) All accounts on Starknet are contracts, with built-in support for: - **Custom signature schemes** (ECDSA / WebAuthn / multisig) - **Session Keys** (users grant apps temporary operation permissions) - **Paymaster** (gas sponsorship, pay with any token) - **Multicall** (execute multiple operations in a single signature) ```typescript // Starknet native multicall (no extra SDK required) const multiCall = await account.execute([ { contractAddress: tokenA, entrypoint: "approve", calldata: [spender, amount] }, { contractAddress: dex, entrypoint: "swap", calldata: [tokenA, tokenB, amount] }, ]) ``` ## Tooling Ecosystem | Tool | Purpose | |------|------| | **Scarb** | Cairo package manager + compiler | | **Starknet Foundry (snforge)** | Testing framework (Foundry-like) | | **ArgentX / Braavos** | Leading wallets | | **Voyager** | Block explorer | | **starknet-devnet** | Local testnet | # Starknet Cairo Development Guide ## Why Starknet? - **ZK Rollup**: All transactions are verified by STARK proofs — security comes from math, not game theory - **Native AA**: All accounts are contract accounts (no EOA concept) - **Cairo VM**: Designed specifically for verifiable computation, independent of the EVM - **Low gas**: Compute-intensive tasks are 10-100x cheaper than Ethereum --- ## Cairo Language Basics ```rust // Cairo 2.x syntax (Rust-like) use starknet::ContractAddress; #[starknet::contract] mod ERC20 { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess, Map}; #[storage] struct Storage { name: ByteArray, total_supply: u256, balances: Map, allowances: Map<(ContractAddress, ContractAddress), u256>, } #[event] #[derive(Drop, starknet::Event)] enum Event { Transfer: Transfer, } #[derive(Drop, starknet::Event)] struct Transfer { #[key] from: ContractAddress, #[key] to: ContractAddress, value: u256, } #[abi(embed_v0)] impl ERC20Impl of super::IERC20 { fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { self.balances.read(account) } fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { let sender = starknet::get_caller_address(); let sender_balance = self.balances.read(sender); assert(sender_balance >= amount, 'Insufficient balance'); self.balances.write(sender, sender_balance - amount); self.balances.write(recipient, self.balances.read(recipient) + amount); self.emit(Transfer { from: sender, to: recipient, value: amount }); true } } } ``` --- ## Key Differences from the EVM | Concept | EVM/Solidity | Starknet/Cairo | |------|-------------|----------------| | Base type | uint256 | felt252 (252-bit field element) | | Storage | mapping | Map | | Accounts | EOA + contracts | **All contracts** | | Events | event | #[event] + #[derive] | | Constructor | constructor | #[constructor] | | Inheritance | is | Composition + interfaces | | msg.sender | msg.sender | get_caller_address() | | address | address | ContractAddress | --- ## OpenZeppelin Cairo Components ```toml # Scarb.toml [dependencies] openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.15.0" } ``` ```rust #[starknet::contract] mod MyToken { use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; use starknet::ContractAddress; component!(path: ERC20Component, storage: erc20, event: ERC20Event); #[abi(embed_v0)] impl ERC20Impl = ERC20Component::ERC20Impl; #[abi(embed_v0)] impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; impl ERC20InternalImpl = ERC20Component::InternalImpl; #[storage] struct Storage { #[substorage(v0)] erc20: ERC20Component::Storage, } #[constructor] fn constructor(ref self: ContractState, recipient: ContractAddress) { self.erc20.initializer("MyToken", "MTK"); self.erc20.mint(recipient, 1000000000000000000000000); // 1M tokens } } ``` --- ## Deployment Workflow ```bash # Install tooling curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | sh pip install starknet-devnet # Create a new project scarb new my_contract cd my_contract && scarb build # Local testnet starknet-devnet --seed 0 # Deploy with sncast sncast --account my_account declare --contract-name MyContract sncast --account my_account deploy --class-hash 0x... ``` --- ## Starknet.js Frontend Integration ```bash npm install starknet ``` ```typescript import { RpcProvider, Account, Contract, cairo } from "starknet" // Connect provider const provider = new RpcProvider({ nodeUrl: "https://starknet-mainnet.public.blastapi.io" }) // Connect wallet (ArgentX / Braavos) const { account } = await window.starknet.enable() // Interact with contract const contract = new Contract(ABI, contractAddress, account) // Read const balance = await contract.balanceOf(address) // Write (invoke) const tx = await contract.transfer(recipient, cairo.uint256(1000n)) await provider.waitForTransaction(tx.transaction_hash) ``` --- ## Account Abstraction (Native Support) All accounts on Starknet are contracts, with built-in support for: - **Custom signature schemes** (ECDSA / WebAuthn / multisig) - **Session Keys** (users grant apps temporary operation permissions) - **Paymaster** (gas sponsorship, pay with any token) - **Multicall** (execute multiple operations in a single signature) ```typescript // Starknet native multicall (no extra SDK required) const multiCall = await account.execute([ { contractAddress: tokenA, entrypoint: "approve", calldata: [spender, amount] }, { contractAddress: dex, entrypoint: "swap", calldata: [tokenA, tokenB, amount] }, ]) ``` ## Tooling Ecosystem | Tool | Purpose | |------|------| | **Scarb** | Cairo package manager + compiler | | **Starknet Foundry (snforge)** | Testing framework (Foundry-like) | | **ArgentX / Braavos** | Leading wallets | | **Voyager** | Block explorer | | **starknet-devnet** | Local testnet | | **Katana** | Local node by the Dojo team |