NomicFoundation / hardhat-ignition

Hardhat Ignition is a declarative deployment system that enables you to deploy your smart contracts without navigating the mechanics of the deployment process.
https://hardhat.org/ignition
MIT License
104 stars 22 forks source link

Ignition guide for creating UUPS upgradeable module #788

Open kanej opened 2 months ago

kanej commented 2 months ago

The current upgrade-able contract docs guide is based around the TransparentUpgradeableProxy pattern:

https://hardhat.org/ignition/docs/guides/upgradeable-proxies

We should add a guide for the Universal Upgradeable Proxy Standard pattern.

See the Open Zeppelin docs for a description of the difference: https://docs.openzeppelin.com/contracts/4.x/api/proxy#transparent-vs-uups

baijunjie commented 2 months ago

Does hardhat-ignition currently support deploying UUPS contracts? I can’t find any documentation, is there any plan to support this feature?

kanej commented 1 month ago

There is no explicit feature supporting UUPS, the API allows you to express contract deployments and calls. This task is to provide documentation for how to do that.

AddressXception commented 1 month ago

you just need to use the generic ERC1967Proxy:

import { buildModule } from '@nomicfoundation/hardhat-ignition/modules';

const ProxyModule = buildModule('ProxyModule', (builder) => {
  // Deploy the implementation contract
  const implementation = builder.contract('MyContract');

  // Encode the initialize function call for the contract.
  const initialize = builder.encodeFunctionCall(implementation, 'initialize', [
    'param1',
    'param2',
  ]);

  // Deploy the ERC1967 Proxy, pointing to the implementation
  const proxy = builder.contract('ERC1967Proxy', [implementation, initialize]);

  return { proxy };
});

export const MyContractModule = buildModule('MyContractModule', (builder) => {
  // Get the proxy from the previous module.
  const { proxy } = builder.useModule(ProxyModule);

  // Create a contract instance using the deployed proxy's address.
  const instance = builder.contractAt('MyContract', proxy);

  return { instance, proxy };
});

export default MyContractModule;
baijunjie commented 1 month ago

you just need to use the generic ERC1967Proxy:

import { buildModule } from '@nomicfoundation/hardhat-ignition/modules';

const ProxyModule = buildModule('ProxyModule', (builder) => {
  // Deploy the implementation contract
  const implementation = builder.contract('MyContract');

  // Encode the initialize function call for the contract.
  const initialize = builder.encodeFunctionCall(implementation, 'initialize', [
    'param1',
    'param2',
  ]);

  // Deploy the ERC1967 Proxy, pointing to the implementation
  const proxy = builder.contract('ERC1967Proxy', [implementation, initialize]);

  return { proxy };
});

export const MyContractModule = buildModule('MyContractModule', (builder) => {
  // Get the proxy from the previous module.
  const { proxy } = builder.useModule(ProxyModule);

  // Create a contract instance using the deployed proxy's address.
  const instance = builder.contractAt('MyContract', proxy);

  return { instance, proxy };
});

export default MyContractModule;

Thank you for providing the solution.

If my contract is inherited from @openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol, can I use this method for deployment? Or is there a specific deployment method for UUPSUpgradeable?

2075 commented 2 days ago

how would you approach an upgrade module based on this example?