Skip to main content

Quick Reference Card

Everything you need on one page.

Commitment Formula

commitment = Poseidon(
Poseidon(secret, nullifierSecret),
Poseidon(dataHash, blinding)
)

Nullifier Formula

nullifier = Poseidon(
Poseidon(nullifierSecret, commitment),
leafIndex
)

Public Input Order

publicInputs[0] = root
publicInputs[1] = nullifier
publicInputs[2] = dataHash
publicInputs[3] = recipient
danger

Get this order wrong = silent InvalidProof revert.

Field Size

21888242871839275222246405745257275088548364400416034343698204186575808495617

All values must be < FIELD_SIZE.

Contract Addresses

ContractAddress
GhostVault0x9A8F98916d324153B61F1C4B6D7d85F52988b3b1
CommitmentTree0xe382a7C7a5CE3B9250D73aE6ab97931E5798e6F7
NullifierRegistry0x03972E4453fD143A5203602020DbE2f7DcF6e0db
ProofVerifier0x2Bc0ac5508FF31A0Ad055A1F823C7653F48D37bE

Network

PropertyValue
Chain ID47474
RPChttps://testnet-rpc.umbraline.com
Explorerhttps://umbrascan.com
Native TokenGHOST

Commit

vault.commit{value: 0.001 ether}(commitment) → leafIndex

Reveal

vault.verifyAndNullify(proof, nullifier, root, dataHash, recipient) → bool

Error Codes

ErrorMeaning
InvalidProofZK proof verification failed
InvalidRootMerkle root not found (stale?)
NullifierAlreadySpentAlready revealed
InsufficientFeeNeed ≥ 0.001 GHOST
DataHashMismatchOff-chain ≠ on-chain

Gas Estimates

OperationGas
commit()~75K-100K
commitWithCallback()~100K-150K
verifyAndNullify()~150K-200K
revealWithCallback()~200K-300K

Proof Struct

struct Proof {
uint256[2] a; // G1 point
uint256[2][2] b; // G2 point
uint256[2] c; // G1 point
}

snarkjs → Contract Conversion

// snarkjs output → contract input
{
a: [pi_a[0], pi_a[1]],
b: [[pi_b[0][1], pi_b[0][0]], [pi_b[1][1], pi_b[1][0]]], // SWAPPED!
c: [pi_c[0], pi_c[1]],
}

The One Thing to Remember

Critical

Your off-chain dataHash computation must EXACTLY match your on-chain computeDataHash.

If they don't match, nothing will work.