/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable unicorn/prefer-top-level-await */ /* eslint-disable no-console */ import fs from "node:fs"; import path from "node:path"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import type { BaseDeployConfig, DefaultAddresses } from "./common"; import { COMMON_DEPLOY_OPTIONS, deployIfNotCached, getWeb3Contract, getOrDeployWormholeContract, topupAccountsIfNecessary, } from "./common"; import type { DeploymentType } from "../src/core/base"; import { toDeploymentType, toPrivateKey } from "../src/core/base"; import { EvmChain } from "../src/core/chains"; import { PULSE_DEFAULT_PROVIDER, PULSE_DEFAULT_KEEPER, EvmPulseContract, } from "../src/core/contracts"; import { DefaultStore } from "../src/node/utils/store"; type DeploymentConfig = { type: DeploymentType; saveContract: boolean; } & BaseDeployConfig; const CACHE_FILE = ".cache-deploy-evm-pulse-contracts"; const parser = yargs(hideBin(process.argv)) .scriptName("deploy_evm_pulse_contracts.ts") .usage( "Usage: $0 --std-output-dir --private-key --chain --default-provider --wormhole-addr ", ) .options({ ...COMMON_DEPLOY_OPTIONS, chain: { type: "string", demandOption: true, desc: "Chain to upload the contract on. Can be one of the evm chains available in the store", }, }); async function deployPulseContracts( chain: EvmChain, config: DeploymentConfig, executorAddr: string, ): Promise { console.log("Deploying PulseUpgradeable on", chain.getId(), "..."); // Get the artifact and ensure bytecode is properly formatted const pulseArtifact = JSON.parse( fs.readFileSync( path.join(config.jsonOutputDir, "PulseUpgradeable.json"), "utf8", ), ); console.log("PulseArtifact bytecode type:", typeof pulseArtifact.bytecode); const pulseImplAddr = await deployIfNotCached( CACHE_FILE, chain, config, "PulseUpgradeable", [], ); console.log("PulseUpgradeable implementation deployed at:", pulseImplAddr); const pulseImplContract = getWeb3Contract( config.jsonOutputDir, "PulseUpgradeable", pulseImplAddr, ); console.log("Preparing initialization data..."); const pulseInitData = pulseImplContract.methods .initialize( executorAddr, // owner executorAddr, // admin "1", // pythFeeInWei executorAddr, // pythAddress - using executor as a placeholder chain.isMainnet() ? PULSE_DEFAULT_PROVIDER.mainnet : PULSE_DEFAULT_PROVIDER.testnet, true, // prefillRequestStorage 3600, // exclusivityPeriodSeconds - 1 hour ) .encodeABI(); console.log("Deploying ERC1967Proxy for Pulse..."); return await deployIfNotCached( CACHE_FILE, chain, config, "ERC1967Proxy", [pulseImplAddr, pulseInitData], // NOTE: we are deploying a ERC1967Proxy when deploying executor // we need to provide a different cache key. As the `artifactname` // is same in both case which means the cache key will be same `${chain.getId()}-ERC1967Proxy-PULSE1`, ); } async function topupPulseAccountsIfNecessary( chain: EvmChain, deploymentConfig: DeploymentConfig, ) { const accounts: [string, DefaultAddresses][] = [ ["keeper", PULSE_DEFAULT_KEEPER], ["provider", PULSE_DEFAULT_PROVIDER], ]; await topupAccountsIfNecessary(chain, deploymentConfig, accounts); } async function main() { const argv = await parser.argv; const chainName = argv.chain; const chain = DefaultStore.chains[chainName]; if (!chain) { throw new Error(`Chain ${chainName} not found`); } else if (!(chain instanceof EvmChain)) { throw new TypeError(`Chain ${chainName} is not an EVM chain`); } const deploymentConfig: DeploymentConfig = { type: toDeploymentType(argv.deploymentType), gasMultiplier: argv.gasMultiplier, gasPriceMultiplier: argv.gasPriceMultiplier, privateKey: toPrivateKey(argv.privateKey), jsonOutputDir: argv.stdOutputDir, saveContract: argv.saveContract, }; const wormholeContract = await getOrDeployWormholeContract( chain, deploymentConfig, CACHE_FILE, ); await topupPulseAccountsIfNecessary(chain, deploymentConfig); const maskedDeploymentConfig = { ...deploymentConfig, privateKey: deploymentConfig.privateKey ? `` : undefined, }; console.log( `Deployment config: ${JSON.stringify(maskedDeploymentConfig, undefined, 2)}\n`, ); console.log(`Deploying pulse contracts on ${chain.getId()}...`); const executorAddr = wormholeContract.address; // Using wormhole contract as executor for Pulse const pulseAddr = await deployPulseContracts( chain, deploymentConfig, executorAddr, ); if (deploymentConfig.saveContract) { console.log("Saving the contract in the store..."); const contract = new EvmPulseContract(chain, pulseAddr); DefaultStore.pulse_contracts[contract.getId()] = contract; DefaultStore.saveAllContracts(); } console.log( `✅ Deployed pulse contracts on ${chain.getId()} at ${pulseAddr}\n\n`, ); } main();