skills/starknet-scanner/SKILL.md
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.
npx skillsauth add 0x-shashi/web3-audit-skills skills/starknet-scannerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
4 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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 |
| 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 |
Every account on Starknet is a smart contract. This means custom validation logic:
#[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
}
}
__validate__ verifies transaction hash signature__validate__ uses stored public key (not hardcoded)chain_id included in signature verification (cross-chain replay)replace_class_syscallStarknet contracts can upgrade their logic using replace_class_syscall:
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 });
}
| 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)Starknet components are reusable modules (similar to Solidity libraries with storage):
// 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,
}
}
| 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 |
#[substorage(v0)] used correctly (automatic storage isolation)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) |
from_address in L1Handler)Starknet-specific error codes and system errors encountered during audits.
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
development
Systematically hunt for every variant of a discovered vulnerability across the entire codebase. Use when a bug is found and all instances of the same root cause pattern must be identified, or when performing variant analysis during competitive audits on Code4rena or Sherlock.
testing
Use when the user wants to audit TON smart contracts for security vulnerabilities, scan FunC or Tact contracts for message chain replay, bounce handling, or gas issues, review TON DeFi protocols for actor-model concurrency flaws, or analyze asynchronous message passing security.
tools
Analyze ERC20/ERC721/ERC1155 token implementations for non-standard behavior, fee-on-transfer mechanics, rebasing logic, blacklists, pausability, and integration risks. Use when reviewing protocols that interact with external tokens or implementing token-related features.
testing
Use when the user wants to audit Sui Move smart contracts, scan Sui-specific patterns including object ownership, shared objects, or dynamic fields, review Sui DeFi protocols for object model security issues, or analyze Sui-specific transaction and consensus patterns.