This ERC extends ERC-7984 with a minimal interface for defining tokenized real world assets. It provides a pair of plaintext eligibility checks, a confidential validation function answering whether a specific transfer is permitted, a confidential figure for the portion of a balance that is currently spendable, and an access restricted forced transfer. Amounts remain confidential pointers throughout. The standard constrains the behavior of minting, burning, halting, and freezing without mandating interfaces for them, leaving issuance and restriction mechanics to implementations.
confidentialCanTransfer and confidentialAvailableBalanceOf have no caller restriction, and the draft leaves who can resolve the returned pointer to the implementation. For plain ERC-7984 that is fine. Here the caller chooses from and amount, and the result must not be false just because operator is unauthorized, so anyone can ask the question about anyone. If the caller can also resolve the answer, each call is a comparison against someone else’s spendable balance:
A euint64 balance falls to binary search in at most 64 calls, and confidentialAvailableBalanceOf(victim) would hand it over in one. OpenZeppelin’s ERC7984Freezable.confidentialAvailable already does the safe thing and only grants the result to the account (FHE.allow(amount, account)). I’d lift that into the spec, something like: a returned pointer MUST NOT become resolvable by any party that could not already resolve from’s balance (or account’s, for the available balance).
Second, the revert clause: “MUST return a pointer to false OR revert if any other rule would prevent the transfer (such as vesting, balance caps, etc)”. Vesting and balance caps depend on the amount, so a revert there publishes exactly the bit your Disclosure through reverts section warns about, without anyone decrypting anything. Reverting on canSend or canReceive is fine since those are plaintext anyway. I’d narrow the revert option to rules whose inputs are all public, and have amount-dependent rules return a pointer to false.
Could the draft clarify which recipient restrictions apply to forced transfers?
canReceive(to) cannot encode quantitative rules. forceConfidentialTransferFrom must enforce that eligibility check, but must not call confidentialCanTransfer and is exempt from the general requirement to honor it. Suppose to is eligible, but receiving another 100 tokens would exceed a confidenital holding cap. For an ordinary transfer confidentialCanTransfer must return a pointer to false or revert. Assuming an authorized caller and sufficient total balance, should a forced transfer bypass that cap, enforce it separately or leave the choice to implementations?
The draft already permits moving zero tokens when the amount exceeds confidentialAvailableBalanceOf(from), making overrides of source-balance restrictions optional. Could it also specify whether recipient-side quantitative and transfer-specific restrictions must apply, must be bypassed or are implemetnation-defined?
If recipient restrictions must apply, implementations could share internal checks, but the interface exposes no separate recipient-specific predicate. confidentialCanTransfer also checks sender eligibility and available balance, so it cannot serve that purpose unchanged.
Each approach seems defensible; integrators need to know which recipient protections a forced transfer preserves.