Consolidated architecture for LumenWipe, an open-source tool that cleanly closes a Stellar account and recovers its locked reserves. Reference implementation extended by this project: stellar.expert/demolisher/public by Orbit Lens.
Contents
- What this is
- The problem
- How a Stellar account closes
- System architecture
- Data sources, and why we run no indexer
- Frontend architecture
- The API service
- The execution plan
- Closing positions: classic and Soroban DeFi
- Asset conversion and routing
- The mediator account flow for exchanges
- Allowance inspection
- Security model
- Trust minimization and decentralization
- Infrastructure and deployment
- User protection and privacy
- Testing strategy
- Maintenance after launch
- Delivery plan
- Traction
- Technology stack and standards
- Failure modes and recovery
- Open questions and known risks
- Glossary
- References
- Executive summary: a one-page overview for a first read.
- Community and communications: building in the open, update cadence, and decentralized social presence.
1. What this is
LumenWipe is a guided, non-custodial tool that walks a user through closing a Stellar account from start to finish. It removes everything that holds an account open, converts leftover assets to XLM, and merges the account into a destination address, returning the locked reserves to the user. “Closing” a Stellar account is not a single operation. An account can only be merged once it holds no subentries apart from its signers and sponsors no other account. Getting there means unwinding whatever the account accumulated over its life: trustlines, open DEX offers, data entries, extra signers, liquidity pool shares, and positions in DeFi protocols such as Blend, Aquarius, Soroswap, Phoenix, and FxDAO. Each of those steps is its own transaction, with its own ordering constraints and its own failure modes. The project extends the public-domain stellar.expert/demolisher/public tool built by Orbit Lens. That tool handles the classic case well: it cancels offers, sells assets on the SDEX, removes trustlines and data entries, works with multisig accounts, and can merge into exchange addresses through an intermediary account. It does not support Soroban, so any account with a Blend loan, an Aquarius LP position, or a Soroswap pair share cannot be closed with it today. This project keeps the parts that work, rebuilds them on the current Stellar stack, and adds full Soroban and DeFi parity, an API-first backend that builds the wind-down, an allowance inspector, and a production-grade UX designed for irreversible actions. Beyond the guided UI, two things widen who can use it: sponsored fees close accounts that hold only their locked reserves and cannot pay their own transaction fees (Section 8.1), and a REST API plus a TypeScript SDK let wallets and platforms drive the same wind-down programmatically (Section 7.3). An API service builds every unsigned transaction; the browser independently verifies each one against the user’s own stated intent before it signs, and your account’s secret keys never reach a server. The API holds no user funds and no user keys. Its only signing key is the shared exchange mediator, which it uses solely to co-sign the forwarding payment to an exchange (see section 11). The codebase is a Bun-workspaces monorepo: a NestJS API service (the product), a thin Next.js web client, and two published packages - a TypeScript SDK (a fetch client for the API) and a shared types package. The web reaches the API only through a server-side proxy, so a browser never holds an API key. Core stack at a glance:2. The problem
Stellar has more than ten million accounts on mainnet, and a large share of them are stale, abandoned, or effectively locked. Two structural facts create the problem. First, every account locks XLM in reserve. The base reserve is currently 0.5 XLM (a network-voted parameter). Since CAP-33, an account’s minimum balance is(2 + numSubEntries + numSponsoring - numSponsored) × base reserve: two base reserves for the account itself, one per subentry it owns (each trustline, offer, data entry, and extra signer), plus one per entry it sponsors for others, minus one per entry of its own that someone else sponsors. A pool-share trustline counts as two base reserves. So an account with four trustlines, two offers, one data entry, and one extra signer locks (2 + 8) * 0.5 = 5 XLM that the user cannot spend until the entries are removed. Across millions of accounts, this is a meaningful amount of capital frozen in the ledger.
Second, closing an account cleanly is a manual, multi-step process that most users cannot perform. Any leftover entry causes the final ACCOUNT_MERGE to fail with ACCOUNT_MERGE_HAS_SUB_ENTRIES. A user has to know to cancel every offer, exit every DeFi position, sell every asset, remove every trustline, and clear every data entry, in a valid order, before the merge will succeed. Miss one and the merge reverts. (Extra signers are the one kind of subentry that does not block the merge: the protocol’s check excludes them, and they are deleted with the account.)
Centralized exchanges make it worse. No major exchange supports ACCOUNT_MERGE. A user who wants to send their remaining XLM to an exchange cannot merge directly into a deposit address, so the final 1 XLM minimum balance stays frozen on the ledger. The reference demolisher solves this with an intermediary account, and this project keeps that approach.
Three groups of users feel this most: individuals consolidating or abandoning wallets, exchanges that need to help users recover funds, and DeFi users with open positions across Stellar protocols. The last group has no tool today, because the existing demolisher has no Soroban support.
3. How a Stellar account closes
ACCOUNT_MERGE transfers the entire XLM balance of the source account to a destination and deletes the source account from the ledger. The protocol enforces strict preconditions. The pre-flight analysis in this tool exists to detect and clear every one of them before it builds a merge transaction.
The merge fails with one of these result codes if a precondition is unmet:
The pre-flight checks map directly onto these codes. Sponsorship detection and revocation resolve
ACCOUNT_MERGE_IS_SPONSOR for every sponsored entry kind that can be revoked. Subentry enumeration and removal prevent ACCOUNT_MERGE_HAS_SUB_ENTRIES. Destination verification prevents ACCOUNT_MERGE_NO_ACCOUNT. The tool never submits a merge it expects to fail.
Note that being a claimant of a claimable balance does not block the merge, but sponsoring one does, because the sponsor carries its reserve (one base reserve per claimant, not per balance). An account that created claimable balances is their sponsor unless the sponsorship was later transferred, so those must be resolved first.
Claimable-balance sponsorships cannot be self-revoked. CAP-33’s RevokeSponsorshipOp fails
with REVOKE_SPONSORSHIP_ONLY_TRANSFERABLE when applied to a CLAIMABLE_BALANCE ledger entry
unless a cooperating new sponsor is sandwiched around it via BeginSponsoringFutureReserves /
EndSponsoringFutureReserves in the same transaction - every other sponsorable entry kind
(account, trustline, offer, data entry, signer) instead reverts to its own owning account’s
default sponsorship, which is what this tool’s REVOKE_SPONSORSHIP step relies on. A guided
close has no third party willing to become that new sponsor, so a claimable balance this account
sponsors is a permanent blocker until a claimant claims it (removing the entry, and its
sponsorship, entirely) - there is no self-service remediation. See issue #72’s implementation
plan for the full CAP-33 citation.
4. System architecture
The system has three layers: an API service that reads account state, aggregates data, and builds every unsigned transaction (and co-signs one thing, the exchange forwarding payment); a browser client that verifies each unsigned transaction against the user’s own choices and signs it locally; and the Stellar network plus the external data services the API reads from. The trust boundary is still the browser, but the guarantee is sharpened: a user’s account keys and signing live entirely on the client side, and the browser never signs bytes it has not first verified. The API builds the transaction, but a client-sideverify() decides whether it gets signed, and it checks the transaction against the user’s own stated intent rather than trusting the API’s word. The API’s only key is the shared mediator, which can co-sign the exchange forwarding payment but cannot sign for a user’s account, change a destination, or move a user’s funds.
verify() passes) makes it valid, and its only signature of its own is the shared mediator’s co-signature on the exchange forwarding payment (section 11). And every external read source is pluggable: RPC, the indexer, the routing API, and the DeFi position API can each be swapped for another provider without touching the transaction logic.
5. Data sources, and why we run no indexer
Building LumenWipe requires reading account state, and that state lives in two places the same way the network splits its tooling: classic ledger state, and live or Soroban state. A practical constraint shapes the whole data design. Stellar RPC’sgetLedgerEntries can only return entries whose keys you already know. You pass it serialized LedgerKey values (up to 200 per request) and it returns those exact entries. It has no scan, filter, or “list all trustlines for this account” capability. To build a trustline LedgerKey you already need the asset; to read an offer you already need the offer ID. RPC alone therefore cannot tell you what an unknown account holds.
Enumerating an account’s subentries (every trustline, offer, data entry, claimable balance, pool share, signer, and sponsorship relationship) requires an indexer. The project takes a clear position here: we do not build or operate an indexer. Stellar RPC is used wherever it serves the read, and everything it cannot serve comes from a single Horizon-compatible endpoint. This is not a preference: getAccount returns only the sequence number and base reserve, and getLedgerEntries fetches a ledger entry whose key is already known but cannot enumerate an account’s trustlines or offers. Horizon’s deprecation in favor of Stellar RPC does not change that, because its named successor cannot do this job. The provider is set by configuration (PATH_ROUTING_API_*), so moving between SDF’s public instance, Blockdaemon, Validation Cloud, QuickNode or a self-hosted Horizon is a config change rather than a code change. SDF reduced its hosted Horizon to one year of history in August 2024 and steers integrators toward Stellar RPC plus ecosystem data services; the reads this tool takes from Horizon-compatible endpoints are current-state queries, unaffected by that history truncation. Running a bespoke indexer (Captive Core, Galexie, a database) is not the problem this project exists to solve, and it would be operational weight with no payoff for the tool.
Instead the tool reads from existing, production-grade sources through pluggable adapters:
The split is deliberate. An indexer answers “what does this account hold”. RPC answers “what is the exact current state of this specific entry, right now, and will this transaction succeed”. The tool enumerates with the indexer, then re-reads each entry over RPC immediately before building the transaction that touches it, so it never signs a transaction based on stale enumeration data. As a completeness check, the enumeration result is reconciled against the account’s
numSubEntries counter from the live AccountEntry: if the counts disagree, the tool surfaces a blocker instead of building a plan that would miss an entry.
Accounts of any age
Account age never limits this design, and that is worth stating precisely because Stellar RPC does have a retention window. The window (at most 7 days) applies only to history-shaped methods:getTransactions, getTransaction, and getEvents. It does not apply to getLedgerEntries, which reads the current ledger snapshot: a trustline created in 2015 and a trustline created yesterday are the same read. Closing an account needs no transaction history at all; it needs current state, which RPC serves for any account regardless of age, and enumeration, which the indexer serves from full history. The one age-correlated wrinkle is Soroban state archival: a long-dormant account’s contract entries (a DeFi position, a token balance) may have expired to the archive, where a plain read no longer sees them. The tool detects archived entries and inserts a RestoreFootprint step before the exit that needs them (Section 22). Classic entries never archive.
Data freshness and consistency
DeFi position data is a snapshot, and acting on a stale snapshot would build a wrong exit. The position API returns freshness metadata with every response: a staleness value in seconds, the last indexed ledger, and a partial-result flag when some protocols could not be read. The tool uses this directly. If position data is older than a short threshold it refreshes before building the plan, and it shows the ledger and staleness so the user knows how fresh the view is. Consistency across the boundary between enumeration and execution is the harder problem. Enumeration says a trustline or position exists; the exact amount can move before the user signs. The tool’s guarantee is the live re-read: every transaction is built from a freshgetLedgerEntries read of the specific entries it touches, taken immediately before construction, not from the enumeration snapshot, and Soroban exits are simulated against current state before signing. Enumeration decides what to do; a live read decides the exact parameters. That keeps the tool from acting on data that moved.
6. Frontend architecture
The frontend is a Next.js application in TypeScript, and a thin client of the API. It owns no transaction construction: it requests unsigned transactions from the API, verifies each one locally, signs it, and submits it back through the API. Ano-restricted-imports boundary lint forbids the web from importing any transaction-building code, so this stays true. It holds the entire flow as an explicit state machine so a user can leave and resume without losing progress, which matters because a full wind-down is several sequential transactions, not one.
The user-facing flow asks for one thing at a time, in the order the work actually needs it. Entry collects only the source account’s public key, nothing else - supplied either by pasting it or by connecting a wallet. The analyze stage reads the account and presents a grouped accordion preview of everything that has to be unwound, and for each non-XLM balance the user makes a per-asset decision: swap it to XLM when a route exists, return it to its issuer, or transfer it intact to an account they name. The return-to-issuer choice is always an explicit confirmation, never a default, and the tool never labels it as a conversion; a transfer is likewise never a default, since it is the one choice that cannot be resolved without an address the user supplies. The destination address and an optional memo are entered last, on the same screen, once the user has decided what the close will do. Exchange detection happens at that point, because it depends on the destination. Only then does the tool build and run the close, and the completion page shows a grouped summary of what happened to each balance and where the reserves went.
6.1 State machine
6.2 Client-side verification (the trust anchor)
The transaction builder lives in the API, not the browser (Section 7). What runs client-side is its counterpart:verify(), the trust anchor. Before the browser signs any API-built transaction, it decodes the XDR and asserts, against the user’s own choices and a bundled exchange registry, that the transaction does exactly what the user asked and nothing more - a merge only to the stated destination, or - for an exchange - into an intermediary that forwards the observed balance straight on to that destination in the same transaction, payments only as return-to-issuer, the mediator forward, or a transfer whose asset, destination and amount all match a choice the user themselves made - and, in every case, sent by the account being closed rather than by some other account the user’s key happens to sign for, conversions to self or native with a positive destination minimum, only removals of trustlines, data entries, and offers, SetOptions that never adds a signer or raises thresholds and only ever removes a signer that is genuinely on the account, a matching memo value and type, and no unrecognized operation. Any mismatch aborts before signing. Most of what verify() checks - destination, memo, the trustlines the user chose to claim, and each transfer’s destination - comes from the user’s own inputs, never from the API response, so a compromised API cannot talk the client into diverting funds on those axes. The transfer is the sharpest case: return-to-issuer and the mediator forward are structurally constrained, since the issuer is derivable from the asset and the forward’s destination is the address the user typed, but a transfer’s destination is an arbitrary address - exactly the shape of a fund diversion - and the only thing separating a legitimate one from an attack is that the expected value came from the user. Its amount is checked as a floor rather than an exact figure, because a claim round can legitimately raise the balance between the client’s read and the build; paying more to an already-pinned destination is not a loss, while paying less would mean part of the balance went elsewhere. The one exception is the account’s real signer set, used only to confirm a SetOptions signer removal targets a signer that actually exists: that value is sourced from a separate account-state read (GET /v1/:network/account/:address), the same trust domain as the transaction being verified, so it hardens against transaction-builder bugs and partial compromise rather than a wholly hostile API, which could in principle keep that read and the transaction mutually consistent. The pure core is unit-tested against hostile XDR.
The builder it verifies (server-side) enforces the 100-operations-per-transaction protocol limit and splits oversized work into the fewest transactions that limit allows; for Soroban steps it assembles InvokeHostFunction operations and defers footprint, authorization, and resource fee to RPC simulation.
6.3 Wallet integration and signing
Signing has two paths. The primary path, implemented as the default wallet tab in the ExecutionWizard, is stellar-wallets-kit, which provides a unified interface across Freighter, xBull, Albedo, Rabet, Hana, and WalletConnect; the same connection can also be initiated earlier at account entry, persists to the signing step, and is automatically selected as the active signer if the address and network match. (LOBSTR’s own kit module is disabled because it cannot sign transactions; LOBSTR users are reached via WalletConnect instead.) The application passes an unsigned XDR and receives a signed XDR throughsignTransaction; the underlying private key never enters the application. For Soroban operations the kit also exposes signAuthEntry, though wallet support varies (Freighter, Hana, WalletConnect, and Ledger implement it; several others do not), so the tool builds its Soroban exits with source-account authorization, which the plain signTransaction path covers on every wallet, and reserves signAuthEntry for the cases that genuinely need a separate auth entry. The secondary path is an advanced secret-key mode for users whose keys are not in any wallet. In that mode the key lives only in memory for the duration of the execution session, never in any persisted storage and never in a network request, and is wiped on completion, on abort, on navigation away from the flow, or when the user explicitly clicks “Forget key”. Section 13 details the handling.
7. The API service
The API is a stateless NestJS service, and the product itself. It reads account state, aggregates the data the client cannot efficiently fetch, builds the minimal set of unsigned transactions that close an account, and caches its reads. It runs as its own service, deployed separately from the web (Section 15), and every request carries an API key. It accepts no user keys and holds no user funds, and every transaction it returns is unsigned: only the user’s browser can turn one into something the network will accept. Its one signing key is the shared mediator, which co-signs the exchange forwarding payment only after validating the transaction shape (operation one merges into the mediator, operation two is a payment from the mediator of at least 1 XLM), and cannot change that payment’s destination or amount. If the API were fully compromised it could return a wrong transaction or wrong read data, but the client-sideverify() refuses to sign anything that does not match the user’s intent (Section 6.2), and reads are backed by confirmations and on-chain simulation, so it could never sign for or move a user’s account.
Building the transactions server-side shapes the rest of the design. The API re-reads live on-chain state itself right before it builds, so it never emits a transaction based on stale data. It stays stateless across a multi-round close by re-deriving the remaining work from current state on each call rather than tracking per-user progress, which is why an interrupted close resumes by simply asking again. And it validates every request, rejecting a bad memo or an unsupported destination with a typed error the client relays in plain language.
The web never calls the API directly from the browser; it goes through a server-side proxy that injects the API key, so no key ever reaches the client. The SDK wraps the same surface for programmatic callers (Section 7.3). The REST surface:
Requests are authenticated with an API key and rate-limited per key; the web’s proxy holds the key server-side and applies its own per-IP limit on top, so the shared key can never be turned into an anonymous amplifier.
7.1 DeFi position adapter
The API consumes OctoPos behind one adapter interface, so the rest of the system never sees provider-specific shapes. OctoPos is a funded DeFi Position API in the Stellar ecosystem, and the API builds on it rather than reinventing protocol indexing. The adapter keeps the provider pluggable: it can be pointed at any compatible provider, and if OctoPos is unavailable the tool enters a degraded mode: classic entries process normally, and the user is warned that DeFi positions could not be detected and must be checked manually. OctoPos covers position detection across Blend, Aquarius, Soroswap, Phoenix, and FxDAO, plus native wallet balances, and reports claimable AQUA rewards and pending Phoenix rewards alongside the positions. It also exposes two pieces the tool leans on directly: for unsubscribed addresses it returnsqueryKeys (ready-made ledger keys plus pool and pair metadata) so positions can be read straight over RPC getLedgerEntries without OctoPos storing anything server-side, which fits this tool’s live re-read invariant exactly. One boundary matters for planning: OctoPos serves mainnet only. On testnet the tool discovers DeFi positions through direct contract reads driven by the contract registry, which is the same code path the degraded mode uses, so the fallback stays exercised by every test run.
The provider returns a position payload, an enrichment dictionary (asset symbols, decimals, USD prices and their source, contract names and versions), and a meta block with freshness and confidence fields. The adapter maps these onto one normalized model so the transaction builder sees a single contract:
The adapter uses the authenticated tier where an API key is configured and the public tier otherwise. It sends only the address it was asked to analyze, and it caches only public position data.
7.2 Caching
Read data is cached with short TTLs, keyed by address: positions for tens of seconds, routing for a few seconds (routing is time sensitive), analysis for a few seconds with explicit refresh on user request. The cache holds public, read-only data. It holds no keys and no user identity.7.3 Integration surfaces: API and SDK
The API is the product, and the guided web app is just its first consumer: it drives the whole wind-down through the same public surface any integrator would use, which is the standing proof that surface is complete. The audiences that close accounts at scale are not clicking through a wizard:- REST API: analysis, plan generation, per-round unsigned-transaction building, mediator co-signing, and submission, so a platform can drive a wind-down from its own backend, verify and sign with its own keys, and submit. It is stateless - each call re-derives the remaining work from live on-chain state - so an operator decommissioning a fleet of deposit or payout accounts just calls again, per account and per round.
- TypeScript SDK (
@lumenwipe/sdk): a thin, dependency-light fetch client over that API that does not bundle the Stellar SDK, so a wallet can embed a “close account” flow inside its own UI and signer. A shared@lumenwipe/typespackage keeps request and response types identical across the API, the SDK, and the web.
8. The execution plan
From the analysis the tool generates a deterministic, ordered plan. Same account state, same plan. The order satisfies ledger constraints: you cannot withdraw collateral while a loan is open, you cannot remove a trustline while it holds a balance, and you cannot merge while any subentry remains.- Signer normalization runs first when extra signers exist, so a single key can authorize every later step. It removes each extra signer with
SetOptionsweight 0 and sets the low, medium, and high thresholds to 0/1/1. This step is a usability and efficiency choice, not a merge precondition: the protocol’s subentry check excludes signers, so an account could merge with them in place. Removing them early collapses a multisig flow to one key for the remaining transactions and turns each signer’s 0.5 XLM reserve into spendable balance mid-flow, where it can cover fees. - Steps with more than 100 operations split into batches of 100, the protocol limit per transaction.
- A step that turns out to be a no-op (no offers, no data entries) is skipped, not submitted.
- Soroban steps are one
InvokeHostFunctionper transaction, because each needs its own RPC simulation for footprint, authorization, and resource fee. - The plan is recomputed on resume, so external changes between sessions are reconciled rather than blindly repeated.
InvokeHostFunction, which a transaction may not mix with other operations, so that conversion becomes its own transaction; and work exceeding one transaction’s worth of operations splits across the fewest transactions the 100-operation limit allows. The API re-quotes swap routes at build time, and if an asset has lost its route between analysis and signing it refuses the build with a quote_drifted error naming the asset, rather than emitting a transaction that would fail - or silently re-deciding the asset for the user. Re-deciding it to a return-to-issuer would destroy a balance on the tool’s own initiative, which the “never silently skipped” invariant exists to prevent; the user re-decides that asset themselves, and since the transfer disposition needs no route, losing one no longer forces a choice between burning the balance and abandoning the close. Because the API re-derives the remaining work from live state on every round, these multi-transaction closes need no server-side progress tracking: the client verifies and signs what it is given, submits, and asks for the next round until none remains.
Because a wind-down can be several sequential transactions, a single end-to-end dry run is not always feasible. The tool’s preview approach is two-tiered: a grouped accordion preview up front that gathers everything to be unwound into sections (signers, data entries, offers, positions, and the per-asset dispositions), with the estimated fee and the estimated final XLM that reaches the destination, and a simulation immediately before each signature using simulateTransaction for Soroban steps and a build-and-validate check for classic steps. Any simulation failure is surfaced in plain language before the user is asked to sign, never after. The completion page mirrors the preview: a grouped summary of what happened to each balance, the transactions that ran, and where the reserves went.
8.1 Sponsored fees: closing accounts that cannot pay their own way
The accounts that most need closing are often the ones that technically cannot start. An account sitting at exactly its minimum balance (the bare 1 XLM minimum, or more XLM locked entirely in subentry reserves) cannot pay even the 100-stroop base fee: the network rejects the transaction withtxINSUFFICIENT_BALANCE because the fee would take the account below its reserve. Without help, these accounts are stuck holding their own reserves hostage.
The fix is the protocol’s fee-bump transaction (CAP-15). The user builds and signs the inner transaction in the browser exactly as in every other step, with its inner fee set to zero. The API wraps it in a fee-bump envelope whose fee source is a dedicated, lightly funded fee account, signs only the outer envelope, and submits. The semantics are exact: the fee account pays the entire fee, the inner source pays nothing, and the inner transaction’s signature covers its contents, so the API cannot alter an operation, an amount, or a destination without invalidating the user’s signature. The fee account never touches user funds; the only thing it can spend is its own XLM, on fees.
Because this adds a second funded key to an API that otherwise holds only the mediator co-sign key, the surface is deliberately narrow, mirroring the mediator co-sign validation:
- The API decodes the inner transaction and sponsors it only if every operation matches the wind-down shapes (
ChangeTrustwith limit 0,ManageSellOffer/ManageBuyOfferwith amount 0,ManageDataremovals,SetOptionssigner normalization,ClaimClaimableBalance, conversion payments,AccountMergeto the session destination). - The outer fee is capped per transaction, requests are rate-limited per account and IP, and the fee account carries a small operational float with a daily spend cap and alerting. Replay is structurally impossible: the inner transaction consumes the source account’s sequence number.
invokeHostFunction operation, so it can sponsor Soroban steps and nothing else; the fee-bump wrap for classic operations (which is most of a wind-down) exists only in the self-hosted relayer’s sponsored-transactions mode. The tool therefore implements the classic fee-bump endpoint inside its own API, which is small (build the envelope, validate, sign the outer layer, submit), and treats the self-hosted OpenZeppelin Relayer as the drop-in alternative for operators who prefer audited policy infrastructure, with the hosted Channels service usable for Soroban-only steps. Fee sponsorship covers transaction fees only; it is distinct from CAP-33 reserve sponsorship, which this flow does not need.
9. Closing positions: classic and Soroban DeFi
This is the part the existing reference tool cannot do, and the core of the technical work. Detection and unwinding are separated. OctoPos tells the tool what positions exist across every supported protocol, along with the contract addresses and pool metadata behind them. The tool then constructs every exit transaction itself, reading exact on-chain state over RPC and simulating before signing. It integrates each protocol through its published SDK, public API, or contract interface; it does not guess at contract shapes. A versioned contract registry maps each pool or vault contract’swasmHash to a known protocol version. An unknown wasmHash flags that position for manual review rather than risking an exit transaction built against the wrong interface.
The protocols and their exit mechanics at a glance:
Coverage is driven by what users actually hold, not by market share. By current activity, Blend is the largest lending market and Aquarius the largest AMM, FxDAO is an active CDP protocol, and Soroswap and Phoenix are smaller. The tool supports all of them because a user with a position in any of them needs to close it to merge. A position in a frozen, deprecated, or winding-down contract must stay exitable: closing a position is exactly the withdraw-and-repay path such a contract still allows, so the tool reads contract status, surfaces it to the user, and never hides a position because its protocol changed state. The user’s funds are still there.
9.1 Classic DEX offers
Open offers are cancelled withManageSellOffer or ManageBuyOffer carrying the existing offer ID and amount = 0, which deletes the offer and frees its 0.5 XLM reserve. Passive sell offers, created with CreatePassiveSellOffer, are cancelled the same way. Offers batch at up to 100 per transaction. No external integration is needed; offers are enumerated from the indexer.
9.2 Classic Stellar liquidity pools
Stellar’s native AMM (CAP-38, protocol 18 and later) holds a user’s stake as a pool-share trustline, which costs two base reserves. The only operation that reduces shares isLiquidityPoolWithdraw, which burns shares and returns both reserve assets. The unwind is two steps: LiquidityPoolWithdraw for the full share balance, then ChangeTrust with limit 0 to remove the pool-share trustline. A pool-share trustline cannot be removed while shares remain, so ordering is enforced.
9.3 Blend (lending and borrowing)
Blend positions are detected by OctoPos: supply held as bTokens, debt as dTokens, with per-position health factors. The tool builds the exit itself with the official@blend-capital/blend-sdk through the Pool.submit entry point, which takes a list of typed requests, each a { request_type, address, amount }. The relevant request types are Repay (5), Withdraw (1), and WithdrawCollateral (3); supplied and collateralized balances are tracked separately, so the exit uses the request type matching how each position is held. For withdrawals, passing an amount larger than the position clamps down to the actual balance, which the tool uses to fully exit without dust. Repay behaves differently: the pool pulls the full stated amount from the account and refunds any excess in the same transaction, so the tool caps the repay amount at what the account actually holds rather than padding it. (OctoPos ships a Transaction Builder that can construct Blend exits server-side, but its own documentation marks it experimental and unmaintained, so the tool does not depend on it.)
9.4 Aquarius (AMM)
Aquarius is a Soroban AMM. LP positions are withdrawn by calling the pool’swithdraw(user, share_amount, min_amounts), which burns shares and returns the reserve assets, with a minimum-received tolerance to bound slippage. OctoPos reports claimable AQUA rewards alongside the LP position, the tool confirms the amount on-chain with get_user_reward(user), and claims with claim(user) before withdrawal when the user opts in; claiming AQUA may require an AQUA trustline, which the tool adds and then resolves in the conversion step. Aquarius pools can have claiming admin-paused (kill_claim), in which case the tool surfaces the paused rewards as a notice instead of failing the exit. Pools and positions are discovered from the DeFi Position API and the Aquarius backend, with direct contract reads over RPC as the fallback.
9.5 Soroswap
Soroswap is a Soroban AMM with a public Soroswap API that returns routes and builds XDR. LP withdrawal calls the router’sremove_liquidity(token_a, token_b, liquidity, amount_a_min, amount_b_min, to, deadline). Pairs are enumerated through the factory (all_pairs_length, all_pairs, get_pair), though in practice the DeFi Position API already reports which pairs the account holds. Where the tool relies on the Soroswap API to assemble a transaction, it signs and submits the API-built XDR directly rather than re-simulating it, which sidesteps a known Soroban simulateTransaction edge case around restored archival entries.
9.6 Phoenix
Phoenix is a Soroban AMM. The pool contract exposeswithdraw_liquidity(recipient, share_amount, min_a, min_b, deadline, auto_unstake), where deadline is optional and auto_unstake takes an optional AutoUnstakeInfo (the stake’s amount and timestamp) that makes the pool unbond before burning shares. Staking itself lives in a separate contract whose entry points are bond and unbond, and unbond requires the original stake’s timestamp, so the tool enumerates individual stakes to exit a staked position. It withdraws the full share balance with a minimum-received bound, unbonding first (or via auto_unstake) where a position is staked.
9.7 FxDAO
FxDAO is a CDP protocol: a user locks XLM collateral in a vault and mints a stablecoin (USDx, EURx, or GBPx, one denomination per vault). Vaults open at a 115% collateral ratio and liquidate below the 110% minimum, both admin-configurable per denomination. Closing a vault means repaying the stablecoin debt and withdrawing the XLM collateral. The vault contract tracks vaults in a sorted linked list, so debt repayment throughpay_debt requires passing the neighboring vault keys, and vaults are enumerated through get_vaults. When the account does not hold enough stablecoin to repay, the tool acquires it through routing first. If a vault is undercollateralized at close time, automatic closure is not safe (it would invite liquidation), so the tool surfaces a clear error and asks the user to manage that vault manually.
9.8 What a protocol exit looks like end to end
For every Soroban exit the shape is the same: detect the position from the DeFi Position API, resolve the contract version from the registry bywasmHash, read exact on-chain amounts over RPC getLedgerEntries with ScVal decoding, build the InvokeHostFunction operation, simulate it over RPC to fill in footprint, authorization, and resource fee, present the simulation result to the user, sign client-side, submit, and poll for confirmation. The same adapter pattern that keeps the position provider pluggable isolates each protocol’s contract interface, so a protocol upgrade is a registry and adapter change, not a rewrite.
9.9 Exit adapter invariants
Because the operations are irreversible, every protocol exit adapter must satisfy the same invariants before its output is signed. These are the contract the adapters are held to, and what the test suite checks.
These invariants aren’t scoped to Soroban exits alone. The two classic-op builders that resolve the merge preconditions from Section 3 hold to the same contract by different means: the
REVOKE_SPONSORSHIP step re-reads each owner’s live sponsorship and reserve headroom immediately before building - a dedicated call, separate from the plan-time affordability check, since minutes can pass between them - and an owner that can no longer absorb the shifted reserve simply drops out of that build rather than failing partway, because it was already surfaced as a sponsorship_unaffordable blocker at plan time. The claimable-balance trustline-remediation path reads from the same fresh per-request account state the round already re-read, and a balance that’s no longer claimable likewise surfaces as a blocker (claimable_balance_unclaimable) or a deliberate, recorded no-op (claimable_balance_forfeited) rather than disappearing quietly.
10. Asset conversion and routing
After positions are unwound, the account may hold several classic and Soroban tokens. Each non-XLM balance gets an explicit, per-asset disposition the user makes in the accordion preview, because “swap everything” is the common case but not the only one the ledger allows:- Swap to XLM (offered whenever a route exists): swap through the best available route, then remove the trustline. This is the disposition the tool selects for any asset that has a route, and the user can leave it as is.
- Return to issuer: send the balance back to its issuer, which clears it from the account. This is the right call for spam tokens and worthless dust. It is never the default and never labeled as a conversion: the user confirms it explicitly, and the tool states plainly that it is irreversible.
- Transfer to another account: pay the balance, as the asset, to an account the user names. The other two both end the close with the position destroyed - swapped away at whatever the market gives, or burned - so this is the only disposition that keeps the asset. It is offered whether or not a route exists, and for an asset with no market it is the only choice that does not destroy the balance.
ChangeTrust that removes the trustline runs immediately after and fails on a non-zero balance, so a partial transfer would strand the close midway with the account still open.
The destination must already hold a trustline for that exact asset, and this is checked against live ledger state before anything is built. LumenWipe cannot add it: creating a trustline needs the destination account’s own signature, which the tool never has. A destination that does not exist, does not trust the asset, holds an unauthorized trustline, has no room under its limit, is the account being closed, or is a recognized exchange deposit address surfaces as a blocker naming the working alternatives - never a silent fallback. The exchange case is refused outright rather than acknowledged: a close carries one transaction-level memo, reserved for the merge, so a token payment cannot carry the deposit memo an exchange needs to credit it, and the balance would be lost exactly as a direct merge into a deposit address is.
A claimable balance is not an acceptable fallback for a destination that cannot receive the asset, and this is written down so nobody later “fixes” the blocker that way. It looks like the obvious way to send the asset anyway, and it is a trap: creating a claimable balance makes the source account the sponsor of a new ledger entry, pushing numSponsoring to 1, and ACCOUNT_MERGE fails with ACCOUNT_MERGE_IS_SPONSOR while numSponsoring > 0. The helpful-looking fallback would make the close structurally impossible and leave the account worse off than before.
The destination is arbitrary and per asset at the API level: each asset carries its own params.destination on its DecisionAnswer, so an SDK consumer can send each balance somewhere different. The web UI offers “use the same account I’m merging into” as a shortcut that prefills an editable field, rather than constraining the choice to one address.
Claimable balances follow the same explicit-choice pattern through a distinct DecisionPoint (type: "claimable_balance", resolved to a ClaimableBalanceSelection in @lumenwipe/types), since a balance the account is claimant of is a separate ledger entry, not a balance the account already holds:
- Claim: submit
ClaimClaimableBalancefor the balance now. Offered, and the opt-out default, whenever the account can already claim it - the asset is native XLM, or an authorized trustline already exists. - Add a trustline, then claim: add the missing trustline first, then claim in the same round. The only path to claiming a balance in an asset the account doesn’t yet trust.
- Forfeit: leave the balance unclaimed and proceed with the rest of the close. No operation is built; the choice is recorded as an acknowledged blocker so the plan stays auditable about what it chose not to do.
needs_decisions until every such balance has an answer.
Routing for the convert path has two engines. The primary is the Soroswap API, which finds optimal routes across Soroswap, Phoenix, Aquarius, and the classic SDEX, handles both classic and Soroban tokens, and builds the swap XDR. Like every server-built transaction, that XDR is decoded and verified client-side before signing (Section 9.9). The fallback for pure-classic assets is strict-send path finding from a Horizon-compatible endpoint, executed with PathPaymentStrictSend across SDEX order books and classic liquidity pools (up to six hops). Either way the tool computes a minimum-received amount from the quoted output and a slippage tolerance, and passes it as the destination minimum so a sudden price move cannot fill the swap at a bad rate.
11. The mediator account flow for exchanges
Exchanges do not supportACCOUNT_MERGE, and their crediting systems only recognize Payment operations with a memo, so a user cannot merge directly into a deposit address (a direct merge is typically lost). The tool bridges this with a single shared mediator account, the same pattern the reference demolisher uses, in one atomic transaction.
confirmation decision point, keyed by the address (destination:G...), and close/transactions refuses to build until it is answered (422 destination_not_acknowledged). The decision id names the address so an answer cannot be replayed for a different destination, and the refusal lives on the build endpoint rather than only in the plan because the plan is advisory - an API or SDK caller can reach the build without ever requesting one. Only the user knows where an address came from, so the tool asks them rather than guessing.
12. Allowance inspection
Independent of closing an account, the tool offers a read-only allowance inspector. This is a security utility: a user who has approved token spending to DeFi contracts can audit and revoke those approvals, which limits exposure if a protocol is later exploited. Soroban tokens follow the SEP-41 interface, includingapprove(from, spender, amount, expiration_ledger) and allowance(from, spender). There is no on-chain way to list every spender an account has approved, so the inspector discovers candidate spenders from approve events (RPC getEvents, with the indexer for older windows) and from the known DeFi contract registry, then reads allowance(owner, spender) for each. Non-zero allowances are shown with the token, the spender contract and its protocol name when recognized, the approved amount, and the expiration ledger. Revoking sets the allowance to zero with approve(owner, spender, 0, ledger), one InvokeHostFunction per revocation, and requires no full wind-down.
13. Security model
The tool builds transactions that drain an account irreversibly, so its security model starts from the assumption that only the user’s own machine should ever be able to sign.13.1 What is at risk and who attacks it
A compromised API cannot redirect a user’s funds. It builds the transactions, but the browser verifies each one against the user’s own inputs before signing (
verify(), Section 6.2), so a transaction that changed a destination, amount, or operation would fail verification and never be signed. For those checks - destination, memo, and the user’s own trustline-claim choices - verify() takes its expected values from the user rather than the API response, so the API cannot supply its own answer. One check is sourced from the API’s own trust domain rather than the user: that a SetOptions signer removal targets a signer that actually exists on the account is confirmed against a separate account-state read, not the transaction itself, so it catches a transaction-builder bug or a partial compromise, though a wholly compromised API could in principle keep that read and the transaction consistent with each other. Its only signing key is the shared mediator, which can co-sign only a payment whose destination and amount the user already fixed in an atomic transaction, so it can neither sign for a user’s account nor redirect the forward payment. Wrong read data is caught by that same verification, on-chain simulation, and explicit confirmation of every destructive step. A passive network observer sees only TLS-protected traffic. An XSS attacker is blocked by a strict Content Security Policy with no inline scripts, no unsafe-eval, and no external scripts, with one intentional exception: style-src 'self' 'unsafe-inline' (required by stellar-wallets-kit’s runtime style injection). A supply-chain attacker is constrained by lockfile-pinned dependencies, audited in CI, with no dependency permitted that needs dynamic code execution.
13.2 Key handling
The wallet path is primary: through stellar-wallets-kit the private key never enters the application. The secret-key advanced mode is for keys not held in any wallet, and is constrained: the input is a password field, the key is held only in memory (never inlocalStorage, sessionStorage, IndexedDB, cookies, or any network request) for the duration of the execution session, and it is wiped on completion, on abort, on navigation away from the flow, or when the user explicitly clicks “Forget key”. The component holding it is also unmounted when the user leaves the signing step. For multisig, keys are gathered one at a time - the active key signs, and switching to a different wallet or secret key (or explicitly clicking “Forget key”) is what clears it, not each individual signature; the underlying memory lifetime is the same one described above for the single-signer case. The shared mediator’s secret is the system’s one server-side signing key; it lives only in the API’s environment, never in the browser, and is used solely to co-sign the exchange forwarding payment (Sections 7 and 11).
13.3 Confirmation and irreversibility controls
Every destructive step requires an explicit acknowledgment that states what will happen, shows the affected entry or balance, and warns that it cannot be undone. The tool never auto-submits; the user triggers each submission. The merge gets its own full-screen confirmation with the destination shown in full, a ledger existence check, and memo validation for exchange destinations. A third layer sits above these two: before any transaction is built or signed, the whole plan is shown at once on a dedicated review step. The client-sideDemolishPhase state machine (Section 6.1) gates this explicitly - the plan-generation phase (PREFLIGHT_COMPLETE) only advances to execution (STEP_EXECUTING) through that page’s own confirmation, and nothing is written to the resumable session store before it fires, so leaving the tab mid-review has nothing to resume. This is additive to the per-step and per-merge confirmations, not a replacement - confirming the whole plan doesn’t skip confirming each step and the merge itself as they happen.
13.4 Security reviews
The codebase undergoes internal security reviews as part of our development process. External security audits will be conducted when possible.14. Trust minimization and decentralization
For a tool that closes accounts, decentralization is first a matter of custody and control, and second a matter of how little anyone has to trust the operator. Custody and control. The tool is non-custodial by construction. A user’s account signing is client-side and their keys never reach a server. The API holds one signing key, the shared exchange mediator, used only to co-sign a forwarding payment the user has already authorized in an atomic transaction. No operator of any component, including the maintainers, can change a destination, move a user’s account funds, or close their account without the user’s own signature. The user authorizes every transaction. Open code, open surfaces. The whole project is open source under a permissive license. The API that builds the transactions and the client-sideverify() that guards signing are both open and auditable, and the signing itself runs in the user’s browser where anyone can read it. Integrators do not have to go through our UI at all: the REST API and the TypeScript SDK (Section 7.3) let a wallet or platform drive the same wind-down with its own interface and its own signers, so the security-critical path is auditable and embeddable rather than locked behind a hosted product. Every external read source sits behind a pluggable adapter, so the deployment can be pointed at any Stellar RPC provider, indexer, or DeFi Position API instance.
Where centralization remains, and why. The remaining centralized pieces are all read-only data sources: RPC providers, the indexer, the routing API, and the DeFi Position API. None can affect custody. Each is pluggable and has multiple independent providers in the Stellar ecosystem, so no single one is a hard dependency. The DeFi Position API (OctoPos) is a deliberate dependency, kept behind an adapter with an explicit degraded mode, so even there an outage limits functionality rather than breaking the tool.
Nothing here can move a user’s funds: the API builds transactions but cannot sign for a user’s account, and signing lives only in the browser behind
verify(). Everything in the external rows is read-only and replaceable.
15. Infrastructure and deployment
The tool runs on light, replaceable infrastructure, which follows from the non-custodial design. The codebase is a Bun-workspaces monorepo -apps/{web,api} and packages/{sdk,types} - with per-package CI so each artifact builds and tests independently.
- API service: a NestJS service that reads state and builds transactions. It holds no per-user state and no user keys (only the shared mediator co-sign key, injected from the environment as a managed secret), so it scales horizontally. It deploys as a container to Google Cloud Run with scale-to-zero (minimum instances 0), so an idle deployment costs nothing and a cold start is a few seconds - acceptable for an infrequent, deliberate close.
- Web client: the Next.js app, deployed to Vercel. It reaches the API only through its own server-side proxy routes, which inject the API key, so the browser never holds one.
- Cache: short-lived public read data only, held in the API.
- Stellar access: Stellar RPC through ecosystem providers, configurable per deployment.
- Data services: one Horizon-compatible endpoint for enumeration and the Soroswap API for routing, both set by configuration; the OctoPos DeFi Position API.
@stellar/stellar-sdk, Stellar RPC, stellar-wallets-kit, and the live network protocol (Protocol 26, Yardstick, on mainnet since May 2026). The contract registry and protocol adapters are versioned so the tool tracks protocol and DeFi upgrades without a rebuild of its core logic.
16. User protection and privacy
The tool protects users on two fronts: their funds and their privacy. Funds. The irreversibility controls in Section 13 are the protection: explicit per-step confirmations, no auto-submission, destination verification, memo validation for exchanges, per-step simulation before signing, and a resume flow that reconciles against on-chain state so an interrupted wind-down never double-acts. Privacy. The tool collects no personal information and requires no account. Secret keys never leave the browser and are never logged. The API handles only public addresses, which it does not retain beyond cache TTLs, and it associates no identity with a request. Any product analytics are privacy-preserving and self-hosted (for example Plausible or Umami) with no personal data, no cross-site tracking, and IP anonymization; the default is to ship no third-party trackers at all, and the Content Security Policy blocks third-party scripts. Abuse protection is rate limiting by API key at the service and by IP at the web proxy, neither of which needs a stored identity.17. Testing strategy
Testing matters more than usual here because the operations are irreversible and touch real balances. The suite has four tiers; automated tests never touch mainnet. Unit and adversarial/edge-case tests are deterministic fixtures and run automatically in CI on every change. Integration and end-to-end tests run against real Stellar testnet and are manual: integration is gated behind theLUMENWIPE_RUN_INTEGRATION environment variable, and Playwright end-to-end is a separate script (test:e2e) that .github/workflows/ci.yml never invokes. Because the codebase is a monorepo, CI runs every package’s checks on each change as a matrix, so a shared type change is validated against every consumer rather than skipped by a path filter.
- Unit: pure logic with deterministic fixtures. Transaction construction, fee estimation, reserve and balance math, routing parameter derivation, state machine transitions, input validation, and batching. The transaction builder is the highest-coverage module.
- Integration: against Stellar testnet with accounts funded by Friendbot at the start of each run. Account analysis, signer removal, offer cancellation, trustline removal, asset conversion, the merge, and each DeFi protocol exit. DeFi detection in these tests runs through the direct contract-read path, since OctoPos serves mainnet only; that keeps the degraded-mode code under permanent test coverage.
- Adversarial and edge case: deliberately unusual or hostile account states. Sponsoring accounts, the 1000-subentry maximum, revoked trustlines, multisig with hash(x) and pre-auth signers, undercollateralized vaults, queued backstop withdrawals, high-slippage conversions, and network failures such as a confirmed transaction whose response is lost (detected on retry through
getTransactionso the step is not resubmitted). - End to end: Playwright drives a real browser against testnet through the full flow, including the multisig path, the mediator path for exchange destinations, session recovery, and the allowance inspector.
18. Maintenance after launch
The design isolates the parts most likely to change. Protocols upgrade, and DeFi contracts get redeployed. The versioned contract registry mapswasmHash to protocol version, so a new protocol version is a registry update (a reviewed pull request), not a code change. An unknown wasmHash degrades gracefully: the affected position is flagged for manual review instead of risking a wrong exit. Each protocol and each data provider sits behind an adapter, so adding a protocol or swapping a provider is a contained change. Dependencies are pinned and audited in CI, with weekly update pull requests. The repository carries a security policy and a responsible-disclosure process. Maintenance commitments, the cadence of protocol-coverage review, and the community update rhythm are detailed in the community and communications document.
19. Delivery plan
The work is delivered in three cumulative tranches, each a working, independently verifiable artifact.20. Traction
The classic wind-down already runs. The current codebase is a working monorepo - a NestJS API and a thin Next.js client - that, on both networks, reads account state over Stellar RPC and one Horizon-compatible endpoint, builds the classic transactions in the API, verifies and signs them in the browser, and executes the full path: signer normalization, data entry removal, offer cancellation, asset conversion through SDEX path payments, trustline removal, andAccountMerge, including the mediator flow for exchange destinations with the correct memo handling. It carries an exchange registry, IndexedDB session recovery, unit tests over the plan builder and helpers, and Playwright end-to-end coverage. This is the foundation the Soroban and DeFi work builds on, and the evidence that the team is already executing rather than starting from a blank page.
21. Technology stack and standards
Plain-English summary of what the tool is built from and why.- Frontend: Next.js and TypeScript, a thin open source web client that verifies and signs, with TypeScript’s type safety guarding the verification and signing path.
- API: NestJS and TypeScript, a stateless service that reads state and builds transactions, with a short-TTL cache for public read data; API-key auth with per-key rate limiting.
- Packaging: a Bun-workspaces monorepo (
apps/{web,api},packages/{sdk,types});@lumenwipe/sdkis a thin fetch client over the API, and@lumenwipe/typesis shared across the API, the SDK, and the web. - Stellar SDK:
@stellar/stellar-sdk, the official SDK, which covers classic and Soroban, used server-side in the API. - Wallets: stellar-wallets-kit (Freighter, xBull, Albedo, Rabet, Hana, WalletConnect; LOBSTR is accessible via WalletConnect), including Soroban authorization-entry signing.
- Network access: Stellar RPC for live reads, simulation, submission, and events; the stellar.expert API for subentry enumeration; the Soroswap API for routing; OctoPos for DeFi position detection.
- DeFi integration: the official Blend SDK, the Soroswap API, and the published contract interfaces for Aquarius, Phoenix, and FxDAO, behind per-protocol adapters and a versioned contract registry.
- State and storage: Zustand for the wizard state machine, IndexedDB for resumable sessions (never keys).
- Testing: the Bun test runner for units, Playwright for end-to-end on testnet, with per-package CI across the monorepo.
Standards we build on
The tool tracks the current stable protocol (Protocol 26, Yardstick, on mainnet since May 2026) and the latest@stellar/stellar-sdk. It builds on these ecosystem standards:
22. Failure modes and recovery
The tool never leaves the user guessing. Every failure is either retryable with a clear path or surfaced as a blocker with a manual resolution, and partial progress is always recoverable from on-chain state.23. Open questions and known risks
These are the items the team is actively resolving. Listing them is deliberate: a tool that drains accounts should be honest about what is still being pinned down.24. Glossary
- Base reserve: the unit of locked XLM, currently 0.5 XLM (network-voted). An account’s minimum balance is two base reserves plus one per subentry, adjusted by sponsorship (
+ numSponsoring - numSponsored). - Subentry: a trustline, offer, data entry, or signer attached to an account. Each adds one base reserve to the minimum balance; a pool-share trustline adds two.
ACCOUNT_MERGE: the operation that transfers an account’s full XLM balance to a destination and deletes the source account. Requires no subentries apart from signers, and no sponsorships.- Sponsorship: an arrangement where one account pays the reserve for another account’s entry. A sponsoring account cannot be merged until it stops sponsoring; for most entry kinds this tool revokes the sponsorship automatically when the entry’s owner can absorb the shifted reserve, but a sponsored claimable balance has no self-service revocation path (§3) and remains a permanent blocker until claimed.
- Trustline: an account’s declared ability to hold a given asset, with a balance and a limit. Removed with
ChangeTrustset to limit 0 once the balance is zero. - Stellar RPC: the JSON-RPC interface for live ledger reads (
getLedgerEntries), Soroban simulation (simulateTransaction), submission (sendTransaction), confirmation (getTransaction), and events (getEvents). It cannot enumerate an account’s unknown subentries. - Indexer: a service that indexes ledger history and exposes enumeration, such as the stellar.expert API or a Horizon-compatible provider. The tool reads enumeration from an existing indexer rather than running its own.
InvokeHostFunction: the Stellar operation that calls a Soroban smart contract. Each one is simulated over RPC to determine its footprint, authorization, and resource fee.ScVal: the value encoding used by Soroban contracts. The tool decodesScValresults when reading on-chain position state.wasmHash: the hash identifying a deployed contract’s code. The tool maps it to a known protocol version to pick the correct exit interface.- bToken / dToken: Blend’s representations of a supply position (bToken) and a debt position (dToken).
- Q4W: Blend’s queue-for-withdrawal cooldown on backstop deposits: 21 days on V1 pools, 17 days on V2.
- CDP: a collateralized debt position, the FxDAO model where XLM collateral backs minted stablecoin.
- SAC: the Stellar Asset Contract, which lets a classic asset (and XLM) be used inside Soroban contracts. It implements the SEP-41 token interface.
- Mediator account: a shared, persistent account, funded once by the operator, used to forward funds to a destination that does not support
ACCOUNT_MERGE, such as an exchange.
25. References
- Reference tool, stellar.expert demolisher (Orbit Lens): https://stellar.expert/demolisher/public
- StellarExpert demolisher announcement: https://medium.com/@orbit.lens/stellarexpert-embeddable-blocks-accounts-demolisher-and-other-new-features-931ec41427a1
- Stellar RPC overview and methods: https://developers.stellar.org/docs/data/apis/rpc
- getLedgerEntries reference: https://developers.stellar.org/docs/data/apis/rpc/api-reference/methods/getLedgerEntries
- SDF Horizon retention change (August 2024): https://stellar.org/blog/foundation-news/sdf-s-horizon-limiting-data-to-1-year
- List of operations (ManageSellOffer, ChangeTrust, AccountMerge, SetOptions, PathPaymentStrictSend): https://developers.stellar.org/docs/learn/fundamentals/transactions/list-of-operations
- Minimum balance and base reserve: https://developers.stellar.org/docs/learn/fundamentals/lumens
- Classic liquidity pools (CAP-38): https://developers.stellar.org/docs/learn/fundamentals/liquidity-on-stellar-sdex-liquidity-pools
- Path payments (strict send and receive): https://developers.stellar.org/docs/build/guides/transactions/path-payments
- Blend SDK: https://www.npmjs.com/package/@blend-capital/blend-sdk and https://docs.blend.capital/tech-docs/integrations/integrate-pool
- Blend backstop and Q4W: https://docs.blend.capital/users/backstopping
- Aquarius Soroban functions: https://docs.aqua.network/developers/aquarius-soroban-functions
- Soroswap API: https://docs.soroswap.finance/soroswap-api
- Phoenix contracts: https://github.com/Phoenix-Protocol-Group/phoenix-contracts
- FxDAO vaults: https://fxdao.io/docs/developers/vaults/overview/
- stellar-wallets-kit: https://github.com/Creit-Tech/Stellar-Wallets-Kit
- Stellar Asset Contract: https://developers.stellar.org/docs/tokens/stellar-asset-contract
- SEP-41 token interface: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0041.md
- OctoPos: https://communityfund.stellar.org/project/octopos-defi-position-api-g6i and https://docs.crediolabs.ai/docs/category/octopos