Skip to main contentA-REX Core

श्वेतपत्र · 11

कार्यान्वयन मार्गदर्शिका

साफ़ क्लोन से लेकर डिप्लॉय किए गए, हुक-युक्त, एटेस्ट किए गए बैच टोकन तक, चरण-दर-चरण। इस अध्याय का प्रत्येक JavaScript ब्लॉक test/standard/ImplementationGuide.test.js से कॉपी किया गया है और CI में चलता है; यदि यहाँ कोई ब्लॉक गलत होता, तो बिल्ड विफल हो जाता।

इस मार्गदर्शिका का उपयोग कैसे करें

चरण एक-दूसरे पर बनते हैं और चर साझा करते हैं: token, attestations, साइनर और रास्ते में बनाई गई आईडी। पहली बार उन्हें क्रम से पढ़ें। उन्हें स्वयं इस प्रकार चलाएँ:

npx hardhat test test/standard/ImplementationGuide.test.js
चरणआपको क्या मिलता हैकौन कार्य करता हैमुख्य कॉल
1 · सेट अपकंपाइल और परीक्षित चेकआउटडेवलपरnpm ci, npm test
2 · डिप्लॉयटोकन प्रॉक्सी + एटेस्टेशन रजिस्ट्रीडिप्लॉयकर्ताupgrades.deployProxy, ARexAttestations
3 · भूमिकाएँन्यूनतम विशेषाधिकार वाले ऑपरेटरप्रशासकgrantRole
4 · बनाएँमेटाडेटा कमिटमेंट वाला बैचजारीकर्ताcreateBatch
5 · पढ़ेंबैच रिकॉर्ड और सप्लाईकोई भीbatchOf, totalSupply(uint256), parentsOf
6 · ट्रांसफ़रएक जाँचा गया ट्रांसफ़रधारकtransferCheck, safeTransferFrom
7 · वंशावलीविभाजन, मर्ज, प्रसंस्करणधारक / ट्रांसफ़ॉर्मरtransform
8 · स्टेटसजीवनचक्र और मेटाडेटा अपडेटस्टेटस भूमिकाsetBatchStatus, setBatchMetadata
9 · हुकएक क्रमबद्ध नियम पाइपलाइनहुक प्रशासकregisterHook, getHooks
10 · एटेस्टसाक्ष्य जो ट्रांसफ़र को अनलॉक करता हैविश्वसनीय जारीकर्ताsetTrustedIssuer, attest
11 · प्रतिबंधस्रोत और TTL के साथ पात्रतापात्रता प्रबंधकsetSanctioned
12 · प्रवर्तनफ़्रीज़, पॉज़, बलपूर्वक ट्रांसफ़रप्रवर्तनकर्ता / पॉज़कर्ताsetFrozenTokens, forcedTransfer
13 · रिटायरलॉट का जीवन-अंतस्टेटस भूमिका, धारकsetBatchStatus(…, 255, …), burn
14 · इंडेक्सइवेंट से वंशावली ग्राफ़इंडेक्सरqueryFilter(BatchTransformed)
15 · अपग्रेडएक वैलिडेट किया गया इम्प्लीमेंटेशन अपग्रेडअपग्रेडरvalidateUpgrade, upgradeProxy

1 · सेट अप

Node.js 20 या 22 LTS और npm। डिपेंडेंसी package-lock.json में पिन की गई हैं (OpenZeppelin 5.3.0); npm ci का उपयोग करें ताकि आपका बिल्ड वही हो जो CI बनाता है।

git clone https://github.com/Arex-Corp/A-REX.git && cd A-REX
npm ci
npm run compile
npm test                     # 46 tests: reference implementation, hooks, extensions, invariants, this guide
npm run interface-ids        # prints the ERC-165 ids; they must match the specification

नीचे के स्निपेट तीन छोटे हेल्पर उपयोग करते हैं और दस खाते नामित करते हैं। explain अस्वीकृति कोड को वापस उसके नाम में बदलता है; सूची ARexCodes में परिभाषित कोड का समूह है।

