create-fhevm-example 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/advanced/BlindAuction.sol +255 -0
- package/contracts/advanced/EncryptedEscrow.sol +315 -0
- package/contracts/advanced/HiddenVoting.sol +231 -0
- package/contracts/advanced/PrivateKYC.sol +309 -0
- package/contracts/advanced/PrivatePayroll.sol +285 -0
- package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +160 -0
- package/contracts/basic/decryption/PublicDecryptSingleValue.sol +142 -0
- package/contracts/basic/decryption/UserDecryptMultipleValues.sol +61 -0
- package/contracts/basic/decryption/UserDecryptSingleValue.sol +59 -0
- package/contracts/basic/encryption/EncryptMultipleValues.sol +72 -0
- package/contracts/basic/encryption/EncryptSingleValue.sol +44 -0
- package/contracts/basic/encryption/FHECounter.sol +54 -0
- package/contracts/basic/fhe-operations/FHEAdd.sol +51 -0
- package/contracts/basic/fhe-operations/FHEArithmetic.sol +99 -0
- package/contracts/basic/fhe-operations/FHEComparison.sol +116 -0
- package/contracts/basic/fhe-operations/FHEIfThenElse.sol +53 -0
- package/contracts/concepts/FHEAccessControl.sol +94 -0
- package/contracts/concepts/FHEAntiPatterns.sol +329 -0
- package/contracts/concepts/FHEHandles.sol +128 -0
- package/contracts/concepts/FHEInputProof.sol +104 -0
- package/contracts/gaming/EncryptedLottery.sol +298 -0
- package/contracts/gaming/EncryptedPoker.sol +337 -0
- package/contracts/gaming/RockPaperScissors.sol +213 -0
- package/contracts/openzeppelin/ERC7984.sol +85 -0
- package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +43 -0
- package/contracts/openzeppelin/SwapERC7984ToERC20.sol +110 -0
- package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +48 -0
- package/contracts/openzeppelin/VestingWallet.sol +147 -0
- package/contracts/openzeppelin/mocks/ERC20Mock.sol +31 -0
- package/dist/scripts/commands/add-mode.d.ts.map +1 -0
- package/dist/scripts/{add-mode.js → commands/add-mode.js} +27 -61
- package/dist/scripts/commands/doctor.d.ts.map +1 -0
- package/dist/scripts/{doctor.js → commands/doctor.js} +2 -2
- package/dist/scripts/commands/generate-config.d.ts.map +1 -0
- package/dist/scripts/{generate-config.js → commands/generate-config.js} +3 -10
- package/dist/scripts/commands/generate-docs.d.ts.map +1 -0
- package/dist/scripts/{generate-docs.js → commands/generate-docs.js} +4 -3
- package/dist/scripts/commands/maintenance.d.ts.map +1 -0
- package/dist/scripts/{maintenance.js → commands/maintenance.js} +11 -10
- package/dist/scripts/index.js +14 -33
- package/dist/scripts/{builders.d.ts → shared/builders.d.ts} +2 -2
- package/dist/scripts/shared/builders.d.ts.map +1 -0
- package/dist/scripts/{builders.js → shared/builders.js} +49 -30
- package/dist/scripts/{config.d.ts → shared/config.d.ts} +0 -2
- package/dist/scripts/shared/config.d.ts.map +1 -0
- package/dist/scripts/{config.js → shared/config.js} +48 -59
- package/dist/scripts/shared/generators.d.ts +42 -0
- package/dist/scripts/shared/generators.d.ts.map +1 -0
- package/dist/scripts/{utils.js → shared/generators.js} +34 -271
- package/dist/scripts/shared/ui.d.ts.map +1 -0
- package/dist/scripts/{ui.js → shared/ui.js} +3 -2
- package/dist/scripts/{utils.d.ts → shared/utils.d.ts} +4 -27
- package/dist/scripts/shared/utils.d.ts.map +1 -0
- package/dist/scripts/shared/utils.js +228 -0
- package/fhevm-hardhat-template/.eslintignore +26 -0
- package/fhevm-hardhat-template/.eslintrc.yml +21 -0
- package/fhevm-hardhat-template/.github/workflows/main.yml +47 -0
- package/fhevm-hardhat-template/.github/workflows/manual-windows.yml +28 -0
- package/fhevm-hardhat-template/.github/workflows/manual.yml +28 -0
- package/fhevm-hardhat-template/.prettierignore +25 -0
- package/fhevm-hardhat-template/.prettierrc.yml +15 -0
- package/fhevm-hardhat-template/.solcover.js +4 -0
- package/fhevm-hardhat-template/.solhint.json +12 -0
- package/fhevm-hardhat-template/.solhintignore +3 -0
- package/fhevm-hardhat-template/.vscode/extensions.json +3 -0
- package/fhevm-hardhat-template/.vscode/settings.json +9 -0
- package/fhevm-hardhat-template/LICENSE +33 -0
- package/fhevm-hardhat-template/README.md +110 -0
- package/fhevm-hardhat-template/contracts/FHECounter.sol +46 -0
- package/fhevm-hardhat-template/deploy/deploy.ts +17 -0
- package/fhevm-hardhat-template/hardhat.config.ts +90 -0
- package/fhevm-hardhat-template/package-lock.json +10405 -0
- package/fhevm-hardhat-template/package.json +104 -0
- package/fhevm-hardhat-template/tasks/FHECounter.ts +184 -0
- package/fhevm-hardhat-template/tasks/accounts.ts +9 -0
- package/fhevm-hardhat-template/test/FHECounter.ts +104 -0
- package/fhevm-hardhat-template/test/FHECounterSepolia.ts +104 -0
- package/fhevm-hardhat-template/tsconfig.json +23 -0
- package/package.json +11 -8
- package/test/advanced/BlindAuction.ts +246 -0
- package/test/advanced/EncryptedEscrow.ts +295 -0
- package/test/advanced/HiddenVoting.ts +268 -0
- package/test/advanced/PrivateKYC.ts +382 -0
- package/test/advanced/PrivatePayroll.ts +253 -0
- package/test/basic/decryption/PublicDecryptMultipleValues.ts +254 -0
- package/test/basic/decryption/PublicDecryptSingleValue.ts +264 -0
- package/test/basic/decryption/UserDecryptMultipleValues.ts +107 -0
- package/test/basic/decryption/UserDecryptSingleValue.ts +97 -0
- package/test/basic/encryption/EncryptMultipleValues.ts +110 -0
- package/test/basic/encryption/EncryptSingleValue.ts +124 -0
- package/test/basic/encryption/FHECounter.ts +112 -0
- package/test/basic/fhe-operations/FHEAdd.ts +97 -0
- package/test/basic/fhe-operations/FHEArithmetic.ts +161 -0
- package/test/basic/fhe-operations/FHEComparison.ts +167 -0
- package/test/basic/fhe-operations/FHEIfThenElse.ts +97 -0
- package/test/concepts/FHEAccessControl.ts +154 -0
- package/test/concepts/FHEAntiPatterns.ts +111 -0
- package/test/concepts/FHEHandles.ts +156 -0
- package/test/concepts/FHEInputProof.ts +151 -0
- package/test/gaming/EncryptedLottery.ts +214 -0
- package/test/gaming/EncryptedPoker.ts +349 -0
- package/test/gaming/RockPaperScissors.ts +205 -0
- package/test/openzeppelin/ERC7984.ts +142 -0
- package/test/openzeppelin/ERC7984ERC20Wrapper.ts +71 -0
- package/test/openzeppelin/SwapERC7984ToERC20.ts +76 -0
- package/test/openzeppelin/SwapERC7984ToERC7984.ts +113 -0
- package/test/openzeppelin/VestingWallet.ts +89 -0
- package/dist/scripts/add-mode.d.ts.map +0 -1
- package/dist/scripts/builders.d.ts.map +0 -1
- package/dist/scripts/config.d.ts.map +0 -1
- package/dist/scripts/doctor.d.ts.map +0 -1
- package/dist/scripts/generate-config.d.ts.map +0 -1
- package/dist/scripts/generate-docs.d.ts.map +0 -1
- package/dist/scripts/help.d.ts +0 -9
- package/dist/scripts/help.d.ts.map +0 -1
- package/dist/scripts/help.js +0 -73
- package/dist/scripts/maintenance.d.ts.map +0 -1
- package/dist/scripts/ui.d.ts.map +0 -1
- package/dist/scripts/utils.d.ts.map +0 -1
- /package/dist/scripts/{add-mode.d.ts → commands/add-mode.d.ts} +0 -0
- /package/dist/scripts/{doctor.d.ts → commands/doctor.d.ts} +0 -0
- /package/dist/scripts/{generate-config.d.ts → commands/generate-config.d.ts} +0 -0
- /package/dist/scripts/{generate-docs.d.ts → commands/generate-docs.d.ts} +0 -0
- /package/dist/scripts/{maintenance.d.ts → commands/maintenance.d.ts} +0 -0
- /package/dist/scripts/{ui.d.ts → shared/ui.d.ts} +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
2
|
+
import { ethers, fhevm } from "hardhat";
|
|
3
|
+
import { FHECounter, FHECounter__factory } from "../types";
|
|
4
|
+
import { expect } from "chai";
|
|
5
|
+
import { FhevmType } from "@fhevm/hardhat-plugin";
|
|
6
|
+
|
|
7
|
+
type Signers = {
|
|
8
|
+
deployer: HardhatEthersSigner;
|
|
9
|
+
alice: HardhatEthersSigner;
|
|
10
|
+
bob: HardhatEthersSigner;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
async function deployFixture() {
|
|
14
|
+
const factory = (await ethers.getContractFactory(
|
|
15
|
+
"FHECounter"
|
|
16
|
+
)) as FHECounter__factory;
|
|
17
|
+
const fheCounterContract = (await factory.deploy()) as FHECounter;
|
|
18
|
+
const fheCounterContractAddress = await fheCounterContract.getAddress();
|
|
19
|
+
|
|
20
|
+
return { fheCounterContract, fheCounterContractAddress };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("FHECounter", function () {
|
|
24
|
+
let signers: Signers;
|
|
25
|
+
let fheCounterContract: FHECounter;
|
|
26
|
+
let fheCounterContractAddress: string;
|
|
27
|
+
|
|
28
|
+
before(async function () {
|
|
29
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
30
|
+
signers = {
|
|
31
|
+
deployer: ethSigners[0],
|
|
32
|
+
alice: ethSigners[1],
|
|
33
|
+
bob: ethSigners[2],
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
beforeEach(async function () {
|
|
38
|
+
// Check whether the tests are running against an FHEVM mock environment
|
|
39
|
+
if (!fhevm.isMock) {
|
|
40
|
+
console.warn(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
41
|
+
this.skip();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
({ fheCounterContract, fheCounterContractAddress } = await deployFixture());
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("encrypted count should be uninitialized after deployment", async function () {
|
|
48
|
+
const encryptedCount = await fheCounterContract.getCount();
|
|
49
|
+
// Expect initial count to be bytes32(0) after deployment,
|
|
50
|
+
// (meaning the encrypted count value is uninitialized)
|
|
51
|
+
expect(encryptedCount).to.eq(ethers.ZeroHash);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("increment the counter by 1", async function () {
|
|
55
|
+
const encryptedCountBeforeInc = await fheCounterContract.getCount();
|
|
56
|
+
expect(encryptedCountBeforeInc).to.eq(ethers.ZeroHash);
|
|
57
|
+
const clearCountBeforeInc = 0;
|
|
58
|
+
|
|
59
|
+
// Encrypt constant 1 as a euint32
|
|
60
|
+
const clearOne = 1;
|
|
61
|
+
const encryptedOne = await fhevm
|
|
62
|
+
.createEncryptedInput(fheCounterContractAddress, signers.alice.address)
|
|
63
|
+
.add32(clearOne)
|
|
64
|
+
.encrypt();
|
|
65
|
+
|
|
66
|
+
const tx = await fheCounterContract
|
|
67
|
+
.connect(signers.alice)
|
|
68
|
+
.increment(encryptedOne.handles[0], encryptedOne.inputProof);
|
|
69
|
+
await tx.wait();
|
|
70
|
+
|
|
71
|
+
const encryptedCountAfterInc = await fheCounterContract.getCount();
|
|
72
|
+
const clearCountAfterInc = await fhevm.userDecryptEuint(
|
|
73
|
+
FhevmType.euint32,
|
|
74
|
+
encryptedCountAfterInc,
|
|
75
|
+
fheCounterContractAddress,
|
|
76
|
+
signers.alice
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(clearCountAfterInc).to.eq(clearCountBeforeInc + clearOne);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("decrement the counter by 1", async function () {
|
|
83
|
+
// Encrypt constant 1 as a euint32
|
|
84
|
+
const clearOne = 1;
|
|
85
|
+
const encryptedOne = await fhevm
|
|
86
|
+
.createEncryptedInput(fheCounterContractAddress, signers.alice.address)
|
|
87
|
+
.add32(clearOne)
|
|
88
|
+
.encrypt();
|
|
89
|
+
|
|
90
|
+
// First increment by 1, count becomes 1
|
|
91
|
+
let tx = await fheCounterContract
|
|
92
|
+
.connect(signers.alice)
|
|
93
|
+
.increment(encryptedOne.handles[0], encryptedOne.inputProof);
|
|
94
|
+
await tx.wait();
|
|
95
|
+
|
|
96
|
+
// Then decrement by 1, count goes back to 0
|
|
97
|
+
tx = await fheCounterContract
|
|
98
|
+
.connect(signers.alice)
|
|
99
|
+
.decrement(encryptedOne.handles[0], encryptedOne.inputProof);
|
|
100
|
+
await tx.wait();
|
|
101
|
+
|
|
102
|
+
const encryptedCountAfterDec = await fheCounterContract.getCount();
|
|
103
|
+
const clearCountAfterInc = await fhevm.userDecryptEuint(
|
|
104
|
+
FhevmType.euint32,
|
|
105
|
+
encryptedCountAfterDec,
|
|
106
|
+
fheCounterContractAddress,
|
|
107
|
+
signers.alice
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(clearCountAfterInc).to.eq(0);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FhevmType,
|
|
3
|
+
HardhatFhevmRuntimeEnvironment,
|
|
4
|
+
} from "@fhevm/hardhat-plugin";
|
|
5
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
6
|
+
import { expect } from "chai";
|
|
7
|
+
import { ethers } from "hardhat";
|
|
8
|
+
import * as hre from "hardhat";
|
|
9
|
+
|
|
10
|
+
import { FHEAdd, FHEAdd__factory } from "../types";
|
|
11
|
+
import type { Signers } from "./types";
|
|
12
|
+
|
|
13
|
+
async function deployFixture() {
|
|
14
|
+
// Contracts are deployed using the first signer/account by default
|
|
15
|
+
const factory = (await ethers.getContractFactory(
|
|
16
|
+
"FHEAdd"
|
|
17
|
+
)) as FHEAdd__factory;
|
|
18
|
+
const fheAdd = (await factory.deploy()) as FHEAdd;
|
|
19
|
+
const fheAdd_address = await fheAdd.getAddress();
|
|
20
|
+
|
|
21
|
+
return { fheAdd, fheAdd_address };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* This trivial example demonstrates the FHE encryption mechanism
|
|
26
|
+
* and highlights a common pitfall developers may encounter.
|
|
27
|
+
*/
|
|
28
|
+
describe("FHEAdd", function () {
|
|
29
|
+
let contract: FHEAdd;
|
|
30
|
+
let contractAddress: string;
|
|
31
|
+
let signers: Signers;
|
|
32
|
+
let bob: HardhatEthersSigner;
|
|
33
|
+
|
|
34
|
+
before(async function () {
|
|
35
|
+
// Check whether the tests are running against an FHEVM mock environment
|
|
36
|
+
if (!hre.fhevm.isMock) {
|
|
37
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
41
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
42
|
+
bob = ethSigners[2];
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
beforeEach(async function () {
|
|
46
|
+
// Deploy a new contract each time we run a new test
|
|
47
|
+
const deployment = await deployFixture();
|
|
48
|
+
contractAddress = deployment.fheAdd_address;
|
|
49
|
+
contract = deployment.fheAdd;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("a + b should succeed", async function () {
|
|
53
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
54
|
+
|
|
55
|
+
let tx;
|
|
56
|
+
|
|
57
|
+
// Let's compute 80 + 123 = 203
|
|
58
|
+
const a = 80;
|
|
59
|
+
const b = 123;
|
|
60
|
+
|
|
61
|
+
// Alice encrypts and sets `a` as 80
|
|
62
|
+
const inputA = await fhevm
|
|
63
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
64
|
+
.add8(a)
|
|
65
|
+
.encrypt();
|
|
66
|
+
tx = await contract
|
|
67
|
+
.connect(signers.alice)
|
|
68
|
+
.setA(inputA.handles[0], inputA.inputProof);
|
|
69
|
+
await tx.wait();
|
|
70
|
+
|
|
71
|
+
// Alice encrypts and sets `b` as 203
|
|
72
|
+
const inputB = await fhevm
|
|
73
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
74
|
+
.add8(b)
|
|
75
|
+
.encrypt();
|
|
76
|
+
tx = await contract
|
|
77
|
+
.connect(signers.alice)
|
|
78
|
+
.setB(inputB.handles[0], inputB.inputProof);
|
|
79
|
+
await tx.wait();
|
|
80
|
+
|
|
81
|
+
// Why Bob has FHE permissions to execute the operation in this case ?
|
|
82
|
+
// See `computeAPlusB()` in `FHEAdd.sol` for a detailed answer
|
|
83
|
+
tx = await contract.connect(bob).computeAPlusB();
|
|
84
|
+
await tx.wait();
|
|
85
|
+
|
|
86
|
+
const encryptedAplusB = await contract.result();
|
|
87
|
+
|
|
88
|
+
const clearAplusB = await fhevm.userDecryptEuint(
|
|
89
|
+
FhevmType.euint8, // Specify the encrypted type
|
|
90
|
+
encryptedAplusB,
|
|
91
|
+
contractAddress, // The contract address
|
|
92
|
+
bob // The user wallet
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
expect(clearAplusB).to.equal(a + b);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FhevmType,
|
|
3
|
+
HardhatFhevmRuntimeEnvironment,
|
|
4
|
+
} from "@fhevm/hardhat-plugin";
|
|
5
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
6
|
+
import { expect } from "chai";
|
|
7
|
+
import { ethers } from "hardhat";
|
|
8
|
+
import * as hre from "hardhat";
|
|
9
|
+
|
|
10
|
+
import { FHEArithmetic, FHEArithmetic__factory } from "../types";
|
|
11
|
+
import type { Signers } from "./types";
|
|
12
|
+
|
|
13
|
+
async function deployFixture() {
|
|
14
|
+
const factory = (await ethers.getContractFactory(
|
|
15
|
+
"FHEArithmetic"
|
|
16
|
+
)) as FHEArithmetic__factory;
|
|
17
|
+
const contract = (await factory.deploy()) as FHEArithmetic;
|
|
18
|
+
const contractAddress = await contract.getAddress();
|
|
19
|
+
return { contract, contractAddress };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @notice Demonstrates all FHE arithmetic operations on encrypted integers.
|
|
24
|
+
* Tests: add, sub, mul, div, rem, min, max
|
|
25
|
+
*/
|
|
26
|
+
describe("FHEArithmetic", function () {
|
|
27
|
+
let contract: FHEArithmetic;
|
|
28
|
+
let contractAddress: string;
|
|
29
|
+
let signers: Signers;
|
|
30
|
+
|
|
31
|
+
before(async function () {
|
|
32
|
+
if (!hre.fhevm.isMock) {
|
|
33
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
34
|
+
}
|
|
35
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
36
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
beforeEach(async function () {
|
|
40
|
+
const deployment = await deployFixture();
|
|
41
|
+
contractAddress = deployment.contractAddress;
|
|
42
|
+
contract = deployment.contract;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("Arithmetic Operations", function () {
|
|
46
|
+
const valueA = 100;
|
|
47
|
+
const valueB = 25;
|
|
48
|
+
|
|
49
|
+
beforeEach(async function () {
|
|
50
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
51
|
+
|
|
52
|
+
// Set encrypted values A and B
|
|
53
|
+
const inputA = await fhevm
|
|
54
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
55
|
+
.add32(valueA)
|
|
56
|
+
.encrypt();
|
|
57
|
+
await contract
|
|
58
|
+
.connect(signers.alice)
|
|
59
|
+
.setA(inputA.handles[0], inputA.inputProof);
|
|
60
|
+
|
|
61
|
+
const inputB = await fhevm
|
|
62
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
63
|
+
.add32(valueB)
|
|
64
|
+
.encrypt();
|
|
65
|
+
await contract
|
|
66
|
+
.connect(signers.alice)
|
|
67
|
+
.setB(inputB.handles[0], inputB.inputProof);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should compute addition correctly (100 + 25 = 125)", async function () {
|
|
71
|
+
await contract.connect(signers.alice).computeAdd();
|
|
72
|
+
const encrypted = await contract.getResult();
|
|
73
|
+
|
|
74
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
75
|
+
FhevmType.euint32,
|
|
76
|
+
encrypted,
|
|
77
|
+
contractAddress,
|
|
78
|
+
signers.alice
|
|
79
|
+
);
|
|
80
|
+
expect(decrypted).to.equal(valueA + valueB);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should compute subtraction correctly (100 - 25 = 75)", async function () {
|
|
84
|
+
await contract.connect(signers.alice).computeSub();
|
|
85
|
+
const encrypted = await contract.getResult();
|
|
86
|
+
|
|
87
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
88
|
+
FhevmType.euint32,
|
|
89
|
+
encrypted,
|
|
90
|
+
contractAddress,
|
|
91
|
+
signers.alice
|
|
92
|
+
);
|
|
93
|
+
expect(decrypted).to.equal(valueA - valueB);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should compute multiplication correctly (100 * 25 = 2500)", async function () {
|
|
97
|
+
await contract.connect(signers.alice).computeMul();
|
|
98
|
+
const encrypted = await contract.getResult();
|
|
99
|
+
|
|
100
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
101
|
+
FhevmType.euint32,
|
|
102
|
+
encrypted,
|
|
103
|
+
contractAddress,
|
|
104
|
+
signers.alice
|
|
105
|
+
);
|
|
106
|
+
expect(decrypted).to.equal(valueA * valueB);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should compute division correctly (100 / 25 = 4)", async function () {
|
|
110
|
+
await contract.connect(signers.alice).computeDiv(valueB);
|
|
111
|
+
const encrypted = await contract.getResult();
|
|
112
|
+
|
|
113
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
114
|
+
FhevmType.euint32,
|
|
115
|
+
encrypted,
|
|
116
|
+
contractAddress,
|
|
117
|
+
signers.alice
|
|
118
|
+
);
|
|
119
|
+
expect(decrypted).to.equal(Math.floor(valueA / valueB));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should compute remainder correctly (100 % 25 = 0)", async function () {
|
|
123
|
+
await contract.connect(signers.alice).computeRem(valueB);
|
|
124
|
+
const encrypted = await contract.getResult();
|
|
125
|
+
|
|
126
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
127
|
+
FhevmType.euint32,
|
|
128
|
+
encrypted,
|
|
129
|
+
contractAddress,
|
|
130
|
+
signers.alice
|
|
131
|
+
);
|
|
132
|
+
expect(decrypted).to.equal(valueA % valueB);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should compute minimum correctly (min(100, 25) = 25)", async function () {
|
|
136
|
+
await contract.connect(signers.alice).computeMin();
|
|
137
|
+
const encrypted = await contract.getResult();
|
|
138
|
+
|
|
139
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
140
|
+
FhevmType.euint32,
|
|
141
|
+
encrypted,
|
|
142
|
+
contractAddress,
|
|
143
|
+
signers.alice
|
|
144
|
+
);
|
|
145
|
+
expect(decrypted).to.equal(Math.min(valueA, valueB));
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should compute maximum correctly (max(100, 25) = 100)", async function () {
|
|
149
|
+
await contract.connect(signers.alice).computeMax();
|
|
150
|
+
const encrypted = await contract.getResult();
|
|
151
|
+
|
|
152
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
153
|
+
FhevmType.euint32,
|
|
154
|
+
encrypted,
|
|
155
|
+
contractAddress,
|
|
156
|
+
signers.alice
|
|
157
|
+
);
|
|
158
|
+
expect(decrypted).to.equal(Math.max(valueA, valueB));
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FhevmType,
|
|
3
|
+
HardhatFhevmRuntimeEnvironment,
|
|
4
|
+
} from "@fhevm/hardhat-plugin";
|
|
5
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
6
|
+
import { expect } from "chai";
|
|
7
|
+
import { ethers } from "hardhat";
|
|
8
|
+
import * as hre from "hardhat";
|
|
9
|
+
|
|
10
|
+
import { FHEComparison, FHEComparison__factory } from "../types";
|
|
11
|
+
import type { Signers } from "./types";
|
|
12
|
+
|
|
13
|
+
async function deployFixture() {
|
|
14
|
+
const factory = (await ethers.getContractFactory(
|
|
15
|
+
"FHEComparison"
|
|
16
|
+
)) as FHEComparison__factory;
|
|
17
|
+
const contract = (await factory.deploy()) as FHEComparison;
|
|
18
|
+
const contractAddress = await contract.getAddress();
|
|
19
|
+
return { contract, contractAddress };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @notice Demonstrates all FHE comparison operations on encrypted integers.
|
|
24
|
+
* Tests: eq, ne, gt, lt, ge, le, select
|
|
25
|
+
*/
|
|
26
|
+
describe("FHEComparison", function () {
|
|
27
|
+
let contract: FHEComparison;
|
|
28
|
+
let contractAddress: string;
|
|
29
|
+
let signers: Signers;
|
|
30
|
+
|
|
31
|
+
before(async function () {
|
|
32
|
+
if (!hre.fhevm.isMock) {
|
|
33
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
34
|
+
}
|
|
35
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
36
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
beforeEach(async function () {
|
|
40
|
+
const deployment = await deployFixture();
|
|
41
|
+
contractAddress = deployment.contractAddress;
|
|
42
|
+
contract = deployment.contract;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("Comparison Operations with A=100, B=25", function () {
|
|
46
|
+
const valueA = 100;
|
|
47
|
+
const valueB = 25;
|
|
48
|
+
|
|
49
|
+
beforeEach(async function () {
|
|
50
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
51
|
+
|
|
52
|
+
const inputA = await fhevm
|
|
53
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
54
|
+
.add32(valueA)
|
|
55
|
+
.encrypt();
|
|
56
|
+
await contract
|
|
57
|
+
.connect(signers.alice)
|
|
58
|
+
.setA(inputA.handles[0], inputA.inputProof);
|
|
59
|
+
|
|
60
|
+
const inputB = await fhevm
|
|
61
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
62
|
+
.add32(valueB)
|
|
63
|
+
.encrypt();
|
|
64
|
+
await contract
|
|
65
|
+
.connect(signers.alice)
|
|
66
|
+
.setB(inputB.handles[0], inputB.inputProof);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should compute equality correctly (100 == 25 is false)", async function () {
|
|
70
|
+
await contract.connect(signers.alice).computeEq();
|
|
71
|
+
const encrypted = await contract.getBoolResult();
|
|
72
|
+
|
|
73
|
+
const decrypted = await hre.fhevm.userDecryptEbool(
|
|
74
|
+
encrypted,
|
|
75
|
+
contractAddress,
|
|
76
|
+
signers.alice
|
|
77
|
+
);
|
|
78
|
+
expect(decrypted).to.equal(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should compute inequality correctly (100 != 25 is true)", async function () {
|
|
82
|
+
await contract.connect(signers.alice).computeNe();
|
|
83
|
+
const encrypted = await contract.getBoolResult();
|
|
84
|
+
|
|
85
|
+
const decrypted = await hre.fhevm.userDecryptEbool(
|
|
86
|
+
encrypted,
|
|
87
|
+
contractAddress,
|
|
88
|
+
signers.alice
|
|
89
|
+
);
|
|
90
|
+
expect(decrypted).to.equal(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should compute greater than correctly (100 > 25 is true)", async function () {
|
|
94
|
+
await contract.connect(signers.alice).computeGt();
|
|
95
|
+
const encrypted = await contract.getBoolResult();
|
|
96
|
+
|
|
97
|
+
const decrypted = await hre.fhevm.userDecryptEbool(
|
|
98
|
+
encrypted,
|
|
99
|
+
contractAddress,
|
|
100
|
+
signers.alice
|
|
101
|
+
);
|
|
102
|
+
expect(decrypted).to.equal(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("should compute less than correctly (100 < 25 is false)", async function () {
|
|
106
|
+
await contract.connect(signers.alice).computeLt();
|
|
107
|
+
const encrypted = await contract.getBoolResult();
|
|
108
|
+
|
|
109
|
+
const decrypted = await hre.fhevm.userDecryptEbool(
|
|
110
|
+
encrypted,
|
|
111
|
+
contractAddress,
|
|
112
|
+
signers.alice
|
|
113
|
+
);
|
|
114
|
+
expect(decrypted).to.equal(false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should compute greater or equal correctly (100 >= 25 is true)", async function () {
|
|
118
|
+
await contract.connect(signers.alice).computeGe();
|
|
119
|
+
const encrypted = await contract.getBoolResult();
|
|
120
|
+
|
|
121
|
+
const decrypted = await hre.fhevm.userDecryptEbool(
|
|
122
|
+
encrypted,
|
|
123
|
+
contractAddress,
|
|
124
|
+
signers.alice
|
|
125
|
+
);
|
|
126
|
+
expect(decrypted).to.equal(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("should compute less or equal correctly (100 <= 25 is false)", async function () {
|
|
130
|
+
await contract.connect(signers.alice).computeLe();
|
|
131
|
+
const encrypted = await contract.getBoolResult();
|
|
132
|
+
|
|
133
|
+
const decrypted = await hre.fhevm.userDecryptEbool(
|
|
134
|
+
encrypted,
|
|
135
|
+
contractAddress,
|
|
136
|
+
signers.alice
|
|
137
|
+
);
|
|
138
|
+
expect(decrypted).to.equal(false);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should compute max via select correctly (max(100, 25) = 100)", async function () {
|
|
142
|
+
await contract.connect(signers.alice).computeMaxViaSelect();
|
|
143
|
+
const encrypted = await contract.getSelectedResult();
|
|
144
|
+
|
|
145
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
146
|
+
FhevmType.euint32,
|
|
147
|
+
encrypted,
|
|
148
|
+
contractAddress,
|
|
149
|
+
signers.alice
|
|
150
|
+
);
|
|
151
|
+
expect(decrypted).to.equal(valueA);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("should compute min via select correctly (min(100, 25) = 25)", async function () {
|
|
155
|
+
await contract.connect(signers.alice).computeMinViaSelect();
|
|
156
|
+
const encrypted = await contract.getSelectedResult();
|
|
157
|
+
|
|
158
|
+
const decrypted = await hre.fhevm.userDecryptEuint(
|
|
159
|
+
FhevmType.euint32,
|
|
160
|
+
encrypted,
|
|
161
|
+
contractAddress,
|
|
162
|
+
signers.alice
|
|
163
|
+
);
|
|
164
|
+
expect(decrypted).to.equal(valueB);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FhevmType,
|
|
3
|
+
HardhatFhevmRuntimeEnvironment,
|
|
4
|
+
} from "@fhevm/hardhat-plugin";
|
|
5
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
6
|
+
import { expect } from "chai";
|
|
7
|
+
import { ethers } from "hardhat";
|
|
8
|
+
import * as hre from "hardhat";
|
|
9
|
+
|
|
10
|
+
import { FHEIfThenElse, FHEIfThenElse__factory } from "../types";
|
|
11
|
+
import type { Signers } from "./types";
|
|
12
|
+
|
|
13
|
+
async function deployFixture() {
|
|
14
|
+
// Contracts are deployed using the first signer/account by default
|
|
15
|
+
const factory = (await ethers.getContractFactory(
|
|
16
|
+
"FHEIfThenElse"
|
|
17
|
+
)) as FHEIfThenElse__factory;
|
|
18
|
+
const fheIfThenElse = (await factory.deploy()) as FHEIfThenElse;
|
|
19
|
+
const fheIfThenElse_address = await fheIfThenElse.getAddress();
|
|
20
|
+
|
|
21
|
+
return { fheIfThenElse, fheIfThenElse_address };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* This trivial example demonstrates the FHE encryption mechanism
|
|
26
|
+
* and highlights a common pitfall developers may encounter.
|
|
27
|
+
*/
|
|
28
|
+
describe("FHEIfThenElse", function () {
|
|
29
|
+
let contract: FHEIfThenElse;
|
|
30
|
+
let contractAddress: string;
|
|
31
|
+
let signers: Signers;
|
|
32
|
+
let bob: HardhatEthersSigner;
|
|
33
|
+
|
|
34
|
+
before(async function () {
|
|
35
|
+
// Check whether the tests are running against an FHEVM mock environment
|
|
36
|
+
if (!hre.fhevm.isMock) {
|
|
37
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
41
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
42
|
+
bob = ethSigners[2];
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
beforeEach(async function () {
|
|
46
|
+
// Deploy a new contract each time we run a new test
|
|
47
|
+
const deployment = await deployFixture();
|
|
48
|
+
contractAddress = deployment.fheIfThenElse_address;
|
|
49
|
+
contract = deployment.fheIfThenElse;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("a >= b ? a : b should succeed", async function () {
|
|
53
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
54
|
+
|
|
55
|
+
let tx;
|
|
56
|
+
|
|
57
|
+
// Let's compute `a >= b ? a : b`
|
|
58
|
+
const a = 80;
|
|
59
|
+
const b = 123;
|
|
60
|
+
|
|
61
|
+
// Alice encrypts and sets `a` as 80
|
|
62
|
+
const inputA = await fhevm
|
|
63
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
64
|
+
.add8(a)
|
|
65
|
+
.encrypt();
|
|
66
|
+
tx = await contract
|
|
67
|
+
.connect(signers.alice)
|
|
68
|
+
.setA(inputA.handles[0], inputA.inputProof);
|
|
69
|
+
await tx.wait();
|
|
70
|
+
|
|
71
|
+
// Alice encrypts and sets `b` as 203
|
|
72
|
+
const inputB = await fhevm
|
|
73
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
74
|
+
.add8(b)
|
|
75
|
+
.encrypt();
|
|
76
|
+
tx = await contract
|
|
77
|
+
.connect(signers.alice)
|
|
78
|
+
.setB(inputB.handles[0], inputB.inputProof);
|
|
79
|
+
await tx.wait();
|
|
80
|
+
|
|
81
|
+
// Why Bob has FHE permissions to execute the operation in this case ?
|
|
82
|
+
// See `computeAPlusB()` in `FHEAdd.sol` for a detailed answer
|
|
83
|
+
tx = await contract.connect(bob).computeMax();
|
|
84
|
+
await tx.wait();
|
|
85
|
+
|
|
86
|
+
const encryptedMax = await contract.result();
|
|
87
|
+
|
|
88
|
+
const clearMax = await fhevm.userDecryptEuint(
|
|
89
|
+
FhevmType.euint8, // Specify the encrypted type
|
|
90
|
+
encryptedMax,
|
|
91
|
+
contractAddress, // The contract address
|
|
92
|
+
bob // The user wallet
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
expect(clearMax).to.equal(a >= b ? a : b);
|
|
96
|
+
});
|
|
97
|
+
});
|