Skip to content

Commit

Permalink
Merge pull request #50 from zama-ai/etherscanVerify
Browse files Browse the repository at this point in the history
chore: use release fhevm-core-contracts, add Etherscan verification
  • Loading branch information
jatZama authored Dec 6, 2024
2 parents 656d9f8 + 5acb2bb commit f9505a6
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 31 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -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"
export SEPOLIA_RPC_URL="https://sepolia.infura.io/v3/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export ETHERSCAN_API_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 is logged when you run the `pnpm deploy-sepolia` deployment script.

### Syntax Highlighting

If you use VSCode, you can get Solidity syntax highlighting with the
Expand Down
3 changes: 2 additions & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
}
}
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions tasks/etherscanVerify.ts
Original file line number Diff line number Diff line change
@@ -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 <network>");
}
}
});
21 changes: 5 additions & 16 deletions tasks/mint.ts → tasks/mintMyConfidentialERC20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
15 changes: 11 additions & 4 deletions test/confidentialERC20/ConfidentialERC20.FHEGas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect } from "chai";
import { network } from "hardhat";

import { getFHEGasFromTxReceipt } from "../coprocessorUtils";
import { createInstance } from "../instance";
Expand Down Expand Up @@ -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);
});
Expand Down Expand Up @@ -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);
});
Expand Down

0 comments on commit f9505a6

Please sign in to comment.