SKIP TO CONTENT
TOKENIZATION6 minStandards That Ship / Ep. 5 · Stage 5-1

Hidden Token Balances: How Can a Freeze Still Work?

Say Alice holds 100 tokens, 30 of them frozen, and neither number is public. How does the token stop her from sending 80?

XIN

IN 30 SECONDS

A confidential balance is a token balance kept encrypted, so outsiders can’t read the amount. For a permissioned token, that changes how a freeze rule runs and what a transfer’s result shows. This stage walks through one real freeze check, step by step.

KEY TAKEAWAYS · 3

  • Hiding balances changes what a transfer’s result shows and who can learn amounts. It doesn’t make every identity or compliance rule impossible.
  • OpenZeppelin ERC7984Freezable v0.5.0 moves an encrypted zero when the unfrozen balance is too low. That’s one implementation’s choice: the ERC-7984 draft also allows a revert.
  • On that version, a successful transaction doesn’t prove the requested amount moved. Read the actual amount returned.
On this page

The problem: a freeze needs numbers nobody should see

IN SHORT With public balances, anyone can follow a freeze check. With hidden balances, the contract must apply it without showing anyone the numbers.

WORDS TO KNOW

Permissioned token
A token that only approved holders can receive, with issuer controls such as freezes. ERC-3643, a Final standard, is one way to build it.
Freeze
An issuer control that locks part or all of a holder’s tokens, so that part can’t be transferred.
Confidential balance
A balance kept encrypted. Anyone can see that a transfer happened, but not the amount.
FHE
Fully homomorphic encryption: a way to add and compare encrypted numbers without decrypting them.
Encrypted selection
Picking one of two encrypted values from an encrypted yes or no, without revealing which one was picked.

A Permissioned token A token that only approved holders can receive, with issuer controls such as freezes. ERC-3643, a Final standard, is one way to build it. See the glossary only lets approved holders receive it. Its issuer can also put a Freeze An issuer control that locks part or all of a holder’s tokens, so that part can’t be transferred. See the glossary on part of a holder’s tokens, so that part can’t be sent.

SAME RULE, TWO SETTINGS

Public balances

  • Balance 100, frozen 30, so 70 can move.
  • A transfer of 80 is rejected, and everyone can see why.

Confidential balances

  • Balance, frozen amount and transfer amount are all encrypted.
  • The contract must still apply the freeze, without revealing the numbers.
Illustrative values.

A Confidential balance A balance kept encrypted. Anyone can see that a transfer happened, but not the amount. See the glossary is kept encrypted. The contract can’t just read 100 and take away 30. In ERC-7984, a Draft Ethereum interface for these tokens, each amount is a confidential pointer to hidden data.

ADAM says: Picture a sealed envelope of cash handed over in front of witnesses: everyone sees the handover, nobody sees the bills. The image breaks because a real envelope can be weighed, or opened by whoever grabs it.

Here, only accounts the protocol allows can decrypt. A value can also be made public on purpose. That rests on code, cryptography and security assumptions, and each needs its own review.

IN PLAIN WORDS

Hiding balances changes what a transfer’s result shows and who can learn amounts. It doesn’t make every identity or compliance rule impossible. The freeze still matters.

This post is a source-based analysis of public specs and one pinned library version. It is not a production case study.

Coming up next: So how does a contract compare numbers it can’t read?

How an encrypted freeze check works, step by step

IN SHORT In OpenZeppelin’s ERC7984Freezable v0.5.0, a transfer bigger than the unfrozen balance doesn’t fail on that check: it moves an encrypted zero.

OpenZeppelin’s version relies on FHE Fully homomorphic encryption: a way to add and compare encrypted numbers without decrypting them. See the glossary, fully homomorphic encryption. It lets code add and compare numbers while they stay encrypted. The comparison and the Encrypted selection Picking one of two encrypted values from an encrypted yes or no, without revealing which one was picked. See the glossary below both work this way.

STEP BY STEP

