From 451d2179c89ab8aec7a5076a870bb194875e6c97 Mon Sep 17 00:00:00 2001 From: Robert Magier <34715949+robertmagier@users.noreply.github.com> Date: Fri, 20 Dec 2024 05:44:17 +0100 Subject: [PATCH] Support TypeChain in deploy and upgrade proxy functions - continuation of work done on #535 (#1099) Co-authored-by: Eric Lau --- packages/plugin-hardhat/CHANGELOG.md | 4 +++ packages/plugin-hardhat/package.json | 2 +- .../plugin-hardhat/src/deploy-beacon-proxy.ts | 21 +++++++++------ .../plugin-hardhat/src/deploy-contract.ts | 27 ++++++++++++------- packages/plugin-hardhat/src/deploy-proxy.ts | 17 +++++++----- .../plugin-hardhat/src/type-extensions.ts | 3 +++ packages/plugin-hardhat/src/upgrade-proxy.ts | 17 +++++++----- .../src/utils/contract-instance.ts | 9 ++++--- 8 files changed, 66 insertions(+), 34 deletions(-) diff --git a/packages/plugin-hardhat/CHANGELOG.md b/packages/plugin-hardhat/CHANGELOG.md index 97f40e6c4..68fb41954 100644 --- a/packages/plugin-hardhat/CHANGELOG.md +++ b/packages/plugin-hardhat/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 3.8.0 (2024-12-19) + +- Support TypeChain in `deployProxy`, `upgradeProxy`, `deployBeaconProxy`, and `defender.deployContract`. ([#1099](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1099)) + ## 3.7.0 (2024-12-04) - Add `proxyFactory` and `deployFunction` options which can be used to deploy a custom proxy contract. ([#1104](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1104)) diff --git a/packages/plugin-hardhat/package.json b/packages/plugin-hardhat/package.json index 298f8dec9..619d5d33e 100644 --- a/packages/plugin-hardhat/package.json +++ b/packages/plugin-hardhat/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/hardhat-upgrades", - "version": "3.7.0", + "version": "3.8.0", "description": "", "repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/plugin-hardhat", "license": "MIT", diff --git a/packages/plugin-hardhat/src/deploy-beacon-proxy.ts b/packages/plugin-hardhat/src/deploy-beacon-proxy.ts index 63686dbed..9755adf5c 100644 --- a/packages/plugin-hardhat/src/deploy-beacon-proxy.ts +++ b/packages/plugin-hardhat/src/deploy-beacon-proxy.ts @@ -1,5 +1,5 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types'; -import { Contract, ContractFactory } from 'ethers'; +import { ContractFactory } from 'ethers'; import { Manifest, @@ -24,27 +24,32 @@ import { } from './utils'; import { enableDefender } from './defender/utils'; import { getContractInstance } from './utils/contract-instance'; +import { ContractTypeOfFactory } from './type-extensions'; export interface DeployBeaconProxyFunction { - ( + ( beacon: ContractAddressOrInstance, - attachTo: ContractFactory, + attachTo: F, args?: unknown[], opts?: DeployBeaconProxyOptions, - ): Promise; - (beacon: ContractAddressOrInstance, attachTo: ContractFactory, opts?: DeployBeaconProxyOptions): Promise; + ): Promise>; + ( + beacon: ContractAddressOrInstance, + attachTo: F, + opts?: DeployBeaconProxyOptions, + ): Promise>; } export function makeDeployBeaconProxy( hre: HardhatRuntimeEnvironment, defenderModule: boolean, ): DeployBeaconProxyFunction { - return async function deployBeaconProxy( + return async function deployBeaconProxy( beacon: ContractAddressOrInstance, - attachTo: ContractFactory, + attachTo: F, args: unknown[] | DeployBeaconProxyOptions = [], opts: DeployBeaconProxyOptions = {}, - ) { + ): Promise> { if (!(attachTo instanceof ContractFactory)) { throw new UpgradesError( `attachTo must specify a contract factory`, diff --git a/packages/plugin-hardhat/src/deploy-contract.ts b/packages/plugin-hardhat/src/deploy-contract.ts index 080d7e677..93c3a746f 100644 --- a/packages/plugin-hardhat/src/deploy-contract.ts +++ b/packages/plugin-hardhat/src/deploy-contract.ts @@ -1,7 +1,7 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; -import type { ContractFactory, Contract } from 'ethers'; +import type { ContractFactory } from 'ethers'; -import { deploy, DeployContractOptions, EthersOrDefenderDeployment } from './utils'; +import { deploy, DeployContractOptions, DeployTransaction, EthersOrDefenderDeployment } from './utils'; import { DeployData, getDeployData } from './utils/deploy-impl'; import { enableDefender } from './defender/utils'; import { @@ -9,19 +9,29 @@ import { inferProxyKind, UpgradesError, inferInitializable, + Deployment, + RemoteDeploymentId, } from '@openzeppelin/upgrades-core'; import { getContractInstance } from './utils/contract-instance'; +import { ContractTypeOfFactory } from './type-extensions'; export interface DeployContractFunction { - (Contract: ContractFactory, args?: unknown[], opts?: DeployContractOptions): Promise; - (Contract: ContractFactory, opts?: DeployContractOptions): Promise; + ( + Contract: F, + args?: unknown[], + opts?: DeployContractOptions, + ): Promise>; + ( + Contract: ContractFactory, + opts?: DeployContractOptions, + ): Promise>; } async function deployNonUpgradeableContract( hre: HardhatRuntimeEnvironment, Contract: ContractFactory, opts: DeployContractOptions, -) { +): Promise & DeployTransaction & RemoteDeploymentId> { const deployData = await getDeployData(hre, Contract, opts); if (!opts.unsafeAllowDeployContract) { @@ -34,7 +44,6 @@ async function deployNonUpgradeableContract( Contract, ...deployData.fullOpts.constructorArgs, ); - return deployment; } @@ -52,11 +61,11 @@ function assertNonUpgradeable(deployData: DeployData) { } export function makeDeployContract(hre: HardhatRuntimeEnvironment, defenderModule: boolean): DeployContractFunction { - return async function deployContract( - Contract, + return async function deployContract( + Contract: F, args: unknown[] | DeployContractOptions = [], opts: DeployContractOptions = {}, - ) { + ): Promise> { if (!Array.isArray(args)) { opts = args; args = []; diff --git a/packages/plugin-hardhat/src/deploy-proxy.ts b/packages/plugin-hardhat/src/deploy-proxy.ts index 370464f56..b912102c3 100644 --- a/packages/plugin-hardhat/src/deploy-proxy.ts +++ b/packages/plugin-hardhat/src/deploy-proxy.ts @@ -1,5 +1,5 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types'; -import type { ContractFactory, Contract } from 'ethers'; +import type { ContractFactory } from 'ethers'; import { Manifest, @@ -25,18 +25,23 @@ import { import { enableDefender } from './defender/utils'; import { getContractInstance } from './utils/contract-instance'; import { getInitialOwner } from './utils/initial-owner'; +import { ContractTypeOfFactory } from './type-extensions'; export interface DeployFunction { - (ImplFactory: ContractFactory, args?: unknown[], opts?: DeployProxyOptions): Promise; - (ImplFactory: ContractFactory, opts?: DeployProxyOptions): Promise; + ( + ImplFactory: F, + args?: unknown[], + opts?: DeployProxyOptions, + ): Promise>; + (ImplFactory: F, opts?: DeployProxyOptions): Promise>; } export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule: boolean): DeployFunction { - return async function deployProxy( - ImplFactory: ContractFactory, + return async function deployProxy( + ImplFactory: F, args: unknown[] | DeployProxyOptions = [], opts: DeployProxyOptions = {}, - ) { + ): Promise> { if (!Array.isArray(args)) { opts = args; args = []; diff --git a/packages/plugin-hardhat/src/type-extensions.ts b/packages/plugin-hardhat/src/type-extensions.ts index 18808b499..76b72b4a9 100644 --- a/packages/plugin-hardhat/src/type-extensions.ts +++ b/packages/plugin-hardhat/src/type-extensions.ts @@ -2,6 +2,9 @@ import 'hardhat/types/runtime'; import 'hardhat/types/config'; import type { HardhatUpgrades, DefenderHardhatUpgrades } from '.'; +import { ContractFactory } from 'ethers'; + +export type ContractTypeOfFactory = ReturnType & ReturnType; declare module 'hardhat/types/runtime' { export interface HardhatRuntimeEnvironment { diff --git a/packages/plugin-hardhat/src/upgrade-proxy.ts b/packages/plugin-hardhat/src/upgrade-proxy.ts index 0bf2acde9..74e421e44 100644 --- a/packages/plugin-hardhat/src/upgrade-proxy.ts +++ b/packages/plugin-hardhat/src/upgrade-proxy.ts @@ -1,5 +1,5 @@ import { HardhatRuntimeEnvironment } from 'hardhat/types'; -import type { ethers, ContractFactory, Contract, Signer } from 'ethers'; +import type { ethers, ContractFactory, Signer } from 'ethers'; import debug from './utils/debug'; import { getAdminAddress, getCode, getUpgradeInterfaceVersion, isEmptySlot } from '@openzeppelin/upgrades-core'; @@ -18,19 +18,24 @@ import { attachProxyAdminV4, attachProxyAdminV5, } from './utils/attach-abi'; +import { ContractTypeOfFactory } from './type-extensions'; -export type UpgradeFunction = ( +export type UpgradeFunction = ( proxy: ContractAddressOrInstance, - ImplFactory: ContractFactory, + ImplFactory: F, opts?: UpgradeProxyOptions, -) => Promise; +) => Promise>; export function makeUpgradeProxy( hre: HardhatRuntimeEnvironment, defenderModule: boolean, log = debug, ): UpgradeFunction { - return async function upgradeProxy(proxy, ImplFactory, opts: UpgradeProxyOptions = {}) { + return async function upgradeProxy( + proxy: ContractAddressOrInstance, + ImplFactory: F, + opts: UpgradeProxyOptions = {}, + ): Promise> { disableDefender(hre, defenderModule, opts, upgradeProxy.name); const proxyAddress = await getContractAddress(proxy); @@ -44,7 +49,7 @@ export function makeUpgradeProxy( const inst = attach(ImplFactory, proxyAddress); // @ts-ignore Won't be readonly because inst was created through attach. inst.deployTransaction = upgradeTx; - return inst; + return inst as ContractTypeOfFactory; }; type Upgrader = (nextImpl: string, call?: string) => Promise; diff --git a/packages/plugin-hardhat/src/utils/contract-instance.ts b/packages/plugin-hardhat/src/utils/contract-instance.ts index 321e2cf36..cafb4cc9e 100644 --- a/packages/plugin-hardhat/src/utils/contract-instance.ts +++ b/packages/plugin-hardhat/src/utils/contract-instance.ts @@ -6,6 +6,7 @@ import { DeployTransaction, DefenderDeploy } from '.'; import { waitForDeployment } from '../defender/utils'; import { Deployment, RemoteDeploymentId, DeployOpts } from '@openzeppelin/upgrades-core'; import { attach } from './ethers'; +import { ContractTypeOfFactory } from '../type-extensions'; /** * Gets a contract instance from a deployment, where the deployment may be remote. @@ -19,13 +20,13 @@ import { attach } from './ethers'; * @param deployTransaction The transaction that deployed the contract, if available * @returns The contract instance */ -export function getContractInstance( +export function getContractInstance( hre: HardhatRuntimeEnvironment, - contract: ContractFactory, + contract: F, opts: DeployOpts & DefenderDeploy, deployment: Deployment & DeployTransaction & RemoteDeploymentId, -) { - const instance = attach(contract, deployment.address); +): ContractTypeOfFactory { + const instance = attach(contract, deployment.address) as ContractTypeOfFactory; // @ts-ignore Won't be readonly because instance was created through attach. instance.deploymentTransaction = () => deployment.deployTransaction ?? null; // Convert undefined to null to conform to ethers.js types.