Skip to main contentA-REX Core

Whitepaper · 05

Technische Spezifikation

Normative Zusammenfassung von A-REX Core: Konformität, Schnittstellen, Operationssemantik, die Pflichten von Token und Hook, Attestierungen, Speicher- und Upgrade-Regeln. Maßgeblich ist der Text in standard/AREX-CORE.md; dieses Kapitel gibt ihn für die Leser des Whitepapers wieder.

Konformität

Ein konformer Token MUSS ERC-1155, ERC-165, IERC7943MultiToken (0x41c4fbad), IARexBatch (0xe22639e0) implementieren und seine Pipeline über IARexHookManager.getHooks() (0x964f4861) bereitstellen. Er SOLLTE IARexAttest (0xad603a6c) direkt oder über einen Begleit-Contract implementieren. Ein konformer Hook MUSS IARexHook (0x332afcbb) und ERC-165 implementieren. Alle IDs werden durch npm run interface-ids reproduziert.

Feste Konstanten

KonstanteWertBedeutung
OP_MINT1neue Einheiten (createBatch)
OP_TRANSFER2vom Inhaber ausgelöster Transfer
OP_BURN3Einlösung, Lieferung, Abschreibung
OP_FORCED_TRANSFER4Durchsetzung
OP_TRANSFORM5Eingaben (to = 0) und Ausgaben (from = 0) von transform
HOOK_CHECK1Hook verlangt check
HOOK_EXECUTED2Hook verlangt onExecuted
HOOK_ELIGIBILITY4Hook beantwortet canSend / canReceive
STATUS_ACTIVE0Standardwert
STATUS_RETIRED255terminal

Alles andere – Rohstoffcodes, Einheitencodes, Status 1..254, Ablehnungscodes, Attestierungsthemen – ist offen. Empfohlene Konventionen stehen in ARexCodes.

Schicht 1 – IARexBatch

interface IARexBatch is IERC165 {
    struct BatchInit { bytes32 commodity; bytes32 unit; uint64 producedAt; uint64 expiresAt; bytes32 originRef; bytes32 metadataHash; string uri; }
    struct Batch { address issuer; uint64 createdAt; uint8 status; bytes32 commodity; bytes32 unit; uint64 producedAt; uint64 expiresAt; bytes32 originRef; bytes32 metadataHash; string uri; }


    event BatchCreated(uint256 indexed id, address indexed issuer, bytes32 indexed commodity, bytes32 unit, uint256 quantity, address to);
    event BatchStatusChanged(uint256 indexed id, uint8 previousStatus, uint8 newStatus, bytes32 reason);
    event BatchMetadataUpdated(uint256 indexed id, bytes32 metadataHash, string uri);
    event BatchTransformed(address indexed operator, uint256[] inputIds, uint256[] inputAmounts, uint256[] outputIds, uint256[] outputAmounts, bytes32 processRef);


    error ARexBatchNotFound(uint256 id);
    error ARexBatchRetired(uint256 id);
    error ARexInvalidBatch(bytes32 reason);
    error ARexInvalidStatusTransition(uint256 id, uint8 from, uint8 to);
    error ARexLengthMismatch();


    function createBatch(BatchInit calldata init, uint256 quantity, address to, bytes calldata data) external returns (uint256 id);
    function batchOf(uint256 id) external view returns (Batch memory);
    function batchExists(uint256 id) external view returns (bool);
    function totalSupply(uint256 id) external view returns (uint256);
    function setBatchStatus(uint256 id, uint8 status, bytes32 reason) external;
    function setBatchMetadata(uint256 id, bytes32 metadataHash, string calldata uri) external;
    function transform(address from, address to, uint256[] calldata inputIds, uint256[] calldata inputAmounts, BatchInit[] calldata outputs, uint256[] calldata outputAmounts, bytes32 processRef) external returns (uint256[] memory outputIds);
    function parentsOf(uint256 id) external view returns (uint256[] memory);
}

Semantik

createBatch

  • MUSS auf autorisierte Konten beschränkt sein; die Autorisierung DARF NICHT aus dem Token-Saldo abgeleitet werden.
  • MUSS mit ARexInvalidBatch revertieren, wenn quantity == 0, commodity == 0, unit == 0 oder expiresAt != 0 && producedAt != 0 && expiresAt <= producedAt gilt.
  • producedAt == 0 MUSS akzeptiert werden (Vorernte).
  • MUSS die Pipeline mit OP_MINT ausführen; MUSS BatchCreated und danach TransferSingle emittieren.

