create-fhevm-example 1.3.1 → 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.
Files changed (122) hide show
  1. package/contracts/advanced/BlindAuction.sol +255 -0
  2. package/contracts/advanced/EncryptedEscrow.sol +315 -0
  3. package/contracts/advanced/HiddenVoting.sol +231 -0
  4. package/contracts/advanced/PrivateKYC.sol +309 -0
  5. package/contracts/advanced/PrivatePayroll.sol +285 -0
  6. package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +160 -0
  7. package/contracts/basic/decryption/PublicDecryptSingleValue.sol +142 -0
  8. package/contracts/basic/decryption/UserDecryptMultipleValues.sol +61 -0
  9. package/contracts/basic/decryption/UserDecryptSingleValue.sol +59 -0
  10. package/contracts/basic/encryption/EncryptMultipleValues.sol +72 -0
  11. package/contracts/basic/encryption/EncryptSingleValue.sol +44 -0
  12. package/contracts/basic/encryption/FHECounter.sol +54 -0
  13. package/contracts/basic/fhe-operations/FHEAdd.sol +51 -0
  14. package/contracts/basic/fhe-operations/FHEArithmetic.sol +99 -0
  15. package/contracts/basic/fhe-operations/FHEComparison.sol +116 -0
  16. package/contracts/basic/fhe-operations/FHEIfThenElse.sol +53 -0
  17. package/contracts/concepts/FHEAccessControl.sol +94 -0
  18. package/contracts/concepts/FHEAntiPatterns.sol +329 -0
  19. package/contracts/concepts/FHEHandles.sol +128 -0
  20. package/contracts/concepts/FHEInputProof.sol +104 -0
  21. package/contracts/gaming/EncryptedLottery.sol +298 -0
  22. package/contracts/gaming/EncryptedPoker.sol +337 -0
  23. package/contracts/gaming/RockPaperScissors.sol +213 -0
  24. package/contracts/openzeppelin/ERC7984.sol +85 -0
  25. package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +43 -0
  26. package/contracts/openzeppelin/SwapERC7984ToERC20.sol +110 -0
  27. package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +48 -0
  28. package/contracts/openzeppelin/VestingWallet.sol +147 -0
  29. package/contracts/openzeppelin/mocks/ERC20Mock.sol +31 -0
  30. package/dist/scripts/commands/add-mode.d.ts.map +1 -0
  31. package/dist/scripts/{add-mode.js → commands/add-mode.js} +27 -61
  32. package/dist/scripts/commands/doctor.d.ts.map +1 -0
  33. package/dist/scripts/{doctor.js → commands/doctor.js} +2 -2
  34. package/dist/scripts/commands/generate-config.d.ts.map +1 -0
  35. package/dist/scripts/{generate-config.js → commands/generate-config.js} +3 -10
  36. package/dist/scripts/commands/generate-docs.d.ts.map +1 -0
  37. package/dist/scripts/{generate-docs.js → commands/generate-docs.js} +4 -3
  38. package/dist/scripts/commands/maintenance.d.ts.map +1 -0
  39. package/dist/scripts/{maintenance.js → commands/maintenance.js} +11 -10
  40. package/dist/scripts/index.js +63 -59
  41. package/dist/scripts/{builders.d.ts → shared/builders.d.ts} +2 -2
  42. package/dist/scripts/shared/builders.d.ts.map +1 -0
  43. package/dist/scripts/{builders.js → shared/builders.js} +49 -30
  44. package/dist/scripts/{config.d.ts → shared/config.d.ts} +0 -2
  45. package/dist/scripts/shared/config.d.ts.map +1 -0
  46. package/dist/scripts/{config.js → shared/config.js} +48 -59
  47. package/dist/scripts/shared/generators.d.ts +42 -0
  48. package/dist/scripts/shared/generators.d.ts.map +1 -0
  49. package/dist/scripts/{utils.js → shared/generators.js} +34 -271
  50. package/dist/scripts/shared/ui.d.ts.map +1 -0
  51. package/dist/scripts/{ui.js → shared/ui.js} +3 -2
  52. package/dist/scripts/{utils.d.ts → shared/utils.d.ts} +4 -27
  53. package/dist/scripts/shared/utils.d.ts.map +1 -0
  54. package/dist/scripts/shared/utils.js +228 -0
  55. package/fhevm-hardhat-template/.eslintignore +26 -0
  56. package/fhevm-hardhat-template/.eslintrc.yml +21 -0
  57. package/fhevm-hardhat-template/.github/workflows/main.yml +47 -0
  58. package/fhevm-hardhat-template/.github/workflows/manual-windows.yml +28 -0
  59. package/fhevm-hardhat-template/.github/workflows/manual.yml +28 -0
  60. package/fhevm-hardhat-template/.prettierignore +25 -0
  61. package/fhevm-hardhat-template/.prettierrc.yml +15 -0
  62. package/fhevm-hardhat-template/.solcover.js +4 -0
  63. package/fhevm-hardhat-template/.solhint.json +12 -0
  64. package/fhevm-hardhat-template/.solhintignore +3 -0
  65. package/fhevm-hardhat-template/.vscode/extensions.json +3 -0
  66. package/fhevm-hardhat-template/.vscode/settings.json +9 -0
  67. package/fhevm-hardhat-template/LICENSE +33 -0
  68. package/fhevm-hardhat-template/README.md +110 -0
  69. package/fhevm-hardhat-template/contracts/FHECounter.sol +46 -0
  70. package/fhevm-hardhat-template/deploy/deploy.ts +17 -0
  71. package/fhevm-hardhat-template/hardhat.config.ts +90 -0
  72. package/fhevm-hardhat-template/package-lock.json +10405 -0
  73. package/fhevm-hardhat-template/package.json +104 -0
  74. package/fhevm-hardhat-template/tasks/FHECounter.ts +184 -0
  75. package/fhevm-hardhat-template/tasks/accounts.ts +9 -0
  76. package/fhevm-hardhat-template/test/FHECounter.ts +104 -0
  77. package/fhevm-hardhat-template/test/FHECounterSepolia.ts +104 -0
  78. package/fhevm-hardhat-template/tsconfig.json +23 -0
  79. package/package.json +13 -10
  80. package/test/advanced/BlindAuction.ts +246 -0
  81. package/test/advanced/EncryptedEscrow.ts +295 -0
  82. package/test/advanced/HiddenVoting.ts +268 -0
  83. package/test/advanced/PrivateKYC.ts +382 -0
  84. package/test/advanced/PrivatePayroll.ts +253 -0
  85. package/test/basic/decryption/PublicDecryptMultipleValues.ts +254 -0
  86. package/test/basic/decryption/PublicDecryptSingleValue.ts +264 -0
  87. package/test/basic/decryption/UserDecryptMultipleValues.ts +107 -0
  88. package/test/basic/decryption/UserDecryptSingleValue.ts +97 -0
  89. package/test/basic/encryption/EncryptMultipleValues.ts +110 -0
  90. package/test/basic/encryption/EncryptSingleValue.ts +124 -0
  91. package/test/basic/encryption/FHECounter.ts +112 -0
  92. package/test/basic/fhe-operations/FHEAdd.ts +97 -0
  93. package/test/basic/fhe-operations/FHEArithmetic.ts +161 -0
  94. package/test/basic/fhe-operations/FHEComparison.ts +167 -0
  95. package/test/basic/fhe-operations/FHEIfThenElse.ts +97 -0
  96. package/test/concepts/FHEAccessControl.ts +154 -0
  97. package/test/concepts/FHEAntiPatterns.ts +111 -0
  98. package/test/concepts/FHEHandles.ts +156 -0
  99. package/test/concepts/FHEInputProof.ts +151 -0
  100. package/test/gaming/EncryptedLottery.ts +214 -0
  101. package/test/gaming/EncryptedPoker.ts +349 -0
  102. package/test/gaming/RockPaperScissors.ts +205 -0
  103. package/test/openzeppelin/ERC7984.ts +142 -0
  104. package/test/openzeppelin/ERC7984ERC20Wrapper.ts +71 -0
  105. package/test/openzeppelin/SwapERC7984ToERC20.ts +76 -0
  106. package/test/openzeppelin/SwapERC7984ToERC7984.ts +113 -0
  107. package/test/openzeppelin/VestingWallet.ts +89 -0
  108. package/dist/scripts/add-mode.d.ts.map +0 -1
  109. package/dist/scripts/builders.d.ts.map +0 -1
  110. package/dist/scripts/config.d.ts.map +0 -1
  111. package/dist/scripts/doctor.d.ts.map +0 -1
  112. package/dist/scripts/generate-config.d.ts.map +0 -1
  113. package/dist/scripts/generate-docs.d.ts.map +0 -1
  114. package/dist/scripts/maintenance.d.ts.map +0 -1
  115. package/dist/scripts/ui.d.ts.map +0 -1
  116. package/dist/scripts/utils.d.ts.map +0 -1
  117. /package/dist/scripts/{add-mode.d.ts → commands/add-mode.d.ts} +0 -0
  118. /package/dist/scripts/{doctor.d.ts → commands/doctor.d.ts} +0 -0
  119. /package/dist/scripts/{generate-config.d.ts → commands/generate-config.d.ts} +0 -0
  120. /package/dist/scripts/{generate-docs.d.ts → commands/generate-docs.d.ts} +0 -0
  121. /package/dist/scripts/{maintenance.d.ts → commands/maintenance.d.ts} +0 -0
  122. /package/dist/scripts/{ui.d.ts → shared/ui.d.ts} +0 -0
