Inline assembly का उपयोग करके transactions को revert करना high-level Solidity revert या require statement का उपयोग करने की तुलना में अधिक gas-efficient हो सकता है। इस गाइड में, हम यह जानेंगे कि Solidity में विभिन्न प्रकार के reverts assembly में उनके implementations को simulate करके under the hood (आंतरिक रूप से) कैसे काम करते हैं।
नीचे दिया गया उदाहरण दिखाता है कि assembly वर्ज़न में revert statement gas cost को 157 gas से घटाकर 126 gas कर देता है, जिससे 31 gas की बचत होती है:

एक pre-requisite (पूर्व-आवश्यकता) के रूप में, हम मानकर चलते हैं कि आपने Try Catch and All the Ways Solidity Reverts आर्टिकल के साथ-साथ article on ABI encoding भी पढ़ लिया है।
EVM में, memory bytes का एक लंबा array होता है जो byte-indexed होता है। यानी, हम उनके index के आधार पर bytes को पढ़ और लिख सकते हैं। भले ही memory bytes indexed है, हम आमतौर पर एक बार में 32 bytes पढ़ते और लिखते हैं।
assembly में mstore और यह कैसे काम करता है
Assembly के साथ revert करना memory में data स्टोर करने के लिए Yul mstore opcode पर काफी हद तक निर्भर करता है, इसलिए आइए पहले उस opcode को गहराई से समझें।
mstore opcode दो arguments लेता है:
- Memory location: वह byte address जहाँ data स्टोर किया जाएगा।
- Data: स्टोर किया जाने वाला 32-byte data।
mstore का उपयोग कैसे करें, इसका एक उदाहरण नीचे दिया गया है:
assembly {
mstore(memoryLocation, dataToStore)
}
यदि आप memory location 0x00 पर 0xFF के 32 bytes स्टोर करना चाहते हैं, तो आप लिखेंगे:
assembly {
mstore(
0x00,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
)
}
यह index 0x00 से शुरू होने वाली पूरी 32-byte value को स्टोर करता है। यदि आप इसके बजाय memory location 0x01 पर value स्टोर करना चाहते हैं, तो आप लिखेंगे:
assembly {
mstore(
0x01,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
)
}
यह data की शुरुआत को एक byte से shift कर देता है, और 0x00 पर पहला byte अप्रभावित (unaffected) रहेगा। नीचे दिया गया diagram दिखाता है कि mstore memory में data कैसे स्टोर करता है:

ध्यान दें कि भले ही हमने mstore(0, ...) में byte index 0 पर लिखने का निर्देश दिया है, हमने 0 पर लिखा — और उसके बाद के 31 bytes पर भी — mstore एक बार में 32 bytes लिखता है।
mstore में Implicit data padding
यदि हम दूसरे argument में 32 bytes (64 hex chars) से कम निर्दिष्ट (specify) करते हैं, तो Solidity compiler इसे बाईं ओर (left-pad) zeros (अधिक significant bytes) से तब तक भरेगा जब तक कि value 32 bytes लंबी न हो जाए, फिर यह mstore के पहले argument में निर्दिष्ट byte index से शुरू करके उन 32 bytes को लिखेगा।
निम्नलिखित उदाहरण पर विचार करें:
assembly {
mstore(0x00, 0xff)
}
उपरोक्त कोड memory में data को 0x00000000000000000000000000000000000000000000000000000000000000ff के रूप में स्टोर करता है, जिसमें 0xff अंतिम byte घेरता है और शेष पिछले 31 bytes zeros से भरे होते हैं।
दूसरे शब्दों में, mstore(0x00, 0xff) implicitly (अंतर्निहित रूप से) mstore(0x00, 0x00000000000000000000000000000000000000000000000000000000000000ff) बन जाता है।
Memory में value का परिणाम यहाँ दिखाया गया है:

