ALLER AU CONTENU
Protocole11 minStandards That Ship / Ep. 5

Le revert devient zéro : soldes confidentiels et conformité des tokens permissionnés

Adam Boudjemaa
PARTAGER

À RETENIR

  • ERC-3643 comporte onze exigences MUST. Elles ont été écrites quand un solde était un nombre lisible par tous, et plusieurs cessent de vouloir dire ce qu'elles disent quand ce n'est plus le cas.
  • Les obligations qui lisent une ADRESSE survivent au chiffrement sans changer. Celles qui lisent un NOMBRE sont redérivées, et leur mode d'échec passe d'un revert à un transfert qui réussit sans rien déplacer.
  • Le gel partiel existe toujours sous chiffrement. Dans ERC7984Freezable d'OpenZeppelin, le require devient un FHE.select, et le comportement documenté est que 0 token est transféré au lieu que l'appel échoue.
  • L'exigence dont je suis le plus fier est celle qui casse le plus fort. Une pré-vérification qui renvoie un oui ou non en clair sur un solde secret est un oracle de déchiffrement, et une dichotomie en extrait le solde.
  • ERC-7984 est une interface en Draft, sans gel, sans transfert forcé et sans lecture en clair. Le cadrage est juste, et il signifie que toute la couche de conformité est du code qui vous appartient.
11
EXIGENCES MUST DANS MON STANDARD
écrites en 2021, quand un solde était un nombre lisible par tous
0
FONCTIONS DE CONFORMITÉ DANS ERC-7984
ni gel, ni transfert forcé, ni lecture en clair. En Draft, et volontairement minimal.

ERC-3643 possède une section exigences. J'ai contribué à écrire le standard, alors je peux encore citer de mémoire celle qui dérange.

Le token DOIT pouvoir geler les tokens sur le portefeuille des investisseurs si nécessaire, partiellement ou totalement.

Partiellement. Ce seul mot porte tout le poids. Un gel partiel réclame un nombre, quelqu'un doit comparer ce nombre à un solde, et la comparaison doit pouvoir dire non.

Chiffrez maintenant le solde.

Chaque étape de cette phrase doit être redérivée. La plupart le peuvent. Une seule ne le peut pas dans la forme que spécifie le standard, et c'est celle dont j'étais le plus fier.

TRANSPARENCE

La limite d'abord, car elle décide de comment lire la suite. Je n'ai jamais déployé de token confidentiel. Je n'ai pas mesuré le chiffrement totalement homomorphe, je n'en ai jamais exploité en production, et partout où la cryptographie porte l'argument ci-dessous, je rapporte ce que dit une spécification ou une bibliothèque, pas ce que j'ai mesuré. Ce que j'ai, c'est l'autre moitié : je suis l'un des cinq auteurs nommés d'ERC-3643, donc les obligations sont les miennes, et je sais lire le code qui tente aujourd'hui de les satisfaire sous chiffrement.

Ce n'est donc pas un article sur le FHE. C'est un article de conformité qui a du chiffrement en face de lui.

La question est étroite et se tranche : une obligation écrite pour un solde en clair veut-elle encore dire quelque chose quand le solde est un handle chiffré ? Certaines survivent intactes. D'autres reviennent sous une autre forme, avec un autre mode d'échec. Une se transforme en fuite.

Ce que mon propre standard exige de pouvoir lire

Onze exigences MUST, et un transfert qui doit satisfaire six conditions distinctes avant de bouger. Cela fait beaucoup de surface, et la bonne façon de la regarder n'est pas par ordre d'importance.

Triez les pouvoirs selon ce que chacun lit réellement.

CE QUE CHAQUE POUVOIR LIT
Les pouvoirs ERC-3643, triés par ce qu'ils lisent
isVerifiedaddress -> bool

Lit une adresse et les attestations rattachées à son identité. Ne touche jamais un solde.

setAddressFrozenaddress, bool

Lit une adresse. Le portefeuille entier cesse de bouger. Aucun montant n'intervient nulle part.

freezePartialTokensaddress, uint256

Stocke un nombre, et chaque transfert ultérieur s'y compare. Partiel veut dire numérique, ou ne veut rien dire.

