Skip to content

Commit f1c82b6

Browse files
authored
Chore(contract manger) lazer deploy script (#2971)
* chore(contract-manager) Lazer Deploy scripts * update * fix polygon rpc * fix * fix * Added script for governance * governance script (WIP) * fix * revert Script lazer * fix * added comment
1 parent d19fc49 commit f1c82b6

File tree

5 files changed

+712
-1
lines changed

5 files changed

+712
-1
lines changed
Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
/**
2+
* PythLazer EVM Contract Deployment and Management Script
3+
*
4+
* This script provides functionality to deploy PythLazer contracts and manage trusted signers
5+
* on EVM-compatible blockchains. It integrates with the DefaultStore system and supports
6+
* both deployment and contract management operations.
7+
*
8+
* FLAGS AND USAGE:
9+
*
10+
* 1. DEPLOYMENT FLAGS:
11+
* --deploy Deploy the PythLazer contract (default: true)
12+
* --verify Verify contract on block explorer after deployment
13+
* --etherscan-api-key <key> Required if --verify is true
14+
*
15+
* 2. TRUSTED SIGNER MANAGEMENT:
16+
* --update-signer <address> Address of the trusted signer to add/update
17+
* --expires-at <timestamp> Unix timestamp when the signer expires
18+
*
19+
* EXAMPLES:
20+
*
21+
* Deploy only:
22+
* npx ts-node deploy_evm_lazer_contracts.ts --chain ethereum --private-key <key>
23+
*
24+
* Deploy with verification:
25+
* npx ts-node deploy_evm_lazer_contracts.ts --chain ethereum --private-key <key> --verify --etherscan-api-key <key>
26+
*
27+
* Update trusted signer only (requires existing contract):
28+
* npx ts-node deploy_evm_lazer_contracts.ts --chain ethereum --private-key <key> --deploy false --update-signer 0x123... --expires-at 1735689600
29+
*
30+
* Deploy and update trusted signer in one command:
31+
* npx ts-node deploy_evm_lazer_contracts.ts --chain ethereum --private-key <key> --update-signer 0x123... --expires-at 1735689600
32+
*
33+
* NOTES:
34+
* - The --deploy flag defaults to true if no other flags are specified
35+
* - Both --update-signer and --expires-at must be provided together
36+
* - If updating trusted signer without deploying, an existing contract must be found
37+
* - The script automatically saves deployed contracts to the store and updates EvmLazerContracts.json
38+
* - All operations use the chain's RPC URL from the DefaultStore
39+
*/
40+
41+
import yargs from "yargs";
42+
import { hideBin } from "yargs/helpers";
43+
import { execSync } from "child_process";
44+
import { join } from "path";
45+
import { DefaultStore } from "../src/node/utils/store";
46+
import { EvmChain } from "../src/core/chains";
47+
import { EvmLazerContract } from "../src/core/contracts/evm";
48+
import { toPrivateKey, PrivateKey } from "../src/core/base";
49+
50+
const parser = yargs(hideBin(process.argv))
51+
.usage(
52+
"Deploys PythLazer contracts and/or updates trusted signers\n" +
53+
"Usage: $0 --chain <chain_name> --private-key <private_key> [--deploy] [--update-signer <address> --expires-at <timestamp>]",
54+
)
55+
.options({
56+
chain: {
57+
type: "string",
58+
description: "Chain name to deploy to (from EvmChains.json)",
59+
demandOption: true,
60+
},
61+
"private-key": {
62+
type: "string",
63+
description: "Private key for deployment and transactions",
64+
demandOption: true,
65+
},
66+
deploy: {
67+
type: "boolean",
68+
description:
69+
"Deploy the PythLazer contract (default: true if no other flags specified)",
70+
default: true,
71+
},
72+
verify: {
73+
type: "boolean",
74+
description:
75+
"Verify contract on block explorer (only used with --deploy)",
76+
default: false,
77+
},
78+
"etherscan-api-key": {
79+
type: "string",
80+
description:
81+
"Etherscan API key for verification (required if --verify is true)",
82+
},
83+
"update-signer": {
84+
type: "string",
85+
description: "Update trusted signer address (requires --expires-at)",
86+
},
87+
"expires-at": {
88+
type: "number",
89+
description:
90+
"Expiration timestamp for trusted signer in Unix timestamp format (required if --update-signer is specified)",
91+
},
92+
})
93+
.check((argv) => {
94+
// If update-signer is specified, expires-at must also be specified
95+
if (argv["update-signer"] && !argv["expires-at"]) {
96+
throw new Error(
97+
"--expires-at is required when --update-signer is specified",
98+
);
99+
}
100+
101+
// If expires-at is specified, update-signer must also be specified
102+
if (argv["expires-at"] && !argv["update-signer"]) {
103+
throw new Error(
104+
"--update-signer is required when --expires-at is specified",
105+
);
106+
}
107+
108+
// If verify is true, etherscan-api-key must be provided
109+
if (argv.verify && !argv["etherscan-api-key"]) {
110+
throw new Error("--etherscan-api-key is required when --verify is true");
111+
}
112+
113+
return true;
114+
});
115+
116+
/**
117+
* Deploys the PythLazer contract using forge script
118+
* @param chain The EVM chain to deploy to
119+
* @param privateKey The private key for deployment
120+
* @param verify Whether to verify the contract
121+
* @param etherscanApiKey The Etherscan API key for verification
122+
* @returns The deployed contract address
123+
*/
124+
async function deployPythLazerContract(
125+
chain: EvmChain,
126+
privateKey: string,
127+
verify: boolean,
128+
etherscanApiKey?: string,
129+
): Promise<string> {
130+
const lazerContractsDir = join(__dirname, "../../lazer/contracts/evm");
131+
const rpcUrl = chain.rpcUrl;
132+
133+
console.log(`Deploying PythLazer contract to ${chain.getId()}...`);
134+
console.log(`RPC URL: ${rpcUrl}`);
135+
136+
// Build forge command
137+
let forgeCommand = `forge script script/PythLazerDeploy.s.sol --rpc-url ${rpcUrl} --private-key ${privateKey} --broadcast`;
138+
139+
if (verify && etherscanApiKey) {
140+
forgeCommand += ` --verify --etherscan-api-key ${etherscanApiKey}`;
141+
}
142+
143+
try {
144+
// Execute forge script
145+
console.log("Running forge deployment script...");
146+
const output = execSync(forgeCommand, {
147+
cwd: lazerContractsDir,
148+
encoding: "utf8",
149+
stdio: "pipe",
150+
});
151+
152+
console.log("Deployment output:");
153+
console.log(output);
154+
155+
// Extract proxy address from output
156+
const proxyMatch = output.match(/Deployed proxy to: (0x[a-fA-F0-9]{40})/);
157+
if (!proxyMatch) {
158+
throw new Error("Could not extract proxy address from deployment output");
159+
}
160+
161+
const proxyAddress = proxyMatch[1];
162+
console.log(`\nPythLazer proxy deployed at: ${proxyAddress}`);
163+
164+
return proxyAddress;
165+
} catch (error) {
166+
console.error("Deployment failed:", error);
167+
throw error;
168+
}
169+
}
170+
171+
/**
172+
* Updates the EvmLazerContracts.json file with the new deployment
173+
* @param chain The chain where the contract was deployed
174+
* @param address The deployed contract address
175+
*/
176+
function updateContractsFile(chain: EvmChain, address: string): void {
177+
console.log(`Updating contracts file for ${chain.getId()}`);
178+
const lazerContract = new EvmLazerContract(chain, address);
179+
DefaultStore.lazer_contracts[lazerContract.getId()] = lazerContract;
180+
DefaultStore.saveAllContracts();
181+
182+
console.log(`\nUpdated EvmLazerContracts.json with new deployment`);
183+
console.log(`Chain: ${chain.getId()}`);
184+
console.log(`Address: ${address}`);
185+
}
186+
187+
/**
188+
* Gets or creates an EvmLazerContract instance
189+
* @param chain The EVM chain
190+
* @param address The contract address
191+
* @returns The EvmLazerContract instance
192+
*/
193+
function getOrCreateLazerContract(
194+
chain: EvmChain,
195+
address: string,
196+
): EvmLazerContract {
197+
return new EvmLazerContract(chain, address);
198+
}
199+
200+
/**
201+
* Updates the trusted signer for a PythLazer contract
202+
* @param chain The EVM chain
203+
* @param contractAddress The contract address
204+
* @param trustedSigner The trusted signer address
205+
* @param expiresAt The expiration timestamp
206+
* @param privateKey The private key for the transaction
207+
*/
208+
async function updateTrustedSigner(
209+
chain: EvmChain,
210+
contractAddress: string,
211+
trustedSigner: string,
212+
expiresAt: number,
213+
privateKey: PrivateKey,
214+
): Promise<void> {
215+
const contract = getOrCreateLazerContract(chain, contractAddress);
216+
await contract.updateTrustedSigner(trustedSigner, expiresAt, privateKey);
217+
}
218+
219+
function findLazerContract(chain: EvmChain): EvmLazerContract | undefined {
220+
for (const contract of Object.values(DefaultStore.lazer_contracts)) {
221+
if (
222+
contract instanceof EvmLazerContract &&
223+
contract.chain.getId() === chain.getId()
224+
) {
225+
console.log(
226+
`Found lazer contract for ${chain.getId()} at ${contract.address}`,
227+
);
228+
return contract;
229+
}
230+
}
231+
}
232+
233+
export async function findOrDeployPythLazerContract(
234+
chain: EvmChain,
235+
privateKey: string,
236+
verify: boolean,
237+
etherscanApiKey?: string,
238+
): Promise<string> {
239+
const lazerContract = findLazerContract(chain);
240+
if (lazerContract) {
241+
console.log(
242+
`Found lazer contract for ${chain.getId()} at ${lazerContract.address}`,
243+
);
244+
return lazerContract.address;
245+
}
246+
const deployedAddress = await deployPythLazerContract(
247+
chain,
248+
privateKey,
249+
verify,
250+
etherscanApiKey,
251+
);
252+
console.log(
253+
`✅ PythLazer contract deployed successfully at ${deployedAddress}`,
254+
);
255+
updateContractsFile(chain, deployedAddress);
256+
return deployedAddress;
257+
}
258+
259+
export async function main() {
260+
const argv = await parser.argv;
261+
262+
// Get the chain from the store
263+
const chain = DefaultStore.getChainOrThrow(argv.chain, EvmChain);
264+
265+
try {
266+
let deployedAddress: string | undefined;
267+
268+
// Step 1: Deploy contract if requested
269+
if (argv.deploy) {
270+
console.log(`Deploying PythLazer contract to ${chain.getId()}...`);
271+
console.log(`Chain: ${chain.getId()}`);
272+
console.log(`RPC URL: ${chain.rpcUrl}`);
273+
console.log(`Verification: ${argv.verify ? "Enabled" : "Disabled"}`);
274+
275+
deployedAddress = await findOrDeployPythLazerContract(
276+
chain,
277+
argv["private-key"],
278+
argv.verify,
279+
argv["etherscan-api-key"],
280+
);
281+
}
282+
283+
// Step 2: Update trusted signer if requested
284+
if (argv["update-signer"] && argv["expires-at"]) {
285+
console.log(`\nUpdating trusted signer on ${chain.getId()}...`);
286+
console.log(`Signer Address: ${argv["update-signer"]}`);
287+
console.log(
288+
`Expires At: ${new Date(argv["expires-at"] * 1000).toISOString()}`,
289+
);
290+
291+
let contractAddress: string;
292+
293+
// Use deployed address if we just deployed, otherwise find existing contract
294+
if (deployedAddress) {
295+
contractAddress = deployedAddress;
296+
console.log(`Using newly deployed contract at ${contractAddress}`);
297+
} else {
298+
const lazerContract = findLazerContract(chain);
299+
if (lazerContract) {
300+
contractAddress = lazerContract.address;
301+
console.log(`Using existing contract at ${contractAddress}`);
302+
} else {
303+
throw new Error(
304+
`No lazer contract found for ${chain.getId()}. Deploy a contract first using --deploy.`,
305+
);
306+
}
307+
}
308+
309+
await updateTrustedSigner(
310+
chain,
311+
contractAddress,
312+
argv["update-signer"],
313+
argv["expires-at"],
314+
toPrivateKey(argv["private-key"]),
315+
);
316+
317+
console.log(`\n✅ Trusted signer updated successfully`);
318+
}
319+
320+
// Summary
321+
console.log(`\n Operation Summary:`);
322+
if (argv.deploy && argv["update-signer"]) {
323+
console.log(`\n✅ Contract deployed at: ${deployedAddress}`);
324+
console.log(`Trusted signer updated: ${argv["update-signer"]}`);
325+
console.log(
326+
`Expires at: ${new Date(argv["expires-at"]! * 1000).toISOString()}`,
327+
);
328+
} else if (argv.deploy) {
329+
console.log(`Contract deployed at ${deployedAddress}`);
330+
} else if (argv["update-signer"]) {
331+
console.log(`Trusted signer updated successfully`);
332+
} else {
333+
console.log(
334+
`No operations performed. Use --deploy to deploy or --update-signer to update trusted signer.`,
335+
);
336+
}
337+
} catch (error) {
338+
console.error("Operation failed:", error);
339+
process.exit(1);
340+
}
341+
}
342+
343+
main();

0 commit comments

Comments
 (0)