याद रखें कि mstore 32 bytes लिखता है, लेकिन इस मामले में, उनमें से 31 bytes zeros हैं, जो 0th byte से 30th byte तक फैले हुए हैं। इसका मतलब है कि byte range 0-31 के भीतर किसी भी data को zeros के साथ overwrite कर दिया जाएगा।
हम देख सकते हैं कि memory में यह कैसा दिखता है यदि हम स्टोर किए गए data को वापस (return) करते हैं जैसा कि नीचे दिए गए स्क्रीनशॉट में दिखाया गया है:

नीचे दिया गया diagram दिखाता है कि mstore विभिन्न hex values को implicitly बाईं ओर (left-pad) कैसे करता है, जिसमें पहली पंक्ति वह उदाहरण है जिसे हमने अभी देखा है।

Memory में data स्टोर करने के लिए mstore8 का उपयोग करना
विकल्प के तौर पर, हम mstore8 opcode का उपयोग कर सकते हैं, जो mstore के समान है लेकिन किसी विशिष्ट memory location पर केवल एक byte data स्टोर करता है।
assembly {
mstore8(memoryLocation, exactlyOneByteOfData)
}
उदाहरण के लिए, यदि हम 31st byte पर single byte data (0xff) स्टोर करना चाहते हैं, तो हम इसे सीधे mstore8 का उपयोग करके इस प्रकार स्टोर कर सकते हैं:
assembly {
mstore8(31, 0xff)
}
और आउटपुट mstore का उपयोग करने के समान ही होगा, जिसमें 0xff अंतिम byte पर होगा।


mstore8 और mstore के बीच मुख्य अंतर यह है कि mstore8 31 अतिरिक्त zeros नहीं जोड़ता है जो mstore के विपरीत 0th से 31st byte तक फैले पहले से स्टोर किए गए data को overwrite कर देगा।
mstore का उपयोग करके दाईं ओर zeros के साथ 0xff लिखना
यदि आप mstore8 के बजाय mstore का उपयोग करके 0th byte में 0xff लिखना चाहते हैं, तो आप 0xff को पहले byte के रूप में स्टोर कर सकते हैं और शेष 31 bytes को zeros से pad कर सकते हैं, जैसा कि नीचे दिखाया गया है:
assembly {
mstore(
0x00, 0xff00000000000000000000000000000000000000000000000000000000000000
)
}
यह value को बिल्कुल वैसे ही स्टोर करेगा जैसा आपने निर्दिष्ट किया है, शुरुआत में 0xff और शेष bytes zeros के रूप में:

Remix में कोड का एक टेस्ट रन यहाँ दिया गया है:

ऐसा लगता है कि यह बहुत सारे zeros हैं, है ना? विकल्प के तौर पर, हम किसी विशिष्ट memory location पर एक byte data स्टोर करने के लिए mstore8 का उपयोग कर सकते हैं। नीचे दिए गए उदाहरण में, हमने 0th byte पर 0xff स्टोर करने के लिए mstore8 का उपयोग किया है:

यह बहुत अधिक compact कोड है जो पिछले स्क्रीनशॉट वाले काम को ही करता है, सिवाय इसके कि यह bytes 1 से 31 में zeros नहीं लिखता है।
ध्यान दें कि:
mstoreपूरा 32 bytes data स्टोर करता है, जबकिmstore8निर्दिष्ट memory location पर केवल एक single byte स्टोर करता है।mstoreका उपयोग करते समय, यदि आपका data 32 bytes से कम है, तो 32 bytes को पूरा करने के लिए इसे स्वचालित रूप से बाईं ओर (lower-indexed memory location) zero bytes से pad कर दिया जाएगा। ये zeros उन सभी अन्य memory contents को overwrite कर देंगे जो पहले वहाँ थे।
यदि आप एक ही location पर कई बार लिखते हैं तो आप memory में data को overwrite कर सकते हैं
याद है जब हमने बताया था कि mstore द्वारा left-pad किए गए अतिरिक्त zeros 0th से 30th byte range के भीतर मौजूद किसी भी content को overwrite कर देंगे? आइए देखते हैं कि वह overwrite कैसे होता है।
यदि हम mstore8 का उपयोग करके 0th byte पर 0xCC लिखते हैं:
assembly {
mstore8(0, 0xCC)
}
अब हमारे पास 0th position पर 0xCC होगा, जबकि शेष memory अपरिवर्तित (unchanged) रहेगी, जैसा कि नीचे दिए गए diagram में दर्शाया गया है।

