# Igra Exit Bridge Message Format

Last updated on: 2026-04-17

## Document Version History

| Version | Date       | Author    | Description                                                                                           |
| ------- | ---------- | --------- | ----------------------------------------------------------------------------------------------------- |
| v1.0    | 2026-04-16 | IGRA Team | Initial release: Body Format Version 1 wire layout, field encoding rules, and Kaspa recipient default |
| v1.1    | 2026-04-17 | IGRA Team | Align terminology with accepted implementation                                                        |

***

## Introduction

Igra Exit Bridge allows users to burn iKAS on the Igra chain and receive native KAS on Kaspa. The bridge smart contract (`KasExitBridge`) on Igra processes exit requests (transactions) submitted (sent) by users. On a successful exit request, `KasExitBridge` calls `dispatch(...)` on the Hyperlane `Mailbox` contract deployed on Igra, producing EVM events (logs) containing the "exit message" - a Hyperlane outbound message targeted at the endpoint on Kaspa of Igra Exit Bridge. Kaspa-side release actors observe these events (logs) directly on Igra and validate exit messages the events contain — the actors fulfill a role analogous to Hyperlane validators and relayers for standard EVM-to-EVM routes. After exit message validation and finality policy is satisfied, Kaspa-side actors unlock the KAS on Kaspa.

This spec defines the format of the exit message used for this exit flow: the body - a payload specific to Igra Exit Bridge - is included into the Hyperlane standard outer envelope. This specification defines the version 1 of the body format; other format versions may be defined in future revisions.

**Terminology note.** "Exit message" refers specifically to a Hyperlane outbound message carrying the body format defined in this specification and produced by `KasExitBridge`. "Dispatched message" refers to any Hyperlane outbound message after it has been processed by `Mailbox` — i.e., `Mailbox.dispatch(...)` was called and the corresponding events were emitted. Every exit message becomes a dispatched message upon successful dispatch.

## Constants

* Origin chain: `IGRA` (EVM-compatible)
* Origin Hyperlane domain: `38833`
* Origin Hyperlane domain hex: `0x97b1`
* Destination chain / Hyperlane domain: `KASPA`
* `destinationDomain`: `1262570320`
* `destinationDomain` hex: `0x4B415350`
* `destinationDomain` mnemonic: `KASP`
* Kaspa bridge-lock address: `kaspa:ppvnxxzm0rr37zpnwux2f2ntvfpr4uqdpm7zsvsztg3en92r7gs0wkmr72q9n`
* Outer recipient preimage: `igra:v1:bridge-lock:kaspa:ppvnxxzm0rr37zpnwux2f2ntvfpr4uqdpm7zsvsztg3en92r7gs0wkmr72q9n`
* Outer recipient hash: `0x12f926e6d45ae9ace4f1fae2b542f7391bb6af8a321ee48dc60aa57507a56cf3`
* Outer sender: `bytes32(address(KasExitBridge))`
* Body version namespace:
  * `0`: reserved / invalid
  * `1`: body format version 1
  * other values: reserved for future body versions
* Message type namespace:
  * `0`: reserved / invalid
  * `1`: `burn_unlock`

## Hyperlane Outer Envelope

Igra Exit Bridge uses non-modified Hyperlane outer envelope.

### Layout

```
version(1)
nonce(4)
originDomain(4)
sender(32)
destinationDomain(4)
recipient(32)
body(variable)
```

### Field Values For Igra Exit Bridge

| Field               | Value                                                                                                                                            |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `version`           | `0x03` - Hyperlane Mailbox version                                                                                                               |
| `nonce`             | Mailbox outbound nonce (assigned by Mailbox)                                                                                                     |
| `originDomain`      | `0x97b1` - Igra                                                                                                                                  |
| `sender`            | `bytes32(address(KasExitBridge))`                                                                                                                |
| `destinationDomain` | `0x4B415350` - Kaspa                                                                                                                             |
| `recipient`         | effective `kaspaBridgeEndpoint` as configured in `KasExitBridge` (default: `0x12f926e6d45ae9ace4f1fae2b542f7391bb6af8a321ee48dc60aa57507a56cf3`) |
| `body`              | bridge payload, encoded according to Body Format (Version 1)                                                                                     |

The outer `sender` identifies the contract that called `Mailbox.dispatch` and carries protocol authority. The outer `recipient` identifies the Kaspa bridge endpoint; the final user payout address is encoded inside the body. The `recipient` value is the `kaspaBridgeEndpoint` storage parameter of `KasExitBridge` (refer to [IGRA Tech Specs - KasExitBridge](/igralabs-docs/for-developers/architecture/specifications/kasexitbridge.md)) — configurable by the bridge owner — which defaults to the hash of the preimage `igra:v1:bridge-lock:kaspa:ppvnxxzm0rr37zpnwux2f2ntvfpr4uqdpm7zsvsztg3en92r7gs0wkmr72q9n`.

## Body Format

The Version 1 of the body format follows. Other format versions may be defined in future revisions of this specification.

### Layout

