Skip to content

Commit 98cf12d

Browse files
committed
some upgrade helper scripts
1 parent 19c219f commit 98cf12d

File tree

5 files changed

+369
-1
lines changed

5 files changed

+369
-1
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"PubkeyRouterFacet": "0x053E196d6A10B7B0C148279aA49a60a734919f85",
3+
"PubkeyRouterViewsFacet": "0x040655A294C0A305057A39Cc20F5aAF975147029",
4+
"BackupRecoveryFacet": "0x6bce4791d2f75B2eDb57Ee8f638A42d64b66d9BE",
5+
"PKPNFTFacet": "0xC080749178f67f4D5D8eB2C8E37acF48fFbC7692",
6+
"PKPPermissionsFacet": "0x0Fbe279EDCB29eF015E1067Aa10cD042292AC144",
7+
"PriceFeedFacet": "0xFEfd9170Bd4FA99BFA5589AC30B4bF5Ee4992827",
8+
"StakingAcrossRealmsFacet": "0x53189d762982D803C1EbBc97111570f46969202a",
9+
"StakingAdminFacet": "0x6272403591a8270E32f3853d3c9e023e9a2dE9Cf",
10+
"StakingFacet": "0x206D76Cf8B15De9c6Cb6f414Dbc662c319a202CB",
11+
"StakingKeySetsFacet": "0x484361FB33428397f6CE29f96018dABe6bdfAA87",
12+
"StakingValidatorFacet": "0x3F6e07B73Ff4efd3fCcb028355AcD3E0961dC2F5",
13+
"StakingViewsFacet": "0x6a6ee7fFE43C131b2E4c3d1b9255Ec8Dc1F589bE"
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
**IMPORTANT** You must run `npx hardhat clean` then `npx hardhat compile` before running the scripts in this directory. Otherwise, you'll deploy all the old contracts.
2+
3+
If you're upgrading a bunch of diamond facets, you can use the `deployMultipleContracts.ts` script to deploy them all in parallel. This will create a JSON file called `deployedMultipleContracts.json` in the root of the project.
4+
5+
Go to the networks repo and grab the `deployed-lit-node-contracts-temp.json` file for the network you're upgrading, and put it in the root of this project (the contracts folder).
6+
7+
You can then run ```HARDHAT_NETWORK=yellowstone npx ts-node --files scripts/contractAndDiamondUpgrades/createDiamondManifest.ts \
8+
--deployed-multiple-contracts-json deployedMultipleContracts.json \
9+
--previous-deploy-json deployed-lit-node-contracts-temp.json``` for example to create the diamond manifest.
10+
11+
You can then run `HARDHAT_NETWORK=yellowstone npx ts-node --files scripts/diamondContracts/diamondCut.ts --diamond-owner-signer-private-key <KEY>` to cut the diamond from the manifest.
12+
13+
After this process, make sure you copy the `deployedMultipleContracts.json` new facet addresses into the correct `deployed-lit-node-contracts-temp.json` file in the Networks repo.
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Full command:
2+
// HARDHAT_NETWORK=<NETWORK> npx ts-node --files scripts/contractAndDiamondUpgrades/createDiamondManifest.ts \
3+
// --deployed-multiple-contracts-json deployedMultipleContracts.json \
4+
// --previous-deploy-json deployed-lit-node-contracts-temp.json \
5+
// [--manifest-file scripts/diamondContracts/manifests/diamondCutManifest.json] \
6+
// [--reset-manifest true]
7+
8+
import hre from 'hardhat';
9+
import yargs from 'yargs';
10+
import fs from 'fs/promises';
11+
import path from 'path';
12+
13+
import { MANIFESTS_DIR, appendDiamondCutOperationToManifest } from '../diamondContracts/lib/diamondCutManifest';
14+
import { FacetCutAction } from '../diamondContracts/lib/types';
15+
16+
const { ethers } = hre;
17+
18+
type Address = string;
19+
20+
type PreviousDeployJson = {
21+
stakingContractAddress?: Address;
22+
pubkeyRouterContractAddress?: Address;
23+
pkpNftContractAddress?: Address;
24+
pkpPermissionsContractAddress?: Address;
25+
backupRecoveryContractAddress?: Address;
26+
priceFeedContractAddress?: Address;
27+
facets?: Record<string, Array<{ facetName: string; facetAddress: Address }>>;
28+
};
29+
30+
type DiamondAddressKey = Exclude<keyof PreviousDeployJson, 'facets'>;
31+
32+
const DIAMOND_GROUP_TO_ADDRESS_KEY: Record<string, DiamondAddressKey> = {
33+
Staking: 'stakingContractAddress',
34+
PubkeyRouter: 'pubkeyRouterContractAddress',
35+
PKPNFT: 'pkpNftContractAddress',
36+
PKPPermissions: 'pkpPermissionsContractAddress',
37+
BackupRecovery: 'backupRecoveryContractAddress',
38+
PriceFeed: 'priceFeedContractAddress',
39+
};
40+
41+
async function run() {
42+
const inputs = await getInputsFromCliOptions();
43+
44+
const previousDeploy: PreviousDeployJson = JSON.parse(
45+
await fs.readFile(inputs.previousDeployJson, 'utf8')
46+
);
47+
const deployedMultiple: Record<string, Address> = JSON.parse(
48+
await fs.readFile(inputs.deployedMultipleContractsJson, 'utf8')
49+
);
50+
51+
if (!previousDeploy.facets) {
52+
throw new Error(
53+
`Missing "facets" in ${inputs.previousDeployJson}; can't look up old facet addresses`
54+
);
55+
}
56+
57+
const manifestFilePath =
58+
inputs.manifestFile ??
59+
path.join(
60+
__dirname,
61+
'..',
62+
'diamondContracts',
63+
MANIFESTS_DIR,
64+
'diamondCutManifest.json'
65+
);
66+
67+
if (inputs.resetManifest) {
68+
await fs.mkdir(path.dirname(manifestFilePath), { recursive: true });
69+
await fs.writeFile(manifestFilePath, JSON.stringify({ operations: [] }, null, 2));
70+
console.log(`Reset manifest at ${manifestFilePath}`);
71+
}
72+
73+
const facetToGroup: Record<string, string> = {};
74+
const facetToOldAddress: Record<string, Address> = {};
75+
76+
for (const [groupName, facets] of Object.entries(previousDeploy.facets)) {
77+
for (const f of facets) {
78+
facetToGroup[f.facetName] = groupName;
79+
facetToOldAddress[f.facetName] = f.facetAddress;
80+
}
81+
}
82+
83+
const facetNames = Object.keys(deployedMultiple);
84+
console.log(`Preparing manifest for ${facetNames.length} facets...`);
85+
86+
for (const contractName of facetNames) {
87+
const newFacetAddress = deployedMultiple[contractName];
88+
const groupName = facetToGroup[contractName];
89+
const oldFacetAddress = facetToOldAddress[contractName];
90+
91+
if (!groupName) {
92+
throw new Error(
93+
`Unable to find diamond group for facet "${contractName}" in ${inputs.previousDeployJson}`
94+
);
95+
}
96+
const diamondAddressKey = DIAMOND_GROUP_TO_ADDRESS_KEY[groupName];
97+
if (!diamondAddressKey) {
98+
throw new Error(
99+
`No diamond address mapping for group "${groupName}". Add it to DIAMOND_GROUP_TO_ADDRESS_KEY.`
100+
);
101+
}
102+
const diamondAddress = previousDeploy[diamondAddressKey];
103+
if (!diamondAddress) {
104+
throw new Error(
105+
`Missing "${diamondAddressKey}" in ${inputs.previousDeployJson}; needed for facet "${contractName}"`
106+
);
107+
}
108+
if (!oldFacetAddress) {
109+
throw new Error(
110+
`Unable to find old facet address for "${contractName}" in ${inputs.previousDeployJson}`
111+
);
112+
}
113+
if (!newFacetAddress) {
114+
throw new Error(
115+
`Missing new facet address for "${contractName}" in ${inputs.deployedMultipleContractsJson}`
116+
);
117+
}
118+
119+
// We still create a contract instance even for Remove; the manifest builder
120+
// uses the live diamond + old facet address to discover selectors for removal.
121+
const newFacet = await ethers.getContractAt(contractName, newFacetAddress);
122+
123+
console.log(
124+
`\n${contractName}\n diamond: ${diamondAddress}\n old: ${oldFacetAddress}\n new: ${newFacetAddress}`
125+
);
126+
127+
// 1) Remove old facet selectors from the diamond (uses oldFacetAddress)
128+
await appendDiamondCutOperationToManifest(
129+
manifestFilePath,
130+
diamondAddress,
131+
newFacet,
132+
FacetCutAction.Remove,
133+
contractName,
134+
oldFacetAddress
135+
);
136+
137+
// 2) Add new facet selectors to the diamond
138+
await appendDiamondCutOperationToManifest(
139+
manifestFilePath,
140+
diamondAddress,
141+
newFacet,
142+
FacetCutAction.Add,
143+
contractName
144+
);
145+
}
146+
147+
console.log(`\nDiamond cut manifest updated at ${manifestFilePath}`);
148+
}
149+
150+
run()
151+
.then(() => process.exit(0))
152+
.catch((error) => {
153+
console.error(error);
154+
process.exit(1);
155+
});
156+
157+
async function getInputsFromCliOptions(): Promise<Inputs> {
158+
const argv = await yargs(process.argv.slice(2)).options({
159+
'deployed-multiple-contracts-json': {
160+
type: 'string',
161+
describe:
162+
'Path to deployedMultipleContracts.json (new facet addresses keyed by facet name)',
163+
default: 'deployedMultipleContracts.json',
164+
},
165+
'previous-deploy-json': {
166+
type: 'string',
167+
describe:
168+
'Path to previous deployment JSON (must contain top-level diamond addresses + facets map)',
169+
default: 'deployed-lit-node-contracts-temp.json',
170+
},
171+
'manifest-file': {
172+
type: 'string',
173+
describe:
174+
'Output manifest file path (defaults to scripts/diamondContracts/manifests/diamondCutManifest.json)',
175+
required: false,
176+
},
177+
'reset-manifest': {
178+
type: 'boolean',
179+
describe:
180+
'If true, overwrite manifest with an empty operations list before appending',
181+
default: true,
182+
},
183+
}).argv;
184+
185+
return {
186+
deployedMultipleContractsJson: argv['deployed-multiple-contracts-json'],
187+
previousDeployJson: argv['previous-deploy-json'],
188+
manifestFile: argv['manifest-file'],
189+
resetManifest: argv['reset-manifest'],
190+
};
191+
}
192+
193+
interface Inputs {
194+
deployedMultipleContractsJson: string;
195+
previousDeployJson: string;
196+
manifestFile?: string;
197+
resetManifest: boolean;
198+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Full command: HARDHAT_NETWORK=<NETWORK> npx ts-node --files scripts/deployMultipleContracts.ts --deployer-private-key <PRIVATE_KEY> [--delay-ms 2000]
2+
3+
import hre from 'hardhat';
4+
import yargs from 'yargs';
5+
import fs from 'fs/promises';
6+
7+
import { hardhatDeployAndVerifySingleContract } from '../utils';
8+
9+
const { ethers } = hre;
10+
11+
const toDeploy: string[] = [
12+
'PubkeyRouterFacet',
13+
'PubkeyRouterViewsFacet',
14+
'BackupRecoveryFacet',
15+
'PKPNFTFacet',
16+
'PKPPermissionsFacet',
17+
'PriceFeedFacet',
18+
'StakingAcrossRealmsFacet',
19+
'StakingAdminFacet',
20+
'StakingFacet',
21+
'StakingKeySetsFacet',
22+
'StakingValidatorFacet',
23+
'StakingViewsFacet',
24+
];
25+
26+
// CONFIGURE THIS //
27+
const args: any[] = [];
28+
29+
// CONFIGURE THIS - delay between deployments in milliseconds (default: 2000ms = 2s)
30+
const DEFAULT_DELAY_MS = 2000;
31+
32+
async function run() {
33+
const inputs = await getInputsFromCliOptions();
34+
35+
const deployer = new ethers.Wallet(inputs.deployerPrivateKey).connect(
36+
ethers.provider
37+
);
38+
39+
const delayMs = inputs.delayMs || DEFAULT_DELAY_MS;
40+
const deploymentPromises: Array<Promise<{ contractName: string; contract: any }>> = [];
41+
42+
console.log(`\n🚀 Starting deployment of ${toDeploy.length} contracts...`);
43+
console.log(`⏱️ Delay between starting each deployment: ${delayMs}ms`);
44+
console.log(`🔄 Deployments will run in parallel after initial delay\n`);
45+
46+
// Start all deployments with delays between each start
47+
for (let i = 0; i < toDeploy.length; i++) {
48+
const contractName = toDeploy[i];
49+
const progress = `[${i + 1}/${toDeploy.length}]`;
50+
51+
// Create a promise that starts after the cumulative delay
52+
const deploymentPromise = (async () => {
53+
// Wait for the cumulative delay before starting this deployment
54+
// First one starts immediately (0ms), second after 2s, third after 4s, etc.
55+
await new Promise((resolve) => setTimeout(resolve, i * delayMs));
56+
57+
console.log(`${progress} 🚀 Starting deployment of ${contractName}...`);
58+
59+
try {
60+
const contract = await hardhatDeployAndVerifySingleContract(
61+
ethers,
62+
hre.network.name,
63+
contractName,
64+
{
65+
signer: deployer,
66+
deploymentArgs: args,
67+
}
68+
);
69+
70+
const contractAddress = await contract.getAddress();
71+
console.log(
72+
`${progress}${contractName} deployed successfully to ${contractAddress}`
73+
);
74+
75+
return { contractName, contract };
76+
} catch (error) {
77+
console.error(
78+
`${progress} ❌ Failed to deploy ${contractName}:`,
79+
error
80+
);
81+
throw error;
82+
}
83+
})();
84+
85+
deploymentPromises.push(deploymentPromise);
86+
}
87+
88+
// Wait for all deployments to complete
89+
console.log(`\n⏳ Waiting for all deployments to complete...\n`);
90+
const results = await Promise.all(deploymentPromises);
91+
92+
// Extract addresses from all completed contracts
93+
const deployedContracts: Record<string, string> = {};
94+
for (const result of results) {
95+
const contractAddress = await result.contract.getAddress();
96+
deployedContracts[result.contractName] = contractAddress;
97+
}
98+
99+
// Save all deployed contract addresses to JSON file
100+
const outputPath = 'deployedMultipleContracts.json';
101+
await fs.writeFile(
102+
outputPath,
103+
JSON.stringify(deployedContracts, null, 2)
104+
);
105+
106+
console.log(`\n✨ All contracts deployed successfully!`);
107+
console.log(`📄 Deployed addresses saved to ${outputPath}\n`);
108+
console.log('Deployed contracts:');
109+
Object.entries(deployedContracts).forEach(([name, address]) => {
110+
console.log(` ${name}: ${address}`);
111+
});
112+
}
113+
114+
run().catch((error) => {
115+
console.error('Deployment failed:', error);
116+
process.exit(1);
117+
});
118+
119+
async function getInputsFromCliOptions(): Promise<Inputs> {
120+
const argv = await yargs(process.argv.slice(2))
121+
.options({
122+
'deployer-private-key': {
123+
type: 'string',
124+
describe:
125+
'Private key of the wallet that will be used to deploy the contracts',
126+
required: true,
127+
},
128+
'delay-ms': {
129+
type: 'number',
130+
describe:
131+
'Delay in milliseconds between each contract deployment (default: 2000)',
132+
default: DEFAULT_DELAY_MS,
133+
},
134+
})
135+
.argv;
136+
137+
return argv;
138+
}
139+
140+
interface Inputs {
141+
deployerPrivateKey: string;
142+
delayMs?: number;
143+
}

blockchain/contracts/scripts/deployContract.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { hardhatDeployAndVerifySingleContract } from './utils';
88
const { ethers } = hre;
99

1010
// CONFIGURE THIS //
11-
const args: any[] = [];
11+
const args: any[] = ['0x0A88e4A0A371F783fCc2dd802f214EAF48D1C6a9', 0];
1212

1313
async function run() {
1414
const inputs = await getInputsFromCliOptions();

0 commit comments

Comments
 (0)