getFrozenTokensaddress -> uint256

Rend ce nombre en clair, à quiconque le demande, pour toujours.

forcedTransferfrom, to, uint256 -> bool

L'agent doit savoir quelle position est là avant de pouvoir la prendre. Un recours a besoin d'une taille.

canTransferfrom, to, uint256 -> bool

Règles globales : nombre maximal de porteurs, montant maximal par investisseur. Compter et comparer.

balanceOfaddress -> uint256

Hérité d'ERC-20. La table de capitalisation qu'un émetteur dépose est assemblée à partir de celle-là.

Les deux premiers lisent une adresse. Tous les suivants lisent un nombre. Cette ligne est tout l'article, et personne ne l'avait tracée avant que le nombre ne s'éteigne.

Deux sortes d'obligations, et une seule survit

Une fois la liste triée ainsi, la réponse cesse d'être une question de recherche et devient de la comptabilité.

Une obligation qui lit une adresse survit entièrement au chiffrement, car l'éligibilité n'a jamais été stockée dans le solde. Une obligation qui lit un nombre doit être reconstruite, et la reconstruire change une chose que l'originale n'avait jamais eu à envisager : ce qui se passe quand la réponse est non.

Obligation
Ce qu'elle lit
Sous un solde chiffré
Contrôle d'éligibilité
Une adresse et ses attestations
Survit sans changement. L'attestation n'a jamais été dans le solde.
Gel du portefeuille entier
Une adresse
Survit. Un booléen par adresse reste un booléen par adresse.
Gel partiel
Un montant, comparé à un solde
Redérivé. La comparaison a toujours lieu ; l'échec change de forme.
Transfert forcé
Une position récupérable
Redérivé, et l'agent peut ne pas voir la taille de ce qu'il prend.
Pré-vérification avant l'envoi
Un oui ou non en clair sur un solde
C'est celle qui se transforme en fuite.
Comptabilité des porteurs et table de capitalisation
Tous les soldes, en clair
Quitte entièrement le token et devient une question de divulgation.

Quatre de ces six relèvent de l'ingénierie ordinaire. Une est une fuite. Une cesse d'être un problème de smart contract. Prenons-les dans cet ordre.

La ligne qui change, et ce en quoi elle change

Commençons par l'ordinaire, parce qu'elle est la plus claire et parce que quelqu'un l'a déjà écrite.

OpenZeppelin publie une extension nommée ERC7984Freezable. Elle stocke un montant gelé chiffré par compte, calcule le solde disponible par une soustraction saturante du gelé au total, et contrôle chaque transfert avec. C'est freezePartialTokens, reconstruit sur un chiffré, par des gens qui ont lu la même exigence que moi.

Voici la règle dans les deux mondes, côte à côte. Quatre lignes, et toute la thèse.

LA MÊME RÈGLE, DEUX FOIS
erc-3643.sol / ERC7984Freezable.sol
// ERC-3643 (Final). From the example transfer() in the specification.
// A plaintext comparison, and a revert when it fails.
require(
    _amount <= balanceOf(msg.sender) - (_frozenTokens[msg.sender]),
    "ERC-3643: Insufficient Balance"
);

// OpenZeppelin ERC7984Freezable._update. The same rule on a ciphertext.
// No require. No revert. The amount is quietly replaced with zero.
euint64 unfrozen = _confidentialAvailable(from);
encryptedAmount = FHE.select(
    FHE.le(encryptedAmount, unfrozen),
    encryptedAmount,
    FHE.asEuint64(0)
);
En haut : la comparaison en clair de mon standard, avec un revert attaché. En bas : la règle identique sur un chiffré, où require est devenu select et où la branche d'échec est le nombre zéro.

Le commentaire de la bibliothèque énonce la conséquence sans détour : le compte source doit disposer d'un solde non gelé suffisant, faute de quoi 0 token est transféré. Pas annulé. Transféré, pour un montant de zéro.

Un transfert bloqué sur ERC-3643

La transaction échoue avec un motif nommé. Le portefeuille affiche un échec, le détenteur le sait aussitôt, le support lit le revert dans un explorateur, et le journal de conformité s'écrit tout seul. Être bloqué est un événement public et lisible.

