ERC1967Proxy के पीछे एक UUPS implementation (CounterV1) deploy करती है, दोनों को एक forge script run में verify करती है, implementation को CounterV2 में upgrade करती है, और एक proxy को फिर से verify करना कवर करती है जो बिना verification के deploy किया गया था। Hardhat equivalent के लिए, देखें Proxy Contracts को Verify करना।
शुरू करने से पहले
- Deploy और verify contracts से setup पूरा करें: एक Virtual Environment, एक funded deployer address, और
cbor_metadata = trueतथाbytecode_hash = "ipfs"के साथ एकfoundry.toml। - environment variables सेट करें:
- base library के साथ OpenZeppelin के upgradeable contracts स्थापित करें:
- दोनों remappings को
remappings.txtमें जोड़ें:
remappings.txt
openzeppelin-contracts proxy (ERC1967Proxy) प्रदान करता है, और openzeppelin-contracts-upgradeable implementation building blocks (Initializable, UUPSUpgradeable, OwnableUpgradeable) प्रदान करता है। upgradeable versions constructors को initializer functions से बदलते हैं, क्योंकि जब state proxy के पीछे रहती है तो constructors नहीं चलते।
implementation contract
src/CounterV1.sol
constructor() { _disableInitializers(); }implementation को लॉक करता है। केवल proxy को state रखनी चाहिए; यह constructor implementation address पर directinitializecall को revert कर देता है।initialize(...)constructor को replace करता है। proxy इसे एक बार delegatecall करता है, उसी transaction में जो proxy को deploy करता है।_authorizeUpgrade(...)आवश्यक UUPS permissioning hook है। यह हरupgradeToAndCallपर चलता है; यदि यह revert होता है, तो upgrade block हो जाता है।
deploy script
script/DeployProxy.s.sol
abi.encodeCall (जो abi.encodeWithSignature के विपरीत compile time पर arguments को type-check करता है) के साथ initialize calldata बनाती है, और proxy को deploy करती है। proxy का constructor implementation address को ERC-1967 implementation slot में store करता है और उसके विरुद्ध initData को delegatecall करता है, atomically proxy के storage को initialize करता है।
दोनों contracts को deploy और verify करें
Proxy और implementation एक script में दो contracts हैं, इसलिए command किसी भी multi-contract deployment जैसा हीforge script आकार है:
showLineNumbers
lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy के रूप में verify किया गया है, आपके lib/ tree से OpenZeppelin contract, src/ में contract नहीं। Foundry इसे स्वचालित रूप से resolve करता है क्योंकि script का broadcast log प्रत्येक new और उसके bytecode को रिकॉर्ड करता है।
proxy linkage की पुष्टि करें
Explorer proxy के माध्यम से calls को decode करता है जब proxy और implementation दोनों verify हों। यह पुष्टि करने के लिए कि proxy delegate कर रहा है, ERC-1967 implementation slot को सीधे पढ़ें:showLineNumbers
ERC1967Proxy source, linked implementation address के साथ एक proxy tag, और एक ABI view दिखाई देता है जिसमें implementation के functions शामिल हैं। proxy.increment() या proxy.setNumber(...) को call करने वाले Transactions call trace में implementation के function names और parameter labels के साथ decode किए जाते हैं।
यदि explorer proxy को दिखाता है लेकिन implementation के ABI के साथ calls को decode नहीं करता, तो सबसे सामान्य कारण यह है कि दोनों contracts में से केवल एक verify है। जो भी छूट गया है उस पर फिर से
forge verify-contract चलाएं।implementation को upgrade करें
UUPS upgradesupgradeToAndCall(newImpl, callData) के माध्यम से जारी किए जाते हैं, जो UUPSUpgradeable से inherit होता है। एक V2 जोड़ें:
src/CounterV2.sol
- V1 से inherit करें, या V1 के storage layout को verbatim copy करें। Storage slots positional हैं; एक मौजूदा state variable से पहले एक नया state variable जोड़ने से proxy का state corrupt हो जाता है।
- नए state variables V1 के मौजूदा variables के बाद जाते हैं, कभी बीच में नहीं।
- मौजूदा variable types को न बदलें या declarations को न reorder करें।
script/UpgradeProxy.s.sol
showLineNumbers
CounterV2 deploy और verify होता है (output (1) contracts की रिपोर्ट करता है)। proxy का address नहीं बदलता; केवल इसका ERC-1967 slot बदलता है। script return होने के बाद:
showLineNumbers
CounterV2 के ABI के विरुद्ध decode करता है। पुराना CounterV1 implementation अपने मूल address पर verified रहता है लेकिन अब सक्रिय implementation नहीं है।
एक proxy को verify करें जो पहले से deployed है
यदि आपने बिना--verify के deploy किया, तो प्रत्येक contract के लिए अलग से forge verify-contract चलाएं।
implementation new CounterV1() के माध्यम से बिना constructor arguments के deploy किया गया था, इसलिए किसी --constructor-args flag की आवश्यकता नहीं है:
showLineNumbers
showLineNumbers
- contract path
lib/location का उपयोग करता है,src/नहीं। proxy OpenZeppelin का code है, इसलिए verifier जिस source से match करता है वहlib/openzeppelin-contracts/...में रहता है।forge script --verifyइसे स्वचालित रूप से resolve करता है;forge verify-contractनहीं। initDataको deploy-timeinitDataसे exactly match करना चाहिए। भले ही deployment के बाद state बदल गई हो, constructor argument मूलinitialize(owner, 42)calldata है जिसके साथ proxy bytecode बनाया गया था।- proxy का constructor signature
(address, bytes)है, औरforge verify-contractपहले से ABI-encoded रूप की अपेक्षा करता है, जोcast abi-encodeउत्पन्न करता है।
अगले कदम
- Contracts deploy और verify करें: Virtual Environment पर base Foundry और Hardhat verification flow।
- Foundry का उपयोग करके Smart Contract Verification: Tenderly के verification API के माध्यम से public networks पर contracts verify करना।
- Proxy Contracts को Verify करना: UUPS, Transparent, और Beacon proxies के लिए Hardhat plugin flow।