Skip to content

Commit c59f5dd

Browse files
committed
Derives a hash for each deployment name and adds a script to generate addresses.json
1 parent 8bb04bb commit c59f5dd

11 files changed

+157
-13
lines changed

deploy/001_deploy_InverseApi3ReaderProxyV1.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
22

3+
import { getDeploymentName } from '../src';
4+
35
const VERIFICATION_BLOCK_CONFIRMATIONS = 5;
46

57
module.exports = async (hre: HardhatRuntimeEnvironment) => {
@@ -27,10 +29,16 @@ module.exports = async (hre: HardhatRuntimeEnvironment) => {
2729
log(`Deployment confirmations: ${confirmations}`);
2830

2931
const contractName = 'InverseApi3ReaderProxyV1';
32+
const constructorArgs = [proxyAddress];
33+
const constructorArgTypes = ['address'];
34+
35+
const deploymentName = getDeploymentName(contractName, constructorArgTypes, constructorArgs);
36+
log(`Generated deterministic deployment name for this instance: ${deploymentName}`);
3037

31-
const deployment = await deploy(contractName, {
38+
const deployment = await deploy(deploymentName, {
39+
contract: contractName,
3240
from: deployerAddress,
33-
args: [proxyAddress],
41+
args: constructorArgs,
3442
log: true,
3543
waitConfirmations: confirmations,
3644
});
@@ -40,7 +48,9 @@ module.exports = async (hre: HardhatRuntimeEnvironment) => {
4048
return;
4149
}
4250

43-
log(`Attempting verification of ${contractName} (already waited for confirmations)...`);
51+
log(
52+
`Attempting verification of ${deploymentName} (contract type ${contractName}) at ${deployment.address} (already waited for confirmations)...`
53+
);
4454
await run('verify:verify', {
4555
address: deployment.address,
4656
constructorArguments: deployment.args,

deploy/002_deploy_NormalizedApi3ReaderProxyV1.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { DeploymentsExtension } from 'hardhat-deploy/types';
33

44
const VERIFICATION_BLOCK_CONFIRMATIONS = 5;
55

6+
import { getDeploymentName } from '../src';
7+
68
const deployTestFeed = async (deployments: DeploymentsExtension, deployerAddress: string) => {
79
const { address: scaledApi3FeedProxyV1Address } = await deployments.get('ScaledApi3FeedProxyV1').catch(async () => {
810
return deployments.deploy('ScaledApi3FeedProxyV1', {
@@ -42,10 +44,16 @@ module.exports = async (hre: HardhatRuntimeEnvironment) => {
4244
log(`Deployment confirmations: ${confirmations}`);
4345

4446
const contractName = 'NormalizedApi3ReaderProxyV1';
47+
const constructorArgs = [feedAddress];
48+
const constructorArgTypes = ['address'];
49+
50+
const deploymentName = getDeploymentName(contractName, constructorArgTypes, constructorArgs);
51+
log(`Generated deterministic deployment name for this instance: ${deploymentName}`);
4552

46-
const deployment = await deploy(contractName, {
53+
const deployment = await deploy(deploymentName, {
54+
contract: contractName,
4755
from: deployerAddress,
48-
args: [feedAddress],
56+
args: constructorArgs,
4957
log: true,
5058
waitConfirmations: confirmations,
5159
});
@@ -55,7 +63,9 @@ module.exports = async (hre: HardhatRuntimeEnvironment) => {
5563
return;
5664
}
5765

58-
log(`Attempting verification of ${contractName} (already waited for confirmations)...`);
66+
log(
67+
`Attempting verification of ${deploymentName} (contract type ${contractName}) at ${deployment.address} (already waited for confirmations)...`
68+
);
5969
await run('verify:verify', {
6070
address: deployment.address,
6171
constructorArguments: deployment.args,

deploy/003_deploy_ProductApi3ReaderProxyV1.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { HardhatRuntimeEnvironment } from 'hardhat/types';
22

3+
import { getDeploymentName } from '../src';
4+
35
const VERIFICATION_BLOCK_CONFIRMATIONS = 5;
46

57
module.exports = async (hre: HardhatRuntimeEnvironment) => {
@@ -36,10 +38,16 @@ module.exports = async (hre: HardhatRuntimeEnvironment) => {
3638
log(`Deployment confirmations: ${confirmations}`);
3739

3840
const contractName = 'ProductApi3ReaderProxyV1';
41+
const constructorArgs = [proxy1Address, proxy2Address];
42+
const constructorArgTypes = ['address', 'address'];
43+
44+
const deploymentName = getDeploymentName(contractName, constructorArgTypes, constructorArgs);
45+
log(`Generated deterministic deployment name for this instance: ${deploymentName}`);
3946

40-
const deployment = await deploy(contractName, {
47+
const deployment = await deploy(deploymentName, {
48+
contract: contractName,
4149
from: deployerAddress,
42-
args: [proxy1Address, proxy2Address],
50+
args: constructorArgs,
4351
log: true,
4452
waitConfirmations: confirmations,
4553
});
@@ -49,7 +57,9 @@ module.exports = async (hre: HardhatRuntimeEnvironment) => {
4957
return;
5058
}
5159

52-
log(`Attempting verification of ${contractName} (already waited for confirmations)...`);
60+
log(
61+
`Attempting verification of ${deploymentName} (contract type ${contractName}) at ${deployment.address} (already waited for confirmations)...`
62+
);
5363
await run('verify:verify', {
5464
address: deployment.address,
5565
constructorArguments: deployment.args,

deploy/004_deploy_ScaledApi3FeedProxyV1.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { DeploymentsExtension } from 'hardhat-deploy/types';
33

44
const VERIFICATION_BLOCK_CONFIRMATIONS = 5;
55

6+
import { getDeploymentName } from '../src';
7+
68
const deployTestProxy = async (deployments: DeploymentsExtension, deployerAddress: string) => {
79
const { address: inverseApi3ReaderProxyV1Address } = await deployments
810
.get('InverseApi3ReaderProxyV1')
@@ -48,10 +50,16 @@ module.exports = async (hre: HardhatRuntimeEnvironment) => {
4850
log(`Deployment confirmations: ${confirmations}`);
4951

5052
const contractName = 'ScaledApi3FeedProxyV1';
53+
const constructorArgs = [proxyAddress, decimals];
54+
const constructorArgTypes = ['address', 'uint8'];
55+
56+
const deploymentName = getDeploymentName(contractName, constructorArgTypes, constructorArgs);
57+
log(`Generated deterministic deployment name for this instance: ${deploymentName}`);
5158

52-
const deployment = await deploy(contractName, {
59+
const deployment = await deploy(deploymentName, {
60+
contract: contractName,
5361
from: deployerAddress,
54-
args: [proxyAddress, decimals],
62+
args: constructorArgs,
5563
log: true,
5664
waitConfirmations: confirmations,
5765
});
@@ -61,7 +69,9 @@ module.exports = async (hre: HardhatRuntimeEnvironment) => {
6169
return;
6270
}
6371

64-
log(`Attempting verification of ${contractName} (already waited for confirmations)...`);
72+
log(
73+
`Attempting verification of ${deploymentName} (contract type ${contractName}) at ${deployment.address} (already waited for confirmations)...`
74+
);
6575
await run('verify:verify', {
6676
address: deployment.address,
6777
constructorArguments: deployment.args,

deployments/addresses.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"deploy:NormalizedApi3ReaderProxyV1": "hardhat deploy --network $NETWORK --tags NormalizedApi3ReaderProxyV1",
2828
"deploy:ProductApi3ReaderProxyV1": "hardhat deploy --network $NETWORK --tags ProductApi3ReaderProxyV1",
2929
"deploy:ScaledApi3FeedProxyV1": "hardhat deploy --network $NETWORK --tags ScaledApi3FeedProxyV1",
30+
"generate:deployment-addresses": "ts-node scripts/generate-deployment-addresses.ts",
3031
"lint": "pnpm run prettier:check && pnpm run lint:eslint && pnpm run lint:solhint",
3132
"lint:solhint": "solhint ./contracts/**/*.sol",
3233
"lint:eslint": "eslint . --ext .js,.ts",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as fs from 'node:fs';
2+
import { join } from 'node:path';
3+
4+
import { getDeploymentAddresses } from './src/deployment-addresses';
5+
6+
async function main(): Promise<void> {
7+
fs.writeFileSync(join('deployments', 'addresses.json'), getDeploymentAddresses());
8+
}
9+
10+
main()
11+
.then(() => process.exit(0))
12+
.catch((error) => {
13+
// eslint-disable-next-line no-console
14+
console.error(error);
15+
process.exit(1);
16+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as fs from 'node:fs';
2+
import { join, parse as parsePath } from 'node:path';
3+
4+
import type { AddressLike } from 'ethers';
5+
6+
export interface DeploymentAddressEntry {
7+
address: AddressLike;
8+
constructorArgs?: any[];
9+
constructorArgTypes?: string[];
10+
}
11+
12+
export interface ChainDeploymentAddresses {
13+
[chainId: string]: DeploymentAddressEntry;
14+
}
15+
16+
export interface AllDeploymentAddresses {
17+
[deploymentName: string]: ChainDeploymentAddresses;
18+
}
19+
20+
/**
21+
* Reads deployment artifacts from the `deployments` directory (specific to data-feed-proxy-combinators)
22+
* and aggregates contract addresses, constructor arguments, and types by deployment name and chain ID.
23+
* @returns A stringified JSON object of deployment addresses.
24+
*/
25+
export function getDeploymentAddresses(): string {
26+
const allAddresses: AllDeploymentAddresses = {};
27+
const deploymentsRoot = join(__dirname, '..', '..', 'deployments');
28+
29+
if (!fs.existsSync(deploymentsRoot)) {
30+
throw new Error(`Deployments directory not found at ${deploymentsRoot}.`);
31+
}
32+
33+
const networkDirs = fs
34+
.readdirSync(deploymentsRoot, { withFileTypes: true })
35+
.filter((dirent) => dirent.isDirectory() && dirent.name !== 'localhost' && dirent.name !== 'hardhat')
36+
.map((dirent) => dirent.name);
37+
38+
for (const networkName of networkDirs) {
39+
const networkPath = join(deploymentsRoot, networkName);
40+
const chainIdFilePath = join(networkPath, '.chainId');
41+
if (!fs.existsSync(chainIdFilePath)) continue;
42+
const chainId = fs.readFileSync(chainIdFilePath, 'utf8').trim();
43+
44+
const deploymentFiles = fs
45+
.readdirSync(networkPath, { withFileTypes: true })
46+
.filter((dirent) => dirent.isFile() && dirent.name.endsWith('.json'))
47+
.map((dirent) => dirent.name);
48+
49+
for (const deploymentFile of deploymentFiles) {
50+
const deploymentName = parsePath(deploymentFile).name;
51+
const artifact = JSON.parse(fs.readFileSync(join(networkPath, deploymentFile), 'utf8'));
52+
const constructorEntry = artifact.abi.find((item: any) => item.type === 'constructor');
53+
const constructorArgTypes = constructorEntry?.inputs?.map((input: any) => input.type) || [];
54+
55+
if (!allAddresses[deploymentName]) allAddresses[deploymentName] = {};
56+
allAddresses[deploymentName][chainId] = {
57+
address: artifact.address,
58+
constructorArgs: artifact.args || [],
59+
constructorArgTypes,
60+
};
61+
}
62+
}
63+
return `${JSON.stringify(allAddresses, null, 2)}\n`;
64+
}

scripts/verify-vendor-contracts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ async function main() {
4343
}
4444
}
4545

46-
/* eslint-disable */
4746
main()
4847
.then(() => process.exit(0))
4948
.catch((error) => {
49+
// eslint-disable-next-line no-console
5050
console.log(error);
5151
process.exit(1);
5252
});

src/deployment.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ethers } from 'ethers';
2+
3+
/**
4+
* Creates a deterministic deployment name based on a base name and constructor arguments.
5+
* @param baseName The base name for the contract (e.g., 'ProductApi3ReaderProxyV1').
6+
* @param constructorArgTypes Array of Solidity types for the constructor arguments.
7+
* @param constructorArgs Array of actual constructor argument values.
8+
* @returns A deterministic deployment name string.
9+
*/
10+
export const getDeploymentName = (baseName: string, constructorArgTypes: string[], constructorArgs: any[]): string => {
11+
// Ensure addresses are checksummed for consistent ABI encoding
12+
const processedArgs = constructorArgs.map((arg, index) => {
13+
if (constructorArgTypes[index] === 'address' && typeof arg === 'string' && ethers.isAddress(arg)) {
14+
return ethers.getAddress(arg); // Ensures checksum
15+
}
16+
return arg;
17+
});
18+
const encodedArgs = ethers.AbiCoder.defaultAbiCoder().encode(constructorArgTypes, processedArgs);
19+
const argsHash = ethers.keccak256(encodedArgs).slice(2, 10); // Use a short 8-char hex hash (e.g., 'a1b2c3d4')
20+
return `${baseName}_${argsHash}`;
21+
};

0 commit comments

Comments
 (0)