Skip to content

Latest commit

 

History

History
218 lines (169 loc) · 9.61 KB

File metadata and controls

218 lines (169 loc) · 9.61 KB

Real-World Asset (RWA) Tokenization on Stellar/Soroban

CI Coverage (contracts) Coverage (CLI) License: MIT

A production-ready suite of Soroban smart contracts and tooling for tokenizing real-world assets (RWAs) on the Stellar network — with KYC/AML compliance, investor whitelisting, transfer/holding limits, and snapshot-based dividend distributions.

The "hug and ripples" concept — an RWA token "hugs" its holders through compliance (KYC-approved, whitelisted investors only) while dividend payouts ripple outward to every qualifying holder in proportion to their holdings.

Contracts

Contract Description
RWA Token (rwa-token) SEP-41 compliant token with compliance extensions: pause, KYC hook, investor registry, transfer/holding limits, dividend distribution
KYC Hook (kyc-hook) KYC/AML verification with expiry, tiered levels, and batch approval
Investor Registry (investor-registry) Whitelist with accredited status, purchase limits, purchase tracking, and batch operations
Dividend Distributor (dividend-distributor) Multi-round dividend distribution with snapshot-based pro-rata calculation, claiming, round closure, and unclaimed fund withdrawal

Architecture

┌──────────────────┐
│    RWA Token     │◄──── SEP-41 Standard
│  (rwa-token)     │       (transfer, approve, mint, burn)
├──────────────────┤
│ Compliance Layer │
│  ┌────────────┐  │
│  │ KYC Hook   │──│──► check_kyc(investor) → bool
│  └────────────┘  │
│  ┌────────────────┐
│  │Investor Registry│──► is_investor(investor) → bool
│  └────────────────┘
│  ┌──────────────────┐
│  │ Transfer/Holding │──► configurable limits
│  │ Limits           │
│  └──────────────────┘
├──────────────────┤
│ Dividend Dist.   │────► snapshot, pro-rata, claim
└──────────────────┘

Repository layout

├── contracts/                 # Soroban smart contracts (Rust)
│   ├── rwa-token/             #   SEP-41 token + compliance + dividends
│   ├── kyc-hook/              #   KYC/AML hook
│   ├── investor-registry/     #   Investor whitelist
│   └── dividend-distributor/  #   Multi-round dividend distributor
├── cli/                       # TypeScript CLI (deploy & manage)
├── scripts/                   # Bash CLI + one-command Docker deploy
├── .github/workflows/ci.yml   # CI: tests, coverage, lint, Docker build
├── Dockerfile                 # Runnable CLI image (contracts + stellar-cli)
├── docker-compose.yml         # Local Soroban sandbox (pinned quickstart)
└── Makefile                   # All dev/deploy targets

Prerequisites

  • Rust — the toolchain is pinned in rust-toolchain.toml (1.97.1 + wasm32v1-none); rustup installs it automatically.
  • Node.js ≥ 22.12 (for the TypeScript CLI).
  • Docker (optional, for the one-command local sandbox).
  • stellar-cli ≥ 22 (optional if using Docker) — stellar-cli 27.x is the pinned version in the image: https://github.com/stellar/stellar-cli
  • A Stellar account with testnet/mainnet funds (only for real deployments).

Quick start

Option A — Docker, one command (recommended)

Spin up a local Soroban sandbox and deploy the entire contract suite:

./scripts/docker-deploy.sh

If STELLAR_SECRET_KEY is not set, the script generates a fresh key for you and saves it to the git-ignored .env. Done — the full suite is deployed to your local sandbox and the config is saved to .rwa/deployment-local.json.

Option B — Native

# 1. Build contracts (produces WASM in target/wasm32v1-none/release)
cargo build --release --target wasm32v1-none --workspace

# 2. Run tests
cargo test --workspace

# 3. Deploy to testnet (or set SOROBAN_NETWORK=mainnet)
export STELLAR_SECRET_KEY=S...
./scripts/rwa-cli.sh deploy --name "T-Bill Token" --symbol "TBILL" --decimals 6

Option C — Everything via Make

make check-all    # fmt + clippy + tests + coverage + wasm + CLI checks (same as CI)
make docker-deploy   # one-command local deployment
make deploy-testnet  # deploy to testnet (STELLAR_SECRET_KEY + TOKEN_* vars)

Environment variables

