Corridor Reference
Every Corridor movement carries the same 32-byte Corridor Reference: in Stellar’s MEMO_HASH, in a Solana SPL Memo on a cross-chain spoke, and in the anchor’s own SEP-31 fields. Reconciliation is a join on this value.
MEMO_HASH is exactly 32 bytes, which is exactly a Corridor Reference: no truncation, no encoding games, and the memo is part of the signed transaction rather than a separate operation.
Byte layout (version 2)
Section titled “Byte layout (version 2)”| Bytes | Field | Notes |
|---|---|---|
| 0 | Version | 0x02 |
| 1 | Kind | payout 0x01 · batch 0x02 · batch item 0x03 · sweep 0x04 · deposit 0x05 · collection 0x06 |
| 2 | Funding currency | One-byte index from the currency table |
| 3 | Beneficiary currency | Same table, so GHS-HKD needs no registry change |
| 4 | Flags | bit 0 = shielded, reserved for the confidential lane |
| 5–20 | Payout id | UUIDv7 (16 bytes): time-ordered, globally unique |
| 21–27 | Customer tag | 7-byte keyed hash (HMAC) of the customer id: links payments to a customer without naming them on a public ledger |
| 28–31 | Checksum | First 4 bytes of SHA-256 over bytes 0–27 |
A real one, decoded
Section titled “A real one, decoded”This is the reference from the Stellar testnet smoke run of 17 October 2026, read back out of the transaction’s MEMO_HASH:
0x 02 01 14 29 00 01a1494e71ec7073bfffc84d758d67d2 c6cf255f57adc6 fd6cb505 │ │ │ │ │ │ │ └ checksum ✓ │ │ │ │ │ │ └ customer tag (keyed hash) │ │ │ │ │ └ payout id 01a1494e-71ec-7073-bfff-c84d758d67d2 (UUIDv7) │ │ │ │ └ flags: public lane │ │ │ └ to: 0x29 = 41 = KES │ │ └ from: 0x14 = 20 = EUR │ └ kind: payout └ version 2Where it lives
Section titled “Where it lives”| Venue | Field | Why it fits |
|---|---|---|
| Stellar | The transaction’s MEMO_HASH |
32 bytes exactly, and part of the signed transaction rather than a separate operation |
| Solana | SPL Memo instruction in the same transaction | The native pattern partners already read, on the one corridor that leaves the hub |
| Anchor | SEP-31 transaction fields, echoed into the bank narration | A bank statement line traces back to the payout that caused it |
Reconciliation is a join, not an index
Section titled “Reconciliation is a join, not an index”Horizon cannot be asked for a memo network-wide, the way an indexed event topic can. It does not need to be: Corridor reads the payment stream of the account it already watches and filters on the memo.
const found = await findTransfersByReference(client, { account: treasury.address, reference });That stream is per-vault and bounded, a stored paging token makes restarts neither miss nor double-count, and no indexer sits in the path. See ADR 0003.
import { encodeRef, decodeRef, customerTag, RefKind, uuidv7 } from "@corridor/core";
const ref = encodeRef({ kind: RefKind.Payout, corridor: "EUR-KES", shielded: false, id: uuidv7(), customerTag: customerTag("eu-payco", process.env.CORRIDOR_TAG_SECRET!),});
decodeRef(ref); // validates version, kind and checksum; throws on any mismatchDesign notes
Section titled “Design notes”- Fixed 32 bytes is the size of
MEMO_HASH, so the hub carries it with no encoding overhead. - The currency pair is in the reference, so any observer knows the corridor without a lookup.
- No PII on-chain. The customer tag is a keyed hash: anyone can see two payments share a tag, but only Corridor can say whose it is.
- Versioned. Version 2 encodes the currency pair directly rather than a corridor id, so any spoke is addressable without a registry change; decoders reject unknown versions.
- Horizon needs
join=transactionsfor the memo to arrive with the payment. Getting that wrong loses every memo silently, so it is covered by tests.