मुख्य सामग्री पर जाएं
एक proxy deployment में दो contracts होते हैं: proxy, जो हर call को delegate करता है, और implementation, जो logic रखता है। दोनों को अलग-अलग verify किया जाना चाहिए। किसी भी proxy के लिए जो ERC-1967 का पालन करता है, Tenderly explorer standard implementation storage slot पढ़ता है, इसलिए एक बार दोनों contracts verify हो जाने के बाद, proxy address पर calls को implementation के ABI के साथ decode किया जाता है। यह गाइड एक Virtual Environment पर एक 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
दोनों libraries अलग-अलग भूमिकाएं निभाती हैं: 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
इस contract के तीन भाग proxy pattern के लिए मायने रखते हैं:
  1. constructor() { _disableInitializers(); } implementation को लॉक करता है। केवल proxy को state रखनी चाहिए; यह constructor implementation address पर direct initialize call को revert कर देता है।
  2. initialize(...) constructor को replace करता है। proxy इसे एक बार delegatecall करता है, उसी transaction में जो proxy को deploy करता है।
  3. _authorizeUpgrade(...) आवश्यक UUPS permissioning hook है। यह हर upgradeToAndCall पर चलता है; यदि यह revert होता है, तो upgrade block हो जाता है।

deploy script

script/DeployProxy.s.sol
script logic contract को deploy करती है, 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
Output का अपेक्षित अंत:
दूसरे submission पथ पर ध्यान दें: proxy को 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
फिर proxy के माध्यम से एक implementation function को call करें:
Tenderly Dashboard में, proxy के address page में verified 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 upgrades upgradeToAndCall(newImpl, callData) के माध्यम से जारी किए जाते हैं, जो UUPSUpgradeable से inherit होता है। एक V2 जोड़ें:
src/CounterV2.sol
V2 लिखते समय Storage layout बाधाएं:
  • 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
इसे उसी verify flags के साथ चलाएं:
showLineNumbers
केवल CounterV2 deploy और verify होता है (output (1) contracts की रिपोर्ट करता है)। proxy का address नहीं बदलता; केवल इसका ERC-1967 slot बदलता है। script return होने के बाद:
showLineNumbers
Explorer में proxy address अब 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
proxy को deploy समय पर उपयोग किए गए exact constructor arguments की आवश्यकता होती है, ABI-encoded:
showLineNumbers
सही करने के लिए तीन विवरण:
  1. contract path lib/ location का उपयोग करता है, src/ नहीं। proxy OpenZeppelin का code है, इसलिए verifier जिस source से match करता है वह lib/openzeppelin-contracts/... में रहता है। forge script --verify इसे स्वचालित रूप से resolve करता है; forge verify-contract नहीं।
  2. initData को deploy-time initData से exactly match करना चाहिए। भले ही deployment के बाद state बदल गई हो, constructor argument मूल initialize(owner, 42) calldata है जिसके साथ proxy bytecode बनाया गया था।
  3. proxy का constructor signature (address, bytes) है, और forge verify-contract पहले से ABI-encoded रूप की अपेक्षा करता है, जो cast abi-encode उत्पन्न करता है।

अगले कदम