@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 +5 -2
- package/package.json +1 -1
- package/templates/buildable/projects/auction/deploy/deploy.ts.tmpl +39 -0
- package/templates/buildable/projects/token/deploy/deploy.ts.tmpl +39 -0
- package/templates/buildable/projects/token/test/Token.test.ts.tmpl +187 -0
- package/templates/buildable/projects/voting/deploy/deploy.ts.tmpl +39 -0
- package/templates/buildable/projects/voting/test/Voting.test.ts.tmpl +135 -0
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 =
|
|
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
|
@@ -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
|
+
});
|