परिचय (Introduction)
इस लेख का उद्देश्य Solidity gasleft() फ़ंक्शन के व्यवहार और इसके उपयोगों का वर्णन करना है।
यह एक बिल्ट-इन फ़ंक्शन है जिसका उपयोग कॉन्ट्रैक्ट कॉल के दौरान शेष गैस की जांच करने के लिए किया जाता है। यह उन विशेष वेरिएबल्स और फ़ंक्शन्स में से एक है जो हमेशा ग्लोबल नेमस्पेस में मौजूद होते हैं और इसलिए इसे इम्पोर्ट करने की आवश्यकता नहीं होती है। Solidity संस्करण 0.4.21 से पहले gasleft() को msg.gas के रूप में जाना जाता था (msg.gas)।
लेखकत्व
यह लेख RareSkills Technical Writing Program के हिस्से के रूप में Jesse Raymond (LinkedIn, Twitter) द्वारा सह-लिखा गया था।
gasleft() क्यों मायने रखता है
स्मार्ट कॉन्ट्रैक्ट्स द्वारा उपयोग की जाने वाली गैस की मात्रा रन किए जा रहे कोड की जटिलता और कॉन्ट्रैक्ट कॉल के दौरान प्रोसेस किए जा रहे डेटा की मात्रा पर निर्भर करती है।
यदि प्रदान की गई गैस पर्याप्त नहीं है, तो ट्रांज़ैक्शन “out of gas” त्रुटि (error) के साथ रिवर्ट (revert) हो जाता है। gasleft() फ़ंक्शन का उचित उपयोग उन स्थितियों को रोक सकता है जहां कॉन्ट्रैक्ट ट्रांज़ैक्शन में गैस खत्म हो जाती है। आइए अगले भाग में एक उदाहरण देखें।
out-of-gas त्रुटि (error) को रोकने का उदाहरण
Ether वितरित करते समय गैस खत्म न होना
लूप के माध्यम से स्मार्ट कॉन्ट्रैक्ट्स में कई एड्रेस (addresses) पर Ether भेजना बहुत महंगा हो सकता है, खासकर जब एड्रेस के एक बड़े ऐरे (array) से निपटना हो।
जैसा कि पहले बताया गया है, यदि ट्रांज़ैक्शन को एक्ज़ीक्यूट करने के लिए उपयोग की गई गैस की मात्रा पर्याप्त नहीं है, तो फ़ंक्शन “out of gas” त्रुटि के साथ विफल हो जाएगा।
हालांकि, gasleft() फ़ंक्शन का उपयोग यह सुनिश्चित करने के लिए किया जा सकता है कि शेष गैस अगले ट्रांसफर को करने के लिए पर्याप्त है, और अन्यथा जल्दी बाहर (exit early) निकल जाएं।
निम्नलिखित कोड इसे प्रदर्शित करता है
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract GasConsumer{
uint constant MINIMUM_AMOUNT = 10_000;
// this is for illustration purposes only, not production
function distributeEther(address[] calldata receivers) external {
for (uint i = 0; i < receivers.length; i++) {
payable(receivers[i]).transfer(1 ether);
if (gasleft() < MINIMUM_AMOUNT) {
return;
}
}
}
receive() external payable {}
}
उपरोक्त कॉन्ट्रैक्ट में “distributeEther” फ़ंक्शन एड्रेस का एक ऐरे लेता है, for लूप का उपयोग करके ऐरे को इटरेट (iterate) करता है, और “transfer” फ़ंक्शन के साथ प्रत्येक एड्रेस पर 1 Ether भेजता है।
“if statement” यह जांचता है कि लूप में प्रत्येक Ether ट्रांसफर के बाद शेष गैस अगले ट्रांसफर के लिए पर्याप्त है या नहीं। यह जांचा जाता है कि क्या ट्रांसफर के बाद “gas left” 10,000 से कम है (ETH ट्रांसफर करने के लिए 9,000 और अन्य छोटे opcodes के लिए अतिरिक्त 1,000)। यदि यह कम है, तो ट्रांज़ैक्शन पिछले ट्रांसफर को रिवर्ट किए बिना समाप्त हो जाता है।
(ध्यान दें कि जब तक एड्रेस विश्वसनीय न हों, Ether को पुश आउट (push out) करना एक अच्छा विचार नहीं है। एक दुर्भावनापूर्ण रिसीवर (malicious receiver) ऐसे स्मार्ट कॉन्ट्रैक्ट का एड्रेस सबमिट कर सकता है जो Ether प्राप्त करते समय रिवर्ट हो जाता है)।
बेंचमार्किंग कोड (Benchmarking code)
एक्ज़ीक्यूशन कॉस्ट (execution cost) मापने के लिए gasleft() फ़ंक्शन का उपयोग करना
एक अन्य उदाहरण कोड के एक सेक्शन द्वारा उपयोग की गई गैस की कुल मात्रा को मापने के लिए gasleft() फ़ंक्शन का उपयोग करना है।
यहाँ Remix में एक उदाहरण दिया गया है:

