@0xflydev/labz 1.0.22 → 1.0.24

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/dist/index.js CHANGED
@@ -1538,7 +1538,7 @@ Base: ${import_chalk7.default.cyan(base.name)} - ${base.description}`));
1538
1538
  const spinner = (0, import_ora5.default)("Generating project...").start();
1539
1539
  try {
1540
1540
  const result = (0, import_composer.merge)(base, modules, {
1541
- projectName: finalProjectName,
1541
+ projectName: path4.basename(finalProjectName),
1542
1542
  typeParams: options.type ? { COUNTER_TYPE: options.type, EXTERNAL_TYPE: `external${options.type.charAt(0).toUpperCase()}${options.type.slice(1)}` } : void 0
1543
1543
  });
1544
1544
  if (!result.success) {
@@ -1651,8 +1651,11 @@ function copyDirSync(src, dest) {
1651
1651
  }
1652
1652
  }
1653
1653
  }
1654
+ function toPascalCase(name) {
1655
+ return name.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1656
+ }
1654
1657
  function generateBuildReadme(projectName, base, result) {
1655
- const contractName = result.stats.baseTemplate.charAt(0).toUpperCase() + result.stats.baseTemplate.slice(1);
1658
+ const contractName = toPascalCase(result.stats.baseTemplate);
1656
1659
  const modules = result.stats.modulesApplied || [];
1657
1660
  const lines = [];
1658
1661
  lines.push(`# ${projectName}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xflydev/labz",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "Lab-Z CLI - Generate composable FHEVM smart contracts",
5
5
  "author": "0xflydev",
6
6
  "license": "MIT",
@@ -1,38 +1,39 @@
1
- import { ethers } from "hardhat";
1
+ import hre from "hardhat";
2
2
 
3
3
  async function main() {
4
- const itemDescription = "Rare Digital Collectible";
5
- const durationSeconds = 86400; // 1 day
6
-
7
4
  console.log("Deploying {{CONTRACT_NAME}}...");
8
- console.log(" Item:", itemDescription);
9
- console.log(" Duration:", durationSeconds / 3600, "hours");
10
-
11
- const AuctionFactory = await ethers.getContractFactory("{{CONTRACT_NAME}}");
12
- const auction = await AuctionFactory.deploy(itemDescription, durationSeconds);
13
-
14
- await auction.waitForDeployment();
15
-
16
- const address = await auction.getAddress();
17
- console.log("\n{{CONTRACT_NAME}} deployed to:", address);
18
-
19
- console.log("\nAuction Lifecycle:");
20
- console.log(" 1. Bid: bid(encryptedAmount, inputProof)");
21
- console.log(" - Bids are encrypted and sealed");
22
- console.log(" - No one can see competitors' bids");
23
- console.log(" 2. Wait: Auction runs until endTime");
24
- console.log(" 3. Request Reveal: requestWinnerReveal()");
25
- console.log(" - Marks winner for decryption");
26
- console.log(" 4. Finalize: finalizeWinner(winnerAddress, winningBid, proof)");
27
- console.log(" - Verifies decryption proof");
28
- console.log(" - Announces winner");
29
- console.log("\nPrivacy Guarantees:");
30
- console.log(" - Bids encrypted until reveal");
31
- console.log(" - Losing bids never disclosed");
32
- console.log(" - Fair competition guaranteed");
5
+
6
+ const Factory = await hre.ethers.getContractFactory("{{CONTRACT_NAME}}");
7
+ const contract = await Factory.deploy();
8
+
9
+ await contract.waitForDeployment();
10
+
11
+ const address = await contract.getAddress();
12
+ console.log(`{{CONTRACT_NAME}} deployed to: ${address}`);
13
+
14
+ // Verify on explorer if not local
15
+ if (hre.network.name !== "hardhat" && hre.network.name !== "localhost") {
16
+ console.log("Waiting for block confirmations...");
17
+ await contract.deploymentTransaction()?.wait(5);
18
+
19
+ console.log("Verifying contract...");
20
+ try {
21
+ await hre.run("verify:verify", {
22
+ address: address,
23
+ constructorArguments: [],
24
+ });
25
+ console.log("Contract verified!");
26
+ } catch (error) {
27
+ console.log("Verification failed:", error);
28
+ }
29
+ }
30
+
31
+ return address;
33
32
  }
