Le bouton de gel cesse de fonctionner : la conformité ERC-3643 face à une frontière de VM
À RETENIR
- Le vrai échec n'est pas le contrôle qui ne s'exécute pas. C'est le recours qui cesse d'exister : une fois la réserve mise sous séquestre au compte du module, l'ERC-20 ne voit plus qu'un détenteur, et le gel comme le transfert forcé n'ont plus rien à saisir.
- Mon propre schéma de l'épisode 1 avait compressé une étape. MsgConvertERC20 appelle la fonction transfer du token : les contrôles ERC-3643 s'exécutent bien, une fois, à la porte. L'émetteur ouvre cette porte volontairement. C'est le couloir derrière qui pose problème.
- ERC-7943 (uRWA) est passé Final le 5 mai 2026. Sa seule idée réellement neuve : séparer l'éligibilité d'un compte de l'autorisation d'un transfert. C'est une interface, pas un système.
- Aucun ERC Final ne couvre l'état de conformité qui franchit une frontière de chaîne ou de VM. Ni ERC-7943, ni ERC-3643, que j'ai contribué à écrire.
- Vérifié sur cosmos/evm au commit 0ae5e223 et sur les 611 fichiers du dépôt ERC. Aucun incident, aucune anecdote de guerre. J'ai lu le module.
Un agent de conformité sur un token ERC-3643 dispose de trois pouvoirs qui comptent le jour où quelque chose dérape. Geler une partie d'un solde. Geler une adresse. Forcer un transfert hors d'un portefeuille.
Les trois opèrent sur des soldes ERC-20.
J'ai passé une semaine à lire le module x/erc20 de Cosmos EVM, parce que l'épisode 1 de cette série affirmait quelque chose que je n'avais jamais vérifié ligne à ligne moi-même. Ce que j'y ai trouvé est pire que l'affirmation, et pire dans une direction que je n'attendais pas.
Quand un token est converti en dénomination du module bank, la réserve convertie est mise sous séquestre à une seule adresse : le compte du module. À partir de cet instant, le contrat ERC-20 n'a plus qu'un détenteur.
L'agent conserve ses trois pouvoirs. Ils n'ont plus rien à viser. Geler quel solde ? Forcer un transfert depuis qui ? Les vrais détenteurs sont passés sur un registre que le token ne voit pas.
Ce n'est pas un contrôle qui a échoué. C'est un recours qui a cessé d'exister. Pour un émetteur régulé, le recours est justement la moitié dont un régulateur vous parlera, et c'est celle qui a disparu.
Deux précisions avant d'aller plus loin. La première : l'épisode 1 de cette série dessinait ce chemin de fuite en quatre étapes, et l'une d'elles était compressée. Je corrige mon propre schéma plus bas.
La seconde : je n'ai aucun incident à vous montrer. J'en ai cherché un, et ce qui remonte, ce sont des piratages de ponts, c'est-à-dire des défauts de vérification de message et des compromissions de clés habillés pareil. Mes preuves ici sont la source du module à un commit épinglé et le texte des standards. C'est plus faible qu'une anecdote de guerre et nettement plus solide qu'une intuition.
Le recours, pas le contrôle
Presque tous ceux qui regardent ce problème regardent le contrôle. La restriction de transfert s'est-elle exécutée, oui ou non. C'est la question intéressante quand on est ingénieur et la question ennuyeuse quand on est émetteur, dont la question est la suivante : un régulateur ou un tribunal veut une adresse gelée, et pouvez-vous encore le faire.
ERC-3643 a été écrit pour que la réponse soit oui. Il donne au rôle d'agent setAddressFrozen, freezePartialTokens et forcedTransfer, et ses exigences de spécification disent noir sur blanc que le token DOIT pouvoir forcer des transferts depuis un portefeuille d'agent. Ce n'est pas un confort ajouté sur le côté. C'est une exigence affichée du standard, parce que le transfert forcé est ce dont l'émission régulée a réellement besoin.
Chacune de ces fonctions prend une adresse et bloque ou déplace un solde ERC-20.
Mettez maintenant la réserve sous séquestre à un compte de module. Les soldes que l'agent peut atteindre se réduisent à une seule entrée. Les soldes qui comptent sont ailleurs.
Ce que l'agent peut faire sur l'EVM
Ce que l'agent atteint après conversion
Je suis l'un des cinq auteurs nommés d'ERC-3643 (T-REX), alors lisez ceci comme quelqu'un qui décrit les limites de son propre travail, pas comme un point marqué contre autrui.
Là où mon propre schéma se trompait
L'épisode 1 dessinait la fuite en quatre nœuds : un token conforme, enregistré dans x/erc20 comme dénomination bank, déplacé par bank.MsgSend là où les contrôles EVM ne tournent jamais, puis sorti par IBC.
La forme est juste. Le compte des étapes ne l'est pas.
Entre enregistré et déplacé il y a une conversion, et cette conversion n'est pas une écriture comptable. MsgConvertERC20 appelle la fonction transfer du token lui-même, dans l'EVM, avec le compte du module comme destinataire.
// cosmos/evm, x/erc20/keeper/convert.go @ 0ae5e223
// Escrow tokens on module account
transferData, err := erc20.Pack("transfer", types.ModuleAddress, amount.BigInt())
if err != nil {
return nil, err
}
res, err := k.evmKeeper.CallEVMWithData(
ctx, stateDB, sender, &erc20Contract, transferData,
commit, callFromPrecompile, nil,
)// ERC-3643: the checks that run on that very call
require(
_tokenIdentityRegistry.isVerified(to),
"ERC-3643: Invalid identity"
);
require(
_tokenCompliance.canTransfer(from, to, amount),
"ERC-3643: Compliance failure"
);Pour un token ERC-3643, cela veut dire que les contrôles de pré-transfert s'exécutent. isVerified est appelé sur le compte du module. canTransfer est appelé sur les règles de l'offre. Si le compte du module n'est pas une identité vérifiée dans le registre d'identité du token, la conversion échoue et rien ne bouge.
La conformité n'est donc pas contournée. Elle a exactement une chance, à la porte, et elle fonctionne. On dirait que cela ruine l'argument, alors que c'est l'inverse.
Autoriser le compte du module est la seule façon de rendre le token utilisable avec l'outillage natif Cosmos, et rendre le token utilisable avec l'outillage natif Cosmos est toute la raison pour laquelle on l'enregistre. Personne n'est dupé ici. L'émetteur ouvre cette porte volontairement, pour une bonne raison, après une réunion.
Ce que l'émetteur ne voit pas, c'est que la porte donne sur un couloir sans aucune autre porte.
Le chemin corrigé, ses cinq étapes
Voici le chemin complet, l'étape manquante rétablie, avec une découverte de plus à la fin que l'épisode 1 n'atteignait pas.
L'étape 2 mérite sa propre note. Le commentaire du gestionnaire d'enregistrement est direct : n'importe quel compte peut enregistrer sans permission un contrat ERC20 natif pour le mapper vers une Coin Cosmos. Quand la chaîne tourne avec l'enregistrement sans permission activé, l'émetteur n'est jamais consulté. Désactivez-le et l'enregistrement passe par une proposition de gouvernance, plus lente et publique, et toujours pas une décision de l'émetteur.
- 1
DétenteurToken ERC-3643
Un transfert EVM normal, parfaitement conforme
Toutes les restrictions de transfert appliquées. Rien à redire ici, et rien à redire encore.
- 2
DétenteurModule x/erc20
MsgRegisterERC20 mappe le token vers une dénomination bank
Sans permission quand la chaîne l'autorise. L'enregistrement seul ne déplace rien et n'appelle rien.
- 3
Module x/erc20Token ERC-3643
MsgConvertERC20 appelle transfer(compteDuModule, montant)
L'étape que l'épisode 1 avait compressée. isVerified et canTransfer s'exécutent ici et bloquent, sauf si le compte du module est autorisé.
- 4
Module x/erc20x/bank
MintCoins, puis SendCoinsFromModuleToAccount
Le solde est désormais une sdk.Coin ordinaire. La réserve ERC-20 sous séquestre tient au compte du module.
- 5
Détenteurx/bank
bank.MsgSend le déplace vers n'importe qui
Aucun hook, aucun wrapper, aucun ante-handler dans x/erc20 n'intercepte un envoi bank d'une dénomination appairée.
- 6
x/bankIBC
Transfert ICS-20, et il quitte la chaîne
L'EVM n'a rien vu de tout cela.
- 7
Token ERC-3643Agent de conformité
balanceOf n'affiche qu'un détenteur
Le gel et le transfert forcé n'ont plus rien à saisir. C'est l'étape que personne ne dessine.
J'ai lu tous les fichiers de x/erc20/keeper en cherchant le hook qui rendrait l'étape 5 fausse. Il n'y en a pas. Les deux seuls endroits où le module invoque un transfer ERC-20 sont les deux chemins de conversion.
C'est un argument par l'absence, alors bornons-le honnêtement. Un module, un commit, 0ae5e223, cosmos/evm actuel. Les chaînes tournant sur d'anciens forks d'Evmos peuvent différer, et je ne les ai pas lues. La généralisation sûre est celle que l'épisode 1 faisait déjà : une conformité qui vit dans un contrat n'est appliquée que sur les chemins qui exécutent ce contrat.
La sortie n'est pas gardée. Le retour, si.
Il y a dans le module un détail qui ressemble à une blague quand on l'a fixé assez longtemps.
La conversion inverse, elle, réexécute la conformité. Le chemin retour appelle transfer depuis l'adresse du module vers le destinataire, donc les contrôles du token s'exécutent au retour, exactement comme prévu.
Le token garde le voyage retour et pas la sortie. Or un token parti par IBC ne fait pas le voyage retour : il existe comme voucher sur une chaîne étrangère, et il n'a jamais besoin de rentrer.
Quant à ce que le pont transporte, la charge utile du paquet ICS-20 tient en cinq champs, au complet :
interface FungibleTokenPacketData {
denom: string
amount: uint256
sender: string
receiver: string
memo: string
}Dénomination, montant, expéditeur, destinataire, mémo. Aucune identité. Aucune attestation. Aucune juridiction. Aucun drapeau de restriction.
Ce n'est pas un oubli non plus. La spécification ICS-20 range parmi ses propriétés recherchées des transferts de tokens sans permission, sans besoin d'autoriser des connexions, des modules ou des dénominations, et précise que le protocole n'exige aucune permission supplémentaire. Il fait exactement ce qui est écrit sur l'étiquette.
Le champ mémo est le seul point d'extension, et c'est une chaîne libre destinée au middleware. Aucun standard ne lui donne un sens de conformité.
Tout ce qui précède suppose qu'il existe quelque part un contrat Solidity capable de porter la logique de conformité. Pour un token né comme coin Cosmos native, il n'y en a pas. Sous Single Token Representation v2, la face ERC-20 d'une coin native est un précompilé à état adossé directement à x/bank, pas un contrat déployé. Il n'existe aucun bytecode où un registre d'identité pourrait vivre, et la conversion est purement désactivée pour ces paires.
Les dénominations arrivant par IBC obtiennent une paire de tokens créée automatiquement, sans étape de gouvernance et sans intervention de l'émetteur. L'épisode 1 disait : quelle que soit la VM que la chaîne ajoutera l'an prochain. Voici cette phrase avec un nom de fichier attaché.
Où le contrôle peut-il vraiment tourner ?
Prenons du recul par rapport à Cosmos une seconde, car le module précis n'est pas le sujet. Le sujet est l'altitude.
Dessinez les couches qu'un transfert de valeur peut traverser, puis marquez la seule couche où un contrôle ERC-3643 existe. Chaque couche au-dessus de cette marque est un chemin que le contrôle ne couvre pas, et les chemins sont ajoutés par qui maintient la chaîne, à son rythme, pas à celui de l'émetteur.
C'est la même discipline que j'applique aux agents qui paient : placer la règle sur la ressource plutôt que sur l'appelant, car un appelant se contourne et une ressource non.
Ce qu'ERC-7943 a vraiment changé
L'épisode 1 décrivait ERC-7943 comme un fil actif sur Ethereum Magicians. C'était exact quand je l'ai écrit et cela a cessé de l'être le 5 mai 2026, quand le dépôt l'a passé en Final. Un cycle de presse a suivi plus tard le même mois, d'où les deux dates que vous croiserez. Celle qui compte est celle du dépôt.
Ce n'est pas mon standard. Dario Lo Buglio, Tino Martinez Molina et Mihai Colceriu l'ont écrit, et ils l'ont mené du premier brouillon au Final en moins d'un an.
Juin 2025 : dépôt
uRWA arrive dans le dépôt ERC comme brouillon : une interface de conformité pour actifs réels couvrant plusieurs types de tokens. Il passe en Review le mois suivant.
Septembre 2025 : alignement sur ERC-3643
Un commit intitulé « Align with ERC-3643 naming conventions » arrive, suivi d'une séparation en interfaces distinctes. La compatibilité est délibérée et datée.
Janvier 2026 : Last Call
La dernière fenêtre de commentaires s'ouvre.
5 mai 2026 : Final
Le commit 46745bdc fait passer l'en-tête de Last Call à Final et supprime au passage la date limite de Last Call. C'est ce commit qu'il faut citer, pas le communiqué de presse trois semaines plus tard. À savoir : un EIP ne porte aucun champ de finalisation, seulement une date de création, donc l'historique du dépôt est le seul endroit où le jour est consigné.
Ce qu'il normalise : une interface partagée de conformité RWA couvrant ERC-20, ERC-721, ERC-1155 et ERC-6909. Six fonctions, forcedTransfer, setFrozenTokens, getFrozenTokens, canSend, canReceive et canTransfer, avec introspection ERC-165 et identifiants d'interface fixes.
L'idée réellement neuve tient en une ligne de cette liste. ERC-7943 sépare la question de savoir si un compte peut participer de celle de savoir si un transfert donné est autorisé. canSend et canReceive évaluent un compte indépendamment de tout paramètre de transfert, ce qui permet d'exprimer une restriction unilatérale : une adresse à qui l'on interdit de recevoir mais à qui l'on permet d'envoyer. Cette situation est banale en marché régulé et ERC-3643 n'a aucun moyen de la dire.
J'utiliserais cette séparation. C'est la bonne décomposition et j'aurais aimé que nous la fassions en 2021.
Un piège, car il attrapera quelqu'un. canTransfer existe dans les deux standards et n'y désigne pas la même chose. Dans ERC-3643 il vit sur le contrat ICompliance et ne regarde que les règles globales de l'offre, en opposition volontaire avec isVerified qui contrôle l'éligibilité d'un investisseur. Dans ERC-7943 il vit sur le token et doit couvrir à la fois l'éligibilité et le solde gelé. Même nom, autre contrat, autre portée.
Si vous choisissez entre des modèles de conformité plutôt que de lire sur un seul, la comparaison d'ERC-3643, ERC-1400 et CMTAT est le texte qui pose l'arbitrage, et il place ERC-7943 dans le même cadre.
Ce qu'il ne corrige pas, y compris dans mon propre standard
J'ai passé toute la spécification ERC-7943 au grep, 446 lignes, en cherchant ce qui touche aux chaînes. Cross-chain, cross-VM, multi-chain, bridge, interoperability, IBC, Cosmos, non-EVM, rollup.
Trois occurrences. La première parle de faire le pont entre finance traditionnelle et finance décentralisée : une métaphore. La deuxième parle d'interopérabilité entre standards de tokens RWA. C'est tout. Les Security Considerations couvrent le contrôle d'accès, le front-running et la sécurité ordinaire des contrats, et ne disent rien des frontières de chaîne.
Ce n'est pas une critique. ERC-7943 se donne pour périmètre une interface de contrat unique et n'a jamais prétendu autre chose ; taper sur ses auteurs parce qu'ils n'ont pas résolu un problème hors de leur périmètre serait facile.
Voici la partie qui me coûte. J'ai passé le même grep sur ERC-3643. Ses 414 lignes. Zéro occurrence. Aucun des deux standards RWA en statut Final ne dit un mot de ce qui arrive quand un token franchit une chaîne ou une VM, et j'ai contribué à écrire l'un des deux.
J'ai donc vérifié le reste du corpus. J'ai cloné le dépôt ERC, 611 fichiers, numéro le plus haut 8330, et retenu tous ceux mentionnant à la fois un terme de conformité et un terme de chaîne. Vingt-cinq correspondaient. J'ai lu les plausibles.
Aucun ERC Final ne couvre l'état de conformité franchissant une frontière de chaîne ou de VM. Le manque nommé par l'épisode 1 est toujours ouvert, et il l'est resté assez longtemps pour que deux standards Final distincts soient sortis en le contournant.
Votre token est-il exposé ?
Vous pouvez situer votre cas en une minute environ, et la réponse est en général l'une de quatre.
Mon token conforme est-il exposé ?
Si Il vit sur une seule chaîne EVM, sans module bank ni pont
Non exposé
Le contrôle et le recours tiennent sur le même registre. Tout cet article est le problème de quelqu'un d'autre.
Si Il est sur une chaîne Cosmos EVM et le compte du module n'est pas une identité vérifiée
Bloqué à la porte
La conversion échoue sur isVerified. Gardez-le ainsi, en sachant que le garder ainsi vous coûte toutes les intégrations natives Cosmos.
Si Vous avez autorisé le compte du module pour que le token marche avec l'outillage natif
Exposé, volontairement
La conversion est légitime. Tout ce qui vient après échappe à ce que votre agent de conformité voit ou atteint.
Si Vous ignorez si quelqu'un a enregistré votre token
Vérifiez aujourd'hui
L'enregistrement est sans permission quand la chaîne l'autorise, et l'émetteur n'est jamais consulté. Interrogez les paires de tokens.
Aucune de ces quatre issues ne se règle par un meilleur contrat. Trois se décident par l'endroit où vit la règle.
C'est l'argument de l'épisode 1, ici avec les lignes de source dedans. Une règle de conformité doit se placer sous tous les chemins qui déplacent de la valeur, pas sur l'un d'eux. Sur la chaîne que je construis, cela veut dire des restrictions de transfert comprises au niveau du protocole plutôt que déployées par-dessus, pour que la couche bank et le pont héritent de la même règle que le token. Le testnet tourne. Le mainnet est en bonne voie pour 2026.
Et si vous voulez la version honnête de ma propre position : j'ai contribué à un standard qui résout magnifiquement ce problème à l'intérieur d'un environnement d'exécution, et qui ne dit rien du second. C'est le manque. Je préfère le nommer plutôt que quelqu'un le découvre en production.
La thèse complète, y compris pourquoi une chaîne dédiée plutôt qu'un L2, est dans le premier épisode de cette série.
FAQ
Non, et ce détail compte. MsgConvertERC20 appelle la fonction transfer du token avec le compte du module comme destinataire : isVerified et canTransfer s'exécutent, et la conversion échoue si ce compte n'est pas une identité vérifiée. Le problème vient après : la réserve sous séquestre tient sur une seule adresse, les envois bank ne portent aucune conformité, et les pouvoirs d'exécution n'ont plus rien à saisir.
Non. setAddressFrozen, freezePartialTokens et forcedTransfer opèrent tous sur des soldes ERC-20, et après conversion l'ERC-20 n'a qu'un détenteur : le compte du module x/erc20. Les détenteurs de la couche bank sont invisibles pour le contrat de token. C'est un recours perdu plutôt qu'un contrôle manqué, et c'est la moitié dont un régulateur vous parlera après coup.
Non, et il ne le prétend pas. ERC-7943 est passé Final le 5 mai 2026 et normalise une interface de conformité pour un seul contrat de token : canSend, canReceive, canTransfer, forcedTransfer, setFrozenTokens et getFrozenTokens. Son texte ne dit rien des chaînes, des ponts ni des machines virtuelles. ERC-3643 non plus, et je suis l'un des cinq auteurs nommés de ce standard.
Aucun en statut Final. J'ai passé en revue les 611 fichiers ERC du dépôt. ERC-7802 est crosschain et délibérément neutre sur le contrôle d'accès. ERC-8121 sait résoudre une attestation détenue sur une autre chaîne. ERC-8262 fait l'inverse et lie les attestations à un seul déploiement pour qu'elles ne voyagent pas. Les trois sont en Draft. ERC-7518 ressemble à la réponse et n'en est pas une : son Interop désigne l'enveloppement de tokens sur la même chaîne, et il est en Review, pas Final.
Pas à ma connaissance, et j'ai cherché. Les incidents de ponts que l'on cite sont des défauts de vérification de message ou des compromissions de clés : un autre problème habillé pareil. Ce que j'ai, c'est la source du module à un commit épinglé et le texte des standards. Mon analyse est que le volume RWA permissionné sur les chaînes Cosmos EVM reste assez faible pour que personne n'ait eu à l'apprendre au prix fort.
Building the RWA Chain
Episode 2 · 1 publies
Cet article vous a plu ?
Recevez les suivants dans votre boîte mail chaque mardi.