gasleft के साथ solidity कोड को बेंचमार्क करना
इस मामले में, gasleft() फ़ंक्शन का उपयोग यह पता लगाने के लिए किया जाता है कि जब “updateArray” फ़ंक्शन का उपयोग करके “numArr” ऐरे में कोई संख्या (number) जोड़ी जाती है तो कितनी गैस का उपयोग किया जा रहा है। यह फ़ंक्शन द्वारा उपयोग की गई गैस की कुल मात्रा नहीं है, बल्कि लाइन 30 पर कोड numArr.push(_num) में ऐरे में संख्या जोड़े जाने से पहले और बाद में उपयोग की गई गैस की मात्रा है।
हाइलाइट किए गए नंबरों को समझाना
हमने “gasUsed” को एक पब्लिक वेरिएबल के रूप में सेट किया है, ताकि हम फ़ंक्शन के एक्ज़ीक्यूट होने के बाद कंटेंट (contents) को आसानी से देख सकें। इसके लिए एक परीक्षण (test) “GasCalc” कॉन्ट्रैक्ट को डिप्लॉय करके और “updateArray” फ़ंक्शन को कॉल करके किया जाता है।
फ़ंक्शन एक टुपल (tuple) लौटाता है, जिसमें पहली प्रविष्टि (entry) का परिणाम 80,348 होता है, जो कि “initialGas” है।
टुपल में दूसरी प्रविष्टि “finalGas” है और यह 35,923 थी, इसमें स्वयं gasleft() फ़ंक्शन के लिए गैस कॉस्ट शामिल है।
initial gas में से final gas को घटाकर, हम यह निर्धारित करते हैं कि लाइन 30 की एक्ज़ीक्यूशन कॉस्ट 44,425 है।
gasleft के पीछे के opcode को एक्ज़ीक्यूट करने के लिए “2 gas” की आवश्यकता होती है
Solidity एक हाई-लेवल भाषा है जिसे बाइटकोड (bytecodes) में संकलित (compiled) किया जाता है जिन्हें Ethereum Virtual Machine (EVM) पर एक्ज़ीक्यूट किया जाता है।
gasleft() फ़ंक्शन के लिए opcode GAS (बाइटकोड 0x5A) है, जिसकी कॉस्ट ethereum documentation के अनुसार “2 gas” है।
रियल-वर्ल्ड एप्लीकेशन (Real-World Applications)
OpenZeppelin proxy – इम्प्लीमेंटेशन कॉन्ट्रैक्ट (implementation contract) को सभी गैस फॉरवर्ड करने के लिए उपयोग किया जाता है
yul में gasleft का उपयोग
Solidity स्मार्ट कॉन्ट्रैक्ट्स में yul (इनलाइन असेंबली) के माध्यम से gasleft() फ़ंक्शन को gas() के रूप में भी एक्सेस किया जा सकता है।
OpenZeppelin proxy कॉन्ट्रैक्ट इसका एक उत्कृष्ट उदाहरण है कि इसे कैसे किया जाता है। इसका उपयोग “delegatecall” फ़ंक्शन में किया जाता है जिसका उपयोग proxy इम्प्लीमेंटेशन कॉन्ट्रैक्ट को कॉल करने के लिए करता है। gas() इस ऑपरेशन के लिए अधिकतम उपलब्ध गैस का उपयोग निर्दिष्ट करने का एक सुविधाजनक तरीका है।

