Midnight’s move to mainnet turns an architectural question into a product question. What should a reusable token, credential or membership standard look like when a blockchain application can keep balances, transfer amounts and eligibility inputs private by default?
That is not a cosmetic variation on ERC-20 or ERC-721. Those standards emerged from a public ledger model in which a contract’s storage is generally readable, a transfer is visible, and an event log is a convenient common record for wallets, indexers and exchanges. A Compact library for Midnight has to work under a different premise: applications can expose a public contract interface while proving the sensitive parts of a state transition in private.
CoinDesk has described Midnight as a zero-knowledge network with a dual-state design that separates public and private data and permits controlled disclosure. Its reporting has also said OpenZeppelin is developing Compact libraries analogous to familiar ERC-20 and ERC-721 building blocks, adapted for Midnight’s environment.
The important word is “adapted.” A private asset library cannot merely rename balanceOf, hide a few fields and call the result confidential. It needs a different model of ownership, supply accounting, authorization and observability. The standard must tell wallets what they can safely display, tell auditors what can be proven, and tell developers which privacy choices are fixed by the library and which remain the application’s responsibility.
This is a near-term design agenda, not a claim that a final OpenZeppelin Compact token API has already been published. The useful question for developers is what such an API should guarantee.
A token is no longer a public mapping
On Ethereum, an ERC-20 token can be summarized as a public mapping:
address => balance
A transfer changes two entries in that mapping. The caller authorizes the change through a signature, the contract checks that the sender has enough tokens, then emits a Transfer event. Any wallet, explorer or compliance tool can independently reconstruct balances and transaction history from public chain data.
A privacy-aware asset generally cannot work that way. Publishing a balance mapping would defeat the purpose. Instead, value may be represented by private notes, commitments or encrypted records. A user proves that they control valid input notes and that the proposed outputs preserve the asset’s rules, without placing the amount or the note secrets on the public ledger.
That does not mean the chain sees nothing. A ledger still needs durable public facts that stop double spends and let validators verify a transaction. It can see commitments to newly created private state, nullifiers that mark old private state as spent, and a proof that the transition followed the circuit’s rules.
The distinction matters because these objects do different jobs.
A private witness is the information available to the prover. It can include a secret key, the opening of a commitment, the amount inside a note, a membership credential, a Merkle path proving that a note exists in the current state, and the randomness used to hide it. The witness should not become a contract argument or a public event.
A state commitment is a cryptographic digest of private state. For a private token note, a simplified commitment might bind together asset identifier, amount, owner public key, secret randomness and perhaps a policy identifier. Validators can store the commitment without learning the underlying amount or owner.
A nullifier is a unique public marker derived from a spent note and an authority secret. The system rejects a nullifier that has already appeared. This is the equivalent of marking a coin as spent, without revealing which commitment it came from.
A public contract call is where the application declares the transition type and public parameters. It might say “private transfer,” “redeem credential,” “mint under issuance epoch 42,” or “disclose to auditor key X.” It can also carry the proof, commitment roots and fee details needed for validation.
An optional disclosure receipt is a narrowly scoped output for a recipient who needs evidence. It may let an auditor verify that a transfer happened within a limit, that a holder passed a sanctioned-party screen, or that a credential was valid at a particular time. It should not automatically become a global transaction history.
This architecture produces a crucial shift in how to read an interface. A conventional public function often says, “move 100 units from Alice to Bob.” A private function says something closer to, “verify that an authorized prover consumed valid private inputs and created valid private outputs while satisfying conservation, policy and disclosure rules.”
That is a more demanding promise, but it is also a more useful primitive for identity, regulated payments, private payroll and tokenized access rights.
The first standard decision: what is public on purpose?
A private token standard should begin by defining a public envelope. That envelope should be intentionally small, stable and legible to wallets. It should not be treated as an accidental leakage channel.
The public envelope will normally include:
The contract and asset identifier. A wallet must know which asset rules it is dealing with.
The function or transition selector. Observers may see that a private transfer, mint, burn, disclosure or policy update occurred, even if they do not know the amount or parties.
State anchors. A root or other reference is needed to establish the state against which private inputs were proven.
New commitments and nullifiers. These let the network update private state and prevent reuse of old notes.
Proof verification material. The chain needs the public inputs that bind the proof to the specific transition.
Fee and execution information. Privacy does not automatically hide transaction timing, network fee consumption, contract address or the fact that a wallet called a particular entry point.
Explicit disclosures. If the application chooses to release a claim, audit receipt or public amount, that output must be marked as deliberate.
The rest belongs on the private side unless there is a reason to reveal it. This usually includes ownership secrets, note values, recipient data, credential predicates, invoice references and detailed policy evidence.
CoinDesk reported that Midnight’s model uses selective disclosure rather than an all-or-nothing notion of anonymity. That framing is useful for standards work: a private library should make selective disclosure structured and reviewable, not make it an improvised escape hatch added after deployment.
Supply invariants belong in the circuit
The defining property of a fungible token is not a Transfer event. It is conservation of value under the rules chosen by the issuer.
For a fixed-supply asset, the central invariant is straightforward:
sum(input values) = sum(output values)
For a mintable asset, the equation becomes:
sum(input values) + authorized mint = sum(output values) + authorized burn
The network should verify this relationship without learning the values. That makes the conservation check a circuit responsibility. A public contract can dispatch the call and record the resulting commitments and nullifiers, but it should not be the only place where value arithmetic is enforced. If the amounts are private, public logic cannot independently inspect them.
The circuit should also bind the following conditions:
Every input commitment existed in an accepted state root.
The prover knows an authorization secret for each input.
Each derived nullifier is correctly formed.
The new output commitments are correctly formed.
Input and output values satisfy the asset’s conservation equation.
Mint and burn paths are activated only by an authorized issuance rule.
Asset identifiers cannot be swapped across otherwise valid proofs.
Any private policy predicate is satisfied.
This division has a practical audit benefit. It tells reviewers where to look. A missing nullifier uniqueness check is a double-spend problem. A missing value conservation constraint is an inflation problem. A circuit that fails to bind the asset identifier is a cross-asset substitution problem.
The public layer still has important work. It maintains the accepted state roots, rejects already used nullifiers, verifies proofs against the right verification key, controls upgrades if the asset permits them, and coordinates public roles or governance. But the public layer should not pretend it can enforce invisible facts that were never bound into the proof.
A good library should make this split visible in its types and naming. If an API labels something “mint” but the actual supply constraint is left for each application to write from scratch, the library has not standardized the dangerous part. Conversely, if every issuer policy is hard-coded into a generic mint circuit, the library becomes unusably rigid.
Issuer controls should authorize policy, not reveal every holder
Public token contracts often use an owner address, a minter role, a pauser role and perhaps an allowlist. Privacy-aware tokens can still have issuer controls, but they should be separated into three categories.
First, there are public governance controls. These include a declared administrator, a multisignature authority, a timelock, an upgrade authority or a visible emergency pause. Users may want these to be public because they define the asset’s trust model.
Second, there are private issuance conditions. A regulated stablecoin issuer may need to mint only to recipients who hold a valid credential, reside in an allowed jurisdiction or have passed a transaction limit. The predicate can be proven privately. The public chain need not learn the recipient’s country, name or credential contents.
Third, there are revocation and recovery controls. These are the hardest. A library must not casually advertise “freeze” or “clawback” as ordinary token features. A private note model makes it possible to create assets that are technically spendable by a holder but policy-restricted under certain conditions. It also makes it possible to force a disclosure or redemption route. Those are not interchangeable designs.
A wallet needs to distinguish them before it accepts an asset. The asset manifest should clearly disclose whether the issuer can mint, burn, pause, deny redemption, rotate policy keys, require disclosures, recover dormant funds or upgrade the circuit. Privacy of balances does not remove the need for visible trust disclosures.
This is one place where standards should be strict. A generic library can standardize machine-readable capability flags, such as:
can_mint
can_burn
can_pause
can_upgrade
can_require_disclosure
can_recover
has_transfer_policy
But it should not standardize an issuer’s precise business policy. “Accredited investor,” “employee in good standing,” “not a sanctioned person” and “under a daily withdrawal limit” are different predicates with different legal, technical and social consequences.
A proposed Compact library shape
A practical Compact library should have a narrow core and composable modules. The core should define private note creation, spending, nullifier generation, commitment updates and conservation. Optional modules should add issuer roles, credentials, disclosure receipts, public representations and recovery rules.
A conceptual API might look like this:
module PrivateFungibleAsset {
constructor(assetManifest, supplyPolicy, disclosurePolicy)
circuit privateTransfer(
inputs: PrivateInputNotes,
outputs: PrivateOutputNotes,
authorization: SpendAuthorization,
policyWitness: OptionalPolicyWitness
) -> PrivateTransition
circuit mint(
outputs: PrivateOutputNotes,
issuerAuthorization: IssuerWitness,
policyWitness: OptionalPolicyWitness
) -> PrivateTransition
circuit burn(
inputs: PrivateInputNotes,
authorization: SpendAuthorization
) -> PrivateTransition
circuit disclose(
inputs: PrivateInputNotes,
claim: DisclosureClaim,
recipientKey: DisclosureRecipientKey
) -> DisclosureReceipt
}
The exact syntax will depend on Compact’s language conventions, but the design principle is more important than the spelling.
privateTransfer should not expose a raw public from, to and amount. It should consume private input notes and create private output notes. The recipient may receive an encrypted note payload through a wallet communication mechanism, while the chain receives only its commitment.
mint should never be merely “callable by issuer.” It should prove that the mint fits the declared supply policy. That policy may be fixed supply, capped supply, epoch-limited issuance, collateral-linked issuance or a regulated issuance rule. The standard should make the policy type explicit.
burn should remove value from private circulation while preventing the holder from reusing the spent note. If a token has a public redemption process, the burn module can also create a verifiable redemption receipt without exposing the entire wallet history.
disclose should create claims with a defined audience, scope and expiry. A disclosure to an auditor should not be a disclosure to every app that later touches the wallet.
The most valuable design decision is to separate proof construction from wallet presentation. A wallet should not need to understand every issuer’s private predicate in order to show a user the basics: asset name, issuer, known capabilities, spendability, received note status, pending transaction state, selected disclosure requests and warnings about recovery powers.
Wallet interoperability needs more than a balance
A conventional wallet can retrieve a token balance from public storage. A private wallet cannot. It has to scan or receive encrypted payloads, test whether they belong to its keys, maintain private witnesses, identify spendable notes, select inputs and construct proofs.
That changes the meaning of interoperability.
A wallet-compatible private asset needs a standard note envelope. At minimum, the wallet needs a way to recognize:
The asset identifier and asset manifest version.
The recipient encryption method.
The note commitment format.
The note’s spend authorization scheme.
The policy identifier required for spending.
The disclosure requirements and supported receipt formats.
Whether the note is pending, confirmed, spent, locked, expired or awaiting an external credential refresh.
A standard should also define what a wallet can safely call a balance. “Total discovered value” is not always “spendable value.” Some notes may be subject to a time lock, an issuer freeze policy, a missing credential, an unverified incoming payload or a pending nullifier update. A privacy-aware wallet should show these categories separately.
Indexers still matter, but their job changes. They can track public contract activity, commitment growth, nullifier usage, proof verification outcomes and explicitly public disclosures. They cannot honestly publish complete holder rankings or account balances unless users or issuers opt into a disclosure system. A standard should avoid implying otherwise.
This is also why event compatibility needs restraint. A public Transfer event with sender, recipient and amount is incompatible with private transfers. Replacing it with an opaque “PrivateTransfer” event may be useful for analytics, but it is not equivalent. It tells the world that a transition occurred, not who paid whom or how much.
The better approach is to define a small set of event-like public notices:
PrivateStateUpdated(assetId, transitionType, commitmentCount, nullifierCount)
DisclosureReceiptCreated(assetId, receiptCommitment, schemaId)
PolicyUpdated(assetId, policyVersion)
IssuerCapabilityChanged(assetId, capabilityFlag)
These notices support wallet synchronization and public monitoring without pretending to provide public accounting.
Disclosure is a capability, not a back door
Selective disclosure is where a privacy-aware standard can become genuinely useful or dangerously vague.
A disclosure receipt should identify four things:
What was proved. For example, “the holder is over 18,” “the transfer amount was below $10,000,” “the payer is permitted to hold this asset,” or “the note was redeemed.”
To whom it was disclosed. This can be a public authority key, an auditor key, a merchant key or a verifier domain.
For what purpose. A payment-limit receipt should not automatically be reusable as a creditworthiness credential.
For how long. A receipt should contain a validity period or an epoch reference, especially if credentials can be revoked.
The standard should support predicates instead of forcing raw data disclosure. “This holder is eligible” is often sufficient. Sending a passport number, full balance or complete transfer graph is usually unnecessary.
A disclosure system must also address replay. If a merchant receives a receipt, can it reuse that receipt to track the user elsewhere? If an auditor obtains a view key, does that key reveal every historical transaction forever? A library should provide scoped disclosures by default and make broad, durable access an explicit higher-risk option.
Private does not mean invisible
The most common mistake in privacy design is to treat a shielded amount as the end of the analysis. A token can hide balances and still leak a great deal.
Metadata can reveal the issuer and asset category. Contract entry points can reveal whether a user is transferring, redeeming, minting, making a compliance disclosure or claiming a credential. Timing can correlate a deposit with a withdrawal, particularly when a small anonymity set is active. Fees may reveal wallet behavior or sponsorship arrangements. Unique denominations, repeated transaction patterns and public disclosures can create linkage. A recipient who later spends a note can reveal information about the original payment.
The degree of exposure also varies by asset design. A payroll token paid to a fixed group on the first of every month has a different privacy profile from a widely used stablecoin with many same-sized transfers. A credential that is disclosed to a single university verifier has a different profile from a credential routinely presented to dozens of merchants.
This is why an OpenZeppelin-style library should standardize security properties, not market a universal privacy guarantee. It can promise that a valid private transfer conserves value, prevents double spends and binds a disclosure to its intended statement. It cannot promise that application-level metadata, timing or user behavior will be anonymous.
What should remain application-specific?
A reusable library should be opinionated about cryptographic correctness and unopinionated about business semantics.
The following belong in the reusable layer:
Commitment and nullifier interfaces.
Note ownership and authorization patterns.
Value conservation and mint or burn hooks.
Standard state transition formats.
Capability manifests for wallets.
Disclosure receipt envelopes.
Public notice schemas.
Versioning and upgrade compatibility rules.
The following should remain application-specific:
The definition of eligibility.
Credential issuer trust lists.
Jurisdictional policy.
Transaction limits and risk scoring.
Recovery, seizure and dispute processes.
Whether transferability is unrestricted, conditional or disabled.
Whether supply is fixed, capped, collateralized or discretionary.
The human meaning of an asset, such as cash claim, loyalty point, ticket, membership or voting credential.
That boundary prevents two bad outcomes. The first is a generic token library that offloads every hard invariant to downstream developers. The second is a compliance-heavy “standard” that forces every private asset to inherit one issuer’s governance model.
The developer review checklist
When reviewing a proposed Compact asset library, developers should ask the following questions.
Can the circuit prove conservation without exposing values? If not, it is not a private token core.
Are nullifiers bound to spent notes and checked publicly for uniqueness? If not, double spends may be possible.
Does every output bind the correct asset identifier and recipient authorization data? If not, values or ownership may be substituted across contexts.
Is mint authority separate from mint arithmetic? A visible issuer role is not enough if the circuit does not enforce caps or supply rules.
Are public capabilities machine-readable to wallets? Users should see whether an asset can be paused, upgraded, recovered or forced through disclosure.
Are disclosure receipts scoped by recipient, statement and expiry? Broad view access should be exceptional, not the default.
What can an indexer infer from the public envelope? The answer should be documented in plain language.
Can a wallet synchronize, receive and classify notes without knowing the issuer’s private business logic? If not, interoperability will remain theoretical.
Midnight’s privacy model creates an opportunity to avoid importing Ethereum-era transparency assumptions into a new environment. The right Compact libraries would not promise that every asset is opaque. They would give developers a disciplined way to decide what is public, what is provable, what is privately held and what can be disclosed under controlled conditions.
That is the real standardization task. Not to recreate ERC-20 with hidden balances, but to make privacy-aware assets predictable enough for wallets, robust enough for auditors and flexible enough for applications that need more than a public ledger can safely reveal.