Skip to main contentA-REX Foundation

Livre blanc · 07

Hooks et modules de référence

Comment des fonctionnalités s'ajoutent à un jeton conforme sans toucher au standard : le contrat des hooks, les hooks de référence, les extensions compagnons, et la manière d'écrire, de tester et d'enregistrer les vôtres.

Les modules sont des démonstrations, non le standard

La relation entre A-REX Core et ses modules est celle qui existe entre ERC-3643 et les modules de conformité de T-REX : l'interface par laquelle les modules se branchent est standardisée ; les modules ne le sont pas. Un jeton conforme sans aucun hook est un jeton A-REX valide. Un hook écrit par n'importe qui est compatible avec tous les jetons conformes. C'est ce qui permet à l'écosystème de croître sans que le standard change.

graph LR
    T["ARexToken<br/>jeton conforme"] -->|"check / onExecuted / canSend / canReceive"| H1[EligibilityHook]
    T --> H2[ExpiryHook]
    T --> H3[AttestationHook]
    T --> H4["Votre hook"]
    H3 --> A[ARexAttestations]

Le contrat de hook

CallbackQuandDoit
checkavant la modification des soldes, par identifiant concerné, hooks dans l'ordre d'enregistrement, arrêt au premier code non nulêtre une fonction de lecture seule (view) ; ne jamais annuler la transaction pour des motifs de politique
onExecutedaprès la modification des soldes, par identifiant, dans l'ordrene pas appeler les fonctions du jeton qui modifient les soldes (le jeton est verrouillé)
canSend / canReceiveà chaque transfert et pour les vues ERC-7943 du jetonêtre des fonctions de lecture seule (view)

op vaut l'un des OP_MINT (1), OP_TRANSFER (2), OP_BURN (3), OP_FORCED_TRANSFER (4), OP_TRANSFORM (5). Dans une transformation, les entrées arrivent avec to == 0 et les sorties avec from == 0. data correspond aux données de transfert ERC-1155, abi.encode(processRef) pour les transformations, vide pour les transferts forcés.

Le jeton appelant est msg.sender. Un hook qui a besoin du lot lit IARexBatch(msg.sender).batchOf(id).

Hooks de référence

EligibilityHook — autoriser, bloquer, sanctionner

Démontre HOOK_ELIGIBILITY. Maintient une liste d'autorisation facultative, une liste de blocage et une liste de sanctions ; canSend/canReceive renvoient leur conjonction ; check renvoie SANCTIONED, SENDER_INELIGIBLE ou RECEIVER_INELIGIBLE afin que les interfaces puissent expliquer le refus. Les transferts forcés sont toujours autorisés : saisir des biens sur un compte sanctionné est précisément l'objet de la mise en application. En production, les listes sont alimentées par un registre d'identité (ONCHAINID, EAS, justificatifs vérifiables) ou par un oracle de sanctions ; peu importe au hook lequel.

ExpiryHook — la périssabilité sans module

Sans état. Lit expiresAt et producedAt dans le lot. Bloque les émissions et les transferts de lots expirés (BATCH_EXPIRED), mais pas les destructions (passage en pertes) ni les transferts forcés (mise en application). Bloque facultativement les transferts de lots pas encore produits (BATCH_NOT_PRODUCED) — une plateforme qui finance l'avant-récolte positionne le drapeau pour les autoriser ; une place de marché au comptant le positionne pour les bloquer. Ce seul hook couvre la partie bloquante de la périssabilité ; les courbes de décroissance deviennent une fonction d'aide en lecture seule (view) ou une valorisation off-chain.

AttestationHook — filtrage réglementaire par denrée

Associe un code de denrée à un thème d'attestation requis. Avant un transfert, lit le lot, recherche l'exigence, interroge le registre d'attestations avec isAttested et renvoie ATTESTATION_MISSING dans le cas contraire. « Le café exige une référence de diligence raisonnée EUDR », « les lots étiquetés biologiques exigent un certificat biologique valide », « les bovins exigent un certificat sanitaire » correspondent chacun à un appel à setRequirement.