इसके बाद, यदि हम mstore(0, 0xFF) का उपयोग करके 0xFF इस प्रकार स्टोर करते हैं:
assembly {
mstore8(0, 0xCC)
mstore(0, 0xFF)
}
0xFF पहले से स्टोर किए गए 0xCC को 0th byte पर overwrite कर देगा और पूरे 32-byte slot (0th से 31st byte तक) को 0xFF से भर देगा।
याद रखें कि mstore पूरे 32-bytes slot में data लिखेगा और यदि हमारे पास 32 bytes से कम हैं, तो यह शेष bytes को zeros से इस प्रकार pad कर देगा:
0x00000000000000000000000000000000000000000000000000000000000000**FF**
नीचे दिया गया एनीमेशन दिखाता है कि यह overwrite कैसे होता है:
यह दर्शाता है कि mstore के 31 padded zeros वास्तव में memory के contents को बदल देते हैं।
mstore padding को कैसे याद रखें
यह याद रखने के बजाय कि mstore hex values को zero के साथ left-pad करता है, हम यह मान सकते हैं कि mstore(0, 0xff) पूरी तरह से mstore(0, 255) के बराबर है।
दूसरे शब्दों में, mstore(0, 255) कह रहा है “संख्या 255 को byte 0 से शुरू होने वाले 32 bytes में स्टोर करें, जहाँ byte 0 में most significant bytes हों।”
चूँकि 255 एक “छोटी संख्या” है उस संख्या की तुलना में जिसे एक 32 byte नंबर रख सकता है (maximum value of uint256), इसलिए केवल least significant bits का उपयोग किया जाएगा। Least significant bits दाईं ओर होते हैं, लेकिन बाईं ओर के significant bits zero पर सेट हो जाते हैं।
इसी प्रकार, संख्या 0xff00000000000000000000000000000000000000000000000000000000000000 काफी बड़ी है।
Decimal में, यह 115339776388732929035197660848497720713218148788040405586178452820382218977280 है। इसलिए, यह most significant bits का उपयोग करता है, जो बाईं ओर होते हैं।
Revert करने के लिए स्टोर किए गए data का उपयोग करना
हमने देखा है कि mstore के साथ memory में data कैसे स्टोर किया जाता है। एक revert के दौरान, हमें error data को memory में स्टोर करना होता है और इसे revert error message के रूप में return करना होता है।
revert opcode दो arguments लेता है: starting memory slot और उस data का total size जिसे हम return करना चाहते हैं।
revert(startingMemorySlot, totalMemorySize)
यहाँ से आगे हम दिखाएँगे कि निम्नलिखित स्थितियों में Solidity revert के behavior की नकल (mimic) कैसे करें:
- बिना reason string के Reverting
- Custom error के साथ Reverting
- Custom error और reason string के साथ Reverting
1. बिना कारण (मैसेज) के Revert
बिना मैसेज के एक साधारण revert के लिए, assembly कोड revert(0,0) व्यवहार और गैस लागत में Solidity में revert() के बराबर है। यह caller को कोई data return नहीं करता है।
आंतरिक रूप से (Under the hood), revert(0,0) का उपयोग करने का अर्थ है “किसी data का उपयोग न करें” क्योंकि जिस data का उल्लेख किया जा रहा है उसकी लंबाई (length) शून्य है। प्रारंभिक बिंदु (starting point) के रूप में memory location 0 का उपयोग करना पारंपरिक है, लेकिन चूंकि हम कुछ भी return नहीं कर रहे हैं, इसलिए हम revert(1,0) कर सकते हैं और वही परिणाम प्राप्त कर सकते हैं।
Assembly का उपयोग करके बिना कारण (reason) के revert का एक सरल उदाहरण यहाँ दिया गया है:
contract ContractA {
function zero() external {
assembly {
revert(0,0) //<--- simple revert without reason
}
}
}
नीचे दिया गया स्क्रीनशॉट ContractA से ContractB पर एक low-level call दिखाता है और यह दिखाता है कि कैसे low-level call ने false return किया क्योंकि ContractB revert हो गया, और कोई data return नहीं हुआ क्योंकि हम revert(0,0) का उपयोग कर रहे हैं।

