diff --git a/.env.example b/.env.example index 37f0cdc..df1dbe0 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ export MNEMONIC="exchange vintage ocean narrow danger return culture ignore trim solve clock hidden buddy wise emotion" -export SEPOLIA_RPC_URL="https://sepolia.infura.io/v3/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ No newline at end of file +export SEPOLIA_RPC_URL="https://sepolia.infura.io/v3/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +export ETHERSCAN_API_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \ No newline at end of file diff --git a/README.md b/README.md index 8308819..53eefcf 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ Finally, a new fhevm-specific feature is available in mocked mode: the `debug.de ### Non-mocked mode - Sepolia -To run your test on a real fhevm node, you can use the coprocessor deployed on the Sepolia test network. To do this, ensure you are using a valid value `SEPOLIA_RPC_URL` in your `.env` file. You can get free Sepolia RPC URLs by creating an account on services such as [Infura](https://www.infura.io/) or [Alchemy](https://www.alchemy.com/). Then you should use the following command: +To run your test on a real fhevm node, you can use the coprocessor deployed on the Sepolia test network. To do this, ensure you are using a valid value `SEPOLIA_RPC_URL` in your `.env` file. You can get free Sepolia RPC URLs by creating an account on services such as [Infura](https://www.infura.io/) or [Alchemy](https://www.alchemy.com/). Then you can use the following command: ```bash npx hardhat test [PATH_TO_YOUR_TEST] --network sepolia @@ -179,6 +179,31 @@ This will let you add them to the Metamask app, to easily fund them from your pe If you don't own already Sepolia test tokens, you can for example use a free faucet such as [https://sepolia-faucet.pk910.de/](https://sepolia-faucet.pk910.de/). +Another faster way to test the coprocessor on Sepolia is to simply run the following command: +``` +pnpm deploy-sepolia +``` +This would automatically deploy an instance of the `MyConfidentialERC20` example contract on Sepolia. You could then use this other command to mint some amount of confidential tokens: +``` +pnpm mint-sepolia +``` + +### Etherscan verification + +If you are using a real instance of the fhEVM, you can verify your deployed contracts on the Etherscan explorer. +You first need to set the `ETHERSCAN_API_KEY` variable in the `.env` file to a valid value. You can get such an API key for free by creating an account on the [Etherscan website](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics). + +Then, simply use the `verify-deployed` hardhat task, via this command: +``` +npx hardhat verify-deployed --address [ADDRESS_CONTRACT_TO_VERIFY] --contract [FULL_CONTRACT_PATH] --args "[CONSTRUCTOR_ARGUMENTS_COMMA_SEPARATED]" --network [NETWORK_NAME] +``` +As a concrete example, to verify the deployed `MyConfidentialERC20` from previous section, you can use: +``` +npx hardhat verify-deployed --address [CONFIDENTIAL_ERC20_ADDRESS] --contract contracts/MyConfidentialERC20.sol:MyConfidentialERC20 --args "Naraggara,NARA" --network sepolia +``` + +Note that you should replace the address placeholder [CONFIDENTIAL_ERC20_ADDRESS] by the concrete address that was logged when you launched previously when you used the `pnpm deploy-sepolia` deployment script. + ### Syntax Highlighting If you use VSCode, you can get Solidity syntax highlighting with the diff --git a/hardhat.config.ts b/hardhat.config.ts index 361fc67..a0ad205 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -11,7 +11,8 @@ import { resolve } from "path"; import CustomProvider from "./CustomProvider"; // Adjust the import path as needed import "./tasks/accounts"; -import "./tasks/mint"; +import "./tasks/etherscanVerify"; +import "./tasks/mintMyConfidentialERC20"; import { setCodeMocked } from "./test/mockedSetup"; extendProvider(async (provider) => { diff --git a/package.json b/package.json index 2c2f18c..c91f7c2 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,8 @@ "ethers": "^6.8.0", "extra-bigint": "^1.1.18", "fhevm": "^0.6.0", - "fhevm-contracts": "0.2.0", - "fhevm-core-contracts": "0.6.0-5", + "fhevm-contracts": "^0.2.0", + "fhevm-core-contracts": "^0.6.0", "fhevmjs": "^0.6.0", "fs-extra": "^10.1.0", "globals": "^15.9.0", @@ -92,6 +92,7 @@ "typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain", "test": "hardhat test --network hardhat", "coverage": "hardhat coverage", - "deploy-sepolia": "hardhat deploy --network sepolia" + "deploy-sepolia": "hardhat deploy --network sepolia", + "mint-sepolia": "hardhat mint --amount 42 --network sepolia" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce5c741..cdd7cb1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,11 +84,11 @@ devDependencies: specifier: ^0.6.0 version: 0.6.0 fhevm-contracts: - specifier: 0.2.0 + specifier: ^0.2.0 version: 0.2.0 fhevm-core-contracts: - specifier: 0.6.0-5 - version: 0.6.0-5 + specifier: ^0.6.0 + version: 0.6.0 fhevmjs: specifier: ^0.6.0 version: 0.6.0 @@ -3192,8 +3192,8 @@ packages: - supports-color dev: true - /fhevm-core-contracts@0.6.0-5: - resolution: {integrity: sha512-o3ZInT2RA+uldzIMM2fHMjoIhI2466psLGI4tU4BJYOeV6wgJpw+T+m6/tN3WSf3nE0bU4yLrD1S/lM3Wk24vg==} + /fhevm-core-contracts@0.6.0: + resolution: {integrity: sha512-j62NNTITZOqHTl0sw4EjBV9YJTPDMnBzzYMoLVtoeHUMibZj6jgkBofEltjVmpKRjQr0O5q6xf+Xw9fDOlFQSg==} dev: true /fhevm@0.6.0: diff --git a/tasks/etherscanVerify.ts b/tasks/etherscanVerify.ts new file mode 100644 index 0000000..55e60b4 --- /dev/null +++ b/tasks/etherscanVerify.ts @@ -0,0 +1,65 @@ +import { task } from "hardhat/config"; + +task("verify-deployed", "Verifies an already deployed contract on Etherscan") + .addParam("address", "The contract's address") + .addParam("contract", "Full contract path (e.g., 'contracts/MyConfidentialERC20.sol:MyConfidentialERC20.sol')") + .addParam("args", "Constructor arguments as comma-separated values", "") + .setAction(async (taskArgs, hre) => { + if (hre.network.name === "hardhat") { + throw Error("Etherscan verification is not possilbe in mocked mode, choose another network"); + } + const { address, contract, args } = taskArgs; + + console.info("\nStarting verification for deployed contract..."); + console.info("Contract:", contract); + console.info("Address:", address); + + try { + // Parse constructor arguments + const constructorArgs = args + ? args.split(",").map((arg) => { + const trimmed = arg.trim(); + // Try to parse as JSON + try { + return JSON.parse(trimmed); + } catch { + // If it's a number + if (!isNaN(trimmed)) { + return Number(trimmed); + } + // If it's a boolean + if (trimmed.toLowerCase() === "true") return true; + if (trimmed.toLowerCase() === "false") return false; + // Otherwise return as string + return trimmed; + } + }) + : []; + + console.info("Constructor Arguments:", constructorArgs); + + // Prepare verification arguments + const verificationArgs = { + address: address, + contract: contract, + constructorArguments: constructorArgs, + }; + + console.info("\nSubmitting verification request..."); + await hre.run("verify:verify", verificationArgs); + + console.info("\n✅ Contract verification completed successfully!"); + } catch (error) { + if (error.message.includes("Already Verified")) { + console.info("\n✓ Contract is already verified!"); + } else { + console.error("\n❌ Verification failed:", error.message); + console.info("\nTo verify your contract, use the following format:"); + console.info("\nnpx hardhat verify-deployed \\"); + console.info(" --address", address, "\\"); + console.info(" --contract", contract, "\\"); + console.info(' --args "arg1,arg2,arg3" \\'); + console.info(" --network "); + } + } + }); diff --git a/tasks/mint.ts b/tasks/mintMyConfidentialERC20.ts similarity index 51% rename from tasks/mint.ts rename to tasks/mintMyConfidentialERC20.ts index 5c505f6..7008271 100644 --- a/tasks/mint.ts +++ b/tasks/mintMyConfidentialERC20.ts @@ -4,26 +4,15 @@ import { HardhatRuntimeEnvironment } from "hardhat/types"; import { MyConfidentialERC20 } from "../types"; -task("task:deployConfidentialERC20").setAction(async function (taskArguments: TaskArguments, { ethers }) { - const signers = await ethers.getSigners(); - const erc20Factory = await ethers.getContractFactory("MyConfidentialERC20"); - const erc20 = await erc20Factory.connect(signers[0]).deploy("Naraggara", "NARA"); - await erc20.waitForDeployment(); - console.log("ConfidentialERC20 deployed to: ", await erc20.getAddress()); -}); - -task("task:mint") - .addParam("mint", "Tokens to mint") +task("mint") + .addParam("amount", "Tokens to mint") .setAction(async function (taskArguments: TaskArguments, hre: HardhatRuntimeEnvironment) { const { ethers, deployments } = hre; const ERC20 = await deployments.get("MyConfidentialERC20"); - const signers = await ethers.getSigners(); - const erc20 = (await ethers.getContractAt("MyConfidentialERC20", ERC20.address)) as MyConfidentialERC20; - - const tx = await erc20.connect(signers[0]).mint(+taskArguments.mint); + const tx = await erc20.connect(signers[0]).mint(+taskArguments.amount); const rcpt = await tx.wait(); - console.log("Mint tx hash: ", rcpt.hash); - console.log("Mint done: ", taskArguments.mint, "tokens were minted succesfully"); + console.info("Mint tx hash: ", rcpt!.hash); + console.info("Mint done: ", taskArguments.amount, "tokens were minted succesfully"); }); diff --git a/test/confidentialERC20/ConfidentialERC20.FHEGas.ts b/test/confidentialERC20/ConfidentialERC20.FHEGas.ts index a80f10a..097d799 100644 --- a/test/confidentialERC20/ConfidentialERC20.FHEGas.ts +++ b/test/confidentialERC20/ConfidentialERC20.FHEGas.ts @@ -1,4 +1,5 @@ import { expect } from "chai"; +import { network } from "hardhat"; import { getFHEGasFromTxReceipt } from "../coprocessorUtils"; import { createInstance } from "../instance"; @@ -33,8 +34,11 @@ describe("ConfidentialERC20:FHEGas", function () { ); const t2 = await tx.wait(); expect(t2?.status).to.eq(1); - const FHEGasConsumedTransfer = getFHEGasFromTxReceipt(t2); - console.log("FHEGas Consumed during transfer", FHEGasConsumedTransfer); + if (network.name === "hardhat") { + // `getFHEGasFromTxReceipt` function only works in mocked mode but gives same exact FHEGas consumed than on the real fhEVM + const FHEGasConsumedTransfer = getFHEGasFromTxReceipt(t2); + console.log("FHEGas Consumed during transfer", FHEGasConsumedTransfer); + } // contrarily to FHEGas, native gas in mocked mode slightly differs from the real gas consumption on fhevm (underestimated by ~20%) console.log("Native Gas Consumed during transfer", t2.gasUsed); }); @@ -64,8 +68,11 @@ describe("ConfidentialERC20:FHEGas", function () { encryptedTransferAmount2.inputProof, ); const t3 = await tx3.wait(); - const FHEGasConsumedTransferFrom = getFHEGasFromTxReceipt(t3); - console.log("FHEGas Consumed during transferFrom", FHEGasConsumedTransferFrom); + if (network.name === "hardhat") { + // `getFHEGasFromTxReceipt` function only works in mocked mode but gives same exact FHEGas consumed than on the real fhEVM + const FHEGasConsumedTransferFrom = getFHEGasFromTxReceipt(t3); + console.log("FHEGas Consumed during transfer", FHEGasConsumedTransferFrom); + } // contrarily to FHEGas, native gas in mocked mode slightly differs from the real gas consumption on fhevm (underestimated by ~20%) console.log("Native Gas Consumed during transfer", t3.gasUsed); });