Un transfert bloqué sur un token confidentiel

La transaction réussit. Statut un, gas dépensé, un événement émis contenant un montant chiffré. Rien n'a bougé. Le détenteur ne peut pas distinguer un transfert abouti d'un transfert bloqué sans déchiffrer, et votre support non plus.

Pourquoi il ne peut pas simplement échouer

L'objection évidente est que la bibliothèque pourrait simplement échouer, et elle mérite d'être prise au sérieux car elle est fausse pour une raison intéressante.

La documentation du protocole Zama est directe sur la mécanique. Les booléens chiffrés ne supportent pas les opérations booléennes classiques comme les instructions if ou les opérateurs logiques, donc la logique conditionnelle doit passer par FHE.select, décrit comme un opérateur ternaire pour valeurs chiffrées. Je le rapporte, je ne l'ai pas testé. Il choisit entre deux valeurs chiffrées au lieu de brancher l'exécution, ce qui explique pourquoi les deux issues possibles doivent exister avant le choix.

Vous pourriez tout de même déchiffrer la comparaison et échouer sur le résultat. C'est là que vit l'échec intéressant.

L'EMBRANCHEMENT IMPOSÉ PAR LA CRYPTOGRAPHIE
Diagramme de flux : 4 étapesDiagramme de flux : 4 étapes. Un transfert arrive, puis Comparer, de façon homomorphe. Comparer, de façon homomorphe, puis Agir sur le résultat. Agir sur le résultat se divise en 2 : Si déchiffrer, puis échouer, alors Publie la réponse. Si substituer zéro, alors Garde le secret. Les deux chemins continuent vers La transaction réussit dans les deux cas.DÉCHIFFRER, PUISÉCHOUERSUBSTITUER ZÉRO1Un transfert arriveLe montant est un handle chiffré. Lecontrat ne peut pas le lire non plus,et c'est ce que l'on oublie.2Comparer, de façon homomorpheFHE.le(montant, non gelé). Lerésultat est un booléen chiffré, pasun booléen. Rien dans l'EVM ne saitbrancher dessus.3Agir sur le résultatDeux façons de finir, et le choixrelève de la politique, pas de latechnique.Publie laréponseLe fait qu'unetransaction aitéchoué est public.Échouez exactementquand unecomparaisonsecrète échoue etvous avez annoncéla comparaison.Garde le secretLe transfertaboutit et nedéplace rien.C'est la brancheque prend labibliothèque.4La transaction réussit dans lesdeux casL'une des deux issues laisse fuir unsolde. L'autre fait qu'une transactionréussie cesse de vouloir dire untransfert réussi.
L'étape trois est un vrai embranchement, et les deux bras compilent. L'un publie la réponse à une comparaison secrète. L'autre garde le secret et vide le mot réussite de son sens.

Voilà tout l'arbitrage, et ce n'est le défaut du Solidity de personne. C'est ce qui arrive quand une règle portant sur un nombre secret doit produire un effet public.

La bibliothèque a choisi juste. Le silence est le bon échec ici, et le signal manquant se reconstruit hors chaîne en laissant le détenteur déchiffrer son propre solde disponible. Ce qui ne se reconstruit pas, c'est l'hypothèse que fait chaque intégration du secteur : qu'une transaction réussie a déplacé le montant qu'elle nomme.

La pré-vérification était le sujet. Elle est la première victime.

Passons à l'exigence qui me coûte quelque chose à écrire.

ERC-3643 dit que le token DOIT disposer d'une interface standard permettant de pré-vérifier si un transfert va passer ou échouer avant de l'envoyer à la blockchain. Le rationnel explique exactement pourquoi, et l'argument est bon. Pour un ERC-20 ordinaire, vous consultez balanceOf et allowance et vous savez si un transfert marchera. Un transfert de titre peut échouer pour des raisons qu'un solde n'explique pas, alors le standard ajoute canTransfer comme moyen général d'obtenir la même chose.

Relisez cette phrase avec un solde chiffré en tête. La pré-vérification existe précisément parce que lire le solde ne suffisait pas. Chiffrez le solde et vous avez supprimé les deux moitiés de la réponse.

