Upgrading many proxies at the same time


I'm building a custom NFT that is meant to be upgradeable. I'm currently using the recommended UUPS proxies,

contract Blabla is Initializable, ERC721Upgradeable, ERC721BurnableUpgradeable, AccessControlUpgradeable, UUPSUpgradeable {

which are created from a factory method (that lives in a separate smart contract) like this:

bytes memory data = // some initialization stuff
ERC1967Proxy proxy = new ERC1967Proxy(implAddress, data);

I'm deploying all with hardhat and it works great - even the upgrades.

The situation at the moment is:

Proxy ----> Impl Address

My understanding is that if I want my NFT to be upgraded, I should upgrade proxy. However, I expect a ton of proxies out there, and I'd like to avoid having to update each individual one. This would be painful both for users or even myself.

Instead, what I was thinking about was:

Proxy, Proxy2 ----> Implementation Proxy -----> Implementation v1

In this context, if I upgraded Implementation Proxy, it would make both Proxy and Proxy2 to point to a newer implementation:

Proxy, Proxy2 ----> Implementation Proxy -----> Implementation v2

Any thoughts on the pros/cons of this? Is this something that makes sense or are there any red flags?

Thanks a lot in advance.

:1234: Code to reproduce

:computer: Environment

Maybe there is some misunderstanding on how UUPS works.
The proxy contract holds the contract data and calls the code in the implementation contract.
The implementation contract holds only code.
To upgrade the code, you need to deploy a new implementation and set the address in the the proxy

Why do you expect to have a ton of proxies for the same implementation?

Yeah, that's how I understand UUPS too, so we have the same understanding!

Why do you expect to have a ton of proxies for the same implementation?

In my dApp, users can create as many proxies as they want. Let me give you an example: let's say that my app delivers smart contract wallets. Users can create as many as they want, and each wallet they create is actually a proxy that points to a specific implementation.

In this context, if I want all users to use the latest implementation, every user (or admins) will have to set the new implementation address in every proxy they manage.

To make operations easier, I'm curious if it's possible to have another proxy:
Smart contract wallet (user managed) --> Implementation Proxy --> Implementation contract

If I only update the implementation address in Implementation Proxy, in theory, all smart contract wallets would point to the new one, right?

I asked because you had mentioned updating an NFT contract, wouldn't make much sense in that case. Makes total sense for a wallet contract. In theory you can have as many proxies in the chain as gas limits allow, every call add a bit of gas to the cost of interacting with the contract.

if your proxyA (wallet) points to proxyB and proxyB to implementation, then updating the implementation address on proxyB will update everybody.

if you don't plan on ever upgrading proxyA/wallet you might be able to use a clone instead of a proxy and save some deployment gas

Why wouldn't it make sense for an NFT contract?
Let's say my users are community managers and through my app, they can create as many NFT contracts as they want (each with its corresponding name-symbol of the community). Every NFT contract would point to a specific implementation, so they are proxies (this is because my NFTs contain custom logic on top of the standard).

If I wanted to upgrade the logic all at once, I couldn't do it. I'd need every community manager to upgrade every single NFT they manage for their communities.

I think what you want is a BeaconProxy take a look at those. They work in a different way and serve that purpose.

From OZ docs:

" An alternative upgradeability mechanism is provided in Beacon. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. In this pattern, the proxy contract doesn’t hold the implementation address in storage like UpgradeableProxy, but the address of a UpgradeableBeacon contract, which is where the implementation address is actually stored and retrieved from. The upgrade operations that change the implementation contract address are then sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded."

1 Like

Ohh yes! This is exactly what I want. Thank you for sharing.

Any thoughts on whether this is recommended or not? From the docs, it sounded like UUPS were recommended above any other method. Thanks again!

AFAIK its fully supported and recommended if it is your use case. This avoids doing individual upgrades if you have multiple proxies deployed and want to update them in one shot.

Makes sense. Yes, and actually, I think these proxies can be cheap clones that point to the same beacon, no need to deploy a whole proxy all the time.

I dont think Clones are upgradeable. You would need to use BeaconProxy Refer to the OZ docs and check their examples.

" The Clones library provides a way to deploy minimal non-upgradeable proxies for cheap"

1 Like