Agent skill

starknet-scanner

Use when the user wants to audit Starknet contracts for security vulnerabilities, scan Cairo contracts for Starknet-specific patterns including account abstraction, class replacement, or L1-L2 messaging, review Starknet DeFi protocols for component architecture flaws, or analyze cross-layer bridge security.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/starknet-scanner

SKILL.md

Starknet Specialized Scanner

Specialized security scanner for Starknet Cairo contracts. Extends the general Cairo Scanner with Starknet-specific patterns: account abstraction, contract upgrades via replace_class, component architecture, and the L1-L2 messaging bridge.


Why a Separate Starknet Scanner?

While the Cairo Scanner covers language-level patterns (felt arithmetic, Sierra safety), Starknet-specific features create unique attack surfaces:

Feature Security Impact
Account Abstraction Custom validation logic = custom attack surface
replace_class_syscall Contract upgrade mechanism — must be protected
Components (like Solidity libraries) Storage collision between components
L1-L2 Messaging Cross-chain replay, message validation
Sequencer Centralized sequencer = MEV, censorship risks
Fee market STRK token fees, gas estimation

Detection Capabilities

Category Detection Severity
Account __validate__ missing signature check Critical
Account __execute__ allows arbitrary call without validation Critical
Account Signature replay across chains (no chain_id in hash) High
Upgrade replace_class_syscall callable by unauthorized party Critical
Upgrade No upgrade delay/timelock High
Upgrade Storage layout incompatibility after upgrade High
Components Storage collision between components High
Components Component events shadowing contract events Medium
L1-L2 Message replay (consumed message not tracked) Critical
L1-L2 Missing sender validation on L1 handler Critical
L1-L2 Message not consumed (stuck funds) High
Felt Felt arithmetic wrapping (p = 2^251 + 17*2^192 + 1) High
Storage Storage address collision (Pedersen hash) Medium
Access Missing caller validation on external function Critical
Access Ownable component not initialized High

Starknet Account Abstraction

Every account on Starknet is a smart contract. This means custom validation logic:

cairo
#[starknet::contract(account)]
mod MyAccount {
    // REQUIRED: Validates transaction signature
    // If this returns successfully, the tx is considered valid
    #[external(v0)]
    fn __validate__(
        ref self: ContractState,
        calls: Array<Call>
    ) -> felt252 {
        // CRITICAL: Must verify the transaction signature
        // If this blindly returns VALIDATED, anyone can submit txs as this account
        let tx_hash = get_tx_info().unbox().transaction_hash;
        let signature = get_tx_info().unbox().signature;
        
        // Verify signature against stored public key
        assert(check_ecdsa_signature(tx_hash, self.public_key.read(), *signature.at(0), *signature.at(1)), 'invalid sig');
        
        starknet::VALIDATED
    }
    
    // REQUIRED: Executes the validated transaction
    #[external(v0)]
    fn __execute__(
        ref self: ContractState,
        calls: Array<Call>
    ) -> Array<Span<felt252>> {
        // Execute each call
        // Typically a loop over calls with call_contract_syscall
    }
}

Account Security Checklist

  • __validate__ verifies transaction hash signature
  • __validate__ uses stored public key (not hardcoded)
  • Signature cannot be replayed (nonce handled by protocol)
  • chain_id included in signature verification (cross-chain replay)
  • Key rotation mechanism exists and is secure
  • Multicall execution handles failures correctly (atomicity)

Contract Upgrade via replace_class_syscall

Starknet contracts can upgrade their logic using replace_class_syscall:

cairo
use starknet::replace_class_syscall;
use starknet::ClassHash;

#[external(v0)]
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
    // CRITICAL: WHO can call this?
    self.ownable.assert_only_owner();
    
    // Replace the contract's class (logic) with new implementation
    replace_class_syscall(new_class_hash).unwrap();
    
    // Emit upgrade event
    self.emit(Upgraded { new_class_hash });
}

Upgrade Security

Risk Description
Unauthorized upgrade Anyone calling replace_class_syscall can change contract logic
No timelock Instant upgrade = no time for users to exit
Storage incompatibility New class may interpret storage differently
Proxy pattern If using proxy, verify replace_class on implementation, not just proxy
  • replace_class_syscall protected by access control (owner, governance)
  • Upgrade delay (timelock) implemented for critical contracts
  • Storage layout documented and verified compatible across versions
  • Upgrade event emitted

Component Architecture

Starknet components are reusable modules (similar to Solidity libraries with storage):

cairo
// Using OpenZeppelin components
#[starknet::contract]
mod MyContract {
    use openzeppelin::access::ownable::OwnableComponent;
    use openzeppelin::token::erc20::ERC20Component;
    
    component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
    component!(path: ERC20Component, storage: erc20, event: ERC20Event);
    
