From 4a4ede3124f6b18db79daf709d729480ee0f76bf Mon Sep 17 00:00:00 2001 From: Noah Passalacqua Date: Wed, 26 Jul 2023 15:53:15 -0500 Subject: [PATCH 01/19] allow for optional tx override parameters --- .idea/.gitignore | 5 +++++ packages/core/src/deployment.ts | 6 ++++++ packages/plugin-hardhat/src/utils/deploy.ts | 6 +++--- packages/plugin-hardhat/src/utils/options.ts | 1 + packages/plugin-truffle/src/deploy-beacon-proxy.ts | 2 +- packages/plugin-truffle/src/deploy-beacon.ts | 2 +- packages/plugin-truffle/src/deploy-proxy-admin.ts | 2 +- packages/plugin-truffle/src/deploy-proxy.ts | 4 ++-- packages/plugin-truffle/src/utils/deploy-impl.ts | 2 +- packages/plugin-truffle/src/utils/deploy.ts | 4 +++- packages/plugin-truffle/src/utils/options.ts | 1 + 11 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 .idea/.gitignore diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..b58b603fe --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/packages/core/src/deployment.ts b/packages/core/src/deployment.ts index d61dd5e03..8dd595008 100644 --- a/packages/core/src/deployment.ts +++ b/packages/core/src/deployment.ts @@ -1,3 +1,4 @@ +import { Overrides } from 'ethers'; import { promisify } from 'util'; import debug from './utils/debug'; @@ -39,6 +40,11 @@ export interface DeployOpts { * Polling interval in milliseconds between checks for the transaction confirmation when deploying an implementation contract or proxy admin contract. */ pollingInterval?: number; + + /** + * Overrides for the transaction sent to deploy the implementation contract or proxy admin contract. + */ + txOverrides?: Overrides; } /** diff --git a/packages/plugin-hardhat/src/utils/deploy.ts b/packages/plugin-hardhat/src/utils/deploy.ts index e016a2a59..ca79e046a 100644 --- a/packages/plugin-hardhat/src/utils/deploy.ts +++ b/packages/plugin-hardhat/src/utils/deploy.ts @@ -1,5 +1,5 @@ import type { Deployment, RemoteDeploymentId } from '@openzeppelin/upgrades-core'; -import type { ethers, ContractFactory } from 'ethers'; +import { ethers, ContractFactory, ContractMethodArgs } from 'ethers'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { platformDeploy } from '../platform/deploy'; import { PlatformDeployOptions, UpgradeOptions } from './options'; @@ -18,13 +18,13 @@ export async function deploy( if (opts?.usePlatformDeploy) { return await platformDeploy(hre, factory, opts, ...args); } else { - return await ethersDeploy(factory, ...args); + return await ethersDeploy(factory, ...args, opts.txOverrides ?? {}); } } async function ethersDeploy( factory: ContractFactory, - ...args: unknown[] + ...args: ContractMethodArgs ): Promise & RemoteDeploymentId> { const contractInstance = await factory.deploy(...args); diff --git a/packages/plugin-hardhat/src/utils/options.ts b/packages/plugin-hardhat/src/utils/options.ts index efa391022..8890f3254 100644 --- a/packages/plugin-hardhat/src/utils/options.ts +++ b/packages/plugin-hardhat/src/utils/options.ts @@ -31,6 +31,7 @@ export function withDefaults(opts: UpgradeOptions = {}): Required = Object.assign( { kind: opts.kind }, - await deploy(deployer, BeaconProxyFactory, beaconAddress, data), + await deploy(deployer, opts, BeaconProxyFactory, beaconAddress, data), ); await manifest.addProxy(proxyDeployment); diff --git a/packages/plugin-truffle/src/deploy-beacon.ts b/packages/plugin-truffle/src/deploy-beacon.ts index 8f72ac495..1d42d732c 100644 --- a/packages/plugin-truffle/src/deploy-beacon.ts +++ b/packages/plugin-truffle/src/deploy-beacon.ts @@ -13,7 +13,7 @@ export async function deployBeacon(Contract: ContractClass, opts: DeployBeaconOp const { deployer } = withDefaults(opts); const UpgradeableBeaconFactory = getUpgradeableBeaconFactory(Contract); - const beaconDeployment = await deploy(deployer, UpgradeableBeaconFactory, impl); + const beaconDeployment = await deploy(deployer, opts, UpgradeableBeaconFactory, impl); const beaconContract = new UpgradeableBeaconFactory(beaconDeployment.address); beaconContract.transactionHash = beaconDeployment.txHash; diff --git a/packages/plugin-truffle/src/deploy-proxy-admin.ts b/packages/plugin-truffle/src/deploy-proxy-admin.ts index a1364fbf9..bb83dc968 100644 --- a/packages/plugin-truffle/src/deploy-proxy-admin.ts +++ b/packages/plugin-truffle/src/deploy-proxy-admin.ts @@ -7,5 +7,5 @@ export async function deployProxyAdmin(opts: DeployProxyAdminOptions = {}): Prom const provider = wrapProvider(deployer.provider); const AdminFactory = getProxyAdminFactory(); - return await fetchOrDeployAdmin(provider, () => deploy(deployer, AdminFactory)); + return await fetchOrDeployAdmin(provider, () => deploy(deployer, opts, AdminFactory)); } diff --git a/packages/plugin-truffle/src/deploy-proxy.ts b/packages/plugin-truffle/src/deploy-proxy.ts index e1cfd74b6..f863b8f11 100644 --- a/packages/plugin-truffle/src/deploy-proxy.ts +++ b/packages/plugin-truffle/src/deploy-proxy.ts @@ -53,7 +53,7 @@ export async function deployProxy( case 'uups': { const ProxyFactory = getProxyFactory(Contract); - proxyDeployment = Object.assign({ kind }, await deploy(deployer, ProxyFactory, impl, data)); + proxyDeployment = Object.assign({ kind }, await deploy(deployer, opts, ProxyFactory, impl, data)); break; } @@ -62,7 +62,7 @@ export async function deployProxy( const TransparentUpgradeableProxyFactory = getTransparentUpgradeableProxyFactory(Contract); proxyDeployment = Object.assign( { kind }, - await deploy(deployer, TransparentUpgradeableProxyFactory, impl, adminAddress, data), + await deploy(deployer, opts, TransparentUpgradeableProxyFactory, impl, adminAddress, data), ); break; } diff --git a/packages/plugin-truffle/src/utils/deploy-impl.ts b/packages/plugin-truffle/src/utils/deploy-impl.ts index bf9b90164..0e1b1d37f 100644 --- a/packages/plugin-truffle/src/utils/deploy-impl.ts +++ b/packages/plugin-truffle/src/utils/deploy-impl.ts @@ -111,7 +111,7 @@ async function deployImpl(deployData: DeployData, Contract: ContractClass, opts: } }); } else { - return deploy(deployData.fullOpts.deployer, Contract, ...deployData.fullOpts.constructorArgs); + return deploy(deployData.fullOpts.deployer, opts, Contract, ...deployData.fullOpts.constructorArgs); } }; const deployment = Object.assign({ abi }, await attemptDeploy()); diff --git a/packages/plugin-truffle/src/utils/deploy.ts b/packages/plugin-truffle/src/utils/deploy.ts index 176d1ae31..c7b1115a5 100644 --- a/packages/plugin-truffle/src/utils/deploy.ts +++ b/packages/plugin-truffle/src/utils/deploy.ts @@ -1,12 +1,14 @@ import type { Deployment } from '@openzeppelin/upgrades-core'; +import { UpgradeOptions } from './options'; import type { ContractClass, Deployer } from './truffle'; export async function deploy( deployer: Deployer, + opts: UpgradeOptions, contract: ContractClass, ...args: unknown[] ): Promise> { - const { address, transactionHash: txHash } = await deployer.deploy(contract, ...args); + const { address, transactionHash: txHash } = await deployer.deploy(contract, ...args, opts.txOverrides ?? {}); if (txHash === undefined) { throw new Error('Transaction hash is undefined'); } diff --git a/packages/plugin-truffle/src/utils/options.ts b/packages/plugin-truffle/src/utils/options.ts index e9e8f4a35..7fa53551e 100644 --- a/packages/plugin-truffle/src/utils/options.ts +++ b/packages/plugin-truffle/src/utils/options.ts @@ -32,6 +32,7 @@ export function withDefaults(opts: UpgradeOptions = {}): Required Date: Wed, 26 Jul 2023 16:11:40 -0500 Subject: [PATCH 02/19] only add tx overrides if it is defined --- packages/plugin-hardhat/src/utils/deploy.ts | 5 ++++- packages/plugin-truffle/src/utils/deploy.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/plugin-hardhat/src/utils/deploy.ts b/packages/plugin-hardhat/src/utils/deploy.ts index ca79e046a..991a78818 100644 --- a/packages/plugin-hardhat/src/utils/deploy.ts +++ b/packages/plugin-hardhat/src/utils/deploy.ts @@ -18,7 +18,10 @@ export async function deploy( if (opts?.usePlatformDeploy) { return await platformDeploy(hre, factory, opts, ...args); } else { - return await ethersDeploy(factory, ...args, opts.txOverrides ?? {}); + if (opts.txOverrides != null) { + args.push(opts.txOverrides); + } + return await ethersDeploy(factory, ...args); } } diff --git a/packages/plugin-truffle/src/utils/deploy.ts b/packages/plugin-truffle/src/utils/deploy.ts index c7b1115a5..128764565 100644 --- a/packages/plugin-truffle/src/utils/deploy.ts +++ b/packages/plugin-truffle/src/utils/deploy.ts @@ -8,7 +8,10 @@ export async function deploy( contract: ContractClass, ...args: unknown[] ): Promise> { - const { address, transactionHash: txHash } = await deployer.deploy(contract, ...args, opts.txOverrides ?? {}); + if (opts.txOverrides != null) { + args.push(opts.txOverrides); + } + const { address, transactionHash: txHash } = await deployer.deploy(contract, ...args); if (txHash === undefined) { throw new Error('Transaction hash is undefined'); } From cc25421e3856fda158b2e227a2364548d985c9f7 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 28 Jul 2023 14:29:38 -0400 Subject: [PATCH 03/19] Move txOverrides to Hardhat/Truffle specific options --- packages/core/src/deployment.ts | 6 --- packages/plugin-hardhat/src/utils/deploy.ts | 6 +-- packages/plugin-hardhat/src/utils/options.ts | 43 ++++++++++++++------ packages/plugin-truffle/src/utils/options.ts | 7 +++- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/packages/core/src/deployment.ts b/packages/core/src/deployment.ts index 8dd595008..d61dd5e03 100644 --- a/packages/core/src/deployment.ts +++ b/packages/core/src/deployment.ts @@ -1,4 +1,3 @@ -import { Overrides } from 'ethers'; import { promisify } from 'util'; import debug from './utils/debug'; @@ -40,11 +39,6 @@ export interface DeployOpts { * Polling interval in milliseconds between checks for the transaction confirmation when deploying an implementation contract or proxy admin contract. */ pollingInterval?: number; - - /** - * Overrides for the transaction sent to deploy the implementation contract or proxy admin contract. - */ - txOverrides?: Overrides; } /** diff --git a/packages/plugin-hardhat/src/utils/deploy.ts b/packages/plugin-hardhat/src/utils/deploy.ts index 991a78818..dc501a2a0 100644 --- a/packages/plugin-hardhat/src/utils/deploy.ts +++ b/packages/plugin-hardhat/src/utils/deploy.ts @@ -2,7 +2,7 @@ import type { Deployment, RemoteDeploymentId } from '@openzeppelin/upgrades-core import { ethers, ContractFactory, ContractMethodArgs } from 'ethers'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { platformDeploy } from '../platform/deploy'; -import { PlatformDeployOptions, UpgradeOptions } from './options'; +import { EthersDeployOptions, PlatformDeployOptions, UpgradeOptions } from './options'; export interface DeployTransaction { deployTransaction?: ethers.TransactionResponse; @@ -10,7 +10,7 @@ export interface DeployTransaction { export async function deploy( hre: HardhatRuntimeEnvironment, - opts: UpgradeOptions & PlatformDeployOptions, + opts: UpgradeOptions & EthersDeployOptions & PlatformDeployOptions, factory: ContractFactory, ...args: unknown[] ): Promise & DeployTransaction & RemoteDeploymentId> { @@ -18,7 +18,7 @@ export async function deploy( if (opts?.usePlatformDeploy) { return await platformDeploy(hre, factory, opts, ...args); } else { - if (opts.txOverrides != null) { + if (opts.txOverrides !== undefined) { args.push(opts.txOverrides); } return await ethersDeploy(factory, ...args); diff --git a/packages/plugin-hardhat/src/utils/options.ts b/packages/plugin-hardhat/src/utils/options.ts index 8890f3254..47591168f 100644 --- a/packages/plugin-hardhat/src/utils/options.ts +++ b/packages/plugin-hardhat/src/utils/options.ts @@ -5,6 +5,7 @@ import { ValidationOptions, withValidationDefaults, } from '@openzeppelin/upgrades-core'; +import { Overrides } from 'ethers'; /** * Options for functions that can deploy an implementation contract. @@ -31,7 +32,6 @@ export function withDefaults(opts: UpgradeOptions = {}): Required { return { deployer: opts.deployer ?? defaultDeployer, + txOverrides: opts.txOverrides ?? {}, timeout: opts.timeout ?? 60e3, // not used for Truffle, but include these anyways pollingInterval: opts.pollingInterval ?? 5e3, // not used for Truffle, but include these anyways constructorArgs: opts.constructorArgs ?? [], useDeployedImplementation: opts.useDeployedImplementation ?? false, redeployImplementation: opts.redeployImplementation ?? 'onchange', - txOverrides: opts.txOverrides ?? {}, ...withValidationDefaults(opts), }; } From 6868acfd88d708512421c59d0b25c2bd94ed7d38 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 28 Jul 2023 14:45:11 -0400 Subject: [PATCH 04/19] Remove .idea folder --- .idea/.gitignore | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .idea/.gitignore diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b58b603fe..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ From 9caeab34052ead24c80cccb43581ff117d6b3230 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 28 Jul 2023 15:19:23 -0400 Subject: [PATCH 05/19] Fix undefined check --- packages/plugin-truffle/src/utils/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-truffle/src/utils/deploy.ts b/packages/plugin-truffle/src/utils/deploy.ts index 128764565..74d76e2a9 100644 --- a/packages/plugin-truffle/src/utils/deploy.ts +++ b/packages/plugin-truffle/src/utils/deploy.ts @@ -8,7 +8,7 @@ export async function deploy( contract: ContractClass, ...args: unknown[] ): Promise> { - if (opts.txOverrides != null) { + if (opts.txOverrides !== undefined) { args.push(opts.txOverrides); } const { address, transactionHash: txHash } = await deployer.deploy(contract, ...args); From 2b5a531faeb405137618d87fed797216ee3a4a63 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 28 Jul 2023 23:30:10 -0400 Subject: [PATCH 06/19] Add overrides for upgradeProxy --- packages/plugin-hardhat/src/upgrade-proxy.ts | 13 +++++++++---- packages/plugin-truffle/src/upgrade-proxy.ts | 11 ++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/plugin-hardhat/src/upgrade-proxy.ts b/packages/plugin-hardhat/src/upgrade-proxy.ts index 49d075ff7..a4e613497 100644 --- a/packages/plugin-hardhat/src/upgrade-proxy.ts +++ b/packages/plugin-hardhat/src/upgrade-proxy.ts @@ -29,7 +29,7 @@ export function makeUpgradeProxy(hre: HardhatRuntimeEnvironment, platformModule: const { impl: nextImpl } = await deployProxyImpl(hre, ImplFactory, opts, proxyAddress); // upgrade kind is inferred above - const upgradeTo = await getUpgrader(proxyAddress, getSigner(ImplFactory.runner)); + const upgradeTo = await getUpgrader(proxyAddress, opts, getSigner(ImplFactory.runner)); const call = encodeCall(ImplFactory, opts.call); const upgradeTx = await upgradeTo(nextImpl, call); @@ -41,18 +41,21 @@ export function makeUpgradeProxy(hre: HardhatRuntimeEnvironment, platformModule: type Upgrader = (nextImpl: string, call?: string) => Promise; - async function getUpgrader(proxyAddress: string, signer?: Signer): Promise { + async function getUpgrader(proxyAddress: string, opts: UpgradeProxyOptions, signer?: Signer): Promise { const { provider } = hre.network; const adminAddress = await getAdminAddress(provider, proxyAddress); const adminBytecode = await getCode(provider, adminAddress); + const overrides = opts.txOverrides ? [opts.txOverrides] : []; + if (isEmptySlot(adminAddress) || adminBytecode === '0x') { // No admin contract: use ITransparentUpgradeableProxyFactory to get proxiable interface const ITransparentUpgradeableProxyFactory = await getITransparentUpgradeableProxyFactory(hre, signer); const proxy = attach(ITransparentUpgradeableProxyFactory, proxyAddress); - return (nextImpl, call) => (call ? proxy.upgradeToAndCall(nextImpl, call) : proxy.upgradeTo(nextImpl)); + return (nextImpl, call) => + call ? proxy.upgradeToAndCall(nextImpl, call, ...overrides) : proxy.upgradeTo(nextImpl, ...overrides); } else { // Admin contract: redirect upgrade call through it const manifest = await Manifest.forNetwork(provider); @@ -65,7 +68,9 @@ export function makeUpgradeProxy(hre: HardhatRuntimeEnvironment, platformModule: } return (nextImpl, call) => - call ? admin.upgradeAndCall(proxyAddress, nextImpl, call) : admin.upgrade(proxyAddress, nextImpl); + call + ? admin.upgradeAndCall(proxyAddress, nextImpl, call, ...overrides) + : admin.upgrade(proxyAddress, nextImpl, ...overrides); } } } diff --git a/packages/plugin-truffle/src/upgrade-proxy.ts b/packages/plugin-truffle/src/upgrade-proxy.ts index 5de922d3a..921bdd6bc 100644 --- a/packages/plugin-truffle/src/upgrade-proxy.ts +++ b/packages/plugin-truffle/src/upgrade-proxy.ts @@ -23,7 +23,7 @@ export async function upgradeProxy( const proxyAddress = getContractAddress(proxy); - const upgradeTo = await getUpgrader(provider, Contract, proxyAddress); + const upgradeTo = await getUpgrader(provider, Contract, opts, proxyAddress); const { impl: nextImpl } = await deployProxyImpl(Contract, opts, proxyAddress); const call = encodeCall(Contract, opts.call); const { tx: txHash } = await upgradeTo(nextImpl, call); @@ -40,18 +40,21 @@ type Upgrader = (nextImpl: string, call?: string) => Promise<{ tx: string }>; async function getUpgrader( provider: EthereumProvider, contractTemplate: ContractClass, + opts: UpgradeProxyOptions, proxyAddress: string, ): Promise { const adminAddress = await getAdminAddress(provider, proxyAddress); const adminBytecode = await getCode(provider, adminAddress); + const overrides = opts.txOverrides ? [opts.txOverrides] : []; + if (isEmptySlot(adminAddress) || adminBytecode === '0x') { // No admin contract: use ITransparentUpgradeableProxyFactory to get proxiable interface const ITransparentUpgradeableProxyFactory = getITransparentUpgradeableProxyFactory(contractTemplate); const proxy = new ITransparentUpgradeableProxyFactory(proxyAddress); return (nextImpl, call) => { - return call ? proxy.upgradeToAndCall(nextImpl, call) : proxy.upgradeTo(nextImpl); + return call ? proxy.upgradeToAndCall(nextImpl, call, ...overrides) : proxy.upgradeTo(nextImpl, ...overrides); }; } else { // Admin contract: redirect upgrade call through it @@ -65,7 +68,9 @@ async function getUpgrader( } return (nextImpl, call) => { - return call ? admin.upgradeAndCall(proxyAddress, nextImpl, call) : admin.upgrade(proxyAddress, nextImpl); + return call + ? admin.upgradeAndCall(proxyAddress, nextImpl, call, ...overrides) + : admin.upgrade(proxyAddress, nextImpl, ...overrides); }; } } From ae4e27bc77628ddb345f7560cccabea84b357bbc Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Sat, 29 Jul 2023 02:20:46 -0400 Subject: [PATCH 07/19] Add override to upgradeBeacon --- packages/plugin-hardhat/src/upgrade-beacon.ts | 4 +++- packages/plugin-truffle/src/upgrade-beacon.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/plugin-hardhat/src/upgrade-beacon.ts b/packages/plugin-hardhat/src/upgrade-beacon.ts index 44a08dd94..609b32f4f 100644 --- a/packages/plugin-hardhat/src/upgrade-beacon.ts +++ b/packages/plugin-hardhat/src/upgrade-beacon.ts @@ -27,7 +27,9 @@ export function makeUpgradeBeacon(hre: HardhatRuntimeEnvironment, platformModule const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(hre, getSigner(ImplFactory.runner)); const beaconContract = attach(UpgradeableBeaconFactory, beaconAddress); - const upgradeTx = await beaconContract.upgradeTo(nextImpl); + + const overrides = opts.txOverrides ? [opts.txOverrides] : []; + const upgradeTx = await beaconContract.upgradeTo(nextImpl, ...overrides); // @ts-ignore Won't be readonly because beaconContract was created through attach. beaconContract.deployTransaction = upgradeTx; diff --git a/packages/plugin-truffle/src/upgrade-beacon.ts b/packages/plugin-truffle/src/upgrade-beacon.ts index 75e480b80..5007cf19d 100644 --- a/packages/plugin-truffle/src/upgrade-beacon.ts +++ b/packages/plugin-truffle/src/upgrade-beacon.ts @@ -18,7 +18,9 @@ export async function upgradeBeacon( const UpgradeableBeaconFactory = getUpgradeableBeaconFactory(Contract); const beaconContract = new UpgradeableBeaconFactory(beaconAddress); - const { tx: upgradeTx } = await beaconContract.upgradeTo(nextImpl); + + const overrides = opts.txOverrides ? [opts.txOverrides] : []; + const { tx: upgradeTx } = await beaconContract.upgradeTo(nextImpl, ...overrides); beaconContract.transactionHash = upgradeTx; return beaconContract; From d0e3103ed62aaf06efae38f95d666970a1596dd4 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Sat, 29 Jul 2023 02:33:41 -0400 Subject: [PATCH 08/19] Adding Hardhat tests --- packages/plugin-hardhat/test/tx-overrides.js | 120 +++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 packages/plugin-hardhat/test/tx-overrides.js diff --git a/packages/plugin-hardhat/test/tx-overrides.js b/packages/plugin-hardhat/test/tx-overrides.js new file mode 100644 index 000000000..8328c22f3 --- /dev/null +++ b/packages/plugin-hardhat/test/tx-overrides.js @@ -0,0 +1,120 @@ +const test = require('ava'); + +const { ethers, upgrades } = require('hardhat'); + +test.before(async t => { + t.context.Greeter = await ethers.getContractFactory('Greeter'); + t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); +}); + +async function assertGasLimit(t, oldBlockNumber, expectedGasLimit, minExpectedBlocks) { + const newBlockNumber = await ethers.provider.getBlockNumber(); + t.true( + newBlockNumber - oldBlockNumber >= minExpectedBlocks, + `Actual number of blocks ${newBlockNumber - oldBlockNumber}, expected at least ${minExpectedBlocks}`, + ); + + for (let i = oldBlockNumber + 1; i <= newBlockNumber; i++) { + const block = await ethers.provider.getBlock(i); + t.is(block.transactions.length, 1); // Assume and assert that tests run with only one tx per block + const txHash = block.transactions[0]; + + const tx = await ethers.provider.getTransaction(txHash); + t.is(tx.gasLimit, expectedGasLimit); + } +} + +test('deployProxy', async t => { + const { Greeter } = t.context; + const oldBlockNumber = await ethers.provider.getBlockNumber(); + + await upgrades.deployProxy(Greeter, ['Hello'], { + redeployImplementation: 'always', + txOverrides: { gasLimit: 10000001n }, + }); + + await assertGasLimit(t, oldBlockNumber, 10000001n, 2); +}); + +test('upgradeProxy', async t => { + const { Greeter, GreeterV2 } = t.context; + const greeter = await upgrades.deployProxy(Greeter, ['Hello']); + const oldBlockNumber = await ethers.provider.getBlockNumber(); + + await upgrades.upgradeProxy(greeter, GreeterV2, { + redeployImplementation: 'always', + txOverrides: { gasLimit: 10000002n }, + }); + + await assertGasLimit(t, oldBlockNumber, 10000002n, 2); +}); + +test('deployBeacon', async t => { + const { Greeter } = t.context; + const oldBlockNumber = await ethers.provider.getBlockNumber(); + + await upgrades.deployBeacon(Greeter, { redeployImplementation: 'always', txOverrides: { gasLimit: 10000003n } }); + + await assertGasLimit(t, oldBlockNumber, 10000003n, 2); +}); + +test('upgradeBeacon', async t => { + const { Greeter, GreeterV2 } = t.context; + const beacon = await upgrades.deployBeacon(Greeter); + const oldBlockNumber = await ethers.provider.getBlockNumber(); + + await upgrades.upgradeBeacon(beacon, GreeterV2, { + redeployImplementation: 'always', + txOverrides: { gasLimit: 10000004n }, + }); + + await assertGasLimit(t, oldBlockNumber, 10000004n, 2); +}); + +test('deployBeaconProxy', async t => { + const { Greeter } = t.context; + const beacon = await upgrades.deployBeacon(Greeter); + + const oldBlockNumber = await ethers.provider.getBlockNumber(); + + await upgrades.deployBeaconProxy(beacon, Greeter, ['Hello'], { txOverrides: { gasLimit: 10000005n } }); + + await assertGasLimit(t, oldBlockNumber, 10000005n, 1); +}); + +test('deployImplementation', async t => { + const { Greeter } = t.context; + const oldBlockNumber = await ethers.provider.getBlockNumber(); + + await upgrades.deployImplementation(Greeter, { + redeployImplementation: 'always', + txOverrides: { gasLimit: 10000006n }, + }); + + await assertGasLimit(t, oldBlockNumber, 10000006n, 1); +}); + +test('prepareUpgrade', async t => { + const { Greeter, GreeterV2 } = t.context; + const greeter = await upgrades.deployProxy(Greeter, ['Hello']); + const oldBlockNumber = await ethers.provider.getBlockNumber(); + + await upgrades.prepareUpgrade(greeter, GreeterV2, { + redeployImplementation: 'always', + txOverrides: { gasLimit: 10000007n }, + }); + + await assertGasLimit(t, oldBlockNumber, 10000007n, 1); +}); + +// test('changeProxyAdmin', async t => { +// const { Greeter } = t.context; +// const greeter = await upgrades.deployProxy(Greeter, ['Hello']); +// const oldBlockNumber = await ethers.provider.getBlockNumber(); + +// const [deployer] = await ethers.getSigners(); + +// await upgrades.admin.changeProxyAdmin(await greeter.getAddress(), '0x0011223344556677889900112233445566778899', deployer); + +// await assertGasLimit(t, oldBlockNumber, 10000008n, 1); +// }); From eecde28d959444568bc3540161f7fb1608bc89a4 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Sat, 29 Jul 2023 02:48:27 -0400 Subject: [PATCH 09/19] Add overrides for admin functions --- packages/plugin-hardhat/src/admin.ts | 31 +++++++++++++++----- packages/plugin-hardhat/test/tx-overrides.js | 31 +++++++++++++++----- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/packages/plugin-hardhat/src/admin.ts b/packages/plugin-hardhat/src/admin.ts index bfd69b578..2b5f482bd 100644 --- a/packages/plugin-hardhat/src/admin.ts +++ b/packages/plugin-hardhat/src/admin.ts @@ -2,19 +2,33 @@ import chalk from 'chalk'; import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import { Manifest, getAdminAddress } from '@openzeppelin/upgrades-core'; import { Contract, Signer } from 'ethers'; -import { getProxyAdminFactory } from './utils'; +import { EthersDeployOptions, getProxyAdminFactory } from './utils'; import { disablePlatform } from './platform/utils'; import { attach } from './utils/ethers'; const SUCCESS_CHECK = chalk.green('✔') + ' '; const FAILURE_CROSS = chalk.red('✘') + ' '; -export type ChangeAdminFunction = (proxyAddress: string, newAdmin: string, signer?: Signer) => Promise; -export type TransferProxyAdminOwnershipFunction = (newOwner: string, signer?: Signer) => Promise; +export type ChangeAdminFunction = ( + proxyAddress: string, + newAdmin: string, + signer?: Signer, + opts?: EthersDeployOptions, +) => Promise; +export type TransferProxyAdminOwnershipFunction = ( + newOwner: string, + signer?: Signer, + opts?: EthersDeployOptions, +) => Promise; export type GetInstanceFunction = (signer?: Signer) => Promise; export function makeChangeProxyAdmin(hre: HardhatRuntimeEnvironment, platformModule: boolean): ChangeAdminFunction { - return async function changeProxyAdmin(proxyAddress, newAdmin, signer?: Signer) { + return async function changeProxyAdmin( + proxyAddress: string, + newAdmin: string, + signer?: Signer, + opts: EthersDeployOptions = {}, + ) { disablePlatform(hre, platformModule, {}, changeProxyAdmin.name); const admin = await getManifestAdmin(hre, signer); @@ -24,7 +38,8 @@ export function makeChangeProxyAdmin(hre: HardhatRuntimeEnvironment, platformMod if (manifestAdminAddress !== proxyAdminAddress) { throw new Error('Proxy admin is not the one registered in the network manifest'); } else if (manifestAdminAddress !== newAdmin) { - await admin.changeProxyAdmin(proxyAddress, newAdmin); + const overrides = opts.txOverrides ? [opts.txOverrides] : []; + await admin.changeProxyAdmin(proxyAddress, newAdmin, ...overrides); } }; } @@ -33,11 +48,13 @@ export function makeTransferProxyAdminOwnership( hre: HardhatRuntimeEnvironment, platformModule: boolean, ): TransferProxyAdminOwnershipFunction { - return async function transferProxyAdminOwnership(newOwner, signer?: Signer) { + return async function transferProxyAdminOwnership(newOwner: string, signer?: Signer, opts: EthersDeployOptions = {}) { disablePlatform(hre, platformModule, {}, transferProxyAdminOwnership.name); const admin = await getManifestAdmin(hre, signer); - await admin.transferOwnership(newOwner); + + const overrides = opts.txOverrides ? [opts.txOverrides] : []; + await admin.transferOwnership(newOwner, ...overrides); const { provider } = hre.network; const manifest = await Manifest.forNetwork(provider); diff --git a/packages/plugin-hardhat/test/tx-overrides.js b/packages/plugin-hardhat/test/tx-overrides.js index 8328c22f3..b4449c268 100644 --- a/packages/plugin-hardhat/test/tx-overrides.js +++ b/packages/plugin-hardhat/test/tx-overrides.js @@ -107,14 +107,29 @@ test('prepareUpgrade', async t => { await assertGasLimit(t, oldBlockNumber, 10000007n, 1); }); -// test('changeProxyAdmin', async t => { -// const { Greeter } = t.context; -// const greeter = await upgrades.deployProxy(Greeter, ['Hello']); -// const oldBlockNumber = await ethers.provider.getBlockNumber(); +test('changeProxyAdmin', async t => { + const { Greeter } = t.context; + const greeter = await upgrades.deployProxy(Greeter, ['Hello']); + const oldBlockNumber = await ethers.provider.getBlockNumber(); + + const [deployer, newAdmin] = await ethers.getSigners(); + + await upgrades.admin.changeProxyAdmin(await greeter.getAddress(), newAdmin.address, deployer, { + txOverrides: { gasLimit: 10000008n }, + }); -// const [deployer] = await ethers.getSigners(); + await assertGasLimit(t, oldBlockNumber, 10000008n, 1); +}); + +test('transferProxyAdminOwnership', async t => { + await upgrades.deployProxyAdmin(); + const oldBlockNumber = await ethers.provider.getBlockNumber(); -// await upgrades.admin.changeProxyAdmin(await greeter.getAddress(), '0x0011223344556677889900112233445566778899', deployer); + const [deployer, newOwner] = await ethers.getSigners(); -// await assertGasLimit(t, oldBlockNumber, 10000008n, 1); -// }); + await upgrades.admin.transferProxyAdminOwnership(newOwner.address, deployer, { + txOverrides: { gasLimit: 10000009n }, + }); + + await assertGasLimit(t, oldBlockNumber, 10000009n, 1); +}); From fa280baabab25f66435239c1f13d52811df212e0 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Sat, 29 Jul 2023 02:51:51 -0400 Subject: [PATCH 10/19] Add overrides for admin functions in Truffle --- packages/plugin-truffle/src/admin.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/plugin-truffle/src/admin.ts b/packages/plugin-truffle/src/admin.ts index 76d9aef21..c9ecf57ac 100644 --- a/packages/plugin-truffle/src/admin.ts +++ b/packages/plugin-truffle/src/admin.ts @@ -14,7 +14,8 @@ async function changeProxyAdmin(proxyAddress: string, newAdmin: string, opts: Up if (admin.address !== proxyAdminAddress) { throw new Error('Proxy admin is not the one registered in the network manifest'); } else if (admin.address !== newAdmin) { - await admin.changeProxyAdmin(proxyAddress, newAdmin); + const overrides = opts.txOverrides ? [opts.txOverrides] : []; + await admin.changeProxyAdmin(proxyAddress, newAdmin, ...overrides); } } @@ -22,7 +23,9 @@ async function transferProxyAdminOwnership(newOwner: string, opts: UpgradeOption const { deployer } = withDefaults(opts); const provider = wrapProvider(deployer.provider); const admin = await getManifestAdmin(provider); - await admin.transferOwnership(newOwner); + + const overrides = opts.txOverrides ? [opts.txOverrides] : []; + await admin.transferOwnership(newOwner, ...overrides); const manifest = await Manifest.forNetwork(provider); const { proxies } = await manifest.read(); From d1001f4894ea8a23ff9b272a4ba75f9677decfa5 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Sat, 29 Jul 2023 03:24:59 -0400 Subject: [PATCH 11/19] Add Truffle tests --- .../plugin-truffle/test/test/tx-overrides.js | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 packages/plugin-truffle/test/test/tx-overrides.js diff --git a/packages/plugin-truffle/test/test/tx-overrides.js b/packages/plugin-truffle/test/test/tx-overrides.js new file mode 100644 index 000000000..c194e7479 --- /dev/null +++ b/packages/plugin-truffle/test/test/tx-overrides.js @@ -0,0 +1,127 @@ +const assert = require('assert'); + +// ignore eslint error for injected web3 +/* global web3 */ + +const { + deployProxy, + upgradeProxy, + deployBeacon, + upgradeBeacon, + deployBeaconProxy, + deployImplementation, + prepareUpgrade, + admin, + deployProxyAdmin, +} = require('@openzeppelin/truffle-upgrades'); + +const Greeter = artifacts.require('Greeter'); +const GreeterV2 = artifacts.require('GreeterV2'); + +async function assertGasLimit(oldBlockNumber, expectedGasLimit, minExpectedBlocks) { + const newBlockNumber = await web3.eth.getBlockNumber(); + assert( + newBlockNumber - oldBlockNumber >= minExpectedBlocks, + `Actual number of blocks ${newBlockNumber - oldBlockNumber}, expected at least ${minExpectedBlocks}`, + ); + + for (let i = oldBlockNumber + 1; i <= newBlockNumber; i++) { + const block = await web3.eth.getBlock(i); + assert.equal(block.transactions.length, 1); // Assume and assert that tests run with only one tx per block + const txHash = block.transactions[0]; + + const tx = await web3.eth.getTransaction(txHash); + assert.equal(tx.gas, expectedGasLimit); + } +} + +contract('Transaction overrides', function () { + it('deployProxy', async function () { + const oldBlockNumber = await web3.eth.getBlockNumber(); + + await deployProxy(Greeter, ['Hello'], { + redeployImplementation: 'always', + txOverrides: { gas: 10000001 }, + }); + + await assertGasLimit(oldBlockNumber, 10000001, 2); + }); + + it('upgradeProxy', async function () { + const greeter = await deployProxy(Greeter, ['Hello']); + const oldBlockNumber = await web3.eth.getBlockNumber(); + + await upgradeProxy(greeter, GreeterV2, { + redeployImplementation: 'always', + txOverrides: { gas: 10000002 }, + }); + + await assertGasLimit(oldBlockNumber, 10000002, 2); + }); + + it('deployBeacon', async function () { + const oldBlockNumber = await web3.eth.getBlockNumber(); + + await deployBeacon(Greeter, { redeployImplementation: 'always', txOverrides: { gas: 10000003 } }); + + await assertGasLimit(oldBlockNumber, 10000003, 2); + }); + + it('upgradeBeacon', async function () { + const greeter = await deployBeacon(Greeter); + const oldBlockNumber = await web3.eth.getBlockNumber(); + + await upgradeBeacon(greeter, GreeterV2, { redeployImplementation: 'always', txOverrides: { gas: 10000004 } }); + + await assertGasLimit(oldBlockNumber, 10000004, 2); + }); + + it('deployBeaconProxy', async function () { + const beacon = await deployBeacon(Greeter); + + const oldBlockNumber = await web3.eth.getBlockNumber(); + + await deployBeaconProxy(beacon, Greeter, ['Hello'], { txOverrides: { gas: 10000005 } }); + + await assertGasLimit(oldBlockNumber, 10000005, 1); + }); + + it('deployImplementation', async function () { + const oldBlockNumber = await web3.eth.getBlockNumber(); + + await deployImplementation(Greeter, { redeployImplementation: 'always', txOverrides: { gas: 10000006 } }); + + await assertGasLimit(oldBlockNumber, 10000006, 1); + }); + + it('prepareUpgrade', async function () { + const greeter = await deployProxy(Greeter, ['Hello']); + const oldBlockNumber = await web3.eth.getBlockNumber(); + + await prepareUpgrade(greeter, GreeterV2, { redeployImplementation: 'always', txOverrides: { gas: 10000007 } }); + + await assertGasLimit(oldBlockNumber, 10000007, 1); + }); + + it('changeProxyAdmin', async function () { + const greeter = await deployProxy(Greeter, ['Hello']); + const oldBlockNumber = await web3.eth.getBlockNumber(); + + const accounts = await web3.eth.getAccounts(); + + await admin.changeProxyAdmin(greeter.address, accounts[1], { txOverrides: { gas: 10000008 } }); + + await assertGasLimit(oldBlockNumber, 10000008, 1); + }); + + it('transferProxyAdminOwnership', async function () { + await deployProxyAdmin(); + const oldBlockNumber = await web3.eth.getBlockNumber(); + + const accounts = await web3.eth.getAccounts(); + + await admin.transferProxyAdminOwnership(accounts[1], { txOverrides: { gas: 10000009 } }); + + await assertGasLimit(oldBlockNumber, 10000009, 1); + }); +}); From 1e9f895b2a7cd89a1b463420e11aff83c59a1b1f Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 31 Jul 2023 11:37:23 -0400 Subject: [PATCH 12/19] Change back to import type --- packages/plugin-hardhat/src/utils/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-hardhat/src/utils/deploy.ts b/packages/plugin-hardhat/src/utils/deploy.ts index dc501a2a0..fc257e98a 100644 --- a/packages/plugin-hardhat/src/utils/deploy.ts +++ b/packages/plugin-hardhat/src/utils/deploy.ts @@ -1,5 +1,5 @@ import type { Deployment, RemoteDeploymentId } from '@openzeppelin/upgrades-core'; -import { ethers, ContractFactory, ContractMethodArgs } from 'ethers'; +import type { ethers, ContractFactory, ContractMethodArgs } from 'ethers'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { platformDeploy } from '../platform/deploy'; import { EthersDeployOptions, PlatformDeployOptions, UpgradeOptions } from './options'; From 67b7b7f53387ab204a8b878966b212b39a558c45 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 31 Jul 2023 16:32:03 -0400 Subject: [PATCH 13/19] Add Truffle options type --- packages/plugin-truffle/src/utils/options.ts | 4 +- packages/plugin-truffle/src/utils/truffle.ts | 48 ++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/packages/plugin-truffle/src/utils/options.ts b/packages/plugin-truffle/src/utils/options.ts index 3db6a1c5c..19dc95c71 100644 --- a/packages/plugin-truffle/src/utils/options.ts +++ b/packages/plugin-truffle/src/utils/options.ts @@ -1,4 +1,4 @@ -import { Deployer, ContractClass, ContractInstance, getTruffleConfig } from './truffle'; +import { Deployer, ContractClass, ContractInstance, getTruffleConfig, TruffleTxOptions } from './truffle'; import { DeployOpts, ProxyKindOption, @@ -13,7 +13,7 @@ type TruffleDeployer = { /** * Overrides for the transaction sent to deploy a contract. */ - txOverrides?: unknown; + txOverrides?: TruffleTxOptions; }; export type StandaloneOptions = StandaloneValidationOptions & diff --git a/packages/plugin-truffle/src/utils/truffle.ts b/packages/plugin-truffle/src/utils/truffle.ts index c4c62f026..9fa720ce2 100644 --- a/packages/plugin-truffle/src/utils/truffle.ts +++ b/packages/plugin-truffle/src/utils/truffle.ts @@ -1,4 +1,5 @@ import { SourceUnit } from 'solidity-ast'; +import { BN } from 'bn.js'; export interface Deployer { provider: TruffleProvider; @@ -104,3 +105,50 @@ export function getTruffleProvider(): TruffleProvider { // artifacts generated by Hardhat, which are only partially compatible. // eslint-disable-next-line @typescript-eslint/no-var-requires export const TruffleContract = require('@truffle/contract') as (artifact: unknown) => ContractClass; + +export type TruffleTxOptions = { + /** + * This should be an address + */ + from?: string; + /** + * This should be an address + */ + to?: string; + gas?: string | number | typeof BN; + gasPrice?: string | number | typeof BN; + maxFeePerGas?: string | number | typeof BN; + maxPriorityFeePerGas?: string | number | typeof BN; + value?: string | number | typeof BN; + /** + * This should be a bytestring (even-length hex string, with "0x") + */ + data?: string; + nonce?: string | number; + /** + * This represents a number, but for compatibility purposes + * it should be given as a hex string. It should be in the + * range of 0x00 to 0xbf. + */ + type?: string; + accessList?: { + /** + * This should be an address + */ + address: string; + /** + * These should be 32-byte bytestrings + */ + storageKeys: string[]; + }[]; + /** + * Quorum-specific; this should be an array of base64-encoded strings, + * each of which encodes a 32-byte bytestring + */ + privateFor?: string[]; + /** + * Truffle-specific; if set to false, the deployer won't deploy this + * contract if one has already been deployed + */ + overwrite?: boolean; +}; From 44d292eb82e5d5f86f994d7e67a875e41cc64ab4 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 31 Jul 2023 16:37:47 -0400 Subject: [PATCH 14/19] Update changelogs --- packages/plugin-hardhat/CHANGELOG.md | 4 ++++ packages/plugin-truffle/CHANGELOG.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/plugin-hardhat/CHANGELOG.md b/packages/plugin-hardhat/CHANGELOG.md index c0fe4e7a6..fef1c39dc 100644 --- a/packages/plugin-hardhat/CHANGELOG.md +++ b/packages/plugin-hardhat/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Add `txOverrides` option for overriding transaction parameters. ([#852](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/852)) + ## 2.0.1 (2023-07-12) - Update OpenZeppelin Platform client dependencies. ([#845](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/845)) diff --git a/packages/plugin-truffle/CHANGELOG.md b/packages/plugin-truffle/CHANGELOG.md index b97c272c6..3f1191313 100644 --- a/packages/plugin-truffle/CHANGELOG.md +++ b/packages/plugin-truffle/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Add `txOverrides` option for overriding transaction parameters. ([#852](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/852)) + ## 1.19.0 (2023-05-26) - Add `redeployImplementation` option, deprecate `useDeployedImplementation`. ([#804](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/804)) From 9b3738031922aca1ac98d9299dc3f92de7764dfe Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 31 Jul 2023 16:58:57 -0400 Subject: [PATCH 15/19] Remove txOverrides for platform.deployContract --- packages/plugin-hardhat/src/utils/options.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/plugin-hardhat/src/utils/options.ts b/packages/plugin-hardhat/src/utils/options.ts index 47591168f..85a80d025 100644 --- a/packages/plugin-hardhat/src/utils/options.ts +++ b/packages/plugin-hardhat/src/utils/options.ts @@ -83,8 +83,7 @@ export type DeployImplementationOptions = EthersDeployOptions & StandaloneOptions & GetTxResponse & PlatformDeployOptions; -export type DeployContractOptions = EthersDeployOptions & - StandaloneOptions & +export type DeployContractOptions = StandaloneOptions & GetTxResponse & PlatformDeployOptions & { unsafeAllowDeployContract?: boolean; From 60178dd3e41a20d9a6270370137f8d7e9e4e83da Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 31 Jul 2023 17:14:44 -0400 Subject: [PATCH 16/19] Update docs --- .../ROOT/pages/api-hardhat-upgrades.adoc | 19 ++++++++++++++++ .../ROOT/pages/api-truffle-upgrades.adoc | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/docs/modules/ROOT/pages/api-hardhat-upgrades.adoc b/docs/modules/ROOT/pages/api-hardhat-upgrades.adoc index fd30e4697..dcdab740f 100644 --- a/docs/modules/ROOT/pages/api-hardhat-upgrades.adoc +++ b/docs/modules/ROOT/pages/api-hardhat-upgrades.adoc @@ -25,6 +25,7 @@ The following options are common to some functions. ** If set to `"always"`, the implementation contract is always redeployed even if it was previously deployed with the same bytecode. This can be used with the `salt` option when deploying a proxy through OpenZeppelin Platform to ensure that the implementation contract is deployed with the same salt as the proxy. ** If set to `"never"`, the implementation contract is never redeployed. If the implementation contract was not previously deployed or is not found in the network file, an error will be thrown. ** If set to `"onchange"`, the implementation contract is redeployed only if the bytecode has changed from previous deployments. +* `txOverrides`: (`ethers.Overrides`) An ethers.js https://docs.ethers.org/v6/api/contract/#Overrides[Overrides] object to override transaction parameters, such as `gasLimit` and `gasPrice`. Applies to all transactions sent by a function with this option, even if the function sends multiple transactions. * `usePlatformDeploy`: (`boolean`) Deploy contracts using the OpenZeppelin Platform instead of ethers.js. See xref:platform-deploy.adoc[Using with OpenZeppelin Platform]. **Note**: OpenZeppelin Platform is in beta and functionality related to Platform is subject to change. * `verifySourceCode`: (`boolean`) When using Platform, whether to verify source code on block explorers. Defaults to `true`. * `relayerId`: (`string`) When using Platform, the ID of the relayer to use for the deployment. Defaults to the relayer configured for your deployment environment on Platform. @@ -53,6 +54,7 @@ async function deployProxy( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: ethers.Overrides, kind?: 'uups' | 'transparent', usePlatformDeploy?: boolean, }, @@ -92,6 +94,7 @@ async function upgradeProxy( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: ethers.Overrides, kind?: 'uups' | 'transparent', }, ): Promise @@ -124,6 +127,7 @@ async function deployBeacon( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: ethers.Overrides, }, ): Promise ---- @@ -160,6 +164,7 @@ async function upgradeBeacon( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: ethers.Overrides, }, ): Promise ---- @@ -192,6 +197,7 @@ async function deployBeaconProxy( args: unknown[] = [], opts?: { initializer?: string | false, + txOverrides?: ethers.Overrides, usePlatformDeploy?: boolean, }, ): Promise @@ -290,6 +296,7 @@ async function deployImplementation( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: ethers.Overrides, getTxResponse?: boolean, kind?: 'uups' | 'transparent' | 'beacon', usePlatformDeploy?: boolean, @@ -381,6 +388,7 @@ async function prepareUpgrade( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: ethers.Overrides, getTxResponse?: boolean, kind?: 'uups' | 'transparent' | 'beacon', usePlatformDeploy?: boolean, @@ -566,6 +574,7 @@ async function deployProxyAdmin( opts?: { timeout?: number, pollingInterval?: number, + txOverrides?: ethers.Overrides, }, ): Promise ---- @@ -595,6 +604,9 @@ async function changeProxyAdmin( proxyAddress: string, newAdmin: string, signer?: ethers.Signer, + opts?: { + txOverrides?: ethers.Overrides, + } ): Promise ---- @@ -605,6 +617,8 @@ Changes the admin for a specific proxy. * `proxyAddress` - the address of the proxy to change. * `newAdmin` - the new admin address. * `signer` - the signer to use for the transaction. +* `opts` - an object with options: +** additional options as described in <>. [[admin-transfer-proxy-admin-ownership]] == admin.transferProxyAdminOwnership @@ -614,6 +628,9 @@ Changes the admin for a specific proxy. async function transferProxyAdminOwnership( newAdmin: string, signer?: ethers.Signer, + opts?: { + txOverrides?: ethers.Overrides, + } ): Promise ---- @@ -623,6 +640,8 @@ Changes the owner of the proxy admin contract, which is the default admin for up * `newAdmin` - the new admin address. * `signer` - the signer to use for the transaction. +* `opts` - an object with options: +** additional options as described in <>. [[erc1967]] == erc1967 diff --git a/docs/modules/ROOT/pages/api-truffle-upgrades.adoc b/docs/modules/ROOT/pages/api-truffle-upgrades.adoc index 10250cdf8..77b0ca74b 100644 --- a/docs/modules/ROOT/pages/api-truffle-upgrades.adoc +++ b/docs/modules/ROOT/pages/api-truffle-upgrades.adoc @@ -24,6 +24,7 @@ The following options are common to some functions. ** If set to `"always"`, the implementation contract is always redeployed even if it was previously deployed with the same bytecode. ** If set to `"never"`, the implementation contract is never redeployed. If the implementation contract was not previously deployed or is not found in the network file, an error will be thrown. ** If set to `"onchange"`, the implementation contract is redeployed only if the bytecode has changed from previous deployments. +* `txOverrides`: (`TruffleTxOptions`) A Truffle https://trufflesuite.com/docs/truffle/how-to/contracts/run-migrations/#deployerdeploycontract-args-options[options] object to override transaction parameters, such as `gas` and `gasPrice`. Applies to all transactions sent by a function with this option, even if the function sends multiple transactions. Note that the options `unsafeAllow` can also be specified in a more granular way directly in the source code if using Solidity >=0.8.2. See xref:faq.adoc#how-can-i-disable-checks[How can I disable some of the checks?] @@ -49,6 +50,7 @@ async function deployProxy( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: TruffleTxOptions, kind?: 'uups' | 'transparent', }, ): Promise @@ -90,6 +92,7 @@ async function upgradeProxy( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: TruffleTxOptions, kind?: 'uups' | 'transparent', }, ): Promise @@ -123,6 +126,7 @@ async function deployBeacon( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: TruffleTxOptions, }, ): Promise ---- @@ -160,6 +164,7 @@ async function upgradeBeacon( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: TruffleTxOptions, }, ): Promise ---- @@ -193,6 +198,7 @@ async function deployBeaconProxy( opts?: { deployer?: Deployer, initializer?: string | false, + txOverrides?: TruffleTxOptions, }, ): Promise ---- @@ -290,6 +296,7 @@ async function deployImplementation( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: TruffleTxOptions, kind?: 'uups' | 'transparent' | 'beacon', }, ): Promise @@ -379,6 +386,7 @@ async function prepareUpgrade( timeout?: number, pollingInterval?: number, redeployImplementation?: 'always' | 'never' | 'onchange', + txOverrides?: TruffleTxOptions, kind?: 'uups' | 'transparent' | 'beacon', }, ): Promise @@ -407,6 +415,7 @@ async function deployProxyAdmin( deployer?: Deployer, timeout?: number, pollingInterval?: number, + txOverrides?: TruffleTxOptions, }, ): Promise ---- @@ -434,6 +443,10 @@ Deploys a https://docs.openzeppelin.com/contracts/4.x/api/proxy#ProxyAdmin[proxy async function changeProxyAdmin( proxyAddress: string, newAdmin: string, + opts?: { + deployer?: Deployer, + txOverrides?: TruffleTxOptions, + }, ): Promise ---- @@ -443,6 +456,8 @@ Changes the admin for a specific proxy. * `proxyAddress` - the address of the proxy to change. * `newAdmin` - the new admin address. +* `opts` - an object with options: +** See <>. [[admin-transfer-proxy-admin-ownership]] == admin.transferProxyAdminOwnership @@ -451,6 +466,11 @@ Changes the admin for a specific proxy. ---- async function transferProxyAdminOwnership( newAdmin: string, + opts?: { + deployer?: Deployer, + txOverrides?: TruffleTxOptions, + }, + ): Promise ---- @@ -459,6 +479,8 @@ Changes the owner of the proxy admin contract, which is the default admin for up *Parameters:* * `newAdmin` - the new admin address. +* `opts` - an object with options: +** See <>. [[erc1967]] == erc1967 From e34da20136e4f8be998864cd10c7e1e78ca9b039 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 31 Jul 2023 17:27:54 -0400 Subject: [PATCH 17/19] Relax type --- packages/plugin-truffle/src/utils/truffle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-truffle/src/utils/truffle.ts b/packages/plugin-truffle/src/utils/truffle.ts index 9fa720ce2..260698a26 100644 --- a/packages/plugin-truffle/src/utils/truffle.ts +++ b/packages/plugin-truffle/src/utils/truffle.ts @@ -124,7 +124,7 @@ export type TruffleTxOptions = { * This should be a bytestring (even-length hex string, with "0x") */ data?: string; - nonce?: string | number; + nonce?: string | number | typeof BN; /** * This represents a number, but for compatibility purposes * it should be given as a hex string. It should be in the From c7f1b6de1dfd7d23ded355d072eddedbf065fe72 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 31 Jul 2023 18:01:52 -0400 Subject: [PATCH 18/19] Simplify types using txOverrides --- packages/plugin-hardhat/src/utils/options.ts | 26 +++++++++----------- packages/plugin-truffle/src/utils/options.ts | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/plugin-hardhat/src/utils/options.ts b/packages/plugin-hardhat/src/utils/options.ts index 85a80d025..bb139e261 100644 --- a/packages/plugin-hardhat/src/utils/options.ts +++ b/packages/plugin-hardhat/src/utils/options.ts @@ -11,7 +11,8 @@ import { Overrides } from 'ethers'; * Options for functions that can deploy an implementation contract. */ export type StandaloneOptions = StandaloneValidationOptions & - DeployOpts & { + DeployOpts & + EthersDeployOptions & { constructorArgs?: unknown[]; /** * @deprecated Use `redeployImplementation = 'never'` instead. @@ -32,6 +33,7 @@ export function withDefaults(opts: UpgradeOptions = {}): Required & // ethers not supported for deployContract GetTxResponse & PlatformDeployOptions & { unsafeAllowDeployContract?: boolean; }; export type DeployProxyAdminOptions = EthersDeployOptions & DeployOpts & Platform; -export type DeployProxyOptions = EthersDeployOptions & StandaloneOptions & Initializer & PlatformDeployOptions; +export type DeployProxyOptions = StandaloneOptions & Initializer & PlatformDeployOptions; export type ForceImportOptions = ProxyKindOption; -export type PrepareUpgradeOptions = EthersDeployOptions & UpgradeOptions & GetTxResponse & PlatformDeployOptions; -export type UpgradeBeaconOptions = EthersDeployOptions & UpgradeOptions & Platform; -export type UpgradeProxyOptions = EthersDeployOptions & - UpgradeOptions & { - call?: { fn: string; args?: unknown[] } | string; - } & Platform; +export type PrepareUpgradeOptions = UpgradeOptions & GetTxResponse & PlatformDeployOptions; +export type UpgradeBeaconOptions = UpgradeOptions & Platform; +export type UpgradeProxyOptions = UpgradeOptions & { + call?: { fn: string; args?: unknown[] } | string; +} & Platform; export type ValidateImplementationOptions = StandaloneValidationOptions; export type ValidateUpgradeOptions = ValidationOptions; diff --git a/packages/plugin-truffle/src/utils/options.ts b/packages/plugin-truffle/src/utils/options.ts index 19dc95c71..8d69cd6b7 100644 --- a/packages/plugin-truffle/src/utils/options.ts +++ b/packages/plugin-truffle/src/utils/options.ts @@ -32,12 +32,12 @@ export type UpgradeOptions = ValidationOptions & StandaloneOptions; export function withDefaults(opts: UpgradeOptions = {}): Required { return { deployer: opts.deployer ?? defaultDeployer, - txOverrides: opts.txOverrides ?? {}, timeout: opts.timeout ?? 60e3, // not used for Truffle, but include these anyways pollingInterval: opts.pollingInterval ?? 5e3, // not used for Truffle, but include these anyways constructorArgs: opts.constructorArgs ?? [], useDeployedImplementation: opts.useDeployedImplementation ?? false, redeployImplementation: opts.redeployImplementation ?? 'onchange', + txOverrides: opts.txOverrides ?? {}, ...withValidationDefaults(opts), }; } From bd85399ffa9e7bab620ebec604a2f1e839f8028e Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 31 Jul 2023 18:04:53 -0400 Subject: [PATCH 19/19] Clarify comment --- packages/plugin-hardhat/src/utils/options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-hardhat/src/utils/options.ts b/packages/plugin-hardhat/src/utils/options.ts index bb139e261..9056290bf 100644 --- a/packages/plugin-hardhat/src/utils/options.ts +++ b/packages/plugin-hardhat/src/utils/options.ts @@ -82,7 +82,7 @@ export type DeployBeaconProxyOptions = EthersDeployOptions & PlatformDeployOptions; export type DeployBeaconOptions = StandaloneOptions & Platform; export type DeployImplementationOptions = StandaloneOptions & GetTxResponse & PlatformDeployOptions; -export type DeployContractOptions = Omit & // ethers not supported for deployContract +export type DeployContractOptions = Omit & // ethers deployment not supported for deployContract GetTxResponse & PlatformDeployOptions & { unsafeAllowDeployContract?: boolean;