diff --git a/.env.example b/.env.example index 5a52f77..acd0a09 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,4 @@ -MNEMONIC="YOUR_MNEMONIC" PRIVATE_KEY="PRIVATE_KEY" ALCHEMY_API_KEY="ALCHEMY_API_KEY" -MAINNET_ETHERSCAN_API_KEY="MAINNET_ETHERSCAN_API_KEY" -POLYGON_ETHERSCAN_API_KEY="POLYGON_ETHERSCAN_API_KEY" +# Unified Etherscan V2 API key used by `rocketh-verify etherscan` +ETHERSCAN_API_KEY="ETHERSCAN_API_KEY" diff --git a/.gitignore b/.gitignore index 594fdf4..9cf4bc2 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ lcov.info artifacts cache_hardhat deployments +generated diff --git a/bun.lockb b/bun.lockb index 90390e6..96e8f35 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/commitlint.config.js b/commitlint.config.cjs similarity index 100% rename from commitlint.config.js rename to commitlint.config.cjs diff --git a/deploy/scripts/fees/MainFeeDistributor.ts b/deploy/scripts/fees/MainFeeDistributor.ts index c6fea49..b88aec9 100644 --- a/deploy/scripts/fees/MainFeeDistributor.ts +++ b/deploy/scripts/fees/MainFeeDistributor.ts @@ -1,6 +1,5 @@ import assert from "assert"; - -import { type DeployFunction } from "hardhat-deploy/types"; +import { deployScript, artifacts } from "@rocketh"; import { checkAddressValid, getTokenAddressFromConfig } from "../../utils"; import { readFileSync } from "fs"; @@ -8,35 +7,31 @@ import { ConfigData } from "../../utils/types"; const contractName = "MainFeeDistributor"; -const deploy: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments } = hre; - - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - - assert(deployer, "Missing deployer account"); - - console.log(`Network: ${hre.network.name}`); - console.log(`Deployer: ${deployer}`); - const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${hre.network.name}/config.json`).toString()); - if (!config.isMainChainFeeDistributor) throw new Error("MainFeeDistributor must be deployed on main chain"); +export default deployScript( + async ({ namedAccounts, name, deploy }) => { + const { deployer } = namedAccounts; + assert(deployer, "Missing deployer account"); + console.log(`Network: ${name}\nDeployer: ${deployer}\nDeploying: ${contractName}`); - const accessManager = checkAddressValid(config.accessManager, "access manager"); + const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${name}/config.json`).toString()); + if (!config.isMainChainFeeDistributor) throw new Error("MainFeeDistributor must be deployed on main chain"); - const feeDistributorData = config.feeDistributor; - const bridgeableToken = checkAddressValid(feeDistributorData.bridgeableToken, "bridgeable token"); + const accessManager = checkAddressValid(config.accessManager, "access manager"); - const feeToken = getTokenAddressFromConfig(feeDistributorData.feeToken, config); + const feeDistributorData = config.feeDistributor; + const bridgeableToken = checkAddressValid(feeDistributorData.bridgeableToken, "bridgeable token"); - const contract = await deploy(contractName, { - from: deployer, - args: [accessManager, bridgeableToken, feeToken], - log: true, - skipIfAlreadyDeployed: false, - }); + const feeToken = getTokenAddressFromConfig(feeDistributorData.feeToken, config); - console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${contract.address}`); -}; + const contract = await deploy(contractName, { + account: deployer, + artifact: artifacts.MainFeeDistributor, + args: [accessManager, bridgeableToken, feeToken], + }); -deploy.tags = [contractName]; -export default deploy; + console.log(`Deployed ${contractName}, network: ${name}, address: ${contract.address}`); + }, + { + tags: [contractName], + }, +); diff --git a/deploy/scripts/fees/SideChainFeeCollector.ts b/deploy/scripts/fees/SideChainFeeCollector.ts index 71f842b..a33ec06 100644 --- a/deploy/scripts/fees/SideChainFeeCollector.ts +++ b/deploy/scripts/fees/SideChainFeeCollector.ts @@ -1,6 +1,5 @@ import assert from "assert"; - -import { type DeployFunction } from "hardhat-deploy/types"; +import { deployScript, artifacts } from "@rocketh"; import { checkAddressValid, getLzEidReceiver, getTokenAddressFromConfig } from "../../utils"; import { readFileSync } from "fs"; @@ -8,37 +7,33 @@ import { ConfigData } from "../../utils/types"; const contractName = "SideChainFeeCollector"; -const deploy: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments } = hre; - - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - - assert(deployer, "Missing deployer account"); - - console.log(`Network: ${hre.network.name}`); - console.log(`Deployer: ${deployer}`); - const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${hre.network.name}/config.json`).toString()); - if (config.isMainChainFeeDistributor) throw new Error("SideChainFeeCollector must be deployed on side chain"); +export default deployScript( + async ({ namedAccounts, name, deploy }) => { + const { deployer } = namedAccounts; + assert(deployer, "Missing deployer account"); + console.log(`Network: ${name}\nDeployer: ${deployer}\nDeploying: ${contractName}`); - const accessManager = checkAddressValid(config.accessManager, "access manager"); + const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${name}/config.json`).toString()); + if (config.isMainChainFeeDistributor) throw new Error("SideChainFeeCollector must be deployed on side chain"); - const feeDistributorData = config.feeDistributor; - const lzEidReceiver = getLzEidReceiver(feeDistributorData.mainChain); - const destinationReceiver = checkAddressValid(feeDistributorData.destinationReceiver, "destination receiver"); - const bridgeableToken = checkAddressValid(feeDistributorData.bridgeableToken, "bridgeable token"); + const accessManager = checkAddressValid(config.accessManager, "access manager"); - const feeToken = getTokenAddressFromConfig(feeDistributorData.feeToken, config); + const feeDistributorData = config.feeDistributor; + const lzEidReceiver = getLzEidReceiver(feeDistributorData.mainChain); + const destinationReceiver = checkAddressValid(feeDistributorData.destinationReceiver, "destination receiver"); + const bridgeableToken = checkAddressValid(feeDistributorData.bridgeableToken, "bridgeable token"); - const contract = await deploy(contractName, { - from: deployer, - args: [accessManager, lzEidReceiver, bridgeableToken, destinationReceiver, feeToken], - log: true, - skipIfAlreadyDeployed: false, - }); + const feeToken = getTokenAddressFromConfig(feeDistributorData.feeToken, config); - console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${contract.address}`); -}; + const contract = await deploy(contractName, { + account: deployer, + artifact: artifacts.SideChainFeeCollector, + args: [accessManager, lzEidReceiver, bridgeableToken, destinationReceiver, feeToken], + }); -deploy.tags = [contractName]; -export default deploy; + console.log(`Deployed ${contractName}, network: ${name}, address: ${contract.address}`); + }, + { + tags: [contractName], + }, +); diff --git a/deploy/scripts/rewardMerkleDistributor/RewardMerkleDistributor.ts b/deploy/scripts/rewardMerkleDistributor/RewardMerkleDistributor.ts index 79ac598..41295cc 100644 --- a/deploy/scripts/rewardMerkleDistributor/RewardMerkleDistributor.ts +++ b/deploy/scripts/rewardMerkleDistributor/RewardMerkleDistributor.ts @@ -1,6 +1,5 @@ import assert from "assert"; - -import { type DeployFunction } from "hardhat-deploy/types"; +import { deployScript, artifacts } from "@rocketh"; import { checkAddressValid, getTokenAddressFromConfig, getWalletAddressFromConfig } from "../../utils"; import { readFileSync } from "fs"; @@ -8,39 +7,34 @@ import { ConfigData } from "../../utils/types"; const contractName = "RewardMerkleDistributor"; -const deploy: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments } = hre; - - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - - assert(deployer, "Missing deployer account"); - - console.log(`Network: ${hre.network.name}`); - console.log(`Deployer: ${deployer}`); - - const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${hre.network.name}/config.json`).toString()); +export default deployScript( + async ({ namedAccounts, name, deploy }) => { + const { deployer } = namedAccounts; + assert(deployer, "Missing deployer account"); + console.log(`Network: ${name}\nDeployer: ${deployer}\nDeploying: ${contractName}`); - const accessManager = checkAddressValid(config.accessManager, "access manager"); + const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${name}/config.json`).toString()); - const rewardMerkleDistributorData = config.rewardMerkleDistributor; + const accessManager = checkAddressValid(config.accessManager, "access manager"); - const token = getTokenAddressFromConfig(rewardMerkleDistributorData.token, config); + const rewardMerkleDistributorData = config.rewardMerkleDistributor; - const expiredRewardsRecipient = getWalletAddressFromConfig( - rewardMerkleDistributorData.expiredRewardsRecipient, - config, - ); + const token = getTokenAddressFromConfig(rewardMerkleDistributorData.token, config); - const contract = await deploy(contractName, { - from: deployer, - args: [accessManager, token, expiredRewardsRecipient], - log: true, - skipIfAlreadyDeployed: false, - }); + const expiredRewardsRecipient = getWalletAddressFromConfig( + rewardMerkleDistributorData.expiredRewardsRecipient, + config, + ); - console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${contract.address}`); -}; + const contract = await deploy(contractName, { + account: deployer, + artifact: artifacts.RewardMerkleDistributor, + args: [accessManager, token, expiredRewardsRecipient], + }); -deploy.tags = [contractName]; -export default deploy; + console.log(`Deployed ${contractName}, network: ${name}, address: ${contract.address}`); + }, + { + tags: [contractName], + }, +); diff --git a/deploy/scripts/sprl/sPRL1.ts b/deploy/scripts/sprl/sPRL1.ts index 3b70243..021d706 100644 --- a/deploy/scripts/sprl/sPRL1.ts +++ b/deploy/scripts/sprl/sPRL1.ts @@ -1,6 +1,5 @@ import assert from "assert"; - -import { type DeployFunction } from "hardhat-deploy/types"; +import { deployScript, artifacts } from "@rocketh"; import { getTokenAddressFromConfig, getWalletAddressFromConfig, isAddressValid } from "../../utils"; import { readFileSync } from "fs"; @@ -8,34 +7,35 @@ import { ConfigData } from "../../utils/types"; const contractName = "sPRL1"; -const deploy: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments } = hre; - - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - - assert(deployer, "Missing deployer account"); - - console.log(`Network: ${hre.network.name}`); - console.log(`Deployer: ${deployer}`); - - const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${hre.network.name}/config.json`).toString()); - const accessManager = config.accessManager; - if (!isAddressValid(accessManager)) throw new Error("Invalid access manager address"); - - const sprl1Data = config.sprl1; - const underlying = getTokenAddressFromConfig(sprl1Data.underlying, config); - const feeReceiver = getWalletAddressFromConfig(sprl1Data.feeReceiver, config); - - const contract = await deploy(contractName, { - from: deployer, - args: [underlying, feeReceiver, accessManager, sprl1Data.startPenaltyPercentage, sprl1Data.timeLockDuration], - log: true, - skipIfAlreadyDeployed: false, - }); - - console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${contract.address}`); -}; - -deploy.tags = [contractName]; -export default deploy; +export default deployScript( + async ({ namedAccounts, name, deploy }) => { + const { deployer } = namedAccounts; + assert(deployer, "Missing deployer account"); + console.log(`Network: ${name}\nDeployer: ${deployer}\nDeploying: ${contractName}`); + + const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${name}/config.json`).toString()); + const accessManager = config.accessManager; + if (!isAddressValid(accessManager)) throw new Error("Invalid access manager address"); + + const sprl1Data = config.sprl1; + const underlying = getTokenAddressFromConfig(sprl1Data.underlying, config); + const feeReceiver = getWalletAddressFromConfig(sprl1Data.feeReceiver, config); + + const contract = await deploy(contractName, { + account: deployer, + artifact: artifacts.sPRL1, + args: [ + underlying, + feeReceiver, + accessManager, + BigInt(sprl1Data.startPenaltyPercentage), + BigInt(sprl1Data.timeLockDuration), + ], + }); + + console.log(`Deployed ${contractName}, network: ${name}, address: ${contract.address}`); + }, + { + tags: [contractName], + }, +); diff --git a/deploy/scripts/sprl/sPRL2.ts b/deploy/scripts/sprl/sPRL2.ts index 0352088..61cc6d0 100644 --- a/deploy/scripts/sprl/sPRL2.ts +++ b/deploy/scripts/sprl/sPRL2.ts @@ -1,6 +1,5 @@ import assert from "assert"; - -import { type DeployFunction } from "hardhat-deploy/types"; +import { deployScript, artifacts } from "@rocketh"; import { checkAddressValid, getTokenAddressFromConfig, getWalletAddressFromConfig, isAddressValid } from "../../utils"; import { readFileSync } from "fs"; @@ -8,60 +7,56 @@ import { ConfigData } from "../../utils/types"; const contractName = "sPRL2"; -const deploy: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments } = hre; - - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - - assert(deployer, "Missing deployer account"); - - console.log(`Network: ${hre.network.name}`); - console.log(`Deployer: ${deployer}`); - - const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${hre.network.name}/config.json`).toString()); - const accessManager = config.accessManager; - if (!isAddressValid(accessManager)) throw new Error("Invalid access manager address"); - - const sprl2Data = config.sprl2; - const prlToken = getTokenAddressFromConfig("prl", config); - const wethToken = getTokenAddressFromConfig("weth", config); - - const permit2 = checkAddressValid(sprl2Data.permit2, "Permit2"); - const feeReceiver = getWalletAddressFromConfig(sprl2Data.feeReceiver, config); - const balancerRouter = checkAddressValid(sprl2Data.balancerV3Router, "Balancer V3 Router"); - const balancerBPT = checkAddressValid(sprl2Data.balancerBPT, "Balancer BPT"); - const auraBoosterLite = checkAddressValid(sprl2Data.auraBoosterLite, "Aura Booster Lite"); - const auraRewardsPool = checkAddressValid(sprl2Data.auraRewardsPool, "Aura Rewards Pool"); - const auraBPT = checkAddressValid(sprl2Data.auraBPT, "Aura BPT"); - const rewardsTokens = sprl2Data.rewardsTokens.map((token) => checkAddressValid(token, "Reward Token")); - - const BPTConfigParams = { - balancerRouter: balancerRouter, - auraBoosterLite: auraBoosterLite, - auraRewardsPool: auraRewardsPool, - balancerBPT: balancerBPT, - prl: prlToken, - weth: wethToken, - rewardTokens: rewardsTokens, - permit2: permit2, - }; - const contract = await deploy(contractName, { - from: deployer, - args: [ - auraBPT, - feeReceiver, - accessManager, - sprl2Data.startPenaltyPercentage, - sprl2Data.timeLockDuration, - BPTConfigParams, - ], - log: true, - skipIfAlreadyDeployed: false, - }); - - console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${contract.address}`); -}; - -deploy.tags = [contractName]; -export default deploy; +export default deployScript( + async ({ namedAccounts, name, deploy }) => { + const { deployer } = namedAccounts; + assert(deployer, "Missing deployer account"); + console.log(`Network: ${name}\nDeployer: ${deployer}\nDeploying: ${contractName}`); + + const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${name}/config.json`).toString()); + const accessManager = config.accessManager; + if (!isAddressValid(accessManager)) throw new Error("Invalid access manager address"); + + const sprl2Data = config.sprl2; + const prlToken = getTokenAddressFromConfig("prl", config); + const wethToken = getTokenAddressFromConfig("weth", config); + + const permit2 = checkAddressValid(sprl2Data.permit2, "Permit2"); + const feeReceiver = getWalletAddressFromConfig(sprl2Data.feeReceiver, config); + const balancerRouter = checkAddressValid(sprl2Data.balancerV3Router, "Balancer V3 Router"); + const balancerBPT = checkAddressValid(sprl2Data.balancerBPT, "Balancer BPT"); + const auraBoosterLite = checkAddressValid(sprl2Data.auraBoosterLite, "Aura Booster Lite"); + const auraRewardsPool = checkAddressValid(sprl2Data.auraRewardsPool, "Aura Rewards Pool"); + const auraBPT = checkAddressValid(sprl2Data.auraBPT, "Aura BPT"); + const rewardsTokens = sprl2Data.rewardsTokens.map((token) => checkAddressValid(token, "Reward Token")); + + const BPTConfigParams = { + balancerRouter, + auraBoosterLite, + auraRewardsPool, + balancerBPT, + prl: prlToken, + weth: wethToken, + rewardTokens: rewardsTokens, + permit2, + }; + + const contract = await deploy(contractName, { + account: deployer, + artifact: artifacts.sPRL2, + args: [ + auraBPT, + feeReceiver, + accessManager, + BigInt(sprl2Data.startPenaltyPercentage), + BigInt(sprl2Data.timeLockDuration), + BPTConfigParams, + ], + }); + + console.log(`Deployed ${contractName}, network: ${name}, address: ${contract.address}`); + }, + { + tags: [contractName], + }, +); diff --git a/deploy/scripts/sprl/sPRL2V2.ts b/deploy/scripts/sprl/sPRL2V2.ts index 3353746..85ad348 100644 --- a/deploy/scripts/sprl/sPRL2V2.ts +++ b/deploy/scripts/sprl/sPRL2V2.ts @@ -1,6 +1,5 @@ import assert from "assert"; - -import { type DeployFunction } from "hardhat-deploy/types"; +import { deployScript, artifacts } from "@rocketh"; import { checkAddressValid, getTokenAddressFromConfig, getWalletAddressFromConfig, isAddressValid } from "../../utils"; import { readFileSync } from "fs"; @@ -8,52 +7,48 @@ import { ConfigData } from "../../utils/types"; const contractName = "sPRL2V2"; -const deploy: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments } = hre; - - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - - assert(deployer, "Missing deployer account"); - - console.log(`Network: ${hre.network.name}`); - console.log(`Deployer: ${deployer}`); - - const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${hre.network.name}/config.json`).toString()); - const accessManager = config.accessManager; - if (!isAddressValid(accessManager)) throw new Error("Invalid access manager address"); - - const sprl2v2Data = config.sprl2v2; - const prlToken = getTokenAddressFromConfig("prl", config); - const wethToken = getTokenAddressFromConfig("weth", config); - - const permit2 = checkAddressValid(sprl2v2Data.permit2, "Permit2"); - const feeReceiver = getWalletAddressFromConfig(sprl2v2Data.feeReceiver, config); - const balancerRouter = checkAddressValid(sprl2v2Data.balancerV3Router, "Balancer V3 Router"); - const balancerBPT = checkAddressValid(sprl2v2Data.balancerBPT, "Balancer BPT"); - - const BPTConfigParams = { - balancerRouter: balancerRouter, - balancerBPT: balancerBPT, - prl: prlToken, - weth: wethToken, - permit2: permit2, - }; - const contract = await deploy(contractName, { - from: deployer, - args: [ - feeReceiver, - accessManager, - sprl2v2Data.startPenaltyPercentage, - sprl2v2Data.timeLockDuration, - BPTConfigParams, - ], - log: true, - skipIfAlreadyDeployed: false, - }); - - console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${contract.address}`); -}; - -deploy.tags = [contractName]; -export default deploy; +export default deployScript( + async ({ namedAccounts, name, deploy }) => { + const { deployer } = namedAccounts; + assert(deployer, "Missing deployer account"); + console.log(`Network: ${name}\nDeployer: ${deployer}\nDeploying: ${contractName}`); + + const config: ConfigData = JSON.parse(readFileSync(`./deploy/config/${name}/config.json`).toString()); + const accessManager = config.accessManager; + if (!isAddressValid(accessManager)) throw new Error("Invalid access manager address"); + + const sprl2v2Data = config.sprl2v2; + const prlToken = getTokenAddressFromConfig("prl", config); + const wethToken = getTokenAddressFromConfig("weth", config); + + const permit2 = checkAddressValid(sprl2v2Data.permit2, "Permit2"); + const feeReceiver = getWalletAddressFromConfig(sprl2v2Data.feeReceiver, config); + const balancerRouter = checkAddressValid(sprl2v2Data.balancerV3Router, "Balancer V3 Router"); + const balancerBPT = checkAddressValid(sprl2v2Data.balancerBPT, "Balancer BPT"); + + const BPTConfigParams = { + balancerRouter, + balancerBPT, + prl: prlToken, + weth: wethToken, + permit2, + }; + + const contract = await deploy(contractName, { + account: deployer, + artifact: artifacts.sPRL2V2, + args: [ + feeReceiver, + accessManager, + BigInt(sprl2v2Data.startPenaltyPercentage), + BigInt(sprl2v2Data.timeLockDuration), + BPTConfigParams, + ], + }); + + console.log(`Deployed ${contractName}, network: ${name}, address: ${contract.address}`); + }, + { + tags: [contractName], + }, +); diff --git a/deploy/utils/index.ts b/deploy/utils/index.ts index 3e01454..1cd7793 100644 --- a/deploy/utils/index.ts +++ b/deploy/utils/index.ts @@ -1,4 +1,4 @@ -import { ethers } from "ethers"; +import { isAddress, zeroAddress } from "viem"; import { Address, ConfigData } from "./types"; import { EndpointId } from "@layerzerolabs/lz-definitions"; @@ -33,5 +33,5 @@ export const checkAddressValid = (address: Address, label: string) => { }; export const isAddressValid = (address: string) => { - return ethers.utils.isAddress(address) && ethers.constants.AddressZero !== address; + return isAddress(address) && zeroAddress !== address; }; diff --git a/deploy/utils/types.ts b/deploy/utils/types.ts index 6c1bfff..83a22bf 100644 --- a/deploy/utils/types.ts +++ b/deploy/utils/types.ts @@ -1,7 +1,7 @@ -import type { BigNumberish } from "ethers"; - export type Address = `0x${string}`; +export type Numeric = string | number | bigint; + export type ConfigData = { isMainChainFeeDistributor: boolean; accessManager: Address; @@ -26,13 +26,13 @@ export type ConfigData = { sprl1: { underlying: Address; feeReceiver: string; - startPenaltyPercentage: BigNumberish; + startPenaltyPercentage: Numeric; timeLockDuration: number; }; sprl2: { permit2: Address; feeReceiver: string; - startPenaltyPercentage: BigNumberish; + startPenaltyPercentage: Numeric; timeLockDuration: number; balancerV3Router: Address; auraRewardsPool: Address; @@ -44,7 +44,7 @@ export type ConfigData = { sprl2v2: { permit2: Address; feeReceiver: string; - startPenaltyPercentage: BigNumberish; + startPenaltyPercentage: Numeric; timeLockDuration: number; balancerV3Router: Address; balancerBPT: Address; diff --git a/hardhat.config.ts b/hardhat.config.ts index bc4d64f..faa6bc8 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,35 +1,30 @@ import "dotenv/config"; -import "hardhat-deploy"; +import HardhatViem from "@nomicfoundation/hardhat-viem"; +import HardhatDeploy from "hardhat-deploy"; -import { HardhatUserConfig, HttpNetworkAccountsUserConfig } from "hardhat/types"; +import { HardhatUserConfig } from "hardhat/types/config"; import { getRpcURL } from "./utils/getRpcURL"; -import { getVerifyConfig } from "./utils/getVerifyConfig"; -const MNEMONIC = process.env.MNEMONIC; - -// If you prefer to be authenticated using a private key, set a PRIVATE_KEY environment variable const PRIVATE_KEY = process.env.PRIVATE_KEY; - -const accounts: HttpNetworkAccountsUserConfig | undefined = MNEMONIC - ? { mnemonic: MNEMONIC } - : PRIVATE_KEY - ? [PRIVATE_KEY] - : undefined; - -if (accounts == null) { - console.warn( - "Could not find MNEMONIC or PRIVATE_KEY environment variables. It will not be possible to execute transactions in your example.", - ); -} +if (!PRIVATE_KEY) throw new Error("PRIVATE_KEY is not set"); +const accounts = [PRIVATE_KEY]; const config: HardhatUserConfig = { + plugins: [HardhatViem, HardhatDeploy], + // Foundry tests live in `test/`; keep Hardhat from compiling them by pointing its + // Solidity sources at `contracts/` and its test dir at an unused path. + paths: { + sources: "contracts", + tests: "hardhat-test", + }, solidity: { compilers: [ { version: "0.8.25", settings: { + evmVersion: "cancun", optimizer: { enabled: true, runs: 10_000, @@ -40,41 +35,36 @@ const config: HardhatUserConfig = { }, networks: { mainnet: { - accounts, + type: "http", url: getRpcURL("mainnet"), - verify: getVerifyConfig("mainnet"), + accounts, }, sepolia: { - accounts, + type: "http", url: getRpcURL("sepolia"), - verify: getVerifyConfig("sepolia"), + accounts, }, arbiSepolia: { - accounts, + type: "http", url: getRpcURL("arbiSepolia"), - verify: getVerifyConfig("arbiSepolia"), + accounts, }, polygon: { - accounts, + type: "http", url: getRpcURL("polygon"), - verify: getVerifyConfig("polygon"), + accounts, }, base: { + type: "http", url: getRpcURL("base"), - verify: getVerifyConfig("base"), accounts, }, sonic: { + type: "http", url: getRpcURL("sonic"), - verify: getVerifyConfig("sonic"), accounts, }, }, - namedAccounts: { - deployer: { - default: 0, - }, - }, }; export default config; diff --git a/package.json b/package.json index 8a0776a..46ac4b7 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,13 @@ "description": "Parallel Tokenomics", "version": "1.0.0", "private": false, + "type": "module", "scripts": { "compile": "forge build && hardhat compile", - "deploy": "npx hardhat deploy", - "clean": "rm -rf cache out", + "deploy": "hardhat deploy", + "clean": "rm -rf cache out generated", "build": "forge build", + "size": "forge build --sizes", "lint": "bun run lint:sol && bun run prettier:check", "lint:sol": "forge fmt --check && bun solhint \"{script,contracts,test}/**/*.sol\"", "prettier:check": "prettier --check \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", @@ -17,27 +19,40 @@ "test:coverage": "forge coverage --ir-minimum", "test:coverage:report": "forge coverage --ir-minimum --report lcov && lcov --remove lcov.info -o lcov.info \"test/*\" && genhtml lcov.info -o coverage", "prepare": "husky", - "verify": "npx hardhat etherscan-verify --sleep" + "verify": "rocketh-verify etherscan" }, "dependencies": { "@layerzerolabs/lz-evm-oapp-v2": "^3.0.14", - "@openzeppelin/contracts": "^5.1.0", - "ts-node": "^10.9.2" + "@openzeppelin/contracts": "^5.1.0" }, "devDependencies": { "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@layerzerolabs/lz-definitions": "^3.0.36", - "@nomicfoundation/hardhat-foundry": "^1.1.2", + "@nomicfoundation/hardhat-viem": "^3.0.0", + "@rocketh/deploy": "^0.19.8", + "@rocketh/doc": "^0.19.7", + "@rocketh/export": "^0.19.8", + "@rocketh/proxy": "^0.19.14", + "@rocketh/read-execute": "^0.19.3", + "@rocketh/signer": "^0.19.3", + "@rocketh/verifier": "^0.19.7", + "@rocketh/viem": "^0.19.3", "@types/fs-extra": "^11.0.4", - "dotenv": "^16.4.5", + "@types/node": "^24.3.0", + "dotenv": "^17.2.1", "forge-std": "github:foundry-rs/forge-std#v1.9.5", - "fs-extra": "^11.2.0", - "hardhat-contract-sizer": "^2.10.0", - "hardhat-deploy": "^0.14.0", + "fs-extra": "^11.3.0", + "hardhat": "^3.0.3", + "hardhat-deploy": "^2.0.0-next.40", "husky": "^9.1.6", "murky": "github:dmfxyz/murky", - "prettier": "^3.3.3", - "solhint": "^5.0.3" + "prettier": "^3.5.3", + "rocketh": "^0.19.7", + "solhint": "^5.0.3", + "ts-node": "^10.9.2", + "tsx": "^4.20.5", + "typescript": "^5.9.2", + "viem": "^2.37.1" } } diff --git a/remappings.txt b/remappings.txt index 422f91d..8a15695 100644 --- a/remappings.txt +++ b/remappings.txt @@ -2,3 +2,4 @@ @forge-std/=node_modules/forge-std/src/ @layerzerolabs/=node_modules/@layerzerolabs/ @murky/=node_modules/murky/src/ +contracts/=contracts/ diff --git a/rocketh.ts b/rocketh.ts new file mode 100644 index 0000000..30ea11c --- /dev/null +++ b/rocketh.ts @@ -0,0 +1,38 @@ +// ------------------------------------------------------------------------------------------------ +// Typed Config +// ------------------------------------------------------------------------------------------------ +import type { UserConfig } from "rocketh/types"; + +export const config = { + accounts: { + deployer: { + default: 0, + }, + }, + data: {}, +} as const satisfies UserConfig; + +// ------------------------------------------------------------------------------------------------ +// Extensions available to the deploy scripts through the environment object. +// ------------------------------------------------------------------------------------------------ +import * as deployExtension from "@rocketh/deploy"; // provides a deploy function +import * as readExecuteExtension from "@rocketh/read-execute"; // provides read/execute functions +import * as deployProxyExtension from "@rocketh/proxy"; // provides deployViaProxy for proxy based contracts +import * as viemExtension from "@rocketh/viem"; // provides viem handles to clients and contracts +const extensions = { + ...deployExtension, + ...readExecuteExtension, + ...deployProxyExtension, + ...viemExtension, +}; + +// ------------------------------------------------------------------------------------------------ +// Re-export the generated artifacts so they are available from the @rocketh alias. +import * as artifacts from "./generated/artifacts"; +export { artifacts }; + +// ------------------------------------------------------------------------------------------------ +// Create the deployScript helper bound to our extensions and typed config. +import { setupDeployScripts } from "rocketh"; +const { deployScript } = setupDeployScripts(extensions); +export { deployScript }; diff --git a/tsconfig.json b/tsconfig.json index 7fc6a13..68ba309 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,21 @@ { "exclude": ["node_modules"], - "include": ["./hardhat.config.ts", "./scripts", "./deploy", "./test", "./tasks"], + "include": ["./hardhat.config.ts", "./rocketh.ts", "./scripts", "./deploy", "./utils"], "compilerOptions": { - "target": "es2020", - "module": "commonjs", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, + "lib": ["es2023"], + "module": "esnext", + "moduleResolution": "bundler", + "target": "es2022", "strict": true, + "esModuleInterop": true, "skipLibCheck": true, - "resolveJsonModule": true + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "outDir": "dist", + "rootDir": ".", + "paths": { + "@rocketh": ["./rocketh.ts"], + "@generated/*": ["./generated/*"] + } } } diff --git a/utils/getVerifyConfig.ts b/utils/getVerifyConfig.ts deleted file mode 100644 index 558c7f4..0000000 --- a/utils/getVerifyConfig.ts +++ /dev/null @@ -1,86 +0,0 @@ -type VerifyConfig = { - etherscan: { - apiUrl: string; - apiKey: string; - }; -}; - -export const getVerifyConfig = (network: string): VerifyConfig => { - switch (network) { - case "mainnet": { - if (!process.env.MAINNET_ETHERSCAN_API_KEY) throw new Error("MAINNET_ETHERSCAN_API_KEY is not set"); - return { - etherscan: { - apiUrl: "https://api.etherscan.io", - apiKey: process.env.MAINNET_ETHERSCAN_API_KEY, - }, - }; - } - case "sepolia": { - if (!process.env.MAINNET_ETHERSCAN_API_KEY) throw new Error("MAINNET_ETHERSCAN_API_KEY is not set"); - return { - etherscan: { - apiUrl: "https://api-sepolia.etherscan.io", - apiKey: process.env.MAINNET_ETHERSCAN_API_KEY, - }, - }; - } - case "polygon": { - if (!process.env.POLYGON_ETHERSCAN_API_KEY) throw new Error("POLYGON_ETHERSCAN_API_KEY is not set"); - return { - etherscan: { - apiUrl: "https://api.polygonscan.com", - apiKey: process.env.POLYGON_ETHERSCAN_API_KEY, - }, - }; - } - case "amoy": { - if (!process.env.POLYGON_ETHERSCAN_API_KEY) throw new Error("POLYGON_ETHERSCAN_API_KEY is not set"); - return { - etherscan: { - apiUrl: "https://api-amoy.polygonscan.com", - apiKey: process.env.POLYGON_ETHERSCAN_API_KEY, - }, - }; - } - case "arbiSepolia": { - if (!process.env.ARBITRUM_ETHERSCAN_API_KEY) throw new Error("ARBITRUM_ETHERSCAN_API_KEY is not set"); - return { - etherscan: { - apiUrl: "https://api-sepolia.arbiscan.io", - apiKey: process.env.ARBITRUM_ETHERSCAN_API_KEY, - }, - }; - } - case "fantom": { - if (!process.env.FANTOM_ETHERSCAN_API_KEY) throw new Error("FANTOM_ETHERSCAN_API_KEY is not set"); - return { - etherscan: { - apiUrl: "https://api.ftmscan.com", - apiKey: process.env.FANTOM_ETHERSCAN_API_KEY, - }, - }; - } - case "base": { - if (!process.env.BASE_ETHERSCAN_API_KEY) throw new Error("BASE_ETHERSCAN_API_KEY is not set"); - return { - etherscan: { - apiUrl: "https://api.basescan.org", - apiKey: process.env.BASE_ETHERSCAN_API_KEY, - }, - }; - } - case "sonic": { - if (!process.env.SONIC_ETHERSCAN_API_KEY) throw new Error("SONIC_ETHERSCAN_API_KEY is not set"); - return { - etherscan: { - apiUrl: "https://api.sonicscan.org", - apiKey: process.env.SONIC_ETHERSCAN_API_KEY, - }, - }; - } - default: { - throw new Error(`${network} Network Verify not configured`); - } - } -};