श्वेतपत्र · 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_ROLE | registerHook, removeHook | वही टाइमलॉक (हुक परिवर्तन लागू होने से पहले दिखाई देते हैं) |
ENFORCER_ROLE | setFrozenTokens, forcedTransfer | प्रवर्तन मल्टीसिग (अनुपालन और विधिक) |
PAUSER_ROLE | pause, unpause | घटना-प्रतिक्रिया कुंजी |
ISSUER_ROLE | createBatch | ओरिजिनेशन सेवा |
STATUS_ROLE | setBatchStatus, 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 == 0 | ARexInvalidBatch("ZERO_QUANTITY") |
commodity == 0 या unit == 0 | ARexInvalidBatch("COMMODITY_REQUIRED") / ("UNIT_REQUIRED") |
दोनों तिथियाँ सेट हैं और expiresAt <= producedAt | ARexInvalidBatch("EXPIRY_BEFORE_PRODUCTION") |
पॉज़ है, प्राप्तकर्ता अपात्र है, या कोई हुक OP_MINT को अस्वीकार करता है | EnforcedPause(), ERC7943CannotReceive(to), ARexHookRejected(hook, code) |
फ़ील्ड परंपराएँ:
| फ़ील्ड | परंपरा | उदाहरण |
|---|---|---|
commodity | keccak256("<scheme>:<code>"), scheme HS, FAO, GTIN, UNSPSC में से या आपकी अपनी | keccak256("HS:100199") गेहूँ |
unit | bytes32 के रूप में UN/CEFACT Recommendation 20 कोड | "KGM", "TNE", "LTR", "BX" |
producedAt | Unix सेकंड; अभी तक न काटे गए लॉट के लिए 0 | फ़ॉरवर्ड बिक्री, फ़सल प्राप्य |
expiresAt | Unix सेकंड; समाप्ति न होने पर 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:PAUSED | EnforcedPause() |
| 2 | प्रेषक पात्र है (प्रत्येक ELIGIBILITY हुक) | AREX:SENDER_INELIGIBLE | ERC7943CannotSend(from) |
| 3 | प्राप्तकर्ता पात्र है | AREX:RECEIVER_INELIGIBLE | ERC7943CannotReceive(to) |
| 4 | बैच मौजूद है | AREX:BATCH_NOT_FOUND | ARexBatchNotFound(id) |
| 5 | बैच रिटायर नहीं है | AREX:BATCH_RETIRED | ARexBatchRetired(id) |
| 6 | amount <= balance − frozen | AREX:FROZEN | ERC7943InsufficientUnfrozenBalance(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
| हुक | फ़्लैग | कंस्ट्रक्टर | इसके साथ कॉन्फ़िगर करें |
|---|---|---|---|
EligibilityHook | CHECK, ELIGIBILITY | (admin) | setAllowlistEnabled, setAllowlisted, setBlocklisted, setSanctioned |
ExpiryHook | CHECK | (allowPreHarvestTransfers) | — (बैच रिकॉर्ड पढ़ता है) |
AttestationHook | CHECK | (owner, attestationRegistry) | setRequirement(commodity, topic) |
PerishabilityHook | CHECK | (admin) | setCommodityDecay, setBatchDecay |
SeasonalHook | CHECK | (admin) | addWindow(commodity, op, start, end), clearWindows |
DeliveryHook | CHECK, EXECUTED | (admin) | schedule, pickUp, markInTransit, recordReading, confirmDelivered, dispute, resolve |
TraceabilityHook | EXECUTED | (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, TrustedIssuerUpdated | ARexAttestations | साक्ष्य |
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() एक्सपोर्ट करता है।
प्रोडक्शन में जाना: चेकलिस्ट
- पुनरुत्पादनीय बिल्ड (
npm ci), CI हरा, इंटरफ़ेस आईडी विनिर्देश के बराबर। - भूमिकाएँ चरण 3 के अनुसार पुनर्वितरित; डिप्लॉयकर्ता की भूमिकाएँ त्यागी गईं।
- हुक ऑडिट किए गए, क्रम प्रलेखित,
getHooks()प्रकाशित। - मेटाडेटा स्कीमा संस्करण और स्टेटस तालिका प्रकाशित;
originRefऔर निर्बंधित डेटा व्यक्तिगत डेटा से मुक्त। - विधिक लिखत मेटाडेटा में संदर्भित; पेशकश का वर्गीकरण विधि-सलाहकार के साथ तय।
- टोकन, एटेस्टेशन रजिस्ट्री और प्रत्येक पंजीकृत हुक का तृतीय-पक्ष ऑडिट (पैकेज:
docs/AUDIT-READINESS.md)। validateUpgradeके साथ फ़ॉर्क पर अपग्रेड का पूर्वाभ्यास।