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
| Konstante | Wert | Bedeutung |
|---|---|---|
OP_MINT | 1 | neue Einheiten (createBatch) |
OP_TRANSFER | 2 | vom Inhaber ausgelöster Transfer |
OP_BURN | 3 | Einlösung, Lieferung, Abschreibung |
OP_FORCED_TRANSFER | 4 | Durchsetzung |
OP_TRANSFORM | 5 | Eingaben (to = 0) und Ausgaben (from = 0) von transform |
HOOK_CHECK | 1 | Hook verlangt check |
HOOK_EXECUTED | 2 | Hook verlangt onExecuted |
HOOK_ELIGIBILITY | 4 | Hook beantwortet canSend / canReceive |
STATUS_ACTIVE | 0 | Standardwert |
STATUS_RETIRED | 255 | terminal |
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
ARexInvalidBatchrevertieren, wennquantity == 0,commodity == 0,unit == 0oderexpiresAt != 0 && producedAt != 0 && expiresAt <= producedAtgilt. producedAt == 0MUSS akzeptiert werden (Vorernte).- MUSS die Pipeline mit
OP_MINTausführen; MUSSBatchCreatedund danachTransferSingleemittieren.
setBatchStatus
- MUSS auf Rollen beschränkt sein.
255ist terminal. Stillgelegte Chargen: keine Mints, keine Transfers (ARexBatchRetired), Burns erlaubt.
transform
msg.senderMUSSfromsein oder vonfromfür alle freigegeben sein.- MUSS bei inkonsistenten Längen mit
ARexLengthMismatchrevertieren und bei leeren Eingabe- oder Ausgabelisten revertieren. - MUSS Eingaben verbrennen, Ausgaben minten,
parentsOf(output) = inputIdssetzen und bei den Ausgabenissuer = msg.sendersetzen. - MUSS die Pipeline für jeden Burn und jeden Mint mit
OP_TRANSFORMausführen, mitdata = abi.encode(processRef). - MUSS je Ausgabe
BatchCreated, die ERC-1155-Ereignisse und einBatchTransformedemittieren. - Mengenerhaltung ist NICHT erforderlich. Die Referenz unterscheidet Re-Batching (gleicher Rohstoff und gleiche Einheit,
Σin == Σout; jeder Inhaber) von Verarbeitung (TRANSFORMER_ROLE).
Metadaten
metadataHashMUSS derkeccak256des kanonischen Dokuments unterurisein. Das Dokument SOLLTEstandard/schema/batch-metadata.schema.jsonfolgen.originRefDARF 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:
canTransferMUSS falsch sein, wenn pausiert ist, wenn die Charge nicht existiert oder stillgelegt ist, wenn die Berechtigung fehlschlägt, wennamount > balance − frozengilt oder wenn ein CHECK-HookOP_TRANSFERablehnt.- Öffentliche Transfers DÜRFEN NICHT erfolgreich sein, wo
canTransferfalsch ist. forcedTransferMUSS auf Rollen beschränkt sein, MUSS die Berechtigung umgehen, MUSS den eingefrorenen Betrag so senken, dass er den verbleibenden Saldo nie übersteigt (unter Emission vonFrozen), MUSS Hooks mitOP_FORCED_TRANSFERausführen, MUSSForcedTransferemittieren und SOLLTE im pausierten Zustand funktionieren.canSend/canReceivesind die Konjunktion allerHOOK_ELIGIBILITY-Hooks.- Ein Token SOLLTE
transferCheck(from, to, id, amount) → bytes32bereitstellen.
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
| Eigenschaft | Wert |
|---|---|
| Basis | OpenZeppelin 5.3.0 upgradefähig: ERC1155, ERC1155Supply, AccessControl, Pausable, UUPS |
| Speicher | ERC-7201-Namensraum arex.storage.token, Slot 0x8ed599b6be05dec05297c009a1279a71df771ad996fe924d0b5a47326ff60900 |
| Compiler | solc 0.8.22, viaIR, EVM paris; optimizer.runs = 1 für ARexToken (24,136 Byte im Deployment), 200 an anderer Stelle |
| Rollen | ISSUER, TRANSFORMER, STATUS, ENFORCER, HOOK_ADMIN, PAUSER, UPGRADER, DEFAULT_ADMIN; ATTEST_ADMIN bei ARexAttestations |
| Reentrancy | Speichersperre um die gesamte _update-Sequenz; durch den Test ReentrantHook geprüft |
| Datenweitergabe | safeTransferFrom / safeBatchTransferFrom / createBatch legen data für Hooks zwischen; transform übergibt abi.encode(processRef) |
| Metadaten | uri(id) liefert die URI der Charge, sofern gesetzt, sonst die Basis-URI |
| Tests | 46 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.