diff --git a/packages/data/src/ethers.ts b/packages/data/src/ethers.ts index 40f565314..1358d5b16 100644 --- a/packages/data/src/ethers.ts +++ b/packages/data/src/ethers.ts @@ -17,8 +17,8 @@ import { Provider } from "ethers/providers" import { SemaphoreABI } from "@semaphore-protocol/utils/constants" -import { requireString } from "@zk-kit/utils/error-handlers" -import { EthersNetwork, EthersOptions, GroupResponse } from "./types" +import { requireString, requireTypes } from "@zk-kit/utils/error-handlers" +import { EthersNetwork, EthersOptions, GroupResponse, GroupId } from "./types" import getEvents from "./getEvents" /** @@ -139,8 +139,8 @@ export default class SemaphoreEthers { * @param groupId The unique identifier of the group. * @returns A promise that resolves to a GroupResponse object. */ - async getGroup(groupId: string): Promise { - requireString(groupId, "groupId") + async getGroup(groupId: GroupId): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number", "object"]) const groupAdmin = await this._contract.getGroupAdmin(groupId) @@ -153,7 +153,7 @@ export default class SemaphoreEthers { const merkleTreeSize = await this._contract.getMerkleTreeSize(groupId) const group: GroupResponse = { - id: groupId, + id: groupId.toString(), admin: groupAdmin, merkleTree: { depth: Number(merkleTreeDepth), @@ -171,8 +171,8 @@ export default class SemaphoreEthers { * @param groupId The unique identifier of the group. * @returns A promise that resolves to an array of member identity commitments as strings. */ - async getGroupMembers(groupId: string): Promise { - requireString(groupId, "groupId") + async getGroupMembers(groupId: GroupId): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number", "object"]) const groupAdmin = await this._contract.getGroupAdmin(groupId) @@ -256,8 +256,8 @@ export default class SemaphoreEthers { * @param groupId The unique identifier of the group. * @returns A promise that resolves to an array of validated proofs. */ - async getGroupValidatedProofs(groupId: string): Promise { - requireString(groupId, "groupId") + async getGroupValidatedProofs(groupId: GroupId): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number", "object"]) const groupAdmin = await this._contract.getGroupAdmin(groupId) @@ -289,8 +289,8 @@ export default class SemaphoreEthers { * @param member The identity commitment of the member to check. * @returns A promise that resolves to true if the member is part of the group, otherwise false. */ - async isGroupMember(groupId: string, member: string): Promise { - requireString(groupId, "groupId") + async isGroupMember(groupId: GroupId, member: string): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number", "object"]) requireString(member, "member") return this._contract.hasMember(groupId, member) diff --git a/packages/data/src/subgraph.ts b/packages/data/src/subgraph.ts index 096240205..cd591ab57 100644 --- a/packages/data/src/subgraph.ts +++ b/packages/data/src/subgraph.ts @@ -1,9 +1,9 @@ import { defaultNetwork, SupportedNetwork } from "@semaphore-protocol/utils/networks" import { AxiosRequestConfig } from "axios" -import { requireString, requireObject, requireBoolean } from "@zk-kit/utils/error-handlers" +import { requireString, requireObject, requireBoolean, requireTypes } from "@zk-kit/utils/error-handlers" import getURL from "./getURL" import request from "./request" -import { GroupOptions, GroupResponse } from "./types" +import { GroupId, GroupOptions, GroupResponse } from "./types" import { jsDateToGraphqlDate } from "./utils" /** @@ -163,8 +163,8 @@ export default class SemaphoreSubgraph { * @param options Configuration options to specify which details to fetch about the group. * @returns A promise that resolves to the details of the specified group. */ - async getGroup(groupId: string, options: Omit = {}): Promise { - requireString(groupId, "groupId") + async getGroup(groupId: GroupId, options: Omit = {}): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number"]) requireObject(options, "options") const { members = false, validatedProofs = false } = options @@ -176,7 +176,7 @@ export default class SemaphoreSubgraph { method: "post", data: JSON.stringify({ query: `{ - groups(where: { id: "${groupId}" }) { + groups(where: { id: "${groupId.toString()}" }) { id merkleTree { root @@ -223,7 +223,7 @@ export default class SemaphoreSubgraph { * @param groupId The unique identifier of the group. * @returns A promise that resolves to an array of group members' identity commitments. */ - async getGroupMembers(groupId: string): Promise { + async getGroupMembers(groupId: GroupId): Promise { const group = await this.getGroup(groupId, { members: true }) // parameters are checked inside getGroup return group.members! } @@ -233,7 +233,7 @@ export default class SemaphoreSubgraph { * @param groupId The unique identifier of the group. * @returns A promise that resolves to an array of validated proofs. */ - async getGroupValidatedProofs(groupId: string): Promise { + async getGroupValidatedProofs(groupId: GroupId): Promise { const group = await this.getGroup(groupId, { validatedProofs: true }) // parameters are checked inside getGroup return group.validatedProofs! @@ -246,15 +246,15 @@ export default class SemaphoreSubgraph { * @param member The identity commitment of the member to check. * @returns A promise that resolves to true if the member is part of the group, otherwise false. */ - async isGroupMember(groupId: string, member: string): Promise { - requireString(groupId, "groupId") + async isGroupMember(groupId: GroupId, member: string): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number"]) requireString(member, "member") const config: AxiosRequestConfig = { method: "post", data: JSON.stringify({ query: `{ - groups(where: { id: "${groupId}", members_: { identityCommitment: "${member}" } }) { + groups(where: { id: "${groupId.toString()}", members_: { identityCommitment: "${member}" } }) { id } }` diff --git a/packages/data/src/types/index.ts b/packages/data/src/types/index.ts index 2d3a38c0a..2de8639e1 100644 --- a/packages/data/src/types/index.ts +++ b/packages/data/src/types/index.ts @@ -1,4 +1,5 @@ import { Chain, PublicClient, Transport } from "viem" +import type { BigNumberish } from "ethers" export type EthersNetwork = | "mainnet" @@ -16,6 +17,8 @@ export type EthersNetwork = export type ViemNetwork = EthersNetwork +export type GroupId = BigNumberish + export type GroupOptions = { members?: boolean validatedProofs?: boolean diff --git a/packages/data/src/viem.ts b/packages/data/src/viem.ts index 9f63f9876..c1044e71e 100644 --- a/packages/data/src/viem.ts +++ b/packages/data/src/viem.ts @@ -5,7 +5,7 @@ import { isSupportedNetwork } from "@semaphore-protocol/utils/networks" import { SemaphoreABI } from "@semaphore-protocol/utils/constants" -import { requireString } from "@zk-kit/utils/error-handlers" +import { requireString, requireTypes } from "@zk-kit/utils/error-handlers" import { Address, createPublicClient, @@ -18,7 +18,15 @@ import { Chain, Log } from "viem" -import { GroupResponse, ViemNetwork, ViemOptions } from "./types" +import { GroupResponse, ViemNetwork, ViemOptions, GroupId } from "./types" + +function toBigIntId(id: GroupId): bigint { + if (typeof id === "bigint") return id + if (typeof id === "number") return BigInt(id) + if (typeof id === "string") return BigInt(id) + if ((id as any) && typeof (id as any).toString === "function") return BigInt((id as any).toString()) + throw new TypeError("groupId must be a valid BigNumberish") +} // Define types for the event logs to properly access args type GroupCreatedLog = Log & { @@ -191,21 +199,21 @@ export default class SemaphoreViem { * @param groupId The unique identifier of the group. * @returns A promise that resolves to a GroupResponse object. */ - async getGroup(groupId: string): Promise { - requireString(groupId, "groupId") + async getGroup(groupId: GroupId): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number", "object"]) - const groupAdmin = await this._contract.read.getGroupAdmin([groupId]) + const groupAdmin = await this._contract.read.getGroupAdmin([toBigIntId(groupId)]) if (groupAdmin === zeroAddress) { throw new Error(`Group '${groupId}' not found`) } - const merkleTreeRoot = await this._contract.read.getMerkleTreeRoot([groupId]) - const merkleTreeDepth = await this._contract.read.getMerkleTreeDepth([groupId]) - const merkleTreeSize = await this._contract.read.getMerkleTreeSize([groupId]) + const merkleTreeRoot = await this._contract.read.getMerkleTreeRoot([toBigIntId(groupId)]) + const merkleTreeDepth = await this._contract.read.getMerkleTreeDepth([toBigIntId(groupId)]) + const merkleTreeSize = await this._contract.read.getMerkleTreeSize([toBigIntId(groupId)]) const group: GroupResponse = { - id: groupId, + id: groupId.toString(), admin: groupAdmin as string, merkleTree: { depth: Number(merkleTreeDepth), @@ -223,10 +231,10 @@ export default class SemaphoreViem { * @param groupId The unique identifier of the group. * @returns A promise that resolves to an array of member identity commitments as strings. */ - async getGroupMembers(groupId: string): Promise { - requireString(groupId, "groupId") + async getGroupMembers(groupId: GroupId): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number", "object"]) - const groupAdmin = await this._contract.read.getGroupAdmin([groupId]) + const groupAdmin = await this._contract.read.getGroupAdmin([toBigIntId(groupId)]) if (groupAdmin === zeroAddress) { throw new Error(`Group '${groupId}' not found`) @@ -238,7 +246,7 @@ export default class SemaphoreViem { abi: SemaphoreABI, eventName: "MemberRemoved", args: { - groupId: BigInt(groupId) + groupId: toBigIntId(groupId) }, fromBlock: BigInt(this._options.startBlock || 0) })) as MemberRemovedLog[] @@ -249,7 +257,7 @@ export default class SemaphoreViem { abi: SemaphoreABI, eventName: "MemberUpdated", args: { - groupId: BigInt(groupId) + groupId: toBigIntId(groupId) }, fromBlock: BigInt(this._options.startBlock || 0) })) as MemberUpdatedLog[] @@ -281,7 +289,7 @@ export default class SemaphoreViem { abi: SemaphoreABI, eventName: "MembersAdded", args: { - groupId: BigInt(groupId) + groupId: toBigIntId(groupId) }, fromBlock: BigInt(this._options.startBlock || 0) })) as MembersAddedLog[] @@ -303,14 +311,14 @@ export default class SemaphoreViem { abi: SemaphoreABI, eventName: "MemberAdded", args: { - groupId: BigInt(groupId) + groupId: toBigIntId(groupId) }, fromBlock: BigInt(this._options.startBlock || 0) })) as MemberAddedLog[] const members: string[] = [] - const merkleTreeSize = await this._contract.read.getMerkleTreeSize([groupId]) + const merkleTreeSize = await this._contract.read.getMerkleTreeSize([toBigIntId(groupId)]) let index = 0 @@ -350,8 +358,8 @@ export default class SemaphoreViem { * @param groupId The unique identifier of the group. * @returns A promise that resolves to an array of validated proofs. */ - async getGroupValidatedProofs(groupId: string): Promise { - requireString(groupId, "groupId") + async getGroupValidatedProofs(groupId: GroupId): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number", "object"]) const groupAdmin = await this._contract.read.getGroupAdmin([groupId]) @@ -364,7 +372,7 @@ export default class SemaphoreViem { abi: SemaphoreABI, eventName: "ProofValidated", args: { - groupId: BigInt(groupId) + groupId: toBigIntId(groupId) }, fromBlock: BigInt(this._options.startBlock || 0) })) as ProofValidatedLog[] @@ -386,8 +394,8 @@ export default class SemaphoreViem { * @param member The identity commitment to check. * @returns A promise that resolves to a boolean indicating whether the member is in the group. */ - async isGroupMember(groupId: string, member: string): Promise { - requireString(groupId, "groupId") + async isGroupMember(groupId: GroupId, member: string): Promise { + requireTypes(groupId as any, "groupId", ["string", "bigint", "number", "object"]) requireString(member, "member") const members = await this.getGroupMembers(groupId)