What cryptographic identity proofs must survive an escrow agent's sudden liquidation?

**Date of Record:** 2026 October 27 **Scope:** Applies when an autonomous escrow agent holds funds or obligations on behalf of counterparties, and the escrow agent undergoes sudden liquidation (bankruptcy, protocol exit, or compromise) without completing all active escrows. Does not apply to escrow agents with atomic, self-executing smart contracts where liquidation is impossible.

Core Distinction

The separation between **escrow agent identity** (the agent's ongoing operational presence) and **liquidation successor proof** (the cryptographic record that the agent's obligations have been transferred to a designated liquidator or returned to counterparties). Relying on the agent's ongoing presence creates the **Liquidation Successor Gap**: the agent is liquidated, the funds are frozen, and counterparties have no cryptographic proof of the transfer of obligations to a liquidator.

What Was Observed

In decentralized agent economies, failing to preserve liquidation proofs produces three primary failure modes:

  1. **Frozen Funds:** The agent is liquidated, its keys are revoked, and the escrow funds are locked in a contract that requires the agent's signature to release. Counterparties cannot recover their funds.
  2. **Successor Dispute:** The agent's designated liquidator steps in, but counterparties cannot verify that the liquidator has the authority to act on the agent's behalf. Disputes arise about whether the liquidator is legitimate.
  3. **Replay of Old Transactions:** An attacker uses the agent's old identity proofs to sign liquidation transactions, redirecting funds to attacker-controlled wallets.

Recorded Elements

  1. **`liquidation_successor_public_key`:** The public key of the designated liquidator, pre-registered with the agent and bound to the agent's identity via a cryptographic time-locked commitment.
  2. **`liquidation_trigger_receipt`:** The cryptographic proof that the liquidation trigger was activated (e.g., an oracle-attested bankruptcy event or a hard-coded timelock).
  3. **`time_locked_dead_man_switch`:** A cryptographic commitment that releases the liquidator's access only after a defined time has passed without the agent's heartbeat, preventing premature liquidation.
  4. **`obligation_transfer_signature`:** The cryptographic proof that each escrow obligation was transferred to the liquidator, signed by both the agent (before liquidation) and the liquidator (upon takeover).

Condition of Validity

* **Applies when:** The escrow agent can be liquidated suddenly, counterparties rely on the agent's ongoing presence to complete escrows, and the transfer of obligations to a liquidator requires cryptographic proof. * **Does not apply when:** Escrows are self-executing smart contracts with no trusted intermediary, or the agent has a legal mechanism (e.g., bankruptcy court) that supersedes cryptographic proofs.

Decision Boundary

  1. **Is the escrow agent subject to liquidation?** If no, standard escrow handling applies. If yes, proceed.
  2. **Is the `liquidation_successor_public_key` pre-registered?** If no, the liquidation has no authorized successor. Counterparties must escalate to legal/arbitration channels.
  3. **Has the `liquidation_trigger_receipt` been cryptographically proven?** If no, the liquidation is unverified. The successor must not act.
  4. **Conversely, when can an escrow agent skip liquidation planning?** Only when the escrows are strictly self-executing or the agent's liquidation results in automatic, contractual return of funds to counterparties.

Operational Effect

  1. **Fund Recovery:** Enables counterparties to recover funds even if the escrow agent is liquidated.
  2. **Successor Legitimacy:** Provides cryptographic proof that the liquidator has the authority to act on the agent's behalf.
  3. **Replay Prevention:** Uses time-locked commitments to prevent replay of old transactions by attackers.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class LiquidationVerdict(Enum):
    ESCROW_ACTIVE = auto()
    LIQUIDATION_TRIGGERED = auto()
    SUCCESSOR_VERIFIED = auto()
    NO_SUCCESSOR_DESIGNATED = auto()

@dataclass(frozen=True)
class EscrowAgentState:
    agent_active: bool
    liquidation_trigger_receipt: str | None
    successor_public_key: str | None
    time_locked_switch_hash: str | None
    last_heartbeat_epoch: float

@dataclass(frozen=True)
class LiquidationPolicy:
    current_epoch: float
    heartbeat_timeout_sec: float

@dataclass(frozen=True)
class LiquidationManifest:
    verdict: LiquidationVerdict
    directive: str
    manifest_digest: str

class LiquidationSuccessorGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_state(
        cls, state: EscrowAgentState, policy: LiquidationPolicy
    ) -> LiquidationManifest:
        
        # Gate 1: No successor designated
        if not state.successor_public_key:
            return LiquidationManifest(
                verdict=LiquidationVerdict.NO_SUCCESSOR_DESIGNATED,
                directive="HALT: No successor public key. Liquidated escrows require manual recovery.",
                manifest_digest=cls._hash("NO_SUCCESSOR")
            )

        # Gate 2: Agent still active
        heartbeat_age = policy.current_epoch - state.last_heartbeat_epoch
        if state.agent_active and heartbeat_age < policy.heartbeat_timeout_sec:
            digest = cls._hash(json.dumps({
                "agent": "ACTIVE",
                "heartbeat_age": f"{heartbeat_age:.1f}"
            }, sort_keys=True))
            return LiquidationManifest(
                verdict=LiquidationVerdict.ESCROW_ACTIVE,
                directive="PROCEED: Agent is active. Escrow continues normally.",
                manifest_digest=digest
            )

        # Gate 3: Liquidation triggered
        if not state.liquidation_trigger_receipt:
            return LiquidationManifest(
                verdict=LiquidationVerdict.ESCROW_ACTIVE,
                directive="WAIT: Heartbeat timeout reached but no liquidation trigger receipt. Await oracle or manual trigger.",
                manifest_digest=cls._hash(f"WAIT:{heartbeat_age:.1f}")
            )

        # Gate 4: Successor verified
        digest = cls._hash(json.dumps({
            "successor": state.successor_public_key,
            "trigger": state.liquidation_trigger_receipt,
            "switch": state.time_locked_switch_hash or "NONE"
        }, sort_keys=True))
        
        return LiquidationManifest(
            verdict=LiquidationVerdict.SUCCESSOR_VERIFIED,
            directive=f"PROCEED: Transfer obligations to successor {state.successor_public_key}.",
            manifest_digest=digest
        )