Skip to content

About

On-chain compliance primitives for Stellar — allowlist/denylist token gating and jurisdiction checks for RWA and stablecoin issuers, built on Soroban.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

655 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

compliance-primitives

On-chain compliance primitives for Stellar — allowlist/denylist token gating and jurisdiction checks for RWA and stablecoin issuers, built on Soroban.

CI

The problem

RWA and stablecoin issuers building on Stellar/Soroban all end up needing the same handful of compliance gates — allowlists, denylists, jurisdiction restrictions — before they can legally let a token move. Today every issuer hand-rolls this logic inside their own token contract, which means more audit surface, more chances for a subtle bug to become a compliance incident, and no shared, reviewed reference to build on. compliance-primitives exists to give the ecosystem a small set of standardized, auditable, independently testable contracts for exactly these checks.

What's in this repo

This is a Cargo workspace with nine focused Soroban contracts, each doing one job:

Contract Description
allowlist-token Token wrapper that only permits transfers between two addresses that are both present on an on-chain allowlist. Admin-managed; forwards cleared transfers to an underlying SEP-41 token contract.
denylist-gate Standalone denylist other contracts call via cross-contract invocation (check(address)) before executing a transfer. Not a token itself — meant to be composed.
jurisdiction-flag Attaches an issuer-controlled jurisdiction code (e.g. an ISO country code) to an address, with a helper (is_permitted_jurisdiction) other contracts can call to check an address against a permitted-jurisdictions list.
audit-log On-chain, append-only audit trail for compliance events emitted by other primitives, queryable by other contracts at invocation time.
circuit-breaker Admin-controlled emergency freeze switch other contracts can check before allowing a transfer.
compliance-aggregator Batches denylist-gate and jurisdiction-flag checks into a single cross-contract call, returning an aggregated pass/fail plus a per-check breakdown.
multisig-admin M-of-N multisig authorization, usable as the admin/issuer address of any of the other primitives via Soroban's custom-account interface.
pausable Shared pause/unpause helper library composed into the other contracts at compile time. Not a deployable contract itself — no #[contract] macro or wasm exports.
policy-engine Composes multiple compliance checks (denylist, jurisdiction) with configurable AND/OR logic into a single evaluate call.

/examples/denylist-gate-consumer is a minimal reference token showing the cross-contract calling pattern for denylist-gate. /examples/rwa-token composes all three primitives in one transfer path — see its TESTNET.md for the testnet reference deployment and walkthrough.

Examples

Each example under /examples demonstrates a different composition pattern:

Quick start

New here? Start with QUICKSTART.md — it walks you through clone → build → test → example → testnet → web playground in one go.

For a condensed reference, the essentials are:

# Clone
git clone https://github.com/stellar-compliance-kit/compliance-primitives.git
cd compliance-primitives

# Run the full test suite (all three contracts + example)
cargo test --workspace

# Lint
cargo clippy --workspace --all-targets -- -D warnings

# Build a contract to wasm (the wasm32v1-none target is pinned in
# rust-toolchain.toml, so rustup installs it automatically)
stellar contract build

# Equivalent, via the cargo alias in .cargo/config.toml
cargo build-wasm

# Deploy a built contract to testnet, e.g. denylist-gate
stellar contract deploy \
  --wasm target/wasm32v1-none/release/denylist_gate.wasm \
  --source <your-testnet-identity> \
  --network testnet

See examples/testnet.env.example for an example config covering the identity/network setup needed for the deploy + invoke round trip above.

Alternatively, use scripts/deploy-testnet.sh to build all contracts and deploy one to testnet in a single step:

STELLAR_SOURCE=<your-testnet-identity> ./scripts/deploy-testnet.sh denylist-gate

To deploy and wire up all eight deployable contracts in the workspace in one step, see scripts/deploy-full-stack-testnet.sh and its walkthrough, docs/full-stack-testnet-walkthrough.md.

Working with a single contract

You don't need to build or test the whole workspace to work on one contract. Each crate can be built and tested in isolation with Cargo's -p <crate> flag, using the crate name from its Cargo.toml (allowlist-token, denylist-gate, or jurisdiction-flag):

# Test just one contract
cargo test -p allowlist-token

# Lint just one contract
cargo clippy -p denylist-gate --all-targets -- -D warnings

# Build just one contract to wasm
cargo build -p jurisdiction-flag --target wasm32v1-none --release

Architecture note

These contracts are building blocks, not standalone products. They're meant to be composed into a real token or RWA wrapper contract via cross-contract calls — for example, a token's transfer function calling denylist-gate.check(address) before moving funds, or checking jurisdiction-flag.is_permitted_jurisdiction(address, allowed_codes) before permitting a transaction. See /examples/denylist-gate-consumer for a worked example of that pattern. allowlist-token is the one exception — it's a thin wrapper you can deploy in front of an existing SEP-41 token — but even it delegates the real transfer to the underlying token contract rather than reimplementing token logic itself. The wrapper cannot stop a holder from calling that underlying contract directly: the underlying token must enforce wrapper-only transfers, or the asset must use a custody design that prevents holders from transferring it outside the wrapper. SAC AUTH_REQUIRED/AUTH_REVOCABLE flags alone do not enforce a caller-contract restriction. See the allowlist-token module documentation and threat model.

Choosing which primitives to compose

With nine contracts now available, the decision of which primitives to use depends on your use case. See docs/PRIMITIVE_SELECTION_GUIDE.md for profiles of common RWA and stablecoin use cases, and which primitive combinations fit each.

Migrating from hand-rolled compliance

If you already have allowlist, denylist, or jurisdiction checks baked into your own token contract, see docs/MIGRATION.md for a step-by-step guide to replacing them with these primitives — including mapping your existing checks, deployment & wiring, data backfill, and rollback.

Contributing

See CONTRIBUTING.md for the fork → branch → PR flow, and GOVERNANCE.md for how issues are triaged and labeled. This repo is part of the Drips Wave Stellar Program, and issues are labeled by complexity (complexity: trivial, complexity: medium, complexity: high) so you can find something that matches how deep you want to go — issues tagged good first issue are a good place to start.

Security

See SECURITY.md for how to report a vulnerability.

License

MIT

About

On-chain compliance primitives for Stellar — allowlist/denylist token gating and jurisdiction checks for RWA and stablecoin issuers, built on Soroban.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages