सॉलिडिटी इवेंट एथेरियम में "प्रिंट" या "कंसोल.लॉग" स्टेटमेंट के सबसे करीब हैं। हम बताएंगे कि वे कैसे काम करते हैं और उनका उपयोग कब करना है। हम अन्य संसाधनों में अक्सर छोड़े गए कई तकनीकी विवरणों में भी जाएंगे।
सॉलिडिटी इवेंट उत्सर्जित करने के लिए यहां एक न्यूनतम उदाहरण दिया गया है।
contract ExampleContract { // We will explain the significance of the indexed parameter later. event ExampleEvent(address indexed sender, uint256 someValue); function exampleFunction(uint256 someValue) public { emit ExampleEvent(sender, someValue); } }
शायद सबसे प्रसिद्ध घटनाएँ ERC20 टोकन द्वारा उत्सर्जित होती हैं जब उन्हें स्थानांतरित किया जाता है। एक घटना में प्रेषक, प्राप्तकर्ता और राशि दर्ज की जाती है।
emit Transfer(from, to, amount);
क्या यह बेमानी नहीं है? स्थानान्तरण देखने के लिए हम पहले से ही पिछले लेन-देन देख सकते हैं, और फिर हम उसी जानकारी को देखने के लिए लेन-देन कॉलडेटा में देख सकते हैं।
यह सही है, कोई घटनाओं को हटा सकता है और स्मार्ट अनुबंध के व्यावसायिक तर्क पर इसका कोई प्रभाव नहीं पड़ता है। हालाँकि, यह इतिहास को देखने का एक कुशल तरीका नहीं होगा।
एथेरियम क्लाइंट के पास "प्रकार" द्वारा लेनदेन सूचीबद्ध करने के लिए एपीआई नहीं है, उदाहरण के लिए "ईआरसी20 के सभी हस्तांतरण लेनदेन।" यदि आप लेनदेन पूछना चाहते हैं तो यहां आपके विकल्प हैं:
getTransaction
getTransactionFromBlock
getTransactionFromBlock
एपीआई आपको केवल यह बता सकता है कि किसी विशेष ब्लॉक पर क्या लेन-देन हुआ है, यह एक स्मार्ट अनुबंध पते को कई ब्लॉकों को लक्षित नहीं कर सकता है।
getTransaction
केवल उन लेन-देन का निरीक्षण कर सकता है जिनके लिए आप लेन-देन हैश जानते हैं।
दूसरी ओर घटनाओं को और अधिक आसानी से प्राप्त किया जा सकता है।
यहाँ एथेरियम ग्राहक विकल्प हैं:
आयोजन
इवेंट्स.ऑलइवेंट्स
getPastEvents
इनमें से प्रत्येक को उस स्मार्ट अनुबंध के पते को निर्दिष्ट करने की आवश्यकता होती है जिसे प्रश्नकर्ता निर्दिष्ट क्वेरी पैरामीटर के अनुसार उत्सर्जित स्मार्ट अनुबंध की घटनाओं का एक सबसेट (या सभी) जांचना और वापस करना चाहता है।
लेन-देन को ट्रैक करने के बजाय आप लेन-देन को ट्रैक करने के लिए घटनाओं का उपयोग क्यों करेंगे, इसके लिए यहां महत्वपूर्ण अंतर्दृष्टि है: एथेरियम एक स्मार्ट अनुबंध के लिए सभी लेनदेन प्राप्त करने के लिए एक तंत्र प्रदान नहीं करता है, लेकिन यह एक स्मार्ट अनुबंध से सभी घटनाओं को प्राप्त करने के लिए एक तंत्र प्रदान करता है।
ऐसा क्यों है? ईवेंट को शीघ्रता से पुनर्प्राप्त करने योग्य बनाने के लिए अतिरिक्त संग्रहण ओवरहेड की आवश्यकता होती है। अगर इथेरियम ने हर लेनदेन के लिए ऐसा किया, तो यह श्रृंखला को काफी बड़ा बना देगा। घटनाओं के साथ, सॉलिडिटी प्रोग्रामर इस बारे में चयनात्मक हो सकते हैं कि त्वरित ऑफ-चेन पुनर्प्राप्ति को सक्षम करने के लिए अतिरिक्त स्टोरेज ओवरहेड का भुगतान किस प्रकार की जानकारी के लायक है।
ऊपर वर्णित एपीआई का उपयोग करने का एक उदाहरण यहां दिया गया है। इस कोड में, ग्राहक स्मार्ट अनुबंध से घटनाओं की सदस्यता लेता है। ये सभी उदाहरण जावास्क्रिप्ट में हैं।
यह कोड हर बार एक कॉलबैक ट्रिगर करता है जब एक ERC20 टोकन ट्रांसफर इवेंट का उत्सर्जन करता है।
const { ethers } = require("ethers"); // const provider = your provider const abi = [ "event Transfer(address indexed from, address indexed to, uint256 value)" ]; const tokenAddress = "0x..."; const contract = new ethers.Contract(tokenAddress, abi, provider); contract.on("Transfer", (from, to, value, event) => { console.log(`Transfer event detected: from=${from}, to=${to}, value=${value}`); });
यदि हम घटनाओं को पूर्वव्यापी रूप से देखना चाहते हैं, तो हम निम्नलिखित कोड का उपयोग कर सकते हैं। इस उदाहरण में, हम ERC20 टोकन में अनुमोदन लेनदेन के लिए अतीत को देखते हैं।
const ethers = require('ethers'); const tokenAddress = '0x...'; const filterAddress = '0x...'; const tokenAbi = [ { "anonymous": false, "inputs": [ { "indexed": true, "name": "from", "type": "address" }, { "indexed": true, "name": "to", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" } ]; const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, provider); // this line filters for Approvals for a particular address. const filter = tokenContract.filters.Approval(filterAddress, null, null); tokenContract.queryFilter(filter).then((events) => { console.log(events); });
यदि आप दो विशेष ज्ञात पतों (यदि ऐसा कोई लेन-देन मौजूद है) के बीच व्यापार की तलाश करना चाहते हैं, तो ethers.js. कोड इस प्रकार होगा:
tokenContract.filters.Transfer(address1, address2, null);
यहाँ ethers.js के बजाय web3.js में एक समान उदाहरण दिया गया है। ध्यान दें कि fromBlock
और toBlock
क्वेरी पैरामीटर जोड़े गए हैं (यह इंगित करने के लिए कि हम केवल इन ब्लॉकों के बीच की घटनाओं की परवाह करते हैं), और हम प्रेषक के रूप में कई पतों को सुनने की क्षमता प्रदर्शित करेंगे। पतों को "OR" शर्त के साथ जोड़ दिया जाता है।
const Web3 = require('web3'); const web3 = new Web3('https://rpc-endpoint'); const contractAddress = '0x...'; // The address of the ERC20 contract const contractAbi = [ { "anonymous": false, "inputs": [ { "indexed": true, "name": "from", "type": "address" }, { "indexed": true, "name": "to", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" } ]; const contract = new web3.eth.Contract(contractAbi, contractAddress); const senderAddressesToWatch = ['0x...', '0x...', '0x...']; // The addresses to watch for transfers from const filter = { fromBlock: 0, toBlock: 'latest', topics: [ web3.utils.sha3('Transfer(address,address,uint256)'), null, senderAddressesToWatch, ] }; contract.getPastEvents('Transfer', { filter: filter, fromBlock: 0, toBlock: 'latest', }, (error, events) => { if (!error) { console.log(events); } });
उपरोक्त उदाहरण काम करता है क्योंकि ERC20 में स्वीकृत (और स्थानांतरण) घटना प्रेषक को अनुक्रमित करने के लिए सेट करती है। यहाँ सॉलिडिटी में ERC20 अप्रूवल इवेंट डिक्लेरेशन है।
event Approval(address indexed owner, address indexed spender, uint256 value);
यदि "मालिक" तर्क को अनुक्रमित नहीं किया गया था, तो पहले का जावास्क्रिप्ट कोड चुपचाप विफल हो जाएगा। यहाँ निहितार्थ यह है कि आप ERC20 ईवेंट के लिए फ़िल्टर नहीं कर सकते हैं जिनके पास स्थानांतरण के लिए एक विशिष्ट value
(टोकन राशि) है, क्योंकि वह अनुक्रमित नहीं है। आपको सभी घटनाओं को खींचना होगा और उन्हें जावास्क्रिप्ट पक्ष में फ़िल्टर करना होगा; यह एथेरियम क्लाइंट में नहीं किया जा सकता है।
एक घटना घोषणा के लिए अनुक्रमित तर्क को एक विषय कहा जाता है।
घटनाओं के लिए आम तौर पर स्वीकृत सर्वोत्तम अभ्यास उन्हें लॉग करना है जब भी कोई संभावित परिणामी स्थिति परिवर्तन होता है। कुछ उदाहरणों में शामिल हैं:
अनुबंध के मालिक को बदलना
गतिमान ईथर
व्यापार करना
प्रत्येक राज्य परिवर्तन के लिए एक घटना की आवश्यकता नहीं होती है। सॉलिडिटी डेवलपर्स को खुद से यह सवाल पूछना चाहिए कि "क्या किसी को इस लेन-देन को जल्दी से खोजने या खोजने में दिलचस्पी होगी?"
इसके लिए कुछ व्यक्तिपरक निर्णय की आवश्यकता होगी। याद रखें, एक गैर-अनुक्रमित पैरामीटर को सीधे नहीं खोजा जा सकता है, लेकिन फिर भी एक अनुक्रमित पैरामीटर के साथ उपयोगी डेटा हो सकता है। इसके लिए अंतर्ज्ञान प्राप्त करने का एक अच्छा तरीका यह देखना है कि कैसे स्थापित कोडबेस अपने ईवेंट को डिज़ाइन करते हैं
अंगूठे के एक सामान्य नियम के रूप में, क्रिप्टोक्यूरेंसी राशियों को अनुक्रमित नहीं किया जाना चाहिए, और एक पता होना चाहिए, लेकिन यह नियम आँख बंद करके लागू नहीं किया जाना चाहिए। अनुक्रमणिका आपको केवल सटीक मान प्राप्त करने की अनुमति देती है, न कि मानों की श्रेणी।
इसका एक उदाहरण एक घटना जोड़ना होगा जब टोकन का खनन किया जाता है क्योंकि अंतर्निहित पुस्तकालय पहले से ही इस घटना का उत्सर्जन करते हैं।
घटनाएँ राज्य बदल रही हैं; वे लॉग को स्टोर करके ब्लॉकचेन की स्थिति को बदल देते हैं। इसलिए, उन्हें दृश्य (या शुद्ध) कार्यों में उपयोग नहीं किया जा सकता है।
अन्य भाषाओं में कंसोल.लॉग और प्रिंट के तरीके को डिबग करने के लिए ईवेंट उतने उपयोगी नहीं हैं; क्योंकि घटनाएँ स्वयं राज्य-परिवर्तनशील हैं, यदि कोई लेन-देन वापस आता है तो वे उत्सर्जित नहीं होते हैं।
अनइंडेक्स्ड तर्कों के लिए, तर्कों की संख्या की कोई आंतरिक सीमा नहीं है, हालांकि निश्चित रूप से अनुबंध आकार और गैस सीमाएं लागू होती हैं। निम्नलिखित निरर्थक उदाहरण मान्य दृढ़ता है:
contract ExampleContract { event Numbers(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); }
इसी तरह, लॉग में संग्रहीत स्ट्रिंग्स या सरणियों की लंबाई की कोई आंतरिक सीमा नहीं है।
हालाँकि, एक घटना में तीन से अधिक अनुक्रमित तर्क (विषय) नहीं हो सकते। एक अनाम घटना में 4 अनुक्रमित तर्क हो सकते हैं (हम इस अंतर को बाद में कवर करेंगे)।
शून्य घटनाओं वाला तर्क भी मान्य है।
निम्नलिखित घटनाएँ समान रूप से व्यवहार करती हैं
event NewOwner(address newOwner); event NewOwner(address);
सामान्य तौर पर, चर नाम सहित आदर्श होगा क्योंकि निम्नलिखित उदाहरण के पीछे शब्दार्थ बहुत अस्पष्ट हैं (यह नहीं है कि आपको घटनाओं की घोषणा कैसे करनी चाहिए!)
event Trade(address,address,address,uint256,uint256);
हम अनुमान लगा सकते हैं कि पते प्रेषक के अनुरूप हैं, और टोकन पते, जबकि uint256es राशियों के अनुरूप हैं, लेकिन इसे समझना कठिन है।
किसी घटना के नाम को बड़ा करना पारंपरिक है, लेकिन संकलक को इसकी आवश्यकता नहीं है। यह एक बेहतर घोषणा होगी:
event Trade(address trader, address token1, address token2, uint256 token1Amount, uint256 token2Amount);
जब एक मूल अनुबंध में एक घटना घोषित की जाती है, तो इसे बाल अनुबंध द्वारा उत्सर्जित किया जा सकता है। घटनाएँ आंतरिक हैं और इन्हें निजी या सार्वजनिक होने के लिए संशोधित नहीं किया जा सकता है।
यहाँ एक उदाहरण है:
contract ParentContract { event NewNumber(uint256 number); function doSomething(uint256 number) public { emit NewNumber(number); } } contract ChildContract is ParentContract { function doSomethingElse(uint256 number) public { emit NewNumber(number); // valid } }
इसी प्रकार, घटनाओं को इंटरफ़ेस में घोषित किया जा सकता है और बच्चे में उपयोग किया जा सकता है, जैसा कि निम्नलिखित उदाहरण में है।
interface IExampleInterface { event Deposit(address indexed sender, uint256 amount); } contract ExampleContract is IExampleInterface { function deposit() external payable { emit Deposit(msg.sender, msg.value); // also valid } }
EVM (Ethereum Virtual Machine) अपने हस्ताक्षर के keccak256 के साथ घटनाओं की पहचान करता है।
सॉलिडिटी संस्करण 0.8.15 या उच्चतर के लिए, आप .selector सदस्य का उपयोग करके चयनकर्ता को पुनः प्राप्त कर सकते हैं।
pragma solidity ^0.8.15; contract ExampleContract { event SomeEvent(uint256 blocknum, uint256 indexed timestamp); function selector() external pure returns (bool) { // true return SomeEvent.selector == keccak256("SomeEvent(uint256,uint256)"); } }
ईवेंट चयनकर्ता वास्तव में स्वयं एक विषय है (हम इस पर बाद के अनुभाग में चर्चा करेंगे)।
वेरिएबल्स को अनुक्रमित या नहीं के रूप में चिह्नित करने से चयनकर्ता नहीं बदलता है।
घटनाओं को अनाम के रूप में चिह्नित किया जा सकता है, जिस स्थिति में उनके पास चयनकर्ता नहीं होगा। इसका अर्थ है कि क्लाइंट-साइड कोड विशेष रूप से उन्हें हमारे पिछले उदाहरणों की तरह एक सबसेट के रूप में अलग नहीं कर सकता है। अज्ञात घटना को देखने के लिए क्लाइंट-साइड कोड का एकमात्र तरीका स्मार्ट अनुबंध के लिए सभी घटनाओं को प्राप्त करना है।
pragma solidity ^0.8.15; contract ExampleContract { event SomeEvent(uint256 blocknum, uint256 timestamp) anonymous; function selector() public pure returns (bool) { // ERROR: does not compile, anonymous events don't have selectors return SomeEvent.selector == keccak256("SomeEvent(uint256,uint256)"); } }
चूँकि ईवेंट सिग्नेचर को इंडेक्स में से एक के रूप में उपयोग किया जाता है, एक अनाम फ़ंक्शन में चार इंडेक्स किए गए विषय हो सकते हैं, क्योंकि फ़ंक्शन सिग्नेचर "फ्रीड अप" विषयों में से एक है।
एक अनाम घटना में अधिकतम चार अनुक्रमित विषय हो सकते हैं। एक गैर-अनाम घटना में तीन तक हो सकते हैं।
contract ExampleContract { // valid event SomeEvent(uint256 indexed, uint256 indexed, address indexed, address indexed) anonymous; }
अज्ञात घटनाओं का व्यवहार में शायद ही कभी उपयोग किया जाता है।
यह खंड ईवीएम के विधानसभा स्तर पर होने वाली घटनाओं का वर्णन करता है। ब्लॉकचैन विकास के लिए नए प्रोग्रामर के लिए इस खंड को छोड़ दिया जा सकता है।
स्मार्ट अनुबंध के साथ हुए प्रत्येक लेन-देन को पुनः प्राप्त करने के लिए, एथेरियम क्लाइंट को प्रत्येक ब्लॉक को स्कैन करना होगा, जो कि एक अत्यंत भारी I/O ऑपरेशन होगा; लेकिन एथेरियम एक महत्वपूर्ण अनुकूलन का उपयोग करता है।
ईवेंट प्रत्येक ब्लॉक के लिए ब्लूम फ़िल्टर डेटा संरचना में संग्रहीत होते हैं। ब्लूम फ़िल्टर एक संभाव्य सेट है जो कुशलता से उत्तर देता है कि कोई सदस्य सेट में है या नहीं। पूरे ब्लॉक को स्कैन करने के बजाय, क्लाइंट ब्लूम फिल्टर से पूछ सकता है कि क्या ब्लॉक में कोई घटना हुई थी। यह क्लाइंट को घटनाओं को खोजने के लिए ब्लॉकचेन को बहुत तेजी से स्कैन करने की अनुमति देता है।
ब्लूम फ़िल्टर संभाव्य हैं: वे कभी-कभी गलत तरीके से रिटर्न देते हैं कि कोई आइटम सेट का सदस्य है, भले ही वह नहीं है। ब्लूम फ़िल्टर में जितने अधिक सदस्य संग्रहीत होते हैं, त्रुटि की संभावना उतनी ही अधिक होती है, और इसकी भरपाई के लिए ब्लूम फ़िल्टर (भंडारण के अनुसार) बड़ा होना चाहिए। इस वजह से, एथेरियम सभी लेन-देन को ब्लूम फ़िल्टर में संग्रहीत नहीं करता है। लेन-देन की तुलना में बहुत कम घटनाएं हैं। यह ब्लॉकचैन पर भंडारण आकार को प्रबंधनीय रखता है।
जब क्लाइंट को ब्लूम फ़िल्टर से सकारात्मक सदस्यता प्रतिक्रिया मिलती है, तो उसे घटना को सत्यापित करने के लिए ब्लॉक को स्कैन करना होगा। हालाँकि, यह केवल ब्लॉक के एक छोटे से सबसेट के लिए होता है, इसलिए औसतन एथेरियम क्लाइंट पहले ईवेंट की उपस्थिति के लिए ब्लूम फ़िल्टर की जाँच करके बहुत अधिक संगणना बचाता है।
यूल इंटरमीडिएट प्रतिनिधित्व में, अनुक्रमित तर्कों (विषयों) और गैर-अनुक्रमित तर्कों के बीच का अंतर स्पष्ट हो जाता है।
निम्नलिखित yul फ़ंक्शंस एमिटिंग इवेंट्स के लिए उपलब्ध हैं (और उनका EVM opcode समान नाम रखता है)। तालिका को कुछ सरलीकरण के साथ yul प्रलेखन से कॉपी किया गया है।
ऑप कोड | प्रयोग |
---|---|
लॉग 0 (पी, एस) | विषयों और डेटा मेम के बिना लॉग इन करें [p…(p+s)) |
लॉग 1 (पी, एस, टी 1) | विषय t1 और डेटा मेम के साथ लॉग इन करें [p…(p+s)) |
लॉग 2 (पी, एस, टी 1, टी 2) | विषय t1, t2 और डेटा मेम के साथ लॉग इन करें [p…(p+s)) |
लॉग 3 (पी, एस, टी 1, टी 2, टी 3) | विषय t1, t2, t3 और डेटा मेम के साथ लॉग इन करें [p…(p+s)) |
लॉग 4 (पी, एस, टी 1, टी 2, टी 3, टी 4) | विषय t1, t2, t3, t4 और डेटा मेम के साथ लॉग इन करें [p…(p+s)) |
एक लॉग में अधिकतम 4 विषय हो सकते हैं, लेकिन एक गैर-अनाम सॉलिडिटी इवेंट में अधिकतम 3 अनुक्रमित तर्क हो सकते हैं। ऐसा इसलिए है क्योंकि इवेंट सिग्नेचर को स्टोर करने के लिए पहले विषय का उपयोग किया जाता है। चार से अधिक विषयों को उत्सर्जित करने के लिए कोई ऑपकोड या यूल फ़ंक्शन नहीं है।
अनइंडेक्स किए गए पैरामीटर केवल स्मृति क्षेत्र [p…(p+s)) में एन्कोडेड हैं और एक लंबे बाइट अनुक्रम के रूप में उत्सर्जित होते हैं।
पहले याद करें कि सॉलिडिटी में एक घटना में कितने गैर-अनुक्रमित तर्क हो सकते हैं, इसके लिए सिद्धांत रूप में कोई सीमा नहीं थी। अंतर्निहित कारण यह है कि लॉग ऑप कोड के पहले दो पैरामीटर में स्मृति क्षेत्र ने कितनी देर तक इंगित किया है, इस पर कोई स्पष्ट सीमा नहीं है। बेशक, अनुबंध आकार और मेमोरी विस्तार गैस लागतों द्वारा प्रदान की गई सीमाएं हैं।
भंडारण चर में लिखने की तुलना में घटनाएँ काफी सस्ती हैं। घटनाओं को स्मार्ट अनुबंधों द्वारा एक्सेस करने का इरादा नहीं है, इसलिए ओवरहेड की सापेक्ष कमी कम गैस लागत को उचित ठहराती है।
एक घटना की लागत कितनी गैस है इसका सूत्र इस प्रकार है ( स्रोत ):
375 + 375 * num_topics + 8 * data_size + mem_expansion cost
प्रत्येक घटना में कम से कम 375 गैस खर्च होती है। प्रत्येक अनुक्रमित पैरामीटर के लिए अतिरिक्त 375 का भुगतान किया जाता है। एक गैर-अनाम घटना में एक अनुक्रमित पैरामीटर के रूप में घटना चयनकर्ता होता है, ताकि अधिकांश समय लागत शामिल हो। फिर हम श्रृंखला में लिखे गए 32 बाइट शब्दों की संख्या का 8 गुना भुगतान करते हैं। क्योंकि यह क्षेत्र उत्सर्जित होने से पहले स्मृति में संग्रहीत होता है, स्मृति विस्तार लागत का भी हिसाब होना चाहिए।
सामान्य तौर पर, अंतर्ज्ञान कि जितना अधिक आप गैस में भुगतान करते हैं उतना अधिक लॉग इन करते हैं, सटीक है।
घटनाएँ ग्राहकों के लिए उन लेन-देन को जल्दी से पुनः प्राप्त करने के लिए हैं जो ब्याज के हो सकते हैं। हालांकि वे स्मार्ट अनुबंध की कार्यक्षमता में बदलाव नहीं करते हैं, लेकिन वे प्रोग्रामर को यह निर्दिष्ट करने की अनुमति देते हैं कि कौन से लेन-देन जल्दी से पुनर्प्राप्त करने योग्य होने चाहिए। इससे dapps के लिए महत्वपूर्ण सूचनाओं को जल्दी से सारांशित करना आसान हो जाता है।
भंडारण की तुलना में घटनाएँ अपेक्षाकृत सस्ते गैस-वार हैं, लेकिन उनकी लागत में सबसे महत्वपूर्ण कारक अनुक्रमित मापदंडों की संख्या है, यह मानते हुए कि कोडर स्मृति की अत्यधिक मात्रा का उपयोग नहीं करता है।
जैसा कि आप यहां क्या ढूंढ़ते हो? अधिक जानने के लिए हमारा उन्नत सॉलिडिटी बूटकैंप देखें।
इस लेख की मुख्य छवि हैकरनून के एआई इमेज जेनरेटर द्वारा "इवेंट्स इन सॉलिडिटी" प्रॉम्प्ट के माध्यम से तैयार की गई थी।