@semaphore-protocol/cli-template-contracts-hardhat 3.6.0-1

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/.env.example ADDED
@@ -0,0 +1,4 @@
1
+ INFURA_API_KEY=
2
+ ETHEREUM_PRIVATE_KEY=
3
+ REPORT_GAS=false
4
+ COINMARKETCAP_API_KEY=
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Semaphore Hardhat template
2
+
3
+ This project demonstrates a basic Semaphore use case. It comes with a sample contract, a test for that contract and a sample task that deploys that contract.
4
+
5
+ ## Usage
6
+
7
+ ### Compile
8
+
9
+ ```bash
10
+ yarn compile
11
+ ```
12
+
13
+ ### Testing
14
+
15
+ ```bash
16
+ yarn test
17
+ ```
18
+
19
+ You can also generate a test coverage report:
20
+
21
+ ```bash
22
+ yarn test:coverage
23
+ ```
24
+
25
+ Or a test gas report:
26
+
27
+ ```bash
28
+ yarn test:report-gas
29
+ ```
30
+
31
+ ### Deploy
32
+
33
+ 1. Copy the `.env.example` file as `.env`.
34
+
35
+ ```bash
36
+ cp .env.example .env
37
+ ```
38
+
39
+ 2. Add your environment variables.
40
+
41
+ > **Note**
42
+ > You should at least set a valid Ethereum URL (e.g. Infura) and a private key with some ethers.
43
+
44
+ 3. And deploy your contract.
45
+
46
+ ```bash
47
+ yarn deploy --semaphore <semaphore-address> --group <group-id> --network goerli
48
+ ```
49
+
50
+ > **Note**
51
+ > Check the Semaphore contract addresses [here](https://semaphore.appliedzkp.org/docs/deployed-contracts#semaphore).
52
+
53
+ > **Warning**
54
+ > The group id is a number!
@@ -0,0 +1,30 @@
1
+ //SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.4;
3
+
4
+ import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
5
+
6
+ contract Feedback {
7
+ ISemaphore public semaphore;
8
+
9
+ uint256 public groupId;
10
+
11
+ constructor(address semaphoreAddress, uint256 _groupId) {
12
+ semaphore = ISemaphore(semaphoreAddress);
13
+ groupId = _groupId;
14
+
15
+ semaphore.createGroup(groupId, 20, address(this));
16
+ }
17
+
18
+ function joinGroup(uint256 identityCommitment) external {
19
+ semaphore.addMember(groupId, identityCommitment);
20
+ }
21
+
22
+ function sendFeedback(
23
+ uint256 feedback,
24
+ uint256 merkleTreeRoot,
25
+ uint256 nullifierHash,
26
+ uint256[8] calldata proof
27
+ ) external {
28
+ semaphore.verifyProof(groupId, merkleTreeRoot, feedback, nullifierHash, groupId, proof);
29
+ }
30
+ }
@@ -0,0 +1,77 @@
1
+ import "@nomiclabs/hardhat-ethers"
2
+ import "@nomicfoundation/hardhat-chai-matchers"
3
+ import "@semaphore-protocol/hardhat"
4
+ import "@typechain/hardhat"
5
+ import { config as dotenvConfig } from "dotenv"
6
+ import "hardhat-gas-reporter"
7
+ import { HardhatUserConfig } from "hardhat/config"
8
+ import { NetworksUserConfig } from "hardhat/types"
9
+ import "solidity-coverage"
10
+ import { config } from "./package.json"
11
+ import "./tasks/deploy"
12
+
13
+ dotenvConfig()
14
+
15
+ function getNetworks(): NetworksUserConfig {
16
+ if (!process.env.INFURA_API_KEY || !process.env.ETHEREUM_PRIVATE_KEY) {
17
+ return {}
18
+ }
19
+
20
+ const accounts = [`0x${process.env.ETHEREUM_PRIVATE_KEY}`]
21
+ const infuraApiKey = process.env.INFURA_API_KEY
22
+
23
+ return {
24
+ goerli: {
25
+ url: `https://goerli.infura.io/v3/${infuraApiKey}`,
26
+ chainId: 5,
27
+ accounts
28
+ },
29
+ sepolia: {
30
+ url: `https://sepolia.infura.io/v3/${infuraApiKey}`,
31
+ chainId: 11155111,
32
+ accounts
33
+ },
34
+ mumbai: {
35
+ url: `https://polygon-mumbai.infura.io/v3/${infuraApiKey}`,
36
+ chainId: 80001,
37
+ accounts
38
+ },
39
+ "optimism-goerli": {
40
+ url: `https://optimism-goerli.infura.io/v3/${infuraApiKey}`,
41
+ chainId: 420,
42
+ accounts
43
+ },
44
+ arbitrum: {
45
+ url: `https://arbitrum-mainnet.infura.io/v3/${infuraApiKey}`,
46
+ chainId: 42161,
47
+ accounts
48
+ }
49
+ }
50
+ }
51
+
52
+ const hardhatConfig: HardhatUserConfig = {
53
+ solidity: config.solidity,
54
+ paths: {
55
+ sources: config.paths.contracts,
56
+ tests: config.paths.tests,
57
+ cache: config.paths.cache,
58
+ artifacts: config.paths.build.contracts
59
+ },
60
+ networks: {
61
+ hardhat: {
62
+ chainId: 1337
63
+ },
64
+ ...getNetworks()
65
+ },
66
+ gasReporter: {
67
+ currency: "USD",
68
+ enabled: process.env.REPORT_GAS === "true",
69
+ coinmarketcap: process.env.COINMARKETCAP_API_KEY
70
+ },
71
+ typechain: {
72
+ outDir: config.paths.build.typechain,
73
+ target: "ethers-v5"
74
+ }
75
+ }
76
+
77
+ export default hardhatConfig
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@semaphore-protocol/cli-template-contracts-hardhat",
3
+ "version": "3.6.0-1",
4
+ "description": "Semaphore Hardhat template.",
5
+ "license": "Unlicense",
6
+ "files": [
7
+ ".gitignore",
8
+ ".env.example",
9
+ "contracts/",
10
+ "scripts/",
11
+ "tasks/",
12
+ "test/",
13
+ "hardhat.config.ts",
14
+ "tsconfig.json",
15
+ "LICENSE",
16
+ "README.md"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "scripts": {
22
+ "dev": "hardhat node & yarn compile && yarn deploy --network localhost",
23
+ "compile": "hardhat compile",
24
+ "download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
25
+ "deploy": "yarn compile && hardhat deploy",
26
+ "test": "hardhat run scripts/download-snark-artifacts.ts && hardhat test",
27
+ "test:report-gas": "REPORT_GAS=true hardhat test",
28
+ "test:coverage": "hardhat coverage",
29
+ "typechain": "hardhat typechain"
30
+ },
31
+ "devDependencies": {
32
+ "@ethersproject/abi": "^5.4.7",
33
+ "@ethersproject/providers": "^5.4.7",
34
+ "@nomicfoundation/hardhat-chai-matchers": "^1.0.0",
35
+ "@nomicfoundation/hardhat-network-helpers": "^1.0.0",
36
+ "@nomicfoundation/hardhat-toolbox": "^2.0.0",
37
+ "@nomiclabs/hardhat-ethers": "^2.0.0",
38
+ "@nomiclabs/hardhat-etherscan": "^3.0.0",
39
+ "@semaphore-protocol/group": "3.6.0-1",
40
+ "@semaphore-protocol/hardhat": "3.6.0-1",
41
+ "@semaphore-protocol/identity": "3.6.0-1",
42
+ "@semaphore-protocol/proof": "3.6.0-1",
43
+ "@typechain/ethers-v5": "^10.1.0",
44
+ "@typechain/hardhat": "^6.1.2",
45
+ "@types/chai": "^4.2.0",
46
+ "@types/download": "^8.0.1",
47
+ "@types/mocha": "^9.1.0",
48
+ "@types/node": ">=12.0.0",
49
+ "chai": "^4.2.0",
50
+ "dotenv": "^16.0.3",
51
+ "download": "^8.0.0",
52
+ "ethers": "^5.4.7",
53
+ "hardhat": "^2.11.0",
54
+ "hardhat-gas-reporter": "^1.0.8",
55
+ "solidity-coverage": "^0.8.1",
56
+ "ts-node": ">=8.0.0",
57
+ "typechain": "^8.1.0",
58
+ "typescript": ">=4.5.0"
59
+ },
60
+ "dependencies": {
61
+ "@semaphore-protocol/contracts": "3.6.0-1"
62
+ },
63
+ "config": {
64
+ "solidity": {
65
+ "version": "0.8.4"
66
+ },
67
+ "paths": {
68
+ "contracts": "./contracts",
69
+ "tests": "./test",
70
+ "cache": "./cache",
71
+ "build": {
72
+ "snark-artifacts": "./build/snark-artifacts",
73
+ "contracts": "./build/contracts",
74
+ "typechain": "./build/typechain"
75
+ }
76
+ }
77
+ },
78
+ "stableVersion": "3.5.0"
79
+ }
@@ -0,0 +1,24 @@
1
+ import download from "download"
2
+ import fs from "fs"
3
+ import { config } from "../package.json"
4
+
5
+ async function main() {
6
+ const snarkArtifactsPath = config.paths.build["snark-artifacts"]
7
+ const url = `http://www.trusted-setup-pse.org/semaphore/${20}`
8
+
9
+ if (!fs.existsSync(snarkArtifactsPath)) {
10
+ fs.mkdirSync(snarkArtifactsPath, { recursive: true })
11
+ }
12
+
13
+ if (!fs.existsSync(`${snarkArtifactsPath}/semaphore.zkey`)) {
14
+ await download(`${url}/semaphore.wasm`, snarkArtifactsPath)
15
+ await download(`${url}/semaphore.zkey`, snarkArtifactsPath)
16
+ }
17
+ }
18
+
19
+ main()
20
+ .then(() => process.exit(0))
21
+ .catch((error) => {
22
+ console.error(error)
23
+ process.exit(1)
24
+ })
@@ -0,0 +1,31 @@
1
+ import { task, types } from "hardhat/config"
2
+
3
+ task("deploy", "Deploy a Feedback contract")
4
+ .addOptionalParam("semaphore", "Semaphore contract address", undefined, types.string)
5
+ .addOptionalParam("group", "Group id", "42", types.string)
6
+ .addOptionalParam("logs", "Print the logs", true, types.boolean)
7
+ .setAction(async ({ logs, semaphore: semaphoreAddress, group: groupId }, { ethers, run }) => {
8
+ if (!semaphoreAddress) {
9
+ const { semaphore } = await run("deploy:semaphore", {
10
+ logs
11
+ })
12
+
13
+ semaphoreAddress = semaphore.address
14
+ }
15
+
16
+ if (!groupId) {
17
+ groupId = process.env.GROUP_ID
18
+ }
19
+
20
+ const FeedbackFactory = await ethers.getContractFactory("Feedback")
21
+
22
+ const feedbackContract = await FeedbackFactory.deploy(semaphoreAddress, groupId)
23
+
24
+ await feedbackContract.deployed()
25
+
26
+ if (logs) {
27
+ console.info(`Feedback contract has been deployed to: ${feedbackContract.address}`)
28
+ }
29
+
30
+ return feedbackContract
31
+ })
@@ -0,0 +1,69 @@
1
+ import { Group } from "@semaphore-protocol/group"
2
+ import { Identity } from "@semaphore-protocol/identity"
3
+ import { generateProof } from "@semaphore-protocol/proof"
4
+ import { expect } from "chai"
5
+ import { formatBytes32String } from "ethers/lib/utils"
6
+ import { run } from "hardhat"
7
+ // @ts-ignore: typechain folder will be generated after contracts compilation
8
+ import { Feedback } from "../build/typechain"
9
+ import { config } from "../package.json"
10
+
11
+ describe("Feedback", () => {
12
+ let feedbackContract: Feedback
13
+ let semaphoreContract: string
14
+
15
+ const groupId = "42"
16
+ const group = new Group(groupId)
17
+ const users: Identity[] = []
18
+
19
+ before(async () => {
20
+ const { semaphore } = await run("deploy:semaphore", {
21
+ logs: false
22
+ })
23
+
24
+ feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: semaphore.address })
25
+ semaphoreContract = semaphore
26
+
27
+ users.push(new Identity())
28
+ users.push(new Identity())
29
+ })
30
+
31
+ describe("# joinGroup", () => {
32
+ it("Should allow users to join the group", async () => {
33
+ for await (const [i, user] of users.entries()) {
34
+ const transaction = feedbackContract.joinGroup(user.commitment)
35
+
36
+ group.addMember(user.commitment)
37
+
38
+ await expect(transaction)
39
+ .to.emit(semaphoreContract, "MemberAdded")
40
+ .withArgs(groupId, i, user.commitment, group.root)
41
+ }
42
+ })
43
+ })
44
+
45
+ describe("# sendFeedback", () => {
46
+ const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
47
+ const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
48
+
49
+ it("Should allow users to send feedback anonymously", async () => {
50
+ const feedback = formatBytes32String("Hello World")
51
+
52
+ const fullProof = await generateProof(users[1], group, groupId, feedback, {
53
+ wasmFilePath,
54
+ zkeyFilePath
55
+ })
56
+
57
+ const transaction = feedbackContract.sendFeedback(
58
+ feedback,
59
+ fullProof.merkleTreeRoot,
60
+ fullProof.nullifierHash,
61
+ fullProof.proof
62
+ )
63
+
64
+ await expect(transaction)
65
+ .to.emit(semaphoreContract, "ProofVerified")
66
+ .withArgs(groupId, fullProof.merkleTreeRoot, fullProof.nullifierHash, groupId, fullProof.signal)
67
+ })
68
+ })
69
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "moduleResolution": "Node",
4
+ "noImplicitAny": true,
5
+ "resolveJsonModule": true,
6
+ "target": "ES2018",
7
+ "module": "CommonJS",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "outDir": "dist",
11
+ "typeRoots": ["node_modules/@types", "types"]
12
+ },
13
+ "include": ["scripts/**/*", "tasks/**/*", "test/**/*", "build/typechain/**/*", "types/**/*"],
14
+ "files": ["./hardhat.config.ts"]
15
+ }