Automatic Recovery
The full recovery lifecycle — trigger conditions, failed transfer handling, and vault abandonment.
When Recovery Triggers
Recovery is automatic and fully on-chain. No human being decides when to execute it. The Aeternum keeper bot monitors every registered vault and triggers recovery the moment three conditions are all simultaneously true:
// From _executeRecovery() — these three checks are re-validated on every
// triggerRecovery() call, regardless of who submits it
if (!config.isActive) return; // 1. vault must be registered and active
if (config.balance == 0) return; // 2. vault must hold a non-zero ETH balance
if (block.timestamp < config.lastActivity + config.inactivityPeriod) return; // 3. timer must be elapsedAll three must hold. If you have an active vault but a zero balance, nothing happens — there is nothing to recover. If your vault has ETH but you interacted an hour ago, nothing happens — your timer is still running. Recovery only fires when all three conditions align.
Info
The keeper bot's database is only a first-pass filter. Before submitting anything on-chain, it re-validates every candidate directly against the contract in a single batched read, to catch stale entries the database missed. Only wallets confirmed due move forward to triggerRecovery(). This on-chain call is permissionless — any address could submit it once a vault is eligible, not only the keeper bot. See Keeper Network for the detailed mechanics.
How Recovery Executes
When your vault is identified as due for recovery, the contract executes a single _executeRecovery() call for your address. The process follows the Checks-Effects-Interactions (CEI) security pattern:
Step 1 — Checks: Conditions are re-validated inside _executeRecovery(), independently of whatever off-chain or on-chain data motivated the call. Even after the keeper's own pre-submission validation, conditions can still change in the gap before the transaction confirms — you sent a ping in that window, for instance — so the contract checks everything again itself. If anything no longer holds, the recovery is silently skipped and no ETH moves.
Step 2 — Effects: Your balance is zeroed, your vault is marked inactive, and your address is removed from the monitoring registry — all before any ETH is transferred.
Step 3 — Interaction: The contract calls your backup address and sends the full ETH balance in a single transfer.
If the transfer succeeds, a RecoveryExecuted event is emitted and your ETH is now at your backup address.
When Transfers Fail
A recovery transfer can fail if the backup address is a smart contract that does not accept ETH (for example, a contract with a revert in its receive() function, or a misconfigured multisig).
When a transfer fails, the contract does not panic or lock up. It handles the failure gracefully:
- Your balance is restored — the ETH is returned to your vault record.
- A failure counter is incremented —
failedRecoveryAttemptsgoes from 0 to 1. - Your vault is re-added to monitoring — the keeper bot will attempt recovery again on a future cycle. A failed transfer for your wallet has no effect on anyone else's recovery, even if several were submitted together.
- A
RecoveryFailedevent is emitted so off-chain monitors and the dashboard can surface the issue.
This means a broken backup address does not lock your funds permanently — it just delays the recovery and gives you (or whoever controls your wallet) an opportunity to update the backup address via updateBackupAddress().
A failing backup address blocks recovery but not withdrawal
If recovery keeps failing, your ETH is not lost. You (the original vault owner) can still call withdrawAll() or send() at any time to move your funds. The backup address only matters if recovery triggers — and only after your inactivity period elapses, meaning you are presumably incapacitated. Make sure your backup address is tested before you rely on it.
The Three-Attempt Limit
To prevent an unbounded retry loop against a permanently broken backup address — which would mean whichever keeper submits the transaction keeps burning real ETH gas on a call that can never succeed, forever — AeternumVault limits consecutive recovery attempts to three.
| Attempt | Result |
|---|---|
| 1st failure | Balance restored, vault re-monitored, RecoveryFailed emitted |
| 2nd failure | Balance restored, vault re-monitored, RecoveryFailed emitted |
| 3rd failure | Vault permanently abandoned, RecoveryAbandoned emitted |
After three consecutive failures, the vault is marked as abandoned:
- It is removed from the monitoring system permanently
- The backup address is recorded as a blocked address — no future registration can use it
- A
RecoveryAbandonedevent is emitted
After Abandonment
Abandonment does not mean you lose your ETH. The two things that change are:
The vault is no longer monitored. The keeper bot will not attempt recovery again for this vault — abandoned vaults are excluded from its scans. The automatic protection is gone.
Your ETH balance remains fully accessible. The balance field in your vault config is still intact. You can call withdrawAll() or send() at any time to retrieve your funds.
To start fresh with new recovery protection, call register() again — but with a different backup address (the failed one is permanently blocked). If you have not withdrawn your balance first, register() will automatically carry it over into your new vault.
// From register() — carried balance from abandoned vault
uint256 carriedBalance = s_configs[msg.sender].isAbandoned
? s_configs[msg.sender].balance
: 0;
// Your new vault inherits the old balance automatically
s_configs[msg.sender] = RecoveryConfig({
balance: msg.value + carriedBalance,
// ... rest of new config
});This design ensures no ETH is ever lost due to a failed recovery — the worst outcome is that your backup plan stops working and you need to set it up again with a valid address.