The Stellar-native layer of Unbound: trustline and issuer checks, strict-send path quotes, unsigned transaction builders, submission, ledger reconciliation, and the one Soroban contract (the redemption escrow).
Part of Unbound - turn stranded value into something usable.
Store credit is usually stuck inside one retailer. Unbound lets a person exchange store credit that a retailer has genuinely issued and agreed to honor for other Stellar assets, at a real order-book quote that they sign on their own device, or redeem it with the issuer.
- Issuer-authorized only. A verified merchant signs a redemption policy with its Stellar issuer key and must authorize every holder's trustline. No issuer, no usable credit; arbitrary gift cards are out of scope.
- No fake liquidity. Quotes come from real Stellar path payments, expire and carry a minimum-output floor. No route means no quote. Unbound never promises a price, liquidity or fiat cash-out.
- Ledger-truthful. Nothing is shown as settled until the ledger confirms it; unknown outcomes reconcile by hash.
- Testnet only today. Mainnet needs an external security review and legal/compliance work that has not happened.
Unbound-stellar owns everything that touches the ledger: verifying issuer accounts and flags, checking trustlines and
authorization, finding strict-send paths with a slippage cap and expiry, building unsigned transactions (settlement,
trustline authorization, issuance, redemption return), classifying submissions, and indexing confirmations into
versioned reconciliation events for Unbound-core. It ships as a TypeScript SDK plus a gateway HTTP service and a
chain worker. Only the Testnet passphrase is accepted.
It also owns Unbound's only smart contract, contracts/redemption-escrow (Rust,
soroban-sdk 29.0.1): returned credit is held until the issuer confirms (released to the issuer), rejects (refunded),
or the deadline passes (anyone can trigger the refund). Why this is the one place Soroban is used:
ADR 0002. The TypeScript side (SorobanRedemptionEscrow) builds
unsigned, simulated invocations and decodes records and events, tested against golden XDR produced by the Rust tests.
Unbound is four independently tested repositories with one owner per boundary. This one is highlighted.
flowchart LR
M["Unbound-mobile<br/>Flutter wallet"]
S["Unbound-site<br/>landing + merchant console"]
C["Unbound-core<br/>HTTP API v1, policy, intents, audit"]
G["Unbound-stellar<br/>SDK, gateway service, chain worker"]
N[("Stellar Testnet<br/>assets, trustlines, DEX,<br/>Soroban redemption escrow")]
M -- "quotes, intents, signed XDR" --> C
S -- "merchant auth, redemption queue" --> C
C -- "quote / build / submit" --> G
G -- "Horizon + Soroban RPC" --> N
G -- "reconciliation events" --> C
style G fill:#7537F5,stroke:#10092E,color:#ffffff
Clients never build Stellar transactions, and core never reimplements ledger logic. Details:
docs/ARCHITECTURE.md and docs/STELLAR_PROTOCOL.md.
Requires Node 22+.
npm ci
npm run check # lint + typecheck + tests + build
npx tsx scripts/m1-demo.ts # offline demo: issue, quote, settle, and a refused no-trustline route
# the Soroban contract (Rust stable + wasm32v1-none)
cd contracts
cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test
SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1 cargo build --release --target wasm32v1-none
DRY_RUN=1 ../scripts/deploy-testnet.sh # prints the stellar-cli deploy steps (the real run is recorded in docs/DEPLOYMENT.md)The demo runs on SimulatedLedger, an in-memory test adapter that verifies real XDR signatures, sequence numbers
and time bounds; its hashes are not Testnet hashes. The gateway (npm run gateway, needs GATEWAY_TOKEN) and worker
(npm run worker, needs CORE_URL + CORE_TOKEN) are written and tested over HTTP but have never been run
against Horizon (see .env.example).
Legend: Built = implemented and covered by automated tests in this repo. Not yet = not started or not verified.
| Capability | Status |
|---|---|
| Exact amount math (integer base units, 7 dp, no floats) | Built |
| Verified-issuer registry, fail-closed on untrusted or malformed assets; issuer flag and trustline checks | Built (on the simulator) |
| Unsigned builders: issuer setup, authorize trustline, issue credit, change trust, redemption return | Built (on the simulator) |
| Strict-send quotes: expiry, slippage cap (<= 10%), min-output floor, tamper-evident id, approved-route filter | Built |
| Settlement builder re-validating freshness, balances, authorization and destination trustline | Built |
| Submit classification (CONFIRMED / FAILED / RECONCILING), reconcile by hash, cursor indexer with replay | Built |
| Gateway service for core (quote, settlement, redemption return, submit, balances) with bearer auth | Built, tested over HTTP on the simulator |
| Chain worker (pending-hash watcher, event sink with retry and dead-letter file) | Built, tested with a stub core |
Horizon adapters (HorizonClient) |
Mapping-tested, and run against live Testnet on 2026-11-04 (paths, submit, transaction lookup); that run found and fixed the SDK ledger link-function bug |
| Soroban redemption escrow contract: open / confirm / reject / expire_refund, single-use ids, bounded deadlines, issuer = SAC admin, pause for new returns only, events | Built: 22 behaviour and failure-path tests, a seeded invariant test (escrow balance == locked == open returns) and a golden-fixture test; wasm builds in CI |
SorobanRedemptionEscrow adapter, gateway /gateway/v1/escrow/* endpoints, golden Rust -> TypeScript XDR fixtures |
Built (on FixtureSorobanRpc); StellarRpcSorobanPort run live against Soroban RPC: prepared open_return / confirm / reject / expire_refund landed on Testnet |
scripts/deploy-testnet.sh (stellar-cli build, deploy, init, admin check, issuer authorize) |
Run for real on 2026-11-04: contract CC4ETHM44IUJ4GDRT57VH4OSNEMPEGUKQHQJCUQDIU2LKIAKZYIYOP6C (see docs/DEPLOYMENT.md) |
Real Testnet hashes: deploy, escrow smoke (scripts/testnet-smoke.ts), core-driven escrow flows, DEX exchange |
Recorded in contracts/deployments/*.json and docs/DEPLOYMENT.md |
Network-anchored gateway clock (LedgerClock): a host clock 95 s slow made every settlement tx_too_late |
Built; found live |
| Account-stream indexing, escrow event indexing from RPC, liquidity pools, reserves/liabilities | Not yet |
| Published npm package, Docker image, metrics endpoint, fee sponsorship, external audit of the contract | Not yet |
- TypeScript API exported from
src/index.ts. schemas/reconciliation-event.v1.json(generated from zod withnpm run schemas, snapshot-tested) andfixtures/reconciliation-event.confirmed.v1.json- consumed byUnbound-core.fixtures/gateway/*.json- gateway contract (incl. escrow endpoints), byte-identical to core's copy; deterministic XDR fixtures for the mobile decoder (now incl. escrowopen_return/expire_refund).fixtures/soroban/redemption-escrow.v1.json- golden contract records and events generated by the Rust tests.
| Repository | Owns | Stack | CI |
|---|---|---|---|
| Unbound-core | Backend API: issuers, policies, intents, redemptions, audit | TypeScript, Node 22, SQLite | |
| Unbound-stellar (this repo) | Stellar SDK, gateway service, chain worker, Soroban redemption escrow | TypeScript, @stellar/stellar-sdk, Rust/Soroban | |
| Unbound-site | Landing page, partner docs and merchant console | Next.js 16, React 19, Tailwind 4 | |
| Unbound-mobile | Flutter consumer wallet | Flutter, Dart, Riverpod |
Contributions are welcome. Start with CONTRIBUTING.md, then pick from the scoped, testable ideas in
docs/CONTRIBUTOR_ISSUES.md. Unbound intends to take part in
Drips Wave; that is subject to Drips' review and
limits, and no rewards are promised. Wave issues are opened only once their dependencies and acceptance tests are ready.
Everyone agrees to the Code of Conduct.
Please report vulnerabilities privately (Security tab, Report a vulnerability) as described in
SECURITY.md. The threat model is in docs/SECURITY.md. Testnet only: never use
real funds, real customer data or Mainnet keys with this code.
docs/PRD.md (product) · docs/SRD.md (requirements, API, states) ·
docs/ARCHITECTURE.md · docs/TECHNICAL_BUILD.md ·
docs/ROADMAP.md · docs/BRAND.md · AGENTS.md (rules for coding agents).
The canonical copy of the shared docs lives in Unbound-core.
MIT. The Unbound logo in brand/ is the project's approved identity and is shared by all four
repositories; it has not been trademark-cleared.