एक smart contract को अपग्रेड करना कई चरणों वाली और त्रुटियों की संभावना वाली प्रक्रिया है, इसलिए मानवीय भूल की संभावनाओं को कम करने के लिए, ऐसे टूल का उपयोग करना वांछनीय है जो प्रक्रिया को यथासंभव स्वचालित (automate) कर सके।
इसलिए, OpenZeppelin Upgrade Plugin Foundry या Hardhat के साथ बने smart contracts को डिप्लॉय करने, अपग्रेड करने और प्रबंधित करने की प्रक्रिया को सरल बनाता है।
इस लेख में, हम सीखेंगे कि अनुबंध (contract) अपग्रेड्स को प्रबंधित करने के लिए Foundry के साथ Upgrade Plugin का उपयोग कैसे करें, दोनों स्थानीय रूप से (locally) और Sepolia Testnet पर। हम यह भी कवर करेंगे कि ये प्लगइन्स सामान्य अपग्रेड समस्याओं से कैसे सुरक्षा प्रदान करते हैं।
पूर्वापेक्षाएँ (Prerequisites)
इस गाइड का अधिकतम लाभ उठाने के लिए, पाठक को निम्नलिखित से परिचित होना चाहिए:
- Solidity में delegatecall ऑपरेशन।
- ERC1967 जैसे मानक (standards) और अपग्रेडेबल कॉन्ट्रैक्ट्स में स्टेट सेट करने में initializers की भूमिका।
- सामान्य अपग्रेड विफलताएं, जिनमें function selector और storage slot टकराव (collisions) शामिल हैं।
- सामान्य प्रॉक्सी पैटर्न जैसे Transparent Upgradeable Proxy, UUPS Proxy, या Beacon Proxy का ज्ञान।
- Namespace स्टोरेज लेआउट, या EIP-7201।
Foundry के लिए Upgrades प्लगइन
Foundry के लिए OpenZeppelin प्लगइन एक उपयोगिता (utility) है जिसे Foundry Solidity स्क्रिप्ट्स या यूनिट टेस्ट्स में इम्पोर्ट किया जा सकता है और इसे निम्नानुसार इम्पोर्ट किया जा सकता है:
import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol";
यह लाइब्रेरी प्रॉक्सी, इम्प्लीमेंटेशन कॉन्ट्रैक्ट्स और अन्य संबंधित कॉन्ट्रैक्ट्स को डिप्लॉय करने के लिए फ़ंक्शंस प्रदान करती है। अगले भाग में हम इसकी क्षमताओं का एक उच्च-स्तरीय (high-level) अवलोकन प्रदान करेंगे, और बाद में दिखाएंगे कि अपग्रेड्स के लिए यूनिट टेस्ट कैसे लिखें, और एक स्क्रिप्ट कैसे बनाएं जो इस प्लगइन का उपयोग करके स्मार्ट कॉन्ट्रैक्ट्स को डिप्लॉय और अपग्रेड करने में सहायता करती है।
Upgrade Plugins की विशेषताएँ
- पिछले स्मार्ट कॉन्ट्रैक्ट इम्प्लीमेंटेशन का संदर्भ (reference) दिए जाने पर, प्लगइन पिछले इम्प्लीमेंटेशन की तुलना नए वाले से करता है ताकि स्टोरेज स्लॉट टकराव और अन्य संभावित समस्याओं की जांच की जा सके, जिन पर हम बाद में चर्चा करेंगे।
- प्लगइन्स UUPS, Transparent और Beacon Proxy Patterns को डिप्लॉय और अपग्रेड करने का समर्थन करते हैं। यह Diamond Proxy Pattern का समर्थन नहीं करता है।
- जब इस प्लगइन का उपयोग करके पहली बार कोई अपग्रेडेबल कॉन्ट्रैक्ट डिप्लॉय किया जाता है, तो स्वचालित रूप से तीन घटक (components) तक बनाए जाते हैं (यह इस पर निर्भर करता है कि अपग्रेड पैटर्न UUPS, Transparent, या Beacon Proxy है):
- The implementation contract: इसमें कॉन्ट्रैक्ट का वास्तविक लॉजिक होता है।
- Proxy: यदि कोई नया प्रॉक्सी डिप्लॉय किया जा रहा है, तो प्लगइन इसके निर्माण को संभालता है और इसे निर्दिष्ट (specified) इम्प्लीमेंटेशन कॉन्ट्रैक्ट से जोड़ता है। हालाँकि, यदि कोई प्रॉक्सी पहले से मौजूद है, तो प्लगइन मौजूदा प्रॉक्सी को एक नए इम्प्लीमेंटेशन से जोड़कर अपग्रेड प्रक्रिया को आसान बनाता है।
- ProxyAdmin: यह प्रशासनिक घटक यह प्रबंधित करता है कि विशेष रूप से transparent proxies के लिए प्रॉक्सी को कौन अपग्रेड कर सकता है (केवल Transparent Upgradeable Proxies ही Proxy Admin का उपयोग करते हैं)।
- Beacon Proxies: Beacon Proxy Pattern प्रॉक्सी को व्यक्तिगत एडमिन एड्रेस निर्दिष्ट नहीं करता है। इसके बजाय, एक ही बीकन (beacon) का एक ओनर होता है जो सभी लिंक किए गए प्रॉक्सी के लिए इम्प्लीमेंटेशन को अपडेट करता है। प्लगइन बीकन और प्रॉक्सी के सेटअप को स्वचालित करता है और बीकन के इम्प्लीमेंटेशन को अपडेट करता है।
- प्लगइन्स Hardhat और Foundry दोनों के साथ काम करने के लिए डिज़ाइन किए गए हैं। जहाँ Hardhat वातावरण डिप्लॉयमेंट का विस्तृत लॉग रखता है, वहीं Foundry अपग्रेड्स की सुरक्षा सुनिश्चित करने के लिए reference contracts के उपयोग पर ध्यान केंद्रित करता है।
Reference Contracts को परिभाषित करना
Hardhat के लिए OpenZeppelin Upgrade प्लगइन के विपरीत, जो JSON के माध्यम से स्वचालित रूप से इम्प्लीमेंटेशन कॉन्ट्रैक्ट्स और उनके संस्करणों (versions) को ट्रैक करता है, Foundry प्लगइन के लिए डेवलपर्स को स्पष्ट रूप से Reference Contracts परिभाषित करने की आवश्यकता होती है।
नीचे दिए गए कॉन्ट्रैक्ट में NatSpec पर विचार करें:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:oz-upgrades-from MyContractV1
contract MyContractV2 {
...
}
या
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:oz-upgrades-from contracts/MyContract.sol:MyContractV1
contract MyContractV2 {
...
}
natspec reference contract को संदर्भित कर रहा है, और इसी का उपयोग Foundry प्लगइन reference contract का संदर्भ प्राप्त करने के लिए करता है।
Reference contract वह कॉन्ट्रैक्ट है जिसे अपग्रेड किया जाना है और यह सुनिश्चित करने के लिए बेसलाइन के रूप में कार्य करता है कि नया इम्प्लीमेंटेशन मूल कॉन्ट्रैक्ट के स्टेट और लेआउट के साथ संगत (compatible) है।
सामान्य तौर पर, आप “reference contract” को “पिछला इम्प्लीमेंटेशन” मान सकते हैं।
प्लगइन का validateUpgrade फ़ंक्शन यह जांचता है कि नया इम्प्लीमेंटेशन कॉन्ट्रैक्ट एक reference contract के साथ संगत है। इसके लिए referenceContract विकल्प का सेट होना आवश्यक है, या नए कॉन्ट्रैक्ट पर @custom:oz-upgrades-from <reference> एनोटेशन का मौजूद होना आवश्यक है।
Upgrades टूल के validateUpgrade फ़ंक्शन का उपयोग कैसे किया जाता है, इसका विवरण इस गाइड में बाद में चर्चा की जाएगी।
Foundry में स्थानीय रूप से (Locally) Smart Contract Upgrades की टेस्टिंग करना
अब हम एक Transparent Upgradeable Proxy को स्थानीय रूप से डिप्लॉय और अपग्रेड करने के चरण दिखाएंगे। बाद में हम दिखाएंगे कि इसे टेस्टनेट (testnet) पर कैसे किया जाए। यहाँ वे चरण दिए गए हैं जो हम उच्च स्तर पर (high level) लेंगे:
- प्रॉक्सी और इम्प्लीमेंटेशन
ContractAको डिप्लॉय करें:ContractAको डिप्लॉय करें और इसे इनिशियलाइज़ (initialize) करें।- यह चरण स्वचालित रूप से
ContractA, एकTransparentUpgradeableProxy, और एकProxy Adminकॉन्ट्रैक्ट को सेट अप करता है। ContractAइस अपग्रेड प्रक्रिया के लिए reference contract के रूप में कार्य करता है।
ContractBमें अपग्रेड करें:- टूल कॉन्ट्रैक्ट को
ContractBमें अपडेट करने के लिए प्रॉक्सी परupgradeAndCallविधि (method) का उपयोग करता है।
- टूल कॉन्ट्रैक्ट को
चरण 1: Environment सेट अप करना
एक नई प्रोजेक्ट डायरेक्टरी बनाकर और Foundry को इनिशियलाइज़ करके शुरुआत करें। अपना टर्मिनल खोलें और निम्नलिखित कमांड्स निष्पादित (execute) करें:
mkdir rareskills-foundry-upgrades && cd rareskills-foundry-upgrades
forge init
इसके बाद, हमें प्रोजेक्ट के लिए आवश्यक फ़ाइलें तैयार करनी होंगी। इनमें स्मार्ट कॉन्ट्रैक्ट फ़ाइलें, एक टेस्ट फ़ाइल, और डिपेंडेंसी मैपिंग्स (dependency mappings) के लिए एक फ़ाइल शामिल हैं।
अपनी प्रोजेक्ट डायरेक्टरी में इन फ़ाइलों को बनाने के लिए नीचे दी गई कमांड निष्पादित करें:
touch src/ContractA.sol && touch src/ContractB.sol && touch test/Upgrades.t.sol && touch remappings.txt
चरण 2: प्रोजेक्ट को कॉन्फ़िगर (Configure) करना
एक बार प्रोजेक्ट इनिशियलाइज़ हो जाने के बाद, अगला कार्य अपग्रेडेबल कॉन्ट्रैक्ट्स को संभालने के लिए आवश्यक OpenZeppelin लाइब्रेरीज़ को सेट अप करना है।
रेमैपिंग्स (remappings) फ़ाइल को निम्नानुसार अपडेट करें:
forge remappings > remappings.txt
आवश्यक लाइब्रेरीज़ स्थापित (install) करने के लिए टर्मिनल में निम्नलिखित कमांड्स चलाएं।
forge install OpenZeppelin/openzeppelin-contracts-upgradeable --no-commit
forge install OpenZeppelin/openzeppelin-foundry-upgrades --no-commit
no-commit फ़्लैग उपरोक्त कमांड्स का उपयोग करने से पहले रेपो की वर्तमान स्थिति (state) को कमिट (commit) करने की परेशानी से बचाता है।
इसके बाद, हमें यह सुनिश्चित करने की आवश्यकता है कि प्रोजेक्ट को पता हो कि OpenZeppelin फ़ाइलें कहाँ खोजनी हैं। यह remappings.txt फ़ाइल को कॉन्फ़िगर करके किया जाता है, जिसे हमने पहले बनाया था।
remappings.txt खोलें और निम्नलिखित पंक्तियाँ (lines) डालें:
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
यदि OpenZeppelin रेमैपिंग्स पहले से ही remapping.txt फ़ाइल में हैं, तो उन्हें ऊपर दिए गए से बदल दें।
इसके बाद, स्वचालित रूप से बनाई गई test/Counter.t.sol टेस्ट और src/Counter.sol को हटा दें (delete):
rm test/Counter.t.sol
rm src/Counter.sol
अंत में, foundry.toml खोलें और निम्नलिखित कॉन्फ़िगरेशन जोड़ें:
[profile.default]
src = "src"
out = "out"
libs = ["node_modules", "lib"]
build_info = true
extra_output = ["storageLayout"]
ffi = true
ast = true
चरण 3: Upgradeable Contracts बनाना
अब हम अपग्रेड प्रक्रिया को प्रदर्शित करने के लिए दो स्मार्ट कॉन्ट्रैक्ट्स, ContractA और ContractB, बनाएंगे।
ContractA.sol से शुरू करें। इस कॉन्ट्रैक्ट में एक एकल (single) सार्वजनिक चर (public variable), value, और एक विधि (method), initialize है, जो कंस्ट्रक्टर को प्रतिस्थापित (replace) करता है।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract ContractA is Initializable{
uint256 public value;
function initialize(uint256 _setValue) public initializer {
value = _setValue;
}
}
इसके बाद, हम ContractA से अपग्रेड पथ (upgrade path) दिखाने के लिए ContractB.sol बनाएंगे।
ContractB, value को बढ़ाने (increment) के लिए एक विधि (method) जोड़कर ContractA की कार्यक्षमता (functionality) का विस्तार करता है।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/// @custom:oz-upgrades-from ContractA
contract ContractB is Initializable {
uint256 public value;
function initialize(uint256 _setValue) public initializer {
value = _setValue;
}
function increaseValue() public {
value += 10;
}
}
@custom:oz-upgrades-from ContractA एनोटेशन का उपयोग करके, हम निर्दिष्ट करते हैं कि ContractB, ContractA का अपग्रेडेड संस्करण है।
इस एनोटेशन के लिए यह आवश्यक नहीं है कि ContractA और ContractB एक ही डायरेक्टरी में हों। यह ContractA को उसके नाम से पहचानता है, बशर्ते इसे प्रोजेक्ट के भीतर विशिष्ट रूप से (uniquely) परिभाषित किया गया हो, अन्यथा, पूरी तरह से योग्य नाम (fully qualified name) की आवश्यकता होती है।
इस एनोटेशन के बिना, प्लगइन आगे नहीं बढ़ेगा और निम्नलिखित जैसी त्रुटि प्रदर्शित करेगा:
`The contract ${sourceContract.fullyQualifiedName} does not specify what contract it upgrades from. Add the \`@custom:oz-upgrades-from <REFERENCE_CONTRACT>\` annotation to the contract, or include the reference contract name when running the validate command or function.`
यद्यपि हम इस उदाहरण के लिए ContractA और ContractB दोनों तैयार कर रहे हैं, इसका मतलब यह नहीं है कि हमें ContractA के भविष्य के अपग्रेड्स का “अनुमान” लगाने की आवश्यकता है। हम केवल इस उदाहरण की सुविधा के लिए ContractB को पहले से बना रहे हैं।
चरण 4: Upgradeable कार्यक्षमता की टेस्टिंग करना
इस चरण में कॉन्ट्रैक्ट्स को संकलित (compile) करना, उन्हें Transparent Proxy Pattern के रूप में डिप्लॉय करना, अपग्रेड करना और यह सत्यापित (verify) करना शामिल है कि क्या कॉन्ट्रैक्ट की स्थिति (state) अपेक्षानुसार अपडेट होती है।
Test Environment तैयार करना
सबसे पहले, test फ़ोल्डर में नेविगेट करें और निम्नलिखित कोड को Upgrades.t.sol फ़ाइल में डालें।
यह सेटअप ContractA से ContractB तक अपग्रेडेबिलिटी का परीक्षण (test) करता है।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "openzeppelin-foundry-upgrades/Upgrades.sol";
import "../src/ContractA.sol";
import "../src/ContractB.sol";
contract UpgradesTest is Test {
// future code will go here
}
प्रारंभिक (initial) परीक्षण में दो मुख्य कार्य शामिल हैं:
- एक पारदर्शी प्रॉक्सी (transparent proxy) का उपयोग करके एक प्रारंभिक मूल्य (initial value) के साथ
ContractAको डिप्लॉय करना। ContractBमें अपग्रेड करना।- अंत में, स्थिति (state) को संशोधित करने के लिए
increaseValueका आह्वान (invoke) करना।
यहाँ परीक्षण (test) के लिए कोड दिया गया है। वर्कफ़्लो को समझने के लिए कृपया नीचे दिए गए कोड में टिप्पणियों (comments) को पढ़ें:
function testTransparent() public {
// Deploy a transparent proxy with ContractA as the implementation and initialize it with 10
address proxy = Upgrades.deployTransparentProxy(
"ContractA.sol",
msg.sender,
abi.encodeCall(ContractA.initialize, (10))
);
// Get the instance of the contract
ContractA instance = ContractA(proxy);
// Get the implementation address of the proxy
address implAddrV1 = Upgrades.getImplementationAddress(proxy);
// Get the admin address of the proxy
address adminAddr = Upgrades.getAdminAddress(proxy);
// Ensure the admin address is valid
assertFalse(adminAddr == address(0));
// Log the initial value
console.log("----------------------------------");
console.log("Value before upgrade --> ", instance.value());
console.log("----------------------------------");
// Verify initial value is as expected
assertEq(instance.value(), 10);
// Upgrade the proxy to ContractB
Upgrades.upgradeProxy(proxy, "ContractB.sol", "", msg.sender);
// Get the new implementation address after upgrade
address implAddrV2 = Upgrades.getImplementationAddress(proxy);
// Verify admin address remains unchanged
assertEq(Upgrades.getAdminAddress(proxy), adminAddr);
// Verify implementation address has changed
assertFalse(implAddrV1 == implAddrV2);
// Invoke the increaseValue function separately
ContractB(address(instance)).increaseValue();
// Log and verify the updated value
console.log("----------------------------------");
console.log("Value after upgrade --> ", instance.value());
console.log("----------------------------------");
assertEq(instance.value(), 20);
}
संक्षेप में, टूल निम्नलिखित कार्य कर रहा है:
Upgrades.deployTransparentProxy("ContractA.sol", msg.sender, abi.encodeCall(ContractA.initialize, (10)));का उपयोग करके एक पारदर्शी प्रॉक्सी के माध्यम सेContractAको डिप्लॉय करें और एक विशिष्ट मूल्य (value) के साथContractAकोinitializeकरें।Upgrades.upgradeProxy(proxy, "ContractB.sol", "", msg.sender);का उपयोग करकेContractBका उपयोग करने के लिए प्रॉक्सी को अपग्रेड करें।- अपग्रेड के बाद एडमिन एड्रेस की निरंतरता (consistency) और अपडेट किए गए मूल्य (value) को सत्यापित (verify) करें।
Test को निष्पादित (Executing) करना
टेस्ट चलाने के लिए, अपने टर्मिनल में निम्नलिखित कमांड दर्ज करें:
forge clean && forge test -vvv --ffi
आपको निम्नलिखित के समान एक आउटपुट दिखाई देगा:
pari@MacBook-Air rareskills-foundry-upgrades % forge clean && forge test --mt testTransparent -vvv
[⠢] Compiling...
[⠊] Compiling 54 files with 0.8.24
[⠆] Solc 0.8.24 finished in 4.94s
Compiler run successful!
Ran 1 test for test/Upgrades.t.sol:UpgradesTest
[PASS] testTransparent() (gas: 1355057)
Logs:
----------------------------------
Value before upgrade --> 10
----------------------------------
----------------------------------
Value after upgrade --> 20
----------------------------------
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.43s (1.43s CPU time)
Ran 1 test suite in 5.47s (1.43s CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
दूसरी बार टेस्ट चलाने से पहले आपको forge cache clean और forge clean चलाने की आवश्यकता हो सकती है।
Beacon Proxy के साथ एक उदाहरण
अब हम प्रदर्शित करते हैं कि Beacon Proxy Pattern के साथ इस प्लगइन का उपयोग कैसे करें। हम पिछले उदाहरण के समान इम्प्लीमेंटेशन कॉन्ट्रैक्ट्स ContractA और ContractB का उपयोग करेंगे।
Test का अवलोकन (Overview)
- बीकन (Beacon) के लिए प्रारंभिक इम्प्लीमेंटेशन के रूप में
ContractAको डिप्लॉय करें। - दो beacon proxies बनाएं, प्रत्येक को अलग-अलग मूल्यों के साथ इनिशियलाइज़ किया गया हो। याद रखें, beacon proxy pattern में, कई प्रॉक्सी एक ही इम्प्लीमेंटेशन को इंगित करते हैं, लेकिन उनकी अपनी अलग स्थिति (state) होती है।
validateUpgradeफ़ंक्शन का उपयोग करके मूल इम्प्लीमेंटेशन (ContractA) के विरुद्ध नए इम्प्लीमेंटेशन (ContractB) को मान्य (validate) करें।- दोनों प्रॉक्सी को एक साथ अपग्रेड करते हुए, बीकन के इम्प्लीमेंटेशन को
ContractBमें अपग्रेड करें। - यह सुनिश्चित करने के लिए दोनों प्रॉक्सी पर नई कार्यक्षमताओं का परीक्षण (Test) करें कि अपग्रेड अपेक्षानुसार परिवर्तनों को लागू करता है।
परीक्षण (test) करने के लिए इस फ़ंक्शन को Upgrades.t.sol फ़ाइल में जोड़ें। टिप्पणियाँ (comments) वर्कफ़्लो की व्याख्या करती हैं:
import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
import {Options} from "openzeppelin-foundry-upgrades/Upgrades.sol";
function testBeacon() public {
// Deploy a beacon with ContractA as the initial implementation
address beacon = Upgrades.deployBeacon("ContractA.sol", msg.sender);
// Get the initial implementation address of the beacon
address implAddrV1 = IBeacon(beacon).implementation();
// Deploy the first beacon proxy and initialize it
address proxy1 = Upgrades.deployBeaconProxy(beacon, abi.encodeCall(ContractA.initialize, 15));
ContractA instance1 = ContractA(proxy1);
// Deploy the second beacon proxy and initialize it
address proxy2 = Upgrades.deployBeaconProxy(beacon, abi.encodeCall(ContractA.initialize, 20));
ContractA instance2 = ContractA(proxy2);
// Check if both proxies point to the same beacon
assertEq(Upgrades.getBeaconAddress(proxy1), beacon);
assertEq(Upgrades.getBeaconAddress(proxy2), beacon);
console.log("----------------------------------");
console.log("Value before upgrade in Proxy 1 --> ", instance1.value());
console.log("Value before upgrade in Proxy 2 --> ", instance2.value());
console.log("----------------------------------");
// Validate the new implementation before upgrading
Options memory opts;
opts.referenceContract = "ContractA.sol";
Upgrades.validateUpgrade("ContractB.sol", opts);
// Upgrade the beacon to use ContractB
Upgrades.upgradeBeacon(beacon, "ContractB.sol", msg.sender);
// Get the new implementation address of the beacon after upgrade
address implAddrV2 = IBeacon(beacon).implementation();
// Activate the increaseValue function in both proxies
ContractB(address(instance1)).increaseValue();
ContractB(address(instance2)).increaseValue();
console.log("----------------------------------");
console.log("Value after upgrade in Proxy 1 --> ", instance1.value());
console.log("Value after upgrade in Proxy 2 --> ", instance2.value());
console.log("----------------------------------");
// Check if the values have been correctly increased
assertEq(instance1.value(), 25);
assertEq(instance2.value(), 30);
// Check if the implementation address has changed
assertFalse(implAddrV1 == implAddrV2);
}
Upgrades प्लगइन की समर्थित कार्यक्षमता
प्लगइन उस कार्यक्षमता से अधिक का समर्थन करता है जिसका उपयोग हमने ऊपर दिए गए दो उदाहरणों में किया था। हम नीचे Upgrades में अन्य फ़ंक्शंस का अवलोकन (overview) प्रदान करते हैं:
Initial Deployment फ़ंक्शंस:
इन फ़ंक्शंस का उपयोग मुख्य रूप से कॉन्ट्रैक्ट्स के प्रारंभिक संस्करणों (versions) के डिप्लॉयमेंट और उनके प्रॉक्सी संरचनाओं (structures) को स्थापित करने के लिए किया जाता है:
deployUUPSProxy(*string contractName, bytes initializerData, struct Options opts*): दिए गए कॉन्ट्रैक्ट का इम्प्लीमेंटेशन के रूप में उपयोग करके एक UUPS प्रॉक्सी डिप्लॉय करता है।deployTransparentProxy(*string contractName, address initialOwner, bytes initializerData, struct Options opts*): दिए गए कॉन्ट्रैक्ट का इम्प्लीमेंटेशन के रूप में उपयोग करके एक transparent प्रॉक्सी डिप्लॉय करता है।deployBeaconProxy(*address beacon, bytes data, struct Options opts*): दिए गए बीकन और कॉल डेटा का उपयोग करके एक beacon प्रॉक्सी डिप्लॉय करता है।deployBeacon(*string contractName, address initialOwner, struct Options opts*): दिए गए कॉन्ट्रैक्ट का इम्प्लीमेंटेशन के रूप में उपयोग करके एक अपग्रेडेबल बीकन डिप्लॉय करता है।
Validation और Implementation फ़ंक्शंस:
इन फ़ंक्शंस का उपयोग अपग्रेड के दौरान इम्प्लीमेंटेशन की संगतता (compatibility) और सुरक्षा सुनिश्चित करने के लिए किया जाता है:
validateImplementation(*string contractName, struct Options opts*): एक इम्प्लीमेंटेशन कॉन्ट्रैक्ट को मान्य (validate) करता है, लेकिन इसे डिप्लॉय नहीं करता है।deployImplementation(*string contractName, struct Options opts*): एक इम्प्लीमेंटेशन कॉन्ट्रैक्ट को मान्य (validate) करता है और डिप्लॉय करता है, और इसका एड्रेस (address) लौटाता है।validateUpgrade(*string contractName, struct Options opts*): एक reference contract की तुलना में एक नए इम्प्लीमेंटेशन कॉन्ट्रैक्ट को मान्य (validate) करता है, लेकिन इसे डिप्लॉय नहीं करता है।prepareUpgrade(*string contractName, struct Options opts*): एक reference contract की तुलना में एक नए इम्प्लीमेंटेशन कॉन्ट्रैक्ट को मान्य (validate) करता है, नए इम्प्लीमेंटेशन कॉन्ट्रैक्ट को डिप्लॉय करता है और इसका एड्रेस लौटाता है।
Upgrade फ़ंक्शंस:
इन फ़ंक्शंस का उपयोग पहले से डिप्लॉय किए गए कॉन्ट्रैक्ट्स के अपग्रेड को प्रबंधित (manage) और कार्यान्वित (implement) करने के लिए किया जाता है:
upgradeProxy(*address proxy, string contractName, bytes data, struct Options opts*): प्रॉक्सी को एक नए इम्प्लीमेंटेशन कॉन्ट्रैक्ट में अपग्रेड करता है। यह फ़ंक्शन आंतरिक रूप से (under the hood)validateUpgrade()को कॉल करता है, इसलिए यदि वैलिडेशन सफल नहीं होता है तो यह विफल हो जाएगा। यदि उपयोगकर्ता कुछ वैलिडेशन को बायपास (bypass) करना चाहता है, तो इसे opts तर्क (argument) में कॉन्फ़िगर किया जा सकता है।upgradeBeacon(*address beacon, string contractName, struct Options opts*): बीकन को एक नए इम्प्लीमेंटेशन कॉन्ट्रैक्ट में अपग्रेड करता है।
अन्य फ़ंक्शंस:
getAdminAddress(*address proxy*): एक transparent proxy के ERC1967 एडमिन स्टोरेज स्लॉट से एडमिन एड्रेस प्राप्त करता है।
Sepolia Testnet पर डिप्लॉय और सत्यापित (Verify) करना
यह अनुभाग (section) आपको ContractA को डिप्लॉय करने, एक Transparent Proxy का उपयोग करके इसे ContractB में अपग्रेड करने और Sepolia Explorer पर कॉन्ट्रैक्ट्स को सत्यापित (verify) करने के लिए मार्गदर्शन करता है।
चरण 1: Environment Variables जोड़ें
सबसे पहले, टेस्टनेट पर डिप्लॉयमेंट के लिए आवश्यक कॉन्फ़िगरेशन सेट अप करें:
- संवेदनशील डेटा (sensitive data) को स्टोर करने के लिए एक
.envफ़ाइल बनाएं। - Github जैसे संस्करण-नियंत्रित (version-controlled) प्लेटफार्मों पर private keys या अन्य संवेदनशील जानकारी के खुलासे को रोकने के लिए अपनी
.gitignoreमें.envफ़ाइल को शामिल करें। .envफ़ाइल में अपना विशिष्ट डेटा (specific data) भरें:
SEPOLIA_RPC_URL=your-sepolia-endpoint
PRIVATE_KEY=your-private-key
ETHERSCAN_API_KEY=your-etherscan-api
SENDER=your-EOA-address
कॉन्फ़िगरेशन विवरण:
SEPOLIA_RPC_URL: Sepolia नेटवर्क से जुड़ने के लिए RPC URL।PRIVATE_KEY: आपकी प्राइवेट की (private key), जिसका उपयोग ट्रांजेक्शन को साइन (sign) करने के लिए किया जाता है। सुनिश्चित करें कि इस वॉलेट में डिप्लॉयमेंट की गैस लागत (gas cost) का भुगतान करने के लिए Ether है।ETHERSCAN_API_KEY: आपकी Etherscan API की (key), जो कॉन्ट्रैक्ट सत्यापन (verification) के लिए आवश्यक है।SENDER: वह Ethereum एड्रेस जहाँ से डिप्लॉयमेंट ट्रांजेक्शन शुरू किए जाएंगे।
चरण 2: foundry.toml फ़ाइल को कॉन्फ़िगर करें
Sepolia Testnet और Etherscan सत्यापन (verification) के लिए सेटिंग्स शामिल करने के लिए foundry.toml फ़ाइल को अपडेट करें:
[etherscan]
sepolia = { key = "${ETHERSCAN_API_KEY}" }
[rpc_endpoints]
sepolia = "${SEPOLIA_RPC_URL}"
यह कॉन्फ़िगरेशन दो कार्य पूरे करता है:
- Etherscan Configuration: डिप्लॉयमेंट के बाद कॉन्ट्रैक्ट सत्यापन के लिए Etherscan API Key को Sepolia नेटवर्क के साथ लिंक करता है।
- RPC Endpoints: Sepolia नेटवर्क के लिए RPC URL निर्दिष्ट (specify) करता है।
चरण 3: डिप्लॉयमेंट और अपग्रेड की स्क्रिप्टिंग (Scripting)
script फ़ोल्डर में नेविगेट करें और डिप्लॉयमेंट और अपग्रेड कार्यों के लिए आवश्यक कोड डालने के लिए Upgrades.s.sol फ़ाइल खोलें।
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script} from "forge-std/Script.sol";
import {ContractA} from '../src/ContractA.sol';
import {ContractB} from '../src/ContractB.sol';
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
contract UpgradesScript is Script {
function setUp() public {}
function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
// Deploy `ContractA` as a transparent proxy using the Upgrades Plugin
address transparentProxy = Upgrades.deployTransparentProxy(
"ContractA.sol",
msg.sender,
abi.encodeCall(ContractA.initialize, 10)
);
}
}
परदे के पीछे (Behind the scenes), यह हमारे लिए Proxy, ProxyAdmin, और ContractA को डिप्लॉय करेगा।
deployTransparentProxy फ़ंक्शन 3 पैरामीटर लेता है:
contractName(string): इम्प्लीमेंटेशन के लिए कॉन्ट्रैक्ट का नाम, जैसे “MyContract.sol”, “MyContract.sol:MyContract”, या एक relative artifact path।initialOwner(address): ProxyAdmin कॉन्ट्रैक्ट के ओनर के रूप में सेट किया गया एड्रेस, जो स्वचालित रूप से प्रॉक्सी के साथ डिप्लॉय होता है।initializerData(bytes): प्रॉक्सी निर्माण के दौरान निष्पादित किए जाने वाले इनिशियलाइज़र फ़ंक्शन के लिए एन्कोडेड कॉल डेटा (encoded call data); यदि किसी इनिशियलाइज़ेशन की आवश्यकता नहीं है तो खाली छोड़ दें।
यह फ़ंक्शन डिप्लॉय किए गए प्रॉक्सी का एड्रेस लौटाता है।
Script को निष्पादित (Executing) करना
Sepolia नेटवर्क पर अपग्रेड को डिप्लॉय और सत्यापित करने के लिए निम्नलिखित कमांड का उपयोग करें।
forge clean && forge script script/UpgradesScript.s.sol --rpc-url sepolia --private-key $PRIVATE_KEY --broadcast --verify --sender $SENDER
यह कमांड पिछले बिल्ड (builds) को साफ (clean) करती है, स्क्रिप्ट को निष्पादित करती है, और Sepolia Explorer पर कॉन्ट्रैक्ट्स को सत्यापित करती है।
निष्पादन (execution) पर, आपको कॉन्ट्रैक्ट्स के सफल डिप्लॉयमेंट और सत्यापन (verification) को इंगित (indicate) करने वाला आउटपुट मिलने की उम्मीद करनी चाहिए:
ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
Total Paid: 0.00356888630073128 ETH (862220 gas * avg 4.139182924 gwei)
##
Start verification for (3) contracts
Start verifying contract `0x427186c574B5fA11cB9d796871861EF87c74Ad37` deployed on sepolia
Submitting verification for [src/ContractA.sol:ContractA] 0x427186c574B5fA11cB9d796871861EF87c74Ad37.
Submitted contract for verification:
Response: `OK`
GUID: `hf2dplvhjmjpj3nixun3kupamtsgmbacngfeygpm9p34mbzb3g`
URL: https://sepolia.etherscan.io/address/0x427186c574b5fa11cb9d796871861ef87c74ad37
Contract verification status:
Response: `NOTOK`
Details: `Pending in queue`
Contract verification status:
Response: `OK`
Details: `Pass - Verified`
Contract successfully verified
Start verifying contract `0xbA58580452Bc758C9a044584F6CEa468e5569a13` deployed on sepolia
Submitting verification for [lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy] 0xbA58580452Bc758C9a044584F6CEa468e5569a13.
Submitted contract for verification:
Response: `OK`
GUID: `biaqcdgrhwjfu8d7b3n9jg6btmsjpktgd61rdx42n7ptttuvre`
URL: https://sepolia.etherscan.io/address/0xba58580452bc758c9a044584f6cea468e5569a13
Contract verification status:
Response: `NOTOK`
Details: `Pending in queue`
Contract verification status:
Response: `OK`
Details: `Pass - Verified`
Contract successfully verified
Start verifying contract `0x8bB6A51C24ad9b6bA276c2bf0380e5E8Ce31E866` deployed on sepolia
Submitting verification for [lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol:ProxyAdmin] 0x8bB6A51C24ad9b6bA276c2bf0380e5E8Ce31E866.
Submitted contract for verification:
Response: `OK`
GUID: `rfnvjnxa8j2rqxxgwtnx9if17rdlyky2nk9eixrmzjbp5pn4gy`
URL: https://sepolia.etherscan.io/address/0x8bb6a51c24ad9b6ba276c2bf0380e5e8ce31e866
Contract verification status:
Response: `NOTOK`
Details: `Pending in queue`
Contract verification status:
Response: `NOTOK`
Details: `Pending in queue`
Contract verification status:
Response: `NOTOK`
Details: `Already Verified`
Contract source code already verified
All (3) contracts were verified!
Transactions saved to: /Users/nest/rareskills/rareskills-foundry-upgrades/broadcast/UpgradesScript.s.sol/11155111/run-latest.json
Sensitive values saved to: /Users/nest/rareskills/rareskills-foundry-upgrades/cache/UpgradesScript.s.sol/11155111/run-latest.json
इस स्क्रिप्ट के दौरान निष्पादित deployTransparentProxy कमांड ने ContractA और एक Proxy Admin Contract के साथ एक Transparent Upgradeable Proxy को डिप्लॉय किया।
ये ट्रांजेक्शन्स Sepolia Explorer पर देखे जा सकते हैं:
- ContractA Deployment: https://sepolia.etherscan.io/tx/0x9dba6d8293629cb9557500d8645659de3127e75abcfa705b06e6cf379092a10e
- Transparent upgradeable Proxy: https://sepolia.etherscan.io/tx/0x9dba6d8293629cb9557500d8645659de3127e75abcfa705b06e6cf379092a10e
- Proxy Admin Contract: https://sepolia.etherscan.io/address/0xba58580452bc758c9a044584f6cea468e5569a13#code#F6#L1
कॉन्ट्रैक्ट को अपग्रेड करना
ContractB में अपग्रेड करने से पहले, हम प्लगइन के validateUpgrade फ़ंक्शन का उपयोग करके reference contract, ContractA के विरुद्ध नए इम्प्लीमेंटेशन को मान्य (validate) करेंगे।
एक बार वैलिडेशन की पुष्टि हो जाने के बाद, आगे हम upgradeProxy फ़ंक्शन का उपयोग करके अपग्रेड के साथ आगे बढ़ेंगे। कृपया नीचे दिए गए कोड में टिप्पणियों को पढ़ें:
import {Options} from "openzeppelin-foundry-upgrades/Upgrades.sol";
function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
// Specifying the address of the existing transparent proxy
address transparentProxy = 'your-transparent-proxy-address';
// Setting options for validating the upgrade
Options memory opts;
opts.referenceContract = "ContractA.sol";
// Validating the compatibility of the upgrade
Upgrades.validateUpgrade("ContractB.sol", opts);
// Upgrading to ContractB and attempting to increase the value
Upgrades.upgradeProxy(transparentProxy, "ContractB.sol", abi.encodeCall(ContractB.increaseValue, ()));
}
यदि नया कॉन्ट्रैक्ट इम्प्लीमेंटेशन reference contract के साथ संगत (compatible) नहीं है, तो यह निम्नलिखित त्रुटि (error) फेंकता है:
revert: Upgrade safety validation failed:
फिर से। ContractB को डिप्लॉय और अपग्रेड करने के लिए उसी स्क्रिप्ट का उपयोग करें और इसे Explorer पर सत्यापित (verify) करें।

अपग्रेड ट्रांजेक्शन यहाँ देखा जा सकता है।
OpenZeppelin Foundry Plugin अपग्रेड समस्याओं के खिलाफ कैसे मदद करता है
यह अनुभाग (section) उन संभावित सुरक्षा समस्याओं (security issues) को सूचीबद्ध करता है जो प्रॉक्सी अपग्रेड्स के लिए विशिष्ट हैं और टूल उनसे कैसे सुरक्षा करता है।
1. Immutable Variables
Solidity में, immutable कीवर्ड वेरिएबल्स को उनके मूल्यों को सीधे बायटेकोड (bytecode) में एम्बेड (embed) करके कॉन्ट्रैक्ट निर्माण के दौरान स्थायी रूप से सेट करने की अनुमति देता है। यह भविष्य के डिप्लॉयमेंट्स को कंस्ट्रक्टर के लिए बिल्कुल उन्हीं तर्कों (arguments) का उपयोग करने के लिए बाध्य करता है, ताकि भविष्य के डिप्लॉयमेंट्स में समान बायटेकोड हो। चूंकि इसे ट्रैक करना मुश्किल है, इसलिए प्लगइन immutable वेरिएबल्स के उपयोग को हतोत्साहित (discourage) करता है।
अपग्रेडेबल कॉन्ट्रैक्ट्स में immutable वेरिएबल्स की अनुमति देने के लिए, डेवलपर्स नीचे दिखाए गए अनुसार @custom:oz-upgrades-unsafe-allow एनोटेशन का उपयोग करके इस सुरक्षा जांच को बायपास (bypass) कर सकते हैं।
contract ImmutableVar {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint256 public immutable a;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(uint256 _a) {
a = _a;
}
}
यह immutable वेरिएबल्स के उपयोग की अनुमति देता है लेकिन डिप्लॉयमेंट निरंतरता (consistency) बनाए रखने के लिए सावधानीपूर्वक प्रबंधन की मांग करता है।
2. स्टोरेज लेआउट (Storage Layout)
स्मार्ट कॉन्ट्रैक्ट्स को अपडेट करते समय एक सुसंगत (consistent) स्टोरेज लेआउट बनाए रखना आवश्यक है। स्टेट वेरिएबल्स (state variables) के क्रम (order) या प्रकार (type) में कोई भी बदलाव डेटा भ्रष्टाचार (data corruption) का कारण बन सकता है।
इसका मतलब यह है कि यदि आपके पास एक प्रारंभिक (initial) कॉन्ट्रैक्ट है जो इस तरह दिखता है:
contract MyContract {
uint256 private x;
string private y;
}
तो आप किसी वेरिएबल का प्रकार (type) नहीं बदल सकते:
contract MyContract {
string private x; // uint256 became a string
string private y;
}
या उस क्रम (order) को नहीं बदल सकते जिसमें उन्हें घोषित (declared) किया गया है:
contract MyContract {
string private y; // x and y switched places
uint256 private x;
}
यदि आपको एक नया वेरिएबल पेश करने की आवश्यकता है, तो सुनिश्चित करें कि आप हमेशा अंत में ऐसा करते हैं:
contract MyContract {
uint256 private x;
string private y;
bytes private z;
}
इस लेख की शुरुआत से हमारे ContractA और ContractB उदाहरणों का उपयोग करते हुए, मान लीजिए कि हमने गलत तरीके से ContractB में एक स्टोरेज वेरिएबल निम्नानुसार डाला था:

हमें OpenZeppelin प्लगइन से निम्नलिखित त्रुटि (error) मिलेगी (हम Transparent Upgradeable Proxy के साथ पहले उदाहरण के समान ही परीक्षण का उपयोग कर रहे हैं):

3. यह मान्य (Validating) करना कि __gap का ठीक से उपयोग किया गया है
जैसा कि Storage Namespaces में हमारे लेख में चर्चा की गई है, __gap वेरिएबल का उपयोग एक नई स्टोरेज वेरिएबल के सम्मिलित होने पर पैरेंट कॉन्ट्रैक्ट्स (parent contracts) को चाइल्ड कॉन्ट्रैक्ट्स (child contracts) के स्टोरेज वेरिएबल्स को स्थानांतरित (shifting) करने से रोकने की एक रणनीति है। __gap वेरिएबल का सही ढंग से उपयोग करने के लिए, एक नया वेरिएबल डालने पर गैप का आकार कम किया जाना चाहिए। निम्नलिखित उदाहरण में, देव (dev) ने एक नया स्टोरेज वेरिएबल डाला और __gap के आकार को कम करना भूल गया:

और टूल इसे पकड़ लेता है (catches this):

OpenZeppelin टूल __gap के उपयोग को लागू (enforce) नहीं करता है। इसके बजाय, यदि एक गैप वेरिएबल (gap variable) मौजूद है, तो टूल यह जांचता है कि अपग्रेड उस तरीके का सम्मान करता है जिस तरह से __gap वेरिएबल का उपयोग किया जाना है, यानी स्टोरेज वेरिएबल संरेखण (alignment) बनाए रखना।
__gap रणनीति का एक अधिक मजबूत विकल्प Named Storage Layouts का उपयोग है, जैसा कि Storage Namespaces पर हमारे ट्यूटोरियल में चर्चा की गई है, और हम अगले अनुभाग में इसका एक उदाहरण प्रदान करते हैं।
4. यह मान्य (Validating) करना कि ERC-7201 का ठीक से पालन किया गया है
शुरुआत से ContractA और ContractB के हमारे चल रहे उदाहरण का उपयोग करते हुए, आइए ERC-7201 का उपयोग करने के लिए कॉन्ट्रैक्ट को बदलें (alter)।
निम्नलिखित परिवर्तनों पर ध्यान दें:
- एक public वेरिएबल होने के बजाय,
valueअब एक public फ़ंक्शन है valueके लिए अंतर्निहित (underlying) स्टोरेज कोMyStoragestruct के अंदर ले जाया गया है- सेटर (setter) फ़ंक्शंस को अब
MyStoragestruct के साथ इंटरैक्ट करना होगा (ध्यान दें कि struct का उपयोग वेरिएबल्स को समूहीकृत करने के लिए किया जाता है, यह वास्तव में कभी भी इनिशियलाइज़ नहीं किया जाता है)।
यहाँ ERC-7201 का पालन करने के लिए समायोजित (adjusted) किया गया ContractA है:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract ContractA is Initializable {
/// @custom:storage-location erc7201:ContractA.storage.MyStorage
struct MyStorage {
uint256 value;
}
// keccak256(abi.encode(uint256(keccak256("ContractA.storage.MyStorage")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant MyStorageLocation = 0xd255ccbed1486709ef10c220c9b584c9ad5cacd00961bdfc2156c2c7f2e4fc00;
function _getMyStorage() private pure returns (MyStorage storage $) {
assembly {
$.slot := MyStorageLocation
}
}
function value() public view returns (uint256) {
MyStorage storage $ = _getMyStorage();
return $.value;
}
function initialize(uint256 _setValue) public initializer {
MyStorage storage $ = _getMyStorage();
$.value = _setValue;
}
}
और ContractB:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/// @custom:oz-upgrades-from ContractA
contract ContractB is Initializable {
/// @custom:storage-location erc7201:ContractA.storage.MyStorage
struct MyStorage {
uint256 value;
}
// keccak256(abi.encode(uint256(keccak256("ContractA.storage.MyStorage")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant MyStorageLocation = 0xd255ccbed1486709ef10c220c9b584c9ad5cacd00961bdfc2156c2c7f2e4fc00;
function _getMyStorage() private pure returns (MyStorage storage $) {
assembly {
$.slot := MyStorageLocation
}
}
function value() public view returns (uint256) {
MyStorage storage $ = _getMyStorage();
return $.value;
}
function initialize(uint256 _setValue) public initializer {
MyStorage storage $ = _getMyStorage();
$.value = _setValue;
}
function increaseValue() public {
MyStorage storage $ = _getMyStorage();
$.value += 10;
}
}
मान लीजिए कि हम अंत के अलावा कहीं और struct में एक वेरिएबल डालकर ContractB में MyStorage struct को गड़बड़ (mess up) कर देते हैं:
/// @custom:storage-location erc7201:ContractA.storage.MyStorage
struct MyStorage {
uint256 badInsert; // this should not be here, it should be at the end
uint256 value;
}
उस बदलाव के साथ, टूल निम्नलिखित त्रुटि (error) प्रस्तुत करेगा:

एक और समस्या जिसे टूल रोकेगा वह है namespace स्लॉट का नाम बदलना। मान लीजिए कि हम struct के ऊपर दिए गए एनोटेशन को निम्नानुसार बदलते हैं:
/// @custom:storage-location erc7201:ContractA.storage.MyStorage
/// @custom:storage-location erc7201:ContractA.storage.MyStorage2
अब हमें निम्नलिखित त्रुटि (error) मिलती है:

अपग्रेड्स के बीच Namespaces को हटाया नहीं जाना चाहिए।
Parent initializers को कॉल करने में विफलता को स्वचालित रूप से नहीं पकड़ा जा सकता है
निम्नलिखित उदाहरण में, ContractA और ContractB दोनों Base से इनहेरिट (inherit) करते हैं। हालाँकि, उनमें से कोई भी पैरेंट के इनिशियलाइज़र को कॉल नहीं करता है, जो कि एक बग (bug) है। चूंकि यह समझना कि क्या कोई फ़ंक्शन एक इनिशियलाइज़र के रूप में कार्य करता है, एक अर्थपूर्ण व्याख्या (semantic interpretation) की आवश्यकता होती है, इसलिए टूल स्वचालित रूप से इस समस्या को नहीं पकड़ सकता है। देव (dev) या ऑडिटर (auditor) को मैन्युअल रूप से यह जाँचना चाहिए कि सभी इनिशियलाइज़र्स को ठीक से कॉल किया गया है (आमतौर पर इस प्रकार की समस्या को यूनिट टेस्ट्स के साथ आसानी से पकड़ा जा सकता है यदि वेरिएबल्स को गैर-शून्य (non-zero) मूल्य में इनिशियलाइज़ किया गया हो)।
निम्नलिखित कोड टूल के साथ किसी भी समस्या को ट्रिगर नहीं करेगा भले ही __Base_init फ़ंक्शन को कॉल नहीं किया गया हो।

निष्कर्ष (Conclusion)
लेख में विस्तार से बताया गया है कि OpenZeppelin अपग्रेड Foundry प्लगइन का उपयोग कैसे करें, प्लगइन का उपयोग यूनिट टेस्ट्स और Foundry स्क्रिप्ट्स के लिए कैसे किया जा सकता है, और यह डिप्लॉयमेंट और अपग्रेड्स की बहु-चरणीय (multistep) प्रक्रिया को कैसे सरल बनाता है। हमने दिखाया कि डिप्लॉयमेंट के लिए पर्यावरण (environment) कैसे सेट अप किया जाए, और कुछ उदाहरण दिए कि टूल स्वचालित रूप से विभिन्न गलतियों को कैसे पकड़ सकता है।
लेखकत्व और आभार (Authorship and Acknowledgements)
यह लेख RareSkills के सहयोग से Pari Tomar द्वारा लिखा गया था।
हम इस लेख के ड्राफ्ट पर उनकी सहायक टिप्पणियों के लिए OpenZeppelin के Eric Lau को धन्यवाद देना चाहते हैं।
मूल रूप से 28 अगस्त, 2024 को प्रकाशित