Smart Contract Development in Cairo (StarkNet)

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Smart Contract Development in Cairo (StarkNet)
Complex
~3-5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1215
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1043
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Smart Contract Development on Cairo (StarkNet)

Cairo is not "yet another smart contract language". It's a language written from scratch for one task: generating provable computations for STARK provers. If you're used to Solidity, first weeks with Cairo will hurt. If you're used to Rust — slightly easier, but still requires mental restructuring.

Why Cairo Isn't Just "StarkNet Solidity"

Main thing to understand: Cairo 1 (current version) compiles to Sierra — intermediate representation, which then compiles to CASM (Cairo Assembly). Sierra guarantees any program terminates — this removes class of attacks where contract deliberately consumes all gas and reverts, leaving sequencer without reward.

In practice this means: no infinite loops without explicit counter, no arbitrary jumps. This is constraint you must work with.

Second fundamental thing: StarkNet is ZK-rollup, and every transaction ultimately confirmed by STARK proof on L1 (Ethereum). This gives L2 execution cheapness with L1 security guarantees. But gas model in StarkNet differs from EVM: gas counted in "Cairo VM steps", not EVM opcodes.

Typical Mistakes Moving from Solidity to Cairo

Storage — Not Mapping, Different World

In Solidity mapping(address => uint256) is familiar construct. In Cairo storage works via StorageMap with explicit serialization. Problem arises when developer tries storing complex structures with nested collections: Cairo requires manual Store trait implementation for custom types, otherwise won't compile.

Real case: token contract with balances: LegacyMap<ContractAddress, u256> works fine. Contract with positions: LegacyMap<ContractAddress, UserPosition>, where UserPosition is custom structure, requires #[derive(Store)] and correct implementation. If structure contains nested Array<u256>, storing directly in storage is impossible — Cairo doesn't support dynamic types in storage. This breaks Solidity patterns where mapping(address => uint256[]) works out of box.

Solution: decompose structures into flat mappings. positions_amount: LegacyMap<ContractAddress, u256>, positions_token: LegacyMap<ContractAddress, ContractAddress> — instead of one mapping with nested struct.

Reentrancy in StarkNet — Different Mechanics

In EVM, reentrancy works via call stack: contract A calls contract B, B calls back to A before first call completes. In StarkNet current architecture (protocol version 0.13+), reentrancy also possible via call_contract_syscall. But nuance: storage state updates immediately on write, not at transaction end (unlike some interpretations).

Protection pattern same: checks-effects-interactions. First update user's balance, then make external call. ReentrancyGuard in Cairo implemented via storage flag:

#[storage]
struct Storage {
    _reentrancy_guard: bool,
}

fn _lock(ref self: ContractState) {
    assert(!self._reentrancy_guard.read(), 'ReentrancyGuard: reentrant call');
    self._reentrancy_guard.write(true);
}

fn _unlock(ref self: ContractState) {
    self._reentrancy_guard.write(false);
}

OpenZeppelin Cairo (github.com/OpenZeppelin/cairo-contracts) provides ready ReentrancyGuardComponent. Use it, don't invent your own.

Integer Types and Overflow

Cairo 1 uses u256, u128, u64, u32, u8, felt252. Type felt252 is prime field (order ~2^251), unsigned integer. Modular arithmetic: if add two felt252 values and result exceeds field boundary, you get wrapping — no panic, no error. This trap for those porting logic from Solidity where uint256 panics on overflow by default.

For financial logic use u256 with explicit checks via u256_overflow_add, or checked_add on integer types. Never store token amounts in felt252.

OpenZeppelin Cairo Components — Architectural Pattern

Cairo contracts in StarkNet ecosystem built on component architecture. OpenZeppelin Cairo provides components for ERC-20, ERC-721, ERC-1155, Ownable, AccessControl, Upgradeable and other patterns.

Component is reusable unit of logic with own storage namespace, mixed into contract via #[starknet::contract] and component!() macro. Important detail: each component has own storage namespace, no collisions. This solves storage collision problem known from proxy patterns in Solidity.

Example of ERC-20 initialization with Ownable:

#[starknet::contract]
mod MyToken {
    use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl};
    use openzeppelin::access::ownable::OwnableComponent;

    component!(path: ERC20Component, storage: erc20, event: ERC20Event);
    component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);

    #[storage]
    struct Storage {
        #[substorage(v0)]
        erc20: ERC20Component::Storage,
        #[substorage(v0)]
        ownable: OwnableComponent::Storage,
    }
}

This pattern differs from Solidity inheritance: here's composition, not hierarchy. Trying to implement inheritance via parent contract function calls in Cairo doesn't work same way as Solidity.

Upgradability: Proxy or replace_class_syscall

StarkNet has native upgrade mechanism: replace_class_syscall. Contract can replace own class hash with new one, storage remains. Similar to UUPS pattern, but without separate proxy contract.

Same risks: if new class version changes storage layout (order of variables in #[storage]), data may be interpreted incorrectly. In Cairo storage addresses computed from variable names (name hash), so renaming variable = losing data in it after upgrade.

OpenZeppelin provides UpgradeableComponent, which limits upgrade() call to owner only and emits event:

fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
    self.ownable.assert_only_owner();
    self.upgradeable.upgrade(new_class_hash);
}

Before mainnet upgrade — mandatory fork test. Verify new class reads old storage correctly.

Testing Cairo Contracts

Tooling ecosystem younger than EVM, but growing fast.

Starknet Foundry (snforge) — main testing tool. Test syntax close to Foundry for EVM:

#[cfg(test)]
mod tests {
    use snforge_std::{declare, ContractClassTrait, start_cheat_caller_address};

    #[test]
    fn test_transfer() {
        let contract = declare("MyToken").unwrap();
        let (contract_address, _) = contract.deploy(@array![]).unwrap();
        // tests
    }
}

Fuzz testing in snforge supported via #[fuzzer] attribute. Less developed than Foundry fuzzing on EVM, but covers basic scenarios.

Test networks. Sepolia — current recommended StarkNet testnet. Goerli decommissioned. For local development use Katana (from dojo package) or starknet-devnet-rs.

Tool Purpose Status
snforge Unit/integration tests Actively developed
sncast CLI for deployment and interaction Stable
Katana Local StarkNet node Actively developed
starknet-devnet-rs Alternative local node Stable
Voyager Block explorer mainnet/testnet Production

Account Abstraction by Default

StarkNet has no EOA (Externally Owned Account) concept in EVM sense. Every account is smart contract implementing IAccount interface. This is Account Abstraction (AA) by default, without EIP-4337.

For developer this means: can't use tx.origin in EOA sense (simply doesn't exist), no ECDSA signatures hardcoded in protocol at account level. Account can implement any signature scheme — multisig, passkey, session keys.

Session keys pattern especially interesting for game contracts: user signs once to issue session key, then game does transactions on their behalf within allowed scope. Without EIP-4337 bundler infrastructure overhead.

Process for Cairo Contract

Analysis. Break down requirements for Cairo specifics: what data goes to storage, which logic requires inter-contract calls (more expensive than internal calls), need upgradability.

Storage design. Most critical stage. Wrong storage layout can't be fixed after deployment without upgrade.

Development. Cairo 2.x, Scarb as build tool, OpenZeppelin Cairo as base. For DeFi logic — study existing audited contracts in ecosystem (Ekubo, JediSwap, zkLend).

Testing. Snforge unit tests, integration tests on Katana, manual tests on Sepolia.

Audit. Auditor ecosystem for Cairo smaller than EVM. Specialized teams: Trail of Bits (Cairo practice), ChainSecurity, Nethermind Security.

Deployment. Via sncast or starknet.js. Declare (publish class) + Deploy (create instance) — two steps instead of one in EVM.

Timeline

Cairo contract development of comparable complexity takes 1.5-2x longer than Solidity equivalent if team transitioning from EVM first time. This time for learning Cairo specifics, not language complexity indicator.

Estimate: basic ERC-20 with custom logic — 3-5 days. DeFi protocol (AMM, lending) — 3-8 weeks. Full development with audit and mainnet deployment — from 2 months.

Cost calculated after technical briefing and requirements analysis.