offline prototype

Prove the old wallet.
Bring the funds home.

Prove ownership of the compromised wallet without revealing its seed. The recovered entitlement arrives at a fresh recovery wallet you control — never back to the wallet that was breached.

offline prototype

Recover funds owed to a compromised Cardano wallet

This page visualizes the Redeemer role for a Plutus V3 recovery contract. All wallet, proof, and chain interactions are mocked. No real proving occurs, no real keys are derived, and this file makes no runtime network calls.

security crux

Three distinct key materials

CIP-30 wallets only operate on their own managed keys. The old seed and new recovery wallet are handled by this page's local code; the connected wallet only funds and submits the claim.

OLD wallet seed secret/local

The compromised 24 words are the witness. They are used locally to build the proof witness and are never sent to a wallet, server, or network endpoint.

NEW recovery wallet destination

This page generates a fresh 24-word recovery wallet in the prototype and displays destination address D. Recovered funds go here.

CONNECTED wallet fees/submit

A separate funded CIP-30 wallet pays fee and collateral, signs only those inputs, and submits the transaction. It cannot prove for old or new seeds.

0

Eligibility check simulated

Optional lookup against the public fixed eligibility snapshot. No secret is required for this step.

not checked
eligible: yes
Credential C 8f2d35b8a7d0f6d96a3d5b9b0e21f6047d919223ab6d089d642d02aa
Committed entitlement 1,250 ADA + 3 SUNDAE
Snapshot version 2026.06 / Merkle root pinned on-chain
1

Connect a funding wallet via CIP-30 mock bridge

The connected wallet pays transaction fees and collateral and submits the claim. It is not the old wallet and not the new recovery wallet.

not connected
Simulates cardano.wallet.enable(), getCollateral(), getUtxos(), and getChangeAddress().
2

Enter the OLD wallet's 24 keywords secret/local witness

Enter the compromised wallet seed locally. This prototype validates only that 24 words are present.

waiting

Your seed never leaves this device

It is used locally to build the proof witness and is never sent anywhere. In this prototype, the seed is not copied into persistent browser storage, a wallet, or a network request.

0 / 24 words present
3

Generate the new recovery wallet + destination D local destination

Recovered funds land at this fresh address, isolated from the compromised wallet. Back up the new seed before continuing.

not generated
Mock entropy, mock seed, mock addr_test1... base address.

Back up this NEW recovery seed

This is where recovered funds will be sent. In the real system this seed must be backed up before submission. This prototype seed is fake and not a valid wallet.

Destination address D
4

Generate the recovery proof locally mock Groth16 proof

Framed as heavy local computation: about 43s and about 7GB RAM in reality. This mock runs for a few seconds.

not ready

Low-RAM device? Use the native helper fallback. The system must never fall back to a remote prover.

