Skip to content
64 changes: 46 additions & 18 deletions skills/smart-contracts/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: smart-contracts
description: Stellar smart contract development (Rust, soroban-sdk). Entry point with project setup, contract anatomy, and build/deploy workflow, routing to three companion files in this directory — development.md (storage, auth, cross-contract calls, events, errors, upgrades, factories, troubleshooting), testing.md (unit, fuzz, property, fork, mutation, integration), and security.md (vulnerability classes, checklists, tooling, audits). Use when writing, testing, securing, or shipping Stellar smart contracts (formerly branded Soroban).
description: Stellar smart contract development (Rust, soroban-sdk). Entry point with project setup, contract anatomy, and build/deploy workflow, routing to three companion files in this directory — development.md (storage/TTL, authorization, cross-contract calls, tokens, events, errors, upgrades, fees, troubleshooting), testing.md (unit, fuzz, property, fork, mutation, integration), and security.md (vulnerability classes, checklists, tooling, audits). Use when writing, testing, reviewing, securing, debugging, or shipping Stellar smart contracts, including anything the user calls "Soroban" — Soroban contracts, soroban-sdk, Soroban auth/storage/TTL errors, SEP-41 tokens, or SAC integration from contract code.
user-invocable: true
argument-hint: "[contract task]"
---
Expand All @@ -13,52 +13,66 @@ This file covers setup and the core workflow. The deep dives live alongside it

| Task | File |
|------|------|
| Storage, auth, cross-contract calls, events, errors, upgrades, factories, troubleshooting | [development.md](development.md) |
| Storage/TTL, authorization, constructors, cross-contract calls, tokens, events, errors, upgrades, factories, governance/DeFi patterns, fees/resources, troubleshooting | [development.md](development.md) |
| Unit, integration, fuzz, property, fork, and mutation testing | [testing.md](testing.md) |
| Security review, vulnerability classes, checklists, audit prep, tooling | [security.md](security.md) |

## When to use this skill
- Writing a Stellar smart contract in Rust
- Setting up contract tests (any layer)
- Reviewing a contract for security issues
- Architecting upgradeable contracts, factories, governance, or DeFi primitives
- Architecting upgradeable contracts, factories, custom accounts, or DeFi primitives
- Debugging a contract-specific error (auth, storage, archival, resource limits)

## Related skills
- Assets, trustlines, and SAC bridge → `../assets/SKILL.md`
- Asset issuance, trustlines, and SAC deployment → `../assets/SKILL.md`
- Frontend/wallets that call your contract → `../dapp/SKILL.md`
- Chain data queries (RPC/Horizon) → `../data/SKILL.md`
- ZK verification (BLS12-381, Groth16, Circom/Noir/RISC Zero) → `../zk-proofs/SKILL.md`
- SEP/CAP standards and ecosystem links → `../standards/SKILL.md`

## Versions

This skill was written against **protocol 27** (`soroban-sdk` v27, `rs-soroban-env` v27, `stellar-cli` v27). Version numbers in examples are illustrative — resolve the current ones from these sources rather than trusting any doc:

