Skip to main contentA-REX Foundation

Livre blanc · 08

Cadre de sécurité

Modèle de menaces, invariants, contrôle d'accès, frontière de confiance des hooks, gestion des clés de mise en application, sûreté des mises à niveau et posture d'audit de l'implémentation de référence d'A-REX Core.

La posture de sécurité en un paragraphe

Le jeton de référence possède un seul point de passage obligé (_update), un ordre d'évaluation fixe, un verrou de réentrance autour de toute la séquence de hooks, une autorité fondée sur les rôles qui n'est jamais dérivée des soldes, un stockage ERC-7201 et une surface réduite : huit fonctions externes modifiant l'état en plus d'ERC-1155. Les capacités les plus puissantes — gel, transfert forcé, administration des hooks, mises à niveau — sont des rôles distincts destinés à des multisigs et à des timelocks, et chacun de leurs usages est un événement.

Invariants

InvariantGaranti parTesté par
totalSupply(id) est égal à la somme des soldes pour idOpenZeppelin ERC1155Supply ; transform détruit puis émet via _updatetests de filiation (fractionnement, fusion, traitement)
status == 255 est terminalsetBatchStatus annule la transaction avec ARexInvalidStatusTransitiontests de statut
Un lot retiré ne peut être ni émis ni transféré, seulement détruit_updatetest du lot retiré
amount ≤ balance − frozen pour les mouvements initiés par le détenteur_update ; forcedTransfer abaisse frozen à balance − amounttests de gel et de transfert forcé
Aucun hook ne peut ré-entrer dans une fonction modifiant les soldesverrou de stockage posé avant les vérifications, levé après les notificationstest ReentrantHook
Chaque modification de solde est évaluée comme sa propre opération, avec ses propres data_update lit et efface le contexte d'opération avant toute vérification ou tout appel externeOpContext.test.js
Les hooks sont évalués dans l'ordre d'enregistrement ; la suppression préserve l'ordredécalage du tableau dans removeHooktest d'ordre
Une opération rejetée ne modifie aucun solde et n'émet aucun transfertles vérifications s'exécutent avant super._updatetest de rejet (compteur d'exécutions inchangé)
L'autorité ne dérive jamais du soldechaque fonction modifiant l'état est conditionnée à un rôle ou à une approbation ERC-1155tests « détenir des jetons ne confère rien »
canTransfer == (transferCheck == 0)canTransfer est défini ainsitests de conformité

Modèle de menaces

Acteurs et capacités

ActeurPeutNe peut pas
Détenteurtransférer des unités non gelées à des destinataires éligibles ; détruire ; réorganiser des lots de même denrée et de même unité avec quantité conservée vers son propre compte (to == from)modifier le statut, émettre, traiter pour obtenir une autre denrée, réorganiser des lots vers un autre compte, déplacer des unités gelées
Opérateur approuvécomme le détenteur, pour le compte qui l'approuvetout ce qu'un détenteur ne peut pas faire
ISSUER_ROLEcréer des lotsdéplacer les jetons d'autrui
TRANSFORMER_ROLEeffectuer des transformations de traitement, et des transformations dont les sorties vont à un autre compte, sur les jetons qu'il détient ou pour lesquels il est approuvétransformer des jetons sans approbation
STATUS_ROLEmodifier le statut (y compris retirer), mettre à jour les métadonnéesdéplacer des jetons
ENFORCER_ROLEgeler n'importe quel montant ; forcer n'importe quel transfertcontourner les hooks (ils s'exécutent avec OP_FORCED_TRANSFER)
HOOK_ADMIN_ROLEajouter/retirer des hooksmodifier directement les soldes
PAUSER_ROLEtout interrompre sauf les transferts forcés—
UPGRADER_ROLEremplacer l'implémentation— (c'est un pouvoir total ; voir la gestion des clés)
ATTEST_ADMIN_ROLEdécider quels émetteurs sont de confiance pour chaque thème d'attestation ; dans les versions de référence jusqu'à 2.0.0-alpha.5, révoquer aussi n'importe quelle attestation ; à partir de 2.0.0-alpha.6, seuls l'émetteur ou un révocateur qu'il a désigné révoquent (ADR-019, Proposé : une révocation administrative NE DEVRAIT PAS exister en 2.x et NE DOIT PAS exister à partir de 3.0)attester au nom d'un émetteur
Émetteur de confianceattester des lots pour ses thèmes ; révoquer ses propres attestations, ou déléguer la révocationattester pour un thème pour lequel il n'est pas de confiance
Contrat de hookrejeter des opérations ; les observer ; répondre sur l'éligibilitéré-entrer ; modifier les soldes

Scénarios

Hook malveillant ou défectueux (déni de service). Un hook qui rejette toujours bloque tous les transferts du jeton ; un hook qui annule la transaction dans check produit le même effet. Atténuation : HOOK_ADMIN_ROLE peut retirer n'importe quel hook à tout moment (ordre préservé) ; PAUSER_ROLE peut tout interrompre pendant l'enquête ; les transferts forcés s'exécutent toujours, de sorte que la mise en application n'est pas perdue. L'enregistrement vérifie la prise en charge d'ERC-165 et des drapeaux non nuls, et rejette les EOA. Recommandation : auditer les hooks avant de les enregistrer ; en garder un petit nombre ; placer l'administrateur derrière un timelock afin que les changements de hooks soient visibles avant leur prise d'effet.

Réentrance d'un hook. Le onExecuted d'un hook rappelle safeTransferFrom. Atténuation : le verrou ; l'appel annule la transaction avec ARexReentrantCall. Notez que le callback de réception d'ERC-1155 (onERC1155Received) s'exécute après la levée du verrou, comme dans tout jeton ERC-1155 ; l'état du jeton est alors définitif.

Contexte d'opération dans les callbacks de réception. Un destinataire de type contrat lance un transfert ou une destruction depuis onERC1155Received. Cet appel imbriqué est un OP_TRANSFER ou un OP_BURN avec ses propres data, et non une partie de l'opération qui a livré les jetons. Jusqu'à 2.0.0-alpha.2, le jeton de référence conservait l'opération externe en stockage jusqu'au retour de l'appel externe : un appel imbriqué après un forcedTransfer s'exécutait donc en tant que OP_FORCED_TRANSFER (en contournant la pause, l'éligibilité et les soldes gelés), et un appel après un transform en tant que OP_TRANSFORM, que les hooks qui ne contrôlent que OP_TRANSFER laissent passer. Atténuation : _update lit et efface le contexte d'opération avant toute vérification ou tout appel externe, de sorte qu'un appel lancé depuis un callback est évalué pour lui-même ; OpContext.test.js utilise un destinataire qui transfère et détruit depuis son callback.