Et une pré-vérification qui renvoie encore un booléen en clair sur un nombre secret n'est pas un compromis. C'est un oracle.

LA PRÉ-VÉRIFICATION COMME ORACLE
Diagramme de séquence : 3 participants : Un appelant, Contrat de conformité, Solde chiffréDiagramme de séquence : 3 participants : Un appelant, Contrat de conformité, Solde chiffré. Étape 1 : Un appelant vers Contrat de conformité, canTransfer(from, to, 1000). Étape 2 : Contrat de conformité vers Solde chiffré, comparer 1000 au solde libre. Étape 3 : Solde chiffré vers Contrat de conformité, un booléen chiffré. Étape 4 : Contrat de conformité vers Un appelant, true. Étape 5 : Un appelant vers Contrat de conformité, le même appel à 2000, puis 4000, puis 3000. Étape 6 : Contrat de conformité vers Un appelant, true, false, true.UN APPELANT > CONTRAT DE CONFORMITÉcanTransfer(from, to, 1000)Une pré-vérification, exactement comme monstandard l'exige. Elle vit sur le contrat deconformité, pas sur le token. Un appel view,gratuit, sans signature, sans trace.CONTRAT DE CONFORMITÉ > SOLDE CHIFFRÉcomparer 1000 au solde libreLa comparaison est homomorphe. Le contrat nevoit toujours pas le nombre.SOLDE CHIFFRÉ > CONTRAT DE CONFORMITÉun booléen chiffréCONTRAT DE CONFORMITÉ > UN APPELANTtrueEt voilà. Une réponse en clair sur un nombresecret, rendue à n'importe qui.UN APPELANT > CONTRAT DE CONFORMITÉle même appel à 2000, puis 4000, puis3000Chaque réponse divise l'intervalle par deux.C'est une dichotomie, pas un exploit, et ellene demande aucun privilège.CONTRAT DE CONFORMITÉ > UN APPELANTtrue, false, trueQuelques dizaines d'appels view et le soldeconfidentiel redevient un nombre.
Rien ici n'attaque la cryptographie. Chaque appel est l'interface que mon propre standard exige, utilisée exactement comme documenté. Notez où elle vit : dans ERC-3643 la pré-vérification est sur le contrat de conformité, et le token la délègue.

Soyons clairs sur ce que j'affirme : je n'ai pas vu cela pratiqué sur un token en production, et je ne rapporte aucun incident. C'est un raisonnement à partir de deux spécifications, dont j'ai contribué à écrire l'une. Le raisonnement est assez court pour être vérifié, et c'est la seule raison pour laquelle je le publie.

Ce qui survit vraiment, et pourquoi

La bonne nouvelle est vraiment bonne, et c'est la moitié dont personne ne parle parce qu'elle n'a rien de spectaculaire.

OpenZeppelin publie aussi ERC7984Restricted, une liste de blocage que l'on peut inverser en liste d'autorisation. Regardez ce qu'elle stocke : une énumération Solidity ordinaire, en clair, par adresse. Son contrôle renvoie un bool simple, et quand il échoue il annule avec une erreur nommée. Pas de chiffré, pas de select, aucune ambiguïté.

Ce n'est pas un raccourci. C'est correct. La restriction n'a jamais été secrète, car c'est une propriété de l'adresse et non de l'argent. Ce qui est exactement la forme d'isVerified, et cela signifie que toute la couche identité d'ERC-3643 se transpose quasiment mot pour mot.