gasleft Solidity के साथ delegate call में सभी गैस फॉरवर्ड करें
लिंक: OpenZeppelin Proxy Contract
OpenZeppelin Minimal Forwarder – यह वैलिडेट (validate) करने के लिए उपयोग किया जाता है कि relayer ने ट्रांज़ैक्शन को एक्ज़ीक्यूट करने के लिए पर्याप्त गैस भेजी है
एक “Relayer” एक ऑफ-चेन (off-chain) एंटिटी है जो किसी अन्य उपयोगकर्ता के ट्रांज़ैक्शन की गैस के लिए भुगतान करती है और ट्रांज़ैक्शन को “Forwarder” कॉन्ट्रैक्ट में भेजा जाता है जो ट्रांज़ैक्शन को एक्ज़ीक्यूट करता है।
जब कोई उपयोगकर्ता relayer को कोई अनुरोध (request) भेजता है, तो उपयोगकर्ता ट्रांज़ैक्शन में शामिल की जाने वाली गैस की मात्रा निर्दिष्ट करता है, और अपने अनुरोध पर डिजिटल रूप से हस्ताक्षर (digitally signs) करता है।
हालांकि, relayer उपयोगकर्ता द्वारा अनुरोधित गैस लिमिट (gas limit) का सम्मान नहीं कर सकता है, और कम मात्रा भेज सकता है। इस हमले (attack) को SWC Registry में SWC-126 के रूप में डॉक्यूमेंट किया गया है।
यह एक gas-griefing हमले का कारण बनता है। यदि forwarding कॉन्ट्रैक्ट में relayer का कॉल सफल होता है, लेकिन उपयोगकर्ता जिस सब-कॉल (sub call) को चाहता है वह विफल हो जाता है, तो relayer एक ऐसा ट्रांज़ैक्शन भेजने के लिए उपयोगकर्ता को “दोष (blame)” दे सकता है जो रिवर्ट हो जाता है, जबकि वास्तविक कारण यह था कि relayer द्वारा पर्याप्त गैस नहीं भेजने के कारण सब-कॉल में गैस खत्म हो गई थी।
एक सब-कॉल रिवर्ट या out-of-gas त्रुटि के कारण विफल हो सकता है, लेकिन विफलता का कारण आमतौर पर नहीं दिया जाता है, केवल बूलियन (boolean) success वेरिएबल false लौटाता है। इसके कारण, हमें नहीं पता होता है कि सब-कॉल अपर्याप्त गैस के कारण विफल हुआ या मूल प्रेषक (original sender) के खराब निर्देशों के कारण।
हम यह निर्धारित करने के लिए “gasleft” का उपयोग कर सकते हैं कि यह कौन सा मामला है।
जब “call” का एक्ज़ीक्यूशन होता है, तो केवल 63/64 गैस फॉरवर्ड की जाती है। यह 63/64 लिमिट EIP 150 में पेश की गई थी।
सब-कॉल पूरा होने के बाद, शेष गैस की मात्रा उपयोगकर्ता द्वारा निर्दिष्ट मूल लिमिट की कम से कम 1/64 होनी चाहिए।
यदि सब-कॉल के बाद अनुरोधित गैस का 1/64 से कम बचता है, तो हम जानते हैं कि relayer ने वह सारी गैस नहीं भेजी जो उन्हें भेजनी चाहिए थी।
यहां forwarding कॉन्ट्रैक्ट जांचता है कि क्या सुरक्षा के मार्जिन (margin of safety) के रूप में मूल लिमिट का कम से कम 1/63 शेष है।
इनवैलिड (invalid) कोड का उपयोग relayer के ट्रांज़ैक्शन को विफल करने के लिए किया जाता है ताकि यह स्पष्ट हो सके कि ट्रांज़ैक्शन relayer के कारण विफल हुआ, न कि सब-कॉल के कारण। आप gas griefing हमले के बारे में यहाँ अधिक पढ़ सकते हैं।

