Constructs a new SemaphoreEthers instance, initializing it with a network or a custom Ethereum node URL, +and optional configuration settings for the ethers provider and contract.
+The Ethereum network name or a custom JSON-RPC URL to connect to.
+Configuration options for the ethers provider and the Semaphore contract.
+Private
_contractPrivate
_networkPrivate
_optionsRetrieves the ethers Contract instance used to interact with the Semaphore contract.
+The Contract instance.
+Retrieves the Ethereum network or custom URL currently used by this instance.
+The network or URL as a string.
+Retrieves the options used for configuring the ethers provider and the Semaphore contract.
+The configuration options.
+Retrieves detailed information about a specific group by its ID. This method queries the Semaphore contract +to get the group's admin, Merkle tree root, depth, and size.
+The unique identifier of the group.
+A promise that resolves to a GroupResponse object.
+Fetches a list of members from a specific group. This method queries the Semaphore contract for events +related to member additions and updates, and constructs the list of current group members.
+The unique identifier of the group.
+A promise that resolves to an array of member identity commitments as strings.
+Retrieves a list of validated proofs for a specific group. This method queries the Semaphore contract +for "ProofValidated" events and returns details about each proof.
+The unique identifier of the group.
+A promise that resolves to an array of validated proofs.
+Checks whether a specific member is part of a group. This method queries the Semaphore contract +to determine if the provided identity commitment is a member of the specified group.
+The unique identifier of the group.
+The identity commitment of the member to check.
+A promise that resolves to true if the member is part of the group, otherwise false.
+The SemaphoreSubgraph class provides an interface to interact with the Semaphore smart contract +via subgraph queries. It enables operations such as retrieving lists of group members and validated proofs, +as well as checking membership within groups. +Each group in Semaphore is represented as a LeanIMT +(Lean Incremental Merkle Tree). This class supports interaction through either a +SupportedNetwork or a direct URL to the subgraph. The subgraphs themselves are hosted on +The Graph protocol, facilitating efficient and decentralized query processing.
+Initializes the SemaphoreSubgraph instance with a supported network or a custom subgraph URL. +This allows to interact with the Semaphore smart contract through the specified endpoint.
+Either a supported network identifier or a direct URL to the subgraph.
+Private
_urlRetrieves the URL of the subgraph currently being used by the instance. +This URL points to the specific subgraph where Semaphore data is stored.
+The URL of the subgraph.
+Fetches detailed information about a specific group by its ID. This method can also retrieve +members and validated proofs for the group if requested via options.
+The unique identifier of the group.
+Configuration options to specify which details to fetch about the group.
+A promise that resolves to the details of the specified group.
+Retrieves detailed information about groups from the subgraph based on the provided options. +This method can filter groups by various parameters and include additional details like members +and validated proofs if specified in the options.
+Configuration options to filter groups and specify which additional details to fetch.
+A promise that resolves to an array of group details.
+Determines whether a specific member is part of a group. This method queries the subgraph to check +if the provided member's identity commitment exists within the specified group.
+The unique identifier of the group.
+The identity commitment of the member to check.
+A promise that resolves to true if the member is part of the group, otherwise false.
+The Semaphore group is a LeanIMT +(Lean Incremental Merkle Tree), i.e. an optimized version of the incremental binary Merkle tree +used by Semaphore V3. The new tree does not use zero hashes, and its depth is dynamic. +The members of a Semaphore group, or the leaves of a tree, are the identity commitments. +Thanks to the properties of Merkle trees, it can be efficiently demonstrated that a group +member belongs to the group. +This class supports operations such as member addition, update, removal and Merkle proof +generation and verification. Groups can also be exported or imported.
+Creates a new instance of the Group. Optionally, a list of identity commitments can
+be passed as a parameter. Adding members in chunks is more efficient than adding
+them one by one with the addMember
function.
A list of identity commitments.
+Returns the depth of the tree.
+The tree depth as a number.
+Returns the members (i.e. identity commitments) of the group.
+The list of members of the group.
+Returns the root hash of the tree.
+The root hash as a string.
+Returns the size of the tree (i.e. number of leaves).
+The tree size as a number.
+Creates a proof of membership for a member of the group.
+The index of the member.
+The MerkleProof object.
+Static
importThe Semaphore identity is essentially an EdDSA +public/private key pair. The EdDSA implementation +in this library uses Baby Jubjub for public key generation +and Poseidon for signatures. +In addition, the commitment, i.e. the hash of the public key, is used to represent +Semaphore identities in groups, adding an additional layer of privacy and security. +The private key of the identity can be exported as a base64 string.
+Initializes the class attributes based on a given private key, which must be text or a buffer. +If the private key is not passed as a parameter, a random private key will be generated. +The EdDSAPoseidon class is used to generate the secret scalar and the public key. +Additionally, the constructor computes a commitment of the public key using a hash function (Poseidon).
+Optional
privateKey: string | Buffer | Uint8ArrayThe private key used to derive the public key (hexadecimal or string).
+// Generates an identity.
const { privateKey, publicKey, commitment } = new Identity("private-key")
+
+// Generates an identity with a random private key.
const { privateKey, publicKey, commitment } = new Identity()
+
+Private
_commitmentPrivate
_privatePrivate
_publicPrivate
_secretReturns the commitment hash of the public key.
+The commitment as a string.
+Returns the private key.
+The private key as a buffer or text.
+Returns the secret scalar.
+The secret scalar as a string.
+Generates a signature for a given message using the private key. +This method demonstrates how to sign a message and could be used +for authentication or data integrity.
+The message to be signed.
+A Signature object containing the signature components.
+const identity = new Identity()
const signature = identity.signMessage("message")
+
+Static
generateGenerates the commitment from the given public key. +This static method is particularly useful after signature verification, +as it allows retrieval of the corresponding commitment associated with the public key.
+The public key to generate the commitment.
+The Semaphore identity commitment.
+const identity = new Identity()
Identity.generateCommitment(identity.publicKey)
+
+Static
importStatic
verifyVerifies a signature against a given message and public key. +This static method allows for the verification of signatures without needing +an instance of the Identity class. It's useful for cases where you only have +the public key, the message and a signature, and need to verify if they match.
+A boolean indicating whether the signature is valid.
+const identity = new Identity()
const signature = identity.signMessage("message")
Identity.verifySignature("message", signature, identity.publicKey)
+
+It generates a Semaphore proof, i.e. a zero-knowledge proof that an identity that +is part of a group has shared an anonymous message. +The message may be any arbitrary user-defined value (e.g. a vote), or the hash of that value. +The scope is a value used like a topic on which users can generate a valid proof only once, +for example the id of an election in which voters can only vote once. +The hash of the identity's scope and secret scalar is called a nullifier and can be +used to verify whether that identity has already generated a valid proof in that scope. +The depth of the tree determines which zero-knowledge artifacts to use to generate the proof. +If it is not defined, it will be inferred from the group or Merkle proof passed as the second parameter. +Finally, the artifacts themselves can be passed manually with file paths, +or they will be automatically fetched. +Please keep in mind that groups with 1 member or 2 members cannot be considered anonymous.
+The Semaphore identity.
+The Semaphore group or its Merkle proof.
+The Semaphore message.
+The Semaphore scope.
+Optional
merkleTreeDepth: numberThe depth of the tree with which the circuit was compiled.
+Optional
snarkArtifacts: SnarkArtifactsSee SnarkArtifacts.
+The Semaphore proof ready to be verified.
+Verifies whether a Semaphore proof is valid. Depending on the depth of the tree used to +generate the proof, a different verification key will be used.
+The Semaphore proof.
+True if the proof is valid, false otherwise.
+Typically used for decoding on-chain Semaphore messages. +When Semaphore messages are text they are converted to bigints before +the proof is generated (and eventually sent on-chain). +This function help devs converting bigint messages to text again. +If the original message was not text the output of this +function won't probably be human-readable text.
+The Semaphore message as a bigint.
+The Semaphore message as a text.
+Returns name, address and start block of a Semaphore contract deployed +on a specific supported network.
+The network supported by Semaphore.
+An object with name, address and start block of the deployed contract.
+Utility function to get an object compatible with the Hardhat 'networks' option. +If the private key is not defined it returns an empty object.
+Optional
privateKey: stringPrivate key to be used with networks.
+An object compatible with the Hardhat 'networks' option.
++
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +Semaphore is a generic privacy layer. Leveraging zero-knowledge technology, users can prove their membership in groups and send messages (extending from votes to endorsements) off-chain or across EVM-compatible blockchains, all without revealing their personal identity. | +
---|
The core of the Semaphore protocol is in the circuit logic. However, Semaphore also provides Solidity contracts and JavaScript libraries to make the steps for offchain proof creation and onchain/offchain verification easier. To learn more about Semaphore visit semaphore.pse.dev.
+++[!IMPORTANT]
+
Help Semaphore prosper by sharing your ideas with the PSE acceleration program.
Package | +Version | +Downloads | + +
---|---|---|
+ + @semaphore-protocol/core + + | ++ + + + + | ++ + + + + | +
+ + @semaphore-protocol/contracts + + | ++ + + + + | ++ + + + + | +
+ + @semaphore-protocol/identity + + + (docs) + + | ++ + + + + | ++ + + + + | +
+ + @semaphore-protocol/group + + + (docs) + + | ++ + + + + | ++ + + + + | +
+ + @semaphore-protocol/proof + + + (docs) + + | ++ + + + + | ++ + + + + | +
+ + @semaphore-protocol/data + + + (docs) + + | ++ + + + + | ++ + + + + | +
+ + @semaphore-protocol/hardhat + + | ++ + + + + | ++ + + + + | +
+ + @semaphore-protocol/cli + + | ++ + + + + | ++ + + + + | +
+ + @semaphore-protocol/utils + + + (docs) + + | ++ + + + + | ++ + + + + | +
Clone this repository:
+git clone https://github.com/semaphore-protocol/semaphore.git
+
+And install the dependencies:
+cd semaphore && yarn
+
+Copy the .env.example
file as .env
:
cp .env.example .env
+
+And add your environment variables.
+Run ESLint to analyze the code and catch bugs:
+yarn lint
+
+Run Prettier to check formatting rules:
+yarn format
+
+Or to automatically format the code:
+yarn format:write
+
+Semaphore uses conventional commits. A command line utility to commit using the correct syntax can be used by running:
+git commit
+
+It will also automatically check that the modified files comply with ESLint and Prettier rules.
+Run Jest to test the JS libraries:
+yarn test:libraries
+
+Run Mocha to test the contracts:
+yarn test:contracts
+
+Or test everything with:
+yarn test
+
+Run Rollup and TheGraph to build all the packages and the subgraph:
+yarn build
+
+Compile the smart contracts with Hardhat:
+yarn compile:contracts
+
+Run TypeDoc to generate a documentation website for each package:
+yarn docs
+
+The output will be placed on the docs
folder.
Bump a new version with:
+yarn version:bump <version>
# e.g. yarn version:bump 2.0.0
+
+It will create a commit and a git tag that will need to be pushed on the main branch. A workflow will be triggered and will +publish the Semaphore packages on npm and release a new version on Github with its changelogs automatically.
++
A library for querying Semaphore smart contract.
+ + ++ + + + + + + + + + + + + + + + + + + + + +
+ +This library provides tools for querying and interacting with the Semaphore.sol smart contract. It supports both the Semaphore subgraph and direct Ethereum network connections via Ethers. Designed for use in both Node.js and browser environments, it facilitates the management of group data and verification processes within the Semaphore protocol. |
+
---|
Install the @semaphore-protocol/data
package with npm:
npm i @semaphore-protocol/data
+
+or yarn:
+yarn add @semaphore-protocol/data
+
+For detailed information on the functions provided by @semaphore-protocol/data
, please refer to the TypeDoc documentation.
Initialize a Semaphore Subgraph instance
+import { SemaphoreSubgraph } from "@semaphore-protocol/data"
const semaphoreSubgraph = new SemaphoreSubgraph()
// or:
const semaphoreSubgraphOnArbitrum = new SemaphoreSubgraph("arbitrum")
// or:
const customSubgraph = new SemaphoreSubgraph(
"https://api.studio.thegraph.com/query/14377/<your-subgraph>/<your-version>"
)
+
+With your SemaphoreSubgraph, you can:
+Query Group IDs
+const groupIds = await semaphoreSubgraph.getGroupIds()
+
+Query Group Details
+const group = await semaphoreSubgraph.getGroup("42")
const { members, verifiedProofs } = await semaphoreSubgraph.getGroup("42", { members: true, verifiedProofs: true })
+
+Query Group Members
+const members = await semaphoreSubgraph.getGroupMembers("42")
+
+Query Verified Proofs
+const verifiedProofs = await semaphoreSubgraph.getGroupVerifiedProofs("42")
+
+Check Group Membership
+const isMember = await semaphoreSubgraph.isGroupMember(
"42",
"16948514235341957898454876473214737047419402240398321289450170535251226167324"
)
+
+Initialize a Semaphore Ethers instance
+import { SemaphoreEthers } from "@semaphore-protocol/data"
const semaphoreEthers = new SemaphoreEthers()
// or:
const semaphoreEthersOnHomestead = new SemaphoreEthers("homestead", {
address: "semaphore-address",
startBlock: 0
})
// or:
const localEthersInstance = new SemaphoreEthers("http://localhost:8545", {
address: "semaphore-address"
})
+
+With your SemaphoreEthers instance, you can:
+Fetch Group IDs
+const groupIds = await semaphoreEthers.getGroupIds()
+
+Fetch Group Details
+const group = await semaphoreEthers.getGroup("42")
+
+Fetch Group Admin
+const admin = await semaphoreEthers.getGroupAdmin("42")
+
+Fetch Group Members
+const members = await semaphoreEthers.getGroupMembers("42")
+
+Fetch Verified Proofs
+const verifiedProofs = await semaphoreEthers.getGroupVerifiedProofs("42")
+
+Check Group Membership
+const isMember = await semaphoreEthers.isGroupMember(
"42",
"16948514235341957898454876473214737047419402240398321289450170535251226167324"
)
+
++
A library to create and manage Semaphore groups.
+ + ++ + + + + + + + + + + + + + + + + + + + + +
+ +This library is an abstraction of the LeanIMT data structure (part of @zk-kit/imt ). The main goal is to make it easier to create offchain groups, which are also used to generate Semaphore proofs. Semaphore groups are actually Merkle trees, and the group members are tree leaves. |
+
---|
Install the @semaphore-protocol/group
package with npm:
npm i @semaphore-protocol/group
+
+or yarn:
+yarn add @semaphore-protocol/group
+
+For more information on the functions provided by @semaphore-protocol/group
, please refer to the TypeDoc documentation.
# new Group(members: BigNumberish[] = []): Group
+import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"
const group1 = new Group()
const identity1 = new Identity()
const identity2 = new Identity()
const group2 = new Group([identity1.commitment, identity2.commitment])
+
+# addMember(member: BigNumberish)
+import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"
const group = new Group()
const { commitment } = new Identity()
group.addMember(commitment)
// 12989101133047504182892154686643420754368236204022364847543591045056549053997n
console.log(group.members[0])
+
+# addMembers(members: BigNumberish[])
+import { Group } from "@semaphore-protocol/group"
import { Identity } from "@semaphore-protocol/identity"
const group = new Group()
const identity1 = new Identity()
const identity2 = new Identity()
group.addMembers([identity1.commitment, identity2.commitment])
+
+# updateMember(index: number, member: BigNumberish)
+import { Group } from "@semaphore-protocol/group"
const group = new Group([1n, 3n])
group.updateMember(0, 2)
console.log(group.members[0]) // "2n"
+
+# removeMember(index: number)
+import { Group } from "@semaphore-protocol/group"
const group = new Group([1n, 3n])
group.removeMember(0)
console.log(group.members[0]) // 0n
+
+# indexOf(member: BigNumberish): number
+import { Group } from "@semaphore-protocol/group"
const group = new Group([1n])
const index = group.indexOf(1)
console.log(index) // 0
+
+# generateMerkleProof(index: number): MerkleProof
+import { Group } from "@semaphore-protocol/group"
const group = new Group([1n, 3n])
const proof = group.generateMerkleProof(0)
console.log(proof)
/*
{
index: 0,
leaf: '1',
root: '21106761926285267690763443010820487107972411248208546226053195422384279971821',
siblings: [ '3' ]
}
*/
+
+# export(): string
+import { Group } from "@semaphore-protocol/group"
const group = new Group([1n, 2n, 3n])
const exportedGroup = group.export()
console.log(exportedGroup)
/*
[["1","2","3"],["7853200120776062878684798364095072458815029376092732009249414926327459813530","3"],["13816780880028945690020260331303642730075999758909899334839547418969502592169"]]
*/
+
+# import(exportedGroup: string): Group
+import { Group } from "@semaphore-protocol/group"
const group1 = new Group([1n, 2n, 3n])
const exportedGroup = group.export()
const group2 = Group.import(exportedGroup)
assert(group1.root === group2.root)
+
++
A library to create Semaphore identities.
+ + ++ + + + + + + + + + + + + + + + + + + + + +
+ +This library provides a class that can be used to create identities compatible with the Semaphore circuits. Each identity contains an EdDSA private key, its public key, and the identity commitment, which is the Poseidon hash of the public key. | +
---|
Install the @semaphore-protocol/identity
package with npm:
npm i @semaphore-protocol/identity
+
+or yarn:
+yarn add @semaphore-protocol/identity
+
+For more information on the functions provided by @semaphore-protocol/identity
, please refer to the TypeDoc documentation.
# new Identity(privateKey?: BigNumberish): Identity
+import { Identity } from "@semaphore-protocol/identity"
// The identity will be generated randomly.
const { privateKey, publicKey, commitment } = new Identity()
// Alternatively, you can pass your private key.
const identity = new Identity("your-private-key")
+
+# identity.export(): string
+import { Identity } from "@semaphore-protocol/identity"
const identity = new Identity()
const privateKey = identity.export()
+
+# identity.import(privateKey: string): Identity
+import { Identity } from "@semaphore-protocol/identity"
const identity = new Identity()
const privateKey = identity.export()
const identity2 = Identity.import(privateKey)
+
+# identity.signMessage(message: BigNumberish): Signature<string>
+import { Identity } from "@semaphore-protocol/identity"
const message = "message"
const identity = new Identity()
const signature = identity.signMessage(message)
+
+# Identity.verifySignature(message: BigNumberish, signature: Signature, publicKey: Point): boolean
+import { Identity } from "@semaphore-protocol/identity"
const message = "message"
const identity = new Identity()
const signature = identity.signMessage(message)
Identity.verifySignature(message, signature, identity.publicKey)
+
+# Identity.generateCommitment(publicKey: Point): bigint
+import { Identity } from "@semaphore-protocol/identity"
Identity.generateCommitment(identity.publicKey)
+
++
A library to generate and verify Semaphore proofs.
+ + ++ + + + + + + + + + + + + + + + + + + + + +
+ +This library provides utility functions to generate and verify Semaphore proofs compatible with the Semaphore circuits. Generating valid zero-knowledge proofs requires files that can only be obtained in an attested trusted-setup ceremony. | +
---|
Install the @semaphore-protocol/proof
package and its peer dependencies with npm:
npm i @semaphore-protocol/identity @semaphore-protocol/group @semaphore-protocol/proof
+
+or yarn:
+yarn add @semaphore-protocol/identity @semaphore-protocol/group @semaphore-protocol/proof
+
+For more information on the functions provided by @semaphore-protocol/proof
, please refer to the TypeDoc documentation.
# generateProof( +identity: Identity, +group: Group, +message: BigNumberish | Uint8Array | string, +scope: BigNumberish | Uint8Array | string, +merkleTreeDepth: number, +snarkArtifacts?: SnarkArtifacts +): Promise<_SemaphoreProof_>
+import { Identity } from "@semaphore-protocol/identity"
import { Group } from "@semaphore-protocol/group"
import { generateProof } from "@semaphore-protocol/proof"
const identity1 = new Identity()
const identity2 = new Identity()
const identity3 = new Identity()
const group = new Group([identity1.commitment, identity2.commitment, identity3.commitment])
const message = "Hello world"
const scope = "Semaphore"
// snarkArtifacts are not provided.
// So they will be automatically downloaded (see https://github.com/privacy-scaling-explorations/snark-artifacts).
const proof1 = await generateProof(identity1, group, message, scope)
// You can also specify the maximum tree depth supported by the proof.
const proof2 = await generateProof(identity2, group, message, scope, 20)
// You can also override our default zkey/wasm files.
const proof3 = await generateProof(identity3, group, message, scope, 20, {
wasm: "./semaphore.wasm",
zkey: "./semaphore.zkey"
})
+
+# verifyProof(semaphoreProof: SemaphoreProof): Promise<_boolean_>
+import { verifyProof } from "@semaphore-protocol/proof"
await verifyProof(proof1)
+
++
A library to provide utility functions to the other Semaphore packages.
+ + ++ + + + + + + + + + + + + + + + + + + + + +
+ +Install the @semaphore-protocol/utils
package with npm:
npm i @semaphore-protocol/utils
+
+or yarn:
+yarn add @semaphore-protocol/utils
+
+For more information on the functions and modules provided by @semaphore-protocol/utils
, please refer to the TypeDoc documentation.
// You can import functions/parameters from the main bundle.
import { supportedNetworks, decodeMessage } from "@semaphore-protocol/utils"
// Or by using conditional exports.
import supportedNetworks from "@semaphore-protocol/utils/supported-networks"
import decodeMessage from "@semaphore-protocol/utils/decode-message"
+
+Optional
address?: stringOptional
apiOptional
applicationOptional
applicationOptional
projectOptional
projectOptional
provider?: "etherscan" | "infura" | "alchemy" | "cloudflare" | "pocket" | "ankr"Optional
startOptional
filters?: { Optional
admin?: stringOptional
identityOptional
timestamp?: DateOptional
timestampOptional
timestampOptional
members?: booleanOptional
validatedOptional
admin?: stringOptional
members?: string[]Optional
validatedThe Merkle Proof contains the necessary parameters to enable the +verifier to be certain that a leaf belongs to the tree. Given the value +of the leaf and its index, it is possible to traverse the tree by +recalculating the hashes up to the root and using the node siblings. +If the calculated root matches the root in the proof, then the leaf +belongs to the tree. It's important to note that the function used +to generate the proof and the one used to verify it must use the +same hash function.
+Const
Const
Const
Const
Const
The SemaphoreEthers class provides a high-level interface to interact with the Semaphore smart contract +using the ethers.js library. It encapsulates all necessary functionalities to connect to Ethereum networks, +manage contract instances, and perform operations such as retrieving group information or checking group memberships. +This class simplifies the interaction with the Ethereum blockchain by abstracting the details of network connections +and contract interactions.
+