Deploying Custom Proxy


I want to implement a custom proxy with a filtering mechanism that allows only certain addresses to make calls to the implementation contract. So basically a regular TUProxy with something like an onlyWhitelisted modifier on the _delegate function.

I have 2 questions:

  1. After writing a contract that inherits TUProxy and customizes its behavior, how do I use the hardhat upgrades plugin to deploy it? I found the kind parameter in the docs but it only appears to accept two options ("transparent" or "uups").
  2. I've seen recommendations for using the UUPS proxy instead of the Transparent proxy, but I couldn't find the UUPS contract. I understand that the implementation contract needs to inherit from UUPSUpgradeable, but which class is the actual proxy that gets deployed alongside the implementation? Is it ERC1967Proxy? Would that be the contract I should inherit from if I want to customize the proxy?


From the Proxies documentation:

UUPS proxies are implemented using an ERC1967Proxy. Note that this proxy is not by itself upgradeable. It is the role of the implementation to include, alongside the contract’s logic, all the code necessary to update the implementation’s address that is stored at a specific slot in the proxy’s storage space. This is where the UUPSUpgradeable contract comes in. Inheriting from it (and overriding the _authorizeUpgrade function with the relevant access control mechanism) will turn your contract into a UUPS compliant implementation.

Hi @wildb,

  • ERC1967Proxy is the UUPS proxy contract.
  • UUPSUpgradeable is what your implementation contract should inherit (if you will be using it with a UUPS proxy), in order for it to have the upgradeTo function.
  • TransparentUpgradeableProxy is the Transparent proxy contract.

See docs for the different contracts above.

If you want to modify when the proxy does delegate calls, then you should inherit ERC1967Proxy or TransparentUpgradeableProxy. Be careful to avoid storage slot conflicts with the implementation though.

To use a custom proxy, you can deploy it directly, then import it into the plugin with the forceImport function. See this testcase for an example of how to deploy a proxy directly and then import it.

1 Like

Thank you Eric, this is very helpful!

Are there any guidelines on how to do this?

And another question that I'm afraid might be silly but I can't figure it out -
When working with a transparent proxy I also need to deploy a ProxyAdmin manually.
How do I do that from my JS environment without manually copying and compile the code from your repo?

I guess maybe just like this?

const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json');

If you are using storage in your proxy itself, one way to avoid storage slot conflicts with the implementation is for the proxy to use unstructured storage with pseudo random slots.

That should work, then get the ContractFactory using await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode);

1 Like