Flow diagram: 6 stepsFlow diagram: 6 steps. Transfer request, then Available balance. Available balance, then Encrypted comparison. Encrypted comparison, then Encrypted selection. Encrypted selection branches into 2: If yes, then Requested amount. If no, then Zero. Both paths continue to Balance update. Balance update, then Actual amount returned.YESNO1Transfer request2Available balanceBalance minus frozen3Encrypted comparisonDoes the request fit?4Encrypted selectionRequested amountZero5Balance updateSucceeds, unless another error6Actual amount returnedStill encrypted

Step 1. Request. A holder asks to send an encrypted amount.

Pick a step to see it move
  1. Request A holder asks to send an encrypted amount.
  2. Available The contract works out balance minus frozen amount, never below zero. The result is encrypted.
  3. Compare It checks whether the request fits in what’s available. The yes or no is encrypted too.
  4. Select It keeps the requested amount on yes and zero on no. The public success status doesn’t say which.
  5. Update Balances move by the selected amount, and the transaction succeeds in both cases. Other errors can still revert.
  6. Result The function returns the amount actually moved, still encrypted. Authorized accounts can decrypt it.
OpenZeppelin ERC7984Freezable v0.5.0, insufficient unfrozen balance only. Every value from step 2 on stays encrypted.

This is one library’s choice, for one case, in one pinned version. The ERC-7984 draft also allows a revert, a public failure, when the balance is too low.

Read the full code of that version before you rely on how it handles minting (creating new tokens), transfers, access and failures.

Coming up next: So what does that look like with real numbers?

A worked example: one freeze, two transfers

IN SHORT If the request fits in the unfrozen balance, all of it moves; if not, zero moves. The public success status is the same for both.

A WORKED EXAMPLE

Illustrative numbers

PASSAlice holds 100 tokens, 30 of them frozen, and sends 50

INPUT

Balance
100 (encrypted)
Frozen
30 (encrypted)
Request
50 (encrypted)

WHAT HAPPENS

  1. Available: 100 minus 30 leaves 70.
  2. 50 fits in 70, so the encrypted answer is yes.
  3. The selection keeps 50.

OUTPUT

Transaction
succeeds
Actual amount moved
50 (encrypted)

ZERO MOVEDSame starting balance, she asks to send 80 instead

INPUT

Request
80 (encrypted)

WHAT HAPPENS

  1. 80 doesn’t fit in 70, so the encrypted answer is no.
  2. The selection keeps zero.

OUTPUT

Transaction
succeeds
Actual amount moved
0 (encrypted)

A wallet that shows “payment completed” after Alice’s second, empty transfer is wrong.

Encryption hides the amount from the public. It doesn’t stop the holder from learning what happened, if you give them an authorized way to decrypt the result or get a proof.

ITEM GET

Read the result, not the status

On OpenZeppelin v0.5.0, a success status doesn’t prove the requested amount moved. Read the actual amount returned, through an authorized decryption or a proof.

Sources: ERC-7984 (Draft), ERC-3643, OpenZeppelin ERC7984Freezable v0.5.0.

STAGE CLEAR

What you now know

  • How a contract can apply a freeze to balances that aren’t publicly readable.
  • Why, on OpenZeppelin v0.5.0, a successful transfer can still move nothing.

Check yourself

Tap or click a question to see the answer.

Illustrative numbers: on OpenZeppelin ERC7984Freezable v0.5.0, Bob holds 100 tokens with 40 frozen. He asks to send 70. Does the transaction fail?

Not for that reason. Only 60 are available, so the selection keeps zero. Unless another error occurs, the transaction succeeds and moves nothing. The actual amount, decrypted or proven by an authorized party, shows it.

Next stage:What can a failed transfer, or a public yes-or-no check, leak about a hidden balance?6 min
Adam Boudjemaa
Adam Boudjemaa

Web3 x AI engineer, former CTO of Integra. Named author (1 of 5) of ERC-3643, first author of ERC-6960, co-author of ERC-7410, and co-author of ERC-8203, which is still a draft.

Have a project to ship or a role to fill?

Discuss your project or role

My work at the web3 x AI intersectionOpen-source tools I build

Follow the engineering notes

Get new articles on production AI and smart contracts by email.