34
33
 
35
- main().catch((error) => {
36
- console.error(error);
37
- process.exitCode = 1;
38
- });
34
+ main()
35
+ .then(() => process.exit(0))
36
+ .catch((error) => {
37
+ console.error(error);
38
+ process.exit(1);
39
+ });
@@ -1,71 +1,65 @@
1
1
  import { expect } from "chai";
2
- import hre from "hardhat";
3
- import { createInstance, type FhevmInstance } from "@fhevm/sdk";
2
+ import { ethers, fhevm } from "hardhat";
3
+ import { FhevmType } from "@fhevm/hardhat-plugin";
4
+ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
4
5
 
5
6
  describe("{{CONTRACT_NAME}}", function () {
6
7
  let contract: any;
7
8
  let contractAddress: string;
8
- let fhevm: FhevmInstance;
9
- let signers: any[];
9
+ let signers: HardhatEthersSigner[];
10
10
 
11
11
  before(async function () {
12
- // Get signers
13
- signers = await hre.ethers.getSigners();
12
+ signers = await ethers.getSigners();
13
+ });
14
14
 
15
- // Deploy contract
16
- const Factory = await hre.ethers.getContractFactory("{{CONTRACT_NAME}}");
15
+ beforeEach(async function () {
16
+ const Factory = await ethers.getContractFactory("{{CONTRACT_NAME}}");
17
17
  contract = await Factory.deploy();
18
18
  await contract.waitForDeployment();
19
19
  contractAddress = await contract.getAddress();
20
-
21
- // Create FHEVM instance
22
- fhevm = await createInstance({
23
- networkUrl: hre.network.config.url || "http://localhost:8545",
24
- gatewayUrl: "http://localhost:7077",
25
- });
26
20
  });
27
21
 
28
22
  describe("Increment", function () {
29
23
  it("should increment the counter with encrypted value", async function () {
30
24
  const [signer] = signers;
31
-
32
- // Encrypt the value to add
33
25
  const valueToAdd = 5;
34
- const encrypted = await fhevm.encrypt32(valueToAdd);
35
26
 
36
- // Call increment
37
- const tx = await contract.connect(signer).increment(
38
- encrypted.handles[0],
39
- encrypted.inputProof
40
- );
27
+ // Create encrypted input using hardhat plugin
28
+ const encryptedInput = await fhevm
29
+ .createEncryptedInput(contractAddress, signer.address)
30
+ .add32(valueToAdd)
31
+ .encrypt();
32
+
33
+ // Call increment with encrypted value and proof
34
+ const tx = await contract
35
+ .connect(signer)
36
+ .increment(encryptedInput.handles[0], encryptedInput.inputProof);
41
37
  await tx.wait();
42
38
 
43
39
  // Get the encrypted count
44
40
  const encryptedCount = await contract.getCount();
45
-
46
- // Request decryption (in real scenario, would use gateway)
47
- expect(encryptedCount).to.not.equal(0n);
41
+ expect(encryptedCount).to.not.equal(ethers.ZeroHash);
48
42
  });
49
43
 
50
44
  it("should allow multiple increments", async function () {
51
45
  const [signer] = signers;
52
46
 
53
47
  // First increment
54
- const encrypted1 = await fhevm.encrypt32(10);
55
- await contract.connect(signer).increment(
56
- encrypted1.handles[0],
57
- encrypted1.inputProof
58
- );
48
+ const encrypted1 = await fhevm
49
+ .createEncryptedInput(contractAddress, signer.address)
50
+ .add32(10)
51
+ .encrypt();
52
+ await contract.connect(signer).increment(encrypted1.handles[0], encrypted1.inputProof);
59
53
 
60
54
  // Second increment
61
- const encrypted2 = await fhevm.encrypt32(20);
62
- await contract.connect(signer).increment(
63
- encrypted2.handles[0],
64
- encrypted2.inputProof
65
- );
55
+ const encrypted2 = await fhevm
56
+ .createEncryptedInput(contractAddress, signer.address)
57
+ .add32(20)
58
+ .encrypt();
59
+ await contract.connect(signer).increment(encrypted2.handles[0], encrypted2.inputProof);
66
60
 
67
61
  const encryptedCount = await contract.getCount();
68
- expect(encryptedCount).to.not.equal(0n);
62
+ expect(encryptedCount).to.not.equal(ethers.ZeroHash);
69
63
  });
70
64
  });
71
65
 
@@ -74,21 +68,21 @@ describe("{{CONTRACT_NAME}}", function () {
74
68
  const [signer] = signers;
75
69
 
76
70
  // First add some value
77
- const addValue = await fhevm.encrypt32(100);
78
- await contract.connect(signer).increment(
79
- addValue.handles[0],
80
- addValue.inputProof
81
- );
71
+ const addValue = await fhevm
72
+ .createEncryptedInput(contractAddress, signer.address)
73
+ .add32(100)
74
+ .encrypt();
75
+ await contract.connect(signer).increment(addValue.handles[0], addValue.inputProof);
82
76
 
83
77
  // Then decrement
84
- const subValue = await fhevm.encrypt32(30);
85
- await contract.connect(signer).decrement(
86
- subValue.handles[0],
87
- subValue.inputProof
88
- );
78
+ const subValue = await fhevm
79
+ .createEncryptedInput(contractAddress, signer.address)
80
+ .add32(30)
81
+ .encrypt();
82
+ await contract.connect(signer).decrement(subValue.handles[0], subValue.inputProof);
89
83
 
90
84
  const encryptedCount = await contract.getCount();
91
- expect(encryptedCount).to.not.equal(0n);
85
+ expect(encryptedCount).to.not.equal(ethers.ZeroHash);
92
86
  });
93
87
  });
