Decommit from Hydra

Decommit is the process of withdrawing UTxOs from an active Hydra Head back to Cardano Layer 1. This lets you exit the Layer 2 environment and make your assets available on the main chain.

Understanding decommit

What is decommit?

Decommit removes UTxOs from the Hydra Head and returns them to Layer 1 while the head remains open. Think of it as moving funds from the express lane back to the main road.

text
┌─────────────────────────────────────────┐
│         Hydra Head (Layer 2)            │
│                                         │
│  Your UTxOs: [UTxO1, UTxO2, UTxO3]      │
│                                         │
└──────────────┬──────────────────────────┘
               │
               │ DECOMMIT
               │ (Remove from Head, return to L1)
               │
┌──────────────▼──────────────────────────┐
│         Cardano Layer 1                 │
│                                         │
│  Available: [UTxO1, UTxO2, UTxO3]       │
│  Back on main chain                     │
│                                         │
└─────────────────────────────────────────┘

Decommit process

When should you decommit?

You should decommit when you want to withdraw assets from a Hydra Head without closing the Head. For example: moving assets back to your Layer 1 wallet or withdrawing a portion to use on Cardano mainnet.

Steps to decommit

  1. Connect to the Hydra node via Hydra Bridge.
  2. Query current UTxOs in the Hydra Head.
  3. Build a decommit transaction with the UTxOs you want to withdraw.
  4. Sign the transaction with your wallet.
  5. Submit the decommit transaction to the Hydra node.
  6. Monitor the snapshot status and confirm the UTxOs have returned to Layer 1.
ts
// nodejs-playground/src/hydra/decommit.ts
import { TxBuilder } from '@hydra-sdk/transaction'
import { HydraBridge } from '@hydra-sdk/bridge'
import { wallet, walletAddress } from './common'
import { Resolver } from '@hydra-sdk/core'

async function main() {
    const bridge = new HydraBridge({
        url: 'ws://localhost:4001'
    })
    const connected = await bridge.connect()
    if (!connected) {
        throw new Error('Failed to connect to Hydra node')
    }
    console.log('>>> Connected to Hydra node')

    const utxoObj = await bridge.querySnapshotUtxo()

    const addrUtxos = await bridge.queryAddressUTxO(walletAddress)
    const txBuilder = new TxBuilder({
        isHydra: true,
        params: {
            minFeeA: 0,
            minFeeB: 0
        }
    })
    const tx = await txBuilder
        .setInputs([
            addrUtxos[0] // utxo to decommit
        ])
        .addOutput({
            address: walletAddress,
            amount: [{ unit: 'lovelace', quantity: String(2_000_000) }]
        })
        .changeAddress(walletAddress)
        .complete()

    const signedCbor = await wallet.signTx(tx.to_hex())
    const txId = Resolver.resolveTxHash(signedCbor)

    const rs = await bridge.decommit({
        cborHex: signedCbor,
        txId,
        timeout: 30000
    })
    console.log('>>> Submit tx result:', rs)
}
main()

Run

bash
npx tsx src/hydra/decommit.ts
plaintext
>>> Connected to Hydra node
>>> Snapshot UTxO: {
    'dee1097738688441b2bcf90d9a20ad8eca859375ffdb8f1fde0f78f461345435#0': {
        address: 'addr_test1qpxsf0x8xypuhq5k408f9kh0meyy6jv2lxgqw2fefvjlte0u06dugtmxuhhw8hschdn4q59g64q5s9z42ax6qyg7ewsqt6e548',
        datum: null,
        datumhash: null,
        inlineDatum: null,
        inlineDatumRaw: null,
        referenceScript: null,
        value: { lovelace: 2000000 }
    },
    'dee1097738688441b2bcf90d9a20ad8eca859375ffdb8f1fde0f78f461345435#1': {
        address: 'addr_test1qpxsf0x8xypuhq5k408f9kh0meyy6jv2lxgqw2fefvjlte0u06dugtmxuhhw8hschdn4q59g64q5s9z42ax6qyg7ewsqt6e548',
        datum: null,
        datumhash: null,
        inlineDatum: null,
        inlineDatumRaw: null,
        referenceScript: null,
        value: { lovelace: 16000000 }
    }
}
>>> Submit tx result: {
    decommitTxId: '7318b97f468ad3dcc91b2fab180995f1bd62b4c49f68d83b3f30b5d8bffc49e4',
    headId: '7489fdc412ff71a7831ed508e73b2872482099fd4d97ed73663c70f8',
    seq: 227169,
    tag: 'DecommitApproved',
    timestamp: '2025-11-26T08:22:34.822012138Z',
    utxoToDecommit: {
        '7318b97f468ad3dcc91b2fab180995f1bd62b4c49f68d83b3f30b5d8bffc49e4#0': {
            address: 'addr_test1qpxsf0x8xypuhq5k408f9kh0meyy6jv2lxgqw2fefvjlte0u06dugtmxuhhw8hschdn4q59g64q5s9z42ax6qyg7ewsqt6e548',
            datum: null,
            datumhash: null,
            inlineDatum: null,
            inlineDatumRaw: null,
            referenceScript: null,
            value: [Object]
        }
    }
}

Decommit message received on Hydra Head WebSocket

json
{
        "distributedUTxO": {
                "e492f070c2f3449273a1dc6c98391992ea46c88cb2b47c38043926415fe9f8f7#0": {
                        "address": "addr_test1qpxsf0x8xypuhq5k408f9kh0meyy6jv2lxgqw2fefvjlte0u06dugtmxuhhw8hschdn4q59g64q5s9z42ax6qyg7ewsqt6e548",
                        "datum": null,
                        "datumhash": null,
                        "inlineDatum": null,
                        "inlineDatumRaw": null,
                        "referenceScript": null,
                        "value": {
                                "lovelace": 2000000
                        }
                }
        },
        "headId": "7489fdc412ff71a7831ed508e73b2872482099fd4d97ed73663c70f8",
        "seq": 227171,
        "tag": "DecommitFinalized",
        "timestamp": "2025-11-26T08:23:07.390634932Z"
}

Best practices

  • Only decommit when necessary to avoid unnecessary Layer 1 fees.
  • Carefully check UTxOs before decommitting to avoid asset loss due to mistakes.
  • Monitor the snapshot status after decommit to ensure UTxOs have returned to Layer 1.
  • If the Head is closed, all UTxOs will be returned to Layer 1 automatically (no manual decommit needed).

References