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,205 @@
1
+ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
2
+ import { ethers, fhevm } from "hardhat";
3
+ import { RockPaperScissors, RockPaperScissors__factory } from "../types";
4
+ import { expect } from "chai";
5
+
6
+ type Signers = {
7
+ deployer: HardhatEthersSigner;
8
+ player1: HardhatEthersSigner;
9
+ player2: HardhatEthersSigner;
10
+ player3: HardhatEthersSigner;
11
+ };
12
+
13
+ async function deployFixture() {
14
+ const factory = (await ethers.getContractFactory(
15
+ "RockPaperScissors"
16
+ )) as RockPaperScissors__factory;
17
+ const rps = (await factory.deploy()) as RockPaperScissors;
18
+ const rpsAddress = await rps.getAddress();
19
+
20
+ return { rps, rpsAddress };
21
+ }
22
+
23
+ /**
24
+ * Rock-Paper-Scissors Tests
25
+ *
26
+ * Tests encrypted move submission and FHE-based winner determination.
27
+ * Demonstrates commit-reveal pattern without trusted third party.
28
+ */
29
+ describe("RockPaperScissors", function () {
30
+ let signers: Signers;
31
+ let rps: RockPaperScissors;
32
+ let rpsAddress: string;
33
+
34
+ before(async function () {
35
+ const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
36
+ signers = {
37
+ deployer: ethSigners[0],
38
+ player1: ethSigners[1],
39
+ player2: ethSigners[2],
40
+ player3: ethSigners[3],
41
+ };
42
+ });
43
+
44
+ beforeEach(async function () {
45
+ if (!fhevm.isMock) {
46
+ console.warn("This test suite cannot run on Sepolia Testnet");
47
+ this.skip();
48
+ }
49
+
50
+ ({ rps, rpsAddress } = await deployFixture());
51
+ });
52
+
53
+ describe("Game Setup", function () {
54
+ it("should initialize with correct state", async function () {
55
+ const state = await rps.getGameState();
56
+ expect(state.p1).to.equal(ethers.ZeroAddress);
57
+ expect(state.p2).to.equal(ethers.ZeroAddress);
58
+ expect(state.currentState).to.equal(0); // WaitingForPlayers
59
+ expect(state.currentWinner).to.equal(ethers.ZeroAddress);
60
+ expect(state.currentGameId).to.equal(1n);
61
+ });
62
+
63
+ it("should allow first player to join", async function () {
64
+ // Create encrypted move (Rock = 0)
65
+ const encryptedMove = await fhevm
66
+ .createEncryptedInput(rpsAddress, signers.player1.address)
67
+ .add8(0)
68
+ .encrypt();
69
+
70
+ await rps
71
+ .connect(signers.player1)
72
+ .play(encryptedMove.handles[0], encryptedMove.inputProof);
73
+
74
+ expect(await rps.player1()).to.equal(signers.player1.address);
75
+ expect(await rps.state()).to.equal(0); // Still waiting for player 2
76
+ });
77
+
78
+ it("should transition to BothMoved when second player joins", async function () {
79
+ // Player 1 plays Rock
80
+ const enc1 = await fhevm
81
+ .createEncryptedInput(rpsAddress, signers.player1.address)
82
+ .add8(0)
83
+ .encrypt();
84
+ await rps.connect(signers.player1).play(enc1.handles[0], enc1.inputProof);
85
+
86
+ // Player 2 plays Paper
87
+ const enc2 = await fhevm
88
+ .createEncryptedInput(rpsAddress, signers.player2.address)
89
+ .add8(1)
90
+ .encrypt();
91
+ await rps.connect(signers.player2).play(enc2.handles[0], enc2.inputProof);
92
+
93
+ expect(await rps.player2()).to.equal(signers.player2.address);
94
+ expect(await rps.state()).to.equal(1); // BothMoved
95
+ });
96
+ });
97
+
98
+ describe("Move Validation", function () {
99
+ it("should prevent same player from playing twice", async function () {
100
+ const enc1 = await fhevm
101
+ .createEncryptedInput(rpsAddress, signers.player1.address)
102
+ .add8(0)
103
+ .encrypt();
104
+ await rps.connect(signers.player1).play(enc1.handles[0], enc1.inputProof);
105
+
106
+ const enc2 = await fhevm
107
+ .createEncryptedInput(rpsAddress, signers.player1.address)
108
+ .add8(1)
109
+ .encrypt();
110
+
111
+ await expect(
112
+ rps.connect(signers.player1).play(enc2.handles[0], enc2.inputProof)
113
+ ).to.be.revertedWith("Already in this game");
114
+ });
115
+
116
+ it("should prevent third player from joining after game is full", async function () {
117
+ // Player 1 plays
118
+ const enc1 = await fhevm
119
+ .createEncryptedInput(rpsAddress, signers.player1.address)
120
+ .add8(0)
121
+ .encrypt();
122
+ await rps.connect(signers.player1).play(enc1.handles[0], enc1.inputProof);
123
+
124
+ // Player 2 plays
125
+ const enc2 = await fhevm
126
+ .createEncryptedInput(rpsAddress, signers.player2.address)
127
+ .add8(1)
128
+ .encrypt();
129
+ await rps.connect(signers.player2).play(enc2.handles[0], enc2.inputProof);
130
+
131
+ // Player 3 tries to play
132
+ const enc3 = await fhevm
133
+ .createEncryptedInput(rpsAddress, signers.player3.address)
134
+ .add8(2)
135
+ .encrypt();
136
+
137
+ await expect(
138
+ rps.connect(signers.player3).play(enc3.handles[0], enc3.inputProof)
139
+ ).to.be.revertedWith("Game not accepting moves");
140
+ });
141
+ });
142
+
143
+ describe("Winner Determination", function () {
144
+ it("should compute winner via FHE after both players move", async function () {
145
+ // Player 1: Rock (0)
146
+ const enc1 = await fhevm
147
+ .createEncryptedInput(rpsAddress, signers.player1.address)
148
+ .add8(0)
149
+ .encrypt();
150
+ await rps.connect(signers.player1).play(enc1.handles[0], enc1.inputProof);
151
+
152
+ // Player 2: Paper (1) - Paper beats Rock
153
+ const enc2 = await fhevm
154
+ .createEncryptedInput(rpsAddress, signers.player2.address)
155
+ .add8(1)
156
+ .encrypt();
157
+ await rps.connect(signers.player2).play(enc2.handles[0], enc2.inputProof);
158
+
159
+ // Determine winner
160
+ await expect(rps.determineWinner()).to.emit(rps, "GameResult");
161
+
162
+ expect(await rps.state()).to.equal(2); // Revealed
163
+ });
164
+
165
+ it("should prevent determineWinner before both players move", async function () {
166
+ await expect(rps.determineWinner()).to.be.revertedWith(
167
+ "Not ready to determine winner"
168
+ );
169
+ });
170
+ });
171
+
172
+ describe("Game Reset", function () {
173
+ it("should allow reset after game is revealed", async function () {
174
+ // Play a full game
175
+ const enc1 = await fhevm
176
+ .createEncryptedInput(rpsAddress, signers.player1.address)
177
+ .add8(0)
178
+ .encrypt();
179
+ await rps.connect(signers.player1).play(enc1.handles[0], enc1.inputProof);
180
+
181
+ const enc2 = await fhevm
182
+ .createEncryptedInput(rpsAddress, signers.player2.address)
183
+ .add8(1)
184
+ .encrypt();
185
+ await rps.connect(signers.player2).play(enc2.handles[0], enc2.inputProof);
186
+
187
+ await rps.determineWinner();
188
+
189
+ // Reset game
190
+ await rps.resetGame();
191
+
192
+ const state = await rps.getGameState();
193
+ expect(state.p1).to.equal(ethers.ZeroAddress);
194
+ expect(state.p2).to.equal(ethers.ZeroAddress);
195
+ expect(state.currentState).to.equal(0); // WaitingForPlayers
196
+ expect(state.currentGameId).to.equal(2n); // Incremented
197
+ });
198
+
199
+ it("should prevent reset before game is finished", async function () {
200
+ await expect(rps.resetGame()).to.be.revertedWith(
201
+ "Current game not finished"
202
+ );
203
+ });
204
+ });
205
+ });
@@ -0,0 +1,142 @@
1
+ import { expect } from "chai";
2
+ import { ethers, fhevm } from "hardhat";
3
+
4
+ describe("ERC7984Example", function () {
5
+ let token: any;
6
+ let owner: any;
7
+ let recipient: any;
8
+ let other: any;
9
+
10
+ const INITIAL_AMOUNT = 1000;
11
+ const TRANSFER_AMOUNT = 100;
12
+
13
+ beforeEach(async function () {
14
+ [owner, recipient, other] = await ethers.getSigners();
15
+
16
+ // Deploy ERC7984 contract
17
+ token = await ethers.deployContract("ERC7984Example", [
18
+ owner.address,
19
+ INITIAL_AMOUNT,
20
+ "Confidential Token",
21
+ "CTKN",
22
+ "https://example.com/token",
23
+ ]);
24
+ });
25
+
26
+ describe("Initialization", function () {
27
+ it("should set the correct name", async function () {
28
+ expect(await token.name()).to.equal("Confidential Token");
29
+ });
30
+
31
+ it("should set the correct symbol", async function () {
32
+ expect(await token.symbol()).to.equal("CTKN");
33
+ });
34
+
35
+ it("should set the correct contract URI", async function () {
36
+ expect(await token.contractURI()).to.equal("https://example.com/token");
37
+ });
38
+
39
+ it("should mint initial amount to owner", async function () {
40
+ const balanceHandle = await token.confidentialBalanceOf(owner.address);
41
+ expect(balanceHandle).to.not.be.undefined;
42
+ });
43
+ });
44
+
45
+ describe("Transfer Process", function () {
46
+ it("should transfer tokens from owner to recipient", async function () {
47
+ const encryptedInput = await fhevm
48
+ .createEncryptedInput(await token.getAddress(), owner.address)
49
+ .add64(TRANSFER_AMOUNT)
50
+ .encrypt();
51
+
52
+ await expect(
53
+ token
54
+ .connect(owner)
55
+ ["confidentialTransfer(address,bytes32,bytes)"](
56
+ recipient.address,
57
+ encryptedInput.handles[0],
58
+ encryptedInput.inputProof
59
+ )
60
+ ).to.not.be.reverted;
61
+
62
+ const recipientBalanceHandle = await token.confidentialBalanceOf(
63
+ recipient.address
64
+ );
65
+ const ownerBalanceHandle = await token.confidentialBalanceOf(
66
+ owner.address
67
+ );
68
+ expect(recipientBalanceHandle).to.not.be.undefined;
69
+ expect(ownerBalanceHandle).to.not.be.undefined;
70
+ });
71
+
72
+ it("should allow recipient to transfer received tokens", async function () {
73
+ // First transfer from owner to recipient
74
+ const encryptedInput1 = await fhevm
75
+ .createEncryptedInput(await token.getAddress(), owner.address)
76
+ .add64(TRANSFER_AMOUNT)
77
+ .encrypt();
78
+
79
+ await token
80
+ .connect(owner)
81
+ ["confidentialTransfer(address,bytes32,bytes)"](
82
+ recipient.address,
83
+ encryptedInput1.handles[0],
84
+ encryptedInput1.inputProof
85
+ );
86
+
87
+ // Second transfer from recipient to other
88
+ const encryptedInput2 = await fhevm
89
+ .createEncryptedInput(await token.getAddress(), recipient.address)
90
+ .add64(50)
91
+ .encrypt();
92
+
93
+ await expect(
94
+ token
95
+ .connect(recipient)
96
+ ["confidentialTransfer(address,bytes32,bytes)"](
97
+ other.address,
98
+ encryptedInput2.handles[0],
99
+ encryptedInput2.inputProof
100
+ )
101
+ ).to.not.be.reverted;
102
+ });
103
+
104
+ it("should revert when sender has zero balance", async function () {
105
+ const encryptedInput = await fhevm
106
+ .createEncryptedInput(await token.getAddress(), recipient.address)
107
+ .add64(100)
108
+ .encrypt();
109
+
110
+ await expect(
111
+ token
112
+ .connect(recipient)
113
+ ["confidentialTransfer(address,bytes32,bytes)"](
114
+ other.address,
115
+ encryptedInput.handles[0],
116
+ encryptedInput.inputProof
117
+ )
118
+ )
119
+ .to.be.revertedWithCustomError(token, "ERC7984ZeroBalance")
120
+ .withArgs(recipient.address);
121
+ });
122
+
123
+ it("should revert when transferring to zero address", async function () {
124
+ const encryptedInput = await fhevm
125
+ .createEncryptedInput(await token.getAddress(), owner.address)
126
+ .add64(TRANSFER_AMOUNT)
127
+ .encrypt();
128
+
129
+ await expect(
130
+ token
131
+ .connect(owner)
132
+ ["confidentialTransfer(address,bytes32,bytes)"](
133
+ ethers.ZeroAddress,
134
+ encryptedInput.handles[0],
135
+ encryptedInput.inputProof
136
+ )
137
+ )
138
+ .to.be.revertedWithCustomError(token, "ERC7984InvalidReceiver")
139
+ .withArgs(ethers.ZeroAddress);
140
+ });
141
+ });
142
+ });
@@ -0,0 +1,71 @@
1
+ import { expect } from "chai";
2
+ import { ethers, fhevm } from "hardhat";
3
+
4
+ describe("ERC7984ERC20Wrapper", function () {
5
+ let wrapper: any;
6
+ let erc20Mock: any;
7
+ let owner: any;
8
+ let user: any;
9
+
10
+ const WRAP_AMOUNT = 1000;
11
+
12
+ beforeEach(async function () {
13
+ [owner, user] = await ethers.getSigners();
14
+
15
+ // Deploy mock ERC20
16
+ erc20Mock = await ethers.deployContract("ERC20Mock", [
17
+ "Mock USDC",
18
+ "USDC",
19
+ 6,
20
+ ]);
21
+
22
+ // Mint ERC20 to user
23
+ await erc20Mock.mint(user.address, WRAP_AMOUNT * 2);
24
+
25
+ // Deploy wrapper
26
+ wrapper = await ethers.deployContract("ERC7984ERC20WrapperExample", [
27
+ await erc20Mock.getAddress(),
28
+ "Wrapped USDC",
29
+ "wUSDC",
30
+ "https://example.com/wrapped",
31
+ ]);
32
+ });
33
+
34
+ describe("Initialization", function () {
35
+ it("should set the correct underlying token", async function () {
36
+ expect(await wrapper.underlying()).to.equal(await erc20Mock.getAddress());
37
+ });
38
+
39
+ it("should set the correct name and symbol", async function () {
40
+ expect(await wrapper.name()).to.equal("Wrapped USDC");
41
+ expect(await wrapper.symbol()).to.equal("wUSDC");
42
+ });
43
+ });
44
+
45
+ describe("Wrapping", function () {
46
+ it("should wrap ERC20 tokens", async function () {
47
+ // Approve wrapper
48
+ await erc20Mock
49
+ .connect(user)
50
+ .approve(await wrapper.getAddress(), WRAP_AMOUNT);
51
+
52
+ // Wrap tokens
53
+ await expect(wrapper.connect(user).wrap(user.address, WRAP_AMOUNT)).to.not
54
+ .be.reverted;
55
+
56
+ // Check ERC20 was transferred
57
+ expect(await erc20Mock.balanceOf(await wrapper.getAddress())).to.equal(
58
+ WRAP_AMOUNT
59
+ );
60
+
61
+ // Check confidential balance exists
62
+ const balanceHandle = await wrapper.confidentialBalanceOf(user.address);
63
+ expect(balanceHandle).to.not.be.undefined;
64
+ });
65
+
66
+ it("should fail wrapping without approval", async function () {
67
+ await expect(wrapper.connect(user).wrap(user.address, WRAP_AMOUNT)).to.be
68
+ .reverted;
69
+ });
70
+ });
71
+ });
@@ -0,0 +1,76 @@
1
+ import { expect } from "chai";
2
+ import { ethers, fhevm } from "hardhat";
3
+
4
+ describe("SwapERC7984ToERC20", function () {
5
+ let swap: any;
6
+ let erc7984: any;
7
+ let erc20Mock: any;
8
+ let owner: any;
9
+ let user: any;
10
+
11
+ const INITIAL_ERC20 = 10000;
12
+
13
+ beforeEach(async function () {
14
+ [owner, user] = await ethers.getSigners();
15
+
16
+ // Deploy ERC7984 mock
17
+ erc7984 = await ethers.deployContract("ERC7984Example", [
18
+ owner.address,
19
+ 10000,
20
+ "Confidential Token",
21
+ "CTKN",
22
+ "https://example.com/token",
23
+ ]);
24
+
25
+ // Deploy ERC20 mock
26
+ erc20Mock = await ethers.deployContract("ERC20Mock", [
27
+ "Mock USDC",
28
+ "USDC",
29
+ 6,
30
+ ]);
31
+
32
+ // Deploy swap contract
33
+ swap = await ethers.deployContract("SwapERC7984ToERC20Example", [
34
+ await erc7984.getAddress(),
35
+ await erc20Mock.getAddress(),
36
+ ]);
37
+
38
+ // Fund swap contract with ERC20
39
+ await erc20Mock.mint(await swap.getAddress(), INITIAL_ERC20);
40
+
41
+ // Transfer some ERC7984 to user
42
+ const encryptedInput = await fhevm
43
+ .createEncryptedInput(await erc7984.getAddress(), owner.address)
44
+ .add64(1000)
45
+ .encrypt();
46
+
47
+ await erc7984
48
+ .connect(owner)
49
+ ["confidentialTransfer(address,bytes32,bytes)"](
50
+ user.address,
51
+ encryptedInput.handles[0],
52
+ encryptedInput.inputProof
53
+ );
54
+
55
+ // Set swap contract as operator for user
56
+ const maxTimestamp = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
57
+ await erc7984
58
+ .connect(user)
59
+ .setOperator(await swap.getAddress(), maxTimestamp);
60
+ });
61
+
62
+ describe("Swap Initiation", function () {
63
+ it("should initiate a swap", async function () {
64
+ const encryptedInput = await fhevm
65
+ .createEncryptedInput(await swap.getAddress(), user.address)
66
+ .add64(100)
67
+ .encrypt();
68
+
69
+ await expect(
70
+ swap
71
+ .connect(user)
72
+ .initiateSwap(encryptedInput.handles[0], encryptedInput.inputProof)
73
+ ).to.emit(swap, "SwapInitiated");
74
+ });
75
+ });
76
+ });
@@ -0,0 +1,113 @@
1
+ import { expect } from "chai";
2
+ import { ethers, fhevm } from "hardhat";
3
+
4
+ describe("SwapERC7984ToERC7984", function () {
5
+ let swap: any;
6
+ let tokenA: any;
7
+ let tokenB: any;
8
+ let owner: any;
9
+ let user: any;
10
+
11
+ beforeEach(async function () {
12
+ [owner, user] = await ethers.getSigners();
13
+
14
+ // Deploy two ERC7984 tokens
15
+ tokenA = await ethers.deployContract("ERC7984Example", [
16
+ owner.address,
17
+ 10000,
18
+ "Token A",
19
+ "TKA",
20
+ "https://example.com/a",
21
+ ]);
22
+
23
+ tokenB = await ethers.deployContract("ERC7984Example", [
24
+ owner.address,
25
+ 10000,
26
+ "Token B",
27
+ "TKB",
28
+ "https://example.com/b",
29
+ ]);
30
+
31
+ // Deploy swap contract
32
+ swap = await ethers.deployContract("SwapERC7984ToERC7984Example", []);
33
+
34
+ // Transfer tokenA to user
35
+ const encryptedInputA = await fhevm
36
+ .createEncryptedInput(await tokenA.getAddress(), owner.address)
37
+ .add64(1000)
38
+ .encrypt();
39
+
40
+ await tokenA
41
+ .connect(owner)
42
+ ["confidentialTransfer(address,bytes32,bytes)"](
43
+ user.address,
44
+ encryptedInputA.handles[0],
45
+ encryptedInputA.inputProof
46
+ );
47
+
48
+ // Transfer tokenB to swap contract
49
+ const encryptedInputB = await fhevm
50
+ .createEncryptedInput(await tokenB.getAddress(), owner.address)
51
+ .add64(1000)
52
+ .encrypt();
53
+
54
+ await tokenB
55
+ .connect(owner)
56
+ ["confidentialTransfer(address,bytes32,bytes)"](
57
+ await swap.getAddress(),
58
+ encryptedInputB.handles[0],
59
+ encryptedInputB.inputProof
60
+ );
61
+
62
+ // Set swap as operator for user's tokenA
63
+ const maxTimestamp = Math.floor(Date.now() / 1000) + 3600;
64
+ await tokenA
65
+ .connect(user)
66
+ .setOperator(await swap.getAddress(), maxTimestamp);
67
+ });
68
+
69
+ describe("Swap", function () {
70
+ it("should swap tokenA for tokenB", async function () {
71
+ const encryptedInput = await fhevm
72
+ .createEncryptedInput(await swap.getAddress(), user.address)
73
+ .add64(100)
74
+ .encrypt();
75
+
76
+ await expect(
77
+ swap
78
+ .connect(user)
79
+ .swapConfidentialForConfidential(
80
+ await tokenA.getAddress(),
81
+ await tokenB.getAddress(),
82
+ encryptedInput.handles[0],
83
+ encryptedInput.inputProof
84
+ )
85
+ ).to.not.be.reverted;
86
+
87
+ // User should have tokenB balance
88
+ const balanceB = await tokenB.confidentialBalanceOf(user.address);
89
+ expect(balanceB).to.not.be.undefined;
90
+ });
91
+
92
+ it("should fail without operator authorization", async function () {
93
+ // Remove operator
94
+ await tokenA.connect(user).setOperator(await swap.getAddress(), 0);
95
+
96
+ const encryptedInput = await fhevm
97
+ .createEncryptedInput(await tokenA.getAddress(), user.address)
98
+ .add64(100)
99
+ .encrypt();
100
+
101
+ await expect(
102
+ swap
103
+ .connect(user)
104
+ .swapConfidentialForConfidential(
105
+ await tokenA.getAddress(),
106
+ await tokenB.getAddress(),
107
+ encryptedInput.handles[0],
108
+ encryptedInput.inputProof
109
+ )
110
+ ).to.be.reverted;
111
+ });
112
+ });
113
+ });
@@ -0,0 +1,89 @@
1
+ import { expect } from "chai";
2
+ import { ethers, fhevm } from "hardhat";
3
+ import { time } from "@nomicfoundation/hardhat-network-helpers";
4
+
5
+ describe("VestingWallet", function () {
6
+ let vestingWallet: any;
7
+ let token: any;
8
+ let owner: any;
9
+ let beneficiary: any;
10
+
11
+ const VESTING_AMOUNT = 1000;
12
+ const VESTING_DURATION = 60 * 60; // 1 hour in seconds
13
+
14
+ beforeEach(async function () {
15
+ [owner, beneficiary] = await ethers.getSigners();
16
+
17
+ // Deploy ERC7984 token
18
+ token = await ethers.deployContract("ERC7984Example", [
19
+ owner.address,
20
+ 10000,
21
+ "Vesting Token",
22
+ "VTK",
23
+ "https://example.com/vesting",
24
+ ]);
25
+
26
+ // Get current time and set vesting to start in 1 minute
27
+ const currentTime = await time.latest();
28
+ const startTime = currentTime + 60;
29
+
30
+ // Deploy vesting wallet
31
+ vestingWallet = await ethers.deployContract("VestingWalletExample", [
32
+ beneficiary.address,
33
+ startTime,
34
+ VESTING_DURATION,
35
+ ]);
36
+
37
+ // Transfer tokens to vesting wallet
38
+ const encryptedInput = await fhevm
39
+ .createEncryptedInput(await token.getAddress(), owner.address)
40
+ .add64(VESTING_AMOUNT)
41
+ .encrypt();
42
+
43
+ await token
44
+ .connect(owner)
45
+ ["confidentialTransfer(address,bytes32,bytes)"](
46
+ await vestingWallet.getAddress(),
47
+ encryptedInput.handles[0],
48
+ encryptedInput.inputProof
49
+ );
50
+ });
51
+
52
+ describe("Initialization", function () {
53
+ it("should set the correct beneficiary", async function () {
54
+ expect(await vestingWallet.owner()).to.equal(beneficiary.address);
55
+ });
56
+
57
+ it("should set the correct duration", async function () {
58
+ expect(await vestingWallet.duration()).to.equal(VESTING_DURATION);
59
+ });
60
+ });
61
+
62
+ describe("Vesting Schedule", function () {
63
+ it("should not release tokens before vesting starts", async function () {
64
+ await expect(
65
+ vestingWallet.connect(beneficiary).release(await token.getAddress())
66
+ ).to.not.be.reverted;
67
+ });
68
+
69
+ it("should release after vesting ends", async function () {
70
+ const endTime = await vestingWallet.end();
71
+ await time.increaseTo(endTime + BigInt(100));
72
+
73
+ await expect(
74
+ vestingWallet.connect(beneficiary).release(await token.getAddress())
75
+ ).to.emit(vestingWallet, "VestingWalletConfidentialTokenReleased");
76
+ });
77
+
78
+ it("should release partial tokens at midpoint", async function () {
79
+ const startTime = await vestingWallet.start();
80
+ const midpoint = Number(startTime) + VESTING_DURATION / 2;
81
+
82
+ await time.increaseTo(midpoint);
83
+
84
+ await expect(
85
+ vestingWallet.connect(beneficiary).release(await token.getAddress())
86
+ ).to.not.be.reverted;
87
+ });
88
+ });
89
+ });
@@ -1 +0,0 @@
1
- {"version":3,"file":"add-mode.d.ts","sourceRoot":"","sources":["../../scripts/add-mode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkRH;;GAEG;AACH,wBAAsB,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ElE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../scripts/builders.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA0DH;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CA8Cf;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAiEf;AAMD;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAuEf"}