const commodity = (scheme, code) => ethers.keccak256(ethers.toUtf8Bytes(`${scheme}:${code}`));
const unit = (u) => ethers.encodeBytes32String(u);
const ref = (text) => ethers.keccak256(ethers.toUtf8Bytes(text));
const REASONS = Object.fromEntries(
  ["PAUSED", "FROZEN", "SENDER_INELIGIBLE", "RECEIVER_INELIGIBLE", "SANCTIONED", "BATCH_NOT_FOUND",
   "BATCH_RETIRED", "BATCH_EXPIRED", "BATCH_NOT_PRODUCED", "ATTESTATION_MISSING", "SEASON_CLOSED",
   "IN_TRANSIT", "LIMIT_EXCEEDED", "DOCUMENT_MISSING"].map((n) => [ethers.keccak256(ethers.toUtf8Bytes(`AREX:${n}`)), n]),
);
const explain = (code) => (code === ethers.ZeroHash ? "OK" : REASONS[code] ?? code);


const [admin, issuer, farmer, buyer, coop, mill, warehouse, certifier, enforcer, custodian] = await ethers.getSigners();

2 · डिप्लॉय

संदर्भ टोकन UUPS-अपग्रेड-योग्य है, इसलिए उसे OpenZeppelin अपग्रेड प्लगइन के साथ ERC-1967 प्रॉक्सी के पीछे डिप्लॉय किया जाता है, जो स्टोरेज लेआउट को भी वैलिडेट करता है। एटेस्टेशन रजिस्ट्री एक सामान्य कॉन्ट्रैक्ट है जो टोकन से बँधा है।

const ARexToken = await ethers.getContractFactory("ARexToken");
const token = await upgrades.deployProxy(ARexToken, [admin.address, "ipfs://<cid>/{id}.json"], { kind: "uups" });
await token.waitForDeployment();
const attestations = await (await ethers.getContractFactory("ARexAttestations"))
  .deploy(admin.address, await token.getAddress());

कमांड लाइन से वही स्टैक, पात्रता और समाप्ति हुक सहित, इस प्रकार डिप्लॉय होता है:

npx hardhat node                                                     # terminal 1
npx hardhat run scripts/deploy-standard.js --network localhost       # token, attestations, 2 hooks
npx hardhat run scripts/deploy-full-standard.js --network localhost  # every hook in order + extensions

या Hardhat Ignition के साथ:

npx hardhat ignition deploy ignition/modules/ARexToken.js --network localhost \
  --parameters '{"ARexTokenModule":{"baseURI":"ipfs://<cid>/{id}.json","allowPreHarvestTransfers":true}}'

टेस्टनेट या L2 के लिए, hardhat.config.js में sepolia, polygon और arbitrum परिभाषित हैं; .env में RPC URL और PRIVATE_KEY सेट करें (टेम्पलेट .env.example में)। ARexToken optimizer.runs = 1 के साथ 24,136 बाइट में संकलित होता है, जो 24,576-बाइट EIP-170 सीमा के भीतर है।

3 · भूमिकाएँ

initialize(admin, baseURI) प्रत्येक भूमिका admin को दे देता है, जो पहली डिप्लॉयमेंट के लिए सुविधाजनक है और प्रोडक्शन के लिए गलत। प्रत्येक परिचालन भूमिका उस सेवा या मल्टीसिग को दें जो उसे निभाती है:

await token.grantRole(await token.ISSUER_ROLE(), issuer.address);
await token.grantRole(await token.TRANSFORMER_ROLE(), mill.address);
await token.grantRole(await token.STATUS_ROLE(), warehouse.address);
await token.grantRole(await token.ENFORCER_ROLE(), enforcer.address);
भूमिकाक्या अनुमति देती हैअनुशंसित धारक
DEFAULT_ADMIN_ROLEप्रत्येक भूमिका देना और रद्द करना; setBaseURIटाइमलॉक के पीछे गवर्नेंस मल्टीसिग
UPGRADER_ROLEइम्प्लीमेंटेशन बदलनावही टाइमलॉक
HOOK_ADMIN_ROLEregisterHook, removeHookवही टाइमलॉक (हुक परिवर्तन लागू होने से पहले दिखाई देते हैं)
ENFORCER_ROLEsetFrozenTokens, forcedTransferप्रवर्तन मल्टीसिग (अनुपालन और विधिक)
PAUSER_ROLEpause, unpauseघटना-प्रतिक्रिया कुंजी
ISSUER_ROLEcreateBatchओरिजिनेशन सेवा
STATUS_ROLEsetBatchStatus, setBatchMetadataवेयरहाउस, निरीक्षक या रजिस्ट्री
TRANSFORMER_ROLEप्रसंस्करण ट्रांसफ़ॉर्म (कमोडिटी, इकाई या मात्रा परिवर्तन)प्रोसेसर
ATTEST_ADMIN_ROLE (ARexAttestations पर)setTrustedIssuer, किसी भी एटेस्टेशन को निरस्त करनाप्रमाणन प्राधिकरण या प्लेटफ़ॉर्म

जब प्रत्येक भूमिका का धारक तय हो जाए, तो डिप्लॉयकर्ता जिसकी उसे अब आवश्यकता नहीं है उसे renounceRole(role, deployer) से त्याग देता है।

4 · बैच बनाएँ

बैच एक ERC-1155 आईडी है। उसका ऑन-चेन रिकॉर्ड छोटा और निश्चित है; वर्णनात्मक सब कुछ एक मेटाडेटा दस्तावेज़ में रहता है जिसका हैश ऑन-चेन कमिट होता है (स्कीमा: standard/schema/batch-metadata.schema.json)।

const metadata = { schemaVersion: "1.0.0", commodity: { scheme: "HS", code: "100199" }, unit: "KGM", quantity: 250000 };
const canonical = JSON.stringify(metadata);                 // RFC 8785 canonicalization in production
const metadataHash = ethers.keccak256(ethers.toUtf8Bytes(canonical));
const uri = "ipfs://<cid>/batch-1.json";                    // wherever you stored `canonical`


const init = {
  commodity: commodity("HS", "100199"),                     // wheat
  unit: unit("KGM"),
  producedAt: Math.floor(Date.parse("2026-03-18T00:00:00Z") / 1000), // 0 = not yet harvested
  expiresAt: 0,                                             // 0 = no expiry
  originRef: ref("DDS-2026-AR-000123"),                     // a reference, never coordinates
  metadataHash,
  uri,
};
const id = await token.connect(issuer).createBatch.staticCall(init, 250_000, farmer.address, "0x");
await (await token.connect(issuer).createBatch(init, 250_000, farmer.address, "0x")).wait();

createBatch.staticCall वह आईडी लौटाता है जो ट्रांज़ैक्शन असाइन करेगा; आईडी 1 से शुरू होती हैं और एक-एक बढ़ती हैं। टोकन इन्हें अस्वीकार करता है:

शर्तएरर
कॉलर के पास ISSUER_ROLE नहीं हैAccessControlUnauthorizedAccount(account, role)
to शून्य पता हैARexInvalidRecipient()
quantity == 0ARexInvalidBatch("ZERO_QUANTITY")
commodity == 0 या unit == 0ARexInvalidBatch("COMMODITY_REQUIRED") / ("UNIT_REQUIRED")
दोनों तिथियाँ सेट हैं और expiresAt <= producedAtARexInvalidBatch("EXPIRY_BEFORE_PRODUCTION")
पॉज़ है, प्राप्तकर्ता अपात्र है, या कोई हुक OP_MINT को अस्वीकार करता हैEnforcedPause(), ERC7943CannotReceive(to), ARexHookRejected(hook, code)

फ़ील्ड परंपराएँ:

फ़ील्डपरंपराउदाहरण
commoditykeccak256("<scheme>:<code>"), scheme HS, FAO, GTIN, UNSPSC में से या आपकी अपनीkeccak256("HS:100199") गेहूँ
unitbytes32 के रूप में UN/CEFACT Recommendation 20 कोड"KGM", "TNE", "LTR", "BX"
producedAtUnix सेकंड; अभी तक न काटे गए लॉट के लिए 0फ़ॉरवर्ड बिक्री, फ़सल प्राप्य
expiresAtUnix सेकंड; समाप्ति न होने पर 0नाशवान उपज
originRefउद्गम साक्ष्य के संदर्भ का हैश; कभी निर्देशांक नहींEUDR DDS संदर्भ, रजिस्ट्री आईडी
metadataHashकैनोनिकल (RFC 8785) मेटाडेटा दस्तावेज़ का keccak256
uriजहाँ दस्तावेज़ रहता है; इसे छोटा रखें, यह ऑन-चेन संग्रहीत होता हैipfs://<cid>, GS1 Digital Link

5 · बैच पढ़ें

const batch = await token.batchOf(id);                      // reverts ARexBatchNotFound if unknown
await token.batchExists(id);                                // true / false, never reverts
await token["totalSupply(uint256)"](id);                    // units in circulation (overloaded name)
await token.parentsOf(id);                                  // [] for an original lot
await token.uri(id);                                        // batch URI, or the base URI if empty
await token.isExpired(id);                                  // reference-implementation helper

totalSupply ERC-1155 Supply में ओवरलोडेड है (totalSupply() और totalSupply(uint256)), इसलिए ethers v6 में उसे सिग्नेचर से कॉल करें। Batch.status 0 (ACTIVE), 255 (RETIRED) या बीच का कोई जारीकर्ता-परिभाषित मान है।

6 · ट्रांसफ़र

हमेशा पहले पूछें। transferCheck ट्रांसफ़र पाइपलाइन को व्यू के रूप में दोहराता है और पहला अस्वीकृति कोड लौटाता है; 0x00…00 का अर्थ है अनुमत। canTransfer उसी उत्तर का ERC-7943 बूलियन संस्करण है।

const code = await token.transferCheck(farmer.address, buyer.address, id, 10_000);
if (code !== ethers.ZeroHash) throw new Error(`transfer blocked: ${explain(code)}`);
await token.connect(farmer).safeTransferFrom(farmer.address, buyer.address, id, 10_000, "0x");

पाइपलाइन क्रम निश्चित है, इसलिए कोड बताता है कि कौन-सा नियम विफल हुआ:

क्रमजाँचtransferCheck द्वारा लौटाया गया कोडट्रांज़ैक्शन पर रिवर्ट
1टोकन पॉज़ हैAREX:PAUSEDEnforcedPause()
2प्रेषक पात्र है (प्रत्येक ELIGIBILITY हुक)AREX:SENDER_INELIGIBLEERC7943CannotSend(from)
3प्राप्तकर्ता पात्र हैAREX:RECEIVER_INELIGIBLEERC7943CannotReceive(to)
4बैच मौजूद हैAREX:BATCH_NOT_FOUNDARexBatchNotFound(id)
5बैच रिटायर नहीं हैAREX:BATCH_RETIREDARexBatchRetired(id)
6amount <= balance − frozenAREX:FROZENERC7943InsufficientUnfrozenBalance(from, id, amount, unfrozen)
7प्रत्येक CHECK हुक, पंजीकरण क्रम मेंहुक का कोडARexHookRejected(hook, code)

व्यू और ट्रांज़ैक्शन एक ही क्रम में मूल्यांकन करते हैं, इसलिए कोड हमेशा उस जाँच का नाम बताता है जिस पर ट्रांज़ैक्शन पहले विफल होता है।

व्यवहार में दो ब्योरे मायने रखते हैं। AREX:FROZEN तब भी लौटाया जाता है जब प्रेषक के पास पर्याप्त इकाइयाँ बस नहीं हैं, क्योंकि जाँच अनफ़्रीज़्ड बैलेंस पर है। और transferCheck हुक को खाली data देता है; जो हुक ट्रांसफ़र डेटा पढ़ता है (जैसे DeliveryHook निपटान) वह वास्तविक ट्रांज़ैक्शन के लिए अलग उत्तर दे सकता है।

