This example demonstrates how to a simple contract with conditions using FHEVM, in comparison to a simple counter.
To run this example correctly, make sure the files are placed in the following directories:
.sol file → <your-project-root-dir>/contracts/
.ts file → <your-project-root-dir>/test/
This ensures Hardhat can compile and test your contracts as expected.
// SPDX-License-Identifier: BSD-3-Clause-Clearpragmasolidity ^0.8.24;import { FHE, ebool, euint8, externalEuint8 } from"@fhevm/solidity/lib/FHE.sol";import { SepoliaConfig } from"@fhevm/solidity/config/ZamaConfig.sol";contractFHEIfThenElseisSepoliaConfig { euint8 private _a; euint8 private _b; euint8 private _max;// solhint-disable-next-line no-empty-blocksconstructor() {}functionsetA(externalEuint8 inputA,bytescalldata inputProof) external { _a = FHE.fromExternal(inputA, inputProof); FHE.allowThis(_a); }functionsetB(externalEuint8 inputB,bytescalldata inputProof) external { _b = FHE.fromExternal(inputB, inputProof); FHE.allowThis(_b); }functioncomputeMax() external {// a >= b// solhint-disable-next-line var-name-mixedcase ebool _a_ge_b = FHE.ge(_a, _b);// a >= b ? a : b _max = FHE.select(_a_ge_b, _a, _b);// For more information about FHE permissions in this case,// read the `computeAPlusB()` commentaries in `FHEAdd.sol`. FHE.allowThis(_max); FHE.allow(_max, msg.sender); }functionresult() publicviewreturns (euint8) {return _max; }}
import { FHEIfThenElse, FHEIfThenElse__factory } from "../../../types";
import type { Signers } from "../../types";
import { FhevmType, HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory("FHEIfThenElse")) as FHEIfThenElse__factory;
const fheIfThenElse = (await factory.deploy()) as FHEIfThenElse;
const fheIfThenElse_address = await fheIfThenElse.getAddress();
return { fheIfThenElse, fheIfThenElse_address };
}
/**
* This trivial example demonstrates the FHE encryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("FHEIfThenElse", function () {
let contract: FHEIfThenElse;
let contractAddress: string;
let signers: Signers;
let bob: HardhatEthersSigner;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
bob = ethSigners[2];
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contractAddress = deployment.fheIfThenElse_address;
contract = deployment.fheIfThenElse;
});
it("a >= b ? a : b should succeed", async function () {
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
let tx;
// Let's compute `a >= b ? a : b`
const a = 80;
const b = 123;
// Alice encrypts and sets `a` as 80
const inputA = await fhevm.createEncryptedInput(contractAddress, signers.alice.address).add8(a).encrypt();
tx = await contract.connect(signers.alice).setA(inputA.handles[0], inputA.inputProof);
await tx.wait();
// Alice encrypts and sets `b` as 203
const inputB = await fhevm.createEncryptedInput(contractAddress, signers.alice.address).add8(b).encrypt();
tx = await contract.connect(signers.alice).setB(inputB.handles[0], inputB.inputProof);
await tx.wait();
// Why Bob has FHE permissions to execute the operation in this case ?
// See `computeAPlusB()` in `FHEAdd.sol` for a detailed answer
tx = await contract.connect(bob).computeMax();
await tx.wait();
const encryptedMax = await contract.result();
const clearMax = await fhevm.userDecryptEuint(
FhevmType.euint8, // Specify the encrypted type
encryptedMax,
contractAddress, // The contract address
bob, // The user wallet
);
expect(clearMax).to.equal(a >= b ? a : b);
});
});