AI agent documentation index: llms.txt. Raw markdown for any page is available by appending .md to the URL. Full content snapshot: llms-full.txt.
llms.txt — complete Cartesi documentation index. Append .md to any page URL for raw Markdown (e.g. /cartesi-rollups/2.0/development/building-an-application.md).
Skip to main content

Emergency Withdrawal Recovery Guide

This guide walks through foreclosing an application and withdrawing an account's funds directly from the base-layer contracts. For the concepts behind each step, see Foreclosure & Emergency Withdrawal and the Claim & Foreclosure Lifecycle.

Before you start

You need:

  • An application that was deployed with a WithdrawalConfig, and that has reached at least one accepted epoch;
  • The guardian key (only the guardian can foreclose);
  • The machine tool (cartesi-rollups-machine-tool), which reproduces the settled machine state and generates the proofs;
  • To send the on-chain transactions, either the cartesi-rollups-cli (see Installing the required tools) or, if you prefer, Foundry's cast with jq;
  • The application's accounts-drive parameters from its withdrawal config: accountsDriveStartIndex, log2MaxNumOfAccounts, and log2LeavesPerAccount. These must match the values the application was deployed with.
  • This should be done in the same environment of the node, with access to the database (and configuration variables)

This guide assumes the application already maintains its accounts drive in the layout its WithdrawalConfig describes. For how that drive is created, sized, and kept, see Emergency Withdrawal (guest requirements), and in particular Creating the accounts drive.

The on-chain steps (1, 4, 5, and 6) can be run with either tool. Choose a tab in each step, and it applies to the others. Steps 2 and 3 use the machine tool either way. In the cast tabs, <app-address> is the application contract address, <rpc-url> is your node RPC endpoint, and the private key is the guardian's (step 1) or your own (steps 4 and 5).

Step 1: Foreclose the application

Signed by the guardian, freeze the application:

cartesi-rollups-cli foreclose <app-name-or-address>

After this, isForeclosed() returns true and the application is frozen at its last accepted epoch.

Step 2: Reproduce the settled machine state

Find the last accepted epoch, then replay the node database into a machine snapshot up to that epoch:

cartesi-rollups-machine-tool replay \
--template <template-path> \
--application <app-name-or-address> \
--to-epoch <accepted-epoch-index> \
--store replay-snapshot

Replay is deterministic: running it again produces the same machine state, so anyone can reproduce this snapshot independently.

Step 3: Generate the proofs

From that snapshot, generate the accounts-drive-root proof and the per-account proof for the account you want to withdraw. The --accounts-drive-* values must match the withdrawal config.

cartesi-rollups-machine-tool prove accounts-drive \
--snapshot replay-snapshot \
--accounts-drive-start-index <accountsDriveStartIndex> \
--log2-max-num-of-accounts <log2MaxNumOfAccounts> \
--log2-leaves-per-account <log2LeavesPerAccount> \
--account <account-address> \
--out-drive-root-proof drive-root-proof.json \
--out-withdraw-proof account-proof.json

This writes two files: drive-root-proof.json (used once, in step 4) and account-proof.json (used per account, in step 5). The cast tabs below read their arguments out of these files with jq.

Step 4: Anchor the accounts-drive root on-chain

Record the accounts-drive root against the settled machine state. This is permissionless and only needs to happen once per foreclosed application:

cartesi-rollups-cli prove-drive-root <app-name-or-address> \
--proof-file drive-root-proof.json

On success the contract stores the root and emits AccountsDriveMerkleRootProved. Anchoring a root from the wrong epoch, or from a different application, is rejected.

Step 5: Withdraw the account's funds

With the root anchored, withdraw the account:

cartesi-rollups-cli withdraw <app-name-or-address> \
--proof-file account-proof.json

The contract validates the account against the anchored root, builds and runs the transfer, marks the account as withdrawn, and emits a Withdrawal event. Withdrawing the same account again is rejected.

Step 6: Verify

# the node indexes the on-chain Withdrawal event
cartesi-rollups-cli read withdrawals <app-name-or-address>

Either way, wereAccountFundsWithdrawn(accountIndex) returns true, and the token balance has moved from the application contract to the account owner.

We use cookies to ensure that we give you the best experience on our website. By using the website, you agree to the use of cookies.