94
88
 
@@ -96,15 +90,15 @@ describe("{{CONTRACT_NAME}}", function () {
96
90
  it("should grant access to caller after increment", async function () {
97
91
  const [signer] = signers;
98
92
 
99
- const encrypted = await fhevm.encrypt32(1);
100
- await contract.connect(signer).increment(
101
- encrypted.handles[0],
102
- encrypted.inputProof
103
- );
93
+ const encrypted = await fhevm
94
+ .createEncryptedInput(contractAddress, signer.address)
95
+ .add32(1)
96
+ .encrypt();
97
+ await contract.connect(signer).increment(encrypted.handles[0], encrypted.inputProof);
104
98
 
105
99
  // Caller should be able to get the count
106
100
  const count = await contract.connect(signer).getCount();
107
- expect(count).to.not.equal(0n);
101
+ expect(count).to.not.equal(ethers.ZeroHash);
108
102
  });
109
103
  });
110
104
 
@@ -1,5 +1,6 @@
1
1
  import { expect } from "chai";
2
2
  import { ethers } from "hardhat";
3
+ import { time } from "@nomicfoundation/hardhat-network-helpers";
3
4
  import { {{CONTRACT_NAME}} } from "../typechain-types";
4
5
  import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
5
6
 
@@ -72,13 +73,15 @@ describe("{{CONTRACT_NAME}}", function () {
72
73
  let deadline: number;
73
74
 
74
75
  beforeEach(async function () {
75
- deadline = Math.floor(Date.now() / 1000) + 1; // 1 second from now
76
+ // Get current block timestamp and add 60 seconds
77
+ const block = await ethers.provider.getBlock("latest");
78
+ deadline = block!.timestamp + 60;
76
79
  const tx = await market.createMarket("Test question?", deadline);
77
80
  await tx.wait();
78
81
  marketId = 0;
79
82
 
80
- // Wait for deadline to pass
81
- await new Promise(resolve => setTimeout(resolve, 2000));
83
+ // Advance blockchain time past deadline
84
+ await time.increaseTo(deadline + 1);
82
85
  });
83
86
 
84
87
  it("Should allow oracle to resolve market", async function () {
@@ -140,11 +143,12 @@ describe("{{CONTRACT_NAME}}", function () {
140
143
  it("Should handle complete market lifecycle", async function () {
141
144
  // 1. Create market
142
145
  const question = "Will BTC hit $100k?";
143
- const deadline = Math.floor(Date.now() / 1000) + 1;
146
+ const block = await ethers.provider.getBlock("latest");
147
+ const deadline = block!.timestamp + 60;
144
148
  await market.createMarket(question, deadline);
145
149
 
146
- // 2. Wait for deadline
147
- await new Promise(resolve => setTimeout(resolve, 2000));
150
+ // 2. Advance time past deadline
151
+ await time.increaseTo(deadline + 1);
148
152
 
149
153
  // 3. Resolve market
150
154
  await market.resolveMarket(0, true);
@@ -1,32 +1,39 @@
1
- import { ethers } from "hardhat";
1
+ import hre from "hardhat";
2
2
 
3
3
  async function main() {
4
- const tokenName = "Confidential Token";
5
- const tokenSymbol = "CFHE";
6
-
7
4
  console.log("Deploying {{CONTRACT_NAME}}...");
8
- console.log(" Name:", tokenName);
9
- console.log(" Symbol:", tokenSymbol);
10
5
 
11
- const TokenFactory = await ethers.getContractFactory("{{CONTRACT_NAME}}");
12
- const token = await TokenFactory.deploy(tokenName, tokenSymbol);
6
+ const Factory = await hre.ethers.getContractFactory("{{CONTRACT_NAME}}");
7
+ const contract = await Factory.deploy();
8
+
9
+ await contract.waitForDeployment();
10
+
11
+ const address = await contract.getAddress();
12
+ console.log(`{{CONTRACT_NAME}} deployed to: ${address}`);
13
13
 
14
- await token.waitForDeployment();
14
+ // Verify on explorer if not local
15
+ if (hre.network.name !== "hardhat" && hre.network.name !== "localhost") {
16
+ console.log("Waiting for block confirmations...");
17
+ await contract.deploymentTransaction()?.wait(5);
15
18
 
16
- const address = await token.getAddress();
17
- console.log("\n{{CONTRACT_NAME}} deployed to:", address);
19
+ console.log("Verifying contract...");
20
+ try {
21
+ await hre.run("verify:verify", {
22
+ address: address,
23
+ constructorArguments: [],
24
+ });
25
+ console.log("Contract verified!");
26
+ } catch (error) {
27
+ console.log("Verification failed:", error);
28
+ }
29
+ }
18
30
 
19
- console.log("\nToken Operations:");
20
- console.log(" - mint(to, encryptedAmount): Mint tokens (owner only)");
21
- console.log(" - transfer(to, encryptedAmount): Transfer tokens");
22
- console.log(" - balanceOf(account): Get encrypted balance handle");
23
- console.log("\nPrivacy Guarantees:");
24
- console.log(" - Balances are encrypted");
25
- console.log(" - Transfer amounts are private");
26
- console.log(" - Only balance owner can decrypt");
31
+ return address;
27
32
  }
28
33
 
29
- main().catch((error) => {
30
- console.error(error);
31
- process.exitCode = 1;
32
- });
34
+ main()
35
+ .then(() => process.exit(0))
36
+ .catch((error) => {
37
+ console.error(error);
38
+ process.exit(1);
39
+ });
@@ -0,0 +1,187 @@
1
+ import { expect } from "chai";
2
+ import { ethers, fhevm } from "hardhat";
3
+ import { FhevmType } from "@fhevm/hardhat-plugin";
4
+ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
5
+
6
+ describe("{{CONTRACT_NAME}}", function () {
7
+ let contract: any;
8
+ let contractAddress: string;
9
+ let signers: { alice: HardhatEthersSigner; bob: HardhatEthersSigner };
10
+
11
+ before(async function () {
12
+ const [alice, bob] = await ethers.getSigners();
13
+ signers = { alice, bob };
14
+ });
15
+
16
+ beforeEach(async function () {
17
+ const Factory = await ethers.getContractFactory("{{CONTRACT_NAME}}");
18
+ contract = await Factory.deploy("TestToken", "TTK");
19
+ await contract.waitForDeployment();
20
+ contractAddress = await contract.getAddress();
21
+ });
22
+
23
+ describe("Metadata", function () {
24
+ it("should return correct name", async function () {
25
+ expect(await contract.name()).to.eq("TestToken");
26
+ });
27
+
28
+ it("should return correct symbol", async function () {
29
+ expect(await contract.symbol()).to.eq("TTK");
30
+ });
31
+ });
32
+
33
+ describe("Mint", function () {
34
+ it("should mint tokens to alice", async function () {
35
+ const encryptedInput = await fhevm
36
+ .createEncryptedInput(contractAddress, signers.alice.address)
37
+ .add64(1000)
38
+ .encrypt();
39
+
40
+ const tx = await contract.mint(
41
+ signers.alice.address,
42
+ encryptedInput.handles[0],
43
+ encryptedInput.inputProof
44
+ );
45
+ await tx.wait();
46
+
47
+ // Get alice's encrypted balance
48
+ const encryptedBalance = await contract.balanceOf(signers.alice.address);
49
+
50
+ // Decrypt and verify
51
+ const clearBalance = await fhevm.userDecryptEuint(
52
+ FhevmType.euint64,
53
+ encryptedBalance,
54
+ contractAddress,
55
+ signers.alice
56
+ );
57
+
58
+ expect(clearBalance).to.eq(1000);
59
+ });
60
+
61
+ it("should mint more tokens and update balance", async function () {
62
+ // First mint
63
+ const input1 = await fhevm
64
+ .createEncryptedInput(contractAddress, signers.alice.address)
65
+ .add64(1000)
66
+ .encrypt();
67
+
68
+ await contract.mint(
69
+ signers.alice.address,
70
+ input1.handles[0],
71
+ input1.inputProof
72
+ );
73
+
74
+ // Mint 500 more tokens
75
+ const input2 = await fhevm
76
+ .createEncryptedInput(contractAddress, signers.alice.address)
77
+ .add64(500)
78
+ .encrypt();
79
+
80
+ await contract.mint(
81
+ signers.alice.address,
82
+ input2.handles[0],
83
+ input2.inputProof
84
+ );
85
+
86
+ // Get alice's encrypted balance (should be 1500)
87
+ const encryptedBalance = await contract.balanceOf(signers.alice.address);
88
+
89
+ const clearBalance = await fhevm.userDecryptEuint(
90
+ FhevmType.euint64,
91
+ encryptedBalance,
92
+ contractAddress,
93
+ signers.alice
94
+ );
95
+
96
+ expect(clearBalance).to.eq(1500);
97
+ });
98
+ });
99
+
100
+ describe("Transfer", function () {
101
+ it("should transfer tokens from alice to bob", async function () {
102
+ // First mint tokens to alice
103
+ const mintInput = await fhevm
104
+ .createEncryptedInput(contractAddress, signers.alice.address)
105
+ .add64(1000)
106
+ .encrypt();
107
+
108
+ await contract.mint(
109
+ signers.alice.address,
110
+ mintInput.handles[0],
111
+ mintInput.inputProof
112
+ );
113
+
114
+ // Alice transfers 300 tokens to bob
115
+ const transferInput = await fhevm
116
+ .createEncryptedInput(contractAddress, signers.alice.address)
117
+ .add64(300)
118
+ .encrypt();
119
+
120
+ const tx = await contract.connect(signers.alice).transfer(
121
+ signers.bob.address,
122
+ transferInput.handles[0],
123
+ transferInput.inputProof
124
+ );
125
+ await tx.wait();
126
+
127
+ // Check alice's balance (should be 1000 - 300 = 700)
128
+ const aliceEncryptedBalance = await contract.balanceOf(signers.alice.address);
129
+ const aliceClearBalance = await fhevm.userDecryptEuint(
130
+ FhevmType.euint64,
131
+ aliceEncryptedBalance,
132
+ contractAddress,
133
+ signers.alice
134
+ );
135
+ expect(aliceClearBalance).to.eq(700);
136
+
137
+ // Check bob's balance (should be 300)
138
+ const bobEncryptedBalance = await contract.balanceOf(signers.bob.address);
139
+ const bobClearBalance = await fhevm.userDecryptEuint(
140
+ FhevmType.euint64,
141
+ bobEncryptedBalance,
142
+ contractAddress,
143
+ signers.bob
144
+ );
145
+ expect(bobClearBalance).to.eq(300);
146
+ });
147
+
148
+ it("should handle transfer exceeding balance (no transfer occurs)", async function () {
149
+ // Mint 300 tokens to bob
150
+ const mintInput = await fhevm
151
+ .createEncryptedInput(contractAddress, signers.bob.address)
152
+ .add64(300)
153
+ .encrypt();
154
+
155
+ await contract.mint(
156
+ signers.bob.address,
157
+ mintInput.handles[0],
158
+ mintInput.inputProof
159
+ );
160
+
161
+ // Bob tries to transfer 500 tokens (only has 300)
162
+ const transferInput = await fhevm
163
+ .createEncryptedInput(contractAddress, signers.bob.address)
164
+ .add64(500)
165
+ .encrypt();
166
+
167
+ const tx = await contract.connect(signers.bob).transfer(
168
+ signers.alice.address,
169
+ transferInput.handles[0],
170
+ transferInput.inputProof
171
+ );
172
+ await tx.wait();
173
+
174
+ // Bob's balance should still be 300 (transfer didn't happen)
175
+ const bobEncryptedBalance = await contract.balanceOf(signers.bob.address);
176
+ const bobClearBalance = await fhevm.userDecryptEuint(
177
+ FhevmType.euint64,
178
+ bobEncryptedBalance,
179
+ contractAddress,
180
+ signers.bob
181
+ );
182
+ expect(bobClearBalance).to.eq(300);
183
+ });
184
+ });
185
+
186
+ {{TEST_EXTENSIONS}}
187
+ });
@@ -80,8 +80,8 @@ contract {{CONTRACT_NAME}} is {{INHERITS}} {
80
80
  Proposal storage proposal = _proposals[proposalId];
81
81
  proposal.description = description;
82
82
  proposal.endTime = block.timestamp + durationSeconds;
83
- proposal.yesVotes = FHE.asEuint64(0);
84
- proposal.noVotes = FHE.asEuint64(0);
83
+ proposal.yesVotes = FHE.asEuint32(0);
84
+ proposal.noVotes = FHE.asEuint32(0);
85
85
  proposal.exists = true;
86
86
 
87
87
  // Set ACL for vote counts
@@ -118,13 +118,13 @@ contract {{CONTRACT_NAME}} is {{INHERITS}} {
118
118
  [[VOTE_TYPE]] eVote = FHE.fromExternal(encryptedVote, inputProof);
119
119
 
120
120
  // Encrypted vote: if vote > 0, it's a yes vote
121
- ebool isYes = FHE.gt(eVote, FHE.asEuint64(0));
121
+ ebool isYes = FHE.gt(eVote, FHE.asEuint32(0));
122
122
 
123
123
  // Homomorphic conditional addition
124
124
  // yesVotes += isYes ? 1 : 0
125
125
  // noVotes += isYes ? 0 : 1
126
- [[VOTE_TYPE]] one = FHE.asEuint64(1);
127
- [[VOTE_TYPE]] zero = FHE.asEuint64(0);
126
+ [[VOTE_TYPE]] one = FHE.asEuint32(1);
127
+ [[VOTE_TYPE]] zero = FHE.asEuint32(0);
128
128
 
129
129
  proposal.yesVotes = FHE.add(proposal.yesVotes, FHE.select(isYes, one, zero));
130
130
  proposal.noVotes = FHE.add(proposal.noVotes, FHE.select(isYes, zero, one));
@@ -1,28 +1,39 @@
1
- import { ethers } from "hardhat";
1
+ import hre from "hardhat";
2
2
 
3
3
  async function main() {
4
4
  console.log("Deploying {{CONTRACT_NAME}}...");
5
5
 
6
- const VotingFactory = await ethers.getContractFactory("{{CONTRACT_NAME}}");
7
- const voting = await VotingFactory.deploy();
6
+ const Factory = await hre.ethers.getContractFactory("{{CONTRACT_NAME}}");
7
+ const contract = await Factory.deploy();
8
8
 
9
- await voting.waitForDeployment();
9
+ await contract.waitForDeployment();
10
10
 
11
- const address = await voting.getAddress();
12
- console.log("\n{{CONTRACT_NAME}} deployed to:", address);
11
+ const address = await contract.getAddress();
12
+ console.log(`{{CONTRACT_NAME}} deployed to: ${address}`);
13
13
 
14
- console.log("\nVoting Operations:");
15
- console.log(" - createProposal(title, description, duration): Create proposal");
16
- console.log(" - vote(proposalId, encryptedVote): Cast encrypted vote");
17
- console.log(" - requestTally(proposalId): Request vote count reveal");
18
- console.log(" - finalizeTally(proposalId, yesVotes, noVotes, proof): Finalize");
19
- console.log("\nPrivacy Guarantees:");
20
- console.log(" - Individual votes are encrypted");
21
- console.log(" - Only final tally is revealed");
22
- console.log(" - No one knows how you voted");
14
+ // Verify on explorer if not local
15
+ if (hre.network.name !== "hardhat" && hre.network.name !== "localhost") {
16
+ console.log("Waiting for block confirmations...");
17
+ await contract.deploymentTransaction()?.wait(5);
18
+
19
+ console.log("Verifying contract...");
20
+ try {
21
+ await hre.run("verify:verify", {
22
+ address: address,
23
+ constructorArguments: [],
24
+ });
25
+ console.log("Contract verified!");
26
+ } catch (error) {
27
+ console.log("Verification failed:", error);
28
+ }
29
+ }
30
+
31
+ return address;
23
32
  }
24
33
 
25
- main().catch((error) => {
26
- console.error(error);
27
- process.exitCode = 1;
28
- });
34
+ main()
35
+ .then(() => process.exit(0))
36
+ .catch((error) => {
37
+ console.error(error);
38
+ process.exit(1);
39
+ });
@@ -0,0 +1,135 @@
1
+ import { expect } from "chai";
2
+ import { ethers, fhevm } from "hardhat";
3
+ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
4
+
5
+ describe("{{CONTRACT_NAME}}", function () {
6
+ let contract: any;
7
+ let contractAddress: string;
8
+ let signers: { alice: HardhatEthersSigner; bob: HardhatEthersSigner; charlie: HardhatEthersSigner };
9
+
10
+ before(async function () {
11
+ const [alice, bob, charlie] = await ethers.getSigners();
12
+ signers = { alice, bob, charlie };
13
+ });
14
+
15
+ beforeEach(async function () {
16
+ const Factory = await ethers.getContractFactory("{{CONTRACT_NAME}}");
17
+ contract = await Factory.deploy();
18
+ await contract.waitForDeployment();
19
+ contractAddress = await contract.getAddress();
20
+ });
21
+
22
+ describe("Proposal Creation", function () {
23
+ it("should create a proposal", async function () {
24
+ const tx = await contract.createProposal("Should we upgrade?", 3600); // 1 hour duration
25
+ await tx.wait();
26
+
27
+ const [description, endTime, exists, finalized] = await contract.getProposal(1);
28
+
29
+ expect(description).to.eq("Should we upgrade?");
30
+ expect(exists).to.be.true;
31
+ expect(finalized).to.be.false;
32
+ });
33
+
34
+ it("should increment proposal ID", async function () {
35
+ await contract.createProposal("Proposal 1", 3600);
36
+ expect(await contract.nextProposalId()).to.eq(2);
37
+ });
38
+ });
39
+
40
+ describe("Voting", function () {
41
+ beforeEach(async function () {
42
+ // Create a proposal for voting tests
43
+ await contract.createProposal("Test Proposal", 3600);
44
+ });
45
+
46
+ it("should allow alice to vote YES (vote = 1)", async function () {
47
+ const encryptedInput = await fhevm
48
+ .createEncryptedInput(contractAddress, signers.alice.address)
49
+ .add32(1) // YES vote
50
+ .encrypt();
51
+
52
+ const tx = await contract.connect(signers.alice).vote(
53
+ 1, // proposalId
54
+ encryptedInput.handles[0],
55
+ encryptedInput.inputProof
56
+ );
57
+ await tx.wait();
58
+
59
+ expect(await contract.hasVoted(1, signers.alice.address)).to.be.true;
60
+ });
61
+
62
+ it("should allow bob to vote NO (vote = 0)", async function () {
63
+ const encryptedInput = await fhevm
64
+ .createEncryptedInput(contractAddress, signers.bob.address)
65
+ .add32(0) // NO vote
66
+ .encrypt();
67
+
68
+ const tx = await contract.connect(signers.bob).vote(
69
+ 1,
70
+ encryptedInput.handles[0],
71
+ encryptedInput.inputProof
72
+ );
73
+ await tx.wait();
74
+
75
+ expect(await contract.hasVoted(1, signers.bob.address)).to.be.true;
76
+ });
77
+
78
+ it("should allow charlie to vote YES", async function () {
79
+ const encryptedInput = await fhevm
80
+ .createEncryptedInput(contractAddress, signers.charlie.address)
81
+ .add32(5) // Any value > 0 is YES
82
+ .encrypt();
83
+
84
+ const tx = await contract.connect(signers.charlie).vote(
85
+ 1,
86
+ encryptedInput.handles[0],
87
+ encryptedInput.inputProof
88
+ );
89
+ await tx.wait();
90
+
91
+ expect(await contract.hasVoted(1, signers.charlie.address)).to.be.true;
92
+ });
93
+
94
+ it("should prevent double voting", async function () {
95
+ // First vote
96
+ const input1 = await fhevm
97
+ .createEncryptedInput(contractAddress, signers.alice.address)
98
+ .add32(1)
99
+ .encrypt();
100
+ await contract.connect(signers.alice).vote(1, input1.handles[0], input1.inputProof);
101
+
102
+ // Try to vote again
103
+ const input2 = await fhevm
104
+ .createEncryptedInput(contractAddress, signers.alice.address)
105
+ .add32(1)
106
+ .encrypt();
107
+
108
+ await expect(
109
+ contract.connect(signers.alice).vote(1, input2.handles[0], input2.inputProof)
110
+ ).to.be.revertedWithCustomError(contract, "AlreadyVoted");
111
+ });
112
+ });
113
+
114
+ describe("Vote Counts", function () {
115
+ it("should return encrypted vote counts (private by design)", async function () {
116
+ // Create proposal and cast votes
117
+ await contract.createProposal("Test Proposal", 3600);
118
+
119
+ const input1 = await fhevm
120
+ .createEncryptedInput(contractAddress, signers.alice.address)
121
+ .add32(1)
122
+ .encrypt();
123
+ await contract.connect(signers.alice).vote(1, input1.handles[0], input1.inputProof);
124
+
125
+ // Vote counts are intentionally private
126
+ const [yesVotes, noVotes] = await contract.getVoteCounts(1);
127
+
128
+ // Verify handles are returned (non-zero bytes32)
129
+ expect(yesVotes).to.not.eq(ethers.ZeroHash);
130
+ expect(noVotes).to.not.eq(ethers.ZeroHash);
131
+ });
132
+ });
133
+
134
+ {{TEST_EXTENSIONS}}
135
+ });