Account Demolisher
How it works

The plan graph

The 13 node kinds, the dependency edges between them, how the graph is validated, and how it is ordered.

generatePlan(audit, positions, allowances, destination, options) in src/lib/plan/generator.ts emits a directed acyclic graph of nodes. It is a pure function: no network calls, and its only impurity is reading the clock to estimate a Blend backstop unlock date.

A node is emitted only when the account needs it. Every node in a generated plan is a real step that runs on chain.

Node kinds

Thirteen kinds, in declaration order.

KindWhat it doesSoroban
RevokeAllowanceSets a SEP-41 approval to zeroYes
RepayBlendRepays a Blend liabilityYes
PayFxDAODebtPays an FxDAO vault debt, returning its collateralYes
WithdrawBlendWithdraws Blend collateral or supplyYes
WithdrawAquariusWithdraws an Aquarius pool shareYes
WithdrawSoroswapLpWithdraws a Soroswap LP positionYes
ClaimBlendEmissionsClaims BLND emissions for a poolYes
ClaimAquariusRewardsClaims Aquarius rewards for a poolYes
ConvertSorobanToXLMSwaps a leftover token to XLM via SoroswapYes
TransferAsIsSends a leftover token to the destination unchangedYes
BackstopQueueQueues a Blend backstop withdrawalYes
FinalClassicTxThe batched classic close, ending in ACCOUNT_MERGENo
MediatorForwardThe exchange delivery hopNo

isSorobanNode returns true for the first eleven and false for FinalClassicTx and MediatorForward.

Node status

Seven values: pending, simulated, signed, submitted, confirmed, failed, skipped.

Dependency edges

dependencies lists the ids a node waits on. A node runs once every dependency reaches confirmed or skipped.

KindWaits on
RevokeAllowanceNothing
RepayBlendNothing
PayFxDAODebtNothing
WithdrawBlendEvery RepayBlend for the same pool
WithdrawAquariusNothing
WithdrawSoroswapLpNothing
ClaimBlendEmissionsEvery WithdrawBlend for the same pool
ClaimAquariusRewardsThe WithdrawAquarius for the same pool index
ConvertSorobanToXLMNothing
TransferAsIsNothing
BackstopQueueNothing
FinalClassicTxEvery node that is not itself FinalClassicTx or MediatorForward
MediatorForwardfinal-classic-tx

The FinalClassicTx edge set is the important one: every Soroban node must complete before the merge, so the account is never closed with a position still open.

Node ids

FinalClassicTx and MediatorForward use the literal ids final-classic-tx and mediator-forward. Every other id is built by lowercasing its parts and joining them with :.

PrefixParts
revokecontract id, spender
blend-repaypool id, asset
fxdao-pay-debtvault denomination
blend-withdraw-collateralpool id, asset
blend-withdraw-supplypool id, asset
aquarius-withdrawpool index
soroswap-withdrawtoken A, token B
blend-claimpool id
aquarius-claimpool index
convert-tokencontract id
drain-tokencontract id
backstop-queuepool id

Entry guards

Generating a mediator plan requires both pieces of mediator state, or the generator throws:

  • useMediator: true without mediatorPublicKey
  • useMediator: true without flowToken

Validation

buildPlanTree(nodes) runs three checks in order, each throwing a plain Error:

  1. Duplicate idsbuildPlanTree: duplicate node id "<id>"
  2. Missing dependency targetsbuildPlanTree: node "<id>" depends on missing node id "<depId>"
  3. CyclesbuildPlanTree: cycle detected in plan dependencies: <a -> b -> a>

Cycle detection

assertAcyclic is an iterative depth-first search with three-colour marking and an explicit stack, so it does not recurse.

Every node starts white. A node being visited is grey, a finished node is black. Reaching a grey node is a back edge, which is a cycle. The reported path is reconstructed from the current stack frame's path, sliced from the first appearance of the repeated node, so the message reads as the cycle itself rather than the whole traversal.

Traversal follows the dependency direction, so the path reads in dependency order.

Ordering

topologicalOrder(tree) is Kahn's algorithm.

In-degree is a node's dependency count. Every zero-in-degree node seeds a FIFO queue; draining it appends each node to the order and decrements its children, enqueueing a child when it reaches zero.

A short result means a cycle survived, and throws topologicalOrder: cycle detected at runtime: produced N of M nodes.

The order is deterministic. Both the seed scan and the children lists preserve Map insertion order, which is the order buildPlanTree inserted the nodes, which is the order generatePlan pushed them.

The tree shape

PlanTree has two fields: rootNodes, the nodes with no dependencies, and allNodes, a flat id-to-node index.

Metadata worth knowing

Two metadata fields carry warnings in the source, both about memos:

  • FinalClassicTxMetadata.memo is the deposit memo for a direct merge to a memo-required destination. It has to be re-applied during execute-time re-batching or the signed merge drops it.
  • MediatorForwardMetadata.memo must be carried verbatim, because that forward is the only hop that reaches the exchange. A dropped numeric or hash memo means the deposit is not credited.

PayFxDAODebtMetadata carries collateral alongside debt so the on-chain vault key, derived from both, can be reconstructed.

Next

On this page