> For the complete documentation index, see [llms.txt](https://blockchain-journal-hope-mabuza.gitbook.io/blockchain-journal-hope-mabuza-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blockchain-journal-hope-mabuza.gitbook.io/blockchain-journal-hope-mabuza-docs/smart-contracts/week-5-upgradable-smart-contracts-and-nft-minting..md).

# Week 5 : Upgradable Smart Contracts & NFT Minting.

#### Day 29 <a href="#day-29" id="day-29"></a>

**Why Would You Make an Immutable Contract Upgradeable?**

* Most mainnet projects use proxies because they let you upgrade a contract without losing its stored data. You can fix bugs or add features without changing the contract address, which means users and integrations don't break. But coming into this week I was genuinely confused about why this exists at all. Smart contracts are supposed to be immutable, that's one of the things that makes them trustworthy. So why would you want to change them?
* The answer is that immutability is about the data and the rules being tamper-proof, not about the code being frozen forever. In the real world, bugs get discovered, requirements change, and projects need to evolve. A proxy lets you separate the storage from the logic, so the data stays safe and permanent while the logic can be swapped out when needed. That reframing helped a lot. I'm looking forward to seeing how it actually works in practice this week.

***

#### Day 30 <a href="#day-30" id="day-30"></a>

**Converting an ERC20 to a UUPS Proxy**

* Today I took my basic ERC20 token and converted it into an upgradeable contract using the UUPS proxy pattern. The first and most important mindset shift was that I couldn't use a constructor anymore. Proxy contracts don't run constructors, so instead I had to use an `initialize()` function that gets called once after deployment to set everything up. That took a moment to get used to.
* I also added role-based minting using `AccessControlUpgradeable`. I created a `MINTER_ROLE` so only accounts with that role can mint new tokens, which made the contract feel more structured and secure compared to just letting the owner do everything. For upgrades, I overrode `_authorizeUpgrade()` so that only the admin can approve a new implementation, which is how upgrade permissions are controlled in UUPS.
* After deploying the proxy to Sepolia and confirming the proxy address, I created a V2 version and tested upgrading locally with Hardhat. Seeing the upgrade work without losing any state was the moment it really clicked for me. The data survived the upgrade exactly as expected.

***

#### Day 31 <a href="#day-31" id="day-31"></a>

**Storage Collisions and Why EIP-1967 Exists**

* Today I reflected on why protecting storage slots is just as important as protecting private keys. I had been confused about why the implementation and proxy addresses are stored in special slots far away from normal variables. But after looking at my deploy scripts more carefully, it made sense.
* When deploying a proxy, the implementation address gets stored in slot 0 by default. But when the proxy delegates a call to the logic contract, the first state variable in that logic contract also tries to use slot 0. That means it can overwrite the implementation address and completely break the contract. That is what a storage collision is.
* EIP-1967 solves this by defining special reserved storage slots for the implementation and admin addresses, slots that are so far from the normal variable range that they will never clash. Private keys protect who controls the contract. Storage layout protects the contract's state. If storage gets corrupted during an upgrade, the contract can break permanently even if the owner is fully secure. That was a genuinely important thing to understand.

***

#### Day 32 <a href="#day-32" id="day-32"></a>

**Building an Upgradable NFT Minter**

* Today I built an upgradable NFT minter using OpenZeppelin's upgradeable ERC-721 contracts. Building on what I learned yesterday, I made sure the storage layout was safe from the start. These are the features I added on top of the base contract:
  * A maximum supply limit so it can't mint more than the cap.
  * An allowlist so only approved addresses can mint.
  * A soulbound toggle to control whether NFTs are transferable or not.
* I deployed the first version, minted 5 NFTs to test, and confirmed the supply tracking was working correctly. Then I performed a successful upgrade where I added a new feature, a baseURI setter and public mint toggle. The upgrade went through and the state, including total supply and balances, was fully preserved after the upgrade.
* What made this feel different from Day 30 was that I was making real design decisions rather than just following the steps. Deciding what features to put in V1 vs V2, thinking about what the allowlist mapping would look like in storage, and making sure the `__gap` was in place before upgrading all felt like actual engineering decisions rather than exercises.

Deployment addresses:

* Proxy V1 - `0x04Fa24Ef54616Db4BACfd0F6Be9475234685ee64`
* Proxy V2 - `0xcfaC232B675412a1A8Da84B90CAC0587a2DBbdcF`
* User mint transaction hash - `0x9ebff7fcae6c6e7cc00a18ebb682c0a8419bfa7430bdd3a180811e2063bb139`

***

#### Day 33 <a href="#day-33" id="day-33"></a>

**Proxy Lab Workshop**

* Today I completed the Week 5 Proxy Lab workshop which covered both the Transparent Proxy and UUPS patterns side by side. Having both in the same workshop made the differences between them much clearer than reading about them separately.
* For the Transparent Proxy part, I deployed CounterV1, interacted with it through the proxy, and then upgraded to CounterV2. After the upgrade I tested the new functions and confirmed the state was preserved. For the UUPS part, I deployed VaultV1, tested deposits through the proxy, and then upgraded to VaultV2 where a withdrawal fee feature was added. The balances stayed intact through the upgrade.
* In Part B I deployed a basic ERC721 NFT contract, minted an NFT, checked the owner using `ownerOf`, checked the metadata using `tokenURI`, and tested transferring it. At this point the ERC-721 standard is starting to feel familiar, which is a good sign.

Deployment addresses:

* **Transparent Proxy**\
  CounterV1 – `0x26BadE204A52fe4F301674acE606A4EB5bc58631`\
  CounterV2 – `0x7eEDFD9E6b43c4A60096bD8680ca549D0FdE2875`\
  Transparent Proxy – `0x99B2E5b97FF90d815258529896a84fc1eDeE5FAA`
* **UUPS Proxy**\
  Admin Proxy – `0x6d2406dC8eE70B332fE4d7320d1A7a09cf2db2b2`\
  UUPS Proxy – `0x2b603845015d1124Ba47Afa48D08A71B67733cEA`
* **NFT Contract**\
  MyNFT – `0xC5e6AE23C9AEA97f02A6ef51899aEfb65a9946d6`

***

#### Day 34 <a href="#day-34" id="day-34"></a>

**Struggling With UUPS Upgrades and Working Through It**

* While working on upgradeable contracts this week I ran into real challenges upgrading using the UUPS proxy pattern, especially moving from VaultV1 to VaultV2. The upgrade flow was confusing, particularly how `upgradeTo` works and how the proxy hands off to the new implementation.
* To get through it I went back to the UUPS code and focused specifically on `upgradeTo` and `_authorizeUpgrade`. That's when I understood that only the owner can trigger upgrades, which is the whole point of the authorization function. On Sepolia I used a manual upgrade approach: deployed V1, deployed V2 separately, called `upgradeTo` on the proxy with a high gas limit, checked the transaction receipt, verified the proxy's implementation slot had updated, and then tested V2 functions to confirm everything worked.
* What made this day valuable wasn't getting it right, it was having to slow down and actually understand each step rather than running a script and hoping it worked. Upgrades are one of those things where if you don't fully understand what's happening, you will break something eventually. Going through it manually forced that understanding.

***
