Account Demolisher
Concepts

Architecture

The module map, how a request flows through the app, and where the trust boundaries fall.

The app is a Next.js 16 application. One page drives the whole closure, backed by a state machine, a pure plan generator, and an executor that walks the plan on chain.

Request flow

A closure runs through /demolish, which is a React page wired to an XState machine.

/demolish (React)
  └─ pageFlowMachine            src/lib/orchestrator/page-flow-machine.ts
       ├─ discover
       │    ├─ auditAccount               Horizon
       │    ├─ DirectContractProvider     on-chain DeFi discovery
       │    └─ enumerateAllowances        Soroban RPC
       ├─ preview
       │    ├─ resolveCreditPaths
       │    ├─ generatePlan               pure
       │    ├─ hydratePlanTransactions
       │    └─ simulateNode               real Soroban RPC simulation
       └─ execute
            └─ executePlanTreeOnChain     src/lib/orchestrator/executor.ts

The machine's states are idle, discovering, previewing, awaiting_confirmation, executing, succeeded, failed, and cancelled.

Module map

AreaPathResponsibility
Flow machinesrc/lib/orchestrator/page-flow-machine.tsScreen sequencing, discovery and preview actors
Executorsrc/lib/orchestrator/executor.tsThe on-chain topological walk
Auditsrc/lib/stellar/account-audit.tsAccount state, mergeability, multisig detection
Discoverysrc/lib/adapters/One subdirectory per protocol, plus positions/
Plansrc/lib/plan/generator, tree, classic-batcher, simulator, hydration
Walletsrc/lib/wallet/Connector abstraction over Stellar Wallets Kit
Mediatorsrc/lib/mediator/Client, forward construction, envelope validator
Multisigsrc/lib/multisig/Signing requests and partial-XDR merging
Serversrc/server/Signing relay, mediator secret, rate limiting
Configsrc/lib/config/Networks, contracts, CSP, environment

Server surface

Three route groups, and nothing else:

RoutePurpose
/api/mediator/signMints a flow and co-signs one validated forward envelope
/api/plan, /api/plan/[id], /api/plan/[id]/sign, /api/plan/[id]/eventsThe multisig signing relay
/api/soroswapServer-side proxy to the Soroswap aggregator

The server never receives a user key, never builds a transaction on a user's behalf, and never submits one.

Purity boundaries

Two core pieces take no network calls at all, which is what makes them testable and deterministic:

  • generatePlan is pure over (audit, positions, allowances, destination, options). Its only impurity is reading the clock to estimate a Blend backstop unlock date, and tests pass a fixed value.
  • batchClassicDemolition is a pure function from an account audit to an ordered list of classic batches.

Everything that touches the network sits outside those two.

Trust boundaries

BoundaryTrust levelWhy
BrowserHighestThe user's key lives here and nowhere else
ServerBoundedHolds one HMAC master secret, never a user key; its powers are constrained by strict validators
Horizon and Soroban RPCUntrusted for integrityReads are treated as hints and re-verified before signing
WalletSeparate trusted signerShows its own confirmation and signs on its own
On-chain contractsUntrustedChecked against the allow-list immediately before every signature

The security model covers what defends each boundary.

Rebuilding from fresh state

A plan you review is not the transaction that gets submitted. Before each classic phase the executor re-reads the account, re-runs the audit, re-resolves credit paths, and re-batches from that fresh state. Soroban nodes are rebuilt and re-simulated the same way.

This is what makes a stale sequence number self-correcting, and what stops a read from a hostile RPC at preview time deciding what you actually sign.

The docs site

This documentation is a separate Next.js app in docs/, tracked in the same repository. It runs on port 3020 in development.

On this page