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,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"}
|