@@ -0,0 +1,154 @@
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 { FHEAccessControl, FHEAccessControl__factory } from "../types";
11
+ import type { Signers } from "./types";
12
+
13
+ async function deployFixture() {
14
+ const factory = (await ethers.getContractFactory(
15
+ "FHEAccessControl"
16
+ )) as FHEAccessControl__factory;
17
+ const contract = (await factory.deploy()) as FHEAccessControl;
18
+ const contractAddress = await contract.getAddress();
19
+ return { contract, contractAddress };
20
+ }
21
+
22
+ /**
23
+ * @notice Tests FHE access control patterns
24
+ * Demonstrates correct and incorrect permission handling
25
+ */
26
+ describe("FHEAccessControl", function () {
27
+ let contract: FHEAccessControl;
28
+ let contractAddress: string;
29
+ let signers: Signers;
30
+ let bob: HardhatEthersSigner;
31
+
32
+ before(async function () {
33
+ if (!hre.fhevm.isMock) {
34
+ throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
35
+ }
36
+ const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
37
+ signers = { owner: ethSigners[0], alice: ethSigners[1] };
38
+ bob = ethSigners[2];
39
+ });
40
+
41
+ beforeEach(async function () {
42
+ const deployment = await deployFixture();
43
+ contractAddress = deployment.contractAddress;
44
+ contract = deployment.contract;
45
+ });
46
+
47
+ describe("Correct Access Control Pattern", function () {
48
+ const secretValue = 42;
49
+
50
+ it("should allow user decryption with full access", async function () {
51
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
52
+
53
+ // Store value with proper permissions
54
+ const input = await fhevm
55
+ .createEncryptedInput(contractAddress, signers.alice.address)
56
+ .add32(secretValue)
57
+ .encrypt();
58
+ await contract
59
+ .connect(signers.alice)
60
+ .storeWithFullAccess(input.handles[0], input.inputProof);
61
+
62
+ // Verify access was granted
63
+ expect(await contract.hasAccess(signers.alice.address)).to.equal(true);
64
+
65
+ // User should be able to decrypt
66
+ const encrypted = await contract.getSecretValue();
67
+ const decrypted = await fhevm.userDecryptEuint(
68
+ FhevmType.euint32,
69
+ encrypted,
70
+ contractAddress,
71
+ signers.alice
72
+ );
73
+ expect(decrypted).to.equal(secretValue);
74
+ });
75
+
76
+ it("should allow granting access to additional users", async function () {
77
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
78
+
79
+ // Alice stores the value
80
+ const input = await fhevm
81
+ .createEncryptedInput(contractAddress, signers.alice.address)
82
+ .add32(secretValue)
83
+ .encrypt();
84
+ await contract
85
+ .connect(signers.alice)
86
+ .storeWithFullAccess(input.handles[0], input.inputProof);
87
+
88
+ // Alice grants access to Bob
89
+ await contract.connect(signers.alice).grantAccess(bob.address);
90
+
91
+ // Bob can now decrypt
92
+ const encrypted = await contract.getSecretValue();
93
+ const decrypted = await fhevm.userDecryptEuint(
94
+ FhevmType.euint32,
95
+ encrypted,
96
+ contractAddress,
97
+ bob
98
+ );
99
+ expect(decrypted).to.equal(secretValue);
100
+ });
101
+ });
102
+
103
+ describe("Wrong Access Control Patterns", function () {
104
+ const secretValue = 42;
105
+
106
+ it("should FAIL user decryption without allowThis", async function () {
107
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
108
+
109
+ // Store value WITHOUT allowThis (wrong pattern)
110
+ const input = await fhevm
111
+ .createEncryptedInput(contractAddress, signers.alice.address)
112
+ .add32(secretValue)
113
+ .encrypt();
114
+ await contract
115
+ .connect(signers.alice)
116
+ .storeWithoutAllowThis(input.handles[0], input.inputProof);
117
+
118
+ // Attempting to decrypt should fail
119
+ const encrypted = await contract.getSecretValue();
120
+ await expect(
121
+ fhevm.userDecryptEuint(
122
+ FhevmType.euint32,
123
+ encrypted,
124
+ contractAddress,
125
+ signers.alice
126
+ )
127
+ ).to.be.rejected;
128
+ });
129
+
130
+ it("should FAIL user decryption without user allow", async function () {
131
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
132
+
133
+ // Store value WITHOUT user allow (wrong pattern)
134
+ const input = await fhevm
135
+ .createEncryptedInput(contractAddress, signers.alice.address)
136
+ .add32(secretValue)
137
+ .encrypt();
138
+ await contract
139
+ .connect(signers.alice)
140
+ .storeWithoutUserAllow(input.handles[0], input.inputProof);
141
+
142
+ // User (alice) cannot decrypt because no permission was granted
143
+ const encrypted = await contract.getSecretValue();
144
+ await expect(
145
+ fhevm.userDecryptEuint(
146
+ FhevmType.euint32,
147
+ encrypted,
148
+ contractAddress,
149
+ signers.alice
150
+ )
151
+ ).to.be.rejected;
152
+ });
153
+ });
154
+ });
@@ -0,0 +1,111 @@
1
+ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
2
+ import { expect } from "chai";
3
+ import { ethers } from "hardhat";
4
+ import * as hre from "hardhat";
5
+
6
+ import { FHEAntiPatterns, FHEAntiPatterns__factory } from "../types";
7
+ import type { Signers } from "./types";
8
+
9
+ async function deployFixture() {
10
+ const factory = (await ethers.getContractFactory(
11
+ "FHEAntiPatterns"
12
+ )) as FHEAntiPatterns__factory;
13
+ const contract = (await factory.deploy()) as FHEAntiPatterns;
14
+ const contractAddress = await contract.getAddress();
15
+ return { contract, contractAddress };
16
+ }
17
+
18
+ /**
19
+ * @notice Tests for FHE anti-patterns
20
+ * Demonstrates correct patterns for common FHE mistakes
21
+ */
22
+ describe("FHEAntiPatterns", function () {
23
+ let contract: FHEAntiPatterns;
24
+ let contractAddress: string;
25
+ let signers: Signers;
26
+
27
+ before(async function () {
28
+ if (!hre.fhevm.isMock) {
29
+ throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
30
+ }
31
+ const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
32
+ signers = { owner: ethSigners[0], alice: ethSigners[1] };
33
+ });
34
+
35
+ beforeEach(async function () {
36
+ const deployment = await deployFixture();
37
+ contractAddress = deployment.contractAddress;
38
+ contract = deployment.contract;
39
+ });
40
+
41
+ describe("Initialization", function () {
42
+ it("should initialize with encrypted balance and threshold", async function () {
43
+ const fhevm = hre.fhevm;
44
+ const balance = 100;
45
+ const threshold = 50;
46
+
47
+ const input = await fhevm
48
+ .createEncryptedInput(contractAddress, signers.alice.address)
49
+ .add32(balance)
50
+ .add32(threshold)
51
+ .encrypt();
52
+
53
+ await contract
54
+ .connect(signers.alice)
55
+ .initialize(input.handles[0], input.handles[1], input.inputProof);
56
+
57
+ // Contract should now have encrypted values
58
+ // We can't directly verify values without decryption, but tx should succeed
59
+ });
60
+ });
61
+
62
+ describe("Correct Conditional Pattern", function () {
63
+ it("should handle conditional logic with FHE.select", async function () {
64
+ const fhevm = hre.fhevm;
65
+
66
+ // Initialize with balance=100, threshold=50
67
+ const input = await fhevm
68
+ .createEncryptedInput(contractAddress, signers.alice.address)
69
+ .add32(100) // balance
70
+ .add32(50) // threshold
71
+ .encrypt();
72
+
73
+ await contract
74
+ .connect(signers.alice)
75
+ .initialize(input.handles[0], input.handles[1], input.inputProof);
76
+
77
+ // Execute correct conditional - should not revert
78
+ await expect(contract.connect(signers.alice).correctConditional()).to.not
79
+ .be.reverted;
80
+ });
81
+ });
82
+
83
+ describe("Correct Computation Pattern", function () {
84
+ it("should compute with proper permission grants", async function () {
85
+ const fhevm = hre.fhevm;
86
+
87
+ const input = await fhevm
88
+ .createEncryptedInput(contractAddress, signers.alice.address)
89
+ .add32(50) // balance
90
+ .add32(25) // threshold
91
+ .encrypt();
92
+
93
+ await contract
94
+ .connect(signers.alice)
95
+ .initialize(input.handles[0], input.handles[1], input.inputProof);
96
+
97
+ // correctCompute grants proper permissions
98
+ await expect(contract.connect(signers.alice).correctCompute()).to.not.be
99
+ .reverted;
100
+ });
101
+ });
102
+
103
+ describe("Rules Reference", function () {
104
+ it("should return the key rules summary", async function () {
105
+ const rules = await contract.getRules();
106
+ expect(rules).to.include("FHE.select");
107
+ expect(rules).to.include("FHE.allowThis");
108
+ expect(rules).to.include("require/revert");
109
+ });
110
+ });
111
+ });
@@ -0,0 +1,156 @@
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 { FHEHandles, FHEHandles__factory } from "../types";
11
+ import type { Signers } from "./types";
12
+
13
+ async function deployFixture() {
14
+ const factory = (await ethers.getContractFactory(
15
+ "FHEHandles"
16
+ )) as FHEHandles__factory;
17
+ const contract = (await factory.deploy()) as FHEHandles;
18
+ const contractAddress = await contract.getAddress();
19
+ return { contract, contractAddress };
20
+ }
21
+
22
+ /**
23
+ * @notice Tests FHE handle lifecycle and operations
24
+ * Demonstrates handle creation, computation, and immutability
25
+ */
26
+ describe("FHEHandles", function () {
27
+ let contract: FHEHandles;
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("Handle Creation", function () {
46
+ it("should create handle from external encrypted input", async function () {
47
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
48
+ const value = 42;
49
+
50
+ const input = await fhevm
51
+ .createEncryptedInput(contractAddress, signers.alice.address)
52
+ .add32(value)
53
+ .encrypt();
54
+
55
+ await contract
56
+ .connect(signers.alice)
57
+ .createFromExternal(input.handles[0], input.inputProof);
58
+
59
+ const encrypted = await contract.getStoredValue();
60
+ const decrypted = await fhevm.userDecryptEuint(
61
+ FhevmType.euint32,
62
+ encrypted,
63
+ contractAddress,
64
+ signers.alice
65
+ );
66
+ expect(decrypted).to.equal(value);
67
+ });
68
+
69
+ it("should create handle from plaintext constant", async function () {
70
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
71
+ const plaintextValue = 100;
72
+
73
+ await contract.connect(signers.alice).createFromPlaintext(plaintextValue);
74
+
75
+ const encrypted = await contract.getStoredValue();
76
+ const decrypted = await fhevm.userDecryptEuint(
77
+ FhevmType.euint32,
78
+ encrypted,
79
+ contractAddress,
80
+ signers.alice
81
+ );
82
+ expect(decrypted).to.equal(plaintextValue);
83
+ });
84
+ });
85
+
86
+ describe("Handle Computation", function () {
87
+ const initialValue = 50;
88
+
89
+ beforeEach(async function () {
90
+ await contract.connect(signers.alice).createFromPlaintext(initialValue);
91
+ });
92
+
93
+ it("should create new handle when computing", async function () {
94
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
95
+
96
+ // computeNewHandle adds 10 to stored value
97
+ await contract.connect(signers.alice).computeNewHandle();
98
+
99
+ const encrypted = await contract.getComputedValue();
100
+ const decrypted = await fhevm.userDecryptEuint(
101
+ FhevmType.euint32,
102
+ encrypted,
103
+ contractAddress,
104
+ signers.alice
105
+ );
106
+ expect(decrypted).to.equal(initialValue + 10);
107
+ });
108
+
109
+ it("should handle chained operations correctly", async function () {
110
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
111
+
112
+ // chainedOperations: (value + 5) * 2 - 1
113
+ // (50 + 5) * 2 - 1 = 55 * 2 - 1 = 110 - 1 = 109
114
+ await contract.connect(signers.alice).chainedOperations();
115
+
116
+ const encrypted = await contract.getComputedValue();
117
+ const decrypted = await fhevm.userDecryptEuint(
118
+ FhevmType.euint32,
119
+ encrypted,
120
+ contractAddress,
121
+ signers.alice
122
+ );
123
+ expect(decrypted).to.equal(109);
124
+ });
125
+ });
126
+
127
+ describe("Handle Immutability", function () {
128
+ it("should demonstrate handle immutability", async function () {
129
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
130
+ const initialValue = 100;
131
+
132
+ await contract.connect(signers.alice).createFromPlaintext(initialValue);
133
+
134
+ // Get original value before update
135
+ const originalEncrypted = await contract.getStoredValue();
136
+
137
+ // This updates _storedValue to (old + 100)
138
+ await contract.connect(signers.alice).demonstrateImmutability();
139
+
140
+ // Get new value
141
+ const newEncrypted = await contract.getStoredValue();
142
+ const newDecrypted = await fhevm.userDecryptEuint(
143
+ FhevmType.euint32,
144
+ newEncrypted,
145
+ contractAddress,
146
+ signers.alice
147
+ );
148
+
149
+ // New value should be initialValue + 100
150
+ expect(newDecrypted).to.equal(initialValue + 100);
151
+
152
+ // The handles should be different (different encrypted values)
153
+ // Note: In mock mode, we can't directly compare handle values
154
+ });
155
+ });
156
+ });
@@ -0,0 +1,151 @@
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 { FHEInputProof, FHEInputProof__factory } from "../types";
11
+ import type { Signers } from "./types";
12
+
13
+ async function deployFixture() {
14
+ const factory = (await ethers.getContractFactory(
15
+ "FHEInputProof"
16
+ )) as FHEInputProof__factory;
17
+ const contract = (await factory.deploy()) as FHEInputProof;
18
+ const contractAddress = await contract.getAddress();
19
+ return { contract, contractAddress };
20
+ }
21
+
22
+ /**
23
+ * @notice Tests input proof validation patterns
24
+ * Demonstrates single and multiple encrypted inputs with proofs
25
+ */
26
+ describe("FHEInputProof", function () {
27
+ let contract: FHEInputProof;
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("Single Input with Proof", function () {
46
+ it("should accept and store a single encrypted value with valid proof", async function () {
47
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
48
+ const secretValue = 123;
49
+
50
+ // Create encrypted input with proof
51
+ const encryptedInput = await fhevm
52
+ .createEncryptedInput(contractAddress, signers.alice.address)
53
+ .add32(secretValue)
54
+ .encrypt();
55
+
56
+ // Submit to contract - proof is validated in fromExternal
57
+ await contract
58
+ .connect(signers.alice)
59
+ .setSingleValue(encryptedInput.handles[0], encryptedInput.inputProof);
60
+
61
+ // Verify the value was stored correctly
62
+ const encrypted = await contract.getSingleValue();
63
+ const decrypted = await fhevm.userDecryptEuint(
64
+ FhevmType.euint32,
65
+ encrypted,
66
+ contractAddress,
67
+ signers.alice
68
+ );
69
+ expect(decrypted).to.equal(secretValue);
70
+ });
71
+ });
72
+
73
+ describe("Multiple Inputs with Batched Proof", function () {
74
+ it("should accept multiple encrypted values with a single proof", async function () {
75
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
76
+ const valueA = 100;
77
+ const valueB = 200n; // uint64
78
+
79
+ // Create batched encrypted input
80
+ const encryptedInput = await fhevm
81
+ .createEncryptedInput(contractAddress, signers.alice.address)
82
+ .add32(valueA) // index 0 -> handles[0]
83
+ .add64(valueB) // index 1 -> handles[1]
84
+ .encrypt();
85
+
86
+ // Submit both values with single proof
87
+ await contract
88
+ .connect(signers.alice)
89
+ .setMultipleValues(
90
+ encryptedInput.handles[0],
91
+ encryptedInput.handles[1],
92
+ encryptedInput.inputProof
93
+ );
94
+
95
+ // Verify both values
96
+ const encryptedA = await contract.getValueA();
97
+ const decryptedA = await fhevm.userDecryptEuint(
98
+ FhevmType.euint32,
99
+ encryptedA,
100
+ contractAddress,
101
+ signers.alice
102
+ );
103
+ expect(decryptedA).to.equal(valueA);
104
+
105
+ const encryptedB = await contract.getValueB();
106
+ const decryptedB = await fhevm.userDecryptEuint(
107
+ FhevmType.euint64,
108
+ encryptedB,
109
+ contractAddress,
110
+ signers.alice
111
+ );
112
+ expect(decryptedB).to.equal(valueB);
113
+ });
114
+ });
115
+
116
+ describe("Computation with New Input Proof", function () {
117
+ it("should add an encrypted value to stored value", async function () {
118
+ const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
119
+ const initialValue = 100;
120
+ const addendValue = 50;
121
+
122
+ // Set initial value
123
+ const input1 = await fhevm
124
+ .createEncryptedInput(contractAddress, signers.alice.address)
125
+ .add32(initialValue)
126
+ .encrypt();
127
+ await contract
128
+ .connect(signers.alice)
129
+ .setSingleValue(input1.handles[0], input1.inputProof);
130
+
131
+ // Add another encrypted value
132
+ const input2 = await fhevm
133
+ .createEncryptedInput(contractAddress, signers.alice.address)
134
+ .add32(addendValue)
135
+ .encrypt();
136
+ await contract
137
+ .connect(signers.alice)
138
+ .addToValue(input2.handles[0], input2.inputProof);
139
+
140
+ // Verify result
141
+ const encrypted = await contract.getSingleValue();
142
+ const decrypted = await fhevm.userDecryptEuint(
143
+ FhevmType.euint32,
144
+ encrypted,
145
+ contractAddress,
146
+ signers.alice
147
+ );
148
+ expect(decrypted).to.equal(initialValue + addendValue);
149
+ });
150
+ });
151
+ });