Livraison par réorganisation de lots. Les deux mouvements d'un transform exécutent les hooks avec OP_TRANSFORM. Si un détenteur pouvait réorganiser des lots vers un autre compte, des biens changeraient de mains sans les vérifications des hooks qui ne contrôlent que OP_TRANSFER, comme une attestation exigée avant la vente ou des unités verrouillées pour une livraison. Atténuation : sans TRANSFORMER_ROLE, une réorganisation de lots n'est permise que si to == from ; les biens réorganisés parviennent à un autre détenteur par un transfert ordinaire qui exécute toutes les règles de transfert (ADR-007, amendé le 2026-10-20).

Contourner un verrou par destruction ou réorganisation de lots. L'état d'un hook conservé par identifiant de lot ne suit pas un lot dans ses sorties. Un hook qui verrouille des unités et ne contrôle que OP_TRANSFER n'empêche pas le détenteur de détruire les unités verrouillées, ni de les réorganiser sur place dans un nouvel identifiant que le verrou ne couvre pas. Atténuation : DeliveryHook rejette avec IN_TRANSIT toute destruction ou tout mouvement d'entrée d'un transform qui ferait passer le solde sous les unités verrouillées ; les transferts forcés restent illimités, et le hook renvoie un code au lieu d'annuler la transaction après une saisie. Les hooks qui réservent des unités devraient contrôler OP_BURN et OP_TRANSFORM en plus de OP_TRANSFER.

Clé de mise en application compromise. Quiconque détient ENFORCER_ROLE peut déplacer n'importe quels jetons. Atténuation : multisig avec une procédure documentée (ordonnance judiciaire, contrat de nantissement, avis de sanctions) ; chaque action émet ForcedTransfer/Frozen ; les hooks peuvent exiger une référence d'ordonnance dans les données de transfert (OP_FORCED_TRANSFER n'est pas exempté des hooks). Envisager un timelock pour la mise en application non urgente.

Compromission du rôle de mise à niveau. Compromission totale. Atténuation : multisig plus timelock ; validations de mise à niveau d'OpenZeppelin en CI ; stockage ERC-7201 pour éviter les accidents de disposition.

Abus de statut. STATUS_ROLE peut retirer un lot détenu par d'autres. C'est intentionnel — un entrepôt, un inspecteur ou un régulateur déclarant un lot impropre — et cela doit être communiqué aux détenteurs. Atténuation : l'attribuer à une institution responsable ; chaque changement est un événement accompagné d'un motif.

Fraude de l'émetteur. ISSUER_ROLE peut créer un lot qui n'existe pas physiquement. Le jeton ne peut pas l'empêcher ; il le rend imputable (issuer, originRef, metadataHash) et permet d'exiger avant tout transfert, via AttestationHook, des attestations de parties indépendantes (entrepôt, inspecteur, registre).

Localisation cachée dans la référence d'origine. Un hash, un geohash ou tout autre encodage des coordonnées d'une parcelle dans originRef semble anonyme, mais il peut être recalculé à partir d'une parcelle devinée et ne peut jamais être effacé de la chaîne. Atténuation : originRef est une référence à des preuves ou une référence aléatoire ; la géolocalisation reste off-chain (pour l'EUDR, dans le système d'information de l'UE). Le projet proposé avec l'ADR-019 (Proposé) fait de « jamais dérivée de la localisation » une exigence de niveau MUST ; c'était déjà la décision de l'ADR-008.