```
byte 0     : header = (body_version << 4) | message_type
bytes 1-4  : burn_id
bytes 5-12 : amount_u64_be
bytes 13-32: origin_burner_evm_address_20
byte 33    : final_recipient_len_u8
bytes 34.. : final_recipient_utf8
```

### Field Values

* `body_version`: `1`
* `message_type`: `1`
* `header`: `0x11`

### Field Semantics

* `burn_id`: exit request identifier; equals `uint32(requestId)` as assigned by `KasExitBridge`. Unique per successful exit.
* `amount_u64_be`: amount of native KAS to unlock, in the KAS smallest unit (SOMPI), encoded big-endian.
* `origin_burner_evm_address_20`: 20-byte EVM address of the Igra account whose iKAS was burned.
* `final_recipient_len_u8`: byte length of `final_recipient_utf8`.
* `final_recipient_utf8`: Kaspa payout address as a UTF-8 string (e.g., `kaspa:...`).

## Dispatch Call

`KasExitBridge` produces the exit message by calling:

```solidity
mailbox.dispatch(
    0x4B415350,   // destinationDomain: Kaspa
    kaspaBridgeEndpoint, // effective recipient: configurable in KasExitBridge; default 0x12f926e6d45ae9ace4f1fae2b542f7391bb6af8a321ee48dc60aa57507a56cf3
    body
);
```

## Hyperlane Smart Contract Events

Hyperlane smart contracts on Igra emit EVM events (logs) when an exit message is dispatched.

Kaspa-side release actors observe `Dispatch` events for the Kaspa destination in order to validate and process exit messages. The actors may also observe `DispatchId` and `InsertedIntoTree` events to validate Igra attesters' attestations to checkpoints agreed between attesters and Kaspa-side release actors.

Igra attesters may observe `InsertedIntoTree` events in order to attest to the state (root) of the Merkle tree of dispatched message IDs (maintained by `MerkleTreeHook`) at checkpoints; this provides Kaspa-side release actors with additional confidence in authenticity of exit messages.

## Annex A: Relevant Hyperlane Smart Contract References

### `Mailbox` smart contract

On `Mailbox.dispatch(...)` call it formats the standard Hyperlane message and emits `Dispatch` and `DispatchId` events.

Deployed on Igra at address `0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7`.

> The `sender` field of the Hyperlane message outer envelope equals the immediate `msg.sender` of the `Mailbox.dispatch` call. `KasExitBridge`, not the user EOA, must call `Mailbox.dispatch(...)` directly.

#### `dispatch` function

```solidity
/**
 * @notice Dispatches a message to the destination domain & recipient
 * using the default hook and empty metadata.
 * @param _destinationDomain Domain of destination chain
 * @param _recipientAddress Address of recipient on destination chain as bytes32
 * @param _messageBody Raw bytes content of message body
 * @return The message ID inserted into the Mailbox's merkle tree
 */
function dispatch(
    uint32 _destinationDomain,
    bytes32 _recipientAddress,
    bytes calldata _messageBody
) external payable override returns (bytes32
```

Expected values for messages on Igra Exit Bridge:

* `_destinationDomain`: `0x4B415350`
* `_recipientAddress`: the effective `kaspaBridgeEndpoint` configured in `KasExitBridge`; the default value when unset is `0x12f926e6d45ae9ace4f1fae2b542f7391bb6af8a321ee48dc60aa57507a56cf3`
* `message`: body in Version 1 format

#### `Dispatch` event

Emitted by

```solidity
event Dispatch(
    address indexed sender,
    uint32 indexed destination,
    bytes32 indexed recipient,
    bytes message
);
```

Expected values for messages on Igra Exit Bridge:

* `sender`: `KasExitBridge` contract address
* `destination`: `0x4B415350`
* `recipient`: effective `kaspaBridgeEndpoint` configured in `KasExitBridge` (default: `0x12f926e6d45ae9ace4f1fae2b542f7391bb6af8a321ee48dc60aa57507a56cf3`)
* `message`: full exit message bytes (outer envelope + body)

Use: Kaspa-side release actors and indexers use this event to obtain the full exit message bytes.

#### `DispatchId` event

```solidity
event DispatchId(bytes32 indexed messageId);
```

Expected value:

* `messageId = keccak256(message)`

Use: stable message identity for observability and correlation.

### `MerkleTreeHook` smart contract

It inserts the message id into the Merkle tree of dispatched message IDs and emits `InsertedIntoTree` event.

Deployed on Igra at address `0x75719C858e0c73e07128F95B2C466d142490e933`.

#### `InsertedIntoTree` event

```solidity
event InsertedIntoTree(bytes32 messageId, uint32 index);
```

Expected values:

* `messageId`: same message id as above
* `index`: leaf index in the Merkle tree of dispatched message IDs

> Kaspa-side release actors and Igra attesters may use the leaf index and `messageId` to prove inclusion of exit messages in the Merkle tree of dispatched message IDs.

## Related Documents

* [IGRA Tech Specs - KasExitBridge](/igralabs-docs/for-developers/architecture/specifications/kasexitbridge.md)

## Open Questions

None.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://igra-labs.gitbook.io/igralabs-docs/for-developers/architecture/specifications/igra-exit-bridge-message-format.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
