Skip to main contentA-REX Core

श्वेतपत्र · 07

हुक और संदर्भ मॉड्यूल

मानक को छुए बिना किसी अनुरूप टोकन में कार्यक्षमता कैसे जोड़ी जाती है: हुक कॉन्ट्रैक्ट, संदर्भ हुक, सहायक एक्सटेंशन, और अपना हुक कैसे लिखें, परखें और पंजीकृत करें।

मॉड्यूल प्रदर्शन हैं, मानक नहीं

A-REX Core और उसके मॉड्यूल के बीच वही संबंध है जो ERC-3643 और T-REX के अनुपालन मॉड्यूल के बीच है: जिस इंटरफ़ेस से मॉड्यूल जुड़ते हैं वह मानकीकृत है; मॉड्यूल नहीं। शून्य हुक वाला अनुरूप टोकन एक वैध A-REX टोकन है। किसी के भी द्वारा लिखा गया हुक प्रत्येक अनुरूप टोकन के साथ संगत है। यही इकोसिस्टम को मानक बदले बिना बढ़ने देता है।

graph LR
    T[ARexToken<br/>अनुरूप टोकन] -->|"check / onExecuted / canSend / canReceive"| H1[EligibilityHook]
    T --> H2[ExpiryHook]
    T --> H3[AttestationHook]
    T --> H4["आपका हुक"]
    H3 --> A[ARexAttestations]

हुक कॉन्ट्रैक्ट

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);
}
कॉलबैककबअनिवार्य अपेक्षा
checkबैलेंस बदलने से पहले, प्रत्येक प्रभावित आईडी के लिए, हुक पंजीकरण क्रम में, पहले गैर-शून्य पर रुकेंव्यू हो; नीति के कारण कभी रिवर्ट न करे
onExecutedबैलेंस बदलने के बाद, प्रत्येक आईडी के लिए, क्रम सेटोकन के बैलेंस बदलने वाले फ़ंक्शन कॉल न करे (टोकन लॉक्ड है)
canSend / canReceiveप्रत्येक ट्रांसफ़र पर और टोकन के ERC-7943 व्यू के लिएव्यू हों

op इनमें से एक है: OP_MINT (1), OP_TRANSFER (2), OP_BURN (3), OP_FORCED_TRANSFER (4), OP_TRANSFORM (5)। ट्रांसफ़ॉर्मेशन में इनपुट to == 0 के साथ और आउटपुट from == 0 के साथ आते हैं। data ERC-1155 ट्रांसफ़र डेटा है; ट्रांसफ़ॉर्म के लिए abi.encode(processRef), बलपूर्वक ट्रांसफ़र के लिए खाली।

कॉल करने वाला टोकन msg.sender है। जिस हुक को बैच की आवश्यकता हो वह IARexBatch(msg.sender).batchOf(id) पढ़ता है।

संदर्भ हुक

EligibilityHook — अनुमति, ब्लॉक, प्रतिबंध

HOOK_ELIGIBILITY का प्रदर्शन करता है। एक वैकल्पिक अनुमति सूची, एक ब्लॉक सूची और एक प्रतिबंध सूची रखता है; canSend/canReceive उनका संयोजन लौटाते हैं; check SANCTIONED, SENDER_INELIGIBLE या RECEIVER_INELIGIBLE लौटाता है ताकि इंटरफ़ेस इनकार का कारण समझा सकें। बलपूर्वक ट्रांसफ़र की हमेशा अनुमति है: किसी प्रतिबंधित खाते से माल ज़ब्त करना ही प्रवर्तन का उद्देश्य है। प्रोडक्शन में सूचियाँ किसी पहचान रजिस्ट्री (ONCHAINID, EAS, वेरिफ़ायबल क्रेडेंशियल) या प्रतिबंध ऑरेकल से भरी जाती हैं; हुक को इससे फ़र्क नहीं पड़ता कि कौन-सा।

ExpiryHook — मॉड्यूल के बिना नाशवानता

स्टेटलेस। बैच से expiresAt और producedAt पढ़ता है। समाप्त हो चुके बैच के मिंट और ट्रांसफ़र को ब्लॉक करता है (BATCH_EXPIRED), पर बर्न (बट्टे खाते) या बलपूर्वक ट्रांसफ़र (प्रवर्तन) को नहीं। वैकल्पिक रूप से उन बैच के ट्रांसफ़र ब्लॉक करता है जो अभी उत्पादित नहीं हुए (BATCH_NOT_PRODUCED) — कटाई-पूर्व वित्तपोषण करने वाला प्लेटफ़ॉर्म उन्हें अनुमति देने के लिए फ़्लैग सेट करता है; स्पॉट मार्केटप्लेस उन्हें ब्लॉक करने के लिए। यह एक हुक नाशवानता का ब्लॉक करने वाला भाग कवर करता है; क्षय वक्र एक व्यू हेल्पर या ऑफ़-चेन मूल्यांकन बन जाते हैं।