setBatchStatus

  • MUSS auf Rollen beschränkt sein. 255 ist terminal. Stillgelegte Chargen: keine Mints, keine Transfers (ARexBatchRetired), Burns erlaubt.

transform

  • msg.sender MUSS from sein oder von from für alle freigegeben sein.
  • MUSS bei inkonsistenten Längen mit ARexLengthMismatch revertieren und bei leeren Eingabe- oder Ausgabelisten revertieren.
  • MUSS Eingaben verbrennen, Ausgaben minten, parentsOf(output) = inputIds setzen und bei den Ausgaben issuer = msg.sender setzen.
  • MUSS die Pipeline für jeden Burn und jeden Mint mit OP_TRANSFORM ausführen, mit data = abi.encode(processRef).
  • MUSS je Ausgabe BatchCreated, die ERC-1155-Ereignisse und ein BatchTransformed emittieren.
  • Mengenerhaltung ist NICHT erforderlich. Die Referenz unterscheidet Re-Batching (gleicher Rohstoff und gleiche Einheit, Σin == Σout; jeder Inhaber) von Verarbeitung (TRANSFORMER_ROLE).

Metadaten

  • metadataHash MUSS der keccak256 des kanonischen Dokuments unter uri sein. Das Dokument SOLLTE standard/schema/batch-metadata.schema.json folgen.
  • originRef DARF KEINE Rohkoordinaten oder Kennungen natürlicher Personen enthalten.

Schicht 0 – IERC7943MultiToken

Wörtlich aus ERC-7943 (forcedTransfer, setFrozenTokens, canSend, canReceive, getFrozenTokens, canTransfer; Ereignisse ForcedTransfer, Frozen; Fehler ERC7943CannotSend, ERC7943CannotReceive, ERC7943CannotTransfer, ERC7943InsufficientUnfrozenBalance). A-REX ergänzt diese Klarstellungen:

  • canTransfer MUSS falsch sein, wenn pausiert ist, wenn die Charge nicht existiert oder stillgelegt ist, wenn die Berechtigung fehlschlägt, wenn amount > balance − frozen gilt oder wenn ein CHECK-Hook OP_TRANSFER ablehnt.
  • Öffentliche Transfers DÜRFEN NICHT erfolgreich sein, wo canTransfer falsch ist.
  • forcedTransfer MUSS auf Rollen beschränkt sein, MUSS die Berechtigung umgehen, MUSS den eingefrorenen Betrag so senken, dass er den verbleibenden Saldo nie übersteigt (unter Emission von Frozen), MUSS Hooks mit OP_FORCED_TRANSFER ausführen, MUSS ForcedTransfer emittieren und SOLLTE im pausierten Zustand funktionieren.
  • canSend / canReceive sind die Konjunktion aller HOOK_ELIGIBILITY-Hooks.
  • Ein Token SOLLTE transferCheck(from, to, id, amount) → bytes32 bereitstellen.

Schicht 2 – Hooks

interface IARexHook is IERC165 {
    error ARexHookRejected(address hook, bytes32 code);
    function hookFlags() external view returns (uint8);
    function check(uint8 op, address operator, address from, address to, uint256 id, uint256 amount, bytes calldata data) external view returns (bytes32 code);
    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);
}
interface IARexHookManager {
    event HookRegistered(address indexed hook, uint8 flags, uint256 position);
    event HookRemoved(address indexed hook);
    error ARexHookAlreadyRegistered(address hook);
    error ARexHookNotRegistered(address hook);
    error ARexInvalidHook(address hook);
    function getHooks() external view returns (address[] memory);
    function hookFlagsOf(address hook) external view returns (uint8);
    function registerHook(address hook) external;
    function removeHook(address hook) external;
}

Pflichten des Tokens: geordnete Liste; supportsInterface(0x332afcbb) prüfen und Flags ungleich null bei der Registrierung zwischenspeichern; check je ID und je CHECK-Hook aufrufen und beim ersten Code ungleich null vor jeder Saldenänderung mit ARexHookRejected(hook, code) revertieren; onExecuted je ID und je EXECUTED-Hook nach den Salden aufrufen; Reentrancy während der Sequenz verhindern; data weiterreichen; Liste und Flags öffentlich halten.

