Intent-based swap-capable cross-chain bridge.
- Corridor 1 (v1, spec frozen): Arc → Stellar. See
xebra-spec_051150 (1).md. - Corridor 2 (this build): Stellar → Solana, with Stellar acting as the settlement hub. Two rails: a direct CCTP USDC leg and the intent/escrow/solver swap leg from corridor 1, auto-routed. See
docs/architecture.mdfor the full design.
apps/
web/ # Next.js frontend (all corridors, both rails)
api/ # tRPC + REST facade over Postgres — the only thing the frontend talks to
solver/ # multi-chain fill bot (chain-adapter composed per corridor)
cctp-relay/ # CCTP mint-relay service (gas-sponsors the Solana receiveMessage call)
arbiter-service/ # KMS-backed resolve() submitter for the v1 admin-arbiter role
projector/ # Kafka->DB projector — populates packages/db from the event backbone
indexer-arc/ # Arc (EVM) chain watcher
indexer-stellar/ # Stellar (classic + Soroban) chain watcher
indexer-solana/ # Solana chain watcher
packages/
intent-schema/ # IntentV2 schema, arc-v1 adapter, canonical hashing
router-core/ # CCTP-direct vs intent/swap routing rule (shared frontend+backend)
chain-adapters/ # Watch/Fill/Claim adapters per chain, composed by apps/solver
cctp-client/ # shared CCTP burn/attest/mint client (used by solver + relay)
arbiter-signer/ # KMS-backed EVM (secp256k1) + Stellar (Ed25519) signer adapters
db/ # Drizzle schema + migrations, shared Postgres access
contracts/
arc-evm/ # XebraEscrow.sol — Arc-side escrow (v1 spec, frozen)
stellar-soroban/ # XebraEscrow (Soroban) — Stellar-as-source escrow (new)
infra/
terraform/ # IaC for staging/prod (ECS Fargate, RDS, ElastiCache, Redpanda, KMS)
docker-compose.yml # local dev: anvil, Stellar quickstart, solana-test-validator, Postgres, Redis, Redpanda
docs/
architecture.md # full design doc (source of truth alongside the spec)
| Layer | Choice | Why |
|---|---|---|
| Monorepo | pnpm workspaces + Turborepo | task-graph caching across many small TS services + non-JS contract packages |
| Contracts (Arc) | Solidity + Foundry | unchanged from v1 spec |
| Contracts (Stellar) | Rust + soroban-sdk |
native require_auth, no bespoke signature scheme needed |
| Solana delivery | SPL Memo + SPL Transfer (no bespoke program) | falsifiability comes from the public tx, not a program attesting to it |
| Backend language | TypeScript (Node 22) | one language across api/solver/relay/indexers/frontend |
| API | tRPC (+ thin REST/OpenAPI facade) | end-to-end types frontend↔backend, REST facade for non-TS consumers |
| DB / ORM | Postgres + Drizzle | queryable mirror of on-chain state; inspectable SQL, no query-engine binary |
| Event backbone | Redpanda (Kafka API) | durable, replayable multi-consumer event stream |
| Job queue | BullMQ + Redis | idempotent retryable actions (CCTP mint submission, claim/finalize, resolve) |
| Frontend | Next.js (App Router) | corridor selector, status dashboard, shareable /intent/[hash] verification pages |
| Arbiter custody | AWS KMS (Ed25519 + secp256k1) | no raw hot key |
| Compute | ECS Fargate | small, stateless service count — no K8s scheduling need |
| IaC | Terraform | |
| Observability | pino + OpenTelemetry → Grafana Cloud | |
| Lint/format | Biome | single fast binary, replaces ESLint+Prettier |
pnpm install
docker compose up -d # anvil, stellar quickstart, solana-test-validator, postgres, redis, redpanda
pnpm db:migrate
pnpm devSee docs/architecture.md for the phased delivery plan. Nothing here is deployed to a public network yet. Progress against that plan:
Built and verified (compiled/tested; several pieces run live — see each package's own README for exactly what):
-
contracts/arc-evm— full XebraEscrow.sol, 17 Foundry tests incl. both dispute outcomes. -
contracts/stellar-soroban— full Stellar-source XebraEscrow, 14 tests, release wasm builds. -
packages/intent-schema,router-core,chain-adapters,cctp-client,event-bus,db— all with real logic and tests;chain-adapters' Arc log decoding is proven against a real anvil-deployed contract (arc/decode.live.test.ts);db's schema has been migrated onto and queried against a live Postgres.chain-adapters' Soroban event decoding (decode-soroban-events.ts) is now also proven against real testnet events (seescripts/e2e-demo/) — that live check caught and fixed a real bug (event topics are snake_case, e.g.intent_opened, not the PascalCaseIntentOpenedthe original code guessed), which had been silently dropping every Soroban event indexer-stellar would ever see. -
apps/api— tRPC + Postgres, run live end-to-end against a seeded database. -
apps/cctp-relay,apps/indexer-arc,apps/indexer-stellar,apps/indexer-solana,apps/solver— real orchestration logic (each unit-tested with injected/mocked chain clients), thin live wiring around it. Not yet run end-to-end against a live Solana validator or Redpanda cluster; the Soroban RPC piece specifically now has live-verified decoding (see above). -
apps/web— Next.js frontend, produces a real successful production build (pnpm build), not just a typecheck; two build-breaking issues (a wagmi dependency-bundling failure, an SSRwindowcrash) were found and fixed by actually running it rather than trusting the code by inspection. Seeapps/web/README.md. -
packages/arbiter-signer— KMS-backed EVM (secp256k1) + Stellar (Ed25519) signer adapters for the v1 arbiter. No real KMS available, so tested against genuine keypairs/signatures via@noble/curvesstanding in for KMS responses — real DER parsing, real recovery-bit math, real low-S normalization, not mocked success responses. -
apps/projector— consumes the event backbone and populatespackages/db'sintents,escrow_events, and nowclaims(project-claim.ts: solver address, delivered amount, bond, challenge status/timestamps — the same Arc-vs-Soroban payload-shape split asproject-intent-opened.ts). Verified against a live Postgres (podman): inserted a realIntentOpenedevent, confirmed idempotent redelivery, advanced status via a later event, and — new — projected a fullclaimsrow throughclaimed -> challenged -> resolvedand asserted each field at each step.apps/api/apps/solverstill read a manual seed / raw events rather than this DB (a real remaining gap, unchanged). -
apps/arbiter-service— KMS-backedresolve()submitter for both escrows, wired end-to-end for both corridors:lookupClaim(the join this file's own doc comment named as blocking it) against the now-populatedclaimstable,verifyClaimAgainstDestinationChaindispatching to either a real fetched-and-parsed Solana tx (@xebra/chain-adapters'verifyDelivery) or a real fetched Horizon payment (verify-stellar-fulfillment.ts, new), thendecideClaimValidityand the realresolveOnArc/resolveOnSorobansubmission. The Stellar-side asset check doesn't need a reverse lookup from the intent's one-waysha256(assetCode||issuer)hash — it hashes the actual payment's asset the same way and compares hashes, sidestepping a schema gap that would otherwise require a new asset registry. Aarbiter_pending_challengesgauge tracks challenges that couldn't be resolved yet (missing claim row or unverifiable destination). -
contracts/arc-evm/script/DisputeWalkthrough.s.sol— live dispute walkthrough (false delivery claim -> challenge -> arbiterresolve(false)-> refund/slash), asserted on-chain viarequire()and run successfully against a real anvil instance, not just narrated. The Soroban side already has an equivalent case (dispute_resolve_invalid_refunds_user_slashes_liarincontracts/stellar-soroban/src/test.rs) from the Phase 1 work. -
infra/terraform— full staging/production IaC:network(VPC, public/private subnets, NAT),database(RDS Postgres 17),cache(ElastiCache Redis),kms(Ed25519 + secp256k1 arbiter keys),ecs-service(reusable Fargate module), ECR repos per service, ALBs forapps/apiandapps/web, and root wiring for all nine services.terraform validatepasses for real against thehashicorp/awsprovider (a genuine bug — the pinned 5.x provider doesn't recognizeECC_NIST_EDWARDS25519as a valid KMS key spec — was caught this way and fixed by moving to provider~> 6.60, which does). Noplan/applywas run (no real AWS credentials in this environment);environments/*.tfvars.exampledocuments what a real deploy needs. -
packages/observability— shared OTel wiring (startObservability,registerPolledGauge) for traces/metrics over OTLP/HTTP to Grafana Cloud, no auto-instrumentation. Live-verified: a test spins up a real local HTTP server standing in for the OTLP gateway and asserts the SDK actually POSTs to/v1/metrics, not just that it constructs without throwing. Wired into all eight backend services (api,solver,cctp-relay,arbiter-service,projector,indexer-arc,indexer-stellar,indexer-solana);apps/webisn't wired yet (Next.js needs a different mechanism —instrumentation.ts/@vercel/otel— not this package directly). All five day-one alerts from docs/architecture.md §11 now have a real signal behind them: relay SOL balance (cctp-relay, polled gauge), solver per-mint inventory (solver— this also replaced a hardcodedgetBalance: async () => 2n ** 64nplaceholder with a real SPL token-account balance check), CCTP attestation-pending age (cctp-relay's worker, using a newcreatedAtfield onRelayJobState), unclaimed-intents-nearing-window-close (projector, a real DB query with a unit-tested pure counting function), and arbiter KMS failures (arbiter-service, counter around the startup KMS calls — the resolve-path Sign calls aren't reachable yet, same gap as before).
apps/web is now wired too, via Next.js's instrumentation.ts hook (register(), gated to the nodejs runtime, dynamically importing @xebra/observability rather than a top-level import) — confirmed with a real pnpm build (exit 0, same class of pre-existing non-fatal webpack "critical dependency" warnings the wagmi/stellar-sdk chain already produced, nothing new broken).
scripts/e2e-demo/— live end-to-end run of the Stellar-source escrow's full happy path (open -> claim -> finalize) against real Stellar testnet infrastructure, not a local simulator: a contract deployed to testnet, a realrequire_auth_for_argssignature verified by a live host (closing an open risk flagged since Phase 1 — this had previously only run under Rust'smock_all_auths()test harness), a real 60-second challenge window waited out for real, andfinalizecalled by a third-party identity to prove it's genuinely permissionless. All five transactions are linked and explained inscripts/e2e-demo/README.md, verifiable independently on stellar.expert. The Solana devnet leg (real delivery tx with a memo) is implemented and ready to run (01-solana-setup.ts) but didn't complete in this environment — devnet's public airdrop faucet returned a hard daily rate limit on every attempt, documented in that README along with the placeholder values used in its place and exactly what to substitute once SOL is available.
Not yet built: nothing from the original hardening list — the Solana leg of the e2e demo above is the one item blocked by an external constraint (a faucet rate limit) rather than by code.