लिंक: OpenZeppelin Minimal Forwarder Contract
Chainlink EthBalance Monitor Contract – out-of-gas त्रुटि को Ether वितरण (distribution) को रोकने से बचाने के लिए उपयोग किया जाता है
यह इस लेख के पहले उदाहरण का एक रियल-लाइफ एप्लीकेशन है, जहां हमने लूप में Ether वितरित किया था। पहले की तुलना में इस कोड में अधिक बिजनेस लॉजिक है, लेकिन यदि हम “gasleft()” जांच (check) को हाइलाइट करें जो लूप से जल्दी बाहर निकलने का कारण बनता है, तो हम देखते हैं कि यह मूल रूप से समान डिज़ाइन है।

Chainlink VRFCoordinatorV2 Contract – Chainlink VRFCoordinatorV2 फुलफिलमेंट (fulfillments) के लिए उपयोग की गई गैस की मात्रा प्राप्त करने के लिए उपयोग किया जाता है
Chainlink “VRFCoordinatorV2” स्मार्ट कॉन्ट्रैक्ट एक “Verifiable Random Function” समन्वयक (coordinator) है, जिसका उपयोग ब्लॉकचेन पर सुरक्षित रैंडम नंबर (secure random numbers on the blockchain) जनरेट करने के लिए क्रिप्टोग्राफिक रूप से किया जाता है।
स्मार्ट कॉन्ट्रैक्ट रैंडमनेस (randomness) का अनुरोध करने और प्राप्त करने के लिए स्मार्ट कॉन्ट्रैक्ट के लिए ऑरेकल (oracle) है। (यहां देखें: VRFCoordinatorV2)।
उपयोगकर्ता से अधिक शुल्क (fee) लेने के लिए कॉन्ट्रैक्ट के “calculatePaymentAmount” फ़ंक्शन में gasleft() फ़ंक्शन का उपयोग किया जाता है यदि नोड्स (nodes) को रैंडमनेस को पूरा करने के लिए अधिक गैस का भुगतान करने की आवश्यकता होती है।
इस परिस्थिति में gasleft() जितना कम होगा, शुल्क (fee) उतना ही अधिक होगा क्योंकि अधिक गैस की खपत होने पर (startGas - gasleft()) बढ़ जाता है।

लिंक: Chainlink VRFCoordinatorV2 Contract
निष्कर्ष (Conclusion)
इस लेख में, हमने gasleft() के लिए विभिन्न उपयोग मामलों (use cases) पर चर्चा की है। इनमें out-of-gas त्रुटियों (errors) को रोकना, Solidity कोड एक्ज़ीक्यूशन कॉस्ट को बेंचमार्क करना, इम्प्लीमेंटेशन कॉन्ट्रैक्ट्स को सभी गैस फॉरवर्ड करना, और relayer DOS को रोकना शामिल है।
RareSkills Blockchain Bootcamp
हमारे द्वारा दी जाने वाली विशेषज्ञ-स्तर (expert-level) की डेवलपर ट्रेनिंग के बारे में अधिक जानने के लिए कृपया हमारी एडवांस blockchain bootcamp पेशकशों को देखें।
मूल रूप से 4 फरवरी, 2023 को प्रकाशित