7 · वंशावली: विभाजन, मर्ज, प्रसंस्करण

transform बैचों से बैच बनाने का एकमात्र तरीका है। यह from से इनपुट बर्न करता है, to को आउटपुट मिंट करता है, parentsOf(output) = inputIds दर्ज करता है और एक BatchTransformed इमिट करता है, जो EPCIS TransformationEvent से मैप होता है। transform.staticCall नई आईडी लौटाता है।

एक री-बैच कमोडिटी, इकाई और कुल मात्रा को बनाए रखता है, और उसे किसी भूमिका की आवश्यकता नहीं, केवल स्वामित्व या ERC-1155 अनुमोदन चाहिए। किसी क्रेता के लिए एक लॉट को दो में बाँटें:

const [lotA, lotB] = await token.connect(farmer).transform.staticCall(
  farmer.address, buyer.address, [id], [400], [init, init], [250, 150], ref("SALE-42"));
await token.connect(farmer).transform(
  farmer.address, buyer.address, [id], [400], [init, init], [250, 150], ref("SALE-42"));

दो लॉट को सहकारी संस्था के लिए एक पूल किए गए लॉट में मर्ज करें:

const [pooled] = await token.connect(buyer).transform.staticCall(
  buyer.address, coop.address, [lotA, lotB], [250, 150], [init], [400], ref("POOL-7"));
await token.connect(buyer).transform(
  buyer.address, coop.address, [lotA, lotB], [250, 150], [init], [400], ref("POOL-7"));

प्रसंस्करण कमोडिटी, इकाई या मात्रा बदलता है, इसलिए अनुमोदन के अतिरिक्त उसे TRANSFORMER_ROLE चाहिए। मिल किसान के 1,000 kg गेहूँ को 750 kg आटे में बदलती है:

const now = (await ethers.provider.getBlock("latest")).timestamp;
const flourInit = { ...init, commodity: commodity("HS", "110100"), producedAt: now };
await token.connect(farmer).setApprovalForAll(mill.address, true);
const [flour] = await token.connect(mill).transform.staticCall(
  farmer.address, mill.address, [id], [1_000], [flourInit], [750], ref("MILL-RUN-7"));
await token.connect(mill).transform(
  farmer.address, mill.address, [id], [1_000], [flourInit], [750], ref("MILL-RUN-7"));
नियमकिसके द्वारा लागू
कॉलर from है या from द्वारा अनुमोदितtransform
प्रत्येक इनपुट मौजूद है, रिटायर नहीं है और उसके पास पर्याप्त अनफ़्रीज़्ड बैलेंस है_update
री-बैच: समान कमोडिटी और इकाई, Σ इनपुट = Σ आउटपुटकिसी भूमिका की आवश्यकता नहीं
इसके अलावा कुछ भीTRANSFORMER_ROLE, अन्यथा AccessControlUnauthorizedAccount
हुक प्रत्येक लेग देखते हैंबर्न और मिंट के लिए OP_TRANSFORM

इस मार्गदर्शिका द्वारा बनाई गई वंशावली:

flowchart LR
    B1["#1 गेहूँ 250,000 kg<br/>किसान"] -->|"विभाजन · SALE-42"| B2["#2 गेहूँ 250"]
    B1 -->|"विभाजन · SALE-42"| B3["#3 गेहूँ 150"]
    B2 -->|"मर्ज · POOL-7"| B4["#4 गेहूँ 400<br/>सहकारी संस्था"]
    B3 -->|"मर्ज · POOL-7"| B4
    B1 -->|"प्रसंस्करण · MILL-RUN-7"| B5["#5 आटा 750<br/>मिल"]

8 · स्टेटस और मेटाडेटा

केवल STATUS_ROLE स्टेटस बदलती है; इकाइयाँ धारण करने से कुछ नहीं मिलता। मान 1..254 आपके परिभाषित करने के लिए हैं; उनका अर्थ मेटाडेटा दस्तावेज़ (status.table) में प्रकाशित करें। मेटाडेटा अपडेट करने से हैश और URI बदल जाते हैं और BatchMetadataUpdated इमिट होता है, इसलिए दस्तावेज़ों का इतिहास सत्यापन-योग्य बना रहता है।

