Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support TypeChain in deploy and upgrade proxy functions - continuation of work done on #535 #1099

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
Loading