Skip to content

Commit

Permalink
Support TypeChain in deploy and upgrade proxy functions - continuatio…
Browse files Browse the repository at this point in the history
…n of work done on #535 (#1099)

Co-authored-by: Eric Lau <[email protected]>
  • Loading branch information
robertmagier and ericglau authored Dec 20, 2024
1 parent 7bdb777 commit 451d217
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 34 deletions.
4 changes: 4 additions & 0 deletions packages/plugin-hardhat/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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))
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-hardhat/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
21 changes: 13 additions & 8 deletions packages/plugin-hardhat/src/deploy-beacon-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
import { Contract, ContractFactory } from 'ethers';
import { ContractFactory } from 'ethers';

import {
Manifest,
Expand All @@ -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 {
(
<F extends ContractFactory>(
beacon: ContractAddressOrInstance,
attachTo: ContractFactory,
attachTo: F,
args?: unknown[],
opts?: DeployBeaconProxyOptions,
): Promise<Contract>;
(beacon: ContractAddressOrInstance, attachTo: ContractFactory, opts?: DeployBeaconProxyOptions): Promise<Contract>;
): Promise<ContractTypeOfFactory<F>>;
<F extends ContractFactory>(
beacon: ContractAddressOrInstance,
attachTo: F,
opts?: DeployBeaconProxyOptions,
): Promise<ContractTypeOfFactory<F>>;
}

export function makeDeployBeaconProxy(
hre: HardhatRuntimeEnvironment,
defenderModule: boolean,
): DeployBeaconProxyFunction {
return async function deployBeaconProxy(
return async function deployBeaconProxy<F extends ContractFactory>(
beacon: ContractAddressOrInstance,
attachTo: ContractFactory,
attachTo: F,
args: unknown[] | DeployBeaconProxyOptions = [],
opts: DeployBeaconProxyOptions = {},
) {
): Promise<ContractTypeOfFactory<F>> {
if (!(attachTo instanceof ContractFactory)) {
throw new UpgradesError(
`attachTo must specify a contract factory`,
Expand Down
27 changes: 18 additions & 9 deletions packages/plugin-hardhat/src/deploy-contract.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
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 {
getContractNameAndRunValidation,
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>;
(Contract: ContractFactory, opts?: DeployContractOptions): Promise<Contract>;
<F extends ContractFactory>(
Contract: F,
args?: unknown[],
opts?: DeployContractOptions,
): Promise<ContractTypeOfFactory<F>>;
<F extends ContractFactory>(
Contract: ContractFactory,
opts?: DeployContractOptions,
): Promise<ContractTypeOfFactory<F>>;
}

async function deployNonUpgradeableContract(
hre: HardhatRuntimeEnvironment,
Contract: ContractFactory,
opts: DeployContractOptions,
) {
): Promise<Required<Deployment> & DeployTransaction & RemoteDeploymentId> {
const deployData = await getDeployData(hre, Contract, opts);

if (!opts.unsafeAllowDeployContract) {
Expand All @@ -34,7 +44,6 @@ async function deployNonUpgradeableContract(
Contract,
...deployData.fullOpts.constructorArgs,
);

return deployment;
}

Expand All @@ -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<F extends ContractFactory>(
Contract: F,
args: unknown[] | DeployContractOptions = [],
opts: DeployContractOptions = {},
) {
): Promise<ContractTypeOfFactory<F>> {
if (!Array.isArray(args)) {
opts = args;
args = [];
Expand Down
17 changes: 11 additions & 6 deletions packages/plugin-hardhat/src/deploy-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
import type { ContractFactory, Contract } from 'ethers';
import type { ContractFactory } from 'ethers';

import {
Manifest,
Expand All @@ -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<Contract>;
(ImplFactory: ContractFactory, opts?: DeployProxyOptions): Promise<Contract>;
<F extends ContractFactory>(
ImplFactory: F,
args?: unknown[],
opts?: DeployProxyOptions,
): Promise<ContractTypeOfFactory<F>>;
<F extends ContractFactory>(ImplFactory: F, opts?: DeployProxyOptions): Promise<ContractTypeOfFactory<F>>;
}

export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule: boolean): DeployFunction {
return async function deployProxy(
ImplFactory: ContractFactory,
return async function deployProxy<F extends ContractFactory>(
ImplFactory: F,
args: unknown[] | DeployProxyOptions = [],
opts: DeployProxyOptions = {},
) {
): Promise<ContractTypeOfFactory<F>> {
if (!Array.isArray(args)) {
opts = args;
args = [];
Expand Down
3 changes: 3 additions & 0 deletions packages/plugin-hardhat/src/type-extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import 'hardhat/types/runtime';
import 'hardhat/types/config';

import type { HardhatUpgrades, DefenderHardhatUpgrades } from '.';
import { ContractFactory } from 'ethers';

export type ContractTypeOfFactory<F extends ContractFactory> = ReturnType<F['attach']> & ReturnType<F['deploy']>;

declare module 'hardhat/types/runtime' {
export interface HardhatRuntimeEnvironment {
Expand Down
17 changes: 11 additions & 6 deletions packages/plugin-hardhat/src/upgrade-proxy.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -18,19 +18,24 @@ import {
attachProxyAdminV4,
attachProxyAdminV5,
} from './utils/attach-abi';
import { ContractTypeOfFactory } from './type-extensions';

export type UpgradeFunction = (
export type UpgradeFunction = <F extends ContractFactory>(
proxy: ContractAddressOrInstance,
ImplFactory: ContractFactory,
ImplFactory: F,
opts?: UpgradeProxyOptions,
) => Promise<Contract>;
) => Promise<ContractTypeOfFactory<F>>;

export function makeUpgradeProxy(
hre: HardhatRuntimeEnvironment,
defenderModule: boolean,
log = debug,
): UpgradeFunction {
return async function upgradeProxy(proxy, ImplFactory, opts: UpgradeProxyOptions = {}) {
return async function upgradeProxy<F extends ContractFactory>(
proxy: ContractAddressOrInstance,
ImplFactory: F,
opts: UpgradeProxyOptions = {},
): Promise<ContractTypeOfFactory<F>> {
disableDefender(hre, defenderModule, opts, upgradeProxy.name);

const proxyAddress = await getContractAddress(proxy);
Expand All @@ -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<F>;
};

type Upgrader = (nextImpl: string, call?: string) => Promise<ethers.TransactionResponse>;
Expand Down
9 changes: 5 additions & 4 deletions packages/plugin-hardhat/src/utils/contract-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<F extends ContractFactory>(
hre: HardhatRuntimeEnvironment,
contract: ContractFactory,
contract: F,
opts: DeployOpts & DefenderDeploy,
deployment: Deployment & DeployTransaction & RemoteDeploymentId,
) {
const instance = attach(contract, deployment.address);
): ContractTypeOfFactory<F> {
const instance = attach(contract, deployment.address) as ContractTypeOfFactory<F>;

// @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.
Expand Down

0 comments on commit 451d217

Please sign in to comment.