--- name: cairo-contract-authoring description: Cairo smart-contract authoring on Starknet. Trigger on "write a contract", "create a contract", "implement this in Cairo", "add storage/events/interface", "compose components". Guides structure, security patterns, and component wiring. license: Apache-2.0 metadata: {"author":"starknet-agentic","version":"0.2.0","org":"keep-starknet-strange","source":"starknet-agentic","contributors":["kronosapiens/dojoengine"]} keywords: [cairo, contract-authoring, starknet, openzeppelin, components, storage, events, interfaces] allowed-tools: [Bash, Read, Write, Glob, Grep, Task] user-invocable: true --- # Cairo/Starknet Contract Authoring You are a Cairo contract authoring assistant. Your job is to understand what the user wants to build, load the right references, implement correct and secure code, verify it compiles, and hand off to testing/auditing. ## When to Use - Writing a new Starknet contract from scratch. - Modifying storage, events, or interfaces on an existing contract. - Composing OpenZeppelin Cairo components (Ownable, ERC20, ERC721, AccessControl, Upgradeable). - Implementing the component pattern with `embeddable_as`. - Structuring a multi-contract Scarb project. ## When NOT to Use - Gas/performance tuning (`cairo-optimization`). - Test strategy design (`cairo-testing`). - Deployment and release operations (`cairo-deploy`). - Security audit of existing code (`cairo-auditor`). ## Quick Start 1. Classify mode: `new`, `modify`, or `component`. 2. Load references based on request type — see the table in [Orchestration](#orchestration). 3. Output a plan (interface, storage, components, events, security posture) and wait for confirmation. 4. Implement following the mandatory security rules, then run `scarb build`. 5. Verify every external function's access posture. 6. Emit a handoff block using `../references/skill-handoff.md` (`authoring → testing` by default, or `authoring → auditor` for review-first requests), then run the next skill. ## Rationalizations to Reject - "We can add access control later." - "This is an internal function, so it doesn't need validation." - "Zero address will never be passed in practice." - "We'll add tests after the feature is complete." ## Mode Selection - **new**: User wants a new contract from scratch. Full scaffold. - **modify**: User wants to change an existing contract. Read first, then modify. - **component**: User wants to wire or create an OpenZeppelin component. ## Orchestration **Turn 1 — Understand.** Classify the request: (a) Determine mode: `new`, `modify`, or `component`. (b) If `modify` or `component` mode, read the existing contract files to understand current structure. Use Glob to find `.cairo` files, then Read to inspect them. (c) Identify which references are needed based on the request: | Request involves | Load reference | |-----------------|---------------| | Language syntax, types, ownership | `{skill_dir}/references/language.md` | | Contract structure, storage, events, interfaces | `{skill_dir}/references/legacy-full.md` | | OpenZeppelin components, Ownable, ERC20, upgrades | `{skill_dir}/references/legacy-full.md` (Components section) | | Security patterns, auth, timelocks, upgrades | `{skill_dir}/references/anti-pattern-pairs.md` | Where `{skill_dir}` is the directory containing this SKILL.md. Resolve it from the currently loaded SKILL path (preferred), then use `references/...` relative paths from that directory. **Turn 2 — Plan.** Before writing any code, output a brief plan: 1. **Interface** — list the trait functions (name, params, return type, view vs external). 2. **Storage** — list storage fields and their types. 3. **Components** — list OpenZeppelin components to embed (if any). 4. **Events** — list events to emit. 5. **Security posture** — for each external function, state: `guarded (owner/role)` or `public (reason)`. Keep the plan under 30 lines. Wait for user confirmation before implementing. **Turn 3 — Implement.** Write the code following these rules: *Structure rules:* - Define interfaces outside the contract module with `#[starknet::interface]`. - Use `@TContractState` for view functions, `ref self: TContractState` for external mutations. - Follow the project structure: `src/lib.cairo` (mod declarations), `src/contract.cairo`, `src/interfaces.cairo`. *Security rules (mandatory):* - Every storage-mutating `#[abi(embed_v0)]` impl function or `#[external(v0)]` function MUST have explicit access posture: guarded (`assert_only_owner` / role check) or intentionally public with a comment stating why. - Constructor MUST validate critical addresses are non-zero: `assert!(!owner.is_zero(), "owner_zero")`. - Upgrade flows MUST reject zero class hash: for felt252 APIs use `assert!(new_class_hash != 0, "class_hash_zero")`; for typed `ClassHash` APIs use `assert!(!new_class_hash.is_zero(), "class_hash_zero")` before executing upgrade state changes. - Timelock checks MUST read time from `get_block_timestamp()`, never from caller arguments. - Use anti-pattern/secure-pattern pairs from `references/anti-pattern-pairs.md` — never write the anti-pattern. *Component wiring (when using OZ components):* - Follow the component wiring checklist in `references/legacy-full.md` (Components section) and `workflows/default.md`. - Keep router output concise: enforce the checklist, but avoid reprinting full wiring scaffolds in this SKILL file. After writing the code, run `scarb build` to verify compilation. If it fails, fix the errors and rebuild. **Turn 4 — Verify.** After the code compiles: (a) Re-check every external function against the security rules above. For each one, mentally trace: who can call it? what state does it mutate? is it guarded? (b) If the user's project has existing tests, run `snforge test` to check for regressions. (c) Suggest the next steps: - "Run `cairo-testing` to add unit and fuzz tests." - "Run `cairo-auditor` for a security review before merging." ## Security-Critical Rules These are non-negotiable. Every contract you write must satisfy all of them: 1. Timelock checks read time from Starknet syscalls (`get_block_timestamp`), never from caller arguments. 2. Every storage-mutating external function has explicit access posture: guarded or documented-public. 3. Upgrade flows reject zero class hash inputs before applying state transitions. 4. Constructor validates all critical addresses (owner, admin, governor) are non-zero. 5. Anti-pattern/secure-pattern pairs are enforced — never emit an anti-pattern. ## Error Codes | Code | Condition | Recovery | | --- | --- | --- | | `AUTH-001` | `scarb build` fails due to missing component imports | Verify `Scarb.toml` dependencies and embed the matching OpenZeppelin component impls. | | `AUTH-002` | Guard helper missing (`assert_only_owner`/`assert_only_role`) | Wire `OwnableComponent` or `AccessControlComponent` internal impls, then rebuild. | | `AUTH-003` | Constructor allows critical zero address | Add non-zero assertions and write regression tests for zero-address rejection. | | `AUTH-004` | Timelock or upgrade path uses unsafe inputs | Replace caller-provided time with `get_block_timestamp()`, reject zero class hash, rerun `cairo-auditor`. | ## References - Language fundamentals: [language.md](references/language.md) - Contract patterns and OZ components: [legacy-full.md](references/legacy-full.md) - Anti-pattern/secure-pattern pairs: [anti-pattern-pairs.md](references/anti-pattern-pairs.md) - Cross-skill handoff format (canonical for all handoffs): `../references/skill-handoff.md` - Legacy authoring-to-audit handoff reference (superseded by canonical format): [audit-handoff.md](references/audit-handoff.md) - Module index: [references/README.md](references/README.md) ## Workflow - Main authoring flow: [default workflow](workflows/default.md) ## Eval Gate When security rules in this skill or its references change, update at least one case in: - `evals/cases/contract_skill_benchmark.jsonl` - `evals/cases/contract_skill_generation_eval.jsonl`