This file provides context for AI agents working with the IOTA Rust SDK repository.
The IOTA Rust SDK is a modular software development kit for integrating with the IOTA blockchain. IOTA is a next-generation smart contract platform powered by Move.
Key Design Goals:
- Modularity: Users only pay for features they use
- Lightweight: Minimal dependency footprint
- WASM Support: Libraries usable in browser environments
- Multi-language: FFI bindings for Go, Kotlin, Python, C#, Swift (via
uniffi)
- NEVER make breaking changes — this SDK is consumed externally. New fields must be optional, removals require a deprecation step first.
- NEVER disable or skip tests — all tests must pass and stay enabled.
- NEVER use
#[allow(dead_code)],#[allow(unused)], or other lint suppressions to silence warnings — fix the underlying issue. - Types in
iota-sdk-typesmust stay BCS-compatible — verify BCS and JSON round-trips when adding or changing a type.u64is serialized as a string in JSON for JS safety. - Format and lint after every change —
cargo +nightly fmt,dprint fmt, andmake bindings-examples-format-checkfor binding examples. - Keep pull requests small — prefer small, focused PRs over large ones. A small diff is easier to review, easier to revert, and less likely to introduce regressions.
- Split work into multiple PRs when possible — if a change spans multiple concerns (e.g. a refactor plus a new feature, or changes across unrelated crates), split it into separate PRs. Land independent pieces incrementally rather than bundling them together. Critically: when given multiple GitHub issues, ALWAYS create one PR per issue — never bundle multiple issues into a single PR unless explicitly instructed or the issues are genuinely interdependent.
- Write only what the diff can't say — PR descriptions, review comments and chat replies cover the reasoning and the high-level shape of a change, never a walkthrough of the diff. See Writing style; this is the most frequently ignored rule in this file.
- Feature flags matter — the umbrella
iota-sdkgates everything behind features. Check what's enabled for the code you're modifying before assuming an item exists. - NEVER hand-edit generated gRPC types under
crates/iota-sdk-grpc-types/src/proto/— they are build output. Changes go into the proto sources /update_grpc_types.sh.
crates/
├── iota-sdk/ # Umbrella SDK that re-exports the other crates behind feature flags
├── iota-sdk-bcs-schema/ # Proc macro that generates BCS schema definitions (ABNF) from Rust types
├── iota-sdk-crypto/ # Signing traits (`IotaSigner`, `IotaVerifier`) and implementations (ed25519, secp256r1, secp256k1, bls12381, passkey)
├── iota-sdk-ffi/ # FFI layer powering language bindings via `uniffi` (not published)
├── iota-sdk-graphql-client/ # Type-safe GraphQL RPC client using `cynic`
├── iota-sdk-graphql-client-build/ # Build-time GraphQL schema registration for `cynic` codegen
├── iota-sdk-grpc-client/ # gRPC client built on `tonic` (ledger, execution, state, move package services)
├── iota-sdk-grpc-proto-build/ # Build-time codegen for gRPC/protobuf types (`update_grpc_types.sh` regenerates from upstream protos)
├── iota-sdk-grpc-types/ # Generated gRPC/protobuf types
├── iota-sdk-transaction-builder/ # Fluent API for building transactions (online/offline modes)
└── iota-sdk-types/ # Core blockchain types (Address, ObjectId, Transaction, Checkpoint, ...) — BCS-compatible
bindings/
├── csharp/ # C# bindings
├── go/ # Go bindings
├── kotlin/ # Kotlin bindings
├── python/ # Python bindings
├── swift/ # Swift bindings
└── wasm/ # WASM/TypeScript bindings (browser + Node)
The iota-sdk umbrella crate exposes the other crates via modules gated by feature flags: crypto, graphql (→ graphql_client), grpc (→ grpc_client + grpc_types), move-types (→ move_types), txn-builder (→ transaction_builder), and types. grpc and move-types are opt-in (not in default); graphql, crypto, types, txn-builder are on by default.
# Lint, format, tests
make test # Unit tests (nextest)
make test-docs # Doc tests
make test-with-localnet # Tests requiring a running localnet
make clippy # Clippy
make fmt # Format Rust code (requires nightly)
make check-fmt # Verify Rust formatting
make bindings-examples-format # Format the examples shipped with each binding
make bindings-examples-format-check # Verify formatting of binding examples
# WASM
make wasm32 # Check that SDK crates compile to wasm32-unknown-unknown
make wasm # Build the WASM/TypeScript bindings package
# FFI bindings
make bindings # Build all bindings
make go # Go only
make kotlin # Kotlin only
make python # Python only
make csharp # C# only
make swift # Swift only
# gRPC proto regeneration
make grpc # Pull/refresh protos and regenerate types
# BCS schema
make bcs-schema # Regenerate bcs-schema.abnf
# Examples
make examples # Run all Rust examples
make bindings-examples # Run all binding examples
make <lang>-example NAME # Run a single example (lang ∈ {go, kotlin, python, csharp, swift})
# Full CI check
make ci # check-features + check-fmt + check-sort-derives + test + wasm32
# Localnet (IOTA node + faucet + indexer + GraphQL + gas station)
./run_localnet.sh start [iota-localnet-binary] # Start localnet + gas station (Postgres, Redis)
./run_localnet.sh stop # Tear it all down
# Defaults to `iota-localnet` on PATH; pass explicit path as second arg to override
# Direct cargo invocations
cargo nextest run # Direct nextest invocation
cargo test --doc # Direct doc test invocation- Edition: 2024
- Formatting: Nightly rustfmt (config in
rustfmt.toml) - Linting: Clippy with warnings as errors (
-Dwarnings) - Naming: crates
iota-sdk-*, modulessnake_case, typesPascalCase, constantsUPPER_SNAKE_CASE - Errors:
thiserrorenums,#[non_exhaustive]at the type level - Feature gating: optional functionality lives behind features; APIs use
#[cfg(feature = "…")]and#[cfg_attr(doc_cfg, doc(cfg(feature = "…")))]for docs.rs visibility - Comments: see Writing style below
All prose — PR/issue descriptions, reviews, chat replies, comments, commit messages:
- One sentence naming the change, then only what the reader can't get from the diff: the problem, the reasoning, real trade-offs. Default budget: 1–3 sentences per section — spend more only on a concern the reader would miss, never to cover the diff more completely.
- No diff inventory — no per-file bullets, no symbol lists, no restating code in prose. Applies to reasons too: a "why" whose content is visible in the diff is inventory.
- No invented motives — never "X because Y" unless Y came from the issue or task.
- No filler — no lead-ins, headings-for-show, verdict paragraphs, or notes about these rules. Reviews: start with the first finding, one point per comment.
- Plain language — only terms already in the codebase or domain; never coin a label and reuse it as vocabulary.
- Match the answer's shape to the question — an enumerable question gets a bullet list, one line per item; prose only where an item needs actual reasoning.
Before posting: delete every sentence the reader could get from the diff; repeat until a pass cuts nothing.
| File | Purpose |
|---|---|
Cargo.toml (root) |
Workspace manifest with shared dependencies |
Makefile |
Build orchestration |
deny.toml |
Security/license policy |
.github/workflows/ |
CI workflows |
crates/iota-sdk-grpc-proto-build/ |
Proto sources and codegen entry point for gRPC types |
crates/iota-sdk-graphql-client/queries/ |
.graphql query files consumed by the cynic codegen |
- Main branch:
develop(notmain) - CI: All tests must pass, no clippy warnings, proper formatting
- Draft PRs can force CI with
[run-ci]in the PR body - PR title format: Titles are validated in CI (
.github/workflows/pr_title.yml) and must follow the Conventional Commits style. Allowed types arefeat,fix,refactor,chore,upstream, andrelease(e.g.feat: add new gRPC method,chore: update docs). No other prefixes (such asdocs:ortest:) are accepted — usechore:for those.
The make wasm32 target checks that the following crates compile to wasm32-unknown-unknown: iota-sdk, iota-sdk-crypto, iota-sdk-graphql-client, iota-sdk-transaction-builder, and iota-sdk-types. The full WASM/TypeScript bindings package (which also builds iota-sdk-ffi for wasm32) is built with make wasm. The gRPC client/types are not built for WASM. When adding dependencies to any of the WASM-built crates:
- Ensure they support
wasm32-unknown-unknown - Use
getrandomwith thejs/wasm_jsfeature for randomness - Avoid OS-specific functionality
- Run
make wasm32after changes to verify the SDK crates still build forwasm32-unknown-unknown