diff --git a/.github/workflows/solidity.yaml b/.github/workflows/solidity.yaml index 7586edb1a..5765f374a 100644 --- a/.github/workflows/solidity.yaml +++ b/.github/workflows/solidity.yaml @@ -172,8 +172,8 @@ jobs: - name: Deploy contracts env: - ACCOUNTS_PRIVATE_KEYS: ${{ secrets.TESTNET_ETH_CONTRACT_OWNER_PRIVATE_KEY }} - CHAIN_API_URL: ${{ secrets.SEPOLIA_CHAIN_API_URL }} + SEPOLIA_PRIVATE_KEY: ${{ secrets.TESTNET_ETH_CONTRACT_OWNER_PRIVATE_KEY }} + SEPOLIA_RPC_URL: ${{ secrets.SEPOLIA_CHAIN_API_URL }} ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }} run: | pnpm run deploy --network ${{ github.event.inputs.environment }} diff --git a/.gitignore b/.gitignore index 629d01034..022d1e746 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,7 @@ tmp/ # Environment configuration files .envrc +.env .env.* !.env.example +!.env.ci.example diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 307077ecd..da6eba6e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -242,6 +242,9 @@ importers: chai: specifier: ^4.3.10 version: 4.3.10 + dotenv-safer: + specifier: ^1.0.0 + version: 1.0.0(dotenv@8.6.0) eslint: specifier: ^8.54.0 version: 8.54.0 @@ -10703,6 +10706,14 @@ packages: /dotenv-expand@5.1.0: resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + /dotenv-safer@1.0.0(dotenv@8.6.0): + resolution: {integrity: sha512-QzRM3UrtdWn9iYJ4rgd1zPwSb1PjYVTpkWk0KVCdwZkatbonwGDuuKkw+lwXGTVAMGN/uVDs0HfOgxrBqlwElQ==} + peerDependencies: + dotenv: '>= 8.2.0' + dependencies: + dotenv: 8.6.0 + dev: true + /dotenv@7.0.0: resolution: {integrity: sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==} engines: {node: '>=6'} diff --git a/solidity/.env b/solidity/.env deleted file mode 100644 index aea8b0315..000000000 --- a/solidity/.env +++ /dev/null @@ -1,4 +0,0 @@ -CHAIN_API_URL=${CHAIN_API_URL} -ACCOUNTS_PRIVATE_KEYS=${ACCOUNTS_PRIVATE_KEYS} -ETHERSCAN_API_KEY=${ETHERSCAN_API_KEY} -COINMARKETCAP_API_KEY=${COINMARKETCAP_API_KEY} diff --git a/solidity/.env.ci.example b/solidity/.env.ci.example new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/solidity/.env.ci.example @@ -0,0 +1 @@ + diff --git a/solidity/.env.example b/solidity/.env.example index 9cba5dee7..ebcf14fb0 100644 --- a/solidity/.env.example +++ b/solidity/.env.example @@ -1,4 +1,9 @@ -CHAIN_API_URL= -ACCOUNTS_PRIVATE_KEYS= +MAINNET_RPC_URL= +MAINNET_PRIVATE_KEY= + +SEPOLIA_RPC_URL= +SEPOLIA_PRIVATE_KEY= + ETHERSCAN_API_KEY= + COINMARKETCAP_API_KEY= diff --git a/solidity/README.md b/solidity/README.md index 177801d40..e842e5564 100644 --- a/solidity/README.md +++ b/solidity/README.md @@ -28,10 +28,12 @@ $ pnpm test To run the integration tests follow these steps: -- Run the Hardhat Node locally forking Mainnet at block `19680873`: +- Define `MAINNET_RPC_URL` environment variable pointing to Ethereum Mainnet RPC URL. + +- Run the Hardhat Node locally forking Mainnet: ``` -$ npx hardhat node --no-deploy --fork https://eth-mainnet.g.alchemy.com/v2/ --fork-block-number 19680873 +$ pnpm run node:forking ``` - Once the local node is running you can execute the integration tests: diff --git a/solidity/hardhat.config.ts b/solidity/hardhat.config.ts index ac6a1492e..71cddc5e2 100644 --- a/solidity/hardhat.config.ts +++ b/solidity/hardhat.config.ts @@ -5,6 +5,28 @@ import "hardhat-contract-sizer" import "hardhat-deploy" import "solidity-docgen" import "@keep-network/hardhat-helpers" +import dotenv from "dotenv-safer" + +dotenv.config({ + allowEmptyValues: true, + example: process.env.CI ? ".env.ci.example" : ".env.example", +}) + +const MAINNET_RPC_URL = process.env.MAINNET_RPC_URL ?? "" + +const MAINNET_PRIVATE_KEY = process.env.MAINNET_PRIVATE_KEY + ? [process.env.MAINNET_PRIVATE_KEY] + : [] + +const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL ?? "" + +const SEPOLIA_PRIVATE_KEY = process.env.SEPOLIA_PRIVATE_KEY + ? [process.env.SEPOLIA_PRIVATE_KEY] + : [] + +const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY ?? "" + +const COINMARKETCAP_API_KEY = process.env.COINMARKETCAP_API_KEY ?? "" const config: HardhatUserConfig = { solidity: { @@ -26,23 +48,25 @@ const config: HardhatUserConfig = { }, networks: { + hardhat: { + forking: + process.env.FORKING === "true" + ? { url: MAINNET_RPC_URL, blockNumber: 19680873 } + : undefined, + }, integration: { url: "http://localhost:8545", }, sepolia: { - url: process.env.CHAIN_API_URL || "", + url: SEPOLIA_RPC_URL, chainId: 11155111, - accounts: process.env.ACCOUNTS_PRIVATE_KEYS - ? process.env.ACCOUNTS_PRIVATE_KEYS.split(",") - : undefined, + accounts: SEPOLIA_PRIVATE_KEY, tags: ["etherscan"], }, mainnet: { - url: process.env.CHAIN_API_URL || "", + url: MAINNET_RPC_URL, chainId: 1, - accounts: process.env.ACCOUNTS_PRIVATE_KEYS - ? process.env.ACCOUNTS_PRIVATE_KEYS.split(",") - : undefined, + accounts: MAINNET_PRIVATE_KEY, tags: ["etherscan"], }, }, @@ -57,8 +81,8 @@ const config: HardhatUserConfig = { etherscan: { apiKey: { - sepolia: process.env.ETHERSCAN_API_KEY, - mainnet: process.env.ETHERSCAN_API_KEY, + sepolia: ETHERSCAN_API_KEY, + mainnet: ETHERSCAN_API_KEY, }, }, @@ -98,7 +122,7 @@ const config: HardhatUserConfig = { gasReporter: { enabled: true, - coinmarketcap: process.env.COINMARKETCAP_API_KEY, + coinmarketcap: COINMARKETCAP_API_KEY, }, typechain: { diff --git a/solidity/package.json b/solidity/package.json index 8cb5969e1..b34e4bbc9 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -15,9 +15,10 @@ ], "scripts": { "clean": "hardhat clean && rm -rf cache/ export/ gen/ export.json", - "build": "hardhat compile", - "deploy": "hardhat deploy --export export.json", - "docs": "hardhat docgen", + "prepare:env": "cp -n .env.example .env || true", + "build": "npm run prepare:env && hardhat compile", + "deploy": "npm run prepare:env && hardhat deploy --export export.json", + "docs": "npm run prepare:env && hardhat docgen", "format": "npm run lint:js && npm run lint:sol && npm run lint:config", "format:fix": "npm run lint:js:fix && npm run lint:sol:fix && npm run lint:config:fix", "lint:js": "eslint .", @@ -26,7 +27,8 @@ "lint:sol:fix": "solhint 'contracts/**/*.sol' --fix && prettier --write 'contracts/**/*.sol'", "lint:config": "prettier --check '**/*.@(json)'", "lint:config:fix": "prettier --write '**/*.@(json)'", - "test": "hardhat test ./test/*.test.ts", + "node:forking": "FORKING=true hardhat node --no-deploy", + "test": "npm run prepare:env && hardhat test ./test/*.test.ts", "test:integration": "hardhat test --deploy-fixture ./test/integration/*.test.ts --network integration" }, "devDependencies": { @@ -45,6 +47,7 @@ "@types/mocha": "^10.0.6", "@types/node": "^20.9.4", "chai": "^4.3.10", + "dotenv-safer": "^1.0.0", "eslint": "^8.54.0", "ethers": "^6.8.1", "hardhat": "^2.19.1", diff --git a/solidity/types/dotenv-safer.d.ts b/solidity/types/dotenv-safer.d.ts new file mode 100644 index 000000000..2d2867330 --- /dev/null +++ b/solidity/types/dotenv-safer.d.ts @@ -0,0 +1,4 @@ +declare module "dotenv-safer" { + // eslint-disable-next-line import/prefer-default-export + export function config(options = {}) +}