Variable Required Default Description
STELLAR_SECRET_KEY yes Admin/deployer secret key. Never commit a real key.
SOROBAN_NETWORK no testnet testnet | mainnet | local
SOROBAN_RPC_URL no per-network Override the Soroban RPC endpoint
RWA_CONFIG_DIR no <repo>/.rwa Where deployment configs are stored
RWA_ROOT no auto-detected Repo root (used by the Docker image)
TOKEN_NAME/SYMBOL/DECIMALS no see example Token parameters for make deploy-*

Copy .env.example to .env — it is auto-loaded by the scripts and is git-ignored.

Usage

Bash CLI (scripts/rwa-cli.sh)

./scripts/rwa-cli.sh deploy --name "T-Bill Token" --symbol TBILL --decimals 6
./scripts/rwa-cli.sh configure --transfer-limit 1000000 --holding-limit 50000000
./scripts/rwa-cli.sh configure --set-kyc-hook C... --set-investor-registry C...
./scripts/rwa-cli.sh investors --add G... --accredited true --purchase-limit 100000
./scripts/rwa-cli.sh mint G... 10000
./scripts/rwa-cli.sh dividends --distribute 5000 --token C...
./scripts/rwa-cli.sh dividends --claim G...
./scripts/rwa-cli.sh pause | unpause | status

TypeScript CLI (cli/)

cd cli
npm install
npm run build
STELLAR_SECRET_KEY=S... node bin/rwa-cli.js deploy -n testnet --name "T-Bill Token" --symbol TBILL --decimals 6
node bin/rwa-cli.js configure -n testnet --transfer-limit 1000000 --holding-limit 50000000
node bin/rwa-cli.js investors -n testnet --add G... --accredited true --purchase-limit 100000
node bin/rwa-cli.js dividends -n testnet --distribute 5000 --token C...

Both CLIs validate inputs (addresses, amounts, networks), read the saved deployment config, and print a summary after every operation.

Contract reference

Full function tables for all four contracts live in docs/CONTRACTS.md (SEP-41 interface, compliance extensions, and dividend lifecycle). Every state-changing function emits events (initialize, transfer, approve, mint, burn, set_paused, kyc_approved, investor_added, dividend_distributed, …).

Testing & quality gates

CI runs every check below; the local equivalents are identical.

Check Command Gate
Rust formatting cargo fmt --all --check clean
Clippy lint cargo clippy --workspace --all-targets -- -D warnings zero warnings
Contract tests cargo test --workspace 60 tests, all green
Contract coverage cargo llvm-cov --workspace --fail-under-lines 85 ~95% lines
WASM release build cargo build --release --target wasm32v1-none succeeds
Docs RUSTDOCFLAGS=-D warnings cargo doc --workspace --no-deps clean
CLI typecheck/lint/format npm run typecheck && npm run lint && npm run format:check clean
CLI tests npm test 45 tests, all green
CLI coverage npm run coverage ~98% lines
ShellCheck docker run --rm -v $PWD:/mnt koalaman/shellcheck:stable /mnt/scripts/rwa-cli.sh clean
Docker image docker build -t rwa-cli:ci . succeeds

Dependency updates are automated via Dependabot for Cargo, npm and GitHub Actions. The Rust toolchain is pinned for reproducible builds.

Deployment

Local sandbox (Docker)

docker compose up -d soroban-local      # standalone network, RPC on :8000/rpc
./scripts/rwa-cli.sh deploy --network local   # via SOROBAN_NETWORK=local
# or: make docker-deploy (builds the image and deploys everything)

Testnet / Mainnet

export STELLAR_SECRET_KEY=S...
make deploy-testnet        # or: SOROBAN_NETWORK=mainnet make deploy-mainnet
./scripts/rwa-cli.sh status

Deployment configs (contract IDs, RPC, admin, token params) are written to .rwa/deployment-<network>.json after every deploy.

Security

  • Admin-only state changes are guarded by require_auth() / admin.require_auth().
  • All math uses checked_add/checked_sub/checked_mul with overflow panics.
  • Inputs are validated in both CLIs (Stellar address format, non-negative amounts, decimal bounds) before any RPC call.
  • Secrets are never committed — see .env.example and SECURITY.md.

Contributing

See CONTRIBUTING.md for setup, conventions, and the PR checklist.

License

MIT — see LICENSE.