    #[storage]
    struct Storage {
        #[substorage(v0)]
        ownable: OwnableComponent::Storage,
        #[substorage(v0)]
        erc20: ERC20Component::Storage,
        // Custom storage
        my_value: felt252,
    }
}

Component Security

Risk Description
Storage collision Two components writing to same storage address
Uninitialized component Ownable without initializer() = no owner set
Event shadowing Component events with same name as contract events
Version mismatch Component version incompatible with contract
  • All components initialized in constructor
  • #[substorage(v0)] used correctly (automatic storage isolation)
  • No manual storage access that could collide with component storage
  • Component versions compatible with each other

L1-L2 Messaging

Starknet communicates with Ethereum L1 via asynchronous messaging:

Direction Mechanism Latency
L1 → L2 send_message_to_l2() on Starknet Core contract ~minutes (L2 block time)
L2 → L1 send_message_to_l1_syscall() in Cairo ~hours (proof verification)

L1-L2 Security Checklist

  • L2 handler validates L1 sender (from_address in L1Handler)
  • L1 handler validates L2 sender (message origin)
  • Messages consumed exactly once (replay protection)
  • Message format matches between L1 and L2 contracts
  • Stuck message handling (cancellation mechanism exists)
  • Fee handling on L1→L2 messages correct

Resources

  • Starknet Patterns

Workflows

  • Starknet Audit

See Also

  • Cairo Scanner for Cairo language patterns
  • Chain Guide: Starknet for chain context

Error Code Reference

Starknet-specific error codes and system errors encountered during audits.

Starknet OS / Sequencer Errors

Error Code Name Meaning
TRANSACTION_FAILED Transaction failure Generic execution failure — check inner error
ENTRYPOINT_NOT_FOUND Missing entrypoint Selector not found on contract — wrong function name/args
UNINITIALIZED_CONTRACT No contract Address has no deployed contract class
ENTRY_POINT_FAILED Execution revert Contract function reverted — check custom error
FEE_TRANSFER_FAILURE Fee payment Insufficient balance to pay transaction fee
VALIDATE_FAILURE Account validation Account __validate__ rejected transaction — signature/auth issue
OUT_OF_RESOURCES Resource limit Transaction exceeded Cairo steps or builtins limit
CLASS_ALREADY_DECLARED Duplicate class Contract class hash already declared on network

Account Abstraction Errors

Error Pattern Source Meaning
'INVALID_SIGNATURE' __validate__ Signature verification failed in account contract
'INVALID_CALLER' Account guard Caller is not the expected account or protocol
'INVALID_TX_VERSION' Version check Transaction version not supported (v1 vs v3)
'EXPIRED' Time check Transaction or session expired
'UNDERSPENT_FEE' Fee estimation Actual fee lower than estimate — potential gas griefing
'PAYMASTER_REJECTED' Paymaster Paymaster refused to sponsor transaction

Starknet Contract Errors (OpenZeppelin Cairo)

Error String Component Meaning
'Caller is not the owner' OwnableComponent Missing owner role — access control
'Caller is the zero address' OwnableComponent Invalid zero caller
'New owner is the zero address' OwnableComponent Invalid ownership transfer
'ERC20: insufficient balance' ERC20Component Token balance too low
'ERC20: insufficient allowance' ERC20Component Approval not set
'ERC721: invalid token ID' ERC721Component Token does not exist
'ERC721: unauthorized caller' ERC721Component Not owner or approved
'ReentrancyGuard: reentrant call' ReentrancyGuardComponent Reentrancy detected
'Class hash cannot be zero' UpgradeableComponent Invalid upgrade target

L1↔L2 Messaging Errors

Error Pattern Direction Meaning
'INVALID_MESSAGE_TO_CONSUME' L1→L2 Message not found in L2 pending messages
'MESSAGE_NOT_SENT' L2→L1 L2 message not recorded by sequencer
'INVALID_FROM_ADDRESS' L1→L2 L1 sender address does not match expected
'INVALID_NONCE' Both Message nonce mismatch — replay or ordering issue

Troubleshooting

Issue Likely Cause Solution
Account abstraction vulnerabilities missed Scanner uses EOA mental model Audit __validate__ and __execute__ in all account contracts; check signature schemes
replace_class upgrade risks not flagged Scanner doesn't track class replacement Map all replace_class_syscall calls; verify upgrade authority and timelock protections
Component storage collision missed Scanner doesn't model Cairo component storage Verify component storage isolation; check for #[storage] field name conflicts across components
L1↔L2 message handling gaps Scanner audits L2 in isolation Audit #[l1_handler] functions; trace message flow from L1 contract through Starknet OS
Fee estimation manipulation not caught Scanner doesn't model Starknet fee mechanism Check __validate__ and __execute__ for fee-related assumptions; test with v3 transactions
Missing event emission in state changes Scanner focuses on logic, not observability Verify all state-changing functions emit events; critical for off-chain indexing and monitoring

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results