2a. बिना parameters के assembly में Custom revert
Assembly का उपयोग करके बिना parameters वाले custom error को simulate करने का तरीका स्पष्ट करने के लिए, आइए उदाहरण के रूप में revert Unauthorized() का उपयोग करें।
हम custom revert के error function selector को memory में एक विशिष्ट स्थान (परंपरागत रूप से 0x00) पर स्टोर करेंगे और revert memory में उस स्थान को point करेगा।
यहाँ Solidity कोड है जिसे हम एक उदाहरण के रूप में उपयोग करेंगे:
contract CustomError {
error Unauthorized();
function revertCustomError() {
revert Unauthorized();
}
}
Assembly में custom revert को पूरा करने के लिए हम नीचे दिए गए चरणों का पालन करेंगे:
- Function selector को memory में स्टोर करें
- Selector की memory location और selector के size (4 bytes) को
revertके arguments के रूप में पास करके revert को trigger करें
assembly {
mstore(memoryLocation, selector)
revert(memoryLocation, sizeOfSelector)
}
1. Function selector को स्टोर करें
जब Solidity किसी custom error को trigger करती है, तो return value स्वयं custom error की ABI encoding होती है, जिसमें function selector (custom error signature के keccak256 hash के पहले चार bytes) शामिल होता है।
चूँकि हम उदाहरण के रूप में custom error Unauthorized() का उपयोग कर रहे हैं, हम पहले function selector (Unauthorized() के keccak256 के पहले चार bytes) को स्टोर करेंगे जो कि 0x82b42900 होगा जिसे अतिरिक्त zeros के साथ pad किया जाएगा ताकि value को 32 bytes तक लंबा किया जा सके, यह सुनिश्चित करने के लिए कि function selector के वास्तविक चार bytes byte 0 से byte 3 तक (inclusive) लिखे गए हैं। इस padding के बिना, selector memory index 0 पर शुरू नहीं होगा।
bytes32 selector = bytes32(abi.encodeWithSignature("Unauthorized()")); // 0x82b42900
assembly {
mstore(0x00, 0x82b4290000000000000000000000000000000000000000000000000000000000)
}
2. Revert को trigger करना
अब हम नीचे दिए गए revert statement के साथ custom error को trigger करेंगे। याद रखें कि revert के लिए टेम्पलेट revert(startingMemorySlot, totalMemorySize) है।
revert(0x00, 0x04)
0x00 वह memory location है जहाँ हमने error data स्टोर किया था, जबकि 0x04 (hex में) error का size है जो केवल 4 bytes है। पूरा revert कोड अब नीचे दिए गए कोड जैसा दिखना चाहिए:
pragma solidity 0.8.27;
contract RevertErrorExample {
function revertWithAssembly() public pure {
assembly {
mstore(
0x00,
0x82b4290000000000000000000000000000000000000000000000000000000000
)
revert(0x0, 0x04)
}
}
}
यहाँ वह कोड है जिसका उपयोग हम अपने assembly implementation की तुलना Solidity implementation से करने के लिए करेंगे:
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
contract RevertErrorExample {
error Unauthorized();
// assembly version
function revertWithAssembly() public pure {
assembly {
mstore(
0x00,
0x82b4290000000000000000000000000000000000000000000000000000000000
)
revert(0x0, 0x04)
}
}
// solidity version
function revertWithoutAssembly() public pure {
revert Unauthorized();
}
}
परिणाम नीचे दिए गए स्क्रीनशॉट में दिखाया गया है। एकमात्र अंतर गैस लागत (gas cost) में है। स्क्रीनशॉट दिखाता है कि हमने Solidity के बजाय Assembly के माध्यम से revert को trigger करके 54 गैस यूनिट्स बचाए हैं।

