@0xflydev/labz 1.0.23 → 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.23",
3
+ "version": "1.0.24",
4
4
  "description": "Lab-Z CLI - Generate composable FHEVM smart contracts",
5
5
  "author": "0xflydev",
6
6
  "license": "MIT",
@@ -0,0 +1,39 @@
1
+ import hre from "hardhat";
2
+
3
+ async function main() {
4
+ console.log("Deploying {{CONTRACT_NAME}}...");
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;
32
+ }
33
+
34
+ main()
35
+ .then(() => process.exit(0))
36
+ .catch((error) => {
37
+ console.error(error);
38
+ process.exit(1);
39
+ });
@@ -0,0 +1,39 @@
1
+ import hre from "hardhat";
2
+
3
+ async function main() {
4
+ console.log("Deploying {{CONTRACT_NAME}}...");
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;
32
+ }
33
+
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
+ });
@@ -0,0 +1,39 @@
1
+ import hre from "hardhat";
2
+
3
+ async function main() {
4
+ console.log("Deploying {{CONTRACT_NAME}}...");
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;
32
+ }
33
+
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
+ });