> ## Documentation Index
> Fetch the complete documentation index at: https://trustodian-recovery.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Cardano vaults

> Rebuilding a Cardano multisig vault from its native script with cardano-cli

> [!WARNING]
>
> **There is no convenient third-party import for a Cardano multisig vault.** Cardano wallets restore
> single-signature wallets from a phrase; none of them will take a Trustodian vault's native script and
> let you spend from it.
>
> Recovering a Cardano vault means building and signing the transaction yourself with `cardano-cli`.
> This page is written for someone willing to do that. Read it before you need it, not during the
> emergency.

## What a Cardano vault is

The vault address is the hash of a **native script**, not a public key. The script is an `atLeast`
script: it lists one `sig` clause per owner, each naming that owner's payment key hash, and requires
`required` of them to sign.

There are two scripts, not one. The vault address is a base address built from both:

| Script | Role | What it controls |
| :- | :- | :- |
| Payment script | Spending | Whether a transaction may move the funds |
| Stake script | Staking | Delegation and rewards for the same address |

The address is derived from **both** script hashes together. You need the payment script to spend and
the stake script to reproduce the exact address.

> [!NOTE]
>
> A vault created without stake keys has no stake script and uses an enterprise address instead. In
> that case the Recovery Kit shows only the payment script, and the address is built from it alone.
> Such a vault can never delegate; nothing else about recovery changes.

Key hashes inside the script are sorted by their raw bytes, so the same owner set always produces a
byte-identical script, hash and address regardless of the order anyone lists them in. Do not reorder
them.

## What you need

From your **Recovery Kit**, on the vault's page:

| Field | Why |
| :- | :- |
| Vault address | What you are proving you rebuilt correctly |
| Payment native script | Required to spend. Without it the funds cannot be moved at all |
| Stake native script | Required to reproduce the exact address |
| Payment script hash | Check value for the script you rebuild |
| Stake script hash | Check value for the same |
| Threshold | How many owners must sign |
| Cosigners: derivation path, payment key hash, stake key hash | Identifies which key each owner brings |
| Birthday slot | Where to start a chain scan, instead of at genesis |

And from the owners: enough **recovery phrases** to meet the threshold. A 2-of-3 vault needs two of
the three owners present, each deriving their own key.

> [!TIP]
>
> The app can save the scripts as files directly, named
> `trustodian-vault-<id>-payment.script` and `trustodian-vault-<id>-stake.script`. They are already
> in the JSON form `cardano-cli` reads, so take those rather than retyping hex off the printed page.

### The script file format

The saved `.script` files look like this: an `atLeast` with the threshold in `required` and one
`sig` per owner:

```json theme={null}
{
  "type": "atLeast",
  "required": 2,
  "scripts": [
    { "type": "sig", "keyHash": "ec161bf7aff0f053ff5559f7c0c6ea325ac01f0281e977d8fea2360f" },
    { "type": "sig", "keyHash": "f30a06e1d8e8de2047002c038a449c2868d8a4a0ade70b58b404dc76" }
  ]
}
```

A 1-of-N vault appears as `"type": "atLeast"` with `"required": 1`, which is the form the chain
stores. That is correct and should not be rewritten as `"type": "any"`.

## The cosigner key

Each owner's vault key is **not** their personal Cardano wallet key. It lives under CIP-1854, the
multisig counterpart of CIP-1852:

```
m/1854'/1815'/{account}'
```

with role 0 index 0 as the payment key and role 2 index 0 as the stake key. The Recovery Kit prints
the account path per cosigner; `{account}` is that vault's account index, not your Cardano wallet
number.

The key derived at role 0, index 0 is the one whose hash appears in the payment script, and the one
that signs.

## Rebuilding the address

Confirm you have the right material before touching a transaction.

**1. Take the script files**

Either the saved `.script` files, or JSON you have typed from the printed page in the shape
shown above.

**2. Rebuild the address from both scripts**

