Skip to content

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.

Corridor Reference byte layout and carriers
Fig. 04 · The 32-byte Corridor Reference of a real Stellar testnet payout, and the field that carries it on each venue.
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

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 2
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

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 mismatch
  • 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=transactions for the memo to arrive with the payment. Getting that wrong loses every memo silently, so it is covered by tests.