- **`soroban-sdk` major version tracks the protocol version** (SDK 27 ↔ protocol 27). This rule outlives any specific release.
- Latest SDK release: [crates.io/crates/soroban-sdk](https://crates.io/crates/soroban-sdk) (or `cargo add soroban-sdk`, which resolves it). Pre-releases (`-rc.x`) exist only during a protocol rollout and must be pinned with the exact version string; [GitHub releases](https://github.com/stellar/rs-soroban-sdk/releases) lists them with changelogs.
- Networks upgrade by validator vote, testnet before mainnet — pin the SDK major matching the network you deploy to. Live protocol version: RPC `getVersionInfo` or [Stellar Lab](https://lab.stellar.org).
- Numeric network limits quoted here are mainnet settings at time of writing; they change by vote — [Stellar Lab's Network Limits page](https://lab.stellar.org/network-limits) and `stellar network settings --network mainnet` show the live values.

## Platform constraints

Contracts are Rust compiled to WebAssembly, run in a sandboxed host:

- `#![no_std]` required — use `soroban_sdk` types (`String`, `Vec`, `Map`, `Symbol`), not the Rust standard library
- 64KB compiled contract size limit — use the release profile below
- `Symbol` is limited to 32 characters; `symbol_short!()` covers up to 9
- Compile for the **`wasm32v1-none`** target (Rust ≥ 1.84) — the only Wasm target the Stellar runtime supports
- 128KB compiled contract size limit (network-configured)
- `Symbol` is limited to 32 characters (`a-zA-Z0-9_`); `symbol_short!()` covers up to 9
- Storage is rented: every entry has a TTL and can be archived — see [development.md](development.md#storage)
- No `delegatecall`, no classical cross-contract reentrancy — see [security.md](security.md)
- No `delegatecall`, and cross-contract reentrancy is blocked by the host — see [security.md](security.md)
- No I/O, no networking, no clock beyond the ledger timestamp — everything a contract can do hangs off `Env`

## Project setup

```bash
stellar contract init my-contract # scaffolds a Cargo workspace with contracts/
cd my-contract
rustup target add wasm32v1-none # once per toolchain
```

`Cargo.toml` essentials:
`Cargo.toml` essentials (what `stellar contract init` generates):

```toml
[lib]
crate-type = ["cdylib"]
crate-type = ["lib", "cdylib"] # lib is needed for tests and fuzzing

[dependencies]
soroban-sdk = "25.0.1" # check https://crates.io/crates/soroban-sdk for latest
soroban-sdk = "27.0.0-rc.1" # protocol 27; pre-releases need the exact version string.
# Mainnet is on protocol 26 at the time of writing — use "26" there
# until the network upgrades. Check crates.io for the latest.

[dev-dependencies]
soroban-sdk = { version = "25.0.1", features = ["testutils"] } # match above
soroban-sdk = { version = "27.0.0-rc.1", features = ["testutils"] } # match above

[profile.release]
opt-level = "z"
Expand All @@ -73,11 +87,13 @@ lto = true

## Contract anatomy

One compact example showing state, constructor, auth, TTL, and a typed error:
One compact example showing state, constructor, auth, TTL, a typed error, and an event:

```rust
#![no_std]
use soroban_sdk::{contract, contracterror, contractimpl, contracttype, Address, Env};
use soroban_sdk::{
contract, contracterror, contractevent, contractimpl, contracttype, Address, Env,
};

#[contracttype]
#[derive(Clone)]
Expand All @@ -93,13 +109,22 @@ pub enum Error {
NotInitialized = 1,
}

// Emitted as topics ("incremented", by) with data {count} — #[topic] fields
// become topics, the rest go in the data payload.
#[contractevent]
pub struct Incremented {
#[topic]
pub by: Address,
pub count: u32,
}

#[contract]
pub struct CounterContract;

#[contractimpl]
impl CounterContract {
// Runs once, atomically, at deploy time (Protocol 22+). Must be named
// `__constructor` and return (). Does not run again on upgrade.
// Runs once, atomically, at deploy time. Must be named `__constructor`.
// Does not run again on upgrade.
pub fn __constructor(env: Env, admin: Address) {
env.storage().instance().set(&DataKey::Admin, &admin);
env.storage().instance().set(&DataKey::Counter, &0u32);
Expand All @@ -118,8 +143,9 @@ impl CounterContract {
env.storage().instance().set(&DataKey::Counter, &count);

// Extend TTL so contract state is not archived (threshold, extend-to)
env.storage().instance().extend_ttl(100, 518400);
env.storage().instance().extend_ttl(120 * 17280, 180 * 17280);

Incremented { by: admin, count }.publish(&env);
Ok(count)
}

Expand All @@ -129,12 +155,13 @@ impl CounterContract {
}
```

Full patterns (three storage types, auth variants, cross-contract calls, events, custom types): [development.md](development.md).
Full patterns (three storage types, auth variants, cross-contract calls, tokens, custom types): [development.md](development.md).

## Build, deploy, invoke

```bash
# Build optimized WASM → target/wasm32v1-none/release/*.wasm
# (optimization is on by default; --optimize=false to disable)
stellar contract build

# Create and fund an identity (testnet)
Expand Down Expand Up @@ -162,6 +189,7 @@ To upload WASM without instantiating (e.g. for factories or upgrades), use `stel
## Minimal test

```rust
// src/test.rs — included from lib.rs with `mod test;`
#![cfg(test)]
use super::*;
use soroban_sdk::{testutils::Address as _, Address, Env};
Expand All @@ -179,7 +207,7 @@ fn test_increment() {
}
```

Auth mocking, fuzzing, fork tests, and CI setup: [testing.md](testing.md).
Auth mocking, event assertions, fuzzing, fork tests, and CI setup: [testing.md](testing.md).

## Before mainnet

Expand Down
Loading