```bash theme={null}
cardano-cli address build \
  --payment-script-file trustodian-vault-abc12345-payment.script \
  --stake-script-file trustodian-vault-abc12345-stake.script \
  --mainnet
```

For an enterprise-address vault, omit `--stake-script-file`.

**3. Compare it to the vault address**

The output must match the vault address in your Recovery Kit character for character. If it does
not, a key hash is wrong or the scripts were reordered. Stop and fix that before going further.

You can also hash each script on its own and compare against the printed **Payment script hash** and
**Stake script hash**, which narrows down which of the two is wrong.

## Deriving a signing key from a phrase

`cardano-cli` does not read a mnemonic. Converting a recovery phrase into a signing key file takes
the `cardano-address` tool, then a conversion step.

**1. Recover the root key from the phrase**

```bash theme={null}
cardano-address key from-recovery-phrase Shelley < phrase.txt > root.xsk
```

Write the phrase into `phrase.txt` on an offline machine, as one line of space-separated words,
and delete it afterwards.

If the account has a passphrase, this is where it is needed; check your tool version's syntax
for supplying it, and verify the resulting key hash before trusting the result.

**2. Derive the CIP-1854 multisig payment key**

```bash theme={null}
cardano-address key child 1854H/1815H/0H/0/0 < root.xsk > vault-payment.xsk
```

Replace the `0H` in the third position with the vault's own account index from the Recovery Kit.
The trailing `0/0` is role 0, index 0: the payment key. Role 2 (`1854H/1815H/0H/2/0`) is the
stake key.

**3. Check the key hash before you rely on it**

```bash theme={null}
cardano-address key public --with-chain-code < vault-payment.xsk | cardano-address key hash
```

The result must equal your **Payment key hash** row in the Recovery Kit. This is the check that
catches a wrong account index or a missing passphrase.

**4. Convert it to a cardano-cli signing key**

```bash theme={null}
cardano-cli key convert-cardano-address-key \
  --shelley-payment-key \
  --signing-key-file vault-payment.xsk \
  --out-file vault-payment.skey
```

> [!NOTE]
>
> Flag names in `cardano-cli` and `cardano-address` have changed between releases, and `cardano-cli`
> has since grown per-era command groups. Check `--help` on the version you actually have rather
> than assuming these invocations are current. The derivation path and the key hash check above are
> fixed; only the tooling around them moves.

## Spending from the vault

Each signature is produced independently and combined at the end. Owners never need to share their
keys with each other.

**1. Find the vault's UTxOs**

Query the address against a synced node, or an explorer. The **Birthday slot** from the Recovery
Kit is the earliest slot the vault could have received anything, so a scan can start there
rather than at genesis.

**2. Build the transaction, spending under the script**

The spending input is witnessed by the script: pass the payment script file alongside the input
(`--tx-in-script-file`) so the node knows what the address's payment credential resolves to.

**3. Each owner witnesses the same transaction body**

```bash theme={null}
cardano-cli transaction witness \
  --tx-body-file tx.raw \
  --signing-key-file vault-payment.skey \
  --mainnet \
  --out-file owner1.witness
```

Distribute `tx.raw` to the owners; each returns a witness file. The body must be byte-identical
for every owner, or the witnesses will not combine.

**4. Assemble and submit**

```bash theme={null}
cardano-cli transaction assemble \
  --tx-body-file tx.raw \
  --witness-file owner1.witness \
  --witness-file owner2.witness \
  --out-file tx.signed

cardano-cli transaction submit --tx-file tx.signed --mainnet
```

Supply at least `required` witnesses. Fewer, and the transaction is rejected.

## If an owner joined with an imported wallet

An owner who brought a wallet into Trustodian by importing its private key contributes that wallet's
own key, not a key derived from the account phrase. Their key hash is in the script like anyone
else's and the vault is fully recoverable, but that owner signs with the private key they imported,
which they must still hold. Their recovery phrase will not produce it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.