AttestationHook — प्रति कमोडिटी विनियामक गेटिंग

कमोडिटी कोड को आवश्यक एटेस्टेशन विषय से मैप करता है। ट्रांसफ़र से पहले बैच पढ़ता है, आवश्यकता खोजता है, एटेस्टेशन रजिस्ट्री से isAttested(id, topic) पूछता है और अन्यथा ATTESTATION_MISSING लौटाता है। "कॉफ़ी को EUDR ड्यू-डिलिजेंस संदर्भ चाहिए", "जैविक-लेबल वाले बैच को वैध जैविक प्रमाणपत्र चाहिए", "मवेशी को स्वास्थ्य प्रमाणपत्र चाहिए" में से प्रत्येक setRequirement की एक कॉल है।

PerishabilityHook — क्षय मॉडल

प्रति कमोडिटी कोड क्षय कॉन्फ़िगरेशन, प्रति बैच ओवरराइड के साथ; मॉडल NONE, LINEAR (समाप्ति पर एक न्यूनतम स्तर तक), EXPONENTIAL (अर्ध-आयु) और STEP (प्रत्येक अंतराल पर गिरावट)। व्यू valueBps, secondsRemaining, isExpired। समाप्त बैच के मिंट और ट्रांसफ़र (BATCH_EXPIRED) तथा उन बैच के ट्रांसफ़र को ब्लॉक करता है जिनका शेष मूल्य शून्य है (PERISH:NO_VALUE); बर्न या प्रवर्तन को कभी ब्लॉक नहीं करता।

SeasonalHook — कटाई और व्यापार विंडो

प्रति कमोडिटी और ऑपरेशन निरपेक्ष [start, end] विंडो (प्रति कुंजी अधिकतम 16)। बिना विंडो वाली कमोडिटी/ऑपरेशन निर्बंध-रहित है। सामान्य उपयोग: OP_MINT कटाई विंडो तक सीमित, OP_TRANSFER व्यापार विंडो तक; अन्यथा SEASON_CLOSED।

DeliveryHook — अभिरक्षा और निपटान

प्रेषक किसी वाहक के माध्यम से किसी प्राप्तकर्ता को amount इकाइयों की डिलीवरी शेड्यूल करता है; इकाइयाँ लॉक हो जाती हैं (एक O(1) काउंटर, इसलिए check इतिहास पर कभी इटरेट नहीं करता)। वाहक और सेंसर पिकअप, ट्रांज़िट और तापमान रीडिंग (उल्लंघन फ़्लैग) दर्ज करते हैं; प्राप्तकर्ता डिलीवरी की पुष्टि करता है; विवाद मध्यस्थ के पास जाते हैं। निपटान स्वयं ERC-1155 ट्रांसफ़र है: प्रेषक → प्राप्तकर्ता, सटीक मात्रा, data = abi.encode(deliveryId) के साथ; check इसे केवल तब अनुमति देता है जब डिलीवरी DELIVERED हो और onExecuted उसे SETTLED चिह्नित करके अनलॉक कर देता है। मुक्त (अनलॉक्ड) इकाइयाँ चलती रहती हैं।

TraceabilityHook — EPCIS-आकार का इवेंट लॉग

प्रत्येक टोकन ऑपरेशन का अवलोकन करता है और उसे CBV बिज़नेस स्टेप (commissioning, shipping, destroying, transforming) तथा डिस्पोज़िशन (active, in_transit, destroyed, seized) से वर्गीकृत करता है; RECORDER_ROLE टोकन से असंबद्ध क्रिटिकल ट्रैकिंग इवेंट (कूलिंग, पैकिंग, प्राप्ति) readPoint और bizLocation के साथ दर्ज करती है। दो मोड: केवल-इमिट (प्रति ट्रांसफ़र लगभग +17 k गैस; इंडेक्सर लॉग फिर से बनाता है) या पर्सिस्ट (लगभग +184 k; getEvents ऑन-चेन उत्तर देता है, पेज-वार)।

सहायक एक्सटेंशन

तीन चिंताओं को कभी ट्रांसफ़र नियम होने की आवश्यकता नहीं थी; वे सामान्य कॉन्ट्रैक्ट हैं जो एटेस्टेशन पढ़ते या लिखते हैं:

