The on-chain game engine for Stellar โ ECS, privacy, and account abstraction in one crate
Cougr is the framework for building on-chain games on Stellar. It gives you an
Entity Component System (ECS), built-in ZK proof tooling, session keys, and contract
standards โ all compiled to a single no_std crate that runs natively on Soroban.
| Capability | Cougr on Stellar |
|---|---|
| Language | Rust |
| ECS runtime | SimpleWorld, ArchetypeWorld, GameApp |
| ZK / hidden state | Groth16 + BLS12-381 + Merkle on-chain (X-Ray host fns) |
| Account abstraction | Session keys, passkeys, social recovery |
| Contract standards | Ownable, AccessControl, Pausable, guards, batch |
| State indexing | Structured Soroban events (COUGR topic prefix) |
| Deployment tooling | stellar contract build/deploy (native CLI) |
| Local dev chain | Stellar local network / Quickstart |
| Client SDK | Soroban CLI + community SDKs |
Stellar's strengths translate directly to better games:
- 3โ5 second finality at fractions of a cent per transaction
- X-Ray host functions run Groth16 and BLS12-381 verification on the host, not in WASM โ ZK games are cheap
- Passkey / WebAuthn auth via
secp256r1lets players use Face ID rather than seed phrases
[dependencies]
cougr-core = "1.1.0"
soroban-sdk = "25.1.0"# Cargo.toml
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
cougr-core = "1.1.0"
soroban-sdk = "25.1.0"#![no_std]
use cougr_core::component::ComponentTrait;
use cougr_core::game::SorobanGame;
use cougr_core::{impl_component_observed, impl_soroban_game};
use soroban_sdk::{contract, contractimpl, contracttype, Env};
// 1. Define a component โ one macro call, no boilerplate
#[contracttype]
#[derive(Clone, Debug)]
pub struct Position { pub x: i32, pub y: i32 }
impl_component_observed!(Position, "position", Table, { x: i32, y: i32 });
// โ Emits a (COUGR, set, position) Soroban event on every change
// so off-chain clients track state without polling.
// 2. Wire the contract to an ECS world โ one macro call
#[contract]
pub struct MyGame;
impl_soroban_game!(MyGame, "world");
// 3. Write game logic in plain Rust
#[contractimpl]
impl MyGame {
pub fn spawn(env: Env) -> u32 {
let mut world = MyGame::load_world(&env);
let player = world.spawn_entity();
world.set_typed_observed(&env, player, &Position { x: 0, y: 0 });
MyGame::save_world(&env, &world);
player
}
pub fn move_right(env: Env, player_id: u32) {
let mut world = MyGame::load_world(&env);
let mut pos: Position = world.get_typed(&env, player_id).unwrap();
pos.x += 1;
world.set_typed_observed(&env, player_id, &pos);
MyGame::save_world(&env, &world);
}
pub fn position(env: Env, player_id: u32) -> Option<Position> {
MyGame::load_world(&env).get_typed(&env, player_id)
}
}# Build, test, deploy
cargo test
stellar contract build
stellar contract deploy --wasm target/wasm32-unknown-unknown/release/my_game.wasm \
--source alice --network testnetSimple components โ fixed-size primitives, fastest storage, optional event emission:
use cougr_core::{impl_component, impl_component_observed};
use soroban_sdk::contracttype;
// No events (internal state)
#[contracttype] #[derive(Clone, Debug)]
pub struct Velocity { pub dx: i32, pub dy: i32 }
impl_component!(Velocity, "velocity", Table, { dx: i32, dy: i32 });
// With events (indexer-friendly)
#[contracttype] #[derive(Clone, Debug)]
pub struct Health { pub current: u32, pub max: u32 }
impl_component_observed!(Health, "health", Table, { current: u32, max: u32 });Rich components โ Soroban XDR codec, supports Address, Vec, String, Option,
nested structs. Use when a component contains types not expressible as fixed-size bytes:
use cougr_core::impl_rich_component;
use soroban_sdk::{contracttype, Address, Vec};
// Address field โ requires XDR / impl_rich_component!
#[contracttype] #[derive(Clone, Debug)]
pub struct Ownership { pub owner: Address, pub co_owners: Vec<Address> }
impl_rich_component!(Ownership, "ownership");
// Inventory with dynamic items
#[contracttype] #[derive(Clone, Debug)]
pub struct Inventory { pub items: Vec<u32>, pub capacity: u32 }
impl_rich_component!(Inventory, "inventory");use cougr_core::simple_world::SimpleWorld;
use soroban_sdk::Env;
let env = Env::default();
let mut world = SimpleWorld::new(&env);
// Spawn entities
let knight = world.spawn_entity(); // โ 1
let dragon = world.spawn_entity(); // โ 2
// Set components (typed, with storage-tier routing)
world.set_typed(&env, knight, &Health { current: 100, max: 100 });
world.set_typed(&env, dragon, &Health { current: 500, max: 500 });
// Query entities that have a component
let env_ref = &env;
let combatants = world.get_entities_with_component(&Health::component_type(), env_ref);
// Remove and despawn
world.remove_typed::<Health>(knight);
world.despawn_entity(dragon);ArchetypeWorld is available for larger entity counts and batch queries. Both backends
share the same ComponentTrait interface.
For complex games, GameApp provides stage-based scheduling and plugin registration:
use cougr_core::app::{named_system, GameApp, ScheduleStage};
use cougr_core::SystemConfig;
use soroban_sdk::Env;
let env = Env::default();
let mut app = GameApp::new(&env);
app.add_systems((
named_system("physics", |world, env| {
// runs every tick in Update
})
.in_stage(ScheduleStage::Update),
named_system("scoring", |world, env| {
// runs after physics in PostUpdate
})
.with_config(SystemConfig::new().in_stage(ScheduleStage::PostUpdate)),
));
app.run(&env).unwrap();Zero-knowledge and hidden state
Cougr bundles the full ZK toolchain for on-chain games โ all verification runs on Stellar's X-Ray host functions at host speed:
use cougr_core::privacy::stable::*;
// Groth16 proof verification (BN254)
let result = verify_groth16(&env, &proof, &vk, &public_inputs);
// Commit-reveal for hidden moves
let commitment = pedersen_commit(&env, &secret_value, &blinding_factor);
// ... reveal later and verify
// Merkle proof for fog-of-war or inventory ownership
let valid = verify_merkle_proof(&env, &root, &leaf, &proof_path);Stable ZK primitives: Groth16, BLS12-381, SHA256 Merkle, Poseidon Merkle, Pedersen commitments.
use cougr_core::auth::*;
// Session keys โ players approve a scoped key for a game session,
// then interact without signing every transaction
let session = SessionBuilder::new(&env, &player_address)
.with_scope(&[contract_id])
.with_expiry(env.ledger().timestamp() + 3600)
.build();
// Social recovery โ guardians can restore access
// Multi-device โ per-device policies with revocation
// Passkey / WebAuthn โ Face ID / Touch ID via secp256r1Drop-in security primitives, identical to OpenZeppelin on EVM:
use cougr_core::ops::*;
// Ownership
let ownable = Ownable::new(&env, symbol_short!("owner"));
ownable.transfer_ownership(&env, &new_owner);
// Role-based access
let acl = AccessControl::new(&env, symbol_short!("acl"));
acl.grant_role(&env, &MINTER_ROLE, &minter_address);
// Emergency stop
let pause = Pausable::new(&env, symbol_short!("pause"));
pause.pause(&env);| Area | Status |
|---|---|
ECS runtime (SimpleWorld, ArchetypeWorld, GameApp) |
Stable |
Standards (ops) |
Stable |
Privacy primitives (privacy::stable, zk::stable) |
Stable |
Accounts and session keys (auth) |
Beta |
Advanced ZK (privacy::experimental) |
Experimental |
| Path | Purpose |
|---|---|
src/ |
Core framework |
examples/spawn_and_move/ |
Canonical starter โ spawn + movement with ECS events |
examples/tic_tac_toe/ |
Turn-based game with rich components and address ownership |
examples/*/ |
20+ standalone game contracts (asteroids, chess, battleship โฆ) |
tests/ |
Integration, edge-case, and stress coverage |
docs/ |
Architecture, API contract, maturity model, threat model |
research/ |
ZK and account abstraction design notes |
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
stellar contract build| Item | Value |
|---|---|
| Rust | 1.70+ |
| Edition | 2021 |
| License | MIT |
| Primary SDK | soroban-sdk 25.1.0 |
| Target | wasm32v1-none (Soroban WASM) |
- ARCHITECTURE.md โ module structure and design rationale
- CHANGELOG.md โ release history
- docs/ECS_CORE.md โ ECS runtime model
- docs/PRIVACY_MODEL.md โ ZK proof tiers
- docs/ACCOUNT_KERNEL.md โ session keys and recovery
- docs/PATTERNS.md โ recommended gameplay patterns
- examples/README.md โ example catalog and usage guide
