OpenZeppelin / OpenZeppelin/compact-contracts
L-04: Zero-Value Transfers Can Be Used To Deny Service To Genuine Credits
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 55
- Forks
- 29
- Avg merge
- 5d 7h
- Merged PRs (30d)
- 25
Description
L-05 Zero-Value Transfers Can Be Used To Deny Service To Genuine Credits
Severity: Low
Source: Midnight Foundation #02 — OpenZeppelin Compact Contracts Audit, Release 0.3.0-alpha.1 (2026-08-18)
Delivering value reads the recipient's memo list, takes its current length as a nonce, folds that nonce into the randomness for the balance update and for the memo, rewrites that recipient's pending ciphertext, and appends to the same list. The length a credit reads is therefore part of what its transaction records having read, and appending the memo changes that same length. Every credit to one account therefore depends on a value it goes on to alter. Two credits prepared against the same view of this value cannot both remain valid, and the module records this as a documented limitation, acceptable for its first target use case of tokenized deposits.
The note describes a concurrency property where two ordinary payments arrive together and one needs a retry. The same mechanism is also available deliberately and at almost no cost. Reaching the credit path requires only a transfer, the per-operation check bounds a value from above and not from below, and a debit compares the caller's balance against the amount requested, a comparison that a zero amount satisfies for any registered account including one that holds the encrypted zero written at registration. A party wanting to disrupt a particular account can therefore send it repeated zero-valued transfers, each invalidating whatever genuine credit was prepared against the previous state, for the price of fees and no capital at all. Any credit has the same effect, since the conflict follows from the read and the append rather than from the amount, but a non-zero transfer pays value to the target and is limited by the sender's balance, whereas the zero-valued form is free and unbounded. The recipient has no response available, since the contention is on the state every incoming credit must touch, and clearing the memo list neither prevents nor slows it.
The use case named as making the limitation acceptable is also the one in which it is most impactful. A tokenized deposit address is published so that others may pay into it, is reused, and is expected to remain reachable, which makes it both an easy target and a costly one to have blocked. The disclosure and the exposure therefore point in opposite directions, and the module does not say that the conflict is reachable on demand by anyone rather than only by coincidence.
Consider giving each incoming credit its own key, so that concurrent payments to one recipient write to distinct locations and are combined when the recipient next sweeps, which removes the shared state that forces them to serialize. Consider at least rejecting zero-valued transfers, which does not address contention between genuine payments but does remove the free and unlimited form of it, and stating in the concurrency note that the conflict can be produced deliberately, so that a deployment relying on inbound liveness evaluates it as an availability property rather than as an occasional retry.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in contracts/src/token/ConfidentialFungibleToken.compact at the recipient memo-list handling, transfer checks, and debit path linked in the report. Read the concurrency limitation near the documented tokenized-deposit use case before choosing a mitigation. The issue leaves the design open; completion requires an agreed approach, validation that zero-value transfers cannot cheaply invalidate credits, and updated documentation of the limitation.
Written by the indexing model from the issue text.
Assessment
- Domain
- blockchain, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 32/100