PerishabilityHook — modèles de décroissance

Configuration de la décroissance par code de denrée, avec dérogation par lot ; modèles NONE, LINEAR (jusqu'à un plancher à l'expiration), EXPONENTIAL (demi-vie) et STEP (baisse à chaque intervalle). Vues valueBps, secondsRemaining, isExpired. Bloque les émissions et les transferts de lots expirés (BATCH_EXPIRED) ainsi que les transferts de lots dont la valeur restante est nulle (PERISH:NO_VALUE) ; ne bloque jamais les destructions ni la mise en application.

SeasonalHook — fenêtres de récolte et de négociation

Fenêtres absolues [start, end] par denrée et par opération (16 au plus par clé). Une denrée/opération sans fenêtre n'est soumise à aucune restriction. Usage typique : OP_MINT limité à la fenêtre de récolte, OP_TRANSFER à la fenêtre de négociation ; SEASON_CLOSED dans les autres cas.

DeliveryHook — garde et règlement

Un expéditeur planifie la livraison de amount unités à un destinataire par l'intermédiaire d'un transporteur ; les unités sont verrouillées (un compteur en O(1), de sorte que check ne parcourt jamais l'historique). Le transporteur et les capteurs consignent l'enlèvement, le transit et les relevés de température (indicateur de dépassement) ; le destinataire confirme la livraison ; les litiges sont portés devant un arbitre. Le règlement est le transfert ERC-1155 lui-même : expéditeur → destinataire, montant exact, avec data = abi.encode(deliveryId) ; check ne l'autorise que lorsque la livraison est DELIVERED, et onExecuted la marque SETTLED et déverrouille. Les unités libres (non verrouillées) continuent de circuler. Les unités verrouillées ne peuvent être ni transférées, ni détruites, ni utilisées comme entrées d'un transform : check renvoie IN_TRANSIT pour toute opération qui ferait passer le solde du détenteur sous le montant verrouillé, à l'exception du transfert de règlement, des émissions et des transferts forcés. Après qu'un transfert forcé a saisi des unités verrouillées, check renvoie toujours un code au lieu d'annuler la transaction.

TraceabilityHook — journal d'événements calqué sur EPCIS

Observe chaque opération sur les jetons et la classe selon les étapes métier CBV (commissioning, shipping, destroying, transforming) et les dispositions (active, in_transit, destroyed, seized) ; RECORDER_ROLE consigne les événements de suivi critique hors jeton (refroidissement, emballage, réception) avec readPoint et bizLocation. Deux modes : émission seule (environ +17 k de gaz par transfert ; un indexeur reconstruit le journal) ou persistance (environ +184 k ; getEvents répond on-chain, avec pagination).

Extensions compagnons

Trois préoccupations n'ont jamais eu besoin d'être des règles de transfert ; ce sont de simples contrats qui lisent ou écrivent des attestations :

ExtensionCe qu'elle fait
CommodityPriceOracledes déclarants soumettent des prix par denrée ; les tours sont numérotés et finalisés par quorum avec la médiane, ou manuellement une fois un minimum atteint ; latestAnswer, isStale
CarbonImmobilizationWrapperenregistre les crédits immobilisés dans un registre (registre, plage de numéros de série, millésime, tonnes, preuve), relie des tonnes à des lots, reflète le retrait et la libération, écrit l'attestation AREX:CARBON:IMMOBILIZED ; n'émet jamais de crédits
ParametricInsurancepool ERC-20 ; police par lot avec un thème déclencheur ; la réclamation est payée lorsque le déclencheur est attesté avant l'expiration

Lots anciens et lots nouveaux

Une règle activée pour un jeton qui détient déjà des lots régit soit tous les lots, soit uniquement les lots créés à partir d'un moment donné. Les ExpiryHook, AttestationHook, SeasonalHook et PerishabilityHook de référence reçoivent ce moment (appliesFrom, 0 pour tous les lots) lors de leur déploiement. Il ne peut plus être modifié ensuite, de sorte que personne ne peut exempter davantage de lots par la suite ; faire un autre choix exige un nouveau déploiement de la règle.

Décrire une règle aux systèmes qui l'utilisent

Une règle ou un module complémentaire peut publier un descripteur arex.module/1 à côté de son code. C'est un document JSON versionné qui nomme les paramètres et les réglages du contrat, les champs du lot qu'il lit, les types de certificats qu'il attend, ses événements et ses codes de rejet, ainsi que la manière dont son état est repris par une nouvelle version. Une plateforme, un portefeuille, un indexeur ou un auditeur le lit pour afficher la règle, la configurer et expliquer un refus sans code écrit spécialement pour cette règle. Depuis la révision 1.1, le descripteur peut aussi indiquer les opérations que la règle gouverne (ops : mint, transfer, burn, forced_transfer, transform). Le champ est informatif : absent, les opérations sont inconnues, et aucun consommateur ne peut s'appuyer sur lui pour appliquer un contrôle, car ce sont les vérifications du hook lui-même qui décident.

Le descripteur est informatif et au statut Proposé : il a été proposé par A-REX Platform, son premier consommateur, et deviendra un format recommandé lorsqu'un second implémenteur indépendant le consommera. Les champs qu'une règle ajoute au document de métadonnées d'un lot se trouveraient sous extensions.<key>, validés par le fragment de schéma du descripteur (ADR-017, Proposé, pour le schéma de métadonnées 1.3.0).

Écrire votre propre hook

L'exemple contracts/examples/SeasonHook.sol reste dans le dépôt. test/standard/ImplementationGuide.test.js l'exerce pour les lecteurs qui ont accès. SeasonalHook est le module de référence pour plusieurs fenêtres par denrée et par opération.

Règles pratiques :

  1. Les symboles OK et OP_FORCED_TRANSFER sont définis dans le texte normatif.
  2. Le comportement de la destruction est défini dans le texte normatif.
  3. Le rappel check est défini dans le texte normatif.
  4. Les noms de code tels que ARexCodes.code("MYNS", "NAME") sont définis dans le texte normatif.
  5. Les valeurs des drapeaux sont définies dans le texte normatif.
  6. Les rappels onExecuted et check sont définis dans le texte normatif.

Enregistrement et ordre

Placez en premier les règles peu coûteuses qui rejettent beaucoup. HOOK_ADMIN_ROLE devrait se trouver derrière un multisig ou un timelock : quiconque contrôle le pipeline contrôle quels transferts sont possibles.

Tests

test/standard/helpers.js déploie la pile de référence. RecordingHook enregistre chaque callback et peut recevoir l'instruction de rejeter avec n'importe quel code ; ReentrantHook tente de ré-entrer dans le jeton depuis onExecuted et prouve le verrou. DomainHooks.test.js et Extensions.test.js couvrent les hooks et les compagnons portés ; Invariants.test.js exécute une séquence aléatoire d'opérations à graine fixée et vérifie la conservation de l'offre, le statut terminal et le fait que canTransfer prédit chaque résultat.

Interopérer avec la conformité ERC-3643

Un module de conformité ERC-3643 expose moduleCheck(from, to, value, compliance). Un hook qui l'appelle pour OP_TRANSFER et fait correspondre false à un code rend réutilisables sur des jetons de lot les restrictions par pays, les plafonds de solde et les verrous temporels écrits pour des jetons de valeurs mobilières. L'inverse — exposer un hook A-REX à un jeton ERC-3643 — est tout aussi mécanique. L'Automated Compliance Engine de Chainlink s'intègre selon le même schéma : son moteur de politiques devient un hook. Ces deux directions sont des conceptions non implémentées. L'adaptateur de référence qui existe va dans l'autre sens et ne fait que lire : il permet aux portefeuilles et aux outils de conformité ERC-3643 de voir chaque lot comme un jeton ERC-3643 grâce à un hook de simple notification qui ne peut ni autoriser ni refuser un transfert (ADR-020, Proposé).