साथ ही, नीचे दिए गए कोड में, callContractB error को पार्स (parse) करने के लिए customRevertWithAssembly और customRevertWithoutAssembly पर अलग-अलग try/catch का उपयोग करता है, यह दिखाते हुए कि उनका behavior समान है।

जब custom error में कोई parameter न हो तो selector को स्टोर करने और revert trigger करने का एक वैकल्पिक तरीका
जब किसी custom error में कोई parameters नहीं होते हैं, तो function selector ही return करने के लिए एकमात्र प्रासंगिक (relevant) data होता है। उस स्थिति में, हम अतिरिक्त zeros को manually जोड़े बिना function selector को स्टोर कर सकते हैं और इसे उस विशिष्ट memory region से revert कर सकते हैं जिसे हमने स्टोर किया था।
उदाहरण के लिए, इस तरह zeros के साथ padding करने के बजाय:
assembly{
mstore(
0x00,
0x82b4290000000000000000000000000000000000000000000000000000000000
)
}
हम इसे manually zeros के साथ pad किए बिना लिख सकते हैं:
assembly{
mstore(0x00,0x82b42900)
}
Memory में, zeros को function selector के बाईं ओर 0th byte से 27th byte तक जोड़ा जाएगा, जबकि वास्तविक selector 28th byte से 31st तक स्टोर किया जाएगा।
दूसरे शब्दों में 0x82b42900 विस्तृत (expand) होकर 0000000000000000000000000000000000000000000000000000000082b42900 हो जाता है और bytes 0 से 31 में स्टोर हो जाता है जैसा कि नीचे दिखाया गया है:

चूँकि function selector अब 28वें byte (hex में 0x1c) पर है, आप नीचे दिखाए गए अनुसार 0x00 के बजाय इस location से revert कर सकते हैं:
assembly {
mstore(0x00,0x82b42900)
revert(0x1c, 0x04)
}
2b. Parameters के साथ assembly में Custom revert
यदि custom error में parameters हैं, तो हमें arguments को भी ABI encode करना होगा क्योंकि यह revert return data का हिस्सा होगा। यह मानते हुए कि इसके argument के रूप में एक address है, हम argument को memory में स्टोर करेंगे और revert arguments को selector और memory में address दोनों पर point करेंगे।
एक उदाहरण के रूप में, आइए assembly में custom revert Unauthorized(address) को replicate करें।
contract CustomError {
error Unauthorized(address caller);
function revertCustomError() {
revert Unauthorized(msg.sender);
}
}
Assembly में arguments के साथ custom revert को replicate करने के चरण बिना arguments वाले के समान ही हैं, एकमात्र अंतर यह है कि हमें arguments (इस मामले में, address) को return data के हिस्से के रूप में स्टोर करना होगा। हम नीचे दिए गए चरणों का पालन करेंगे:
- Custom error के लिए function selector को memory में स्टोर करें
- Selector के बाद argument को memory में स्टोर करें
- Starting memory location और total size (selector के लिए 4 bytes + argument size) को
revertफ़ंक्शन में पास करके revert को trigger करें
1. Custom error के लिए function selector को memory में स्टोर करें
बिना parameters वाले custom error की तरह ही, हमें सबसे पहले function selector को इस तरह प्राप्त (derive) करना होगा:
bytes4 selector = bytes4(abi.encodeWithSignature("Unauthorized(address)")
);
और selector 0x8e4a23d6 होगा। अब हम नीचे दिखाए गए अनुसार mstore के साथ 0x00 memory location से शुरू करके selector को स्टोर करने के लिए आगे बढ़ेंगे:
assembly{
// Store the function selector at the memoryy location `0x00`
mstore(0x00, 0x8e4a23d600000000000000000000000000000000000000000000000000000000)
}
2. Selector के बाद argument को memory में स्टोर करें
0th byte से शुरू करके function selector को memory में लिखने के बाद, अब हम 4th byte से address स्टोर करेंगे जैसा कि नीचे दिखाया गया है:
assembly{
//...
// Store the address
// *Note that `caller()` in assembly is the same as `msg.sender` in Solidity.*
mstore(0x04, caller())
}
फ़ंक्शन caller() 32 bytes तक upcasted address return करेगा। इसलिए यदि मूल address 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 था, तो caller() 0x00000000000000000000000005B38Da6a701c568545dCfcB03FcB875f56beddC4 return करेगा और यह वह 32-byte value है जिसे mstore byte 4 से शुरू करके memory में रखेगा।
3. Revert को trigger करें
और अंततः, अब हम starting memory location और अब तक स्टोर किए गए data के total size (selector के लिए 4 bytes + address के लिए 32 bytes) जो 36 bytes (hex 0x24) घेरता है, को arguments के रूप में पास करके revert trigger कर सकते हैं जैसा कि नीचे दिखाया गया है:
function customRevertWithAssembly() public pure {
assembly {
//...
// 4 bytes for selector + 32 bytes for the address
revert(0x00, 0x24)
}
}
और assembly में parameters के साथ custom revert के लिए पूरा कोड कुछ इस तरह दिखेगा:
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.27;
contract A {
function customRevertWithAssembly() public view {
assembly {
// Store the function selector at the memory location `0x00`
mstore(0x00, 0x8e4a23d600000000000000000000000000000000000000000000000000000000)
// Store the address
// N*ote that `caller()` in assembly is the same as `msg.sender` in Solidity.*
mstore(0x04, caller())
// 4 bytes for selector + 32 bytes for the address
revert(0x00, 0x24)
}
}
}
इस तरह से Solidity revert data को memory में स्टोर करेगी और अंततः revert data का परिणाम return किया जाएगा।
3. Assembly में reason के साथ Revert
जब revert("reason") जैसे reason string के साथ कोई revert trigger किया जाता है, तो reverting कॉन्ट्रैक्ट Error(string) की ABI encoding, string argument के साथ return करता है। यह उसी तरह है जैसे Solidity में reason के साथ require काम करता है।
Assembly में reason string के साथ revert को simulate करने के लिए, हमें उसी फ़ंक्शन और string argument को memory में ABI encode करना होगा।
आइए एक उदाहरण के रूप में revert("Unauthorized") का उपयोग करें:
contract A {
function revertWithAString() external pure {
revert("Unauthorized");
}
}
यदि हम उपरोक्त कॉन्ट्रैक्ट में revert("Unauthorized"); फ़ंक्शन को trigger करते हैं, तो परिणाम नीचे दिए गए उदाहरण जैसा दिखेगा।

