From e9671c927db6d2b13430097dd81378f5d55e2803 Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Sat, 18 Oct 2025 14:29:09 -0300 Subject: [PATCH 01/11] feat(data): add semaphore event listeners add listeners for groups and members re #326 --- packages/data/src/ethers.ts | 38 +++++++++++++++++++++++ packages/data/tests/ethers.test.ts | 50 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index 87080bbdd..7d301d8d8 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -295,4 +295,42 @@ export default class SemaphoreEthers { return this._contract.hasMember(groupId, member) } + + /** + * Listens to the GroupCreated event. + * @param callback Called with the groupId of the newly created group. + */ + onGroup(callback: (groupId: string) => void): void { + this._contract.on("GroupCreated", (groupId: string) => { + callback(groupId.toString()) + }) + } + + offGroup(): void { + this._contract.removeAllListeners("GroupCreated") + } + + /** + * Listens to member-related events (MemberAdded, MemberRemoved, MemberUpdated). + * @param callback Called with member data depending on the event type. + */ + onMember(callback: (eventType: string, identityCommitment: string, oldIdentityCommitment?: string) => void): void { + this._contract.on("MemberAdded", (_groupId, _index, identityCommitment) => { + callback("added", identityCommitment.toString()) + }) + + this._contract.on("MemberUpdated", (_groupId, _index, oldIdentityCommitment, newIdentityCommitment) => { + callback("updated", newIdentityCommitment.toString(), oldIdentityCommitment.toString()) + }) + + this._contract.on("MemberRemoved", (_groupId, _index, identityCommitment) => { + callback("removed", identityCommitment.toString()) + }) + } + + offMember(): void { + this._contract.removeAllListeners("MemberAdded") + this._contract.removeAllListeners("MemberUpdated") + this._contract.removeAllListeners("MemberRemoved") + } } diff --git a/packages/data/tests/ethers.test.ts b/packages/data/tests/ethers.test.ts index 27c8fd19f..6f6309445 100644 --- a/packages/data/tests/ethers.test.ts +++ b/packages/data/tests/ethers.test.ts @@ -257,4 +257,54 @@ describe("SemaphoreEthers", () => { expect(isMember).toBeFalsy() }) }) + + describe("Event listeners", () => { + let mockOn: jest.Mock + let mockRemove: jest.Mock + + beforeEach(() => { + mockOn = jest.fn() + mockRemove = jest.fn() + + ContractMocked.mockImplementation( + () => + ({ + on: mockOn, + removeAllListeners: mockRemove + }) as any + ) + }) + + it("onGroup should call the callback with groupId", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + const cb = jest.fn() + + semaphore.onGroup(cb) + const handler = mockOn.mock.calls.find(([e]) => e === "GroupCreated")![1] + handler("42") + + expect(cb).toHaveBeenCalledWith("42") + }) + + it("onMember should handle added, updated, and removed events", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + const cb = jest.fn() + + semaphore.onMember(cb) + + // simulamos las tres variantes + const added = mockOn.mock.calls.find(([e]) => e === "MemberAdded")![1] + added("g1", 0, "111") + + const updated = mockOn.mock.calls.find(([e]) => e === "MemberUpdated")![1] + updated("g1", 0, "111", "222") + + const removed = mockOn.mock.calls.find(([e]) => e === "MemberRemoved")![1] + removed("g1", 0, "333") + + expect(cb).toHaveBeenNthCalledWith(1, "added", "111") + expect(cb).toHaveBeenNthCalledWith(2, "updated", "222", "111") + expect(cb).toHaveBeenNthCalledWith(3, "removed", "333") + }) + }) }) From bc66e6e0bceee61232dce121338fa83755973ba5 Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Mon, 20 Oct 2025 13:35:27 -0300 Subject: [PATCH 02/11] feat(data): add semaphore event listeners add listeners for ProoValidated and GroupAdminUpdated events re #326 --- packages/data/src/ethers.ts | 49 ++++++++++++++++++++++++++++++ packages/data/tests/ethers.test.ts | 46 ++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index 7d301d8d8..c081faa93 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -333,4 +333,53 @@ export default class SemaphoreEthers { this._contract.removeAllListeners("MemberUpdated") this._contract.removeAllListeners("MemberRemoved") } + + /** + * Listens to the ProofValidated event. + * @param callback Called with proof parameters. + */ + onValidatedProof( + callback: (proof: { + groupId: string + merkleTreeDepth: number + merkleTreeRoot: string + nullifier: string + message: string + scope: string + points: string[] + }) => void + ): void { + this._contract.on( + "ProofValidated", + (groupId, merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, points) => { + callback({ + groupId: groupId.toString(), + merkleTreeDepth: Number(merkleTreeDepth), + merkleTreeRoot: merkleTreeRoot.toString(), + nullifier: nullifier.toString(), + message: message.toString(), + scope: scope.toString(), + points: points.map((p: any) => p.toString()) + }) + } + ) + } + + offValidatedProof(): void { + this._contract.removeAllListeners("ProofValidated") + } + + /** + * Listens to the GroupAdminUpdated event. + * @param callback Called with the old admin and new admin addresses. + */ + onGroupAdmin(callback: (oldAdmin: string, newAdmin: string) => void): void { + this._contract.on("GroupAdminUpdated", (_groupId, oldAdmin, newAdmin) => { + callback(oldAdmin.toString(), newAdmin.toString()) + }) + } + + offGroupAdmin(): void { + this._contract.removeAllListeners("GroupAdminUpdated") + } } diff --git a/packages/data/tests/ethers.test.ts b/packages/data/tests/ethers.test.ts index 6f6309445..3a7d7e207 100644 --- a/packages/data/tests/ethers.test.ts +++ b/packages/data/tests/ethers.test.ts @@ -306,5 +306,51 @@ describe("SemaphoreEthers", () => { expect(cb).toHaveBeenNthCalledWith(2, "updated", "222", "111") expect(cb).toHaveBeenNthCalledWith(3, "removed", "333") }) + + it("onValidatedProof should call the callback with proof data", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + const cb = jest.fn() + + semaphore.onValidatedProof(cb) + + const handler = mockOn.mock.calls.find(([e]) => e === "ProofValidated")![1] + handler("g1", 3, "root", "null", "msg", "scope", ["1", "2"]) + + expect(cb).toHaveBeenCalledWith({ + groupId: "g1", + merkleTreeDepth: 3, + merkleTreeRoot: "root", + nullifier: "null", + message: "msg", + scope: "scope", + points: ["1", "2"] + }) + }) + it("onGroupAdmin should call the callback with old and new admin", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + const cb = jest.fn() + + semaphore.onGroupAdmin(cb) + const handler = mockOn.mock.calls.find(([e]) => e === "GroupAdminUpdated")![1] + handler("g1", "0xOLD", "0xNEW") + + expect(cb).toHaveBeenCalledWith("0xOLD", "0xNEW") + }) + + it("off functions should remove all listeners", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + + semaphore.offGroup() + semaphore.offMember() + semaphore.offValidatedProof() + semaphore.offGroupAdmin() + + expect(mockRemove).toHaveBeenCalledWith("GroupCreated") + expect(mockRemove).toHaveBeenCalledWith("MemberAdded") + expect(mockRemove).toHaveBeenCalledWith("MemberUpdated") + expect(mockRemove).toHaveBeenCalledWith("MemberRemoved") + expect(mockRemove).toHaveBeenCalledWith("ProofValidated") + expect(mockRemove).toHaveBeenCalledWith("GroupAdminUpdated") + }) }) }) From 49c0cee14e42ea5d5c23a86526c769c886c8161b Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Mon, 20 Oct 2025 13:37:16 -0300 Subject: [PATCH 03/11] feat(data): add semaphore event listeners --- packages/data/tests/ethers.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data/tests/ethers.test.ts b/packages/data/tests/ethers.test.ts index 3a7d7e207..d54169340 100644 --- a/packages/data/tests/ethers.test.ts +++ b/packages/data/tests/ethers.test.ts @@ -292,7 +292,7 @@ describe("SemaphoreEthers", () => { semaphore.onMember(cb) - // simulamos las tres variantes + // Simulate three variants const added = mockOn.mock.calls.find(([e]) => e === "MemberAdded")![1] added("g1", 0, "111") From dc7cd2057421e1efec30ff3e2fdc4125349b2426 Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Thu, 23 Oct 2025 11:11:28 -0300 Subject: [PATCH 04/11] feat(data): feat(data): add semaphore event listeners the onMember method is separated and some names are adjusted BREAKING CHANGE: N re #326 --- packages/data/src/ethers.ts | 64 +++++++++++----- packages/data/tests/ethers.test.ts | 119 +++++++++++++++++++---------- 2 files changed, 123 insertions(+), 60 deletions(-) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index c081faa93..362c23297 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -300,37 +300,57 @@ export default class SemaphoreEthers { * Listens to the GroupCreated event. * @param callback Called with the groupId of the newly created group. */ - onGroup(callback: (groupId: string) => void): void { - this._contract.on("GroupCreated", (groupId: string) => { - callback(groupId.toString()) + onGroupCreated(callback: (groupId: string, event: any) => void): void { + this._contract.on("GroupCreated", (groupId, event) => { + callback(groupId.toString(), event) }) } - offGroup(): void { + offGropupCreated(): void { this._contract.removeAllListeners("GroupCreated") } /** - * Listens to member-related events (MemberAdded, MemberRemoved, MemberUpdated). - * @param callback Called with member data depending on the event type. + * Listens to MemberAdded events. + * @param callback Receives the groupId, identityCommitment and event metadata. */ - onMember(callback: (eventType: string, identityCommitment: string, oldIdentityCommitment?: string) => void): void { - this._contract.on("MemberAdded", (_groupId, _index, identityCommitment) => { - callback("added", identityCommitment.toString()) + onMemberAdded(callback: (groupId: string, identityCommitment: string, event: any) => void): void { + this._contract.on("MemberAdded", (groupId, _index, identityCommitment, event) => { + callback(groupId.toString(), identityCommitment.toString(), event) }) + } - this._contract.on("MemberUpdated", (_groupId, _index, oldIdentityCommitment, newIdentityCommitment) => { - callback("updated", newIdentityCommitment.toString(), oldIdentityCommitment.toString()) - }) + offMemberAdded(): void { + this._contract.removeAllListeners("MemberAdded") + } - this._contract.on("MemberRemoved", (_groupId, _index, identityCommitment) => { - callback("removed", identityCommitment.toString()) + /** + * Listens to MemberUpdated events. + * @param callback Receives the groupId, old identityCommitment, new identityCommitment, and event metadata. + */ + onMemberUpdated( + callback: (groupId: string, oldIdentityCommitment: string, newIdentityCommitment: string, event: any) => void + ): void { + this._contract.on("MemberUpdated", (groupId, _index, oldIdentityCommitment, newIdentityCommitment, event) => { + callback(groupId.toString(), oldIdentityCommitment.toString(), newIdentityCommitment.toString(), event) }) } - offMember(): void { - this._contract.removeAllListeners("MemberAdded") + offMemberUpdated(): void { this._contract.removeAllListeners("MemberUpdated") + } + + /** + * Listens to MemberRemoved events. + * @param callback Receives the groupId, identityCommitment and event metadata. + */ + onMemberRemoved(callback: (groupId: string, identityCommitment: string, event: any) => void): void { + this._contract.on("MemberRemoved", (groupId, _index, identityCommitment, event) => { + callback(groupId.toString(), identityCommitment.toString(), event) + }) + } + + offMemberRemoved(): void { this._contract.removeAllListeners("MemberRemoved") } @@ -347,11 +367,12 @@ export default class SemaphoreEthers { message: string scope: string points: string[] + event: any }) => void ): void { this._contract.on( "ProofValidated", - (groupId, merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, points) => { + (groupId, merkleTreeDepth, merkleTreeRoot, nullifier, message, scope, points, event) => { callback({ groupId: groupId.toString(), merkleTreeDepth: Number(merkleTreeDepth), @@ -359,7 +380,8 @@ export default class SemaphoreEthers { nullifier: nullifier.toString(), message: message.toString(), scope: scope.toString(), - points: points.map((p: any) => p.toString()) + points: points.map((p: any) => p.toString()), + event }) } ) @@ -373,9 +395,9 @@ export default class SemaphoreEthers { * Listens to the GroupAdminUpdated event. * @param callback Called with the old admin and new admin addresses. */ - onGroupAdmin(callback: (oldAdmin: string, newAdmin: string) => void): void { - this._contract.on("GroupAdminUpdated", (_groupId, oldAdmin, newAdmin) => { - callback(oldAdmin.toString(), newAdmin.toString()) + onGroupAdmin(callback: (oldAdmin: string, newAdmin: string, event: any) => void): void { + this._contract.on("GroupAdminUpdated", (oldAdmin, newAdmin, event) => { + callback(oldAdmin.toString(), newAdmin.toString(), event) }) } diff --git a/packages/data/tests/ethers.test.ts b/packages/data/tests/ethers.test.ts index d54169340..a29da1d71 100644 --- a/packages/data/tests/ethers.test.ts +++ b/packages/data/tests/ethers.test.ts @@ -275,81 +275,122 @@ describe("SemaphoreEthers", () => { ) }) - it("onGroup should call the callback with groupId", () => { + it("onGroupCreated should call the callback with groupId and event", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) const cb = jest.fn() - semaphore.onGroup(cb) + semaphore.onGroupCreated(cb) + + // Simulamos la emisión del evento const handler = mockOn.mock.calls.find(([e]) => e === "GroupCreated")![1] - handler("42") + const fakeEvent = { blockNumber: 123 } + handler("42", fakeEvent) + + expect(cb).toHaveBeenCalledWith("42", fakeEvent) + expect(mockOn).toHaveBeenCalledWith("GroupCreated", expect.any(Function)) + }) - expect(cb).toHaveBeenCalledWith("42") + it("offGroupCreated should remove all listeners for GroupCreated", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + semaphore.offGropupCreated() + expect(mockRemove).toHaveBeenCalledWith("GroupCreated") }) - it("onMember should handle added, updated, and removed events", () => { + it("onMemberAdded should call callback with groupId, identityCommitment and event", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) const cb = jest.fn() - semaphore.onMember(cb) + semaphore.onMemberAdded(cb) + const handler = mockOn.mock.calls.find(([e]) => e === "MemberAdded")![1] + const fakeEvent = { txHash: "0xabc" } - // Simulate three variants - const added = mockOn.mock.calls.find(([e]) => e === "MemberAdded")![1] - added("g1", 0, "111") + handler("group1", 0, "identity123", fakeEvent) - const updated = mockOn.mock.calls.find(([e]) => e === "MemberUpdated")![1] - updated("g1", 0, "111", "222") + expect(cb).toHaveBeenCalledWith("group1", "identity123", fakeEvent) + }) - const removed = mockOn.mock.calls.find(([e]) => e === "MemberRemoved")![1] - removed("g1", 0, "333") + it("onMemberUpdated should call callback with groupId, old and new identity commitments", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + const cb = jest.fn() + + semaphore.onMemberUpdated(cb) + const handler = mockOn.mock.calls.find(([e]) => e === "MemberUpdated")![1] + const fakeEvent = { blockNumber: 200 } - expect(cb).toHaveBeenNthCalledWith(1, "added", "111") - expect(cb).toHaveBeenNthCalledWith(2, "updated", "222", "111") - expect(cb).toHaveBeenNthCalledWith(3, "removed", "333") + handler("groupX", 1, "old123", "new456", fakeEvent) + + expect(cb).toHaveBeenCalledWith("groupX", "old123", "new456", fakeEvent) }) - it("onValidatedProof should call the callback with proof data", () => { + it("onMemberRemoved should call callback with groupId, identityCommitment and event", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) const cb = jest.fn() - semaphore.onValidatedProof(cb) + semaphore.onMemberRemoved(cb) + const handler = mockOn.mock.calls.find(([e]) => e === "MemberRemoved")![1] + const fakeEvent = { txHash: "0xdeadbeef" } + + handler("groupZ", 2, "identity999", fakeEvent) + + expect(cb).toHaveBeenCalledWith("groupZ", "identity999", fakeEvent) + }) + + it("offMemberAdded/Updated/Removed should remove all corresponding listeners", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + + semaphore.offMemberAdded() + semaphore.offMemberUpdated() + semaphore.offMemberRemoved() + + expect(mockRemove).toHaveBeenCalledWith("MemberAdded") + expect(mockRemove).toHaveBeenCalledWith("MemberUpdated") + expect(mockRemove).toHaveBeenCalledWith("MemberRemoved") + }) + + it("onValidatedProof should call callback with proof object", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + const cb = jest.fn() + semaphore.onValidatedProof(cb) const handler = mockOn.mock.calls.find(([e]) => e === "ProofValidated")![1] - handler("g1", 3, "root", "null", "msg", "scope", ["1", "2"]) + + const fakeEvent = { blockNumber: 400 } + handler("group1", 3, "root123", "nullifierXYZ", "msg1", "scope1", ["p1", "p2"], fakeEvent) expect(cb).toHaveBeenCalledWith({ - groupId: "g1", + groupId: "group1", merkleTreeDepth: 3, - merkleTreeRoot: "root", - nullifier: "null", - message: "msg", - scope: "scope", - points: ["1", "2"] + merkleTreeRoot: "root123", + nullifier: "nullifierXYZ", + message: "msg1", + scope: "scope1", + points: ["p1", "p2"], + event: fakeEvent }) }) - it("onGroupAdmin should call the callback with old and new admin", () => { + + it("offValidatedProof should remove all ProofValidated listeners", () => { + const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) + semaphore.offValidatedProof() + expect(mockRemove).toHaveBeenCalledWith("ProofValidated") + }) + + it("onGroupAdmin should call callback with groupId, oldAdmin, newAdmin and event", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) const cb = jest.fn() semaphore.onGroupAdmin(cb) const handler = mockOn.mock.calls.find(([e]) => e === "GroupAdminUpdated")![1] - handler("g1", "0xOLD", "0xNEW") + const fakeEvent = { txHash: "0xbeef" } + + handler("0xOLD", "0xNEW", fakeEvent) - expect(cb).toHaveBeenCalledWith("0xOLD", "0xNEW") + expect(cb).toHaveBeenCalledWith("0xOLD", "0xNEW", fakeEvent) }) - it("off functions should remove all listeners", () => { + it("offGroupAdmin should remove all GroupAdminUpdated listeners", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) - - semaphore.offGroup() - semaphore.offMember() - semaphore.offValidatedProof() semaphore.offGroupAdmin() - - expect(mockRemove).toHaveBeenCalledWith("GroupCreated") - expect(mockRemove).toHaveBeenCalledWith("MemberAdded") - expect(mockRemove).toHaveBeenCalledWith("MemberUpdated") - expect(mockRemove).toHaveBeenCalledWith("MemberRemoved") - expect(mockRemove).toHaveBeenCalledWith("ProofValidated") expect(mockRemove).toHaveBeenCalledWith("GroupAdminUpdated") }) }) From 4d31138cd6a4967877df162fe53fa9b9fffff07b Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Thu, 23 Oct 2025 11:14:43 -0300 Subject: [PATCH 05/11] feat(data): feat(data): add semaphore event listeners the onMember method is separated and some names are adjusted BREAKING CHANGE: N re #326 --- packages/data/tests/ethers.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/data/tests/ethers.test.ts b/packages/data/tests/ethers.test.ts index a29da1d71..c93e4ed40 100644 --- a/packages/data/tests/ethers.test.ts +++ b/packages/data/tests/ethers.test.ts @@ -281,7 +281,6 @@ describe("SemaphoreEthers", () => { semaphore.onGroupCreated(cb) - // Simulamos la emisión del evento const handler = mockOn.mock.calls.find(([e]) => e === "GroupCreated")![1] const fakeEvent = { blockNumber: 123 } handler("42", fakeEvent) From 86a07d72803f9d6d6be0ec3272486372073c7917 Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Thu, 23 Oct 2025 15:27:08 -0300 Subject: [PATCH 06/11] feat(data): feat(data) add semaphore event listeners add missing merkleTreeRoot element in onMember events re #326 --- packages/data/src/ethers.ts | 43 +++++++++++++++++++++--------- packages/data/tests/ethers.test.ts | 20 +++++++------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index 362c23297..5a0886f7a 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -314,9 +314,11 @@ export default class SemaphoreEthers { * Listens to MemberAdded events. * @param callback Receives the groupId, identityCommitment and event metadata. */ - onMemberAdded(callback: (groupId: string, identityCommitment: string, event: any) => void): void { - this._contract.on("MemberAdded", (groupId, _index, identityCommitment, event) => { - callback(groupId.toString(), identityCommitment.toString(), event) + onMemberAdded( + callback: (groupId: string, identityCommitment: string, merkleTreeRoot: string, event: any) => void + ): void { + this._contract.on("MemberAdded", (groupId, _index, identityCommitment, merkleTreeRoot, event) => { + callback(groupId.toString(), identityCommitment.toString(), merkleTreeRoot.toString(), event) }) } @@ -329,11 +331,26 @@ export default class SemaphoreEthers { * @param callback Receives the groupId, old identityCommitment, new identityCommitment, and event metadata. */ onMemberUpdated( - callback: (groupId: string, oldIdentityCommitment: string, newIdentityCommitment: string, event: any) => void + callback: ( + groupId: string, + oldIdentityCommitment: string, + newIdentityCommitment: string, + merkleTreeRoot: string, + event: any + ) => void ): void { - this._contract.on("MemberUpdated", (groupId, _index, oldIdentityCommitment, newIdentityCommitment, event) => { - callback(groupId.toString(), oldIdentityCommitment.toString(), newIdentityCommitment.toString(), event) - }) + this._contract.on( + "MemberUpdated", + (groupId, _index, oldIdentityCommitment, newIdentityCommitment, merkleTreeRoot, event) => { + callback( + groupId.toString(), + oldIdentityCommitment.toString(), + newIdentityCommitment.toString(), + merkleTreeRoot.toString(), + event + ) + } + ) } offMemberUpdated(): void { @@ -344,9 +361,11 @@ export default class SemaphoreEthers { * Listens to MemberRemoved events. * @param callback Receives the groupId, identityCommitment and event metadata. */ - onMemberRemoved(callback: (groupId: string, identityCommitment: string, event: any) => void): void { - this._contract.on("MemberRemoved", (groupId, _index, identityCommitment, event) => { - callback(groupId.toString(), identityCommitment.toString(), event) + onMemberRemoved( + callback: (groupId: string, identityCommitment: string, merkleTreeRoot: string, event: any) => void + ): void { + this._contract.on("MemberRemoved", (groupId, _index, identityCommitment, merkleTreeRoot, event) => { + callback(groupId.toString(), identityCommitment.toString(), merkleTreeRoot.toString(), event) }) } @@ -358,7 +377,7 @@ export default class SemaphoreEthers { * Listens to the ProofValidated event. * @param callback Called with proof parameters. */ - onValidatedProof( + onProofValidated( callback: (proof: { groupId: string merkleTreeDepth: number @@ -387,7 +406,7 @@ export default class SemaphoreEthers { ) } - offValidatedProof(): void { + offProofValidated(): void { this._contract.removeAllListeners("ProofValidated") } diff --git a/packages/data/tests/ethers.test.ts b/packages/data/tests/ethers.test.ts index c93e4ed40..1b0a097c4 100644 --- a/packages/data/tests/ethers.test.ts +++ b/packages/data/tests/ethers.test.ts @@ -303,9 +303,9 @@ describe("SemaphoreEthers", () => { const handler = mockOn.mock.calls.find(([e]) => e === "MemberAdded")![1] const fakeEvent = { txHash: "0xabc" } - handler("group1", 0, "identity123", fakeEvent) + handler("group1", 0, "identity123", "root111", fakeEvent) - expect(cb).toHaveBeenCalledWith("group1", "identity123", fakeEvent) + expect(cb).toHaveBeenCalledWith("group1", "identity123", "root111", fakeEvent) }) it("onMemberUpdated should call callback with groupId, old and new identity commitments", () => { @@ -316,9 +316,9 @@ describe("SemaphoreEthers", () => { const handler = mockOn.mock.calls.find(([e]) => e === "MemberUpdated")![1] const fakeEvent = { blockNumber: 200 } - handler("groupX", 1, "old123", "new456", fakeEvent) + handler("groupX", 1, "old123", "new456", "root111", fakeEvent) - expect(cb).toHaveBeenCalledWith("groupX", "old123", "new456", fakeEvent) + expect(cb).toHaveBeenCalledWith("groupX", "old123", "new456", "root111", fakeEvent) }) it("onMemberRemoved should call callback with groupId, identityCommitment and event", () => { @@ -329,9 +329,9 @@ describe("SemaphoreEthers", () => { const handler = mockOn.mock.calls.find(([e]) => e === "MemberRemoved")![1] const fakeEvent = { txHash: "0xdeadbeef" } - handler("groupZ", 2, "identity999", fakeEvent) + handler("groupZ", 2, "identity999", "root111", fakeEvent) - expect(cb).toHaveBeenCalledWith("groupZ", "identity999", fakeEvent) + expect(cb).toHaveBeenCalledWith("groupZ", "identity999", "root111", fakeEvent) }) it("offMemberAdded/Updated/Removed should remove all corresponding listeners", () => { @@ -346,11 +346,11 @@ describe("SemaphoreEthers", () => { expect(mockRemove).toHaveBeenCalledWith("MemberRemoved") }) - it("onValidatedProof should call callback with proof object", () => { + it("onProofValidated should call callback with proof object", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) const cb = jest.fn() - semaphore.onValidatedProof(cb) + semaphore.onProofValidated(cb) const handler = mockOn.mock.calls.find(([e]) => e === "ProofValidated")![1] const fakeEvent = { blockNumber: 400 } @@ -368,9 +368,9 @@ describe("SemaphoreEthers", () => { }) }) - it("offValidatedProof should remove all ProofValidated listeners", () => { + it("offProofValidated should remove all ProofValidated listeners", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) - semaphore.offValidatedProof() + semaphore.offProofValidated() expect(mockRemove).toHaveBeenCalledWith("ProofValidated") }) From 4d55a863989e2d2696f370810cda3191cafb7b36 Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Thu, 23 Oct 2025 17:15:21 -0300 Subject: [PATCH 07/11] feat(data): feat(data) add semaphore event listeners fix name listener of GroupAdminUpdated event re #326 --- packages/data/src/ethers.ts | 4 ++-- packages/data/tests/ethers.test.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index 5a0886f7a..53ae9c00a 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -414,13 +414,13 @@ export default class SemaphoreEthers { * Listens to the GroupAdminUpdated event. * @param callback Called with the old admin and new admin addresses. */ - onGroupAdmin(callback: (oldAdmin: string, newAdmin: string, event: any) => void): void { + onGroupAdminUpdated(callback: (oldAdmin: string, newAdmin: string, event: any) => void): void { this._contract.on("GroupAdminUpdated", (oldAdmin, newAdmin, event) => { callback(oldAdmin.toString(), newAdmin.toString(), event) }) } - offGroupAdmin(): void { + offGroupAdminUpdated(): void { this._contract.removeAllListeners("GroupAdminUpdated") } } diff --git a/packages/data/tests/ethers.test.ts b/packages/data/tests/ethers.test.ts index 1b0a097c4..430f4162e 100644 --- a/packages/data/tests/ethers.test.ts +++ b/packages/data/tests/ethers.test.ts @@ -374,11 +374,11 @@ describe("SemaphoreEthers", () => { expect(mockRemove).toHaveBeenCalledWith("ProofValidated") }) - it("onGroupAdmin should call callback with groupId, oldAdmin, newAdmin and event", () => { + it("onGroupAdminUpdated should call callback with groupId, oldAdmin, newAdmin and event", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) const cb = jest.fn() - semaphore.onGroupAdmin(cb) + semaphore.onGroupAdminUpdated(cb) const handler = mockOn.mock.calls.find(([e]) => e === "GroupAdminUpdated")![1] const fakeEvent = { txHash: "0xbeef" } @@ -387,9 +387,9 @@ describe("SemaphoreEthers", () => { expect(cb).toHaveBeenCalledWith("0xOLD", "0xNEW", fakeEvent) }) - it("offGroupAdmin should remove all GroupAdminUpdated listeners", () => { + it("offGroupAdminUpdated should remove all GroupAdminUpdated listeners", () => { const semaphore = new SemaphoreEthers("sepolia", { address: "0x0000" }) - semaphore.offGroupAdmin() + semaphore.offGroupAdminUpdated() expect(mockRemove).toHaveBeenCalledWith("GroupAdminUpdated") }) }) From 238b80d33bb406ccc883921c914c196191f20cda Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Thu, 23 Oct 2025 17:21:57 -0300 Subject: [PATCH 08/11] feat(data): feat(data) add semaphore event listeners add groupId param to listener onGroupAdminUpdated re #326 --- packages/data/src/ethers.ts | 6 +++--- packages/data/tests/ethers.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index 53ae9c00a..60580c3f9 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -414,9 +414,9 @@ export default class SemaphoreEthers { * Listens to the GroupAdminUpdated event. * @param callback Called with the old admin and new admin addresses. */ - onGroupAdminUpdated(callback: (oldAdmin: string, newAdmin: string, event: any) => void): void { - this._contract.on("GroupAdminUpdated", (oldAdmin, newAdmin, event) => { - callback(oldAdmin.toString(), newAdmin.toString(), event) + onGroupAdminUpdated(callback: (groupId: string, oldAdmin: string, newAdmin: string, event: any) => void): void { + this._contract.on("GroupAdminUpdated", (groupId, oldAdmin, newAdmin, event) => { + callback(groupId.toString(), oldAdmin.toString(), newAdmin.toString(), event) }) } diff --git a/packages/data/tests/ethers.test.ts b/packages/data/tests/ethers.test.ts index 430f4162e..7f9db91f5 100644 --- a/packages/data/tests/ethers.test.ts +++ b/packages/data/tests/ethers.test.ts @@ -382,9 +382,9 @@ describe("SemaphoreEthers", () => { const handler = mockOn.mock.calls.find(([e]) => e === "GroupAdminUpdated")![1] const fakeEvent = { txHash: "0xbeef" } - handler("0xOLD", "0xNEW", fakeEvent) + handler("group1", "0xOLD", "0xNEW", fakeEvent) - expect(cb).toHaveBeenCalledWith("0xOLD", "0xNEW", fakeEvent) + expect(cb).toHaveBeenCalledWith("group1", "0xOLD", "0xNEW", fakeEvent) }) it("offGroupAdminUpdated should remove all GroupAdminUpdated listeners", () => { From 3c3fb330df3bc358f86cb2970bcca6c463fabd64 Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Thu, 23 Oct 2025 17:29:47 -0300 Subject: [PATCH 09/11] feat(data): feat(data) add semaphore event listeners Update method description re #326 --- packages/data/src/ethers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index 60580c3f9..ad758d8ef 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -375,7 +375,7 @@ export default class SemaphoreEthers { /** * Listens to the ProofValidated event. - * @param callback Called with proof parameters. + * @param callback Called with proof parameters and event metadata. */ onProofValidated( callback: (proof: { @@ -412,7 +412,7 @@ export default class SemaphoreEthers { /** * Listens to the GroupAdminUpdated event. - * @param callback Called with the old admin and new admin addresses. + * @param callback callback Receives the groupId, old admin and new admin addresses and event metadata. */ onGroupAdminUpdated(callback: (groupId: string, oldAdmin: string, newAdmin: string, event: any) => void): void { this._contract.on("GroupAdminUpdated", (groupId, oldAdmin, newAdmin, event) => { From 026ea3859a3baf5adcc3d1c799ff37b0ac67ff03 Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Thu, 23 Oct 2025 17:34:51 -0300 Subject: [PATCH 10/11] feat(data): feat(data) add semaphore event listeners fix descriptions re #326 --- packages/data/src/ethers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index ad758d8ef..4424f076b 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -412,7 +412,7 @@ export default class SemaphoreEthers { /** * Listens to the GroupAdminUpdated event. - * @param callback callback Receives the groupId, old admin and new admin addresses and event metadata. + * @param callback Receives the groupId, old admin and new admin addresses and event metadata. */ onGroupAdminUpdated(callback: (groupId: string, oldAdmin: string, newAdmin: string, event: any) => void): void { this._contract.on("GroupAdminUpdated", (groupId, oldAdmin, newAdmin, event) => { From 5700323db669ad5b1d251923c702c319066e35ae Mon Sep 17 00:00:00 2001 From: Lautaro Di Sanza Date: Thu, 23 Oct 2025 18:41:15 -0300 Subject: [PATCH 11/11] feat(data): feat(data) add semaphore event listeners Add short descriptions to off listeners methods re #326 --- packages/data/src/ethers.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index 4424f076b..8ae7eac1d 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -306,6 +306,10 @@ export default class SemaphoreEthers { }) } + /** + * Removes all listeners for the GroupCreated event. + * Stop receiving group creation notifications. + */ offGropupCreated(): void { this._contract.removeAllListeners("GroupCreated") } @@ -322,6 +326,10 @@ export default class SemaphoreEthers { }) } + /** + * Removes all listeners for the MemberAdded event. + * Stop tracking when new members are added. + */ offMemberAdded(): void { this._contract.removeAllListeners("MemberAdded") } @@ -353,6 +361,10 @@ export default class SemaphoreEthers { ) } + /** + * Removes all listeners for the MemberUpdated event. + * Stop receiving updates when members change their commitment. + */ offMemberUpdated(): void { this._contract.removeAllListeners("MemberUpdated") } @@ -369,6 +381,10 @@ export default class SemaphoreEthers { }) } + /** + * Removes all listeners for the MemberRemoved event. + * Stop listening for member removals. + */ offMemberRemoved(): void { this._contract.removeAllListeners("MemberRemoved") } @@ -406,6 +422,10 @@ export default class SemaphoreEthers { ) } + /** + * Removes all listeners for the ProofValidated event. + * Stop receiving proof validation notifications. + */ offProofValidated(): void { this._contract.removeAllListeners("ProofValidated") } @@ -420,6 +440,10 @@ export default class SemaphoreEthers { }) } + /** + * Removes all listeners for the GroupAdminUpdated event. + * Stop tracking when a group's admin is updated. + */ offGroupAdminUpdated(): void { this._contract.removeAllListeners("GroupAdminUpdated") }