Account Demolisher
How it works

The classic batch

The nine operation phases of the final classic transaction, in order, and how they split across transactions.

batchClassicDemolition(audit, options, paths) in src/lib/plan/classic-batcher.ts turns an account audit into an ordered list of classic batches. It is a pure function with no network calls.

The operation order is fixed. Each phase runs only after the phase it depends on, so a trustline is never removed before its balance is handled and the merge is always last.

The nine phases

1. Withdraw liquidity pools

One operation per pool-share trustline with a positive balance and two known reserves.

Minimums matter here. The reserve amounts Horizon returns are the whole pool's, not your share. The expected payout for a reserve is reserve * yourShares / totalShares, and the slippage haircut applies to that figure. Applying it to the whole reserve would revert with LIQUIDITY_POOL_WITHDRAW_UNDER_MINIMUM. When totalShares is missing or zero the floor falls back to zero.

2. Cancel offers

One operation for every offer, unconditionally.

3. Claim claimable balances

One operation per balance you opted into.

The opt-in list has a meaningful three-state behaviour: undefined means claim all, and an empty array means claim none.

A balance whose claimableNow is explicitly false is always skipped, because the operation would fail on chain.

4. Dispose of credit balances

For each positive credit balance, in strict precedence:

OrderOperationCondition
1PATH_PAYMENT_STRICT_SEND to XLMA conversion path exists
2Payment to the destinationYou consented, and the destination already trusts the asset
3Payment to the issuerYou consented to return it
4Nothing at allNo path and no consent

The path payment sends to the account itself: it converts the balance in place rather than paying anyone. Its destMin carries the slippage haircut.

Send-to-destination is offered only when the destination already trusts the asset, which is verified at preview time, so it cannot fail with op_no_trust.

Case 4 emits no operation on purpose. The balance stays, its trustline is kept in phase 5, and that blocks the merge until you resolve it. The plan surfaces this rather than quietly forfeiting the funds.

5. Remove trustlines

Two loops, pool-share trustlines first, then credit trustlines.

Removing a pool-share trustline needs the full liquidity-pool asset (both assets and the fee), not just the pool id. Horizon returns reserves in canonical order.

A credit trustline is removed only when its balance is handled: zero, converted, or consented to a disposal. An un-routable, unconsented, non-zero line keeps its trustline, which is exactly what blocks the merge.

6. Delete data entries

One operation per data entry.

7. Release sponsorships

This phase emits nothing. Self-sponsored entries need no explicit revoke: removing, cancelling, or clearing them, or claiming their claimable balances, releases the sponsorship automatically and drops num_sponsoring so the merge passes.

Sponsorships this account holds for other accounts cannot be enumerated from its own Horizon record, so they are blocked upstream in computeMergeability rather than papered over with a wrong revoke operation.

8. Clear signers and reset thresholds

One operation per signer with a positive weight that is not the master key, because setOptions accepts one signer mutation per operation.

A threshold reset is appended when the account is not already at low 0, medium 0, high 0, and master weight 1. The reset sets exactly those values.

9. Merge

Guarded first: a direct close whose destination equals the account being closed throws, because Stellar rejects a self-merge with ACCOUNT_MERGE_MALFORMED.

The merge destination is the mediator's public key when the exchange path is in use, and your destination otherwise. The operation is always appended, so the operation list is never empty.

The mediator prepend

When the exchange path is in use and the mediator is not already funded, a create_account operation funding the mediator with 2 XLM is prepended to the first batch.

Splitting

A Stellar transaction holds at most 100 operations (MAX_OPS_PER_TX). When the operation list exceeds that, it splits across several transactions.

The merge always lands in the last transaction.

Fees and time bounds

ConstantValue
Base fee per operation100 stroops (the network minimum)
Fee ceiling4,294,967,295 (max uint32)
Time bound300 seconds
Multisig signing window259,200 seconds (72 hours)

The executor bids above the base fee when the network is congested and escalates on a too-low-fee rejection. See Execution and recovery.

Rebuilt at execution time

The batches attached to the plan node are not what gets submitted. The executor re-audits, re-resolves credit paths, and re-batches before every classic phase, then builds only the first batch of the fresh result and repeats until one batch remains.

It forwards the state that must survive that rebuild: whether the mediator is already funded, the claimable-balance opt-ins, both consent lists, the fallback address, the mediator public key, and the memo.

The memo matters. A direct merge to a memo-required destination drops its memo if the re-batch does not re-apply it.

Intermediate batches deliberately do not record an executed transaction hash, because that record is the resume signal, and setting it would let a retry skip a still-unrun merge while reporting success.

On this page