diff --git a/packages/utils/LICENSE b/packages/utils/LICENSE
new file mode 100644
index 000000000..8ef16f7a5
--- /dev/null
+++ b/packages/utils/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Ethereum Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/utils/README.md b/packages/utils/README.md
new file mode 100644
index 000000000..77e86e326
--- /dev/null
+++ b/packages/utils/README.md
@@ -0,0 +1,80 @@
+
+
+ Semaphore utils
+
+ A library to provide utility functions to the other Semaphore packages.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+> [!NOTE]
+> Please, for more information on the modules provided by this library, see its code documentation [here](https://js.semaphore.pse.dev/modules/_semaphore_protocol_utils).
+
+## 🛠 Install
+
+### npm or yarn
+
+Install the `@semaphore-protocol/utils` package with npm:
+
+```bash
+npm i @semaphore-protocol/utils
+```
+
+or yarn:
+
+```bash
+yarn add @semaphore-protocol/utils
+```
+
+## 📜 Usage
+
+```typescript
+// You can import modules from the main bundle.
+import { errors, types } from "@semaphore-protocol/utils"
+
+// Or by using conditional exports.
+import { requireNumber } from "@semaphore-protocol/utils/errors"
+import { isNumber } from "@semaphore-protocol/utils/types"
+```
diff --git a/packages/utils/build.tsconfig.json b/packages/utils/build.tsconfig.json
new file mode 100644
index 000000000..2d4a1d6da
--- /dev/null
+++ b/packages/utils/build.tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "declarationDir": "dist/types"
+ },
+ "include": ["src"]
+}
diff --git a/packages/utils/package.json b/packages/utils/package.json
new file mode 100644
index 000000000..bf837b6cd
--- /dev/null
+++ b/packages/utils/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "@semaphore-protocol/utils",
+ "version": "4.0.0-alpha.8",
+ "description": "A library to provide utility functions to the other Semaphore packages.",
+ "type": "module",
+ "license": "MIT",
+ "main": "dist/index.js",
+ "types": "dist/types/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/types/index.d.ts",
+ "require": "./dist/index.cjs",
+ "default": "./dist/index.js"
+ },
+ "./errors": {
+ "types": "./dist/types/errors.d.ts",
+ "require": "./dist/lib.commonjs/errors.cjs",
+ "default": "./dist/lib.esm/errors.js"
+ },
+ "./types": {
+ "types": "./dist/types/types.d.ts",
+ "require": "./dist/lib.commonjs/types.cjs",
+ "default": "./dist/lib.esm/types.js"
+ }
+ },
+ "files": [
+ "dist/",
+ "src/",
+ "LICENSE",
+ "README.md"
+ ],
+ "repository": "https://github.com/semaphore-protocol/semaphore",
+ "homepage": "https://github.com/semaphore-protocol/semaphore/tree/main/packages/utils",
+ "bugs": {
+ "url": "https://github.com/semaphore-protocol/semaphore.git/issues"
+ },
+ "scripts": {
+ "build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
+ "prepublishOnly": "yarn build"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "devDependencies": {
+ "rollup-plugin-cleanup": "^3.2.1",
+ "rollup-plugin-typescript2": "^0.36.0"
+ }
+}
diff --git a/packages/utils/rollup.config.ts b/packages/utils/rollup.config.ts
new file mode 100644
index 000000000..535b0adbd
--- /dev/null
+++ b/packages/utils/rollup.config.ts
@@ -0,0 +1,30 @@
+import * as fs from "fs"
+import cleanup from "rollup-plugin-cleanup"
+import typescript from "rollup-plugin-typescript2"
+
+const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8"))
+const banner = `/**
+ * @module ${pkg.name}
+ * @version ${pkg.version}
+ * @file ${pkg.description}
+ * @copyright Ethereum Foundation 2024
+ * @license ${pkg.license}
+ * @see [Github]{@link ${pkg.homepage}}
+*/`
+
+export default {
+ input: "src/index.ts",
+ output: [
+ { file: pkg.exports["."].require, format: "cjs", banner, exports: "auto" },
+ { file: pkg.exports["."].default, format: "es", banner },
+ { dir: "./dist/lib.commonjs", format: "cjs", banner, preserveModules: true },
+ { dir: "./dist/lib.esm", format: "es", banner, preserveModules: true }
+ ],
+ plugins: [
+ typescript({
+ tsconfig: "./build.tsconfig.json",
+ useTsconfigDeclarationDir: true
+ }),
+ cleanup({ comments: "jsdoc" })
+ ]
+}
diff --git a/packages/utils/src/errors.ts b/packages/utils/src/errors.ts
new file mode 100644
index 000000000..842bd479a
--- /dev/null
+++ b/packages/utils/src/errors.ts
@@ -0,0 +1,108 @@
+/**
+ * @module Errors
+ * This module is designed to provide utility functions for validating
+ * function parameters. It includes functions that throw type errors if
+ * the parameters do not meet specified criteria, such as being defined,
+ * a number, a string, a function, or an array. This module helps ensure
+ * that functions receive the correct types of inputs, enhancing code
+ * reliability and reducing runtime errors.
+ */
+
+import {
+ SupportedType,
+ isArray,
+ isDefined,
+ isFunction,
+ isNumber,
+ isString,
+ isSupportedType,
+ isType,
+ isUint8Array
+} from "./types"
+
+/**
+ * It throws a type error if the parameter value has not been defined.
+ * @param parameterValue The parameter value.
+ * @param parameterName The parameter name.
+ */
+export function requireDefined(parameterValue: any, parameterName: string) {
+ if (!isDefined(parameterValue)) {
+ throw new TypeError(`Parameter '${parameterName}' is not defined`)
+ }
+}
+
+/**
+ * It throws a type error if the parameter value is not a number.
+ * @param parameterValue The parameter value.
+ * @param parameterName The parameter name.
+ */
+export function requireNumber(parameterValue: number, parameterName: string) {
+ if (!isNumber(parameterValue)) {
+ throw new TypeError(`Parameter '${parameterName}' is not a number`)
+ }
+}
+
+/**
+ * It throws a type error if the parameter value is not a string.
+ * @param parameterValue The parameter value.
+ * @param parameterName The parameter name.
+ */
+export function requireString(parameterValue: string, parameterName: string) {
+ if (!isString(parameterValue)) {
+ throw new TypeError(`Parameter '${parameterName}' is not a string`)
+ }
+}
+
+/**
+ * It throws a type error if the parameter value is not a function.
+ * @param parameterValue The parameter value.
+ * @param parameterName The parameter name.
+ */
+export function requireFunction(parameterValue: Function, parameterName: string) {
+ if (!isFunction(parameterValue)) {
+ throw new TypeError(`Parameter '${parameterName}' is not a function`)
+ }
+}
+
+/**
+ * It throws a type error if the parameter value is not an array.
+ * @param parameterValue The parameter value.
+ * @param parameterName The parameter name.
+ */
+export function requireArray(parameterValue: any[], parameterName: string) {
+ if (!isArray(parameterValue)) {
+ throw new TypeError(`Parameter '${parameterName}' is not an array`)
+ }
+}
+
+/**
+ * It throws a type error if the parameter value is not a uint8array.
+ * @param parameterValue The parameter value.
+ * @param parameterName The parameter name.
+ */
+export function requireUint8Array(parameterValue: Uint8Array, parameterName: string) {
+ if (!isUint8Array(parameterValue)) {
+ throw new TypeError(`Parameter '${parameterName}' is not a Uint8Array`)
+ }
+}
+
+/**
+ * It throws a type error if the parameter value type is not part of the list of types.
+ * @param parameterValue The parameter value.
+ * @param parameterName The parameter name.
+ */
+export function requireTypes(parameterValue: any, parameterName: string, types: SupportedType[]) {
+ for (const type of types) {
+ if (!isSupportedType(type)) {
+ throw new Error(`Type '${type}' is not supported`)
+ }
+ }
+
+ for (const type of types) {
+ if (isType(parameterValue, type)) {
+ return
+ }
+ }
+
+ throw new TypeError(`Parameter '${parameterName}' is none of the following types: ${types.join(", ")}`)
+}
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
new file mode 100644
index 000000000..a76016484
--- /dev/null
+++ b/packages/utils/src/index.ts
@@ -0,0 +1,4 @@
+import * as errors from "./errors"
+import * as types from "./types"
+
+export { errors, types }
diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts
new file mode 100644
index 000000000..be6944e4f
--- /dev/null
+++ b/packages/utils/src/types.ts
@@ -0,0 +1,101 @@
+/**
+ * @module Types
+ * This module provides utility functions to check data types.
+ * It defines a set of supported types and includes functions to check if
+ * a value is defined and if it matches a supported type. These functions
+ * are useful for type checking and validation in the other libraries,
+ * enhancing code robustness and reliability.
+ */
+
+// The list of types supported by this utility functions.
+const supportedTypes = ["number", "string", "function", "array", "uint8array"] as const
+
+// Type extracted from the list above.
+export type SupportedType = (typeof supportedTypes)[number]
+
+/**
+ * It returns true if the value is defined, false otherwise.
+ * @param value The value to be checked.
+ * @returns True or false.
+ */
+export function isDefined(value: any): boolean {
+ return typeof value !== "undefined"
+}
+
+/**
+ * It returns true if the value is a number, false otherwise.
+ * @param value The value to be checked.
+ * @returns True or false.
+ */
+export function isNumber(value: any): boolean {
+ return typeof value === "number"
+}
+
+/**
+ * It returns true if the value is a string, false otherwise.
+ * @param value The value to be checked.
+ * @returns True or false.
+ */
+export function isString(value: any): boolean {
+ return typeof value === "string"
+}
+
+/**
+ * It returns true if the value is a function, false otherwise.
+ * @param value The value to be checked.
+ * @returns True or false.
+ */
+export function isFunction(value: any): boolean {
+ return typeof value === "function"
+}
+
+/**
+ * It returns true if the value is an array, false otherwise.
+ * @param value The value to be checked.
+ * @returns True or false.
+ */
+export function isArray(value: any): boolean {
+ return typeof value === "object" && Array.isArray(value)
+}
+
+/**
+ * It returns true if the value is a uint8array, false otherwise.
+ * @param value The value to be checked.
+ * @returns True or false.
+ */
+export function isUint8Array(value: any): boolean {
+ return value instanceof Uint8Array
+}
+
+/**
+ * It returns true if the value type is the same as the type passed
+ * as the second parameter, false otherwise.
+ * @param type The expected type.
+ * @returns True or false.
+ */
+export function isType(value: any, type: SupportedType): boolean {
+ switch (type) {
+ case "number":
+ return isNumber(value)
+ case "string":
+ return isString(value)
+ case "function":
+ return isFunction(value)
+ case "array":
+ return isArray(value)
+ case "uint8array":
+ return isUint8Array(value)
+ default:
+ return false
+ }
+}
+
+/**
+ * Return true if the type is being supported by this utility
+ * functions, false otherwise.
+ * @param type The type to be checked.
+ * @returns True or false
+ */
+export function isSupportedType(type: string): type is SupportedType {
+ return (supportedTypes as readonly string[]).includes(type)
+}
diff --git a/packages/utils/tests/index.test.ts b/packages/utils/tests/index.test.ts
new file mode 100644
index 000000000..6366fdc28
--- /dev/null
+++ b/packages/utils/tests/index.test.ts
@@ -0,0 +1,162 @@
+import { errors, types } from "../src"
+
+describe("Utils", () => {
+ describe("# types", () => {
+ it("Should return true if the value is a number", () => {
+ expect(types.isNumber(1)).toBeTruthy()
+ })
+
+ it("Should return false if the value is not a number", () => {
+ expect(types.isNumber("string")).toBeFalsy()
+ })
+
+ it("Should return true if the value is a string", () => {
+ expect(types.isString("string")).toBeTruthy()
+ })
+
+ it("Should return false if the value is not a string", () => {
+ expect(types.isString(1)).toBeFalsy()
+ })
+
+ it("Should return true if the value is a function", () => {
+ expect(types.isFunction(() => true)).toBeTruthy()
+ })
+
+ it("Should return false if the value is not a function", () => {
+ expect(types.isFunction(1)).toBeFalsy()
+ })
+
+ it("Should return true if the value is an array", () => {
+ expect(types.isArray([])).toBeTruthy()
+ })
+
+ it("Should return false if the value is not an array", () => {
+ expect(types.isArray(1)).toBeFalsy()
+ })
+
+ it("Should return true if the value is an uint8array", () => {
+ expect(types.isUint8Array(new Uint8Array([]))).toBeTruthy()
+ })
+
+ it("Should return false if the value is not an uint8array", () => {
+ expect(types.isArray(1)).toBeFalsy()
+ })
+
+ it("Should return true if the value type is the one expected", () => {
+ expect(types.isType(1, "number")).toBeTruthy()
+ expect(types.isType("string", "string")).toBeTruthy()
+ expect(types.isType(() => true, "function")).toBeTruthy()
+ expect(types.isType([], "array")).toBeTruthy()
+ expect(types.isType(new Uint8Array([]), "uint8array")).toBeTruthy()
+ })
+
+ it("Should return false if the value type is not the one expected or is not supported", () => {
+ expect(types.isType("string", "number")).toBeFalsy()
+ expect(types.isType(1, "string")).toBeFalsy()
+ expect(types.isType(1, "function")).toBeFalsy()
+ expect(types.isType(1, "array")).toBeFalsy()
+ expect(types.isType(1, "uint8array")).toBeFalsy()
+ expect(types.isType(1, "type" as any)).toBeFalsy()
+ })
+
+ it("Should return true if the type is supported", () => {
+ expect(types.isSupportedType("number")).toBeTruthy()
+ })
+
+ it("Should return false if the type is not supported", () => {
+ expect(types.isSupportedType("type")).toBeFalsy()
+ })
+ })
+
+ describe("# errors", () => {
+ it("Should throw an error if the parameter is not defined", () => {
+ const fun = () => errors.requireDefined(undefined as any, "parameter")
+
+ expect(fun).toThrow("Parameter 'parameter' is not defined")
+ })
+
+ it("Should not throw an error if the parameter is defined", () => {
+ const fun = () => errors.requireDefined(1, "parameter")
+
+ expect(fun).not.toThrow()
+ })
+
+ it("Should throw an error if the parameter is not a number", () => {
+ const fun = () => errors.requireNumber("euo" as any, "parameter")
+
+ expect(fun).toThrow("Parameter 'parameter' is not a number")
+ })
+
+ it("Should not throw an error if the parameter is a number", () => {
+ const fun = () => errors.requireNumber(1, "parameter")
+
+ expect(fun).not.toThrow()
+ })
+
+ it("Should throw an error if the parameter is not a string", () => {
+ const fun = () => errors.requireString(1 as any, "parameter")
+
+ expect(fun).toThrow("Parameter 'parameter' is not a string")
+ })
+
+ it("Should not throw an error if the parameter is a string", () => {
+ const fun = () => errors.requireString("string", "parameter")
+
+ expect(fun).not.toThrow()
+ })
+
+ it("Should throw an error if the parameter is not an array", () => {
+ const fun = () => errors.requireArray(1 as any, "parameter")
+
+ expect(fun).toThrow("Parameter 'parameter' is not an array")
+ })
+
+ it("Should not throw an error if the parameter is an array", () => {
+ const fun = () => errors.requireArray([], "parameter")
+
+ expect(fun).not.toThrow()
+ })
+
+ it("Should throw an error if the parameter is not a uint8array", () => {
+ const fun = () => errors.requireUint8Array([] as any, "parameter")
+
+ expect(fun).toThrow("Parameter 'parameter' is not a Uint8Array")
+ })
+
+ it("Should not throw an error if the parameter is a uint8array", () => {
+ const fun = () => errors.requireUint8Array(new Uint8Array([]), "parameter")
+
+ expect(fun).not.toThrow()
+ })
+
+ it("Should throw an error if the parameter is not a function", () => {
+ const fun = () => errors.requireFunction(1 as any, "parameter")
+
+ expect(fun).toThrow("Parameter 'parameter' is not a function")
+ })
+
+ it("Should not throw an error if the parameter is a function", () => {
+ const fun = () => errors.requireFunction(() => true, "parameter")
+
+ expect(fun).not.toThrow()
+ })
+
+ it("Should throw an error if the parameter is neither a function nor a number", () => {
+ const fun = () => errors.requireTypes("string", "parameter", ["function", "number"])
+
+ expect(fun).toThrow("Parameter 'parameter' is none of the following types: function, number")
+ })
+
+ it("Should not throw an error if the parameter is either a string or an array", () => {
+ const fun = () => errors.requireTypes("string", "parameter", ["string", "array"])
+
+ expect(fun).not.toThrow()
+ })
+
+ it("Should throw an error if the parameter types are not supported", () => {
+ const fun = () => errors.requireTypes("string", "parameter", ["string", "type" as any])
+
+ expect(fun).toThrow("Type 'type' is not supported")
+ })
+ })
+})
diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json
new file mode 100644
index 000000000..71510a096
--- /dev/null
+++ b/packages/utils/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../../tsconfig.json",
+ "include": ["src", "tests", "rollup.config.ts"]
+}
diff --git a/packages/utils/typedoc.json b/packages/utils/typedoc.json
new file mode 100644
index 000000000..77a471c91
--- /dev/null
+++ b/packages/utils/typedoc.json
@@ -0,0 +1,3 @@
+{
+ "entryPoints": ["src/index.ts"]
+}
diff --git a/yarn.lock b/yarn.lock
index cf953157d..17cc334b6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8533,6 +8533,7 @@ __metadata:
dependencies:
"@rollup/plugin-alias": ^5.1.0
"@rollup/plugin-json": ^6.1.0
+ "@semaphore-protocol/utils": 4.0.0-alpha.8
"@types/download": ^8.0.5
"@types/snarkjs": 0.7.8
"@types/tmp": ^0.2.6
@@ -8549,6 +8550,15 @@ __metadata:
languageName: unknown
linkType: soft
+"@semaphore-protocol/utils@4.0.0-alpha.8, @semaphore-protocol/utils@workspace:packages/utils":
+ version: 0.0.0-use.local
+ resolution: "@semaphore-protocol/utils@workspace:packages/utils"
+ dependencies:
+ rollup-plugin-cleanup: ^3.2.1
+ rollup-plugin-typescript2: ^0.36.0
+ languageName: unknown
+ linkType: soft
+
"@sentry/core@npm:5.30.0":
version: 5.30.0
resolution: "@sentry/core@npm:5.30.0"