Compare commits

..

9 Commits

Author SHA1 Message Date
Vivian Plasencia
cd04b0c5c7 chore: v4.12.0 2025-07-17 16:38:26 +02:00
aritra
b6a7a9883b refactor: transport selection logic (#1007) 2025-07-17 16:30:04 +02:00
aritra
fc1fe4d086 Allow injecting existing PublicClient into SemaphoreViem (#1006)
* refactor: allow injecting existing PublicClient into SemaphoreViem

* fix: remove comment
2025-07-17 13:48:13 +02:00
Vivian Plasencia
feb8c9c97d docs(website): add ethdam video recording (#996) 2025-06-09 15:50:00 +02:00
César Henrique
b56e9690a8 Test(data): add test coverage for getEvents utility (#989)
test(data): add test coverage for getEvents utility

re #986
2025-05-26 19:10:24 +02:00
Vivian Plasencia
c973bab503 cocs: add new issue link (#988)
* docs: update contributing file

* docs: update pull request template

* docs: add new issue link to contributing
2025-05-25 23:14:11 +02:00
Vivian Plasencia
27aee4bf43 Docs/update contributing (#987)
* docs: update contributing file

* docs: update pull request template
2025-05-25 22:54:29 +02:00
Vivian Plasencia
ea62310df6 chore: v4.11.1 2025-05-21 16:49:07 +02:00
Vivian Plasencia
6bc415f4d5 fix(data): fix semaphore viem class (#985)
re #984
2025-05-21 16:44:38 +02:00
28 changed files with 278 additions and 358 deletions

View File

@@ -37,4 +37,4 @@
- [ ] New and existing unit tests pass locally with my changes
> [!IMPORTANT]
> We do not accept minor grammatical fixes (e.g., correcting typos, rewording sentences) unless they significantly improve clarity in technical documentation. These contributions, while appreciated, are not a priority for merging. If there is a grammatical error feel free to message the team.
> We do not accept pull requests for minor grammatical fixes (e.g., correcting typos, rewording sentences) or for fixing broken links, unless they significantly improve clarity or functionality. These contributions, while appreciated, are not a priority for merging. If you notice any of these issues, please create a [GitHub Issue](https://github.com/semaphore-protocol/semaphore/issues/new?template=BLANK_ISSUE) to report them so they can be properly tracked and addressed.

View File

@@ -31,7 +31,7 @@ Pull requests are great if you want to add a feature or fix a bug. Here's a quic
7. Push to your fork and submit a pull request on our `main` branch. Please provide us with some explanation of why you made the changes you made. For new features make sure to explain a standard use case to us.
> [!IMPORTANT]
> We do not accept minor grammatical fixes (e.g., correcting typos, rewording sentences) unless they significantly improve clarity in technical documentation. These contributions, while appreciated, are not a priority for merging. If there is a grammatical error feel free to message the team.
> We do not accept pull requests for minor grammatical fixes (e.g., correcting typos, rewording sentences) or for fixing broken links, unless they significantly improve clarity or functionality. These contributions, while appreciated, are not a priority for merging. If you notice any of these issues, please create a [GitHub Issue](https://github.com/semaphore-protocol/semaphore/issues/new?template=BLANK_ISSUE) to report them so they can be properly tracked and addressed.
## CI (Github Actions) Tests

View File

@@ -19,7 +19,7 @@
"@docusaurus/core": "3.5.2",
"@docusaurus/preset-classic": "3.5.2",
"@mdx-js/react": "^3.0.0",
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/utils": "4.12.0",
"@svgr/webpack": "^5.5.0",
"clsx": "^1.2.1",
"docusaurus-plugin-sass": "^0.2.5",

View File

@@ -118,5 +118,13 @@
"speakers": ["Vivian Plasencia"],
"url": "https://youtu.be/ux5Xy_lpiYk",
"thumbnail": "https://img.youtube.com/vi/ux5Xy_lpiYk/0.jpg"
},
{
"title": "Scaling Semaphore",
"eventName": "ETHDam",
"date": "2025-05-10",
"speakers": ["Vivian Plasencia"],
"url": "https://youtu.be/IkYtKSQLR-A",
"thumbnail": "https://img.youtube.com/vi/IkYtKSQLR-A/0.jpg"
}
]

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/circuits",
"version": "4.11.0",
"version": "4.12.0",
"description": "Semaphore Circom circuits to generate zero-knowledge proofs.",
"license": "MIT",
"files": [

View File

@@ -1,10 +1,10 @@
{
"name": "@semaphore-protocol/cli-template-contracts-foundry",
"version": "4.11.0",
"version": "4.12.0",
"description": "Semaphore Foundry template.",
"license": "Unlicense",
"devDependencies": {
"@semaphore-protocol/contracts": "4.11.0",
"@semaphore-protocol/contracts": "4.12.0",
"@zk-kit/lean-imt.sol": "2.0.1",
"forge-std": "github:foundry-rs/forge-std#v1.9.4",
"poseidon-solidity": "0.0.5",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/cli-template-contracts-hardhat",
"version": "4.11.0",
"version": "4.12.0",
"description": "Semaphore Hardhat template.",
"license": "Unlicense",
"files": [
@@ -42,9 +42,9 @@
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@semaphore-protocol/core": "4.11.0",
"@semaphore-protocol/hardhat": "4.11.0",
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/core": "4.12.0",
"@semaphore-protocol/hardhat": "4.12.0",
"@semaphore-protocol/utils": "4.12.0",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/chai": "^4.2.0",
@@ -72,7 +72,7 @@
"typescript": "^5.3.3"
},
"dependencies": {
"@semaphore-protocol/contracts": "4.11.0"
"@semaphore-protocol/contracts": "4.12.0"
},
"packageManager": "yarn@4.1.0"
}

View File

@@ -20,9 +20,9 @@
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@semaphore-protocol/core": "4.11.0",
"@semaphore-protocol/hardhat": "4.11.0",
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/core": "4.12.0",
"@semaphore-protocol/hardhat": "4.12.0",
"@semaphore-protocol/utils": "4.12.0",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/chai": "^4.2.0",
@@ -50,7 +50,7 @@
"typescript": "^5.3.3"
},
"dependencies": {
"@semaphore-protocol/contracts": "4.11.0"
"@semaphore-protocol/contracts": "4.12.0"
},
"packageManager": "yarn@4.1.0"
}

View File

@@ -9,9 +9,9 @@
"lint": "next lint"
},
"dependencies": {
"@semaphore-protocol/core": "4.11.0",
"@semaphore-protocol/data": "4.11.0",
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/core": "4.12.0",
"@semaphore-protocol/data": "4.12.0",
"@semaphore-protocol/utils": "4.12.0",
"ethers": "^6.13.4",
"next": "14.1.0",
"next-pwa": "^5.6.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/cli-template-monorepo-ethers",
"version": "4.11.0",
"version": "4.12.0",
"description": "Semaphore Hardhat + Next.js + SemaphoreEthers template.",
"license": "Unlicense",
"files": [

View File

@@ -20,9 +20,9 @@
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@semaphore-protocol/core": "4.11.0",
"@semaphore-protocol/hardhat": "4.11.0",
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/core": "4.12.0",
"@semaphore-protocol/hardhat": "4.12.0",
"@semaphore-protocol/utils": "4.12.0",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/chai": "^4.2.0",
@@ -50,7 +50,7 @@
"typescript": "^5.3.3"
},
"dependencies": {
"@semaphore-protocol/contracts": "4.11.0"
"@semaphore-protocol/contracts": "4.12.0"
},
"packageManager": "yarn@4.1.0"
}

View File

@@ -9,9 +9,9 @@
"lint": "next lint"
},
"dependencies": {
"@semaphore-protocol/core": "4.11.0",
"@semaphore-protocol/data": "4.11.0",
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/core": "4.12.0",
"@semaphore-protocol/data": "4.12.0",
"@semaphore-protocol/utils": "4.12.0",
"ethers": "^6.13.4",
"next": "14.1.0",
"next-pwa": "^5.6.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/cli-template-monorepo-subgraph",
"version": "4.11.0",
"version": "4.12.0",
"description": "Semaphore Hardhat + Next.js + SemaphoreSubgraph template.",
"license": "Unlicense",
"files": [

View File

@@ -1,7 +1,7 @@
{
"name": "@semaphore-protocol/cli",
"type": "module",
"version": "4.11.0",
"version": "4.12.0",
"description": "A command line tool to set up your Semaphore project and get group data.",
"license": "MIT",
"bin": {
@@ -41,8 +41,8 @@
"rollup-plugin-cleanup": "^3.2.1"
},
"dependencies": {
"@semaphore-protocol/data": "4.11.0",
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/data": "4.12.0",
"@semaphore-protocol/utils": "4.12.0",
"axios": "^1.6.7",
"boxen": "^7.1.1",
"chalk": "^5.3.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/contracts",
"version": "4.11.0",
"version": "4.12.0",
"description": "Semaphore contracts to manage groups and broadcast anonymous signals.",
"license": "MIT",
"files": [

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/core",
"version": "4.11.0",
"version": "4.12.0",
"description": "Core library for the essential Semaphore features.",
"type": "module",
"license": "MIT",
@@ -42,8 +42,8 @@
"access": "public"
},
"dependencies": {
"@semaphore-protocol/group": "4.11.0",
"@semaphore-protocol/identity": "4.11.0",
"@semaphore-protocol/proof": "4.11.0"
"@semaphore-protocol/group": "4.12.0",
"@semaphore-protocol/identity": "4.12.0",
"@semaphore-protocol/proof": "4.12.0"
}
}

View File

@@ -173,10 +173,10 @@ const admin = await semaphoreEthers.getGroupAdmin("42")
const members = await semaphoreEthers.getGroupMembers("42")
```
**Fetch Verified Proofs**
**Fetch Validated Proofs**
```typescript
const verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs("42")
const verifiedProofs = await semaphoreEthers.getGroupValidatedProofs("42")
```
**Check Group Membership**

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/data",
"version": "4.11.0",
"version": "4.12.0",
"description": "A library for querying Semaphore smart contract.",
"type": "module",
"license": "MIT",
@@ -37,7 +37,7 @@
"rollup-plugin-cleanup": "^3.2.1"
},
"dependencies": {
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/utils": "4.12.0",
"@zk-kit/utils": "1.3.0",
"axios": "1.6.6",
"ethers": "6.13.4",

View File

@@ -1,4 +1,4 @@
import { Chain, Transport } from "viem"
import { Chain, PublicClient, Transport } from "viem"
export type EthersNetwork =
| "mainnet"
@@ -65,4 +65,5 @@ export type ViemOptions = {
transport?: Transport // Transport from viem
chain?: Chain // Chain from viem
apiKey?: string
publicClient?: PublicClient
}

View File

@@ -93,13 +93,6 @@ export default class SemaphoreViem {
constructor(networkOrEthereumURL: ViemNetwork | string = defaultNetwork, options: ViemOptions = {}) {
requireString(networkOrEthereumURL, "networkOrEthereumURL")
if (options.transport) {
// Transport is provided directly
} else if (!networkOrEthereumURL.startsWith("http")) {
// Default to http transport if no transport is provided and network is not a URL
options.transport = http()
}
if (options.apiKey) {
requireString(options.apiKey, "apiKey")
}
@@ -108,7 +101,7 @@ export default class SemaphoreViem {
const { address, startBlock } = getDeployedContract(networkOrEthereumURL as SupportedNetwork)
options.address ??= address
options.startBlock ??= BigInt(startBlock || 0)
options.startBlock ??= BigInt(startBlock)
} else {
options.startBlock ??= 0n
}
@@ -117,13 +110,12 @@ export default class SemaphoreViem {
throw new Error(`Network '${networkOrEthereumURL}' needs a Semaphore contract address`)
}
// Create the public client
let transport: Transport
if (options.transport) {
transport = options.transport
} else if (!networkOrEthereumURL.startsWith("http")) {
transport = http()
} else {
// If no transport is provided, use http transport with the URL
transport = http(networkOrEthereumURL)
}
@@ -131,10 +123,12 @@ export default class SemaphoreViem {
this._options = options
// Create the public client
this._client = createPublicClient({
transport,
chain: options.chain as Chain
})
this._client =
options.publicClient ??
createPublicClient({
transport,
chain: options.chain as Chain
})
// Create the contract instance
this._contract = getContract({
@@ -244,7 +238,7 @@ export default class SemaphoreViem {
abi: SemaphoreABI,
eventName: "MemberRemoved",
args: {
groupId
groupId: BigInt(groupId)
},
fromBlock: BigInt(this._options.startBlock || 0)
})) as MemberRemovedLog[]
@@ -255,7 +249,7 @@ export default class SemaphoreViem {
abi: SemaphoreABI,
eventName: "MemberUpdated",
args: {
groupId
groupId: BigInt(groupId)
},
fromBlock: BigInt(this._options.startBlock || 0)
})) as MemberUpdatedLog[]
@@ -287,7 +281,7 @@ export default class SemaphoreViem {
abi: SemaphoreABI,
eventName: "MembersAdded",
args: {
groupId
groupId: BigInt(groupId)
},
fromBlock: BigInt(this._options.startBlock || 0)
})) as MembersAddedLog[]
@@ -309,7 +303,7 @@ export default class SemaphoreViem {
abi: SemaphoreABI,
eventName: "MemberAdded",
args: {
groupId
groupId: BigInt(groupId)
},
fromBlock: BigInt(this._options.startBlock || 0)
})) as MemberAddedLog[]
@@ -328,12 +322,10 @@ export default class SemaphoreViem {
index += identityCommitments.length
} else {
const currentIndex = index // Create a closure to capture the current index value
const event = memberAddedEvents.find((e) => e.args.index && Number(e.args.index) === currentIndex)
const event = memberAddedEvents.find((e) => Number(e.args.index) === currentIndex)
if (event && event.args.identityCommitment) {
members.push(event.args.identityCommitment.toString())
} else {
members.push("0") // Placeholder for missing member
}
index += 1
@@ -372,7 +364,7 @@ export default class SemaphoreViem {
abi: SemaphoreABI,
eventName: "ProofValidated",
args: {
groupId
groupId: BigInt(groupId)
},
fromBlock: BigInt(this._options.startBlock || 0)
})) as ProofValidatedLog[]

View File

@@ -0,0 +1,108 @@
import { Contract, EventLog } from "ethers/contract"
import getEvents from "../src/getEvents"
jest.mock("ethers/contract", () => ({
__esModule: true,
Contract: jest.fn(),
EventLog: jest.fn()
}))
describe("getEvents", () => {
let mockContract: jest.Mocked<Contract>
beforeEach(() => {
mockContract = {
filters: {
TestEvent: jest.fn()
},
queryFilter: jest.fn()
} as any
})
describe("# getEvents", () => {
it("should fetch events with basic parameters", async () => {
const mockEvents = [
{
args: ["arg1", "arg2"],
blockNumber: 123
},
{
args: ["arg3", "arg4"],
blockNumber: 124
}
] as EventLog[]
mockContract.queryFilter.mockResolvedValueOnce(mockEvents)
const result = await getEvents(mockContract, "TestEvent")
expect(mockContract.filters.TestEvent).toHaveBeenCalled()
expect(mockContract.queryFilter).toHaveBeenCalled()
expect(result).toEqual([
["arg1", "arg2", 123],
["arg3", "arg4", 124]
])
})
it("should handle filter arguments", async () => {
const filterArgs = ["arg1", "arg2"]
const mockEvents = [
{
args: ["arg1", "arg2"],
blockNumber: 123
}
] as EventLog[]
mockContract.queryFilter.mockResolvedValueOnce(mockEvents)
await getEvents(mockContract, "TestEvent", filterArgs)
expect(mockContract.filters.TestEvent).toHaveBeenCalledWith(...filterArgs)
})
it("should use startBlock parameter", async () => {
const startBlock = 1000
const mockEvents = [
{
args: ["arg1"],
blockNumber: 1001
}
] as EventLog[]
mockContract.queryFilter.mockResolvedValueOnce(mockEvents)
await getEvents(mockContract, "TestEvent", [], startBlock)
expect(mockContract.queryFilter).toHaveBeenCalledWith(undefined, startBlock)
})
it("should handle empty events array", async () => {
mockContract.queryFilter.mockResolvedValueOnce([])
const result = await getEvents(mockContract, "TestEvent")
expect(result).toEqual([])
})
it("should handle undefined filterArgs gracefully", async () => {
const mockEvents = [
{
args: ["arg1"],
blockNumber: 101
}
] as EventLog[]
mockContract.queryFilter.mockResolvedValueOnce(mockEvents)
await getEvents(mockContract, "TestEvent", undefined)
expect(mockContract.filters.TestEvent).toHaveBeenCalledWith()
})
it("should handle contract errors", async () => {
mockContract.queryFilter.mockRejectedValue(new Error("Contract error"))
await expect(getEvents(mockContract, "TestEvent")).rejects.toThrow("Contract error")
})
})
})

View File

@@ -186,6 +186,7 @@ describe("SemaphoreViem", () => {
const groupIds = await semaphoreViem.getGroupIds()
expect(groupIds).toContain("42")
expect(groupIds).toHaveLength(2)
expect(mockGetContractEvents).toHaveBeenCalledWith(
expect.objectContaining({
eventName: "GroupCreated"
@@ -268,102 +269,34 @@ describe("SemaphoreViem", () => {
})
describe("# getGroupMembers", () => {
it("should return a list of group members", async () => {
it("should return all the existing groups members", async () => {
const semaphoreViem = createSemaphoreViem()
// Create a custom implementation for the getGroupMembers method
// @ts-ignore - Mocking the implementation
semaphoreViem.getGroupMembers = jest
.fn()
.mockResolvedValue(["0", "113", "114", "0", "209", "210", "310", "312"])
const members = await semaphoreViem.getGroupMembers("42")
// Verify results
expect(members).toHaveLength(8)
expect(members[0]).toBe("0") // Default value for missing member
expect(members[1]).toBe("113") // From MemberUpdated
expect(members[2]).toBe("114") // From MemberAdded
expect(members[3]).toBe("0") // Removed member (MemberRemoved)
expect(members[4]).toBe("209") // From MembersAdded
expect(members[5]).toBe("210") // From MembersAdded
expect(members[6]).toBe("310") // From MemberAdded
expect(members[7]).toBe("312") // From MemberAdded
})
it("should handle edge cases in event data", async () => {
const semaphoreViem = createSemaphoreViem()
// Mock the contract read methods
// @ts-ignore - Mocking the contract read methods
semaphoreViem.contract.read.getMerkleTreeSize = jest.fn().mockReturnValue(BigInt(5))
// Mock the getContractEvents method with incomplete/missing data
const mockGetContractEvents = jest.fn().mockImplementation((params) => {
if (params.eventName === "MemberRemoved") {
return [
{
// Missing args.index to test that branch
args: {
groupId: "42"
},
blockNumber: BigInt(1000)
},
{
// Missing blockNumber to test that branch
args: {
groupId: "42",
index: BigInt(1)
}
}
]
// Mock the getContractEvents method
const mockGetContractEvents = jest.fn().mockResolvedValue([
{
eventName: "MemberAdded",
args: { groupId: "42", index: BigInt(0), identityCommitment: "1" }
},
{
eventName: "MemberAdded",
args: { groupId: "42", index: BigInt(1), identityCommitment: "2" }
}
if (params.eventName === "MemberUpdated") {
return [
{
// Missing newIdentityCommitment to test that branch
args: {
groupId: "42",
index: BigInt(2)
},
blockNumber: BigInt(900)
},
{
// Missing blockNumber to test that branch
args: {
groupId: "42",
index: BigInt(3),
newIdentityCommitment: "333"
}
}
]
}
if (params.eventName === "MembersAdded") {
return [
{
// Missing identityCommitments to test that branch
args: {
groupId: "42",
startIndex: BigInt(0)
}
}
]
}
if (params.eventName === "MemberAdded") {
return []
}
return []
})
])
// @ts-ignore - Mocking the client's getContractEvents method
semaphoreViem.client.getContractEvents = mockGetContractEvents
const members = await semaphoreViem.getGroupMembers("42")
semaphoreViem.contract.read.getMerkleTreeSize = jest.fn().mockReturnValue(BigInt(2))
// Just verify that the method completes without errors
expect(members).toBeDefined()
expect(Array.isArray(members)).toBe(true)
expect(members).toHaveLength(5)
const groupMembers = await semaphoreViem.getGroupMembers("42")
expect(groupMembers).toHaveLength(2)
expect(mockGetContractEvents).toHaveBeenCalledWith(
expect.objectContaining({
eventName: "MemberAdded"
})
)
})
it("should handle all event types and update paths correctly", async () => {
@@ -394,7 +327,7 @@ describe("SemaphoreViem", () => {
args: {
groupId: "42",
index: BigInt(1),
newIdentityCommitment: "999"
newIdentityCommitment: "111"
},
blockNumber: BigInt(1000)
},
@@ -416,19 +349,43 @@ describe("SemaphoreViem", () => {
args: {
groupId: "42",
startIndex: BigInt(5),
identityCommitments: ["501", "502", "503"]
identityCommitments: ["6", "7", "8"]
}
}
]
}
if (params.eventName === "MemberAdded") {
return [
{
// Valid member added event - individual addition at index 2
args: {
groupId: "42",
index: BigInt(0),
identityCommitment: "1"
}
},
{
// Valid member added event - individual addition at index 2
args: {
groupId: "42",
index: BigInt(1),
identityCommitment: "2"
}
},
{
// Valid member added event - individual addition at index 2
args: {
groupId: "42",
index: BigInt(2),
identityCommitment: "222"
identityCommitment: "3"
}
},
{
// Valid member added event - individual addition at index 2
args: {
groupId: "42",
index: BigInt(3),
identityCommitment: "4"
}
},
{
@@ -436,7 +393,7 @@ describe("SemaphoreViem", () => {
args: {
groupId: "42",
index: BigInt(4),
identityCommitment: "444"
identityCommitment: "5"
}
},
{
@@ -444,7 +401,7 @@ describe("SemaphoreViem", () => {
args: {
groupId: "42",
index: BigInt(8),
identityCommitment: "888"
identityCommitment: "9"
}
}
]
@@ -458,17 +415,16 @@ describe("SemaphoreViem", () => {
const members = await semaphoreViem.getGroupMembers("42")
// Verify the results cover all the branches
expect(members).toHaveLength(10)
expect(members[0]).toBe("0") // Default placeholder (no event)
expect(members[1]).toBe("999") // From MemberUpdated
expect(members[2]).toBe("222") // From MemberAdded
expect(members).toHaveLength(9)
expect(members[0]).toBe("1")
expect(members[1]).toBe("111") // From MemberUpdated
expect(members[2]).toBe("3") // From MemberAdded
expect(members[3]).toBe("0") // From MemberRemoved (overriding MemberUpdated)
expect(members[4]).toBe("444") // From MemberAdded
expect(members[5]).toBe("501") // From MembersAdded (batch)
expect(members[6]).toBe("502") // From MembersAdded (batch)
expect(members[7]).toBe("503") // From MembersAdded (batch)
expect(members[8]).toBe("888") // From MemberAdded
expect(members[9]).toBe("0") // Default placeholder (no event)
expect(members[4]).toBe("5") // From MemberAdded
expect(members[5]).toBe("6") // From MembersAdded (batch)
expect(members[6]).toBe("7") // From MembersAdded (batch)
expect(members[7]).toBe("8") // From MembersAdded (batch)
expect(members[8]).toBe("9") // From MemberAdded
})
it("should throw an error if the group does not exist", async () => {
@@ -1029,151 +985,6 @@ describe("SemaphoreViem", () => {
// Should have undefined timestamp
expect(proofs[0].timestamp).toBeUndefined()
})
// Additional test for line 111 - startBlock initialization with defined startBlock
it("should handle defined startBlock from getDeployedContract", () => {
// Create a new instance with a supported network
const semaphoreViem = new SemaphoreViem("sepolia")
// @ts-ignore - accessing private property for testing
expect(semaphoreViem._options.startBlock).not.toBe(0n)
})
// Additional test for lines 249-260 - memberUpdatedEvents with valid args
it("should handle memberUpdatedEvents with valid args", async () => {
const semaphoreViem = createSemaphoreViem()
// Mock getContractEvents to return valid memberUpdatedEvents
const mockGetContractEvents = jest.fn().mockImplementation(({ eventName }) => {
if (eventName === "MemberUpdated") {
return [
{
args: {
groupId: "1",
index: 0n,
newIdentityCommitment: 123n
},
blockNumber: 123n,
address: "0x1234" as `0x${string}`,
blockHash: "0xabc" as `0x${string}`,
data: "0x" as `0x${string}`,
logIndex: 0,
transactionHash: "0xdef" as `0x${string}`,
transactionIndex: 0,
removed: false,
topics: ["0x"],
eventName: "MemberUpdated"
}
]
}
return []
})
// Mock contract read methods
const mockContract = {
read: {
getGroupAdmin: jest.fn().mockResolvedValue("0x1234"),
getMerkleTreeRoot: jest.fn().mockResolvedValue("root"),
getMerkleTreeDepth: jest.fn().mockResolvedValue(20n),
getMerkleTreeSize: jest.fn().mockResolvedValue(1n)
}
}
// @ts-ignore - Replace client and contract with mocks
semaphoreViem._client = {
getContractEvents: mockGetContractEvents
}
// @ts-ignore
semaphoreViem._contract = mockContract
const members = await semaphoreViem.getGroupMembers("1")
// Should include the updated member - actual value is "0"
expect(members).toEqual(["0"])
})
// Additional test for line 292 - membersAddedEvents with valid args
it("should handle membersAddedEvents with valid args", async () => {
const semaphoreViem = createSemaphoreViem()
// Mock getContractEvents to return valid membersAddedEvents
const mockGetContractEvents = jest.fn().mockImplementation(({ eventName }) => {
if (eventName === "MembersAdded") {
return [
{
args: {
groupId: "1",
startIndex: 0n,
identityCommitments: ["123", "456"]
},
address: "0x1234" as `0x${string}`,
blockHash: "0xabc" as `0x${string}`,
blockNumber: 123n,
data: "0x" as `0x${string}`,
logIndex: 0,
transactionHash: "0xdef" as `0x${string}`,
transactionIndex: 0,
removed: false,
topics: ["0x"],
eventName: "MembersAdded"
}
]
}
return []
})
// Mock contract read methods
const mockContract = {
read: {
getGroupAdmin: jest.fn().mockResolvedValue("0x1234"),
getMerkleTreeRoot: jest.fn().mockResolvedValue("root"),
getMerkleTreeDepth: jest.fn().mockResolvedValue(20n),
getMerkleTreeSize: jest.fn().mockResolvedValue(2n)
}
}
// @ts-ignore - Replace client and contract with mocks
semaphoreViem._client = {
getContractEvents: mockGetContractEvents
}
// @ts-ignore
semaphoreViem._contract = mockContract
const members = await semaphoreViem.getGroupMembers("1")
// Should include the batch-added members - actual values are "0", "0"
expect(members).toEqual(["0", "0"])
})
// Additional test for line 314 - non-zero merkleTreeSize
it("should handle non-zero merkleTreeSize", async () => {
const semaphoreViem = createSemaphoreViem()
// Mock contract read methods
const mockContract = {
read: {
getGroupAdmin: jest.fn().mockResolvedValue("0x1234"),
getMerkleTreeRoot: jest.fn().mockResolvedValue("root"),
getMerkleTreeDepth: jest.fn().mockResolvedValue(20n),
getMerkleTreeSize: jest.fn().mockResolvedValue(3n)
}
}
// Mock client with empty events
const mockGetContractEvents = jest.fn().mockResolvedValue([])
// @ts-ignore - Replace client and contract with mocks
semaphoreViem._client = {
getContractEvents: mockGetContractEvents
}
// @ts-ignore
semaphoreViem._contract = mockContract
const members = await semaphoreViem.getGroupMembers("1")
// Should return array with empty strings for each index
expect(members).toEqual(["0", "0", "0"])
})
})
describe("# Branch coverage for startBlock fallbacks", () => {

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/group",
"version": "4.11.0",
"version": "4.12.0",
"description": "A library to create and manage Semaphore groups.",
"type": "module",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/hardhat",
"version": "4.11.0",
"version": "4.12.0",
"description": "A Hardhat plugin to deploy Semaphore contracts.",
"type": "module",
"license": "MIT",
@@ -41,7 +41,7 @@
},
"dependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.0",
"@semaphore-protocol/contracts": "4.11.0",
"@semaphore-protocol/contracts": "4.12.0",
"ethers": "^6.13.4",
"hardhat-dependency-compiler": "^1.1.3"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/identity",
"version": "4.11.0",
"version": "4.12.0",
"description": "A library to create Semaphore identities.",
"type": "module",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/proof",
"version": "4.11.0",
"version": "4.12.0",
"description": "A library to generate and verify Semaphore proofs.",
"type": "module",
"license": "MIT",
@@ -47,11 +47,11 @@
"rollup-plugin-cleanup": "^3.2.1"
},
"peerDependencies": {
"@semaphore-protocol/group": "4.11.0",
"@semaphore-protocol/identity": "4.11.0"
"@semaphore-protocol/group": "4.12.0",
"@semaphore-protocol/identity": "4.12.0"
},
"dependencies": {
"@semaphore-protocol/utils": "4.11.0",
"@semaphore-protocol/utils": "4.12.0",
"@zk-kit/artifacts": "1.8.0",
"@zk-kit/utils": "1.3.0",
"ethers": "6.13.4",

View File

@@ -1,6 +1,6 @@
{
"name": "@semaphore-protocol/utils",
"version": "4.11.0",
"version": "4.12.0",
"description": "A library to provide utility functions to the other Semaphore packages.",
"type": "module",
"license": "MIT",

View File

@@ -7536,7 +7536,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@semaphore-protocol/cli-template-contracts-foundry@workspace:packages/cli-template-contracts-foundry"
dependencies:
"@semaphore-protocol/contracts": "npm:4.11.0"
"@semaphore-protocol/contracts": "npm:4.12.0"
"@zk-kit/lean-imt.sol": "npm:2.0.1"
forge-std: "github:foundry-rs/forge-std#v1.9.4"
poseidon-solidity: "npm:0.0.5"
@@ -7556,10 +7556,10 @@ __metadata:
"@nomicfoundation/hardhat-network-helpers": "npm:^1.0.0"
"@nomicfoundation/hardhat-toolbox": "npm:^4.0.0"
"@nomicfoundation/hardhat-verify": "npm:^2.0.0"
"@semaphore-protocol/contracts": "npm:4.11.0"
"@semaphore-protocol/core": "npm:4.11.0"
"@semaphore-protocol/hardhat": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/contracts": "npm:4.12.0"
"@semaphore-protocol/core": "npm:4.12.0"
"@semaphore-protocol/hardhat": "npm:4.12.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@typechain/ethers-v6": "npm:^0.5.0"
"@typechain/hardhat": "npm:^9.0.0"
"@types/chai": "npm:^4.2.0"
@@ -7609,8 +7609,8 @@ __metadata:
resolution: "@semaphore-protocol/cli@workspace:packages/cli"
dependencies:
"@rollup/plugin-typescript": "npm:^11.1.6"
"@semaphore-protocol/data": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/data": "npm:4.12.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@types/figlet": "npm:^1.5.8"
"@types/inquirer": "npm:^9.0.7"
"@types/pacote": "npm:^11.1.8"
@@ -7634,7 +7634,7 @@ __metadata:
languageName: unknown
linkType: soft
"@semaphore-protocol/contracts@npm:4.11.0, @semaphore-protocol/contracts@workspace:packages/contracts/contracts":
"@semaphore-protocol/contracts@npm:4.12.0, @semaphore-protocol/contracts@workspace:packages/contracts/contracts":
version: 0.0.0-use.local
resolution: "@semaphore-protocol/contracts@workspace:packages/contracts/contracts"
dependencies:
@@ -7642,23 +7642,23 @@ __metadata:
languageName: unknown
linkType: soft
"@semaphore-protocol/core@npm:4.11.0, @semaphore-protocol/core@workspace:^, @semaphore-protocol/core@workspace:packages/core":
"@semaphore-protocol/core@npm:4.12.0, @semaphore-protocol/core@workspace:^, @semaphore-protocol/core@workspace:packages/core":
version: 0.0.0-use.local
resolution: "@semaphore-protocol/core@workspace:packages/core"
dependencies:
"@semaphore-protocol/group": "npm:4.11.0"
"@semaphore-protocol/identity": "npm:4.11.0"
"@semaphore-protocol/proof": "npm:4.11.0"
"@semaphore-protocol/group": "npm:4.12.0"
"@semaphore-protocol/identity": "npm:4.12.0"
"@semaphore-protocol/proof": "npm:4.12.0"
languageName: unknown
linkType: soft
"@semaphore-protocol/data@npm:4.11.0, @semaphore-protocol/data@workspace:packages/data":
"@semaphore-protocol/data@npm:4.12.0, @semaphore-protocol/data@workspace:packages/data":
version: 0.0.0-use.local
resolution: "@semaphore-protocol/data@workspace:packages/data"
dependencies:
"@rollup/plugin-json": "npm:^6.1.0"
"@rollup/plugin-typescript": "npm:^11.1.6"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@zk-kit/utils": "npm:1.3.0"
axios: "npm:1.6.6"
ethers: "npm:6.13.4"
@@ -7669,7 +7669,7 @@ __metadata:
languageName: unknown
linkType: soft
"@semaphore-protocol/group@npm:4.11.0, @semaphore-protocol/group@workspace:packages/group":
"@semaphore-protocol/group@npm:4.12.0, @semaphore-protocol/group@workspace:packages/group":
version: 0.0.0-use.local
resolution: "@semaphore-protocol/group@workspace:packages/group"
dependencies:
@@ -7683,13 +7683,13 @@ __metadata:
languageName: unknown
linkType: soft
"@semaphore-protocol/hardhat@npm:4.11.0, @semaphore-protocol/hardhat@workspace:packages/hardhat":
"@semaphore-protocol/hardhat@npm:4.12.0, @semaphore-protocol/hardhat@workspace:packages/hardhat":
version: 0.0.0-use.local
resolution: "@semaphore-protocol/hardhat@workspace:packages/hardhat"
dependencies:
"@nomicfoundation/hardhat-ethers": "npm:^3.0.0"
"@rollup/plugin-typescript": "npm:^11.1.6"
"@semaphore-protocol/contracts": "npm:4.11.0"
"@semaphore-protocol/contracts": "npm:4.12.0"
ethers: "npm:^6.13.4"
hardhat: "npm:^2.19.4"
hardhat-dependency-compiler: "npm:^1.1.3"
@@ -7701,7 +7701,7 @@ __metadata:
languageName: unknown
linkType: soft
"@semaphore-protocol/identity@npm:4.11.0, @semaphore-protocol/identity@workspace:packages/identity":
"@semaphore-protocol/identity@npm:4.12.0, @semaphore-protocol/identity@workspace:packages/identity":
version: 0.0.0-use.local
resolution: "@semaphore-protocol/identity@workspace:packages/identity"
dependencies:
@@ -7717,14 +7717,14 @@ __metadata:
languageName: unknown
linkType: soft
"@semaphore-protocol/proof@npm:4.11.0, @semaphore-protocol/proof@workspace:packages/proof":
"@semaphore-protocol/proof@npm:4.12.0, @semaphore-protocol/proof@workspace:packages/proof":
version: 0.0.0-use.local
resolution: "@semaphore-protocol/proof@workspace:packages/proof"
dependencies:
"@rollup/plugin-alias": "npm:^5.1.0"
"@rollup/plugin-json": "npm:^6.1.0"
"@rollup/plugin-typescript": "npm:^11.1.6"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@types/snarkjs": "npm:^0"
"@zk-kit/artifacts": "npm:1.8.0"
"@zk-kit/utils": "npm:1.3.0"
@@ -7734,12 +7734,12 @@ __metadata:
rollup-plugin-cleanup: "npm:^3.2.1"
snarkjs: "npm:0.7.4"
peerDependencies:
"@semaphore-protocol/group": 4.11.0
"@semaphore-protocol/identity": 4.11.0
"@semaphore-protocol/group": 4.12.0
"@semaphore-protocol/identity": 4.12.0
languageName: unknown
linkType: soft
"@semaphore-protocol/utils@npm:4.11.0, @semaphore-protocol/utils@workspace:packages/utils":
"@semaphore-protocol/utils@npm:4.12.0, @semaphore-protocol/utils@workspace:packages/utils":
version: 0.0.0-use.local
resolution: "@semaphore-protocol/utils@workspace:packages/utils"
dependencies:
@@ -21571,10 +21571,10 @@ __metadata:
"@nomicfoundation/hardhat-network-helpers": "npm:^1.0.0"
"@nomicfoundation/hardhat-toolbox": "npm:^4.0.0"
"@nomicfoundation/hardhat-verify": "npm:^2.0.0"
"@semaphore-protocol/contracts": "npm:4.11.0"
"@semaphore-protocol/core": "npm:4.11.0"
"@semaphore-protocol/hardhat": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/contracts": "npm:4.12.0"
"@semaphore-protocol/core": "npm:4.12.0"
"@semaphore-protocol/hardhat": "npm:4.12.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@typechain/ethers-v6": "npm:^0.5.0"
"@typechain/hardhat": "npm:^9.0.0"
"@types/chai": "npm:^4.2.0"
@@ -21607,9 +21607,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "monorepo-ethers-web-app@workspace:packages/cli-template-monorepo-ethers/apps/web-app"
dependencies:
"@semaphore-protocol/core": "npm:4.11.0"
"@semaphore-protocol/data": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/core": "npm:4.12.0"
"@semaphore-protocol/data": "npm:4.12.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@types/node": "npm:^20"
"@types/react": "npm:^18"
"@types/react-dom": "npm:^18"
@@ -21634,10 +21634,10 @@ __metadata:
"@nomicfoundation/hardhat-network-helpers": "npm:^1.0.0"
"@nomicfoundation/hardhat-toolbox": "npm:^4.0.0"
"@nomicfoundation/hardhat-verify": "npm:^2.0.0"
"@semaphore-protocol/contracts": "npm:4.11.0"
"@semaphore-protocol/core": "npm:4.11.0"
"@semaphore-protocol/hardhat": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/contracts": "npm:4.12.0"
"@semaphore-protocol/core": "npm:4.12.0"
"@semaphore-protocol/hardhat": "npm:4.12.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@typechain/ethers-v6": "npm:^0.5.0"
"@typechain/hardhat": "npm:^9.0.0"
"@types/chai": "npm:^4.2.0"
@@ -21670,9 +21670,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "monorepo-subgraph-web-app@workspace:packages/cli-template-monorepo-subgraph/apps/web-app"
dependencies:
"@semaphore-protocol/core": "npm:4.11.0"
"@semaphore-protocol/data": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/core": "npm:4.12.0"
"@semaphore-protocol/data": "npm:4.12.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@types/node": "npm:^20"
"@types/react": "npm:^18"
"@types/react-dom": "npm:^18"
@@ -26178,7 +26178,7 @@ __metadata:
"@docusaurus/preset-classic": "npm:3.5.2"
"@docusaurus/tsconfig": "npm:3.5.2"
"@mdx-js/react": "npm:^3.0.0"
"@semaphore-protocol/utils": "npm:4.11.0"
"@semaphore-protocol/utils": "npm:4.12.0"
"@svgr/webpack": "npm:^5.5.0"
"@types/react": "npm:^18.2.29"
clsx: "npm:^1.2.1"