CE QUI SURVIT AU CHIFFREMENT
Diagramme en couches : 5 niveaux, de haut en basDiagramme en couches : 5 niveaux, de haut en bas. Niveau 1, IDENTITÉ : Attestations, pays, émetteurs de confiance, Se transpose sans changement. Niveau 2, RESTRICTION DE COMPTE : Bloqué ou autorisé, par adresse, Toujours un bool simple, toujours un revert nommé. Niveau 3, RÈGLES DE MONTANT : Gel partiel, solde libre, maximum par investisseur, Redérivées en select, plus en require. Niveau 4, COMPTABILITÉ DES PORTEURS : Combien de porteurs, et qui détient quoi, Un solde nul est indistinguable sans déchiffrement. Niveau 5, REPORTING : La table de capitalisation réellement déposée, Quitte le contrat, devient de la gestion de clés.IDENTITÉAttestations, pays, émetteurs deconfianceSe transpose sans changementRESTRICTION DE COMPTEBloqué ou autorisé, par adresseToujours un bool simple, toujours unrevert nomméRÈGLES DE MONTANTGel partiel, solde libre, maximum parinvestisseurRedérivées en select, plus en requireCOMPTABILITÉ DES PORTEURSCombien de porteurs, et qui détientquoiUn solde nul est indistinguable sansdéchiffrementREPORTINGLa table de capitalisation réellementdéposéeQuitte le contrat, devient de lagestion de clés
La ligne de partage n'est pas la sophistication cryptographique. C'est de savoir si l'obligation lit une adresse ou lit un nombre, et les deux bandes sous la ligne concentrent tout le travail.

La quatrième bande mérite sa propre phrase, car c'est discrètement la pire.

ERC-3643 permet à un émetteur de plafonner le nombre de porteurs, y compris par pays. Un décompte de porteurs, c'est le nombre d'adresses à solde non nul. Sous chiffrement, un solde nul et un solde énorme sont le même handle opaque, donc le décompte ne peut plus être tiré de la chaîne sans déchiffrer chaque position. C'est ma lecture des deux spécifications mises côte à côte, pas une affirmation de l'une ou de l'autre, et j'aimerais qu'on me la réfute avant de bâtir dessus.

Quelqu'un doit encore pouvoir le lire

Ce qui mène à la question que tout émetteur finit par poser, en général dans une pièce où se trouve un juriste. Si le solde est secret, qui lit la table de capitalisation ?

La réponse est que quelqu'un détient une clé, et cette phrase travaille plus qu'il n'y paraît. getFrozenTokens était un getter. Son équivalent confidentiel est un droit de déchiffrement. Ce ne sont pas des objets de même nature : un getter est sans état et se change en redéployant, tandis qu'un droit de déchiffrement durable est une clé que quelqu'un possède désormais et que vous ne pourrez peut-être pas reprendre.

Trois conceptions circulent déjà, et elles distribuent cette clé très différemment.

