diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/.env.development b/packages/cli-template-monorepo-ethers/apps/web-app/.env.development
index 1d41062f3..1bed8b578 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/.env.development
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/.env.development
@@ -1,4 +1,4 @@
NEXT_PUBLIC_DEFAULT_NETWORK=localhost
-NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
+NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS=0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9
NEXT_PUBLIC_SEMAPHORE_CONTRACT_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
NEXT_PUBLIC_GROUP_ID=0
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/.env.production b/packages/cli-template-monorepo-ethers/apps/web-app/.env.production
index fa2cbb454..1bea727d0 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/.env.production
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/.env.production
@@ -1,5 +1,8 @@
NEXT_PUBLIC_DEFAULT_NETWORK=sepolia
+NEXT_PUBLIC_INFURA_API_KEY=abf67af1010b4b8d877e04244f1eac3d
NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS=
NEXT_PUBLIC_SEMAPHORE_CONTRACT_ADDRESS=0x1e0d7FF1610e480fC93BdEC510811ea2Ba6d7c2f
NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK=https://api.defender.openzeppelin.com/actions/20fce2ae-844b-4ec0-a6a2-90a3350a9d2c/runs/webhook/303216d1-fa7d-4fca-8c5b-7ba1ba544fc7/2T7i9xrkZA5j37hoaQLUuw
+NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT=https://api.gelato.digital/relays/v2/sponsored-call
+NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID=11155111
NEXT_PUBLIC_GROUP_ID=
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/.gitignore b/packages/cli-template-monorepo-ethers/apps/web-app/.gitignore
index d183c4f53..55811698f 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/.gitignore
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/.gitignore
@@ -1,5 +1,8 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+# testing
+/coverage
+
# next.js
/.next/
/out/
@@ -7,3 +10,18 @@
# production
/build
+# misc
+.DS_Store
+*.pem
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+# Auto Generated PWA files
+public/sw.js
+public/workbox-*.js
+public/worker-*.js
+public/sw.js.map
+public/workbox-*.js.map
+public/worker-*.js.map
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/contract-artifacts/Feedback.json b/packages/cli-template-monorepo-ethers/apps/web-app/contract-artifacts/Feedback.json
index 123448e9e..5b51c7a42 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/contract-artifacts/Feedback.json
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/contract-artifacts/Feedback.json
@@ -9,11 +9,6 @@
"internalType": "address",
"name": "semaphoreAddress",
"type": "address"
- },
- {
- "internalType": "uint256",
- "name": "_groupId",
- "type": "uint256"
}
],
"stateMutability": "nonpayable",
@@ -92,8 +87,8 @@
"type": "function"
}
],
- "bytecode": "0x608060405234801561001057600080fd5b5060405161083f38038061083f833981810160405281019061003291906101a8565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060018190555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c96e71fb600154306040518363ffffffff1660e01b81526004016100d6929190610206565b600060405180830381600087803b1580156100f057600080fd5b505af1158015610104573d6000803e3d6000fd5b50505050505061022f565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061013f82610114565b9050919050565b61014f81610134565b811461015a57600080fd5b50565b60008151905061016c81610146565b92915050565b6000819050919050565b61018581610172565b811461019057600080fd5b50565b6000815190506101a28161017c565b92915050565b600080604083850312156101bf576101be61010f565b5b60006101cd8582860161015d565b92505060206101de85828601610193565b9150509250929050565b6101f181610172565b82525050565b61020081610134565b82525050565b600060408201905061021b60008301856101e8565b61022860208301846101f7565b9392505050565b6106018061023e6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b6040516100669190610301565b60405180910390f35b6100896004803603810190610084919061037e565b6100e9565b005b6100936101ea565b6040516100a09190610409565b60405180910390f35b6100c360048036038101906100be9190610424565b6101f0565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040518060c001604052808781526020018681526020018581526020018481526020016001548152602001836008806020026040519081016040528092919082600860200280828437600081840152601f19601f820116905080830192505050505050815250905060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0d898dd600154836040518363ffffffff1660e01b81526004016101b0929190610578565b600060405180830381600087803b1580156101ca57600080fd5b505af11580156101de573d6000803e3d6000fd5b50505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b815260040161024d9291906105a2565b600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102c76102c26102bd84610282565b6102a2565b610282565b9050919050565b60006102d9826102ac565b9050919050565b60006102eb826102ce565b9050919050565b6102fb816102e0565b82525050565b600060208201905061031660008301846102f2565b92915050565b600080fd5b6000819050919050565b61033481610321565b811461033f57600080fd5b50565b6000813590506103518161032b565b92915050565b600080fd5b60008190508260206008028201111561037857610377610357565b5b92915050565b6000806000806000610180868803121561039b5761039a61031c565b5b60006103a988828901610342565b95505060206103ba88828901610342565b94505060406103cb88828901610342565b93505060606103dc88828901610342565b92505060806103ed8882890161035c565b9150509295509295909350565b61040381610321565b82525050565b600060208201905061041e60008301846103fa565b92915050565b60006020828403121561043a5761043961031c565b5b600061044884828501610342565b91505092915050565b61045a81610321565b82525050565b600060089050919050565b600081905092915050565b6000819050919050565b600061048c8383610451565b60208301905092915050565b6000602082019050919050565b6104ae81610460565b6104b8818461046b565b92506104c382610476565b8060005b838110156104f45781516104db8782610480565b96506104e683610498565b9250506001810190506104c7565b505050505050565b6101a0820160008201516105136000850182610451565b5060208201516105266020850182610451565b5060408201516105396040850182610451565b50606082015161054c6060850182610451565b50608082015161055f6080850182610451565b5060a082015161057260a08501826104a5565b50505050565b60006101c08201905061058e60008301856103fa565b61059b60208301846104fc565b9392505050565b60006040820190506105b760008301856103fa565b6105c460208301846103fa565b939250505056fea2646970667358221220f33606b2d5ad7c0dfc5d22afb43476e1974ea7fd160e1f28203a3e433f29cb4964736f6c63430008170033",
- "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b6040516100669190610301565b60405180910390f35b6100896004803603810190610084919061037e565b6100e9565b005b6100936101ea565b6040516100a09190610409565b60405180910390f35b6100c360048036038101906100be9190610424565b6101f0565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040518060c001604052808781526020018681526020018581526020018481526020016001548152602001836008806020026040519081016040528092919082600860200280828437600081840152601f19601f820116905080830192505050505050815250905060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0d898dd600154836040518363ffffffff1660e01b81526004016101b0929190610578565b600060405180830381600087803b1580156101ca57600080fd5b505af11580156101de573d6000803e3d6000fd5b50505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b815260040161024d9291906105a2565b600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102c76102c26102bd84610282565b6102a2565b610282565b9050919050565b60006102d9826102ac565b9050919050565b60006102eb826102ce565b9050919050565b6102fb816102e0565b82525050565b600060208201905061031660008301846102f2565b92915050565b600080fd5b6000819050919050565b61033481610321565b811461033f57600080fd5b50565b6000813590506103518161032b565b92915050565b600080fd5b60008190508260206008028201111561037857610377610357565b5b92915050565b6000806000806000610180868803121561039b5761039a61031c565b5b60006103a988828901610342565b95505060206103ba88828901610342565b94505060406103cb88828901610342565b93505060606103dc88828901610342565b92505060806103ed8882890161035c565b9150509295509295909350565b61040381610321565b82525050565b600060208201905061041e60008301846103fa565b92915050565b60006020828403121561043a5761043961031c565b5b600061044884828501610342565b91505092915050565b61045a81610321565b82525050565b600060089050919050565b600081905092915050565b6000819050919050565b600061048c8383610451565b60208301905092915050565b6000602082019050919050565b6104ae81610460565b6104b8818461046b565b92506104c382610476565b8060005b838110156104f45781516104db8782610480565b96506104e683610498565b9250506001810190506104c7565b505050505050565b6101a0820160008201516105136000850182610451565b5060208201516105266020850182610451565b5060408201516105396040850182610451565b50606082015161054c6060850182610451565b50608082015161055f6080850182610451565b5060a082015161057260a08501826104a5565b50505050565b60006101c08201905061058e60008301856103fa565b61059b60208301846104fc565b9392505050565b60006040820190506105b760008301856103fa565b6105c460208301846103fa565b939250505056fea2646970667358221220f33606b2d5ad7c0dfc5d22afb43476e1974ea7fd160e1f28203a3e433f29cb4964736f6c63430008170033",
+ "bytecode": "0x608060405234801561001057600080fd5b506040516108473803806108478339818101604052810190610032919061017d565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635c3f3b60306040518263ffffffff1660e01b81526004016100cb91906101b9565b6020604051808303816000875af11580156100ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061010e919061020a565b60018190555050610237565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061014a8261011f565b9050919050565b61015a8161013f565b811461016557600080fd5b50565b60008151905061017781610151565b92915050565b6000602082840312156101935761019261011a565b5b60006101a184828501610168565b91505092915050565b6101b38161013f565b82525050565b60006020820190506101ce60008301846101aa565b92915050565b6000819050919050565b6101e7816101d4565b81146101f257600080fd5b50565b600081519050610204816101de565b92915050565b6000602082840312156102205761021f61011a565b5b600061022e848285016101f5565b91505092915050565b610601806102466000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b6040516100669190610301565b60405180910390f35b6100896004803603810190610084919061037e565b6100e9565b005b6100936101ea565b6040516100a09190610409565b60405180910390f35b6100c360048036038101906100be9190610424565b6101f0565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040518060c001604052808781526020018681526020018581526020018481526020016001548152602001836008806020026040519081016040528092919082600860200280828437600081840152601f19601f820116905080830192505050505050815250905060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0d898dd600154836040518363ffffffff1660e01b81526004016101b0929190610578565b600060405180830381600087803b1580156101ca57600080fd5b505af11580156101de573d6000803e3d6000fd5b50505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b815260040161024d9291906105a2565b600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102c76102c26102bd84610282565b6102a2565b610282565b9050919050565b60006102d9826102ac565b9050919050565b60006102eb826102ce565b9050919050565b6102fb816102e0565b82525050565b600060208201905061031660008301846102f2565b92915050565b600080fd5b6000819050919050565b61033481610321565b811461033f57600080fd5b50565b6000813590506103518161032b565b92915050565b600080fd5b60008190508260206008028201111561037857610377610357565b5b92915050565b6000806000806000610180868803121561039b5761039a61031c565b5b60006103a988828901610342565b95505060206103ba88828901610342565b94505060406103cb88828901610342565b93505060606103dc88828901610342565b92505060806103ed8882890161035c565b9150509295509295909350565b61040381610321565b82525050565b600060208201905061041e60008301846103fa565b92915050565b60006020828403121561043a5761043961031c565b5b600061044884828501610342565b91505092915050565b61045a81610321565b82525050565b600060089050919050565b600081905092915050565b6000819050919050565b600061048c8383610451565b60208301905092915050565b6000602082019050919050565b6104ae81610460565b6104b8818461046b565b92506104c382610476565b8060005b838110156104f45781516104db8782610480565b96506104e683610498565b9250506001810190506104c7565b505050505050565b6101a0820160008201516105136000850182610451565b5060208201516105266020850182610451565b5060408201516105396040850182610451565b50606082015161054c6060850182610451565b50608082015161055f6080850182610451565b5060a082015161057260a08501826104a5565b50505050565b60006101c08201905061058e60008301856103fa565b61059b60208301846104fc565b9392505050565b60006040820190506105b760008301856103fa565b6105c460208301846103fa565b939250505056fea264697066735822122078569abd7f309f3107c4d19e9b4a4f4812522ccc5dc57c7ccbe2b06a5ba461b064736f6c63430008170033",
+ "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b6040516100669190610301565b60405180910390f35b6100896004803603810190610084919061037e565b6100e9565b005b6100936101ea565b6040516100a09190610409565b60405180910390f35b6100c360048036038101906100be9190610424565b6101f0565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040518060c001604052808781526020018681526020018581526020018481526020016001548152602001836008806020026040519081016040528092919082600860200280828437600081840152601f19601f820116905080830192505050505050815250905060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0d898dd600154836040518363ffffffff1660e01b81526004016101b0929190610578565b600060405180830381600087803b1580156101ca57600080fd5b505af11580156101de573d6000803e3d6000fd5b50505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b815260040161024d9291906105a2565b600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102c76102c26102bd84610282565b6102a2565b610282565b9050919050565b60006102d9826102ac565b9050919050565b60006102eb826102ce565b9050919050565b6102fb816102e0565b82525050565b600060208201905061031660008301846102f2565b92915050565b600080fd5b6000819050919050565b61033481610321565b811461033f57600080fd5b50565b6000813590506103518161032b565b92915050565b600080fd5b60008190508260206008028201111561037857610377610357565b5b92915050565b6000806000806000610180868803121561039b5761039a61031c565b5b60006103a988828901610342565b95505060206103ba88828901610342565b94505060406103cb88828901610342565b93505060606103dc88828901610342565b92505060806103ed8882890161035c565b9150509295509295909350565b61040381610321565b82525050565b600060208201905061041e60008301846103fa565b92915050565b60006020828403121561043a5761043961031c565b5b600061044884828501610342565b91505092915050565b61045a81610321565b82525050565b600060089050919050565b600081905092915050565b6000819050919050565b600061048c8383610451565b60208301905092915050565b6000602082019050919050565b6104ae81610460565b6104b8818461046b565b92506104c382610476565b8060005b838110156104f45781516104db8782610480565b96506104e683610498565b9250506001810190506104c7565b505050505050565b6101a0820160008201516105136000850182610451565b5060208201516105266020850182610451565b5060408201516105396040850182610451565b50606082015161054c6060850182610451565b50608082015161055f6080850182610451565b5060a082015161057260a08501826104a5565b50505050565b60006101c08201905061058e60008301856103fa565b61059b60208301846104fc565b9392505050565b60006040820190506105b760008301856103fa565b6105c460208301846103fa565b939250505056fea264697066735822122078569abd7f309f3107c4d19e9b4a4f4812522ccc5dc57c7ccbe2b06a5ba461b064736f6c63430008170033",
"linkReferences": {},
"deployedLinkReferences": {}
}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/next.config.mjs b/packages/cli-template-monorepo-ethers/apps/web-app/next.config.mjs
index 2233f10f5..ae9ad74ec 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/next.config.mjs
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/next.config.mjs
@@ -14,7 +14,8 @@ const nextConfig = withPWA({
})({
env: {
INFURA_API_KEY: process.env.INFURA_API_KEY,
- ETHEREUM_PRIVATE_KEY: process.env.ETHEREUM_PRIVATE_KEY
+ ETHEREUM_PRIVATE_KEY: process.env.ETHEREUM_PRIVATE_KEY,
+ GELATO_RELAYER_API_KEY: process.env.GELATO_RELAYER_API_KEY
}
})
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/api/feedback/route.ts b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/api/feedback/route.ts
index 795b2c5dd..45698f55c 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/api/feedback/route.ts
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/api/feedback/route.ts
@@ -3,26 +3,14 @@ import { NextRequest } from "next/server"
import Feedback from "../../../../contract-artifacts/Feedback.json"
export async function POST(req: NextRequest) {
- if (typeof process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS in your .env file")
- }
-
- if (typeof process.env.NEXT_PUBLIC_DEFAULT_NETWORK !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_DEFAULT_NETWORK in your .env file")
- }
-
- if (typeof process.env.INFURA_API_KEY !== "string" && process.env.NEXT_PUBLIC_DEFAULT_NETWORK !== "localhost") {
- throw new Error("Please, define INFURA_API_KEY in your .env file")
- }
-
if (typeof process.env.ETHEREUM_PRIVATE_KEY !== "string") {
throw new Error("Please, define ETHEREUM_PRIVATE_KEY in your .env file")
}
const ethereumPrivateKey = process.env.ETHEREUM_PRIVATE_KEY
- const ethereumNetwork = process.env.NEXT_PUBLIC_DEFAULT_NETWORK
- const infuraApiKey = process.env.INFURA_API_KEY
- const contractAddress = process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS
+ const ethereumNetwork = process.env.NEXT_PUBLIC_DEFAULT_NETWORK as string
+ const infuraApiKey = process.env.NEXT_PUBLIC_INFURA_API_KEY as string
+ const contractAddress = process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS as string
const provider =
ethereumNetwork === "localhost"
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/api/join/route.ts b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/api/join/route.ts
index 608055f9e..1d0ee24b9 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/api/join/route.ts
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/api/join/route.ts
@@ -3,26 +3,14 @@ import { NextRequest } from "next/server"
import Feedback from "../../../../contract-artifacts/Feedback.json"
export async function POST(req: NextRequest) {
- if (typeof process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS in your .env file")
- }
-
- if (typeof process.env.NEXT_PUBLIC_DEFAULT_NETWORK !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_DEFAULT_NETWORK in your .env file")
- }
-
- if (typeof process.env.INFURA_API_KEY !== "string" && process.env.NEXT_PUBLIC_DEFAULT_NETWORK !== "localhost") {
- throw new Error("Please, define INFURA_API_KEY in your .env file")
- }
-
if (typeof process.env.ETHEREUM_PRIVATE_KEY !== "string") {
throw new Error("Please, define ETHEREUM_PRIVATE_KEY in your .env file")
}
const ethereumPrivateKey = process.env.ETHEREUM_PRIVATE_KEY
- const ethereumNetwork = process.env.NEXT_PUBLIC_DEFAULT_NETWORK
- const infuraApiKey = process.env.INFURA_API_KEY
- const contractAddress = process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS
+ const ethereumNetwork = process.env.NEXT_PUBLIC_DEFAULT_NETWORK as string
+ const infuraApiKey = process.env.NEXT_PUBLIC_INFURA_API_KEY as string
+ const contractAddress = process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS as string
const provider =
ethereumNetwork === "localhost"
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/globals.css b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/globals.css
index 505ae8579..738c3fa56 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/globals.css
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/globals.css
@@ -1,29 +1,40 @@
:root {
- --slate100: #f1f5f9;
- --slate200: #e2e8f0;
- --slate300: #cbd5e1;
- --slate400: #94a3b8;
- --slate500: #64748b;
- --slate700: #334155;
- --blue200: #bfdbfe;
- --blue500: #3b82f6;
- --blue600: #2563eb;
- --blue700: #1d4ed8;
- --blue800: #1e40af;
- --blue900: #1e3a8a;
+ --blue100: #dde6fc;
+ --blue200: #c3d4fa;
+ --blue300: #9abaf6;
+ --blue400: #6a95f0;
+ --blue500: #4771ea;
+ --blue600: #3555df;
+ --blue700: #2940cc;
+ --blue800: #2735a6;
+ --blue900: #253183;
+ --blue950: #1b2050;
--darkBlueBg: #00020d;
+ --slate50: #f7f7f8;
+ --slate100: #eeeef0;
+ --slate200: #d9d9de;
+ --slate300: #b8b9c1;
+ --slate400: #92939e;
+ --slate500: #747583;
+ --slate600: #5e5f6b;
+ --slate700: #4d4e57;
+ --slate800: #42424a;
+ --slate900: #3a3a40;
+ --slate950: #26262b;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
+ font-family:"Outfit", sans-serif;;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
+ height: 100vh;
}
body {
@@ -33,17 +44,30 @@ body {
p {
line-height: 1.5rem;
+ font-weight: 300;
+ overflow-wrap: break-word;
+ font-size: 1rem;
+}
+
+b {
+ font-weight: 600;
+}
+
+.key-wrapper {
+ padding-bottom: 1.5rem;
+ padding-left: 0.5rem;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
- max-width: 32rem;
-
+ max-width: 40vw;
+ min-width: 35rem;
margin: auto;
padding: 1rem;
-
+ height: calc(100vh - 7rem - 1px);
+ padding-bottom: 5rem;
min-height: calc(100vh - 3.5rem);
}
@@ -57,28 +81,25 @@ p {
margin-bottom: 1rem;
}
-ol {
- padding: 1rem;
-}
-
-li {
- margin-top: 1rem;
-}
-
h2 {
font-size: 2.25rem;
font-weight: 500;
margin-bottom: 1rem;
+ line-height: 1.2;
}
h3 {
- font-size: 1.125rem;
+ font-size: 1.15rem;
font-weight: 500;
}
.divider {
- height: 1px;
- background: var(--slate500);
+ opacity: 0.6;
+ border: 0;
+ border-style: solid;
+ border-bottom-width: 1px;
+ width: 100%;
+ border-color: var(--slate400);
margin: 2rem 0;
}
@@ -89,31 +110,44 @@ h3 {
}
a {
- color: var(--blue500);
+ color: var(--blue400);
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+ text-decoration-color: var(--blue400);
}
.button {
- background-color: var(--blue800);
- width: 100%;
- padding: 0.8rem 1rem;
- border: 1px;
border-radius: 100px;
- cursor: pointer;
- color: var(--slate100);
- font-size: 1.125rem;
- font-weight: 500;
- transition: all 200ms linear;
- margin-top: 1rem;
- margin-bottom: 1.5rem;
- opacity: 0.9;
+ font-weight: 400;
+ transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+ transition-duration: 200ms;
+ padding-left: 18px!important;
+ padding-right: 18px!important;
+ height: 2.5rem;
+ min-width: 2.5rem;
+ font-size: 1rem;
+ -webkit-padding-start: 1rem;
+ padding-inline-start: 1rem;
+ -webkit-padding-end: 1rem;
+ padding-inline-end: 1rem;
+ background: var(--blue500);
+ color: white;
+ background-image: linear-gradient(to right, var(--blue500), var(--blue800));
+ transition-timing-function: linear;
+ width: 100%;
+ border: none;
display: flex;
- justify-content: center;
- height: 3rem;
align-items: center;
+ justify-content: center;
}
.button:hover {
- background-color: var(--blue900);
+ background-color: var(--blue800);
+ background-image: none;
+ cursor: pointer;
}
.button:disabled {
@@ -123,54 +157,126 @@ a {
.button:disabled:hover {
background-color: var(--blue800);
+ background-image: none;
}
.button-stepper {
cursor: pointer;
- color: var(--blue600);
+ color: var(--blue500);
font-size: 1.1rem;
border: none;
background: none;
+ width: 4rem;
+ margin: 0 1rem;
+ display: flex;
+ justify-content: center;
+}
+
+.button-stepper:hover {
+ text-decoration: underline;
}
-.button-link {
+.refresh-wrapper {
+ color: var(--slate400);
+ display: flex;
+ align-items: center;
+}
+
+.refresh-wrapper:hover {
+ text-decoration: underline;
+}
+
+.refresh-button {
cursor: pointer;
color: var(--slate300);
font-size: 1.1rem;
border: none;
background: none;
+ padding-right: 1rem;
+ display: flex;
+ align-items: center;
+}
+
+.refresh-button:hover {
+ text-decoration: underline;
+}
+
+.refresh-span {
+ width: 1.5em;
+ height: 1em;
+ display: inline-block;
+ line-height: 1em;
+ -webkit-flex-shrink: 0;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ color: currentColor;
+ vertical-align: middle;
+}
+
+.refresh-icon {
+ margin-inline-end: 0.5rem
+}
+
+.stepper-icon {
+ display: inline-flex;
+ align-self: center;
+ -webkit-flex-shrink: 0;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+}
+
+.left-pad {
+ margin-inline-end: 0.5rem;
+}
+
+.right-pad {
+ margin-inline-start: 0.5rem;
+}
+
+.stepper-icon svg {
+ width: 1em;
+ height: 1em;
+ display: inline-block;
+ line-height: 1em;
+ -webkit-flex-shrink: 0;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ color: currentColor;
+ vertical-align:super
}
.box {
- padding: 0.8rem;
- border-style: solid;
- border-width: 1px;
- border-color: var(--slate500);
- border-radius: 4px;
+ border-bottom: 1px solid white;
+ padding: 0.8rem 0;
+ max-height: 50px;
}
.box-text {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
- margin: 0.5rem;
+ font-size: 1rem;
}
.header {
display: flex;
justify-content: space-between;
- padding: 1.5rem;
+ padding: 0 1.5rem;
+ height: 3.5rem;
}
.header-left {
font-size: 1.125rem;
font-weight: 500;
- text-decoration: none;
+ text-decoration: none !important;
+ display: flex;
+ align-items: center;
}
.header-right {
display: flex;
gap: 1.5rem;
+ align-items: center;
}
.footer {
@@ -188,6 +294,55 @@ a {
background: var(--slate500);
}
+.users-wrapper,.feedback-wrapper {
+ max-height: 300px;
+ overflow-y: scroll;
+}
+
+.keys-header {
+ margin-bottom: 1.5rem;
+}
+
+.users-header {
+ font-weight: 700;
+ font-size: 1.125rem;
+ font-family: 'DM Sans', sans-serif;
+}
+
+.join-group-button, .send-feedback-button {
+ margin-top: 1.5rem;
+}
+
+.github-icon {
+ width: 1.5rem;
+ height: 1.5rem;
+ display: inline-block;
+ line-height: 1em;
+ -webkit-flex-shrink: 0;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ color: currentColor;
+}
+
+.github-button {
+ cursor: pointer;
+ color: var(--slate100);
+ font-size: 1.1rem;
+ border: none;
+ background: none;
+ padding-right: 1rem;
+ display: flex;
+ align-items: center;
+ box-sizing: border-box;
+ display: inline-block;
+ overflow: visible !important;
+ fill: currentColor;
+ color: #f0f6fc !important;
+ vertical-align: middle !important;
+ width: 2rem;
+ height: 2rem;
+}
+
.loader {
width: 25px;
height: 25px;
@@ -196,11 +351,11 @@ a {
border-right: 2px solid transparent;
animation: spin 1s linear infinite;
z-index: 20;
- margin-left: 1rem;
+ margin-left: 0.5rem;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
-}
+}
\ No newline at end of file
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/group/page.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/group/page.tsx
new file mode 100644
index 000000000..28dfc4465
--- /dev/null
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/group/page.tsx
@@ -0,0 +1,177 @@
+"use client"
+import Stepper from "@/components/Stepper"
+import { useLogContext } from "@/context/LogContext"
+import { useSemaphoreContext } from "@/context/SemaphoreContext"
+import { useRouter } from "next/navigation"
+import { useCallback, useEffect, useMemo } from "react"
+import Feedback from "../../../contract-artifacts/Feedback.json"
+import { ethers } from "ethers"
+import useSemaphoreIdentity from "@/hooks/useSemaphoreIdentity"
+import { useState } from "react"
+
+export default function GroupsPage() {
+ const router = useRouter()
+ const { setLog } = useLogContext()
+ const { _users, refreshUsers, addUser } = useSemaphoreContext()
+ const [_loading, setLoading] = useState(false)
+ const { _identity } = useSemaphoreIdentity()
+
+ useEffect(() => {
+ if (_users.length > 0) {
+ setLog(`${_users.length} user${_users.length > 1 ? "s" : ""} retrieved from the group 🤙🏽`)
+ }
+ }, [_users, setLog])
+
+ const users = useMemo(() => [..._users].reverse(), [_users])
+
+ const joinGroup = useCallback(async () => {
+ if (!_identity) {
+ return
+ }
+
+ setLoading(true)
+ setLog(`Joining the Feedback group...`)
+
+ let joinedGroup: boolean = false
+
+ if (process.env.NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK) {
+ const response = await fetch(process.env.NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ abi: Feedback.abi,
+ address: process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS as string,
+ functionName: "joinGroup",
+ functionParameters: [_identity.commitment.toString()]
+ })
+ })
+
+ if (response.status === 200) {
+ joinedGroup = true
+ }
+ } else if (
+ process.env.NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT &&
+ process.env.NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID &&
+ process.env.GELATO_RELAYER_API_KEY
+ ) {
+ const iface = new ethers.Interface(Feedback.abi)
+ const request = {
+ chainId: process.env.NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID,
+ target: process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS,
+ data: iface.encodeFunctionData("joinGroup", [_identity.commitment.toString()]),
+ sponsorApiKey: process.env.GELATO_RELAYER_API_KEY
+ }
+ const response = await fetch(process.env.NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(request)
+ })
+
+ if (response.status === 201) {
+ joinedGroup = true
+ }
+ } else {
+ const response = await fetch("api/join", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ identityCommitment: _identity.commitment.toString()
+ })
+ })
+
+ if (response.status === 200) {
+ joinedGroup = true
+ }
+ }
+
+ if (joinedGroup) {
+ addUser(_identity.commitment.toString())
+
+ setLog(`You have joined the Feedback group event 🎉 Share your feedback anonymously!`)
+ } else {
+ setLog("Some error occurred, please try again!")
+ }
+
+ setLoading(false)
+ }, [_identity, addUser, setLoading, setLog])
+
+ const userHasJoined = useMemo(
+ () => _identity !== undefined && _users.includes(_identity.commitment.toString()),
+ [_identity, _users]
+ )
+
+ return (
+ <>
+
Groups
+
+
+
+ Semaphore groups
+ {" "}
+ are{" "}
+
+ Lean incremental Merkle trees
+ {" "}
+ in which each leaf contains an identity commitment for a user. Groups can be abstracted to represent
+ events, polls, or organizations.
+
+
+
+
+
+
Group users ({_users.length})
+
+
+
+
+
+
+ Refresh
+
+
+
+ {_users.length > 0 && (
+
+ {users.map((user, i) => (
+
+
+ {_identity?.commitment.toString() === user ? {user} : user}
+
+
+ ))}
+
+ )}
+
+
+
+ Join group
+ {_loading &&
}
+
+
+
+
+
+ router.push("/")}
+ onNextClick={userHasJoined ? () => router.push("/proofs") : undefined}
+ />
+ >
+ )
+}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/groups/page.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/groups/page.tsx
deleted file mode 100644
index 8e72752ef..000000000
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/groups/page.tsx
+++ /dev/null
@@ -1,142 +0,0 @@
-"use client"
-
-import { Identity } from "@semaphore-protocol/core"
-import { useRouter } from "next/navigation"
-import { useCallback, useContext, useEffect, useState } from "react"
-import Feedback from "../../../contract-artifacts/Feedback.json"
-import Stepper from "@/components/Stepper"
-import LogsContext from "@/context/LogsContext"
-import SemaphoreContext from "@/context/SemaphoreContext"
-
-export default function GroupsPage() {
- const router = useRouter()
- const { setLogs } = useContext(LogsContext)
- const { _users, refreshUsers, addUser } = useContext(SemaphoreContext)
- const [_loading, setLoading] = useState(false)
- const [_identity, setIdentity] = useState()
-
- useEffect(() => {
- const privateKey = localStorage.getItem("identity")
-
- if (!privateKey) {
- router.push("/")
- return
- }
-
- setIdentity(new Identity(privateKey))
- }, [router])
-
- useEffect(() => {
- if (_users.length > 0) {
- setLogs(`${_users.length} user${_users.length > 1 ? "s" : ""} retrieved from the group 🤙🏽`)
- }
- }, [_users, setLogs])
-
- const joinGroup = useCallback(async () => {
- if (!_identity) {
- return
- }
-
- setLoading(true)
- setLogs(`Joining the Feedback group...`)
-
- let response: any
-
- if (process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK) {
- response = await fetch(process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- abi: Feedback.abi,
- address: process.env.FEEDBACK_CONTRACT_ADDRESS,
- functionName: "joinGroup",
- functionParameters: [_identity.commitment.toString()]
- })
- })
- } else {
- response = await fetch("api/join", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- identityCommitment: _identity.commitment.toString()
- })
- })
- }
-
- if (response.status === 200) {
- addUser(_identity.commitment.toString())
-
- setLogs(`You have joined the Feedback group event 🎉 Share your feedback anonymously!`)
- } else {
- setLogs("Some error occurred, please try again!")
- }
-
- setLoading(false)
- }, [_identity, addUser, setLogs])
-
- const userHasJoined = useCallback((identity: Identity) => _users.includes(identity.commitment.toString()), [_users])
-
- return (
- <>
- Groups
-
-
-
- Semaphore groups
- {" "}
- are{" "}
-
- Lean incremental Merkle trees
- {" "}
- in which each leaf contains an identity commitment for a user. Groups can be abstracted to represent
- events, polls, or organizations.
-
-
-
-
-
-
Group users ({_users.length})
-
- Refresh
-
-
-
-
-
- Join group
- {_loading &&
}
-
-
-
- {_users.length > 0 && (
-
- {_users.map((user, i) => (
-
- ))}
-
- )}
-
-
-
- router.push("/")}
- onNextClick={_identity && userHasJoined(_identity) ? () => router.push("/proofs") : undefined}
- />
- >
- )
-}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/layout.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/layout.tsx
index e6a6d1aac..138bb727a 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/layout.tsx
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/layout.tsx
@@ -1,8 +1,10 @@
import PageContainer from "@/components/PageContainer"
import type { Metadata } from "next"
-import { Inter } from "next/font/google"
+import { LogContextProvider } from "@/context/LogContext"
+import { SemaphoreContextProvider } from "@/context/SemaphoreContext"
import "./globals.css"
+import { Inter } from "next/font/google"
const inter = Inter({ subsets: ["latin"] })
export const metadata: Metadata = {
@@ -32,8 +34,20 @@ export default function RootLayout({
}>) {
return (
+
+
+
+
+
- {children}
+
+
+ {children}
+
+
)
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/page.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/page.tsx
index 1469af1e5..ee800be1f 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/page.tsx
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/page.tsx
@@ -2,42 +2,42 @@
import { Identity } from "@semaphore-protocol/core"
import { useRouter } from "next/navigation"
-import { useCallback, useContext, useEffect, useState } from "react"
+import { useCallback, useEffect, useState } from "react"
import Stepper from "../components/Stepper"
-import LogsContext from "../context/LogsContext"
+import { useLogContext } from "../context/LogContext"
export default function IdentitiesPage() {
const router = useRouter()
- const { setLogs } = useContext(LogsContext)
+ const { setLog } = useLogContext()
const [_identity, setIdentity] = useState()
useEffect(() => {
const privateKey = localStorage.getItem("identity")
if (privateKey) {
- const identity = new Identity(privateKey)
+ const identity = Identity.import(privateKey)
setIdentity(identity)
- setLogs("Your Semaphore identity has been retrieved from the browser cache 👌🏽")
+ setLog("Your Semaphore identity has been retrieved from the browser cache 👌🏽")
} else {
- setLogs("Create your Semaphore identity 👆🏽")
+ setLog("Create your Semaphore identity 👆🏽")
}
- }, [setLogs])
+ }, [setLog])
const createIdentity = useCallback(async () => {
const identity = new Identity()
setIdentity(identity)
- localStorage.setItem("identity", identity.privateKey.toString())
+ localStorage.setItem("identity", identity.export())
- setLogs("Your new Semaphore identity has just been created 🎉")
- }, [setLogs])
+ setLog("Your new Semaphore identity has just been created 🎉")
+ }, [setLog])
return (
<>
- Identities
+ Identities
The identity of a user in the Semaphore protocol. A{" "}
@@ -59,35 +59,36 @@ export default function IdentitiesPage() {
public/private key pair and a commitment, used as the public identifier of the identity.
-
+
-
+
Identity
- {_identity && (
-
- New
-
- )}
- {_identity ? (
-
-
-
Private Key: {_identity.privateKey.toString()}
-
Commitment: {_identity.commitment.toString()}
-
-
- ) : (
-
-
- Create identity
-
+ {_identity && (
+
+
+ Private Key (base64) : {_identity.export()}
+
+
+ Public Key : [{_identity.publicKey[0].toString()},{" "}
+ {_identity.publicKey[1].toString()}]
+
+
+ Commitment : {_identity.commitment.toString()}
+
)}
-
+
+
+ Create identity
+
+
+
+
-
router.push("/groups"))} />
+ router.push("/group"))} />
>
)
}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/proofs/page.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/proofs/page.tsx
index de060019a..0ee9a92fd 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/app/proofs/page.tsx
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/app/proofs/page.tsx
@@ -1,78 +1,94 @@
"use client"
-import { Group, Identity, generateProof } from "@semaphore-protocol/core"
+import Stepper from "@/components/Stepper"
+import { useLogContext } from "@/context/LogContext"
+import { useSemaphoreContext } from "@/context/SemaphoreContext"
+import { generateProof, Group } from "@semaphore-protocol/core"
+import { encodeBytes32String, ethers } from "ethers"
import { useRouter } from "next/navigation"
-import { useCallback, useContext, useEffect, useState } from "react"
+import { useCallback, useEffect, useMemo, useState } from "react"
import Feedback from "../../../contract-artifacts/Feedback.json"
-import Stepper from "../../components/Stepper"
-import LogsContext from "../../context/LogsContext"
-import SemaphoreContext from "../../context/SemaphoreContext"
+import useSemaphoreIdentity from "@/hooks/useSemaphoreIdentity"
export default function ProofsPage() {
const router = useRouter()
- const { setLogs } = useContext(LogsContext)
- const { _users, _feedback, refreshFeedback, addFeedback } = useContext(SemaphoreContext)
+ const { setLog } = useLogContext()
+ const { _users, _feedback, refreshFeedback, addFeedback } = useSemaphoreContext()
const [_loading, setLoading] = useState(false)
- const [_identity, setIdentity] = useState()
-
- useEffect(() => {
- const privateKey = localStorage.getItem("identity")
-
- if (!privateKey) {
- router.push("/")
- return
- }
-
- setIdentity(new Identity(privateKey))
- }, [router])
+ const { _identity } = useSemaphoreIdentity()
useEffect(() => {
if (_feedback.length > 0) {
- setLogs(`${_feedback.length} feedback retrieved from the group 🤙🏽`)
+ setLog(`${_feedback.length} feedback retrieved from the group 🤙🏽`)
}
- }, [_feedback, setLogs])
+ }, [_feedback, setLog])
+
+ const feedback = useMemo(() => [..._feedback].reverse(), [_feedback])
const sendFeedback = useCallback(async () => {
if (!_identity) {
return
}
- if (typeof process.env.NEXT_PUBLIC_GROUP_ID !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_GROUP_ID in your .env file")
- }
-
const feedback = prompt("Please enter your feedback:")
if (feedback && _users) {
setLoading(true)
- setLogs(`Posting your anonymous feedback...`)
+ setLog(`Posting your anonymous feedback...`)
try {
const group = new Group(_users)
- const { points, merkleTreeDepth, merkleTreeRoot, nullifier, message } = await generateProof(
+ const message = encodeBytes32String(feedback)
+
+ const { points, merkleTreeDepth, merkleTreeRoot, nullifier } = await generateProof(
_identity,
group,
- feedback,
+ message,
process.env.NEXT_PUBLIC_GROUP_ID as string
)
- let response: any
-
- if (process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK) {
- response = await fetch(process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK, {
+ let feedbackSent: boolean = false
+ const params = [merkleTreeDepth, merkleTreeRoot, nullifier, message, points]
+ if (process.env.NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK) {
+ const response = await fetch(process.env.NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
abi: Feedback.abi,
- address: process.env.FEEDBACK_CONTRACT_ADDRESS,
+ address: process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS,
functionName: "sendFeedback",
- functionParameters: [merkleTreeDepth, merkleTreeRoot, nullifier, message, points]
+ functionParameters: params
})
})
+
+ if (response.status === 200) {
+ feedbackSent = true
+ }
+ } else if (
+ process.env.NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT &&
+ process.env.NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID &&
+ process.env.GELATO_RELAYER_API_KEY
+ ) {
+ const iface = new ethers.Interface(Feedback.abi)
+ const request = {
+ chainId: process.env.NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID,
+ target: process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS,
+ data: iface.encodeFunctionData("sendFeedback", params),
+ sponsorApiKey: process.env.GELATO_RELAYER_API_KEY
+ }
+ const response = await fetch(process.env.NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(request)
+ })
+
+ if (response.status === 201) {
+ feedbackSent = true
+ }
} else {
- response = await fetch("api/feedback", {
+ const response = await fetch("api/feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
@@ -83,24 +99,28 @@ export default function ProofsPage() {
points
})
})
+
+ if (response.status === 200) {
+ feedbackSent = true
+ }
}
- if (response.status === 200) {
+ if (feedbackSent) {
addFeedback(feedback)
- setLogs(`Your feedback has been posted 🎉`)
+ setLog(`Your feedback has been posted 🎉`)
} else {
- setLogs("Some error occurred, please try again!")
+ setLog("Some error occurred, please try again!")
}
} catch (error) {
console.error(error)
- setLogs("Some error occurred, please try again!")
+ setLog("Some error occurred, please try again!")
} finally {
setLoading(false)
}
}
- }, [_identity, _users, addFeedback, setLogs])
+ }, [_identity, _users, addFeedback, setLoading, setLog])
return (
<>
@@ -122,22 +142,23 @@ export default function ProofsPage() {
-
Feedback messages ({_feedback.length})
-
+ Feedback ({_feedback.length})
+
+
+
+
+
+
Refresh
-
-
- Send Feedback
- {_loading &&
}
-
-
-
- {_feedback.length > 0 && (
-
- {_feedback.map((f, i) => (
+ {feedback.length > 0 && (
+
+ {feedback.map((f, i) => (
@@ -145,9 +166,16 @@ export default function ProofsPage() {
)}
+
+
+ Send Feedback
+ {_loading &&
}
+
+
+
-
router.push("/groups")} />
+ router.push("/group")} />
>
)
}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/components/PageContainer.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/components/PageContainer.tsx
index 63b1499ee..98e441b50 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/components/PageContainer.tsx
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/components/PageContainer.tsx
@@ -1,12 +1,8 @@
"use client"
-import LogsContext from "@/context/LogsContext"
-import SemaphoreContext from "@/context/SemaphoreContext"
-import useSemaphore from "@/hooks/useSemaphore"
+import { useLogContext } from "@/context/LogContext"
import shortenString from "@/utils/shortenString"
-import { SupportedNetwork } from "@semaphore-protocol/utils"
import { usePathname } from "next/navigation"
-import { useEffect, useState } from "react"
import Link from "next/link"
export default function PageContainer({
@@ -15,15 +11,9 @@ export default function PageContainer({
children: React.ReactNode
}>) {
const pathname = usePathname()
- const semaphore = useSemaphore()
- const [_logs, setLogs] = useState("")
+ const { log } = useLogContext()
- useEffect(() => {
- semaphore.refreshUsers()
- semaphore.refreshFeedback()
- }, [])
-
- function getExplorerLink(network: SupportedNetwork, address: string) {
+ function getExplorerLink(network: string, address: string) {
switch (network) {
case "sepolia":
return `https://sepolia.etherscan.io/address/${address}`
@@ -35,7 +25,7 @@ export default function PageContainer({
}
return (
-
+ <>
Feedback
@@ -43,7 +33,7 @@ export default function PageContainer({
-
-
-
+
+
+
+
+
-
-
-
- {children}
-
-
-
+ {children}
-
+
- {_logs.endsWith("...")}
-
{_logs || `Current step: ${pathname}`}
+ {log.endsWith("...")}
+
{log || `Current step: ${pathname}`}
-
+ >
)
}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/components/Stepper.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/components/Stepper.tsx
index 55cc89330..ab10ba036 100644
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/components/Stepper.tsx
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/components/Stepper.tsx
@@ -1,3 +1,5 @@
+"use client"
+
export type StepperProps = {
step: number
onPrevClick?: () => void
@@ -8,21 +10,47 @@ export default function Stepper({ step, onPrevClick, onNextClick }: StepperProps
return (
{onPrevClick !== undefined ? (
-
+
+
+
+
+
+
Prev
) : (
-
+
)}
{step.toString()}/3
{onNextClick !== undefined ? (
-
+
Next
+
+
+
+
+
) : (
-
+
)}
)
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/context/LogContext.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/context/LogContext.tsx
new file mode 100644
index 000000000..8885447f1
--- /dev/null
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/context/LogContext.tsx
@@ -0,0 +1,37 @@
+"use client"
+
+import React, { createContext, ReactNode, useContext, useState } from "react"
+
+export type LogContextType = {
+ log: string
+ setLog: (logs: string) => void
+}
+
+const LogContext = createContext(null)
+
+interface ProviderProps {
+ children: ReactNode
+}
+
+export const LogContextProvider: React.FC = ({ children }) => {
+ const [log, setLog] = useState("")
+
+ return (
+
+ {children}
+
+ )
+}
+
+export const useLogContext = () => {
+ const context = useContext(LogContext)
+ if (context === null) {
+ throw new Error("LogContext must be used within a LogContextProvider")
+ }
+ return context
+}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/context/LogsContext.ts b/packages/cli-template-monorepo-ethers/apps/web-app/src/context/LogsContext.ts
deleted file mode 100644
index 5f5e17692..000000000
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/context/LogsContext.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from "react"
-
-export type LogsContextType = {
- _logs: string
- setLogs: (logs: string) => void
-}
-
-export default React.createContext({
- _logs: "",
- setLogs: (logs: string) => logs
-})
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/context/SemaphoreContext.ts b/packages/cli-template-monorepo-ethers/apps/web-app/src/context/SemaphoreContext.ts
deleted file mode 100644
index 4cac350ed..000000000
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/context/SemaphoreContext.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from "react"
-
-export type SemaphoreContextType = {
- _users: string[]
- _feedback: string[]
- refreshUsers: () => Promise
- addUser: (user: string) => void
- refreshFeedback: () => Promise
- addFeedback: (feedback: string) => void
-}
-
-export default React.createContext({
- _users: [],
- _feedback: [],
- refreshUsers: () => Promise.resolve(),
- addUser: () => {},
- refreshFeedback: () => Promise.resolve(),
- addFeedback: () => {}
-})
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/context/SemaphoreContext.tsx b/packages/cli-template-monorepo-ethers/apps/web-app/src/context/SemaphoreContext.tsx
new file mode 100644
index 000000000..425b545ef
--- /dev/null
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/context/SemaphoreContext.tsx
@@ -0,0 +1,94 @@
+"use client"
+
+import React, { createContext, ReactNode, useCallback, useContext, useEffect, useState } from "react"
+import { SemaphoreEthers } from "@semaphore-protocol/data"
+import { decodeBytes32String, toBeHex } from "ethers"
+
+export type SemaphoreContextType = {
+ _users: string[]
+ _feedback: string[]
+ refreshUsers: () => Promise
+ addUser: (user: string) => void
+ refreshFeedback: () => Promise
+ addFeedback: (feedback: string) => void
+}
+
+const SemaphoreContext = createContext(null)
+
+interface ProviderProps {
+ children: ReactNode
+}
+
+const ethereumNetwork =
+ process.env.NEXT_PUBLIC_DEFAULT_NETWORK === "localhost"
+ ? "http://127.0.0.1:8545"
+ : process.env.NEXT_PUBLIC_DEFAULT_NETWORK
+
+export const SemaphoreContextProvider: React.FC = ({ children }) => {
+ const [_users, setUsers] = useState([])
+ const [_feedback, setFeedback] = useState([])
+
+ const refreshUsers = useCallback(async (): Promise => {
+ const semaphore = new SemaphoreEthers(ethereumNetwork, {
+ address: process.env.NEXT_PUBLIC_SEMAPHORE_CONTRACT_ADDRESS,
+ projectId: process.env.NEXT_PUBLIC_INFURA_API_KEY
+ })
+
+ const members = await semaphore.getGroupMembers(process.env.NEXT_PUBLIC_GROUP_ID as string)
+
+ setUsers(members.map((member) => member.toString()))
+ }, [])
+
+ const addUser = useCallback(
+ (user: any) => {
+ setUsers([..._users, user])
+ },
+ [_users]
+ )
+
+ const refreshFeedback = useCallback(async (): Promise => {
+ const semaphore = new SemaphoreEthers(ethereumNetwork, {
+ address: process.env.NEXT_PUBLIC_SEMAPHORE_CONTRACT_ADDRESS,
+ projectId: process.env.NEXT_PUBLIC_INFURA_API_KEY
+ })
+
+ const proofs = await semaphore.getGroupValidatedProofs(process.env.NEXT_PUBLIC_GROUP_ID as string)
+
+ setFeedback(proofs.map(({ message }: any) => decodeBytes32String(toBeHex(message, 32))))
+ }, [])
+
+ const addFeedback = useCallback(
+ (feedback: string) => {
+ setFeedback([..._feedback, feedback])
+ },
+ [_feedback]
+ )
+
+ useEffect(() => {
+ refreshUsers()
+ refreshFeedback()
+ }, [refreshFeedback, refreshUsers])
+
+ return (
+
+ {children}
+
+ )
+}
+
+export const useSemaphoreContext = () => {
+ const context = useContext(SemaphoreContext)
+ if (context === null) {
+ throw new Error("SemaphoreContext must be used within a SemaphoreContextProvider")
+ }
+ return context
+}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/hooks/useSemaphore.ts b/packages/cli-template-monorepo-ethers/apps/web-app/src/hooks/useSemaphore.ts
deleted file mode 100644
index 4f5dab63e..000000000
--- a/packages/cli-template-monorepo-ethers/apps/web-app/src/hooks/useSemaphore.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { SemaphoreEthers } from "@semaphore-protocol/data"
-import { decodeBytes32String, toBeHex } from "ethers"
-import { useCallback, useState } from "react"
-import { SemaphoreContextType } from "../context/SemaphoreContext"
-
-const ethereumNetwork =
- process.env.NEXT_PUBLIC_DEFAULT_NETWORK === "localhost"
- ? "http://127.0.0.1:8545"
- : process.env.NEXT_PUBLIC_DEFAULT_NETWORK
-
-export default function useSemaphore(): SemaphoreContextType {
- const [_users, setUsers] = useState([])
- const [_feedback, setFeedback] = useState([])
-
- const refreshUsers = useCallback(async (): Promise => {
- const semaphore = new SemaphoreEthers(ethereumNetwork, {
- address: process.env.NEXT_PUBLIC_SEMAPHORE_CONTRACT_ADDRESS
- })
-
- const members = await semaphore.getGroupMembers(process.env.NEXT_PUBLIC_GROUP_ID as string)
-
- setUsers(members)
- }, [])
-
- const addUser = useCallback(
- (user: any) => {
- setUsers([..._users, user])
- },
- [_users]
- )
-
- const refreshFeedback = useCallback(async (): Promise => {
- const semaphore = new SemaphoreEthers(ethereumNetwork, {
- address: process.env.NEXT_PUBLIC_SEMAPHORE_CONTRACT_ADDRESS
- })
-
- const proofs = await semaphore.getGroupValidatedProofs(process.env.NEXT_PUBLIC_GROUP_ID as string)
-
- setFeedback(proofs.map(({ message }: any) => decodeBytes32String(toBeHex(message, 32))))
- }, [])
-
- const addFeedback = useCallback(
- (feedback: string) => {
- setFeedback([..._feedback, feedback])
- },
- [_feedback]
- )
-
- return {
- _users,
- _feedback,
- refreshUsers,
- addUser,
- refreshFeedback,
- addFeedback
- }
-}
diff --git a/packages/cli-template-monorepo-ethers/apps/web-app/src/hooks/useSemaphoreIdentity.ts b/packages/cli-template-monorepo-ethers/apps/web-app/src/hooks/useSemaphoreIdentity.ts
new file mode 100644
index 000000000..26e2ba773
--- /dev/null
+++ b/packages/cli-template-monorepo-ethers/apps/web-app/src/hooks/useSemaphoreIdentity.ts
@@ -0,0 +1,23 @@
+import { useEffect, useState } from "react"
+import { Identity } from "@semaphore-protocol/core"
+import { useRouter } from "next/navigation"
+
+export default function useSemaphoreIdentity() {
+ const router = useRouter()
+ const [_identity, setIdentity] = useState()
+
+ useEffect(() => {
+ const privateKey = localStorage.getItem("identity")
+
+ if (!privateKey) {
+ router.push("/")
+ return
+ }
+
+ setIdentity(new Identity(privateKey))
+ }, [router])
+
+ return {
+ _identity
+ }
+}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/.env.development b/packages/cli-template-monorepo-subgraph/apps/web-app/.env.development
index 1d41062f3..1bed8b578 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/.env.development
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/.env.development
@@ -1,4 +1,4 @@
NEXT_PUBLIC_DEFAULT_NETWORK=localhost
-NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
+NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS=0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9
NEXT_PUBLIC_SEMAPHORE_CONTRACT_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
NEXT_PUBLIC_GROUP_ID=0
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/.env.production b/packages/cli-template-monorepo-subgraph/apps/web-app/.env.production
index fa2cbb454..1bea727d0 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/.env.production
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/.env.production
@@ -1,5 +1,8 @@
NEXT_PUBLIC_DEFAULT_NETWORK=sepolia
+NEXT_PUBLIC_INFURA_API_KEY=abf67af1010b4b8d877e04244f1eac3d
NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS=
NEXT_PUBLIC_SEMAPHORE_CONTRACT_ADDRESS=0x1e0d7FF1610e480fC93BdEC510811ea2Ba6d7c2f
NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK=https://api.defender.openzeppelin.com/actions/20fce2ae-844b-4ec0-a6a2-90a3350a9d2c/runs/webhook/303216d1-fa7d-4fca-8c5b-7ba1ba544fc7/2T7i9xrkZA5j37hoaQLUuw
+NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT=https://api.gelato.digital/relays/v2/sponsored-call
+NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID=11155111
NEXT_PUBLIC_GROUP_ID=
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/.gitignore b/packages/cli-template-monorepo-subgraph/apps/web-app/.gitignore
index d183c4f53..55811698f 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/.gitignore
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/.gitignore
@@ -1,5 +1,8 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+# testing
+/coverage
+
# next.js
/.next/
/out/
@@ -7,3 +10,18 @@
# production
/build
+# misc
+.DS_Store
+*.pem
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+# Auto Generated PWA files
+public/sw.js
+public/workbox-*.js
+public/worker-*.js
+public/sw.js.map
+public/workbox-*.js.map
+public/worker-*.js.map
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/contract-artifacts/Feedback.json b/packages/cli-template-monorepo-subgraph/apps/web-app/contract-artifacts/Feedback.json
index 123448e9e..5b51c7a42 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/contract-artifacts/Feedback.json
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/contract-artifacts/Feedback.json
@@ -9,11 +9,6 @@
"internalType": "address",
"name": "semaphoreAddress",
"type": "address"
- },
- {
- "internalType": "uint256",
- "name": "_groupId",
- "type": "uint256"
}
],
"stateMutability": "nonpayable",
@@ -92,8 +87,8 @@
"type": "function"
}
],
- "bytecode": "0x608060405234801561001057600080fd5b5060405161083f38038061083f833981810160405281019061003291906101a8565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060018190555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c96e71fb600154306040518363ffffffff1660e01b81526004016100d6929190610206565b600060405180830381600087803b1580156100f057600080fd5b505af1158015610104573d6000803e3d6000fd5b50505050505061022f565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061013f82610114565b9050919050565b61014f81610134565b811461015a57600080fd5b50565b60008151905061016c81610146565b92915050565b6000819050919050565b61018581610172565b811461019057600080fd5b50565b6000815190506101a28161017c565b92915050565b600080604083850312156101bf576101be61010f565b5b60006101cd8582860161015d565b92505060206101de85828601610193565b9150509250929050565b6101f181610172565b82525050565b61020081610134565b82525050565b600060408201905061021b60008301856101e8565b61022860208301846101f7565b9392505050565b6106018061023e6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b6040516100669190610301565b60405180910390f35b6100896004803603810190610084919061037e565b6100e9565b005b6100936101ea565b6040516100a09190610409565b60405180910390f35b6100c360048036038101906100be9190610424565b6101f0565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040518060c001604052808781526020018681526020018581526020018481526020016001548152602001836008806020026040519081016040528092919082600860200280828437600081840152601f19601f820116905080830192505050505050815250905060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0d898dd600154836040518363ffffffff1660e01b81526004016101b0929190610578565b600060405180830381600087803b1580156101ca57600080fd5b505af11580156101de573d6000803e3d6000fd5b50505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b815260040161024d9291906105a2565b600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102c76102c26102bd84610282565b6102a2565b610282565b9050919050565b60006102d9826102ac565b9050919050565b60006102eb826102ce565b9050919050565b6102fb816102e0565b82525050565b600060208201905061031660008301846102f2565b92915050565b600080fd5b6000819050919050565b61033481610321565b811461033f57600080fd5b50565b6000813590506103518161032b565b92915050565b600080fd5b60008190508260206008028201111561037857610377610357565b5b92915050565b6000806000806000610180868803121561039b5761039a61031c565b5b60006103a988828901610342565b95505060206103ba88828901610342565b94505060406103cb88828901610342565b93505060606103dc88828901610342565b92505060806103ed8882890161035c565b9150509295509295909350565b61040381610321565b82525050565b600060208201905061041e60008301846103fa565b92915050565b60006020828403121561043a5761043961031c565b5b600061044884828501610342565b91505092915050565b61045a81610321565b82525050565b600060089050919050565b600081905092915050565b6000819050919050565b600061048c8383610451565b60208301905092915050565b6000602082019050919050565b6104ae81610460565b6104b8818461046b565b92506104c382610476565b8060005b838110156104f45781516104db8782610480565b96506104e683610498565b9250506001810190506104c7565b505050505050565b6101a0820160008201516105136000850182610451565b5060208201516105266020850182610451565b5060408201516105396040850182610451565b50606082015161054c6060850182610451565b50608082015161055f6080850182610451565b5060a082015161057260a08501826104a5565b50505050565b60006101c08201905061058e60008301856103fa565b61059b60208301846104fc565b9392505050565b60006040820190506105b760008301856103fa565b6105c460208301846103fa565b939250505056fea2646970667358221220f33606b2d5ad7c0dfc5d22afb43476e1974ea7fd160e1f28203a3e433f29cb4964736f6c63430008170033",
- "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b6040516100669190610301565b60405180910390f35b6100896004803603810190610084919061037e565b6100e9565b005b6100936101ea565b6040516100a09190610409565b60405180910390f35b6100c360048036038101906100be9190610424565b6101f0565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040518060c001604052808781526020018681526020018581526020018481526020016001548152602001836008806020026040519081016040528092919082600860200280828437600081840152601f19601f820116905080830192505050505050815250905060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0d898dd600154836040518363ffffffff1660e01b81526004016101b0929190610578565b600060405180830381600087803b1580156101ca57600080fd5b505af11580156101de573d6000803e3d6000fd5b50505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b815260040161024d9291906105a2565b600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102c76102c26102bd84610282565b6102a2565b610282565b9050919050565b60006102d9826102ac565b9050919050565b60006102eb826102ce565b9050919050565b6102fb816102e0565b82525050565b600060208201905061031660008301846102f2565b92915050565b600080fd5b6000819050919050565b61033481610321565b811461033f57600080fd5b50565b6000813590506103518161032b565b92915050565b600080fd5b60008190508260206008028201111561037857610377610357565b5b92915050565b6000806000806000610180868803121561039b5761039a61031c565b5b60006103a988828901610342565b95505060206103ba88828901610342565b94505060406103cb88828901610342565b93505060606103dc88828901610342565b92505060806103ed8882890161035c565b9150509295509295909350565b61040381610321565b82525050565b600060208201905061041e60008301846103fa565b92915050565b60006020828403121561043a5761043961031c565b5b600061044884828501610342565b91505092915050565b61045a81610321565b82525050565b600060089050919050565b600081905092915050565b6000819050919050565b600061048c8383610451565b60208301905092915050565b6000602082019050919050565b6104ae81610460565b6104b8818461046b565b92506104c382610476565b8060005b838110156104f45781516104db8782610480565b96506104e683610498565b9250506001810190506104c7565b505050505050565b6101a0820160008201516105136000850182610451565b5060208201516105266020850182610451565b5060408201516105396040850182610451565b50606082015161054c6060850182610451565b50608082015161055f6080850182610451565b5060a082015161057260a08501826104a5565b50505050565b60006101c08201905061058e60008301856103fa565b61059b60208301846104fc565b9392505050565b60006040820190506105b760008301856103fa565b6105c460208301846103fa565b939250505056fea2646970667358221220f33606b2d5ad7c0dfc5d22afb43476e1974ea7fd160e1f28203a3e433f29cb4964736f6c63430008170033",
+ "bytecode": "0x608060405234801561001057600080fd5b506040516108473803806108478339818101604052810190610032919061017d565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635c3f3b60306040518263ffffffff1660e01b81526004016100cb91906101b9565b6020604051808303816000875af11580156100ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061010e919061020a565b60018190555050610237565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061014a8261011f565b9050919050565b61015a8161013f565b811461016557600080fd5b50565b60008151905061017781610151565b92915050565b6000602082840312156101935761019261011a565b5b60006101a184828501610168565b91505092915050565b6101b38161013f565b82525050565b60006020820190506101ce60008301846101aa565b92915050565b6000819050919050565b6101e7816101d4565b81146101f257600080fd5b50565b600081519050610204816101de565b92915050565b6000602082840312156102205761021f61011a565b5b600061022e848285016101f5565b91505092915050565b610601806102466000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b6040516100669190610301565b60405180910390f35b6100896004803603810190610084919061037e565b6100e9565b005b6100936101ea565b6040516100a09190610409565b60405180910390f35b6100c360048036038101906100be9190610424565b6101f0565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040518060c001604052808781526020018681526020018581526020018481526020016001548152602001836008806020026040519081016040528092919082600860200280828437600081840152601f19601f820116905080830192505050505050815250905060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0d898dd600154836040518363ffffffff1660e01b81526004016101b0929190610578565b600060405180830381600087803b1580156101ca57600080fd5b505af11580156101de573d6000803e3d6000fd5b50505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b815260040161024d9291906105a2565b600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102c76102c26102bd84610282565b6102a2565b610282565b9050919050565b60006102d9826102ac565b9050919050565b60006102eb826102ce565b9050919050565b6102fb816102e0565b82525050565b600060208201905061031660008301846102f2565b92915050565b600080fd5b6000819050919050565b61033481610321565b811461033f57600080fd5b50565b6000813590506103518161032b565b92915050565b600080fd5b60008190508260206008028201111561037857610377610357565b5b92915050565b6000806000806000610180868803121561039b5761039a61031c565b5b60006103a988828901610342565b95505060206103ba88828901610342565b94505060406103cb88828901610342565b93505060606103dc88828901610342565b92505060806103ed8882890161035c565b9150509295509295909350565b61040381610321565b82525050565b600060208201905061041e60008301846103fa565b92915050565b60006020828403121561043a5761043961031c565b5b600061044884828501610342565b91505092915050565b61045a81610321565b82525050565b600060089050919050565b600081905092915050565b6000819050919050565b600061048c8383610451565b60208301905092915050565b6000602082019050919050565b6104ae81610460565b6104b8818461046b565b92506104c382610476565b8060005b838110156104f45781516104db8782610480565b96506104e683610498565b9250506001810190506104c7565b505050505050565b6101a0820160008201516105136000850182610451565b5060208201516105266020850182610451565b5060408201516105396040850182610451565b50606082015161054c6060850182610451565b50608082015161055f6080850182610451565b5060a082015161057260a08501826104a5565b50505050565b60006101c08201905061058e60008301856103fa565b61059b60208301846104fc565b9392505050565b60006040820190506105b760008301856103fa565b6105c460208301846103fa565b939250505056fea264697066735822122078569abd7f309f3107c4d19e9b4a4f4812522ccc5dc57c7ccbe2b06a5ba461b064736f6c63430008170033",
+ "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d2534146100515780637b85d27a1461006f578063a0f44c921461008b578063eed02e4b146100a9575b600080fd5b6100596100c5565b6040516100669190610301565b60405180910390f35b6100896004803603810190610084919061037e565b6100e9565b005b6100936101ea565b6040516100a09190610409565b60405180910390f35b6100c360048036038101906100be9190610424565b6101f0565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006040518060c001604052808781526020018681526020018581526020018481526020016001548152602001836008806020026040519081016040528092919082600860200280828437600081840152601f19601f820116905080830192505050505050815250905060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0d898dd600154836040518363ffffffff1660e01b81526004016101b0929190610578565b600060405180830381600087803b1580156101ca57600080fd5b505af11580156101de573d6000803e3d6000fd5b50505050505050505050565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b815260040161024d9291906105a2565b600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b5050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102c76102c26102bd84610282565b6102a2565b610282565b9050919050565b60006102d9826102ac565b9050919050565b60006102eb826102ce565b9050919050565b6102fb816102e0565b82525050565b600060208201905061031660008301846102f2565b92915050565b600080fd5b6000819050919050565b61033481610321565b811461033f57600080fd5b50565b6000813590506103518161032b565b92915050565b600080fd5b60008190508260206008028201111561037857610377610357565b5b92915050565b6000806000806000610180868803121561039b5761039a61031c565b5b60006103a988828901610342565b95505060206103ba88828901610342565b94505060406103cb88828901610342565b93505060606103dc88828901610342565b92505060806103ed8882890161035c565b9150509295509295909350565b61040381610321565b82525050565b600060208201905061041e60008301846103fa565b92915050565b60006020828403121561043a5761043961031c565b5b600061044884828501610342565b91505092915050565b61045a81610321565b82525050565b600060089050919050565b600081905092915050565b6000819050919050565b600061048c8383610451565b60208301905092915050565b6000602082019050919050565b6104ae81610460565b6104b8818461046b565b92506104c382610476565b8060005b838110156104f45781516104db8782610480565b96506104e683610498565b9250506001810190506104c7565b505050505050565b6101a0820160008201516105136000850182610451565b5060208201516105266020850182610451565b5060408201516105396040850182610451565b50606082015161054c6060850182610451565b50608082015161055f6080850182610451565b5060a082015161057260a08501826104a5565b50505050565b60006101c08201905061058e60008301856103fa565b61059b60208301846104fc565b9392505050565b60006040820190506105b760008301856103fa565b6105c460208301846103fa565b939250505056fea264697066735822122078569abd7f309f3107c4d19e9b4a4f4812522ccc5dc57c7ccbe2b06a5ba461b064736f6c63430008170033",
"linkReferences": {},
"deployedLinkReferences": {}
}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/next.config.mjs b/packages/cli-template-monorepo-subgraph/apps/web-app/next.config.mjs
index 2233f10f5..ae9ad74ec 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/next.config.mjs
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/next.config.mjs
@@ -14,7 +14,8 @@ const nextConfig = withPWA({
})({
env: {
INFURA_API_KEY: process.env.INFURA_API_KEY,
- ETHEREUM_PRIVATE_KEY: process.env.ETHEREUM_PRIVATE_KEY
+ ETHEREUM_PRIVATE_KEY: process.env.ETHEREUM_PRIVATE_KEY,
+ GELATO_RELAYER_API_KEY: process.env.GELATO_RELAYER_API_KEY
}
})
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/api/feedback/route.ts b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/api/feedback/route.ts
index 795b2c5dd..45698f55c 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/api/feedback/route.ts
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/api/feedback/route.ts
@@ -3,26 +3,14 @@ import { NextRequest } from "next/server"
import Feedback from "../../../../contract-artifacts/Feedback.json"
export async function POST(req: NextRequest) {
- if (typeof process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS in your .env file")
- }
-
- if (typeof process.env.NEXT_PUBLIC_DEFAULT_NETWORK !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_DEFAULT_NETWORK in your .env file")
- }
-
- if (typeof process.env.INFURA_API_KEY !== "string" && process.env.NEXT_PUBLIC_DEFAULT_NETWORK !== "localhost") {
- throw new Error("Please, define INFURA_API_KEY in your .env file")
- }
-
if (typeof process.env.ETHEREUM_PRIVATE_KEY !== "string") {
throw new Error("Please, define ETHEREUM_PRIVATE_KEY in your .env file")
}
const ethereumPrivateKey = process.env.ETHEREUM_PRIVATE_KEY
- const ethereumNetwork = process.env.NEXT_PUBLIC_DEFAULT_NETWORK
- const infuraApiKey = process.env.INFURA_API_KEY
- const contractAddress = process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS
+ const ethereumNetwork = process.env.NEXT_PUBLIC_DEFAULT_NETWORK as string
+ const infuraApiKey = process.env.NEXT_PUBLIC_INFURA_API_KEY as string
+ const contractAddress = process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS as string
const provider =
ethereumNetwork === "localhost"
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/api/join/route.ts b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/api/join/route.ts
index 608055f9e..1d0ee24b9 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/api/join/route.ts
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/api/join/route.ts
@@ -3,26 +3,14 @@ import { NextRequest } from "next/server"
import Feedback from "../../../../contract-artifacts/Feedback.json"
export async function POST(req: NextRequest) {
- if (typeof process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS in your .env file")
- }
-
- if (typeof process.env.NEXT_PUBLIC_DEFAULT_NETWORK !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_DEFAULT_NETWORK in your .env file")
- }
-
- if (typeof process.env.INFURA_API_KEY !== "string" && process.env.NEXT_PUBLIC_DEFAULT_NETWORK !== "localhost") {
- throw new Error("Please, define INFURA_API_KEY in your .env file")
- }
-
if (typeof process.env.ETHEREUM_PRIVATE_KEY !== "string") {
throw new Error("Please, define ETHEREUM_PRIVATE_KEY in your .env file")
}
const ethereumPrivateKey = process.env.ETHEREUM_PRIVATE_KEY
- const ethereumNetwork = process.env.NEXT_PUBLIC_DEFAULT_NETWORK
- const infuraApiKey = process.env.INFURA_API_KEY
- const contractAddress = process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS
+ const ethereumNetwork = process.env.NEXT_PUBLIC_DEFAULT_NETWORK as string
+ const infuraApiKey = process.env.NEXT_PUBLIC_INFURA_API_KEY as string
+ const contractAddress = process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS as string
const provider =
ethereumNetwork === "localhost"
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/globals.css b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/globals.css
index 505ae8579..738c3fa56 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/globals.css
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/globals.css
@@ -1,29 +1,40 @@
:root {
- --slate100: #f1f5f9;
- --slate200: #e2e8f0;
- --slate300: #cbd5e1;
- --slate400: #94a3b8;
- --slate500: #64748b;
- --slate700: #334155;
- --blue200: #bfdbfe;
- --blue500: #3b82f6;
- --blue600: #2563eb;
- --blue700: #1d4ed8;
- --blue800: #1e40af;
- --blue900: #1e3a8a;
+ --blue100: #dde6fc;
+ --blue200: #c3d4fa;
+ --blue300: #9abaf6;
+ --blue400: #6a95f0;
+ --blue500: #4771ea;
+ --blue600: #3555df;
+ --blue700: #2940cc;
+ --blue800: #2735a6;
+ --blue900: #253183;
+ --blue950: #1b2050;
--darkBlueBg: #00020d;
+ --slate50: #f7f7f8;
+ --slate100: #eeeef0;
+ --slate200: #d9d9de;
+ --slate300: #b8b9c1;
+ --slate400: #92939e;
+ --slate500: #747583;
+ --slate600: #5e5f6b;
+ --slate700: #4d4e57;
+ --slate800: #42424a;
+ --slate900: #3a3a40;
+ --slate950: #26262b;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
+ font-family:"Outfit", sans-serif;;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
+ height: 100vh;
}
body {
@@ -33,17 +44,30 @@ body {
p {
line-height: 1.5rem;
+ font-weight: 300;
+ overflow-wrap: break-word;
+ font-size: 1rem;
+}
+
+b {
+ font-weight: 600;
+}
+
+.key-wrapper {
+ padding-bottom: 1.5rem;
+ padding-left: 0.5rem;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
- max-width: 32rem;
-
+ max-width: 40vw;
+ min-width: 35rem;
margin: auto;
padding: 1rem;
-
+ height: calc(100vh - 7rem - 1px);
+ padding-bottom: 5rem;
min-height: calc(100vh - 3.5rem);
}
@@ -57,28 +81,25 @@ p {
margin-bottom: 1rem;
}
-ol {
- padding: 1rem;
-}
-
-li {
- margin-top: 1rem;
-}
-
h2 {
font-size: 2.25rem;
font-weight: 500;
margin-bottom: 1rem;
+ line-height: 1.2;
}
h3 {
- font-size: 1.125rem;
+ font-size: 1.15rem;
font-weight: 500;
}
.divider {
- height: 1px;
- background: var(--slate500);
+ opacity: 0.6;
+ border: 0;
+ border-style: solid;
+ border-bottom-width: 1px;
+ width: 100%;
+ border-color: var(--slate400);
margin: 2rem 0;
}
@@ -89,31 +110,44 @@ h3 {
}
a {
- color: var(--blue500);
+ color: var(--blue400);
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+ text-decoration-color: var(--blue400);
}
.button {
- background-color: var(--blue800);
- width: 100%;
- padding: 0.8rem 1rem;
- border: 1px;
border-radius: 100px;
- cursor: pointer;
- color: var(--slate100);
- font-size: 1.125rem;
- font-weight: 500;
- transition: all 200ms linear;
- margin-top: 1rem;
- margin-bottom: 1.5rem;
- opacity: 0.9;
+ font-weight: 400;
+ transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+ transition-duration: 200ms;
+ padding-left: 18px!important;
+ padding-right: 18px!important;
+ height: 2.5rem;
+ min-width: 2.5rem;
+ font-size: 1rem;
+ -webkit-padding-start: 1rem;
+ padding-inline-start: 1rem;
+ -webkit-padding-end: 1rem;
+ padding-inline-end: 1rem;
+ background: var(--blue500);
+ color: white;
+ background-image: linear-gradient(to right, var(--blue500), var(--blue800));
+ transition-timing-function: linear;
+ width: 100%;
+ border: none;
display: flex;
- justify-content: center;
- height: 3rem;
align-items: center;
+ justify-content: center;
}
.button:hover {
- background-color: var(--blue900);
+ background-color: var(--blue800);
+ background-image: none;
+ cursor: pointer;
}
.button:disabled {
@@ -123,54 +157,126 @@ a {
.button:disabled:hover {
background-color: var(--blue800);
+ background-image: none;
}
.button-stepper {
cursor: pointer;
- color: var(--blue600);
+ color: var(--blue500);
font-size: 1.1rem;
border: none;
background: none;
+ width: 4rem;
+ margin: 0 1rem;
+ display: flex;
+ justify-content: center;
+}
+
+.button-stepper:hover {
+ text-decoration: underline;
}
-.button-link {
+.refresh-wrapper {
+ color: var(--slate400);
+ display: flex;
+ align-items: center;
+}
+
+.refresh-wrapper:hover {
+ text-decoration: underline;
+}
+
+.refresh-button {
cursor: pointer;
color: var(--slate300);
font-size: 1.1rem;
border: none;
background: none;
+ padding-right: 1rem;
+ display: flex;
+ align-items: center;
+}
+
+.refresh-button:hover {
+ text-decoration: underline;
+}
+
+.refresh-span {
+ width: 1.5em;
+ height: 1em;
+ display: inline-block;
+ line-height: 1em;
+ -webkit-flex-shrink: 0;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ color: currentColor;
+ vertical-align: middle;
+}
+
+.refresh-icon {
+ margin-inline-end: 0.5rem
+}
+
+.stepper-icon {
+ display: inline-flex;
+ align-self: center;
+ -webkit-flex-shrink: 0;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+}
+
+.left-pad {
+ margin-inline-end: 0.5rem;
+}
+
+.right-pad {
+ margin-inline-start: 0.5rem;
+}
+
+.stepper-icon svg {
+ width: 1em;
+ height: 1em;
+ display: inline-block;
+ line-height: 1em;
+ -webkit-flex-shrink: 0;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ color: currentColor;
+ vertical-align:super
}
.box {
- padding: 0.8rem;
- border-style: solid;
- border-width: 1px;
- border-color: var(--slate500);
- border-radius: 4px;
+ border-bottom: 1px solid white;
+ padding: 0.8rem 0;
+ max-height: 50px;
}
.box-text {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
- margin: 0.5rem;
+ font-size: 1rem;
}
.header {
display: flex;
justify-content: space-between;
- padding: 1.5rem;
+ padding: 0 1.5rem;
+ height: 3.5rem;
}
.header-left {
font-size: 1.125rem;
font-weight: 500;
- text-decoration: none;
+ text-decoration: none !important;
+ display: flex;
+ align-items: center;
}
.header-right {
display: flex;
gap: 1.5rem;
+ align-items: center;
}
.footer {
@@ -188,6 +294,55 @@ a {
background: var(--slate500);
}
+.users-wrapper,.feedback-wrapper {
+ max-height: 300px;
+ overflow-y: scroll;
+}
+
+.keys-header {
+ margin-bottom: 1.5rem;
+}
+
+.users-header {
+ font-weight: 700;
+ font-size: 1.125rem;
+ font-family: 'DM Sans', sans-serif;
+}
+
+.join-group-button, .send-feedback-button {
+ margin-top: 1.5rem;
+}
+
+.github-icon {
+ width: 1.5rem;
+ height: 1.5rem;
+ display: inline-block;
+ line-height: 1em;
+ -webkit-flex-shrink: 0;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ color: currentColor;
+}
+
+.github-button {
+ cursor: pointer;
+ color: var(--slate100);
+ font-size: 1.1rem;
+ border: none;
+ background: none;
+ padding-right: 1rem;
+ display: flex;
+ align-items: center;
+ box-sizing: border-box;
+ display: inline-block;
+ overflow: visible !important;
+ fill: currentColor;
+ color: #f0f6fc !important;
+ vertical-align: middle !important;
+ width: 2rem;
+ height: 2rem;
+}
+
.loader {
width: 25px;
height: 25px;
@@ -196,11 +351,11 @@ a {
border-right: 2px solid transparent;
animation: spin 1s linear infinite;
z-index: 20;
- margin-left: 1rem;
+ margin-left: 0.5rem;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
-}
+}
\ No newline at end of file
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/group/page.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/group/page.tsx
new file mode 100644
index 000000000..28dfc4465
--- /dev/null
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/group/page.tsx
@@ -0,0 +1,177 @@
+"use client"
+import Stepper from "@/components/Stepper"
+import { useLogContext } from "@/context/LogContext"
+import { useSemaphoreContext } from "@/context/SemaphoreContext"
+import { useRouter } from "next/navigation"
+import { useCallback, useEffect, useMemo } from "react"
+import Feedback from "../../../contract-artifacts/Feedback.json"
+import { ethers } from "ethers"
+import useSemaphoreIdentity from "@/hooks/useSemaphoreIdentity"
+import { useState } from "react"
+
+export default function GroupsPage() {
+ const router = useRouter()
+ const { setLog } = useLogContext()
+ const { _users, refreshUsers, addUser } = useSemaphoreContext()
+ const [_loading, setLoading] = useState(false)
+ const { _identity } = useSemaphoreIdentity()
+
+ useEffect(() => {
+ if (_users.length > 0) {
+ setLog(`${_users.length} user${_users.length > 1 ? "s" : ""} retrieved from the group 🤙🏽`)
+ }
+ }, [_users, setLog])
+
+ const users = useMemo(() => [..._users].reverse(), [_users])
+
+ const joinGroup = useCallback(async () => {
+ if (!_identity) {
+ return
+ }
+
+ setLoading(true)
+ setLog(`Joining the Feedback group...`)
+
+ let joinedGroup: boolean = false
+
+ if (process.env.NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK) {
+ const response = await fetch(process.env.NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ abi: Feedback.abi,
+ address: process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS as string,
+ functionName: "joinGroup",
+ functionParameters: [_identity.commitment.toString()]
+ })
+ })
+
+ if (response.status === 200) {
+ joinedGroup = true
+ }
+ } else if (
+ process.env.NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT &&
+ process.env.NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID &&
+ process.env.GELATO_RELAYER_API_KEY
+ ) {
+ const iface = new ethers.Interface(Feedback.abi)
+ const request = {
+ chainId: process.env.NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID,
+ target: process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS,
+ data: iface.encodeFunctionData("joinGroup", [_identity.commitment.toString()]),
+ sponsorApiKey: process.env.GELATO_RELAYER_API_KEY
+ }
+ const response = await fetch(process.env.NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(request)
+ })
+
+ if (response.status === 201) {
+ joinedGroup = true
+ }
+ } else {
+ const response = await fetch("api/join", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ identityCommitment: _identity.commitment.toString()
+ })
+ })
+
+ if (response.status === 200) {
+ joinedGroup = true
+ }
+ }
+
+ if (joinedGroup) {
+ addUser(_identity.commitment.toString())
+
+ setLog(`You have joined the Feedback group event 🎉 Share your feedback anonymously!`)
+ } else {
+ setLog("Some error occurred, please try again!")
+ }
+
+ setLoading(false)
+ }, [_identity, addUser, setLoading, setLog])
+
+ const userHasJoined = useMemo(
+ () => _identity !== undefined && _users.includes(_identity.commitment.toString()),
+ [_identity, _users]
+ )
+
+ return (
+ <>
+ Groups
+
+
+
+ Semaphore groups
+ {" "}
+ are{" "}
+
+ Lean incremental Merkle trees
+ {" "}
+ in which each leaf contains an identity commitment for a user. Groups can be abstracted to represent
+ events, polls, or organizations.
+
+
+
+
+
+
Group users ({_users.length})
+
+
+
+
+
+
+ Refresh
+
+
+
+ {_users.length > 0 && (
+
+ {users.map((user, i) => (
+
+
+ {_identity?.commitment.toString() === user ? {user} : user}
+
+
+ ))}
+
+ )}
+
+
+
+ Join group
+ {_loading &&
}
+
+
+
+
+
+ router.push("/")}
+ onNextClick={userHasJoined ? () => router.push("/proofs") : undefined}
+ />
+ >
+ )
+}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/layout.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/layout.tsx
index e6a6d1aac..138bb727a 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/layout.tsx
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/layout.tsx
@@ -1,8 +1,10 @@
import PageContainer from "@/components/PageContainer"
import type { Metadata } from "next"
-import { Inter } from "next/font/google"
+import { LogContextProvider } from "@/context/LogContext"
+import { SemaphoreContextProvider } from "@/context/SemaphoreContext"
import "./globals.css"
+import { Inter } from "next/font/google"
const inter = Inter({ subsets: ["latin"] })
export const metadata: Metadata = {
@@ -32,8 +34,20 @@ export default function RootLayout({
}>) {
return (
+
+
+
+
+
- {children}
+
+
+ {children}
+
+
)
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/page.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/page.tsx
index 1469af1e5..ee800be1f 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/page.tsx
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/page.tsx
@@ -2,42 +2,42 @@
import { Identity } from "@semaphore-protocol/core"
import { useRouter } from "next/navigation"
-import { useCallback, useContext, useEffect, useState } from "react"
+import { useCallback, useEffect, useState } from "react"
import Stepper from "../components/Stepper"
-import LogsContext from "../context/LogsContext"
+import { useLogContext } from "../context/LogContext"
export default function IdentitiesPage() {
const router = useRouter()
- const { setLogs } = useContext(LogsContext)
+ const { setLog } = useLogContext()
const [_identity, setIdentity] = useState()
useEffect(() => {
const privateKey = localStorage.getItem("identity")
if (privateKey) {
- const identity = new Identity(privateKey)
+ const identity = Identity.import(privateKey)
setIdentity(identity)
- setLogs("Your Semaphore identity has been retrieved from the browser cache 👌🏽")
+ setLog("Your Semaphore identity has been retrieved from the browser cache 👌🏽")
} else {
- setLogs("Create your Semaphore identity 👆🏽")
+ setLog("Create your Semaphore identity 👆🏽")
}
- }, [setLogs])
+ }, [setLog])
const createIdentity = useCallback(async () => {
const identity = new Identity()
setIdentity(identity)
- localStorage.setItem("identity", identity.privateKey.toString())
+ localStorage.setItem("identity", identity.export())
- setLogs("Your new Semaphore identity has just been created 🎉")
- }, [setLogs])
+ setLog("Your new Semaphore identity has just been created 🎉")
+ }, [setLog])
return (
<>
- Identities
+ Identities
The identity of a user in the Semaphore protocol. A{" "}
@@ -59,35 +59,36 @@ export default function IdentitiesPage() {
public/private key pair and a commitment, used as the public identifier of the identity.
-
+
-
+
Identity
- {_identity && (
-
- New
-
- )}
- {_identity ? (
-
-
-
Private Key: {_identity.privateKey.toString()}
-
Commitment: {_identity.commitment.toString()}
-
-
- ) : (
-
-
- Create identity
-
+ {_identity && (
+
+
+ Private Key (base64) : {_identity.export()}
+
+
+ Public Key : [{_identity.publicKey[0].toString()},{" "}
+ {_identity.publicKey[1].toString()}]
+
+
+ Commitment : {_identity.commitment.toString()}
+
)}
-
+
+
+ Create identity
+
+
+
+
-
router.push("/groups"))} />
+ router.push("/group"))} />
>
)
}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/proofs/page.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/proofs/page.tsx
index de060019a..0ee9a92fd 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/proofs/page.tsx
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/proofs/page.tsx
@@ -1,78 +1,94 @@
"use client"
-import { Group, Identity, generateProof } from "@semaphore-protocol/core"
+import Stepper from "@/components/Stepper"
+import { useLogContext } from "@/context/LogContext"
+import { useSemaphoreContext } from "@/context/SemaphoreContext"
+import { generateProof, Group } from "@semaphore-protocol/core"
+import { encodeBytes32String, ethers } from "ethers"
import { useRouter } from "next/navigation"
-import { useCallback, useContext, useEffect, useState } from "react"
+import { useCallback, useEffect, useMemo, useState } from "react"
import Feedback from "../../../contract-artifacts/Feedback.json"
-import Stepper from "../../components/Stepper"
-import LogsContext from "../../context/LogsContext"
-import SemaphoreContext from "../../context/SemaphoreContext"
+import useSemaphoreIdentity from "@/hooks/useSemaphoreIdentity"
export default function ProofsPage() {
const router = useRouter()
- const { setLogs } = useContext(LogsContext)
- const { _users, _feedback, refreshFeedback, addFeedback } = useContext(SemaphoreContext)
+ const { setLog } = useLogContext()
+ const { _users, _feedback, refreshFeedback, addFeedback } = useSemaphoreContext()
const [_loading, setLoading] = useState(false)
- const [_identity, setIdentity] = useState()
-
- useEffect(() => {
- const privateKey = localStorage.getItem("identity")
-
- if (!privateKey) {
- router.push("/")
- return
- }
-
- setIdentity(new Identity(privateKey))
- }, [router])
+ const { _identity } = useSemaphoreIdentity()
useEffect(() => {
if (_feedback.length > 0) {
- setLogs(`${_feedback.length} feedback retrieved from the group 🤙🏽`)
+ setLog(`${_feedback.length} feedback retrieved from the group 🤙🏽`)
}
- }, [_feedback, setLogs])
+ }, [_feedback, setLog])
+
+ const feedback = useMemo(() => [..._feedback].reverse(), [_feedback])
const sendFeedback = useCallback(async () => {
if (!_identity) {
return
}
- if (typeof process.env.NEXT_PUBLIC_GROUP_ID !== "string") {
- throw new Error("Please, define NEXT_PUBLIC_GROUP_ID in your .env file")
- }
-
const feedback = prompt("Please enter your feedback:")
if (feedback && _users) {
setLoading(true)
- setLogs(`Posting your anonymous feedback...`)
+ setLog(`Posting your anonymous feedback...`)
try {
const group = new Group(_users)
- const { points, merkleTreeDepth, merkleTreeRoot, nullifier, message } = await generateProof(
+ const message = encodeBytes32String(feedback)
+
+ const { points, merkleTreeDepth, merkleTreeRoot, nullifier } = await generateProof(
_identity,
group,
- feedback,
+ message,
process.env.NEXT_PUBLIC_GROUP_ID as string
)
- let response: any
-
- if (process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK) {
- response = await fetch(process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK, {
+ let feedbackSent: boolean = false
+ const params = [merkleTreeDepth, merkleTreeRoot, nullifier, message, points]
+ if (process.env.NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK) {
+ const response = await fetch(process.env.NEXT_PUBLIC_OPENZEPPELIN_AUTOTASK_WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
abi: Feedback.abi,
- address: process.env.FEEDBACK_CONTRACT_ADDRESS,
+ address: process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS,
functionName: "sendFeedback",
- functionParameters: [merkleTreeDepth, merkleTreeRoot, nullifier, message, points]
+ functionParameters: params
})
})
+
+ if (response.status === 200) {
+ feedbackSent = true
+ }
+ } else if (
+ process.env.NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT &&
+ process.env.NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID &&
+ process.env.GELATO_RELAYER_API_KEY
+ ) {
+ const iface = new ethers.Interface(Feedback.abi)
+ const request = {
+ chainId: process.env.NEXT_PUBLIC_GELATO_RELAYER_CHAIN_ID,
+ target: process.env.NEXT_PUBLIC_FEEDBACK_CONTRACT_ADDRESS,
+ data: iface.encodeFunctionData("sendFeedback", params),
+ sponsorApiKey: process.env.GELATO_RELAYER_API_KEY
+ }
+ const response = await fetch(process.env.NEXT_PUBLIC_GELATO_RELAYER_ENDPOINT, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(request)
+ })
+
+ if (response.status === 201) {
+ feedbackSent = true
+ }
} else {
- response = await fetch("api/feedback", {
+ const response = await fetch("api/feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
@@ -83,24 +99,28 @@ export default function ProofsPage() {
points
})
})
+
+ if (response.status === 200) {
+ feedbackSent = true
+ }
}
- if (response.status === 200) {
+ if (feedbackSent) {
addFeedback(feedback)
- setLogs(`Your feedback has been posted 🎉`)
+ setLog(`Your feedback has been posted 🎉`)
} else {
- setLogs("Some error occurred, please try again!")
+ setLog("Some error occurred, please try again!")
}
} catch (error) {
console.error(error)
- setLogs("Some error occurred, please try again!")
+ setLog("Some error occurred, please try again!")
} finally {
setLoading(false)
}
}
- }, [_identity, _users, addFeedback, setLogs])
+ }, [_identity, _users, addFeedback, setLoading, setLog])
return (
<>
@@ -122,22 +142,23 @@ export default function ProofsPage() {
-
Feedback messages ({_feedback.length})
-
+ Feedback ({_feedback.length})
+
+
+
+
+
+
Refresh
-
-
- Send Feedback
- {_loading &&
}
-
-
-
- {_feedback.length > 0 && (
-
- {_feedback.map((f, i) => (
+ {feedback.length > 0 && (
+
+ {feedback.map((f, i) => (
@@ -145,9 +166,16 @@ export default function ProofsPage() {
)}
+
+
+ Send Feedback
+ {_loading &&
}
+
+
+
-
router.push("/groups")} />
+ router.push("/group")} />
>
)
}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/providers.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/providers.tsx
new file mode 100644
index 000000000..910643fc2
--- /dev/null
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/app/providers.tsx
@@ -0,0 +1,13 @@
+"use client"
+
+import { CacheProvider } from "@chakra-ui/next-js"
+import { ChakraProvider } from "@chakra-ui/react"
+import theme from "../styles/index"
+
+export default function Providers({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/components/PageContainer.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/components/PageContainer.tsx
index 63b1499ee..f4240ef7b 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/components/PageContainer.tsx
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/components/PageContainer.tsx
@@ -1,12 +1,8 @@
"use client"
-import LogsContext from "@/context/LogsContext"
-import SemaphoreContext from "@/context/SemaphoreContext"
-import useSemaphore from "@/hooks/useSemaphore"
+import { useLogContext } from "@/context/LogContext"
import shortenString from "@/utils/shortenString"
-import { SupportedNetwork } from "@semaphore-protocol/utils"
import { usePathname } from "next/navigation"
-import { useEffect, useState } from "react"
import Link from "next/link"
export default function PageContainer({
@@ -15,15 +11,9 @@ export default function PageContainer({
children: React.ReactNode
}>) {
const pathname = usePathname()
- const semaphore = useSemaphore()
- const [_logs, setLogs] = useState("")
+ const { log } = useLogContext()
- useEffect(() => {
- semaphore.refreshUsers()
- semaphore.refreshFeedback()
- }, [])
-
- function getExplorerLink(network: SupportedNetwork, address: string) {
+ function getExplorerLink(network: string, address: string) {
switch (network) {
case "sepolia":
return `https://sepolia.etherscan.io/address/${address}`
@@ -35,7 +25,7 @@ export default function PageContainer({
}
return (
-
+ <>
Feedback
@@ -43,7 +33,7 @@ export default function PageContainer({
-
-
-
+
+
+
+
+
-
-
-
- {children}
-
-
-
+ {children}
-
+
- {_logs.endsWith("...")}
-
{_logs || `Current step: ${pathname}`}
+ {log.endsWith("...")}
+
{log || `Current step: ${pathname}`}
-
+ >
)
}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/components/Stepper.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/components/Stepper.tsx
index 55cc89330..ab10ba036 100644
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/components/Stepper.tsx
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/components/Stepper.tsx
@@ -1,3 +1,5 @@
+"use client"
+
export type StepperProps = {
step: number
onPrevClick?: () => void
@@ -8,21 +10,47 @@ export default function Stepper({ step, onPrevClick, onNextClick }: StepperProps
return (
{onPrevClick !== undefined ? (
-
+
+
+
+
+
+
Prev
) : (
-
+
)}
{step.toString()}/3
{onNextClick !== undefined ? (
-
+
Next
+
+
+
+
+
) : (
-
+
)}
)
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/LogContext.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/LogContext.tsx
new file mode 100644
index 000000000..8885447f1
--- /dev/null
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/LogContext.tsx
@@ -0,0 +1,37 @@
+"use client"
+
+import React, { createContext, ReactNode, useContext, useState } from "react"
+
+export type LogContextType = {
+ log: string
+ setLog: (logs: string) => void
+}
+
+const LogContext = createContext(null)
+
+interface ProviderProps {
+ children: ReactNode
+}
+
+export const LogContextProvider: React.FC = ({ children }) => {
+ const [log, setLog] = useState("")
+
+ return (
+
+ {children}
+
+ )
+}
+
+export const useLogContext = () => {
+ const context = useContext(LogContext)
+ if (context === null) {
+ throw new Error("LogContext must be used within a LogContextProvider")
+ }
+ return context
+}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/LogsContext.ts b/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/LogsContext.ts
deleted file mode 100644
index 5f5e17692..000000000
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/LogsContext.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from "react"
-
-export type LogsContextType = {
- _logs: string
- setLogs: (logs: string) => void
-}
-
-export default React.createContext({
- _logs: "",
- setLogs: (logs: string) => logs
-})
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/SemaphoreContext.ts b/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/SemaphoreContext.ts
deleted file mode 100644
index 4cac350ed..000000000
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/SemaphoreContext.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from "react"
-
-export type SemaphoreContextType = {
- _users: string[]
- _feedback: string[]
- refreshUsers: () => Promise
- addUser: (user: string) => void
- refreshFeedback: () => Promise
- addFeedback: (feedback: string) => void
-}
-
-export default React.createContext({
- _users: [],
- _feedback: [],
- refreshUsers: () => Promise.resolve(),
- addUser: () => {},
- refreshFeedback: () => Promise.resolve(),
- addFeedback: () => {}
-})
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/SemaphoreContext.tsx b/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/SemaphoreContext.tsx
new file mode 100644
index 000000000..fd67bcf29
--- /dev/null
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/context/SemaphoreContext.tsx
@@ -0,0 +1,88 @@
+"use client"
+
+import React, { createContext, ReactNode, useCallback, useContext, useEffect, useState } from "react"
+import { SemaphoreSubgraph } from "@semaphore-protocol/data"
+import { decodeBytes32String, toBeHex } from "ethers"
+
+export type SemaphoreContextType = {
+ _users: string[]
+ _feedback: string[]
+ refreshUsers: () => Promise
+ addUser: (user: string) => void
+ refreshFeedback: () => Promise
+ addFeedback: (feedback: string) => void
+}
+
+const SemaphoreContext = createContext(null)
+
+interface ProviderProps {
+ children: ReactNode
+}
+
+const ethereumNetwork =
+ process.env.NEXT_PUBLIC_DEFAULT_NETWORK === "localhost"
+ ? "http://127.0.0.1:8545"
+ : process.env.NEXT_PUBLIC_DEFAULT_NETWORK
+
+export const SemaphoreContextProvider: React.FC = ({ children }) => {
+ const [_users, setUsers] = useState([])
+ const [_feedback, setFeedback] = useState([])
+
+ const refreshUsers = useCallback(async (): Promise => {
+ const semaphore = new SemaphoreSubgraph(ethereumNetwork)
+
+ const members = await semaphore.getGroupMembers(process.env.NEXT_PUBLIC_GROUP_ID as string)
+
+ setUsers(members.map((member) => member.toString()))
+ }, [])
+
+ const addUser = useCallback(
+ (user: any) => {
+ setUsers([..._users, user])
+ },
+ [_users]
+ )
+
+ const refreshFeedback = useCallback(async (): Promise => {
+ const semaphore = new SemaphoreSubgraph(ethereumNetwork)
+
+ const proofs = await semaphore.getGroupValidatedProofs(process.env.NEXT_PUBLIC_GROUP_ID as string)
+
+ setFeedback(proofs.map(({ message }: any) => decodeBytes32String(toBeHex(message, 32))))
+ }, [])
+
+ const addFeedback = useCallback(
+ (feedback: string) => {
+ setFeedback([..._feedback, feedback])
+ },
+ [_feedback]
+ )
+
+ useEffect(() => {
+ refreshUsers()
+ refreshFeedback()
+ }, [refreshFeedback, refreshUsers])
+
+ return (
+
+ {children}
+
+ )
+}
+
+export const useSemaphoreContext = () => {
+ const context = useContext(SemaphoreContext)
+ if (context === null) {
+ throw new Error("SemaphoreContext must be used within a SemaphoreContextProvider")
+ }
+ return context
+}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/hooks/useSemaphore.ts b/packages/cli-template-monorepo-subgraph/apps/web-app/src/hooks/useSemaphore.ts
deleted file mode 100644
index 7af264f82..000000000
--- a/packages/cli-template-monorepo-subgraph/apps/web-app/src/hooks/useSemaphore.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { SemaphoreSubgraph } from "@semaphore-protocol/data"
-import { decodeBytes32String, toBeHex } from "ethers"
-import { useCallback, useState } from "react"
-import { SemaphoreContextType } from "../context/SemaphoreContext"
-
-const ethereumNetwork =
- process.env.NEXT_PUBLIC_DEFAULT_NETWORK === "localhost"
- ? "http://127.0.0.1:8545"
- : process.env.NEXT_PUBLIC_DEFAULT_NETWORK
-
-export default function useSemaphore(): SemaphoreContextType {
- const [_users, setUsers] = useState([])
- const [_feedback, setFeedback] = useState([])
-
- const refreshUsers = useCallback(async (): Promise => {
- const semaphore = new SemaphoreSubgraph(ethereumNetwork)
-
- const group = await semaphore.getGroup(process.env.NEXT_PUBLIC_GROUP_ID as string, { members: true })
-
- setUsers(group.members!)
- }, [])
-
- const addUser = useCallback(
- (user: any) => {
- setUsers([..._users, user])
- },
- [_users]
- )
-
- const refreshFeedback = useCallback(async (): Promise => {
- const semaphore = new SemaphoreSubgraph(ethereumNetwork)
-
- const group = await semaphore.getGroup(process.env.NEXT_PUBLIC_GROUP_ID as string, {
- validatedProofs: true
- })
-
- setFeedback(group.validatedProofs!.map(({ message }: any) => decodeBytes32String(toBeHex(message, 32))))
- }, [])
-
- const addFeedback = useCallback(
- (feedback: string) => {
- setFeedback([..._feedback, feedback])
- },
- [_feedback]
- )
-
- return {
- _users,
- _feedback,
- refreshUsers,
- addUser,
- refreshFeedback,
- addFeedback
- }
-}
diff --git a/packages/cli-template-monorepo-subgraph/apps/web-app/src/hooks/useSemaphoreIdentity.ts b/packages/cli-template-monorepo-subgraph/apps/web-app/src/hooks/useSemaphoreIdentity.ts
new file mode 100644
index 000000000..26e2ba773
--- /dev/null
+++ b/packages/cli-template-monorepo-subgraph/apps/web-app/src/hooks/useSemaphoreIdentity.ts
@@ -0,0 +1,23 @@
+import { useEffect, useState } from "react"
+import { Identity } from "@semaphore-protocol/core"
+import { useRouter } from "next/navigation"
+
+export default function useSemaphoreIdentity() {
+ const router = useRouter()
+ const [_identity, setIdentity] = useState()
+
+ useEffect(() => {
+ const privateKey = localStorage.getItem("identity")
+
+ if (!privateKey) {
+ router.push("/")
+ return
+ }
+
+ setIdentity(new Identity(privateKey))
+ }, [router])
+
+ return {
+ _identity
+ }
+}