Références en amont non vérifiées. Un lot fabriqué à partir du lot d'un autre jeton le désigne dans la liste upstream de ses métadonnées. C'est une affirmation de l'émetteur en aval : il peut désigner un lot qu'il n'a jamais détenu ou en omettre un qu'il a détenu. Atténuation : les indexeurs, les auditeurs et les outils de rappel vérifient chaque référence auprès du jeton en amont (le lot existe, le hash des métadonnées correspond, le transfert vers l'émetteur a bien eu lieu lorsqu'un hash de transaction est fourni) et signalent comme non vérifiées celles qu'ils n'ont pas pu vérifier.

Substitution de métadonnées. setBatchMetadata modifie le hash et l'URI. Atténuation : accès réservé à un rôle ; les événements BatchMetadataUpdated et URI conservent l'historique ; les documents antérieurs restent vérifiables par rapport aux hashes antérieurs.

Front-running de transferCheck. Une vérification préalable peut devenir obsolète avant l'exécution de la transaction (l'état d'un hook change). La transaction réévalue ; le pire cas est un revert avec un code précis.

Griefing de gaz via les hooks. Chaque hook ajoute un appel externe par identifiant concerné et par phase. Un hook avec des boucles non bornées rend les transferts inabordables. Atténuation : règles d'écriture des hooks (check borné), revue avant l'enregistrement.

Confiance envers les hooks pouvant être mis à niveau. Un hook déployé derrière un proxy peut modifier la politique de transfert sans aucun changement sur le jeton. Atténuation : traiter les clés d'administration des hooks avec le même soin que HOOK_ADMIN_ROLE ; privilégier des hooks immuables en production.

Modèle de contrôle d'accès

AccessControl d'OpenZeppelin avec huit rôles sur le jeton et un sur le registre d'attestations ; DEFAULT_ADMIN_ROLE les administre tous. Disposition recommandée en production :

RôleTitulaire
DEFAULT_ADMIN_ROLE, UPGRADER_ROLE, HOOK_ADMIN_ROLEmultisig de gouvernance derrière un timelock
ENFORCER_ROLEmultisig de mise en application (conformité + juridique)
PAUSER_ROLEclé de réponse aux incidents (rapide)
ISSUER_ROLE, STATUS_ROLE, TRANSFORMER_ROLEservices opérationnels au moindre privilège, avec rotation des clés
ATTEST_ADMIN_ROLEla gouvernance du jeton : elle décide quels émetteurs sont de confiance pour chaque thème, non ce qu'ils attestent

initialize accorde tout à un seul administrateur par commodité ; la liste de contrôle de déploiement exige une redistribution avant la production.

Sémantique de la mise en application et de la pause

  • La pause bloque les émissions, les transferts, les destructions et les transformations ; elle ne bloque pas les transferts forcés, de sorte qu'un ordre légal peut être exécuté pendant un incident.
  • Les transferts forcés ignorent canSend/canReceive et la vérification du solde gelé, abaissent le montant gelé à ce qui reste, et exécutent tout de même les hooks avec OP_FORCED_TRANSFER. Les hooks de référence renvoient OK pour cette opération ; un hook qui régit la mise en application peut exiger des références d'ordonnance dans data.
  • Le gel peut dépasser le solde (ERC-7943), de sorte qu'une sûreté peut couvrir les dépôts futurs.

Les capacités dans le standard, la politique dans les profils

L'ADR-019 (Proposé) trace une même ligne à travers toutes les fonctions de mise en application. Le standard fixe ce qui existe et ce qu'il garantit : les rôles, les événements, ce qu'une pause arrête, les codes AREX:PAUSED et AREX:SANCTIONED, qui peut révoquer une attestation, et les règles de protection de la vie privée. Un profil de politique, tenu par juridiction et par organisation et versionné avec des dates comme une réglementation, indique qui peut utiliser chaque capacité et à quelles conditions :