await token.connect(warehouse).setBatchStatus(flour, 1, ref("IN_STORAGE"));  // 1..254 are yours
const labReport = { ...metadata, lab: { moisture: 14.2 } };
await token.connect(warehouse).setBatchMetadata(
  flour, ethers.keccak256(ethers.toUtf8Bytes(JSON.stringify(labReport))), "ipfs://<cid>/flour-lab.json");

9 · हुक

हुक स्वतंत्र कॉन्ट्रैक्ट हैं जिन्हें टोकन पंजीकरण क्रम में कॉल करता है। यहाँ पहले पात्रता हुक चलता है, फिर समाप्ति, फिर वह नियम कि कॉफ़ी को बेचे जाने से पहले एटेस्ट किया हुआ EUDR ड्यू-डिलिजेंस कथन चाहिए।

const eligibility = await (await ethers.getContractFactory("EligibilityHook")).deploy(admin.address);
const expiry = await (await ethers.getContractFactory("ExpiryHook")).deploy(true); // allow pre-harvest
const attestationHook = await (await ethers.getContractFactory("AttestationHook"))
  .deploy(admin.address, await attestations.getAddress());
const COFFEE = commodity("HS", "090111");
const EUDR_DDS = ref("AREX:EUDR:DDS");
await attestationHook.setRequirement(COFFEE, EUDR_DDS);     // coffee needs a due-diligence statement


await token.registerHook(await eligibility.getAddress());      // evaluated first
await token.registerHook(await expiry.getAddress());
await token.registerHook(await attestationHook.getAddress());  // evaluated last
await token.getHooks();                                        // the ordered list
हुकफ़्लैगकंस्ट्रक्टरइसके साथ कॉन्फ़िगर करें
EligibilityHookCHECK, ELIGIBILITY(admin)setAllowlistEnabled, setAllowlisted, setBlocklisted, setSanctioned
ExpiryHookCHECK(allowPreHarvestTransfers)— (बैच रिकॉर्ड पढ़ता है)
AttestationHookCHECK(owner, attestationRegistry)setRequirement(commodity, topic)
PerishabilityHookCHECK(admin)setCommodityDecay, setBatchDecay
SeasonalHookCHECK(admin)addWindow(commodity, op, start, end), clearWindows
DeliveryHookCHECK, EXECUTED(admin)schedule, pickUp, markInTransit, recordReading, confirmDelivered, dispute, resolve
TraceabilityHookEXECUTED(admin, persist)recordEvent (RECORDER_ROLE)

पंजीकरण EOA, IARexHook ERC-165 समर्थन के बिना कॉन्ट्रैक्ट और ऐसे हुक अस्वीकार करता है जिनके फ़्लैग 0 हैं। सस्ते, बार-बार अस्वीकार करने वाले हुक पहले रखें: अस्वीकृति पाइपलाइन रोक देती है। अपना हुक लिखना: docs/HOOK-DEVELOPMENT.md।

10 · एटेस्टेशन

एटेस्टेशन (batch, topic) → (issuer, reference, issuedAt, expiresAt, revoked) है, जो केवल तब तक वैध रहता है जब तक उसका जारीकर्ता उस विषय के लिए विश्वसनीय है। चरण 9 की आवश्यकता लागू होने पर, कॉफ़ी लॉट तब तक नहीं हिल सकता जब तक कथन एटेस्ट न हो जाए:

const coffee = await token.connect(issuer).createBatch.staticCall({ ...init, commodity: COFFEE }, 60, farmer.address, "0x");
await token.connect(issuer).createBatch({ ...init, commodity: COFFEE }, 60, farmer.address, "0x");
explain(await token.transferCheck(farmer.address, buyer.address, coffee, 1));   // "ATTESTATION_MISSING"


