OpenZeppelin / OpenZeppelin/compact-contracts
N-06: Coin Information Is Recoverable Only From Discardable Return Values
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 55
- Forks
- 29
- Avg merge
- 5d 7h
- Merged PRs (30d)
- 25
Description
N-10 Coin Information Is Recoverable Only From Discardable Return Values
Severity: Note
Source: Midnight Foundation #02 — OpenZeppelin Compact Contracts Audit, Release 0.3.0-alpha.1 (2026-08-18)
Contract-initiated shielded sends create no coin ciphertext, so a recipient cannot discover a coin by scanning the chain, and the module records that the returned values are the only copies of those coins' information. Three circuits hand back such a value, a mint returning the recipient's only record, a burn returning the refund, and _burnFromSelf returning the change that replaces what the contract spent. Neither the absence of ciphertexts nor the decision to return the information is a defect, the former being a platform property and the latter the only mechanism available.
The defect is that nothing obliges a caller to retain what it receives. A circuit may be invoked for its effects with its result unused and the compiler accepts it, so a consumer treating one of these calls as void meets no error at compile time, no assertion at proving time, and no observable failure on chain. The obligation rests on prose, where a consumer is told it SHOULD persist the returned change. What is lost is not the value, which remains provably present, but every means of spending it. Burning from the contract's own holding is the sharpest case, the loss falling on the contract rather than on a user who chose to interact with it, and that holding never having been a ledger entry against which the omission could be reconciled.
Consider delivering the coin information through the contract rather than through the return value, by way of an optional extension that encrypts each created coin's fields to the recipient's public key and records the ciphertext in ledger state, so that a recipient discovers the coin by scanning contract state and decrypting with its own key. The primitive already exists as EcdhMask, which ConfidentialFungibleToken uses to deliver amounts through a per-account memo list. Two costs attach: recipients would need a registered encryption key, which this standard does not establish, and the stored ciphertexts would grow without bound. Both appear acceptable where the alternative is irrecoverable loss. Alternatively, consider strengthening documentation from SHOULD to MUST and shipping a reference consumer.
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 with contracts/src/token/NativeShieldedTokenCore.compact, especially the mint, burn, and _burnFromSelf circuits, then read contracts/src/crypto/EcdhMask.compact and ConfidentialFungibleToken's memo handling. The issue presents multiple possible remedies rather than a defined task; first determine whether the project wants an encrypted ledger-state extension, stronger documentation, or a reference consumer, then define tests and acceptance criteria.
Written by the indexing model from the issue text.
Assessment
- Domain
- blockchain, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100