diff --git a/packages/plugin-hardhat/CHANGELOG.md b/packages/plugin-hardhat/CHANGELOG.md index 36512332e..a712a890d 100644 --- a/packages/plugin-hardhat/CHANGELOG.md +++ b/packages/plugin-hardhat/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +- Add `createFactoryAddress` option for Defender deployments. ([#920](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/920)) + +**Note**: OpenZeppelin Defender deployments is in beta and its functionality is subject to change. + ## 2.3.3 (2023-10-12) - Update OpenZeppelin Defender deployments to use Defender SDK ([#888](https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/888)) diff --git a/packages/plugin-hardhat/src/defender/deploy.ts b/packages/plugin-hardhat/src/defender/deploy.ts index e69c5cbc5..a751a89c1 100644 --- a/packages/plugin-hardhat/src/defender/deploy.ts +++ b/packages/plugin-hardhat/src/defender/deploy.ts @@ -3,7 +3,7 @@ import { CompilerInput, CompilerOutputContract, HardhatRuntimeEnvironment } from import { parseFullyQualifiedName } from 'hardhat/utils/contract-names'; -import { DeploymentResponse, SourceCodeLicense } from '@openzeppelin/defender-sdk-deploy-client'; +import { DeploymentResponse, SourceCodeLicense, DeployContractRequest } from '@openzeppelin/defender-sdk-deploy-client'; import { Deployment, RemoteDeploymentId, @@ -54,6 +54,11 @@ type CompilerOutputWithMetadata = CompilerOutputContract & { metadata?: string; }; +type DeployRequest = DeployContractRequest & { + // TODO: remove this when defender-sdk-deploy-client dependency is updated + createFactoryAddress?: string; +}; + export async function defenderDeploy( hre: HardhatRuntimeEnvironment, factory: ContractFactory, @@ -80,19 +85,22 @@ export async function defenderDeploy( debug(`Salt: ${opts.salt}`); } + const deploymentRequest: DeployRequest = { + contractName: contractInfo.contractName, + contractPath: contractInfo.sourceName, + network: network, + artifactPayload: JSON.stringify(contractInfo.buildInfo), + licenseType: license as SourceCodeLicense | undefined, // cast without validation but catch error from API below + constructorInputs: constructorArgs, + verifySourceCode: verifySourceCode, + relayerId: opts.relayerId, + salt: opts.salt, + createFactoryAddress: opts.createFactoryAddress, + }; + let deploymentResponse: DeploymentResponse; try { - deploymentResponse = await client.deployContract({ - contractName: contractInfo.contractName, - contractPath: contractInfo.sourceName, - network: network, - artifactPayload: JSON.stringify(contractInfo.buildInfo), - licenseType: license as SourceCodeLicense | undefined, // cast without validation but catch error from API below - constructorInputs: constructorArgs, - verifySourceCode: verifySourceCode, - relayerId: opts.relayerId, - salt: opts.salt, - }); + deploymentResponse = await client.deployContract(deploymentRequest); } catch (e: any) { if (e.response?.data?.message?.includes('licenseType should be equal to one of the allowed values')) { throw new UpgradesError( diff --git a/packages/plugin-hardhat/src/utils/options.ts b/packages/plugin-hardhat/src/utils/options.ts index fd1bb90df..1b706f37d 100644 --- a/packages/plugin-hardhat/src/utils/options.ts +++ b/packages/plugin-hardhat/src/utils/options.ts @@ -63,6 +63,7 @@ export type DefenderDeployOptions = DefenderDeploy & { verifySourceCode?: boolean; relayerId?: string; salt?: string; + createFactoryAddress?: string; }; /** diff --git a/packages/plugin-hardhat/test/defender-deploy.js b/packages/plugin-hardhat/test/defender-deploy.js index ecc8709d3..09ca2ad7c 100644 --- a/packages/plugin-hardhat/test/defender-deploy.js +++ b/packages/plugin-hardhat/test/defender-deploy.js @@ -19,6 +19,7 @@ const TX_RESPONSE = 'mocked response'; const ETHERSCAN_API_KEY = 'fakeKey'; const RELAYER_ID = '123-abc'; const SALT = 'customsalt'; +const CREATE_FACTORY = '0x0000000000000000000000000000000000000010'; const LOGIC_ADDRESS = '0x0000000000000000000000000000000000000003'; const ADMIN_ADDRESS = '0x0000000000000000000000000000000000000004'; @@ -99,6 +100,7 @@ test('calls defender deploy', async t => { verifySourceCode: true, relayerId: undefined, salt: undefined, + createFactoryAddress: undefined, }); assertResult(t, result); @@ -124,6 +126,7 @@ test('calls defender deploy with relayerId', async t => { verifySourceCode: true, relayerId: RELAYER_ID, salt: undefined, + createFactoryAddress: undefined, }); assertResult(t, result); @@ -149,6 +152,33 @@ test('calls defender deploy with salt', async t => { verifySourceCode: true, relayerId: undefined, salt: SALT, + createFactoryAddress: undefined, + }); + + assertResult(t, result); +}); + +test('calls defender deploy with createFactoryAddress', async t => { + const { spy, deploy, fakeHre, fakeChainId } = t.context; + + const contractPath = 'contracts/Greeter.sol'; + const contractName = 'Greeter'; + + const factory = await ethers.getContractFactory(contractName); + const result = await deploy.defenderDeploy(fakeHre, factory, { createFactoryAddress: CREATE_FACTORY }); + + const buildInfo = await hre.artifacts.getBuildInfo(`${contractPath}:${contractName}`); + sinon.assert.calledWithExactly(spy, { + contractName: contractName, + contractPath: contractPath, + network: fakeChainId, + artifactPayload: JSON.stringify(buildInfo), + licenseType: 'None', + constructorInputs: [], + verifySourceCode: true, + relayerId: undefined, + salt: undefined, + createFactoryAddress: CREATE_FACTORY, }); assertResult(t, result); @@ -174,6 +204,7 @@ test('calls defender deploy with license', async t => { verifySourceCode: true, relayerId: undefined, salt: undefined, + createFactoryAddress: undefined, }); assertResult(t, result); @@ -199,6 +230,7 @@ test('calls defender deploy with constructor args', async t => { verifySourceCode: true, relayerId: undefined, salt: undefined, + createFactoryAddress: undefined, }); assertResult(t, result); @@ -224,6 +256,7 @@ test('calls defender deploy with verify false', async t => { verifySourceCode: false, relayerId: undefined, salt: undefined, + createFactoryAddress: undefined, }); assertResult(t, result); @@ -249,6 +282,7 @@ test('calls defender deploy with ERC1967Proxy', async t => { verifySourceCode: true, relayerId: undefined, salt: undefined, + createFactoryAddress: undefined, }); }); @@ -272,6 +306,7 @@ test('calls defender deploy with BeaconProxy', async t => { verifySourceCode: true, relayerId: undefined, salt: undefined, + createFactoryAddress: undefined, }); }); @@ -295,5 +330,6 @@ test('calls defender deploy with TransparentUpgradeableProxy', async t => { verifySourceCode: true, relayerId: undefined, salt: undefined, + createFactoryAddress: undefined, }); });