Whitepaper · 07
Hooks und Referenzmodule
Wie einem konformen Token Funktionalität hinzugefügt wird, ohne den Standard anzutasten: die Hook-Schnittstelle, die Referenz-Hooks, die Begleiterweiterungen und wie eigene Hooks geschrieben, getestet und registriert werden.
Module sind Demonstrationen, nicht der Standard
Das Verhältnis zwischen A-REX Core und seinen Modulen entspricht dem zwischen ERC-3643 und den Compliance-Modulen von T-REX: Die Schnittstelle, über die Module angebunden werden, ist standardisiert; die Module sind es nicht. Ein konformer Token ohne Hooks ist ein gültiger A-REX-Token. Ein von beliebiger Seite geschriebener Hook ist mit jedem konformen Token kompatibel. Dadurch wächst das Ökosystem, ohne dass sich der Standard ändert.
graph LR
T["ARexToken<br/>konformer Token"] -->|"check / onExecuted / canSend / canReceive"| H1[EligibilityHook]
T --> H2[ExpiryHook]
T --> H3[AttestationHook]
T --> H4["Eigener Hook"]
H3 --> A[ARexAttestations]Die Hook-Schnittstelle
interface IARexHook is IERC165 {
function hookFlags() external view returns (uint8); // CHECK=1 · EXECUTED=2 · ELIGIBILITY=4
function check(uint8 op, address operator, address from, address to, uint256 id, uint256 amount, bytes calldata data)
external view returns (bytes32 code); // 0 = allowed
function onExecuted(uint8 op, address operator, address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function canSend(address account) external view returns (bool);
function canReceive(address account) external view returns (bool);
}
| Callback | Wann | Muss |
|---|---|---|
check | bevor sich Salden ändern, je betroffener ID, Hooks in Registrierungsreihenfolge, Abbruch beim ersten Wert ungleich null | eine View sein; aus Richtliniengründen nie revertieren |
onExecuted | nachdem sich Salden geändert haben, je ID, der Reihe nach | keine saldenändernden Funktionen des Tokens aufrufen (der Token ist gesperrt) |
canSend / canReceive | bei jedem Transfer und für die ERC-7943-Views des Tokens | Views sein |
op ist einer von OP_MINT (1), OP_TRANSFER (2), OP_BURN (3), OP_FORCED_TRANSFER (4), OP_TRANSFORM (5). Bei einer Transformation kommen Eingaben mit to == 0 und Ausgaben mit from == 0 an. data sind die ERC-1155-Transferdaten, bei Transformationen abi.encode(processRef), bei erzwungenen Transfers leer.
Der aufrufende Token ist msg.sender. Ein Hook, der die Charge benötigt, liest IARexBatch(msg.sender).batchOf(id).
Referenz-Hooks
EligibilityHook – Zulassen, Sperren, Sanktionieren
Demonstriert HOOK_ELIGIBILITY. Führt eine optionale Zulassungsliste, eine Sperrliste und eine Sanktionsliste; canSend/canReceive geben deren Konjunktion zurück; check liefert SANCTIONED, SENDER_INELIGIBLE oder RECEIVER_INELIGIBLE, damit Benutzeroberflächen die Ablehnung erklären können. Erzwungene Transfers sind stets erlaubt: Güter von einem sanktionierten Konto zu beschlagnahmen, ist der Zweck der Durchsetzung. Im Produktivbetrieb werden die Listen von einem Identitätsregister (ONCHAINID, EAS, Verifiable Credentials) oder einem Sanktions-Oracle gespeist; dem Hook ist gleichgültig, welches.
ExpiryHook – Verderblichkeit ohne Modul
Zustandslos. Liest expiresAt und producedAt aus der Charge. Blockiert Mints und Transfers abgelaufener Chargen (BATCH_EXPIRED), nicht aber Burns (Abschreibung) oder erzwungene Transfers (Durchsetzung). Blockiert optional Transfers von Chargen, die noch nicht produziert sind (BATCH_NOT_PRODUCED) – eine Plattform, die Vorernte finanziert, setzt das Flag so, dass sie erlaubt sind; ein Spot-Marktplatz setzt es so, dass sie blockiert werden. Dieser eine Hook deckt den blockierenden Teil der Verderblichkeit ab; Verfallskurven werden zu einem View-Helfer oder einer Off-Chain-Bewertung.
AttestationHook – regulatorische Freigabesteuerung je Rohstoff
Ordnet einem Rohstoffcode ein erforderliches Attestierungsthema zu. Vor einem Transfer liest er die Charge, schlägt die Anforderung nach, fragt das Attestierungsregister mit isAttested(id, topic) und liefert andernfalls ATTESTATION_MISSING. „Kaffee benötigt eine EUDR-Sorgfaltsreferenz“, „als Bio gekennzeichnete Chargen benötigen ein gültiges Bio-Zertifikat“, „Rinder benötigen ein Tiergesundheitszeugnis“ sind jeweils ein Aufruf von setRequirement.
PerishabilityHook – Verfallsmodelle
Verfallskonfiguration je Rohstoffcode mit Überschreibung je Charge; Modelle NONE, LINEAR (bis zu einer Untergrenze bei Ablauf), EXPONENTIAL (Halbwertszeit) und STEP (Abfall in jedem Intervall). Views valueBps, secondsRemaining, isExpired. Blockiert Mints und Transfers abgelaufener Chargen (BATCH_EXPIRED) sowie Transfers von Chargen, deren Restwert null beträgt (PERISH:NO_VALUE); blockiert nie Burns oder Durchsetzung.
SeasonalHook – Ernte- und Handelsfenster
Absolute Fenster [start, end] je Rohstoff und Operation (höchstens 16 je Schlüssel). Eine Kombination aus Rohstoff und Operation ohne Fenster ist unbeschränkt. Typische Verwendung: OP_MINT auf das Erntefenster beschränkt, OP_TRANSFER auf das Handelsfenster; andernfalls SEASON_CLOSED.
DeliveryHook – Verwahrung und Abwicklung
Ein Absender plant die Lieferung von amount Einheiten über einen Frachtführer an einen Empfänger; die Einheiten werden gesperrt (ein O(1)-Zähler, sodass check nie über die Historie iteriert). Frachtführer und Sensoren erfassen Abholung, Transport und Temperaturmesswerte (Verstoß-Flag); der Empfänger bestätigt die Lieferung; Streitfälle gehen an einen Schlichter. Die Abwicklung ist der ERC-1155-Transfer selbst: Absender → Empfänger, exakter Betrag, mit data = abi.encode(deliveryId); check erlaubt ihn nur, wenn die Lieferung DELIVERED ist, und onExecuted markiert sie als SETTLED und hebt die Sperre auf. Freie (nicht gesperrte) Einheiten bewegen sich weiter.
TraceabilityHook – EPCIS-förmiges Ereignisprotokoll
Beobachtet jede Token-Operation und klassifiziert sie mit CBV-Geschäftsschritten (commissioning, shipping, destroying, transforming) und Dispositionen (active, in_transit, destroyed, seized); RECORDER_ROLE erfasst kritische Verfolgungsereignisse, die keine Token-Operationen sind (Kühlung, Verpackung, Wareneingang), mit readPoint und bizLocation. Zwei Modi: nur emittieren (etwa +17 k Gas pro Transfer; ein Indexer baut das Protokoll neu auf) oder persistieren (etwa +184 k; getEvents antwortet On-Chain, paginiert).
Begleiterweiterungen
Drei Belange mussten nie Transferregeln sein; es sind einfache Contracts, die Attestierungen lesen oder schreiben:
| Erweiterung | Was sie tut |
|---|---|
CommodityPriceOracle | Melder übermitteln Preise je Rohstoff; Runden sind nummeriert und werden per Quorum mit dem Median finalisiert oder manuell, sobald ein Minimum erreicht ist; latestAnswer, isStale |
CarbonImmobilizationWrapper | erfasst in einem Register immobilisierte Gutschriften (Register, Seriennummernbereich, Vintage, Tonnen, Nachweis), verknüpft Tonnen mit Chargen, bildet Stilllegung und Freigabe nach, schreibt die Attestierung AREX:CARBON:IMMOBILIZED; mintet niemals Gutschriften |
ParametricInsurance | ERC-20-Pool; Police je Charge mit einem Auslöserthema; die Auszahlung erfolgt, wenn der Auslöser vor Ablauf attestiert ist |
Einen eigenen Hook schreiben
Der folgende Hook ist contracts/examples/SeasonHook.sol; er wird in der CI kompiliert, und test/standard/ImplementationGuide.test.js führt ihn auf einem Token aus. Für den Produktivbetrieb ist SeasonalHook zu verwenden, der mehrere Fenster je Rohstoff und je Operation unterstützt.
contract SeasonHook is ARexHookBase, Ownable {
mapping(bytes32 => uint64[2]) public window; // commodity → [open, close]
constructor(address owner_) Ownable(owner_) {}
function setWindow(bytes32 commodity, uint64 open, uint64 close) external onlyOwner {
window[commodity] = [open, close];
}
function hookFlags() public pure override returns (uint8) {
return ARexCodes.HOOK_CHECK;
}
function check(uint8 op, address, address, address, uint256 id, uint256, bytes calldata)
external
view
override
returns (bytes32)
{
if (op != ARexCodes.OP_TRANSFER) return ARexCodes.OK; // never block mints, burns or enforcement
IARexBatch.Batch memory b = IARexBatch(msg.sender).batchOf(id);
uint64[2] memory w = window[b.commodity];
if (w[1] == 0) return ARexCodes.OK; // no window configured
return (block.timestamp >= w[0] && block.timestamp <= w[1]) ? ARexCodes.OK : ARexCodes.SEASON_CLOSED;
}
}
Faustregeln:
- Für
OP_FORCED_TRANSFEROKzurückgeben, es sei denn, der Hook regelt die Durchsetzung selbst. - Burns nur blockieren, wenn dies beabsichtigt ist: Burns sind Lieferungen und Abschreibungen.
checkgünstig und begrenzt halten; die Funktion läuft bei jedem Transfer jeder ID. Niemals über die Historie iterieren.- Codes mit einem Namensraum versehen:
ARexCodes.code("MYNS", "NAME"). - Flags konstant halten; der Token speichert sie zwischen.
- Zustandsänderungen in
onExecutedvornehmen, niemals incheck.
Registrieren und Reihenfolge
await token.registerHook(sanctionsHook); // evaluated first
await token.registerHook(expiryHook);
await token.registerHook(attestationHook); // evaluated last
await token.getHooks(); // ordered list
await token.removeHook(expiryHook); // order of the rest preserved
Günstige Regeln mit hoher Ablehnungsquote gehören an den Anfang. HOOK_ADMIN_ROLE sollte hinter einem Multisig oder Timelock liegen: Wer die Pipeline kontrolliert, kontrolliert, welche Transfers möglich sind.
Tests
test/standard/helpers.js rollt den Referenz-Stack aus. RecordingHook zeichnet jeden Callback auf und kann angewiesen werden, mit einem beliebigen Code abzulehnen; ReentrantHook versucht, den Token aus onExecuted erneut zu betreten, und belegt damit die Sperre. DomainHooks.test.js und Extensions.test.js decken die portierten Hooks und Begleiter ab; Invariants.test.js führt eine mit festem Seed erzeugte zufällige Operationsfolge aus und prüft Mengenerhaltung, terminalen Status und dass canTransfer jedes Ergebnis vorhersagt.
Zusammenspiel mit ERC-3643-Compliance
Ein ERC-3643-Compliance-Modul stellt moduleCheck(from, to, value, compliance) bereit. Ein Hook, der es für OP_TRANSFER aufruft und false auf einen Code abbildet, macht für Security-Token geschriebene Länderbeschränkungen, Saldenobergrenzen und Zeitsperren für Chargen-Token wiederverwendbar. Der umgekehrte Weg – einen A-REX-Hook einem ERC-3643-Token zugänglich zu machen – ist ebenso mechanisch. Die Automated Compliance Engine von Chainlink lässt sich nach demselben Muster integrieren: Deren Policy-Engine wird zu einem Hook.