await attestations.setTrustedIssuer(EUDR_DDS, certifier.address, true);         // ATTEST_ADMIN_ROLE
await attestations.connect(certifier).attest(coffee, EUDR_DDS, ref("DDS-2026-AR-000123"), 0);
await attestations.isAttested(coffee, EUDR_DDS);                                 // true
explain(await token.transferCheck(farmer.address, buyer.address, coffee, 1));   // "OK"

isAttested तब false है जब कोई एटेस्टेशन नहीं है, जब वह निरस्त है, जब expiresAt बीत चुका है, या जब जारीकर्ता उस विषय के लिए अब विश्वसनीय नहीं है। जारीकर्ता या ATTEST_ADMIN_ROLE निरस्त कर सकते हैं। विषय मुक्त bytes32 मान हैं; keccak256("<NAMESPACE>:<NAME>") का उपयोग करें, उदाहरण के लिए AREX:EUDR:DDS या AREX:CERT:ORGANIC_EU।

11 · प्रतिबंध और पात्रता

EligibilityHook टोकन के लिए canSend और canReceive का उत्तर देता है। प्रतिबंध अपना स्रोत और एक वैकल्पिक समाप्ति दर्ज करता है, ताकि सूची का अपडेट स्वतः समाप्त हो सके।

await eligibility.setSanctioned(buyer.address, true, ethers.encodeBytes32String("OFAC-SDN"), 0);
explain(await token.transferCheck(farmer.address, buyer.address, id, 1));       // "RECEIVER_INELIGIBLE"
await eligibility.setSanctioned(buyer.address, false, ethers.encodeBytes32String("OFAC-SDN"), 0);

12 · प्रवर्तन

फ़्रीज़ और बलपूर्वक ट्रांसफ़र ERC-7943 प्रवर्तन सतह हैं, ग्रहणाधिकार, न्यायालय आदेश और ज़ब्ती के लिए। फ़्रीज़ वर्तमान बैलेंस से अधिक हो सकता है और तब भावी जमा को भी कवर करता है। बलपूर्वक ट्रांसफ़र पात्रता और फ़्रीज़ जाँच को छोड़ देता है, टोकन के पॉज़ होने पर भी काम करता है, फ़्रीज़ की गई राशि को शेष मात्रा तक घटा देता है, और फिर भी हुक को OP_FORCED_TRANSFER के साथ चलाता है।

await token.connect(enforcer).setFrozenTokens(buyer.address, id, 6_000);          // may exceed the balance
await token.getFrozenTokens(buyer.address, id);                                   // 6_000
explain(await token.transferCheck(buyer.address, farmer.address, id, 5_000));    // "FROZEN"
await token.pause();                                                              // PAUSER_ROLE
await token.connect(enforcer).forcedTransfer(buyer.address, custodian.address, id, 6_000); // works while paused
await token.unpause();

वह प्रक्रिया प्रलेखित करें जिसके अंतर्गत ENFORCER_ROLE कार्य करती है (न्यायालय आदेश, गिरवी करार, प्रतिबंध सूचना) और भूमिका को मल्टीसिग के पीछे रखें। प्रत्येक कार्रवाई Frozen या ForcedTransfer इमिट करती है।

13 · रिटायर

RETIRED (255) अंतिम है: कोई ट्रांसफ़र, मिंट या ट्रांसफ़ॉर्म नहीं, और वापसी का कोई मार्ग नहीं। बर्न करना संभव रहता है ताकि डिलीवर या नष्ट किया गया माल सप्लाई से बाहर हो जाए।

await token.connect(warehouse).setBatchStatus(flour, 255, ref("DELIVERED"));    // RETIRED is terminal
explain(await token.transferCheck(mill.address, buyer.address, flour, 1));       // "BATCH_RETIRED"
await token.connect(mill).burn(mill.address, flour, 750);                         // burning still works

14 · इंडेक्स

वंशावली ग्राफ़ BatchTransformed इवेंट की सूची है; अलग-अलग ट्रांसफ़र से कुछ भी पुनर्निर्मित करने की आवश्यकता नहीं।