local only
Binds C, committed entitlement, destination D, script hash, root, snapshot version, and role into one public input.
    proof ready

    Mock 336-byte proof:

    Only the proof and destination D leave the local proof boundary. The old seed, master key, derivation path, Merkle path, and witness remain local.

    5

    Submit transaction CIP-30 or relayer

    The claim transaction spends the custody UTxO and pays exactly the committed entitlement to D. Redeemer data is (proof, D, role).

    not submitted
    client-side walkthrough

    How it works

    The recovery relation proves control of the compromised wallet without revealing the seed. The on-chain verifier sees one public field element and the claim transaction; the private witness stays on the user's device.

    Stays local

    • OLD seed and master extended private key
    • Derivation path and leaf scalar
    • Merkle inclusion path and witness
    • New recovery seed

    Leaves the proof boundary

    • About 336-byte Groth16 proof
    • Public destination address D
    • Role tag in the redeemer data
    • Normal transaction data for fees and outputs

    Checked on-chain

    • One reconstructed public input pub
    • Commitment-aware Groth16 verification
    • Output credentials match D
    • Value equals committed entitlement
    pub = Fr(Blake2b-256(domain_sep || scriptHash || snapshot_version || merkle_root || C || entitlement || D || role))
    1

    Eligibility lookup: no secret

    The client queries the public eligibility snapshot, a Merkle tree over (credential C, entitlement) leaves whose root is pinned on-chain, and shows the entitlement for the user's credential.

    public snapshot
    2

    Old seed to master key: local

    The 24 words are converted via BIP39 to entropy, then to a Cardano CIP-1852 / BIP32-Ed25519 Icarus master extended private key (kL || kR || chain code, 96 bytes). This stays in memory on the device.

    local secret
    3

    Derive credential C: local

    The client walks the CIP-1852 path m/1852'/1815'/account'/role/index to the leaf scalar kL, computes the leaf public key A = compress(kL * B) on Ed25519, and sets credential C = Blake2b-224(A). It confirms C equals the eligible snapshot leaf.

    local derivation
    4

    New recovery wallet plus destination D: local

    The client generates fresh entropy, a new 24-word seed, a new master key, and a fresh base address D (header || payment-credential-28 || stake-credential-28, 57 bytes). The new seed is shown for backup.

    new wallet
    5

    Assemble the witness: local and secret

    The witness includes the old master key, the Merkle inclusion path (siblings and directions) for C, the entitlement, snapshot_version, scriptHash, root, destination D, and role.

    witness
    6

    Compute the public input: local

    The digest is read little-endian and reduced modulo the BLS12-381 scalar field order: pub = Fr(Blake2b-256(domain_sep || scriptHash || snapshot_version || root || C || entitlement || D || role)).

    one field
    7

    Generate the Groth16 proof: local and heavy

    The client runs the about 3.45M-constraint recovery circuit prover in WASM, or in the downloadable native helper, about 43s and about 7GB RAM in reality. The proof carries a Pedersen (BSB22) commitment forced by the circuit's lookup-table hashing. Only the about 336-byte proof and public destination D leave the device; the seed and witness never do.

    local proving
    8

    Build the claim transaction

    Input is the custody UTxO at the contract. Output is exactly the entitlement to D. Redeemer data is (336-byte proof, D, role). Fee and collateral come from the connected CIP-30 wallet.

    transaction
    9

    Sign and submit

    The connected wallet partial-signs only the fee and collateral inputs with signTx(tx, true) and submits with submitTx(tx), or the operator relayer submits if chosen.

    submit
    replayable animation

    On submit: redemption contract path

    This animation shows what happens when the claim transaction reaches the redemption smart contract. Toggle the rejected branch to see a tampered proof fail without moving funds.

    tx

    1. Connected wallet

    Claim transaction leaves with the 336-byte proof plus destination D.

    2. Custody UTxO

    It arrives at the redemption contract's locked custody UTxO.

    3. Reconstruct pub

    Authenticated on-chain data plus D and role converge into one field element.

    4. Groth16 verifier

    Commitment-aware verification checks the proof, commitment point, and final pairing.

    5. Payout check

    Output credentials match D and value equals the committed entitlement.

    6. Nullifier spent

    The nullifier for C is marked spent, preventing another claim.

    7. Funds to D

    1,250 ADA + 3 SUNDAE flow to the fresh recovery wallet.

    Result

    Ready to play.

    Validator reconstructs the single public input pub
    domain_sep scriptHash snapshot_version merkle_root C entitlement D role Fr(Blake2b-256(...))
    Commitment-aware verifier: reconstruct + check commitment point; Pedersen proof-of-knowledge pairing; derive challenge e_cmt; fold vk_x = K0 + pub*K1 + e_cmt*K2 + commitment; check e(A,B) = e(alpha,beta) * e(vk_x,gamma) * e(C,delta).
      product requirements

      Redeemer requirements

      Plain English first, then the same role requirements in EARS notation.

      Plain English

      1. A redeemer can check whether a compromised credential is eligible and see its committed entitlement before entering any secret.
      2. The redeemer enters the OLD wallet's 24-word seed into the client only; it stays in local memory and is never transmitted to any server, wallet, or network endpoint.
      3. The client derives, locally from the old seed, the master key and the eligibility credential C, and confirms C matches an eligible snapshot leaf.
      4. The client generates a fresh recovery wallet, derives destination address D from it, and prompts the user to back up the new seed; recovered funds go only to D.
      5. The client generates the zero-knowledge recovery proof locally, with a downloadable native helper fallback for low-RAM devices, binding C, the committed entitlement, and D. Proving is always local; it never falls back to remote proving.
      6. The redeemer connects an existing funded wallet via CIP-30 to provide fees plus collateral and submit the claim; an operator relayer is the fallback for users with no spare ADA.
      7. The client builds the claim transaction that spends the custody UTxO and pays exactly the committed entitlement to D, with the redeemer data being (proof, D, role).
      8. Submission goes through the connected CIP-30 wallet by default, and the recovered entitlement arrives at D.
      9. The payout is cryptographically bound to the proven destination D, so a revealed proof cannot be redirected or front-run to another address.
      10. Re-claiming the same credential is impossible because a nullifier marks it spent; a second attempt releases nothing.
      11. The redeemer can independently verify both eligibility through snapshot inclusion of C and that the payout equals the committed entitlement.
      12. The single-claim path is permissionless and un-starvable: no operator or third party can block or censor an honest claim.

      EARS Notation

      ID Type Requirement
      REQ-RD-01 ubiquitous The redeemer client shall let a user look up a credential's eligibility and committed entitlement from the published snapshot without entering any secret.
      REQ-RD-02 ubiquitous The redeemer client shall hold the old wallet's 24-word seed only in local device memory and shall never transmit it to any server, wallet, or network endpoint.
      REQ-RD-03 event-driven When the user submits the old seed, the client shall derive the master key and credential C locally and confirm C matches an eligible snapshot leaf.
      REQ-RD-04 event-driven When the old seed is accepted, the client shall generate a fresh 24-word recovery wallet, derive the destination address D, and display the new seed for backup before proceeding.
      REQ-RD-05 event-driven When the user requests proof generation, the client shall generate the zk recovery proof locally, binding C, the entitlement, and D into the single public input.
      REQ-RD-06 state-driven While no funded wallet is connected, the client shall prevent submission and prompt the user to connect a CIP-30 wallet or choose the relayer.
      REQ-RD-07 event-driven When the proof is ready, the client shall build the claim transaction that spends the custody UTxO and pays exactly the committed entitlement to D, with redeemer = (proof, D, role).
      REQ-RD-08 event-driven When the user submits, the client shall submit via the connected CIP-30 wallet by default, or via the operator relayer when selected.
      REQ-RD-09 unwanted If the device cannot complete in-browser proving, then the client shall offer the downloadable native helper and shall never send the seed off-device for remote proving.
      REQ-RD-10 unwanted If a credential's nullifier is already spent, then the claim shall be rejected and no funds released.
      REQ-RD-11 ubiquitous The payout shall be cryptographically bound to the proven destination D so it cannot be redirected or front-run.
      REQ-RD-12 optional Where the user requests it, the client shall verify snapshot inclusion of C and that the payout equals the committed entitlement.
      REQ-RD-13 ubiquitous The redeemer client shall display, at all times, which of the three key materials each field concerns: old seed = secret/local; new wallet = destination; connected wallet = fees/submit.