Pflichten des Hooks: konstante Flags; check ist eine View, die Codes zurückgibt und aus Richtliniengründen nie revertiert; der aufrufende Token ist msg.sender; für OP_FORCED_TRANSFER 0 zurückgeben, sofern die Regel nicht die Durchsetzung betrifft; onExecuted ruft nie saldenändernde Funktionen auf.

Schicht 3 – IARexAttest

interface IARexAttest is IERC165 {
    struct Attestation { address issuer; bytes32 ref; uint64 issuedAt; uint64 expiresAt; bool revoked; }
    event Attested(uint256 indexed id, bytes32 indexed topic, address indexed issuer, bytes32 ref, uint64 expiresAt);
    event AttestationRevoked(uint256 indexed id, bytes32 indexed topic, address indexed issuer, bytes32 reason);
    event TrustedIssuerUpdated(bytes32 indexed topic, address indexed issuer, bool trusted);
    error ARexUntrustedIssuer(bytes32 topic, address issuer);
    error ARexAttestationNotFound(uint256 id, bytes32 topic);
    function attest(uint256 id, bytes32 topic, bytes32 ref, uint64 expiresAt) external;
    function revokeAttestation(uint256 id, bytes32 topic, bytes32 reason) external;
    function getAttestation(uint256 id, bytes32 topic) external view returns (Attestation memory);
    function isAttested(uint256 id, bytes32 topic) external view returns (bool);
    function isTrustedIssuer(bytes32 topic, address issuer) external view returns (bool);
}

attest erfordert einen für das Thema vertrauenswürdigen Aussteller und eine existierende Charge. isAttested ist falsch, wenn die Attestierung fehlt, widerrufen oder abgelaufen ist oder der Aussteller nicht mehr vertrauenswürdig ist. Aussteller widerrufen ihre eigenen Attestierungen; ein Administrator KANN jede widerrufen.

Ablehnungscodes (empfohlen)

keccak256("AREX:" + NAME) für PAUSED, FROZEN, SENDER_INELIGIBLE, RECEIVER_INELIGIBLE, SANCTIONED, BATCH_NOT_FOUND, BATCH_RETIRED, BATCH_INACTIVE, BATCH_EXPIRED, BATCH_NOT_PRODUCED, LIMIT_EXCEEDED, DOCUMENT_MISSING, SEASON_CLOSED, IN_TRANSIT, ATTESTATION_MISSING. Eigene Codes: keccak256("NAMESPACE:NAME").

Fakten zur Referenzimplementierung

EigenschaftWert
BasisOpenZeppelin 5.3.0 upgradefähig: ERC1155, ERC1155Supply, AccessControl, Pausable, UUPS
SpeicherERC-7201-Namensraum arex.storage.token, Slot 0x8ed599b6be05dec05297c009a1279a71df771ad996fe924d0b5a47326ff60900
Compilersolc 0.8.22, viaIR, EVM paris; optimizer.runs = 1 für ARexToken (24,136 Byte im Deployment), 200 an anderer Stelle
RollenISSUER, TRANSFORMER, STATUS, ENFORCER, HOOK_ADMIN, PAUSER, UPGRADER, DEFAULT_ADMIN; ATTEST_ADMIN bei ARexAttestations
ReentrancySpeichersperre um die gesamte _update-Sequenz; durch den Test ReentrantHook geprüft
DatenweitergabesafeTransferFrom / safeBatchTransferFrom / createBatch legen data für Hooks zwischen; transform übergibt abi.encode(processRef)
Metadatenuri(id) liefert die URI der Charge, sofern gesetzt, sonst die Basis-URI
Tests46 in test/standard: Lebenszyklus, Abstammung, Einfrieren und erzwungener Transfer, Hook-Reihenfolge, Ablehnung, Daten und Reentrancy, Attestierungen, Schnittstellen-IDs, Upgrade-Autorisierung, Domänen-Hooks, Erweiterungen, randomisierte Invarianten sowie der durchgängig ausgeführte Implementierungsleitfaden

Versionierung des Standards

Jede Änderung an einer Signatur, einem Ereignis oder einer MUSS-Anforderung ist eine Hauptversion mit einem Migrationshinweis. Additive Empfehlungen (Codes, Schemafelder) sind Nebenversionen. Änderungen werden nur akzeptiert, wenn zwei unabhängige Implementierer die Übernahme bestätigen. Die Schnittstellen werden erst dann als ERC eingereicht, wenn ein Deployment im Produktivbetrieb sechs Monate gelaufen ist, drei unabhängige Hook-Implementierungen existieren und ein Audit durch Dritte veröffentlicht ist.