CapacitéFixé par le standardDéfini par le profil de politique
Gel et transfert forcésetFrozenTokens, forcedTransfer, leurs événements, un rôle dédié distinct de l'administration, des hooks exécutés avec OP_FORCED_TRANSFERsi le rôle peut être activé ou non, qui le détient et avec combien de signataires, quelle référence juridique chaque usage doit porter, qui notifier
Pausece qu'une pause arrête, AREX:PAUSED, les transferts forcés toujours permisqui peut mettre en pause et lever la pause, la confirmation et le motif exigés, qui est informé
SanctionsAREX:SANCTIONED et le sens de canSend / canReceivequelles listes officielles s'appliquent (ONU, OFAC, UE, OFSI ou une combinaison), comment elles sont tenues à jour, et l'exigence qu'une personne confirme une correspondance avant qu'un compte soit marqué
Révocation d'attestationl'émetteur révoque, un révocateur qu'il a désigné le peut ; un rôle administratif ne le devrait pas en 2.x et ne le doit pas à partir de 3.0 ; attester à nouveau ne remplace jamais l'enregistrement d'un autre émetteur et ne rétablit jamais un enregistrement révoquéquels émetteurs un jeton reconnaît pour chaque thème ; comment un opérateur consigne une contestation
Confidentialité de l'origineoriginRef ne contient ni coordonnées ni identifiants personnels et n'est pas dérivé de la localisationquelle référence un émetteur utilise, et où la géolocalisation est conservée off-chain

Un changement de loi devient alors une mise à jour de profil, jamais un nouveau contrat ni une nouvelle version du standard.

Sûreté des mises à niveau

  • UUPS ; _authorizeUpgrade conditionné par UPGRADER_ROLE.
  • Structure de stockage ERC-7201 à espace de noms ; ajouts uniquement.
  • Les validations du plugin de mises à niveau d'OpenZeppelin s'exécutent dans les tests et devraient s'exécuter en CI avant toute transaction de mise à niveau.
  • Le paramètre optimizer.runs = 1 par fichier maintient l'implémentation sous EIP-170 ; une modification qui la fait dépasser la limite échoue à la compilation avec un avertissement et au déploiement avec un revert.

Sécurité des dépendances et du build

  • OpenZeppelin Contracts et Contracts-Upgradeable épinglés à 5.3.0 ; fichier de verrouillage versionné ; la CI utilise npm ci.
  • Solidity 0.8.22, viaIR, EVM paris (aucune dépendance au stockage transitoire, déployable sur des chaînes qui n'ont pas adopté Cancun).
  • Aucune bibliothèque externe liée ; aucun delegatecall en dehors du proxy UUPS.

Posture d'audit

  • 46 tests sur l'implémentation de référence, les hooks et les extensions couvrent chaque invariant ci-dessus, y compris une exécution d'invariants aléatoire à graine fixée.
  • Le gaz et la taille sont mesurés par des scripts du dépôt.
  • Un audit par un tiers de ARexToken, ARexAttestations et des hooks de référence est une condition préalable déclarée pour un participant d'ancrage en production et pour la soumission de l'ERC (voir Évolutions futures).
  • La vérification formelle des invariants du pipeline figure sur la feuille de route ; les invariants sont suffisamment petits pour être énoncés sous forme de propriétés.

Avis de sécurité

AvisConcerneCorrigé dansProblèmes
AREX-2026-001implémentation de référence jusqu'à 2.0.0-alpha.2 inclus2.0.0-alpha.3le contexte d'opération fuyait dans les callbacks de réception ERC-1155 ; la réorganisation de lots sans rôle pouvait livrer des biens à un autre détenteur en contournant les hooks OP_TRANSFER ; les verrous de DeliveryHook pouvaient être contournés par destruction ou réorganisation de lots

2.0.0-alpha.3 remplace 2.0.0-alpha.2. Les proxys de jeton déployés sont mis à niveau vers la nouvelle implémentation via UPGRADER_ROLE ; la disposition du stockage est inchangée. DeliveryHook n'est pas évolutif : déployer la nouvelle version, l'enregistrer avec registerHook, retirer l'ancienne avec removeHook, et replanifier les livraisons actives, car leurs verrous résident dans le stockage de l'ancien hook. Les intégrations qui fractionnent ou fusionnent directement vers le compte d'un acheteur doivent réorganiser sur place puis transférer, ou utiliser un compte doté de TRANSFORMER_ROLE. Les interfaces du standard ne changent pas.

Risques résiduels que le standard ne supprime pas

  • L'existence physique et la qualité d'un lot sont attestées, non prouvées ; le jeton rend les affirmations imputables, non vraies.
  • La disponibilité des métadonnées off-chain dépend du stockage choisi ; le hash garantit l'intégrité, non la disponibilité.
  • La gouvernance des hooks et des modules ne vaut que ce que valent les clés qui la contrôlent.