const transforms = await token.queryFilter(token.filters.BatchTransformed());
const edges = transforms.flatMap((e) =>
  e.args.inputIds.flatMap((input) => e.args.outputIds.map((output) => [input, output, e.args.processRef])));
// edges: [1 → 2], [1 → 3], [2 → 4], [3 → 4], [1 → 5]
इवेंटकिसके द्वारा इमिटउपयोग
BatchCreated(id, issuer, commodity, unit, quantity, to)createBatchनया लॉट; EPCIS ObjectEvent ADD
BatchTransformed(operator, inputIds, inputAmounts, outputIds, outputAmounts, processRef)transformवंशावली एज; EPCIS TransformationEvent
BatchStatusChanged(id, previousStatus, newStatus, reason)setBatchStatusजीवनचक्र
BatchMetadataUpdated(id, metadataHash, uri)setBatchMetadataदस्तावेज़ इतिहास
TransferSingle, TransferBatchप्रत्येक बैलेंस परिवर्तनहोल्डिंग; EPCIS ObjectEvent OBSERVE
Frozen(account, tokenId, amount), ForcedTransfer(from, to, tokenId, amount)प्रवर्तनग्रहणाधिकार, ज़ब्ती
HookRegistered(hook, flags, position), HookRemoved(hook)हुक प्रशासकनियम परिवर्तन
Attested, AttestationRevoked, TrustedIssuerUpdatedARexAttestationsसाक्ष्य

15 · अपग्रेड

स्टोरेज ERC-7201 नेमस्पेस्ड है (erc7201:arex.storage.token); TokenStorage में फ़ील्ड जोड़ें; उन्हें पुनः क्रमबद्ध करना या हटाना कभी न करें। प्रत्येक अपग्रेड से पहले वैलिडेट करें; प्लगइन उन लेआउट को अस्वीकार करता है जो स्टोरेज को दूषित कर देंगे।

await upgrades.validateUpgrade(await token.getAddress(), ARexToken, { kind: "uups" });
await upgrades.upgradeProxy(await token.getAddress(), ARexToken);               // UPGRADER_ROLE

प्रोडक्शन में UPGRADER_ROLE मल्टीसिग और टाइमलॉक के पीछे होती है, और अपग्रेड का पहले लक्षित चेन के फ़ॉर्क पर पूर्वाभ्यास किया जाता है।

अपने इंटीग्रेशन का परीक्षण

npx hardhat test test/standard/ImplementationGuide.test.js   # this chapter, executed
npx hardhat test test/standard/ARexToken.test.js             # token behaviour and conformance
npx hardhat run scripts/smoke-standard.js                    # narrated walkthrough of one lot
npx hardhat run scripts/gas-report-standard.js               # gas with 0, 1 and 2 hooks, and per hook

test/standard/helpers.js आपके अपने टेस्ट के लिए deployStack(), batchInit(), OP, HOOK, STATUS और CODES स्थिरांक तथा supply() एक्सपोर्ट करता है।

प्रोडक्शन में जाना: चेकलिस्ट

  1. पुनरुत्पादनीय बिल्ड (npm ci), CI हरा, इंटरफ़ेस आईडी विनिर्देश के बराबर।
  2. भूमिकाएँ चरण 3 के अनुसार पुनर्वितरित; डिप्लॉयकर्ता की भूमिकाएँ त्यागी गईं।
  3. हुक ऑडिट किए गए, क्रम प्रलेखित, getHooks() प्रकाशित।
  4. मेटाडेटा स्कीमा संस्करण और स्टेटस तालिका प्रकाशित; originRef और निर्बंधित डेटा व्यक्तिगत डेटा से मुक्त।
  5. विधिक लिखत मेटाडेटा में संदर्भित; पेशकश का वर्गीकरण विधि-सलाहकार के साथ तय।
  6. टोकन, एटेस्टेशन रजिस्ट्री और प्रत्येक पंजीकृत हुक का तृतीय-पक्ष ऑडिट (पैकेज: docs/AUDIT-READINESS.md)।
  7. validateUpgrade के साथ फ़ॉर्क पर अपग्रेड का पूर्वाभ्यास।