एक्सटेंशनयह क्या करता है
CommodityPriceOracleरिपोर्टर प्रति कमोडिटी मूल्य जमा करते हैं; राउंड क्रमांकित होते हैं, कोरम द्वारा माध्यिका के साथ अंतिम किए जाते हैं, या न्यूनतम संख्या पूरी होने पर मैन्युअल रूप से; latestAnswer, isStale
CarbonImmobilizationWrapperकिसी रजिस्ट्री में इम्मोबिलाइज़्ड क्रेडिट दर्ज करता है (रजिस्ट्री, सीरियल रेंज, विंटेज, टन, प्रमाण), टन को बैच से जोड़ता है, रिटायरमेंट और रिलीज़ को प्रतिबिंबित करता है, एटेस्टेशन AREX:CARBON:IMMOBILIZED लिखता है; क्रेडिट कभी मिंट नहीं करता
ParametricInsuranceERC-20 पूल; प्रति बैच एक ट्रिगर विषय वाली पॉलिसी; दावा तब भुगतान करता है जब ट्रिगर समाप्ति से पहले एटेस्ट हो

अपना हुक लिखना

नीचे दिया गया हुक contracts/examples/SeasonHook.sol है; यह CI में कंपाइल होता है और test/standard/ImplementationGuide.test.js उसे एक टोकन पर चलाकर परखता है। प्रोडक्शन के लिए SeasonalHook का उपयोग करें, जो प्रति कमोडिटी और प्रति ऑपरेशन कई विंडो का समर्थन करता है।

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;
    }
}

व्यावहारिक नियम:

  1. OP_FORCED_TRANSFER के लिए OK लौटाएँ, जब तक आपका हुक स्वयं प्रवर्तन को शासित न करता हो।
  2. बर्न को तब तक ब्लॉक न करें जब तक आपका ऐसा आशय न हो: बर्न डिलीवरी और बट्टे खाते हैं।
  3. check को सस्ता और सीमाबद्ध रखें; यह प्रत्येक आईडी के प्रत्येक ट्रांसफ़र पर चलता है। इतिहास पर कभी इटरेट न करें।
  4. अपने कोड को नेमस्पेस दें: ARexCodes.code("MYNS", "NAME")।
  5. फ़्लैग स्थिर रखें; टोकन उन्हें कैश करता है।
  6. स्थिति परिवर्तन onExecuted में रखें, check में कभी नहीं।

पंजीकरण और क्रम

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

सस्ते, उच्च-अस्वीकृति वाले नियम पहले रखें। HOOK_ADMIN_ROLE को मल्टीसिग या टाइमलॉक के पीछे होना चाहिए: जो पाइपलाइन को नियंत्रित करता है वही यह नियंत्रित करता है कि कौन-से ट्रांसफ़र संभव हैं।

परीक्षण

test/standard/helpers.js संदर्भ स्टैक डिप्लॉय करता है। RecordingHook प्रत्येक कॉलबैक दर्ज करता है और उसे किसी भी कोड के साथ अस्वीकार करने को कहा जा सकता है; ReentrantHook onExecuted से टोकन में पुनः प्रवेश का प्रयास करता है और लॉक को सिद्ध करता है। DomainHooks.test.js और Extensions.test.js पोर्ट किए गए हुक और सहायक कॉन्ट्रैक्ट कवर करते हैं; Invariants.test.js ऑपरेशनों का एक सीडेड यादृच्छिक अनुक्रम चलाता है और सप्लाई संरक्षण, अंतिम स्टेटस, तथा यह जाँचता है कि canTransfer प्रत्येक परिणाम की भविष्यवाणी करता है।

ERC-3643 अनुपालन के साथ अंतरसंचालन

एक ERC-3643 अनुपालन मॉड्यूल moduleCheck(from, to, value, compliance) उपलब्ध कराता है। जो हुक OP_TRANSFER के लिए इसे कॉल करता है और false को किसी कोड में मैप करता है, वह प्रतिभूति टोकन के लिए लिखे गए देश-स्तरीय निर्बंध, बैलेंस सीमाएँ और टाइम लॉक को बैच टोकन पर पुनः उपयोग-योग्य बनाता है। इसका उलटा — किसी A-REX हुक को ERC-3643 टोकन के सामने उपलब्ध कराना — उतना ही यांत्रिक है। Chainlink का Automated Compliance Engine उसी पैटर्न से एकीकृत होता है: उसका पॉलिसी इंजन एक हुक बन जाता है।