Conception de divulgation
Conception de divulgation
Qui peut lire
Ce que ça vous coûte
Octroi par handle (liste de contrôle d'accès FHEVM)
Exactement les adresses que vous avez autorisées, durablement
Vous faites de la conservation de clés pour un superviseur, et un octroi durable se retire mal
Clé d'auditeur sur le mint (Solana Token-2022)
Un auditeur, chaque montant transféré, dès le premier
Une seule clé qui lit tout le registre. Le système de preuves l'impose, ce n'est donc pas optionnel par transfert.
Divulgation publique (AmountDisclosed d'ERC-7984)
Tout le monde
C'est le seul événement de divulgation du standard et il n'est pas sélectif. L'utiliser pour le reporting défait la raison d'avoir chiffré.
La conception Solana est celle que j'étudierais en premier, car elle rend l'arbitrage explicite au niveau du protocole. Si le mint porte une clé publique d'auditeur, chaque transfert confidentiel doit aussi chiffrer le montant sous cette clé, et une preuve de validité l'impose. Le superviseur ne touche jamais un secret de compte. C'est rapporté de la documentation, pas mesuré.

Remarquez ce qu'aucune des trois n'est. Aucune n'est une fonction sur le token qu'un agent appelle pour lire une position. Le recours d'ERC-3643 supposait un getter. La confidentialité remplace le getter par une cérémonie de clés, et une cérémonie de clés est un problème d'organisation déguisé en cryptographie.

ERC-7984 lui-même n'a aucun avis là-dessus, et c'est le bon périmètre pour une interface. Ni gel, ni transfert forcé, ni lecture en clair, ni identité. C'est un Draft, créé en juillet 2025, et il fait exactement un travail.

Ce qui place un token permissionné confidentiel là où cette série est déjà passée : un noyau propre, avec la logique de conformité entièrement laissée à vous. C'est l'arbitrage CMTAT, et son avertissement. Un hook vide est un security token sans conformité, et une couche de conformité confidentielle non construite est la même chose avec une meilleure cryptographie.

Ce que je ferais vraiment

Rien de tout cela n'est un argument contre les soldes confidentiels. C'est un argument pour décider dans le bon ordre. Voici la version que j'utiliserais en première réunion.

DÉCIDER AVANT DE CHIFFRER
Décision: Cet instrument doit-il avoir des soldes confidentiels ?Arbre de décision : Cet instrument doit-il avoir des soldes confidentiels ? Si Les montants sont commercialement sensibles et vos porteurs sont déjà permissionnés, alors Oui, et gardez l'identité en clair. Si Vos intégrations doivent savoir à l'avance si un transfert passera, alors Repensez le contrôle, ne le transposez pas. Si Un régulateur exige une table de capitalisation vivante et vous n'avez aucun plan de conservation de clés, alors Pas encore. Si Vous le faites parce que la confidentialité sonnait comme une fonctionnalité, alors Non.DÉCISIONCet instrument doit-il avoir dessoldes confidentiels ?SI Les montants sontcommercialement sensibles et vosporteurs sont déjà permissionnésOui, et gardez l'identité enclairChiffrez l'argent, pasl'appartenance. Les contrôlesd'adresse survivent intacts, nepayez pas pour les cacher.SI Vos intégrations doivent savoirà l'avance si un transfert passeraRepensez le contrôle, ne letransposez pasUne réponse en clair sur un soldesecret est un oracle. Faites entrerla décision dans la transaction etprévenez le détenteur après.SI Un régulateur exige une table decapitalisation vivante et vousn'avez aucun plan de conservationde clésPas encoreL'obligation de reporting nedisparaît pas parce que le solde adisparu. Réglez qui détient ledroit de déchiffrement avant dechoisir un schéma.SI Vous le faites parce que laconfidentialité sonnait comme unefonctionnalitéNonChaque recours de votre pile doitêtre redérivé, et le nouveau moded'échec est un transfert quiréussit sans rien déplacer.
Seule la première branche est un oui net. Les trois autres se tranchent sur des obligations qui existent que le solde soit lisible ou non, et c'est bien le sujet.

Ce n'est pas un cas limite, c'est l'échec par défaut de toute règle de montant sur un token confidentiel. Les logiciels de conservation, les agents de transfert et les travaux de rapprochement supposent tous qu'une transaction réussie a déplacé le montant qu'elle nommait. Sur un token confidentiel, cette hypothèse est fausse par construction, et elle l'est en silence. Testez le chemin bloqué avant le chemin heureux.

canTransfer compilera très bien au-dessus d'un solde chiffré si vous déchiffrez la comparaison avant de renvoyer. Cela passera la revue, car cela ressemble exactement à la fonction que le standard exige. C'est aussi une lecture gratuite, non authentifiée et illimitée de chaque position du token, et personne ne le remarque jusqu'à ce que quelqu'un publie une table de capitalisation que vous croyiez privée.

Des équipes choisissent un schéma de confidentialité comme elles choisissent une base de données, puis découvrent l'obligation de reporting six mois plus tard et greffent un mécanisme de divulgation sur une conception qui n'en prévoyait aucun. Qui peut déchiffrer, sous quelle autorité, et comment un octroi se révoque sont les trois premières questions, pas les trois dernières. Ce sont aussi celles que votre régulateur posera en premier.

Le résumé honnête de ma position est court. J'ai contribué à écrire un standard qui suppose que l'émetteur peut lire le nombre, et cette hypothèse porte l'édifice à six endroits que je peux nommer.

Ce n'est pas un défaut d'ERC-3643 et ce n'est pas un défaut d'ERC-7984. C'est une couture, et une couture est l'endroit où s'écrit le standard suivant. Ce qui manque n'est pas plus de cryptographie. C'est un vocabulaire de conformité pour des montants que personne ne peut lire : un gel dont l'échec est lisible par le détenteur, une pré-vérification qui répond sans publier, un décompte de porteurs qui survit à l'opacité.

Voilà une spécification que quelqu'un devrait écrire, et je préfère nommer le manque plutôt que le voir découvert en production.

C'est la deuxième fois que j'écris cette phrase à propos de mon propre travail. La fois précédente, le solde restait lisible et le registre était parti là où le token ne voyait plus. Cette fois le registre reste en place et le nombre s'éteint. Même leçon, prise à l'envers : une règle de conformité vaut exactement ce qu'elle peut encore lire.

FAQ

Pas tel qu'il est écrit. ERC-3643 est Final et ses exigences supposent que le token peut lire un nombre : il DOIT pouvoir geler des tokens partiellement, il DOIT pouvoir forcer des transferts depuis un portefeuille d'agent, et le contrat de conformité auquel il délègue DOIT exposer une pré-vérification indiquant si un transfert passera. La moitié identité du standard se transpose presque sans changement à un token confidentiel, car l'éligibilité est une propriété d'une adresse et non d'un montant. La moitié montant doit être redérivée, et une exigence, la pré-vérification en clair, n'a aucune version confidentielle qui ne soit pas une fuite.

Trois choses, par ordre de douleur. La pré-vérification casse en premier : un oui ou non en clair sur un solde secret est un oracle qu'on peut attaquer par dichotomie. La comptabilité des porteurs casse ensuite, car on ne peut pas compter combien de détenteurs ont un solde non nul sans les déchiffrer un à un. Et toute règle portant sur un montant change de mode d'échec : au lieu d'échouer, le transfert réussit et déplace zéro. La règle est toujours appliquée. Elle cesse de s'annoncer.

Parce qu'un revert est public et que le solde ne l'est pas. La documentation du protocole Zama est explicite : les booléens chiffrés ne supportent ni les instructions if ni les opérateurs logiques, donc la logique conditionnelle passe par FHE.select, qui choisit entre deux valeurs chiffrées au lieu de brancher l'exécution. Vous pourriez toujours échouer sur le résultat déchiffré, et c'est précisément le problème : le fait qu'une transaction ait échoué est visible de tous, donc un contrat qui échoue exactement quand une comparaison secrète échoue a publié la réponse à cette comparaison.

Oui, et le mécanisme existe déjà. OpenZeppelin publie ERC7984Freezable, qui stocke un montant gelé chiffré par compte et calcule le solde disponible par une soustraction saturante. Son _update contrôle le transfert avec FHE.select plutôt qu'avec require, et la documentation du contrat dit que le compte doit disposer d'un solde non gelé suffisant, faute de quoi 0 token est transféré. Le pouvoir survit donc. Ce qui change, c'est que le montant gelé est désormais un chiffré, et que le détenteur n'apprend rien d'une transaction réussie.

En détenant une clé, pas en appelant un getter, et c'est la partie que les émetteurs sous-estiment. Trois conceptions existent déjà. La liste de contrôle d'accès de FHEVM accorde à une adresse nommée un droit de déchiffrement durable sur un handle précis. Solana Token-2022 place une clé publique ElGamal d'auditeur optionnelle sur le mint, si bien que chaque transfert confidentiel doit aussi chiffrer le montant sous cette clé. Et ERC-7984 lui-même ne définit qu'AmountDisclosed, que son texte décrit comme une divulgation publique. Un getter se révoque en redéployant. Un droit de déchiffrement est une clé que quelqu'un détient désormais.

Non, et il ne le prétend pas. ERC-7984 est une interface en Draft pour tokens fongibles confidentiels, créée en juillet 2025, et elle ne contient ni gel, ni transfert forcé, ni lecture de solde en clair, ni la moindre notion d'identité. C'est un cadrage juste pour une interface, comme l'est celui d'ERC-7943. Cela signifie aussi que si vous bâtissez un instrument permissionné dessus, chaque obligation de conformité vit dans du code d'extension qui vous appartient et que vous devez garder correct.

Standards That Ship

Episode 5 · 4 publies

PrecedentSuivant
Adam Boudjemaa

Adam Boudjemaa

Ancien CTO d'Integra. Auteur nommé (1 sur 5) d'ERC-3643, premier auteur d'ERC-6960, co-auteur d'ERC-7410 et co-auteur d'ERC-8203, encore à l'état de brouillon. Conçoit des systèmes d'IA en production et des systèmes Web3 régulés.

Cet article vous a plu ?

Recevez les suivants dans votre boîte mail chaque mardi.