इस अनुभाग (section) में, हम नीचे दिए गए चरणों का पालन करके assembly का उपयोग करके Solidity में string के साथ revert वाले behavior को replicate करेंगे:
Error(string)के लिए function selector को memory में स्टोर करें- Error message string के offset को स्टोर करें
- Error message string की length को स्टोर करें
- वास्तविक (actual) error message को स्टोर करें
- Revert को trigger करें
नीचे assembly कोड में उपरोक्त चरणों का एक त्वरित प्रतिनिधित्व (representation) दिया गया है:
contract RevertErrorExample {
function revertWithAssembly() public pure {
assembly {
// store the selector
mstore(
0x00,
0x08c379a000000000000000000000000000000000000000000000000000000000
)
mstore(0x04, 0x20) // store the offset
mstore(0x24, 0xc) // store the length of the string
mstore(
0x44,
0x556E617574686F72697A65640000000000000000000000000000000000000000
) // store the actual data
revert(0x00, 0x64) // trigger a revert
}
}
}
आइए assembly block की लाइन-बाय-लाइन जांच करें।
1. Error(string) के लिए function selector को स्टोर करें
हम सबसे पहले starting memory location (0x00) पर function selector को स्टोर करते हैं:
assembly {
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) //Store the function selector
}
आप function selector (keccak256(Error(string)) के पहले 4 bytes) को abi.encodeWithSignature("Error(string)") के साथ प्राप्त कर सकते हैं और फिर इसे इस तरह bytes32 type के साथ 32-byte word में कास्ट कर सकते हैं:
bytes32 selector = bytes32(abi.encodeWithSignature("Error(string)"));
परिणाम होगा:
0x08c379a000000000000000000000000000000000000000000000000000000000
पहले 4 bytes (0x08c379a0) selector है जिसे 32-bytes की आवश्यकता को पूरा करने के लिए zeros के साथ pad किया गया है।

2. Error message string के offset को स्टोर करें
String error का अगला हिस्सा जिसे हम स्टोर करते हैं वह offset है। Offset 32 bytes (hex में 0x20) है।
mstore(0x04, 0x20) // 4 is 0x04 in hex
याद रखें, हमने उल्लेख किया था कि यदि दो memory locations overlap होते हैं तो memory को overwrite करना संभव है, है ना? प्रारंभ में, function selector को 0th byte से शुरू करके 32-byte word के रूप में स्टोर किया गया था। अब, हम 4th byte से शुरू करके offset स्टोर कर रहे हैं।
इसका मतलब है कि function selector का शेष data (इस मामले में padded zeros) 4th byte से शुरू होकर replace हो जाएगा, जैसा कि नीचे दिए गए diagram में दिखाया गया है:

3. Error message string की length स्टोर करें
String का तीसरा हिस्सा जिसे हमें स्टोर करने की आवश्यकता है वह string data की length है। याद करें कि हमने function selector को 0x00 location पर स्टोर किया था और इसमें 4 bytes लगे थे। फिर अगली memory location 0x04 पर offset थी जिसने 32 bytes लिए थे।
इसका मतलब है 4 bytes selector + 32 bytes offset हमें बताता है कि अगला memory slot 36 bytes पर होना चाहिए जहाँ हम string की length स्टोर करेंगे।
Unauthorized string की length 12 (0xc) bytes है।
mstore(0x24, 0xc) // 36 is 0x24 in hex

4. वास्तविक (actual) error message string स्टोर करें
वास्तविक (actual) string Unauthorized शुरुआत से 68 (0x44) bytes पर स्टोर की जाती है जो selector के लिए 4 bytes + 32 bytes offset + length के लिए 32 bytes से मेल खाती है। अब तक, हमने 100 bytes data लिखा है।
mstore(0x44, "Unauthorized") //68 is 0x44 in hex
// We can store Unathorized as hex as well. Unauthorized in hex is ⤵️
// 0x556E617574686F72697A65640000000000000000000000000000000000000000

5. Revert को trigger करें:
Revert operation revert को trigger करने के लिए starting memory location और data के total size का उपयोग करता है।
Selector के लिए 4 bytes, offset के लिए 32 bytes, string की length के लिए 32 bytes और string content “Unauthorized” के लिए 32 bytes जोड़ने पर total size 100 (0x64) bytes होगा।
Assembly में revert के लिए टेम्पलेट याद रखें:
revert(StartingMemorySlot, totalMemorySize)
इस तरह से हम revert को trigger करेंगे:
revert(0x00, 0x64) // 100 is 0x64 in hex
तो, revert trigger होने पर बिल्कुल 100 bytes के साथ निम्नलिखित data return करेगा:

भले ही string Unauthorized पूरे 32 bytes का उपयोग नहीं करती है, length parameter 0x0c के कारण receiver जान जाएगा कि केवल 12 bytes data ही पढ़ना है।
यदि हम सभी चरणों को एक साथ रखें, तो हम इस कोड पर पहुँचेंगे:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ContractA {
function revertWithAssembly() external pure {
assembly {
mstore(
0x00,
0x08c379a000000000000000000000000000000000000000000000000000000000
) // store the selector
mstore(0x04, 0x20) // store the offset
mstore(0x24, 0xc) // store the length of the string
mstore(
0x44,
0x556E617574686F72697A65640000000000000000000000000000000000000000
) // store the actual data
revert(0x00, 0x64) // trigger a revert
}
}
}
}
यहाँ एक स्क्रीनशॉट दिया गया है जो revertWithAssembly() फ़ंक्शन को कॉल करने पर revert का आउटपुट दिखाता है। परिणाम वैसा ही है जैसा हमने Solidity में revert("Unauthorized") trigger करते समय देखा था।

हालाँकि, अंतर उन दोनों द्वारा consume की जाने वाली गैस की मात्रा में है। गैस लागत में अंतर देखने के लिए निम्नलिखित कॉन्ट्रैक्ट्स में reverts रन करें। गैस लागत (gas costs) का टेस्ट करने के लिए हम जिस कोड का उपयोग करते हैं वह नीचे दिया गया है:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ContractA {
function revertWithAssembly() external pure {
assembly {
mstore(
0x00,
0x08c379a000000000000000000000000000000000000000000000000000000000
) // store the selector
mstore(0x04, 0x20) // store the offset
mstore(0x24, 0xc) // store the length of the string
mstore(
0x44,
0x556E617574686F72697A65640000000000000000000000000000000000000000
) // store the actual data
revert(0x00, 0x64) // trigger a revert
}
}
}
contract ContractB {
function revertWithoutAssembly() external pure {
revert("Unauthorized");
}
}
नीचे दिया गया चित्र revertWithAssembly फ़ंक्शन और revertWithoutAssembly के लिए गैस लागत में अंतर दिखाता है।

उपरोक्त टेस्ट से, हमने 273 gas बचाई क्योंकि बिना assembly वाले revert की लागत 428 gas थी जबकि assembly के साथ revert की लागत 155 gas थी। अंतर 273 का है।
यह आगे सत्यापित (verify) करने के लिए कि error ठीक से बना था, हम नीचे दिए गए स्क्रीनशॉट में दिखाए अनुसार try/catch ब्लॉक में error को कैच (catch) करने का प्रयास कर सकते हैं:

उपरोक्त स्क्रीनशॉट से, हम देख सकते हैं कि उम्मीद के मुताबिक error Error catch ब्लॉक में कैच किया गया था और कारण Unauthorized प्रिंट किया गया था।
निष्कर्ष (Conclusion)
इस गाइड में, हमने inline assembly का उपयोग करके Solidity reverts को manually implement करके यह सीखा है कि revert कैसे काम करता है।
हमने कवर किया:
mstoreऔरmstore8कैसे काम करते हैं- निम्नलिखित प्रकार के reverts की नकल (mimic) कैसे करें:
- बिना कारणों (reasons) के reverts
- custom error reverts
- और reason के साथ reverts
हमने यह भी देखा कि assembly के माध्यम से revert का उपयोग करके हम कैसे कुछ गैस बचा सकते हैं। मैं आपको खुद इसका प्रयोग (experiment) करने के लिए प्रोत्साहित करता हूँ क्योंकि यह पूरी तरह से समझने का सबसे अच्छा तरीका है कि यह सब एक साथ कैसे काम करता है।
हैप्पी कोडिंग (Happy coding)
यह आर्टिकल Eze Sunday द्वारा RareSkills के सहयोग से लिखा गया था।