627 lines
27 KiB
Diff
627 lines
27 KiB
Diff
diff --git a/node_modules/@openzeppelin/hardhat-upgrades/dist/deploy-proxy.js b/node_modules/@openzeppelin/hardhat-upgrades/dist/deploy-proxy.js
|
|
index 01e2cd9..04e8e48 100644
|
|
--- a/node_modules/@openzeppelin/hardhat-upgrades/dist/deploy-proxy.js
|
|
+++ b/node_modules/@openzeppelin/hardhat-upgrades/dist/deploy-proxy.js
|
|
@@ -15,7 +15,95 @@ function makeDeployProxy(hre, defenderModule) {
|
|
opts = (0, utils_2.enableDefender)(hre, defenderModule, opts);
|
|
const { provider } = hre.network;
|
|
const manifest = await upgrades_core_1.Manifest.forNetwork(provider);
|
|
- const { impl, kind } = await (0, utils_1.deployProxyImpl)(hre, ImplFactory, opts);
|
|
+
|
|
+ let impl;
|
|
+ let kind;
|
|
+
|
|
+ // Check if deployment of implementation contract should use CREATE2
|
|
+ if (opts.create2Factory && opts.create2Factory.deployImpl !== false) {
|
|
+ const { getDeployData } = await Promise.resolve().then(() => require('./utils/deploy-impl'));
|
|
+
|
|
+ // Get deployment data for validation
|
|
+ const deployData = await getDeployData(hre, ImplFactory, opts);
|
|
+ const { validateProxyImpl } = await Promise.resolve().then(() => require('./utils/validate-impl'));
|
|
+ await validateProxyImpl(deployData, opts);
|
|
+
|
|
+ if (opts.kind === undefined) {
|
|
+ throw new Error('Broken invariant: Proxy kind is undefined');
|
|
+ }
|
|
+ kind = opts.kind;
|
|
+
|
|
+ // Get the implementation contract bytecode
|
|
+ const implBytecode = ImplFactory.bytecode;
|
|
+
|
|
+ // Implementation contract salt
|
|
+ let implSalt = opts.create2Factory.implSalt;
|
|
+ if (!implSalt) {
|
|
+ // If implSalt is not provided, generate one based on proxySalt
|
|
+ const proxySaltBytes = hre.ethers.getBytes(opts.create2Factory.salt);
|
|
+ const implSaltBytes = new Uint8Array(proxySaltBytes);
|
|
+ implSaltBytes[implSaltBytes.length - 1] ^= 0x01;
|
|
+ implSalt = hre.ethers.hexlify(implSaltBytes);
|
|
+ }
|
|
+
|
|
+ // Calculate expected address to check if already deployed
|
|
+ const initCode = hre.ethers.concat([implBytecode, '0x']);
|
|
+ const expectedAddress = hre.ethers.getCreate2Address(
|
|
+ opts.create2Factory.address,
|
|
+ implSalt,
|
|
+ hre.ethers.keccak256(initCode)
|
|
+ );
|
|
+
|
|
+ // Check if implementation already exists on chain
|
|
+ const existingCode = await hre.ethers.provider.getCode(expectedAddress);
|
|
+ const implExists = existingCode !== '0x';
|
|
+
|
|
+ // Use fetchOrDeployGetDeployment to deploy and record to manifest
|
|
+ const deployment = await upgrades_core_1.fetchOrDeployGetDeployment(
|
|
+ deployData.version,
|
|
+ deployData.provider,
|
|
+ async () => {
|
|
+ const abi = ImplFactory.interface.format(true);
|
|
+
|
|
+ if (implExists) {
|
|
+ // Contract already exists, just return the info
|
|
+ console.log(` Contract already exists at address: ${expectedAddress}`);
|
|
+ return {
|
|
+ abi,
|
|
+ layout: deployData.layout,
|
|
+ address: expectedAddress,
|
|
+ txHash: undefined,
|
|
+ };
|
|
+ }
|
|
+
|
|
+ // Deploy implementation contract via CREATE2
|
|
+ const { deployViaCreate2Factory } = await Promise.resolve().then(() => require('./utils/deploy'));
|
|
+ const implDeployment = await deployViaCreate2Factory(
|
|
+ hre,
|
|
+ opts.create2Factory.address,
|
|
+ implSalt,
|
|
+ implBytecode,
|
|
+ '0x'
|
|
+ );
|
|
+ return {
|
|
+ abi,
|
|
+ layout: deployData.layout,
|
|
+ address: implDeployment.address,
|
|
+ txHash: implDeployment.txHash,
|
|
+ deployTransaction: implDeployment.deployTransaction,
|
|
+ remoteDeploymentId: implDeployment.remoteDeploymentId || ''
|
|
+ };
|
|
+ },
|
|
+ opts,
|
|
+ false
|
|
+ );
|
|
+
|
|
+ impl = deployment.address;
|
|
+ } else {
|
|
+ const result = await (0, utils_1.deployProxyImpl)(hre, ImplFactory, opts);
|
|
+ impl = result.impl;
|
|
+ kind = result.kind;
|
|
+ }
|
|
const contractInterface = ImplFactory.interface;
|
|
const data = (0, utils_1.getInitializerData)(contractInterface, args, opts.initializer);
|
|
const deployFn = opts.deployFunction || utils_1.deploy;
|
|
@@ -44,7 +132,54 @@ function makeDeployProxy(hre, defenderModule) {
|
|
throw new upgrades_core_1.InitialOwnerUnsupportedKindError(kind);
|
|
}
|
|
const ProxyFactory = opts.proxyFactory || (await (0, utils_1.getProxyFactory)(hre, signer));
|
|
- proxyDeployment = Object.assign({ kind }, await deployFn(hre, opts, ProxyFactory, impl, data));
|
|
+
|
|
+ // Check if using CREATE2 Factory
|
|
+ if (opts.create2Factory) {
|
|
+ // Get the proxy contract bytecode
|
|
+ const proxyBytecode = ProxyFactory.bytecode;
|
|
+
|
|
+ // Encode constructor arguments: constructor(address implementation, bytes memory _data)
|
|
+ const constructorArgs = hre.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
+ ['address', 'bytes'],
|
|
+ [impl, data]
|
|
+ );
|
|
+
|
|
+ // Calculate expected proxy address
|
|
+ const proxyInitCode = hre.ethers.concat([proxyBytecode, constructorArgs]);
|
|
+ const expectedProxyAddress = hre.ethers.getCreate2Address(
|
|
+ opts.create2Factory.address,
|
|
+ opts.create2Factory.salt,
|
|
+ hre.ethers.keccak256(proxyInitCode)
|
|
+ );
|
|
+
|
|
+ // Check if proxy already exists
|
|
+ const existingProxyCode = await hre.ethers.provider.getCode(expectedProxyAddress);
|
|
+ const proxyExists = existingProxyCode !== '0x';
|
|
+
|
|
+ if (proxyExists) {
|
|
+ console.log(` Contract already exists at address: ${expectedProxyAddress}`);
|
|
+ proxyDeployment = {
|
|
+ kind,
|
|
+ address: expectedProxyAddress,
|
|
+ txHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
+ remoteDeploymentId: ''
|
|
+ };
|
|
+ } else {
|
|
+ // Deploy using CREATE2 Factory
|
|
+ const { deployViaCreate2Factory } = await Promise.resolve().then(() => require('./utils/deploy'));
|
|
+ const deployment = await deployViaCreate2Factory(
|
|
+ hre,
|
|
+ opts.create2Factory.address,
|
|
+ opts.create2Factory.salt,
|
|
+ proxyBytecode,
|
|
+ constructorArgs
|
|
+ );
|
|
+ proxyDeployment = Object.assign({ kind }, deployment);
|
|
+ }
|
|
+ } else {
|
|
+ // Original deployment logic
|
|
+ proxyDeployment = Object.assign({ kind }, await deployFn(hre, opts, ProxyFactory, impl, data));
|
|
+ }
|
|
break;
|
|
}
|
|
case 'transparent': {
|
|
@@ -53,7 +188,54 @@ function makeDeployProxy(hre, defenderModule) {
|
|
throw new upgrades_core_1.UpgradesError('`initialOwner` must not be a ProxyAdmin contract.', () => `If the contract at address ${initialOwner} is not a ProxyAdmin contract and you are sure that this contract is able to call functions on an actual ProxyAdmin, skip this check with the \`unsafeSkipProxyAdminCheck\` option.`);
|
|
}
|
|
const TransparentUpgradeableProxyFactory = opts.proxyFactory || (await (0, utils_1.getTransparentUpgradeableProxyFactory)(hre, signer));
|
|
- proxyDeployment = Object.assign({ kind }, await deployFn(hre, opts, TransparentUpgradeableProxyFactory, impl, initialOwner, data));
|
|
+
|
|
+ // Check if using CREATE2 Factory
|
|
+ if (opts.create2Factory) {
|
|
+ // Get the proxy contract bytecode
|
|
+ const proxyBytecode = TransparentUpgradeableProxyFactory.bytecode;
|
|
+
|
|
+ // Encode constructor arguments: constructor(address _logic, address initialOwner, bytes memory _data)
|
|
+ const constructorArgs = hre.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
+ ['address', 'address', 'bytes'],
|
|
+ [impl, initialOwner, data]
|
|
+ );
|
|
+
|
|
+ // Calculate expected proxy address
|
|
+ const proxyInitCode = hre.ethers.concat([proxyBytecode, constructorArgs]);
|
|
+ const expectedProxyAddress = hre.ethers.getCreate2Address(
|
|
+ opts.create2Factory.address,
|
|
+ opts.create2Factory.salt,
|
|
+ hre.ethers.keccak256(proxyInitCode)
|
|
+ );
|
|
+
|
|
+ // Check if proxy already exists
|
|
+ const existingProxyCode = await hre.ethers.provider.getCode(expectedProxyAddress);
|
|
+ const proxyExists = existingProxyCode !== '0x';
|
|
+
|
|
+ if (proxyExists) {
|
|
+ console.log(` Contract already exists at address: ${expectedProxyAddress}`);
|
|
+ proxyDeployment = {
|
|
+ kind,
|
|
+ address: expectedProxyAddress,
|
|
+ txHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
+ remoteDeploymentId: ''
|
|
+ };
|
|
+ } else {
|
|
+ // Deploy using CREATE2 Factory
|
|
+ const { deployViaCreate2Factory } = await Promise.resolve().then(() => require('./utils/deploy'));
|
|
+ const deployment = await deployViaCreate2Factory(
|
|
+ hre,
|
|
+ opts.create2Factory.address,
|
|
+ opts.create2Factory.salt,
|
|
+ proxyBytecode,
|
|
+ constructorArgs
|
|
+ );
|
|
+ proxyDeployment = Object.assign({ kind }, deployment);
|
|
+ }
|
|
+ } else {
|
|
+ // Original deployment logic
|
|
+ proxyDeployment = Object.assign({ kind }, await deployFn(hre, opts, TransparentUpgradeableProxyFactory, impl, initialOwner, data));
|
|
+ }
|
|
break;
|
|
}
|
|
}
|
|
diff --git a/node_modules/@openzeppelin/hardhat-upgrades/dist/utils/deploy.js b/node_modules/@openzeppelin/hardhat-upgrades/dist/utils/deploy.js
|
|
index 46c2c7f..7204eb1 100644
|
|
--- a/node_modules/@openzeppelin/hardhat-upgrades/dist/utils/deploy.js
|
|
+++ b/node_modules/@openzeppelin/hardhat-upgrades/dist/utils/deploy.js
|
|
@@ -1,6 +1,7 @@
|
|
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.deploy = deploy;
|
|
+exports.deployViaCreate2Factory = deployViaCreate2Factory;
|
|
const deploy_1 = require("../defender/deploy");
|
|
async function deploy(hre, opts, factory, ...args) {
|
|
if (opts?.useDefenderDeploy) {
|
|
@@ -23,4 +24,76 @@ async function ethersDeploy(factory, ...args) {
|
|
const txHash = deployTransaction.hash;
|
|
return { address, txHash, deployTransaction };
|
|
}
|
|
+
|
|
+/**
|
|
+ * Deploy contract using CREATE2 Factory
|
|
+ */
|
|
+async function deployViaCreate2Factory(hre, factoryAddress, salt, proxyBytecode, constructorArgs) {
|
|
+ const [deployer] = await hre.ethers.getSigners();
|
|
+
|
|
+ // Assemble the complete initCode
|
|
+ const initCode = hre.ethers.concat([proxyBytecode, constructorArgs]);
|
|
+
|
|
+ // Calculate the expected address
|
|
+ const expectedAddress = hre.ethers.getCreate2Address(
|
|
+ factoryAddress,
|
|
+ salt,
|
|
+ hre.ethers.keccak256(initCode)
|
|
+ );
|
|
+
|
|
+ // Get the Factory contract instance
|
|
+ const factoryContract = await hre.ethers.getContractAt(
|
|
+ [
|
|
+ 'function deploy(bytes32 salt, bytes memory initCode, bytes memory data, uint256 create2ForwardValue, uint256 callForwardValue) external payable returns (address deployed)',
|
|
+ 'event Deployed(address indexed addr)',
|
|
+ ],
|
|
+ factoryAddress,
|
|
+ deployer
|
|
+ );
|
|
+
|
|
+ // Call the deploy function
|
|
+ const tx = await factoryContract.deploy(
|
|
+ salt,
|
|
+ initCode,
|
|
+ '0x',
|
|
+ 0,
|
|
+ 0,
|
|
+ { value: 0 }
|
|
+ );
|
|
+
|
|
+ const receipt = await tx.wait();
|
|
+
|
|
+ if (!receipt) {
|
|
+ throw new Error('Deployment transaction failed');
|
|
+ }
|
|
+
|
|
+ // Get the actual deployed address from the event
|
|
+ let actualAddress = expectedAddress;
|
|
+ const deployedEvent = receipt.logs.find((log) => {
|
|
+ try {
|
|
+ const iface = new hre.ethers.Interface(['event Deployed(address indexed addr)']);
|
|
+ const parsed = iface.parseLog(log);
|
|
+ return parsed?.name === 'Deployed';
|
|
+ } catch {
|
|
+ return false;
|
|
+ }
|
|
+ });
|
|
+
|
|
+ if (deployedEvent) {
|
|
+ const iface = new hre.ethers.Interface(['event Deployed(address indexed addr)']);
|
|
+ const parsed = iface.parseLog(deployedEvent);
|
|
+ actualAddress = parsed?.args[0];
|
|
+ }
|
|
+
|
|
+ if (actualAddress.toLowerCase() !== expectedAddress.toLowerCase()) {
|
|
+ console.warn(`Warning: Actual address ${actualAddress} does not match expected address ${expectedAddress}`);
|
|
+ }
|
|
+
|
|
+ return {
|
|
+ address: actualAddress,
|
|
+ txHash: receipt.hash,
|
|
+ deployTransaction: tx,
|
|
+ remoteDeploymentId: '',
|
|
+ };
|
|
+}
|
|
//# sourceMappingURL=deploy.js.map
|
|
\ No newline at end of file
|
|
diff --git a/node_modules/@openzeppelin/hardhat-upgrades/src/deploy-proxy.ts b/node_modules/@openzeppelin/hardhat-upgrades/src/deploy-proxy.ts
|
|
index b912102..fa1f37a 100644
|
|
--- a/node_modules/@openzeppelin/hardhat-upgrades/src/deploy-proxy.ts
|
|
+++ b/node_modules/@openzeppelin/hardhat-upgrades/src/deploy-proxy.ts
|
|
@@ -52,7 +52,96 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule:
|
|
const { provider } = hre.network;
|
|
const manifest = await Manifest.forNetwork(provider);
|
|
|
|
- const { impl, kind } = await deployProxyImpl(hre, ImplFactory, opts);
|
|
+ let impl: string;
|
|
+ let kind: ProxyDeployment['kind'];
|
|
+
|
|
+ // Check if deploying implementation contract using CREATE2
|
|
+ if (opts.create2Factory && opts.create2Factory.deployImpl !== false) {
|
|
+ const { getDeployData } = await import('./utils/deploy-impl');
|
|
+ const { fetchOrDeployGetDeployment } = await import('@openzeppelin/upgrades-core');
|
|
+
|
|
+ // Get deployment data for validation
|
|
+ const deployData = await getDeployData(hre, ImplFactory, opts);
|
|
+ const { validateProxyImpl } = await import('./utils/validate-impl');
|
|
+ await validateProxyImpl(deployData, opts);
|
|
+
|
|
+ if (opts.kind === undefined) {
|
|
+ throw new Error('Broken invariant: Proxy kind is undefined');
|
|
+ }
|
|
+ kind = opts.kind;
|
|
+
|
|
+ // Get the implementation contract bytecode
|
|
+ const implBytecode = ImplFactory.bytecode;
|
|
+
|
|
+ // Implementation contract salt (if provided, use it; otherwise use modified salt)
|
|
+ let implSalt = opts.create2Factory.implSalt;
|
|
+ if (!implSalt) {
|
|
+ // If implSalt is not provided, generate one based on proxySalt (flip the last byte)
|
|
+ const proxySaltBytes = hre.ethers.getBytes(opts.create2Factory.salt);
|
|
+ const implSaltBytes = new Uint8Array(proxySaltBytes);
|
|
+ implSaltBytes[implSaltBytes.length - 1] ^= 0x01; // Flip the last byte
|
|
+ implSalt = hre.ethers.hexlify(implSaltBytes);
|
|
+ }
|
|
+
|
|
+ // Calculate expected address to check if already deployed
|
|
+ const initCode = hre.ethers.concat([implBytecode, '0x']);
|
|
+ const expectedAddress = hre.ethers.getCreate2Address(
|
|
+ opts.create2Factory.address,
|
|
+ implSalt,
|
|
+ hre.ethers.keccak256(initCode)
|
|
+ );
|
|
+
|
|
+ // Check if implementation already exists on chain
|
|
+ const existingCode = await hre.ethers.provider.getCode(expectedAddress);
|
|
+ const implExists = existingCode !== '0x';
|
|
+
|
|
+ // Use fetchOrDeployGetDeployment to deploy and record to manifest
|
|
+ const deployment = await fetchOrDeployGetDeployment(
|
|
+ deployData.version,
|
|
+ deployData.provider,
|
|
+ async () => {
|
|
+ const abi = ImplFactory.interface.format(true) as string[];
|
|
+
|
|
+ if (implExists) {
|
|
+ // Contract already exists, just return the info
|
|
+ console.log(` Contract already exists at address: ${expectedAddress}`);
|
|
+ return {
|
|
+ abi,
|
|
+ layout: deployData.layout,
|
|
+ address: expectedAddress,
|
|
+ txHash: undefined as any,
|
|
+ };
|
|
+ }
|
|
+
|
|
+ // Deploy implementation contract via CREATE2
|
|
+ const { deployViaCreate2Factory } = await import('./utils/deploy');
|
|
+ const implDeployment = await deployViaCreate2Factory(
|
|
+ hre,
|
|
+ opts.create2Factory.address,
|
|
+ implSalt,
|
|
+ implBytecode,
|
|
+ '0x' // Implementation contract usually has no constructor arguments
|
|
+ );
|
|
+ return {
|
|
+ abi,
|
|
+ layout: deployData.layout,
|
|
+ address: implDeployment.address,
|
|
+ txHash: implDeployment.txHash,
|
|
+ deployTransaction: implDeployment.deployTransaction,
|
|
+ remoteDeploymentId: implDeployment.remoteDeploymentId || ''
|
|
+ };
|
|
+ },
|
|
+ opts,
|
|
+ false
|
|
+ );
|
|
+
|
|
+ impl = deployment.address;
|
|
+ } else {
|
|
+ // Original deployment logic
|
|
+ const result = await deployProxyImpl(hre, ImplFactory, opts);
|
|
+ impl = result.impl;
|
|
+ kind = result.kind;
|
|
+ }
|
|
|
|
const contractInterface = ImplFactory.interface;
|
|
const data = getInitializerData(contractInterface, args, opts.initializer);
|
|
@@ -86,7 +175,54 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule:
|
|
}
|
|
|
|
const ProxyFactory = opts.proxyFactory || (await getProxyFactory(hre, signer));
|
|
- proxyDeployment = Object.assign({ kind }, await deployFn(hre, opts, ProxyFactory, impl, data));
|
|
+
|
|
+ // Check if using CREATE2 Factory
|
|
+ if (opts.create2Factory) {
|
|
+ // Get the proxy contract bytecode
|
|
+ const proxyBytecode = ProxyFactory.bytecode;
|
|
+
|
|
+ // Encode constructor arguments: constructor(address implementation, bytes memory _data)
|
|
+ const constructorArgs = hre.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
+ ['address', 'bytes'],
|
|
+ [impl, data]
|
|
+ );
|
|
+
|
|
+ // Calculate expected proxy address
|
|
+ const proxyInitCode = hre.ethers.concat([proxyBytecode, constructorArgs]);
|
|
+ const expectedProxyAddress = hre.ethers.getCreate2Address(
|
|
+ opts.create2Factory.address,
|
|
+ opts.create2Factory.salt,
|
|
+ hre.ethers.keccak256(proxyInitCode)
|
|
+ );
|
|
+
|
|
+ // Check if proxy already exists
|
|
+ const existingProxyCode = await hre.ethers.provider.getCode(expectedProxyAddress);
|
|
+ const proxyExists = existingProxyCode !== '0x';
|
|
+
|
|
+ if (proxyExists) {
|
|
+ console.log(` Contract already exists at address: ${expectedProxyAddress}`);
|
|
+ proxyDeployment = {
|
|
+ kind,
|
|
+ address: expectedProxyAddress,
|
|
+ txHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
+ remoteDeploymentId: ''
|
|
+ };
|
|
+ } else {
|
|
+ // Deploy using CREATE2 Factory
|
|
+ const { deployViaCreate2Factory } = await import('./utils/deploy');
|
|
+ const deployment = await deployViaCreate2Factory(
|
|
+ hre,
|
|
+ opts.create2Factory.address,
|
|
+ opts.create2Factory.salt,
|
|
+ proxyBytecode,
|
|
+ constructorArgs
|
|
+ );
|
|
+ proxyDeployment = Object.assign({ kind }, deployment);
|
|
+ }
|
|
+ } else {
|
|
+ // Original deployment logic
|
|
+ proxyDeployment = Object.assign({ kind }, await deployFn(hre, opts, ProxyFactory, impl, data));
|
|
+ }
|
|
break;
|
|
}
|
|
|
|
@@ -103,10 +239,57 @@ export function makeDeployProxy(hre: HardhatRuntimeEnvironment, defenderModule:
|
|
|
|
const TransparentUpgradeableProxyFactory =
|
|
opts.proxyFactory || (await getTransparentUpgradeableProxyFactory(hre, signer));
|
|
- proxyDeployment = Object.assign(
|
|
- { kind },
|
|
- await deployFn(hre, opts, TransparentUpgradeableProxyFactory, impl, initialOwner, data),
|
|
- );
|
|
+
|
|
+ // Check if using CREATE2 Factory
|
|
+ if (opts.create2Factory) {
|
|
+ // Get the proxy contract bytecode
|
|
+ const proxyBytecode = TransparentUpgradeableProxyFactory.bytecode;
|
|
+
|
|
+ // Encode constructor arguments: constructor(address _logic, address initialOwner, bytes memory _data)
|
|
+ const constructorArgs = hre.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
+ ['address', 'address', 'bytes'],
|
|
+ [impl, initialOwner, data]
|
|
+ );
|
|
+
|
|
+ // Calculate expected proxy address
|
|
+ const proxyInitCode = hre.ethers.concat([proxyBytecode, constructorArgs]);
|
|
+ const expectedProxyAddress = hre.ethers.getCreate2Address(
|
|
+ opts.create2Factory.address,
|
|
+ opts.create2Factory.salt,
|
|
+ hre.ethers.keccak256(proxyInitCode)
|
|
+ );
|
|
+
|
|
+ // Check if proxy already exists
|
|
+ const existingProxyCode = await hre.ethers.provider.getCode(expectedProxyAddress);
|
|
+ const proxyExists = existingProxyCode !== '0x';
|
|
+
|
|
+ if (proxyExists) {
|
|
+ console.log(` Contract already exists at address: ${expectedProxyAddress}`);
|
|
+ proxyDeployment = {
|
|
+ kind,
|
|
+ address: expectedProxyAddress,
|
|
+ txHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
+ remoteDeploymentId: ''
|
|
+ };
|
|
+ } else {
|
|
+ // Deploy using CREATE2 Factory
|
|
+ const { deployViaCreate2Factory } = await import('./utils/deploy');
|
|
+ const deployment = await deployViaCreate2Factory(
|
|
+ hre,
|
|
+ opts.create2Factory.address,
|
|
+ opts.create2Factory.salt,
|
|
+ proxyBytecode,
|
|
+ constructorArgs
|
|
+ );
|
|
+ proxyDeployment = Object.assign({ kind }, deployment);
|
|
+ }
|
|
+ } else {
|
|
+ // Original deployment logic
|
|
+ proxyDeployment = Object.assign(
|
|
+ { kind },
|
|
+ await deployFn(hre, opts, TransparentUpgradeableProxyFactory, impl, initialOwner, data),
|
|
+ );
|
|
+ }
|
|
break;
|
|
}
|
|
}
|
|
diff --git a/node_modules/@openzeppelin/hardhat-upgrades/src/utils/deploy.ts b/node_modules/@openzeppelin/hardhat-upgrades/src/utils/deploy.ts
|
|
index 9543317..a8e2432 100644
|
|
--- a/node_modules/@openzeppelin/hardhat-upgrades/src/utils/deploy.ts
|
|
+++ b/node_modules/@openzeppelin/hardhat-upgrades/src/utils/deploy.ts
|
|
@@ -45,3 +45,77 @@ async function ethersDeploy(
|
|
|
|
return { address, txHash, deployTransaction };
|
|
}
|
|
+
|
|
+export async function deployViaCreate2Factory(
|
|
+ hre: HardhatRuntimeEnvironment,
|
|
+ factoryAddress: string,
|
|
+ salt: string,
|
|
+ proxyBytecode: string,
|
|
+ constructorArgs: string,
|
|
+): Promise<EthersDeployment> {
|
|
+ const [deployer] = await hre.ethers.getSigners();
|
|
+
|
|
+ // Assemble the complete initCode = bytecode + constructor args
|
|
+ const initCode = hre.ethers.concat([proxyBytecode, constructorArgs]);
|
|
+
|
|
+ // Calculate the expected address
|
|
+ const expectedAddress = hre.ethers.getCreate2Address(
|
|
+ factoryAddress,
|
|
+ salt,
|
|
+ hre.ethers.keccak256(initCode)
|
|
+ );
|
|
+
|
|
+ // Get the Factory contract instance
|
|
+ const factoryContract = await hre.ethers.getContractAt(
|
|
+ [
|
|
+ 'function deploy(bytes32 salt, bytes memory initCode, bytes memory data, uint256 create2ForwardValue, uint256 callForwardValue) external payable returns (address deployed)',
|
|
+ 'event Deployed(address indexed addr)',
|
|
+ ],
|
|
+ factoryAddress,
|
|
+ deployer
|
|
+ );
|
|
+
|
|
+ // Call the deploy function
|
|
+ const tx = await factoryContract.deploy(
|
|
+ salt,
|
|
+ initCode,
|
|
+ '0x', // After deployment, no additional call is needed
|
|
+ 0, // create2ForwardValue
|
|
+ 0, // callForwardValue
|
|
+ { value: 0 }
|
|
+ );
|
|
+ const receipt = await tx.wait();
|
|
+
|
|
+ if (!receipt) {
|
|
+ throw new Error('Deployment transaction failed');
|
|
+ }
|
|
+
|
|
+ // Get the actual deployed address from the event
|
|
+ let actualAddress = expectedAddress;
|
|
+ const deployedEvent = receipt.logs.find((log: any) => {
|
|
+ try {
|
|
+ const iface = new hre.ethers.Interface(['event Deployed(address indexed addr)']);
|
|
+ const parsed = iface.parseLog(log);
|
|
+ return parsed?.name === 'Deployed';
|
|
+ } catch {
|
|
+ return false;
|
|
+ }
|
|
+ });
|
|
+
|
|
+ if (deployedEvent) {
|
|
+ const iface = new hre.ethers.Interface(['event Deployed(address indexed addr)']);
|
|
+ const parsed = iface.parseLog(deployedEvent);
|
|
+ actualAddress = parsed?.args[0];
|
|
+ }
|
|
+
|
|
+ if (actualAddress.toLowerCase() !== expectedAddress.toLowerCase()) {
|
|
+ console.warn(`Warning: Actual address ${actualAddress} does not match expected address ${expectedAddress}`);
|
|
+ }
|
|
+
|
|
+ return {
|
|
+ address: actualAddress,
|
|
+ txHash: receipt.hash,
|
|
+ deployTransaction: tx,
|
|
+ remoteDeploymentId: '',
|
|
+ };
|
|
+}
|
|
diff --git a/node_modules/@openzeppelin/hardhat-upgrades/src/utils/options.ts b/node_modules/@openzeppelin/hardhat-upgrades/src/utils/options.ts
|
|
index 3ee7954..05b4684 100644
|
|
--- a/node_modules/@openzeppelin/hardhat-upgrades/src/utils/options.ts
|
|
+++ b/node_modules/@openzeppelin/hardhat-upgrades/src/utils/options.ts
|
|
@@ -22,6 +22,16 @@ export type DeployFactoryOpts = {
|
|
* Allows to customize the deploy function used instead of utils/deploy.ts:deploy
|
|
*/
|
|
deployFunction?: () => Promise<EthersOrDefenderDeployment>;
|
|
+
|
|
+ /**
|
|
+ * Use CREATE2 Factory to deploy proxy and implementation contracts, ensuring consistent addresses across chains
|
|
+ */
|
|
+ create2Factory?: {
|
|
+ address: string; // Create2 Factory contract address
|
|
+ salt: string; // 32 bytes salt for proxy contract
|
|
+ implSalt?: string; // 32 bytes salt for implementation contract (optional)
|
|
+ deployImpl?: boolean; // Whether to deploy implementation contract using CREATE2 (default true)
|
|
+ };
|
|
};
|
|
|
|
/**
|