Skip to main content

Note Disclosure

A note disclosure key is a compact, shareable string that lets you prove the exact contents of a shielded note — value, asset, and commitment — to any third party, without granting them any ability to spend the note.

This is Orbinum's mechanism for voluntary transparency: you remain private by default, but you can selectively reveal a note when you choose to.


Background: what a note is

When you shield tokens, the runtime records a commitment on-chain — a hash of the note's plaintext contents. The commitment is all that is ever publicly visible.

commitment = Poseidon4(value, asset_id, owner_pk_ax, blinding)
FieldDescription
valueToken amount (e.g. 100000000000000000000000 for 100 000 ORB)
asset_idAsset identifier (0 = ORB)
owner_pk_axx-coordinate of the owner's Baby JubJub public key
blindingRandom 32-byte scalar preventing brute-force preimage recovery

Because the commitment is a one-way hash, anyone can see it on-chain but no one can determine what it contains. A disclosure key solves this: it encodes the four inputs so that any holder can verify the hash and confirm the note's contents.


The disclosure key format

A disclosure key is a string with the prefix orbdisc: followed by a base64url-encoded JSON payload:

orbdisc:<base64url(JSON)>

The JSON payload (version 1):

{
"v": 1,
"c": "0x2d0aba56a9fb579a007a1980a6164f248682546aa460ea46956be66fabeb8ea7",
"val": "0x152d02c7e14af6800000",
"aid": "0x0",
"opk": "0x1d4a09a1ab9ffff52fbef32c393617d70c03c92835bc6331fbb4a45e381d9735",
"bld": "0x2dcbb6f4d229a19c91e1a4ee057bafa3a96573fa3655964486 4c811b13ae8d62"
}

All numeric values use little-endian hex — the canonical representation of BN254 field elements as used by the ZK circuit.

Commitment endianness

The same commitment appears in two forms in the system. On-chain (Substrate, block explorers) it is stored as big-endian H256. Inside the circuit and in the disclosure key it is stored as a little-endian BN254 field element. They are byte-reversed versions of each other.

Example:

On-chain (big-endian):   0xa78eebab6fe66b9546ea60a46a548286244f16a680197a009a57fba956ba0a2d
Disclosure key (LE): 0x2d0aba56a9fb579a007a1980a6164f248682546aa460ea46956be66fabeb8ea7

Both represent the same commitment.


What a disclosure key reveals

Revealed
  • Exact token amount (value)
  • Asset type (asset_id)
  • Owner's Baby JubJub public key (x-coordinate)
  • Blinding scalar
  • Commitment (cryptographically verified)
Never revealed
  • Spending key
  • Nullifier (cannot be derived without spending key)
  • EVM wallet address
  • Any other note owned by the same key
  • Transaction history

Two separate key systems

Understanding why the owner public key is different from the EVM wallet address requires knowing how Orbinum's key system works.

Every user operates two independent key pairs:

EVM keypairBaby JubJub keypair
Curvesecp256k1Baby JubJub (twisted Edwards)
Where it livesEthereum wallet (MetaMask, etc.)Derived inside the SDK
Public identifierEVM address (0xf24ff3a9...)BJJ point x-coordinate (0x1d4a09a1...)
PurposeSigns transactions, pays gasOwns shielded notes, used in ZK circuits

The BJJ keypair is ZK-friendly: scalar multiplications on Baby JubJub cost ~3 000–4 000 R1CS constraints inside a Groth16 circuit, versus hundreds of thousands for secp256k1. This is why notes use BJJ keys internally rather than EVM keys.

Spending key derivation

EVM wallet
└─ signMessage("orbinum-spending-key-v1:<address>")
└─ HKDF-SHA256(signature bytes)
└─ spending_key (BJJ scalar, kept secret)
└─ owner_pk = spending_key · G (BJJ point, Ax stored on-chain)

The spending key never leaves the SDK. The public key (owner_pk.Ax) is what gets embedded in the note commitment. It cannot be reverse-mapped to the EVM wallet address.


Cryptographic verification

When a recipient decodes a disclosure key, they recompute the commitment from the revealed inputs and compare it against the embedded "c" field:

recomputed = Poseidon4(value, asset_id, owner_pk_ax, blinding)
valid = (recomputed === commitment)

This is a cryptographic proof of knowledge of the preimage. A forged key with an incorrect value will produce a different hash and fail verification. The decodeNoteDisclosureKey function in the SDK returns null for any key that fails this check.


How to generate and verify a disclosure key

Generating a key (note owner)

import { createNoteDisclosureKey } from '@orbinum/sdk/shielded-pool/protocol';

const key = createNoteDisclosureKey(note);
// "orbdisc:eyJ2IjoxLCJjIjoi..."

The note must be a ZkNote — a decrypted note object held by the owner. The function extracts value, assetId, ownerPk, and blinding from the note and serialises them as a disclosure key. The spending key is never included.

Verifying a key (any third party)

import { decodeNoteDisclosureKey } from '@orbinum/sdk/shielded-pool/protocol';

const disclosure = decodeNoteDisclosureKey(key);

if (disclosure === null) {
// key is malformed, tampered, or contains an incorrect preimage
} else {
console.log('value :', disclosure.value); // bigint
console.log('assetId :', disclosure.assetId); // bigint
console.log('commitment verified ✓');
}

No network call is required. Verification is a local Poseidon hash computation.


Use cases

Proof of solvency

Share a disclosure key to prove that a specific commitment holds a given amount, without revealing your full balance or other notes.

Auditor compliance

Provide a disclosure key to a regulator or auditor for a specific note. They can verify the amount and asset without seeing the rest of your portfolio.

Dispute resolution

Prove to a counterparty that a payment was made by sharing the disclosure key for the output note of a private transfer.


Security properties

One disclosure key per note

Each key covers exactly one note. Sharing a disclosure key does not reveal any information about other notes belonging to the same owner, even if they use the same Baby JubJub key.

The blinding scalar is in the key

A disclosure key encodes the blinding value. Anyone with the key can reconstruct the full Poseidon4 preimage and confirm the note's contents. Do not share disclosure keys with parties you do not intend to inform.

Spending capability is not transferred

The spending key — which is required to generate a nullifier and spend the note — is never included in the disclosure key. A recipient of a disclosure key cannot spend or freeze the note.