feat: relayer data checks and sdk integration (#58)

The core functionality of the feature takes place at
`packages/relayer/src/services/privacyPoolRelayer.service.ts` in the
`validateWithdrawal` function. There we check in order:
1. Decode Withdrawal.data and check feeReceiver ==
CONFIGURED_FEE_RECEIVER && feeBPS == CONFIGURED_FEE_BPS
2. Hash (Withdrawal || Scope) and check hash == Proof.context
3. Verify proof with SDK.verifyProof(Proof)
4. Call Entrypoint.scopeToPool() to get asset pool
5. Call Pool.ASSET() to get withdrawn asset
6. Check proof.withdrawnAmount() >=
CONFIGURED_MINIMUM_AMOUNT[withdrawn_asset]

If everything is ok, we call the Entrypoint.relay() with the user's
proof.

I've added examples for the config files `env.example`,
`withdraw_amount.example.json`. The first one are environment variables
to be defined in the server's environment. The second one contains a
mapping of the mimimum withdraw limits for the different supported
assets. There's an env var that should contain a path to this file.

Added a `Dockerfile` and a `docker-compose.yml` to the relayer to test
it or use it in dev flows. The circuit artifacts are loaded as volumes,
so before starting the docker-compose, you should run `yarn present`
from the circuits package dir to group all the artifacts and make them
easily accesible.

Additionally added a small package in
`/packages/relayer/test/integration/relay` that can be built and run to
test the relayer.
This commit is contained in:
bezze
2025-02-05 11:24:29 -03:00
committed by GitHub
parent 3265e9b732
commit b6f58e6887
47 changed files with 1235 additions and 1570 deletions

View File

@@ -12,6 +12,9 @@
"packages/sdk",
"packages/relayer"
],
"scripts": {
"docker:build:relayer": "docker build -f packages/relayer/Dockerfile -t privacy-pool/relayer ."
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
@@ -19,6 +22,7 @@
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.2.1",
"prettier": "3.3.3",
"typescript": "5.5.4",
"typescript-eslint": "8.20.0"
},
"dependencies": {

View File

@@ -19,6 +19,7 @@
"setup:withdraw": "npx circomkit setup withdraw ptau/powersOfTau28_hez_final_16.ptau",
"setup:commitment": "npx circomkit setup commitment ptau/powersOfTau28_hez_final_16.ptau",
"setup:merkle": "npx circomkit setup merkleTree ptau/powersOfTau28_hez_final_16.ptau",
"present": "sh ./scripts/present.sh",
"prove:withdraw": "npx circomkit prove withdraw default",
"prove:commitment": "npx circomkit prove commitment default",
"verify:withdraw": "npx circomkit verify withdraw default",

View File

@@ -0,0 +1,13 @@
#!/bin/bash
CIRCUITS=("merkleTree" "commitment" "withdraw")
BUILD_DIR="build"
DEST_DIR="artifacts"
mkdir -p "$DEST_DIR"
for circuit in "${CIRCUITS[@]}"
do
cp "$BUILD_DIR/$circuit/groth16_pkey.zkey" "$DEST_DIR/${circuit}.zkey"
cp "$BUILD_DIR/$circuit/groth16_vkey.json" "$DEST_DIR/${circuit}.vkey"
cp "$BUILD_DIR/$circuit/${circuit}_js/${circuit}.wasm" "$DEST_DIR/"
done

1
packages/relayer/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
**/dist

View File

@@ -0,0 +1,17 @@
FROM node:23 AS sdk-builder
WORKDIR /build
COPY ./package.json ./yarn.lock ./tsconfig.base.json .
COPY ./packages/sdk/package.json ./packages/sdk/tsconfig.build.json ./packages/sdk/
RUN yarn install
COPY ./packages/sdk/configs/ ./packages/sdk/configs/
COPY ./packages/sdk/src/ ./packages/sdk/src/
RUN cd ./packages/sdk && yarn install && yarn build
FROM sdk-builder AS final
COPY ./packages/relayer/package.json ./packages/relayer/tsconfig.build.json ./packages/relayer/
RUN yarn install
COPY ./packages/relayer/src/ ./packages/relayer/src/
RUN cd ./packages/relayer && yarn install && yarn build
WORKDIR packages/relayer
EXPOSE 3000:3000
ENTRYPOINT ["node", "./dist/src/index.js"]

View File

@@ -0,0 +1,24 @@
services:
relayer:
network_mode: host
user: 1000:1000
build:
context: ../../
dockerfile: packages/relayer/Dockerfile
environment:
FEE_RECEIVER_ADDRESS: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
PROVIDER_URL: http://0.0.0.0:8545
FEE_BPS: 1000
SIGNER_PRIVATE_KEY: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
SQLITE_DB_PATH: /tmp/pp_relayer.sqlite
CONFIG_PATH: ./withdraw_amounts.example.json
ENTRYPOINT_ADDRESS: 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
POOL_ADDRESS: 0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6
CHAIN: localhost
CHAIN_ID: 31337
volumes:
- /tmp/pp_relayer.sqlite:/pp_relayer.sqlite
- ./withdraw_amounts.example.json:/build/packages/relayer/withdraw_amounts.example.json
- ../circuits/artifacts:/build/node_modules/@privacy-pool-core/sdk/dist/node/artifacts
ports:
- "3000:3000" # HOST:CONTAINER

View File

@@ -0,0 +1,11 @@
# env example using anvil setup
export FEE_RECEIVER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
export PROVIDER_URL=http://127.0.0.1:8545
export FEE_BPS=1000
export SIGNER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
export SQLITE_DB_PATH=/tmp/pp_relayer.sqlite
export CONFIG_PATH=./withdraw_amounts.example.json
export ENTRYPOINT_ADDRESS=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
export POOL_ADDRESS=0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6
export CHAIN=localhost
export CHAIN_ID=31337

View File

@@ -31,7 +31,9 @@
"test:cov": "vitest run --config vitest.config.ts --coverage"
},
"dependencies": {
"@privacy-pool-core/sdk": "0.1.0",
"ajv": "8.17.1",
"body-parser": "1.20.3",
"express": "4.21.2",
"sqlite": "5.1.1",
"sqlite3": "5.1.7",

View File

@@ -17,10 +17,13 @@ const parseJsonMiddleware = bodyParser.json();
app.use(parseJsonMiddleware);
app.use(marshalResponseMiddleware);
// ping route
app.use("/ping", (req: Request, res: Response, next: NextFunction) => {
res.send("pong");
next();
});
// relayer route
app.use("/relayer", relayerRouter);
// Error and 404 handling

View File

@@ -1,14 +1,19 @@
import path from "node:path";
import fs from "node:fs";
import { getAddress } from "viem";
import { Address, Chain, defineChain, getAddress, Hex, isHex } from "viem";
import { ConfigError } from "./exceptions/base.exception.js";
import { localhost, mainnet, sepolia } from "viem/chains";
const enum ConfigEnv {
CONFIG_PATH = "CONFIG_PATH",
FEE_RECEIVER_ADDRESS = "FEE_RECEIVER_ADDRESS",
ENTRYPOINT_ADDRESS = "ENTRYPOINT_ADDRESS",
PROVIDER_URL = "PROVIDER_URL",
SIGNER_PRIVATE_KEY = "SIGNER_PRIVATE_KEY",
FEE_BPS = "FEE_BPS",
SQLITE_DB_PATH = "SQLITE_DB_PATH",
CHAIN = "CHAIN",
CHAIN_ID = "CHAIN_ID",
}
type ConfigEnvString = `${ConfigEnv}`;
@@ -31,17 +36,36 @@ function checkConfigVar(
try {
checker(varNameValue);
} catch (error) {
console.error(error);
throw ConfigError.default({
context: `Environment variable \`${varName}\` has an incorrect format`,
});
if (error instanceof ConfigError) {
throw error;
} else {
throw ConfigError.default({
context: `Environment variable \`${varName}\` has an incorrect format`,
});
}
}
}
return varNameValue;
}
function getFeeReceiverAddress(): string {
return checkConfigVar(ConfigEnv.FEE_RECEIVER_ADDRESS, (v) => getAddress(v));
function checkHex(v: string) {
if (!isHex(v, { strict: true })) {
throw ConfigError.default({
context: `String ${v} is not a properly formatted hex string`,
});
}
}
function getFeeReceiverAddress(): Address {
return getAddress(
checkConfigVar(ConfigEnv.FEE_RECEIVER_ADDRESS, (v) => getAddress(v)),
);
}
function getEntrypointAddress(): Address {
return getAddress(
checkConfigVar(ConfigEnv.ENTRYPOINT_ADDRESS, (v) => getAddress(v)),
);
}
function getProviderURL() {
@@ -51,12 +75,17 @@ function getProviderURL() {
function getSignerPrivateKey() {
// TODO: check pk format
return checkConfigVar(ConfigEnv.SIGNER_PRIVATE_KEY);
return checkConfigVar(ConfigEnv.SIGNER_PRIVATE_KEY, checkHex) as Hex;
}
function getFeeBps() {
// TODO: check feeBPS format
return checkConfigVar(ConfigEnv.FEE_BPS);
const feeBps = BigInt(checkConfigVar(ConfigEnv.FEE_BPS));
// range validation
if (feeBps > 10_000n || feeBps < 0) {
throw ConfigError.feeBpsOutOfBounds();
}
return feeBps;
}
function getSqliteDbPath() {
@@ -69,8 +98,55 @@ function getSqliteDbPath() {
});
}
function getMinWithdrawAmounts(): Record<string, bigint> {
const envVar = checkConfigVar(ConfigEnv.CONFIG_PATH, (v) => {
const configPath = path.resolve(v);
if (!fs.existsSync(v)) {
throw ConfigError.default({
context: `${configPath} does not exist.`,
});
}
});
const withdrawAmountsRaw = JSON.parse(
fs.readFileSync(path.resolve(envVar), { encoding: "utf-8" }),
);
const withdrawAmounts: Record<string, bigint> = {};
for (const entry of Object.entries(withdrawAmountsRaw)) {
const [asset, amount] = entry;
if (typeof amount === "string" || typeof amount === "number") {
withdrawAmounts[asset] = BigInt(amount);
} else {
console.error(`Unable to parse asset ${asset} with value ${amount}`);
}
}
return withdrawAmounts;
}
function getChainConfig(): Chain {
const chainName = checkConfigVar(ConfigEnv.CHAIN);
const chainId = process.env[ConfigEnv.CHAIN_ID];
return ((chainNameValue) => {
switch (chainNameValue) {
case "localhost":
if (chainId) {
return defineChain({ ...localhost, id: Number(chainId) });
}
return localhost;
case "sepolia":
return sepolia;
case "mainnet":
return mainnet;
default:
throw ConfigError.chainNotSupported();
}
})(chainName);
}
export const FEE_RECEIVER_ADDRESS = getFeeReceiverAddress();
export const ENTRYPOINT_ADDRESS = getEntrypointAddress();
export const PROVIDER_URL = getProviderURL();
export const SIGNER_PRIVATE_KEY = getSignerPrivateKey();
export const FEE_BPS = getFeeBps();
export const SQLITE_DB_PATH = getSqliteDbPath();
export const WITHDRAW_AMOUNTS = getMinWithdrawAmounts();
export const CHAIN = getChainConfig();

View File

@@ -6,14 +6,27 @@ export enum ErrorCode {
UNKNOWN = "UNKNOWN",
INVALID_INPUT = "INVALID_INPUT",
// Withdrawal data assertions
INVALID_DATA = "INVALID_DATA",
INVALID_ABI = "INVALID_ABI",
RECEIVER_MISMATCH = "RECEIVER_MISMATCH",
FEE_MISMATCH = "FEE_MISMATCH",
CONTEXT_MISMATCH = "CONTEXT_MISMATCH",
INSUFFICIENT_WITHDRAWN_VALUE = "INSUFFICIENT_WITHDRAWN_VALUE",
// Config errors
INVALID_CONFIG = "INVALID_CONFIG",
FEE_BPS_OUT_OF_BOUNDS = "FEE_BPS_OUT_OF_BOUNDS",
CHAIN_NOT_SUPPORTED = "CHAIN_NOT_SUPPORTED",
// Proof errors
INVALID_PROOF = "INVALID_PROOF",
// Contract errors
CONTRACT_ERROR = "CONTRACT_ERROR",
// SDK error. Wrapper for sdk's native errors
SDK_ERROR = "SDK_ERROR",
}
/**
@@ -24,7 +37,7 @@ export class RelayerError extends Error {
constructor(
message: string,
public readonly code: ErrorCode = ErrorCode.UNKNOWN,
public readonly details?: Record<string, unknown>,
public readonly details?: Record<string, unknown> | string,
) {
super(message);
this.name = this.constructor.name;
@@ -44,6 +57,10 @@ export class RelayerError extends Error {
details: this.details,
};
}
public static unknown(message?: string): RelayerError {
return new RelayerError(message || "", ErrorCode.UNKNOWN);
}
}
export class ValidationError extends RelayerError {
@@ -105,9 +122,95 @@ export class ConfigError extends RelayerError {
return new ConfigError("Invalid config", ErrorCode.INVALID_CONFIG, details);
}
public static unknown(message: string): ConfigError {
public static feeBpsOutOfBounds(
details?: Record<string, unknown>,
): ConfigError {
return new ConfigError(
"Invalid config: FEE_BPS must be in range [0, 10_000]",
ErrorCode.FEE_BPS_OUT_OF_BOUNDS,
details,
);
}
public static chainNotSupported(
details?: Record<string, unknown>,
): ConfigError {
return new ConfigError(
"Invalid config: CHAIN must be one of `localhost`, `sepolia`, `mainnet`",
ErrorCode.CHAIN_NOT_SUPPORTED,
details,
);
}
public static override unknown(message: string): ConfigError {
return new ConfigError("Invalid config", ErrorCode.INVALID_CONFIG, {
context: `Unknown error for ${message}`,
});
}
}
export class WithdrawalValidationError extends RelayerError {
constructor(
message: string,
code: ErrorCode = ErrorCode.INVALID_DATA,
details?: Record<string, unknown> | string,
) {
super(message, code, details);
this.name = this.constructor.name;
}
public static invalidWithdrawalAbi(
details?: Record<string, unknown>,
): WithdrawalValidationError {
return new WithdrawalValidationError(
"Failed to parse withdrawal data",
ErrorCode.INVALID_ABI,
details,
);
}
public static feeReceiverMismatch(
details: string,
): WithdrawalValidationError {
return new WithdrawalValidationError(
"Fee receiver does not match relayer",
ErrorCode.RECEIVER_MISMATCH,
details,
);
}
public static feeMismatch(details: string) {
return new WithdrawalValidationError(
"Fee does not match relayer fee",
ErrorCode.FEE_MISMATCH,
details,
);
}
public static contextMismatch(details: string) {
return new WithdrawalValidationError(
"Context does not match public signal",
ErrorCode.CONTEXT_MISMATCH,
details,
);
}
public static withdrawnValueTooSmall(details: string) {
return new WithdrawalValidationError(
"Withdrawn value is too small",
ErrorCode.INSUFFICIENT_WITHDRAWN_VALUE,
details,
);
}
}
export class SdkError extends RelayerError {
constructor(message: string, details?: Record<string, unknown> | string) {
super(message, ErrorCode.SDK_ERROR, details);
this.name = this.constructor.name;
}
public static scopeDataError(error: Error) {
return new SdkError(`SdkError: SCOPE_DATA_ERROR ${error.message}`);
}
}

View File

@@ -1 +0,0 @@
// Add your external exports here

View File

@@ -1,24 +1,31 @@
import { Hash, WithdrawalPayload } from "@privacy-pool-core/sdk";
import { Hash } from "@privacy-pool-core/sdk";
import { NextFunction, Request, Response } from "express";
import { getAddress } from "viem";
import { ValidationError } from "../../exceptions/base.exception.js";
import {
RelayerResponse,
RelayRequestBody,
WithdrawalPayload,
} from "../../interfaces/relayer/request.js";
import { validateRelayRequestBody } from "../../schemes/relayer/request.scheme.js";
import { RequestMashall } from "../../types.js";
import { PrivacyPoolRelayer } from "../../services/index.js";
import { RequestMashall } from "../../types.js";
/**
* Converts a RelayRequestBody into a WithdrawalPayload.
*
* @param {RelayRequestBody} body - The relay request body containing proof and withdrawal details.
* @returns {WithdrawalPayload} - The formatted withdrawal payload.
*/
function relayRequestBodyToWithdrawalPayload(
body: RelayRequestBody,
): WithdrawalPayload {
const proof = { ...body.proof, protocol: "groth16", curve: "bn128" };
const publicSignals = body.publicSignals;
const withdrawal = {
procesooor: getAddress(body.withdrawal.procesooor),
processooor: getAddress(body.withdrawal.processooor),
scope: BigInt(body.withdrawal.scope) as Hash,
data: Uint8Array.from(Buffer.from(body.withdrawal.data, "hex")),
data: body.withdrawal.data as `0x{string}`,
};
const wp = {
proof: {
@@ -30,6 +37,13 @@ function relayRequestBodyToWithdrawalPayload(
return wp;
}
/**
* Parses and validates the withdrawal request body.
*
* @param {Request["body"]} body - The request body to parse.
* @returns {WithdrawalPayload} - The validated and formatted withdrawal payload.
* @throws {ValidationError} - If the input data is invalid.
*/
function parseWithdrawal(body: Request["body"]): WithdrawalPayload {
if (validateRelayRequestBody(body)) {
try {
@@ -46,6 +60,13 @@ function parseWithdrawal(body: Request["body"]): WithdrawalPayload {
}
}
/**
* Express route handler for relaying requests.
*
* @param {Request} req - The incoming HTTP request.
* @param {Response} res - The HTTP response object.
* @param {NextFunction} next - The next middleware function.
*/
export async function relayRequestHandler(
req: Request,
res: Response,

View File

@@ -1,4 +1,3 @@
export * from "./external.js";
import { app } from "./app.js";
import { db } from "./providers/db.provider.js";

View File

@@ -1,3 +1,8 @@
import { Withdrawal, WithdrawalProof } from "@privacy-pool-core/sdk";
/**
* Represents the proof payload for a relayer request.
*/
export interface ProofRelayerPayload {
pi_a: string[];
pi_b: string[][];
@@ -5,39 +10,83 @@ export interface ProofRelayerPayload {
}
/**
* Withdrawal
* Represents the public signals for a withdrawal operation.
*/
export interface WithdrawPublicSignals {
/** Hash of new commitment */
newCommitmentHash: bigint;
/** Hash of the existing commitment nullifier */
existingNullifierHash: bigint;
/** Withdrawn value */
withdrawnValue: bigint;
/** State root */
stateRoot: bigint;
/** Depth of the state tree */
stateTreeDepth: bigint;
/** ASP root */
ASPRoot: bigint;
/** Depth of the ASP tree */
ASPTreeDepth: bigint;
/** Context value */
context: bigint;
}
/**
* Represents the payload for a withdrawal relayer request.
*/
export interface WithdrawalRelayerPayload {
/**
* Relayer address (0xAdDrEsS)
*/
procesooor: string;
/**
* Relayer scope (bigint as string)
*/
/** Relayer address (0xAdDrEsS) */
processooor: string;
/** Relayer scope (bigint as string) */
scope: string;
/**
* Tx data (hex encoded)
*/
/** Transaction data (hex encoded) */
data: string;
}
/**
* Represents the request body for a relayer operation.
*/
export interface RelayRequestBody {
/** Withdrawal details */
withdrawal: WithdrawalRelayerPayload;
/** Public signals as string array */
publicSignals: string[];
/** Proof details */
proof: ProofRelayerPayload;
}
/**
* Complete withdrawal payload including proof and public signals.
*/
export interface WithdrawalPayload {
readonly proof: WithdrawalProof;
readonly withdrawal: Withdrawal;
}
/**
* Represents the response from a relayer operation.
*/
export interface RelayerResponse {
/** Indicates if the request was successful */
success: boolean;
/** Timestamp of the response */
timestamp: number;
/** Unique request identifier (UUID) */
requestId: string;
/** Optional transaction hash */
txHash?: string;
/** Optional error message */
error?: string;
}
/**
* Enum representing the possible statuses of a relayer request.
*/
export const enum RequestStatus {
BROADCASTED = "BROADCASTED",
FAILED = "FAILED",
/** Request has been received */
RECEIVED = "RECEIVED",
/** Request has been broadcasted */
BROADCASTED = "BROADCASTED",
/** Request has failed */
FAILED = "FAILED",
}

View File

@@ -1 +0,0 @@
// Add your internal exports here

View File

@@ -0,0 +1,64 @@
import { NextFunction, Request, Response } from "express";
import { RelayerError } from "../exceptions/base.exception.js";
import { RelayerMarshall } from "../types.js";
/**
* Middleware to attach a marshaller function to the response locals.
* This function formats the response data in a standardized way.
*
* @param {Request} _req - Express request object (unused).
* @param {Response} res - Express response object.
* @param {NextFunction} next - Express next function.
*/
export function marshalResponseMiddleware(
_req: Request,
res: Response,
next: NextFunction,
) {
res.locals.marshalResponse = (data: RelayerMarshall) => ({
...data.toJSON(),
});
next();
}
/**
* Middleware to handle errors and send appropriate responses.
*
* @param {Error} err - The error object.
* @param {Request} _req - Express request object (unused).
* @param {Response} res - Express response object.
* @param {NextFunction} next - Express next function.
*/
export function errorHandlerMiddleware(
err: Error,
_req: Request,
res: Response,
next: NextFunction,
) {
if (err instanceof RelayerError) {
// TODO: error handling based on RelayerError subtypes should be done by checking `err.name`
res.status(400).json({ error: err.toJSON() });
} else {
res.status(500).json({ error: "Internal Server Error" });
}
next();
}
/**
* Middleware to handle 404 (Not Found) responses.
* If no response has been sent, it returns a 404 error.
*
* @param {Request} _req - Express request object (unused).
* @param {Response} res - Express response object.
* @param {NextFunction} next - Express next function.
*/
export function notFoundMiddleware(
_req: Request,
res: Response,
next: NextFunction,
) {
if (!res.writableFinished) {
res.status(404).json({ error: "Route not found" });
}
next();
}

View File

@@ -1,41 +1,2 @@
import { NextFunction, Request, Response } from "express";
import { RelayerError } from "../exceptions/base.exception.js";
import { RelayerMarshall } from "../types.js";
export function marshalResponseMiddleware(
_req: Request,
res: Response,
next: NextFunction,
) {
res.locals.marshalResponse = (data: RelayerMarshall) => ({
success: true,
data: data.toJSON(),
});
next();
}
export function errorHandlerMiddleware(
err: Error,
_req: Request,
res: Response,
next: NextFunction,
) {
if (err instanceof RelayerError) {
// TODO: error handling based in RelayerError subtypes should be done by checking `err.name`
res.status(400).json({ error: err.toJSON() });
} else {
res.status(500).json({ error: "Internal Server Error" });
}
next();
}
export function notFoundMiddleware(
_req: Request,
res: Response,
next: NextFunction,
) {
if (!res.writableFinished) {
res.status(404).json({ error: "Route not found" });
}
next();
}
export * from "./base.js";
export * from "./relayer/request.js";

View File

@@ -1,24 +1,100 @@
import {
Circuits,
PrivacyPoolSDK,
WithdrawalPayload,
WithdrawalProof,
} from "@privacy-pool-core/sdk";
/**
* Provides an interface to interact with the Privacy Pool SDK.
*/
import {
calculateContext,
Circuits,
ContractInteractionsService,
PrivacyPoolSDK,
Withdrawal,
WithdrawalProof,
SDKError,
} from "@privacy-pool-core/sdk";
import { Address } from "viem";
import {
CHAIN,
ENTRYPOINT_ADDRESS,
PROVIDER_URL,
SIGNER_PRIVATE_KEY,
} from "../config.js";
import { WithdrawalPayload } from "../interfaces/relayer/request.js";
import { RelayerError, SdkError } from "../exceptions/base.exception.js";
/**
* Class representing the SDK provider for interacting with Privacy Pool SDK.
*/
export class SdkProvider {
/** Instance of the PrivacyPoolSDK. */
private sdk: PrivacyPoolSDK;
private contracts: ContractInteractionsService;
/**
* Initializes a new instance of the SDK provider.
*/
constructor() {
this.sdk = new PrivacyPoolSDK(new Circuits());
this.contracts = this.sdk.createContractInstance(
PROVIDER_URL,
CHAIN,
ENTRYPOINT_ADDRESS,
SIGNER_PRIVATE_KEY,
);
}
async verifyWithdrawal(withdrawalPayload: WithdrawalProof) {
/**
* Verifies a withdrawal proof.
*
* @param {WithdrawalProof} withdrawalPayload - The withdrawal proof payload.
* @returns {Promise<boolean>} - A promise resolving to a boolean indicating verification success.
*/
async verifyWithdrawal(withdrawalPayload: WithdrawalProof): Promise<boolean> {
return await this.sdk.verifyWithdrawal(withdrawalPayload);
}
/**
* Broadcasts a withdrawal transaction.
*
* @param {WithdrawalPayload} withdrawalPayload - The withdrawal payload.
* @returns {Promise<{ hash: string }>} - A promise resolving to an object containing the transaction hash.
*/
async broadcastWithdrawal(
_withdrawalPayload: WithdrawalPayload, // eslint-disable-line @typescript-eslint/no-unused-vars
_brodacastDetails: { privateKey: string; rpcUrl: string }, // eslint-disable-line @typescript-eslint/no-unused-vars
withdrawalPayload: WithdrawalPayload,
): Promise<{ hash: string }> {
return {
hash: "0x",
};
return this.contracts.relay(
withdrawalPayload.withdrawal,
withdrawalPayload.proof,
);
}
/**
* Calculates the context for a withdrawal.
*
* @param {Withdrawal} withdrawal - The withdrawal object.
* @returns {string} - The calculated context.
*/
calculateContext(withdrawal: Withdrawal): string {
return calculateContext(withdrawal);
}
/**
* Converts a scope value to an asset address.
*
* @param {bigint} scope - The scope value.
* @returns {Promise<{ poolAddress: Address; assetAddress: Address; }>} - A promise resolving to the asset address.
*/
async scopeData(
scope: bigint,
): Promise<{ poolAddress: Address; assetAddress: Address }> {
try {
const data = await this.contracts.getScopeData(scope);
return data;
} catch (error) {
if (error instanceof SDKError) {
throw SdkError.scopeDataError(error);
} else {
throw RelayerError.unknown(JSON.stringify(error));
}
}
}
}

View File

@@ -1,22 +1,28 @@
import sqlite3 from "sqlite3";
import { open, Database } from "sqlite";
import { WithdrawalPayload } from "@privacy-pool-core/sdk";
import path from "path";
import { SQLITE_DB_PATH } from "../config.js";
import { RelayerDatabase } from "../types/db.types.js";
import { RequestStatus } from "../interfaces/relayer/request.js";
import {
RequestStatus,
WithdrawalPayload,
} from "../interfaces/relayer/request.js";
function replacer(key: string, value: unknown) {
return typeof value === "bigint" ? { $bigint: value.toString() } : value;
}
// TODO
/**
* Class representing an SQLite database for managing relayer requests.
*/
export class SqliteDatabase implements RelayerDatabase {
/** Path to the SQLite database file. */
readonly dbPath: string;
/** Indicates whether the database has been initialized. */
private _initialized: boolean = false;
/** Database connection instance. */
private db!: Database<sqlite3.Database, sqlite3.Statement>;
/** SQL statement for creating the requests table. */
private createTableRequest = `
CREATE TABLE IF NOT EXISTS requests (
id UUID PRIMARY KEY,
@@ -28,15 +34,28 @@ CREATE TABLE IF NOT EXISTS requests (
);
`;
/**
* Initializes the database with the given path.
*/
constructor() {
this.dbPath = path.resolve(SQLITE_DB_PATH);
}
/**
* Getter for the database initialization status.
*
* @returns {boolean} - Whether the database is initialized.
*/
get initialized(): boolean {
return this._initialized;
}
async init() {
/**
* Initializes the database connection and creates necessary tables.
*
* @returns {Promise<void>} - A promise that resolves when initialization is complete.
*/
async init(): Promise<void> {
try {
this.db = await open({
driver: sqlite3.Database,
@@ -50,11 +69,19 @@ CREATE TABLE IF NOT EXISTS requests (
console.log("sqlite db initialized");
}
/**
* Inserts a new request record into the database.
*
* @param {string} requestId - Unique ID for the request.
* @param {number} timestamp - Timestamp of the request.
* @param {WithdrawalPayload} req - The withdrawal payload associated with the request.
* @returns {Promise<void>} - A promise that resolves when the request is stored.
*/
async createNewRequest(
requestId: string,
timestamp: number,
req: WithdrawalPayload,
) {
): Promise<void> {
const strigifiedPayload = JSON.stringify(req, replacer);
// Store initial request
await this.db.run(
@@ -66,7 +93,17 @@ CREATE TABLE IF NOT EXISTS requests (
);
}
async updateBroadcastedRequest(requestId: string, txHash: string) {
/**
* Updates a request record with broadcast status and transaction hash.
*
* @param {string} requestId - The ID of the request.
* @param {string} txHash - The transaction hash.
* @returns {Promise<void>} - A promise that resolves when the update is complete.
*/
async updateBroadcastedRequest(
requestId: string,
txHash: string,
): Promise<void> {
// Update database
await this.db.run(
`
@@ -78,7 +115,17 @@ CREATE TABLE IF NOT EXISTS requests (
);
}
async updateFailedRequest(requestId: string, errorMessage: string) {
/**
* Updates a request record with failed status and error message.
*
* @param {string} requestId - The ID of the request.
* @param {string} errorMessage - The error message.
* @returns {Promise<void>} - A promise that resolves when the update is complete.
*/
async updateFailedRequest(
requestId: string,
errorMessage: string,
): Promise<void> {
// Update database with error
await this.db.run(
`
@@ -90,3 +137,14 @@ CREATE TABLE IF NOT EXISTS requests (
);
}
}
/**
* Custom JSON replacer function to handle BigInt serialization.
*
* @param {string} key - The JSON key.
* @param {unknown} value - The JSON value.
* @returns {unknown} - The transformed value.
*/
function replacer(key: string, value: unknown) {
return typeof value === "bigint" ? { $bigint: value.toString() } : value;
}

View File

@@ -10,11 +10,11 @@ const relayRequestSchema: JSONSchemaType<RelayRequestBody> = {
withdrawal: {
type: "object",
properties: {
procesooor: { type: "string" },
processooor: { type: "string" },
scope: { type: "string" },
data: { type: "string" },
data: { type: "string", pattern: "0x[0-9a-fA-F]+" },
},
required: ["procesooor", "scope", "data"],
required: ["processooor", "scope", "data"],
},
publicSignals: {
type: "array",

View File

@@ -1,42 +1,70 @@
import { WithdrawalPayload } from "@privacy-pool-core/sdk";
import { PROVIDER_URL, SIGNER_PRIVATE_KEY } from "../config.js";
import { RelayerError, ZkError } from "../exceptions/base.exception.js";
import { RelayerResponse } from "../interfaces/relayer/request.js";
/**
* Handles withdrawal requests within the Privacy Pool relayer.
*/
import { getAddress } from "viem";
import {
FEE_BPS,
FEE_RECEIVER_ADDRESS,
PROVIDER_URL,
SIGNER_PRIVATE_KEY,
WITHDRAW_AMOUNTS,
} from "../config.js";
import {
RelayerError,
WithdrawalValidationError,
ZkError,
} from "../exceptions/base.exception.js";
import {
RelayerResponse,
WithdrawalPayload,
} from "../interfaces/relayer/request.js";
import { db, SdkProvider } from "../providers/index.js";
import { RelayerDatabase } from "../types/db.types.js";
import { decodeWithdrawalData, parseSignals } from "../utils.js";
/**
* Class representing the Privacy Pool Relayer, responsible for processing withdrawal requests.
*/
export class PrivacyPoolRelayer {
/** Database instance for storing and updating request states. */
private db: RelayerDatabase;
private sdkService: SdkProvider;
/** SDK provider for handling blockchain interactions. */
private sdkProvider: SdkProvider;
/** RPC URL for blockchain communication. */
private readonly rpcUrl: string = PROVIDER_URL;
/** Private key for signing transactions. */
private readonly privateKey: string = SIGNER_PRIVATE_KEY;
/**
* Initializes a new instance of the Privacy Pool Relayer.
*/
constructor() {
// Initialize database, provider, wallet, contract...
this.db = db;
this.sdkService = new SdkProvider();
this.sdkProvider = new SdkProvider();
}
/**
* Handles a withdrawal request.
*
* @param {WithdrawalPayload} req - The withdrawal request payload.
* @returns {Promise<RelayerResponse>} - A promise resolving to the relayer response.
*/
async handleRequest(req: WithdrawalPayload): Promise<RelayerResponse> {
const requestId = crypto.randomUUID();
const timestamp = Date.now();
try {
// Store initial request
this.db.createNewRequest(requestId, timestamp, req);
await this.validateWithdrawal(req);
// Verify commitment proof
const isValidWithdrawal = await this.verifyProof(req.proof);
if (!isValidWithdrawal) {
const isValidWithdrawalProof = await this.verifyProof(req.proof);
if (!isValidWithdrawalProof) {
throw ZkError.invalidProof();
}
const response = await this.broadcastWithdrawal(req);
// Update database
await this.db.updateBroadcastedRequest(requestId, response.hash);
// Return success response
return {
success: true,
txHash: response.hash,
@@ -44,16 +72,9 @@ export class PrivacyPoolRelayer {
requestId,
};
} catch (error) {
let message: string;
if (error instanceof RelayerError) {
message = error.message;
} else {
message = JSON.stringify(error);
}
// Update database with error
const message: string =
error instanceof RelayerError ? error.message : JSON.stringify(error);
await this.db.updateFailedRequest(requestId, message);
// Return error response
return {
success: false,
error: message,
@@ -63,17 +84,74 @@ export class PrivacyPoolRelayer {
}
}
// Helper methods
/**
* Verifies a withdrawal proof.
*
* @param {WithdrawalPayload["proof"]} proof - The proof to be verified.
* @returns {Promise<boolean>} - A promise resolving to a boolean indicating verification success.
*/
private async verifyProof(
proof: WithdrawalPayload["proof"],
): Promise<boolean> {
return this.sdkService.verifyWithdrawal(proof);
return this.sdkProvider.verifyWithdrawal(proof);
}
private async broadcastWithdrawal(withdrawal: WithdrawalPayload) {
return this.sdkService.broadcastWithdrawal(withdrawal, {
privateKey: this.privateKey,
rpcUrl: this.rpcUrl,
});
/**
* Broadcasts a withdrawal transaction.
*
* @param {WithdrawalPayload} withdrawal - The withdrawal payload.
* @returns {Promise<{ hash: string }>} - A promise resolving to the transaction hash.
*/
private async broadcastWithdrawal(
withdrawal: WithdrawalPayload,
): Promise<{ hash: string }> {
return this.sdkProvider.broadcastWithdrawal(withdrawal);
}
/**
* Validates a withdrawal request against relayer rules.
*
* @param {WithdrawalPayload} wp - The withdrawal payload.
* @throws {WithdrawalValidationError} - If validation fails.
* @throws {ValidationError} - If public signals are malformed.
*/
private async validateWithdrawal(wp: WithdrawalPayload) {
const { feeRecipient, relayFeeBPS } = decodeWithdrawalData(
wp.withdrawal.data,
);
const proofSignals = parseSignals(wp.proof.publicSignals);
if (getAddress(feeRecipient) !== FEE_RECEIVER_ADDRESS) {
throw WithdrawalValidationError.feeReceiverMismatch(
`Fee recipient mismatch: expected "${FEE_RECEIVER_ADDRESS}", got "${feeRecipient}".`,
);
}
if (relayFeeBPS !== FEE_BPS) {
throw WithdrawalValidationError.feeMismatch(
`Relay fee mismatch: expected "${FEE_BPS}", got "${relayFeeBPS}".`,
);
}
const withdrawalContext = BigInt(
this.sdkProvider.calculateContext(wp.withdrawal),
);
if (proofSignals.context !== withdrawalContext) {
throw WithdrawalValidationError.contextMismatch(
`Context mismatch: expected "${withdrawalContext.toString(16)}", got "${proofSignals.context.toString(16)}".`,
);
}
const { assetAddress } = await this.sdkProvider.scopeData(
wp.withdrawal.scope,
);
const MIN_WITHDRAW_DEFAULT = 100_000n;
const minWithdrawAmount =
WITHDRAW_AMOUNTS[assetAddress] ?? MIN_WITHDRAW_DEFAULT;
if (proofSignals.withdrawnValue < minWithdrawAmount) {
throw WithdrawalValidationError.withdrawnValueTooSmall(
`Withdrawn value too small: expected minimum "${minWithdrawAmount}", got "${proofSignals.withdrawnValue}".`,
);
}
}
}

View File

@@ -0,0 +1,11 @@
export const FeeDataAbi = [
{
name: "FeeData",
type: "tuple",
components: [
{ name: "recipient", type: "address" },
{ name: "feeRecipient", type: "address" },
{ name: "relayFeeBPS", type: "uint256" },
],
},
] as const;

View File

@@ -1,4 +1,4 @@
import { WithdrawalPayload } from "@privacy-pool-core/sdk";
import { WithdrawalPayload } from "../interfaces/relayer/request.js";
export interface RelayerDatabase {
initialized: boolean;

View File

@@ -0,0 +1,50 @@
import { decodeAbiParameters, DecodeAbiParametersErrorType } from "viem";
import {
ValidationError,
WithdrawalValidationError,
} from "./exceptions/base.exception.js";
import { FeeDataAbi } from "./types/abi.types.js";
import {
RelayRequestBody,
WithdrawPublicSignals,
} from "./interfaces/relayer/request.js";
export function decodeWithdrawalData(data: `0x${string}`) {
try {
const [{ recipient, feeRecipient, relayFeeBPS }] = decodeAbiParameters(
FeeDataAbi,
data,
);
return { recipient, feeRecipient, relayFeeBPS };
} catch (e) {
const error = e as DecodeAbiParametersErrorType;
throw WithdrawalValidationError.invalidWithdrawalAbi({
name: error.name,
message: error.message,
});
}
}
export function parseSignals(
signals: RelayRequestBody["publicSignals"],
): WithdrawPublicSignals {
const badSignals = signals
.map((x, i) => (x === undefined ? i : null))
.filter((i) => i !== null);
if (badSignals.length > 0) {
throw ValidationError.invalidInput({
details: `Signals ${badSignals.join(", ")} are undefined`,
});
}
/// XXX: beware this signal distribution is based on how the circuits were compiled with circomkit, first 2 are the public outputs, next are the public inputs
return {
newCommitmentHash: BigInt(signals[0]!), // Hash of new commitment
existingNullifierHash: BigInt(signals[1]!), // Hash of the existing commitment nullifier
withdrawnValue: BigInt(signals[2]!),
stateRoot: BigInt(signals[3]!),
stateTreeDepth: BigInt(signals[4]!),
ASPRoot: BigInt(signals[5]!),
ASPTreeDepth: BigInt(signals[6]!),
context: BigInt(signals[7]!),
};
}

View File

@@ -0,0 +1,19 @@
{
"name": "api-test",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"type": "module",
"dependencies": {
"viem": "2.22.14"
},
"scripts": {
"build": "tsc -p ./tsconfig.json",
"main": "node dist/main.js"
},
"devDependencies": {
"@privacy-pool-core/sdk": "0.1.0",
"@types/node": "^22.13.0",
"typescript": "5.5.4"
}
}

View File

@@ -0,0 +1,31 @@
export const ping = async () => {
let r = await fetch("http://localhost:3000/ping", {
method: "get",
});
console.log(JSON.stringify(await r.text(), null, 2));
};
export const details = async () => {
let r = await fetch("http://localhost:3000/relayer/details", {
method: "get",
});
console.log(JSON.stringify(await r.json(), null, 2));
};
export const notFound = async () => {
let r = await fetch("http://localhost:3000/HOLA", {
method: "get",
});
console.log(JSON.stringify(await r.json(), null, 2));
};
export const request = async (requestBody) => {
let r = await fetch("http://localhost:3000/relayer/request", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
console.log(JSON.stringify(await r.json(), null, 2));
};

View File

@@ -0,0 +1,171 @@
import {
bigintToHash,
calculateContext,
Circuits,
getCommitment,
hashPrecommitment,
LeanIMTMerkleProof,
PrivacyPoolSDK,
Secret,
Withdrawal,
WithdrawalProof,
WithdrawalProofInput,
} from "@privacy-pool-core/sdk";
import { Address, defineChain, Hex } from "viem";
import { localhost } from "viem/chains";
/*
TestToken deployed at: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
Withdrawal Verifier deployed at: 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9
Ragequit Verifier deployed at: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707
Entrypoint deployed at: 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
ETH Pool deployed at: 0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6
TST Pool deployed at: 0x610178dA211FEF7D417bC0e6FeD39F05609AD788
*/
const LOCAL_ANVIL_RPC = "http://127.0.0.1:8545";
const ENTRYPOINT_ADDRESS: Address =
"0xa513E6E4b8f2a923D98304ec87F64353C4D5C853";
// const PRIVACY_POOL_ADDRESS: Address = "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6";
const PRIVATE_KEY: Hex =
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
const anvilChain = defineChain({ ...localhost, id: 31337 });
const sdk = new PrivacyPoolSDK(new Circuits());
const contracts = sdk.createContractInstance(
LOCAL_ANVIL_RPC,
anvilChain,
ENTRYPOINT_ADDRESS,
PRIVATE_KEY,
);
export async function deposit() {
const existingValue = BigInt("5000000000000000000"); // 5 eth
const existingNullifier = BigInt("2827991637673173") as Secret;
const existingSecret = BigInt("7338940278733227") as Secret;
const precommitment = {
hash: hashPrecommitment(existingNullifier, existingSecret),
nullifier: existingNullifier,
secret: existingSecret,
};
return contracts.depositETH(existingValue, precommitment.hash);
}
export async function proveWithdrawal(w: Withdrawal): Promise<WithdrawalProof> {
try {
console.log("🚀 Initializing PrivacyPoolSDK...");
// **Retrieve On-Chain Scope**
console.log(
"🔹 Retrieved Scope from Withdrawal:",
`0x${w.scope.toString(16)}`,
);
// **Load Valid Input Values**
const withdrawnValue = BigInt("100000000000000000"); // 0.1 eth
const stateRoot = BigInt(
"11647068014638404411083963959916324311405860401109309104995569418439086324505",
);
const stateTreeDepth = BigInt("2");
const aspRoot = BigInt(
"17509119559942543382744731935952318540675152427220720285867932301410542597330",
);
const aspTreeDepth = BigInt("2");
const label = BigInt("2310129299332319");
// **Commitment Data**
const existingValue = BigInt("5000000000000000000");
const existingNullifier = BigInt("2827991637673173") as Secret;
const existingSecret = BigInt("7338940278733227") as Secret;
const newNullifier = BigInt("1800210687471587") as Secret;
const newSecret = BigInt("6593588285288381") as Secret;
console.log("🛠️ Generating commitments...");
const commitment = getCommitment(
existingValue,
label,
existingNullifier,
existingSecret,
);
// **State Merkle Proof**
const stateMerkleProof: LeanIMTMerkleProof = {
root: stateRoot,
leaf: commitment.hash,
index: 3,
siblings: [
BigInt("6398878698952029"),
BigInt(
"13585012987205807684735841540436202984635744455909835202346884556845854938903",
),
...Array(30).fill(BigInt(0)),
],
};
// **ASP Merkle Proof**
const aspMerkleProof: LeanIMTMerkleProof = {
root: aspRoot,
leaf: label,
index: 3,
siblings: [
BigInt("3189334085279373"),
BigInt(
"1131383056830993841196498111009024161908281953428245130508088856824218714105",
),
...Array(30).fill(BigInt(0)),
],
};
// console.log("✅ State Merkle Proof:", stateMerkleProof);
// console.log("✅ ASP Merkle Proof:", aspMerkleProof);
// **Correctly Compute Context Hash**
const computedContext = calculateContext(w);
console.log("🔹 Computed Context:", computedContext.toString());
// **Create Withdrawal Proof Input**
const proofInput: WithdrawalProofInput = {
context: BigInt(computedContext),
withdrawalAmount: withdrawnValue,
stateMerkleProof: stateMerkleProof,
aspMerkleProof: aspMerkleProof,
stateRoot: bigintToHash(stateRoot),
stateTreeDepth: stateTreeDepth,
aspRoot: bigintToHash(aspRoot),
aspTreeDepth: aspTreeDepth,
newSecret: newSecret,
newNullifier: newNullifier,
};
console.log("🚀 Generating withdrawal proof...");
const proofPayload: WithdrawalProof = await sdk.proveWithdrawal(
commitment,
proofInput,
);
return proofPayload;
// if (!proofPayload) {
// throw new Error("❌ Withdrawal proof generation failed: proofPayload is null or undefined");
// }
// console.log("✅ Proof Payload:", proofPayload);
// console.log("🚀 Sending withdrawal transaction...");
// const withdrawalTx = await sdk.getContractInteractions().withdraw(withdrawObj, proofPayload);
// console.log("✅ Withdrawal transaction sent:", withdrawalTx?.hash ?? "❌ No transaction hash returned");
// if (!withdrawalTx?.hash) {
// throw new Error("❌ Withdrawal transaction failed: No transaction hash returned.");
// }
// await withdrawalTx.wait();
// console.log("🎉 Withdrawal transaction confirmed!");
} catch (error) {
console.error("❌ **Error running testWithdraw script**:", error);
process.exit(1);
}
}

View File

@@ -0,0 +1,58 @@
import { encodeAbiParameters, getAddress, Hex } from "viem";
import { request } from "./api-test.js";
import { deposit, proveWithdrawal } from "./create-withdrawal.js";
import { Hash, Withdrawal } from "@privacy-pool-core/sdk";
const FeeDataAbi = [
{
name: "FeeData",
type: "tuple",
components: [
{ name: "recipient", type: "address" },
{ name: "feeRecipient", type: "address" },
{ name: "relayFeeBPS", type: "uint256" },
],
},
];
const recipient = getAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
const processooor = getAddress("0xa513E6E4b8f2a923D98304ec87F64353C4D5C853"); // when relaying processooor is the entrypoint
const FEE_RECEIVER_ADDRESS = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
async function prove(w: Withdrawal) {
return proveWithdrawal(w);
}
async function depositEth() {
const r = await deposit();
console.log(r.hash);
await r.wait();
console.log("done");
}
(async () => {
const scope = BigInt(
"0x0555c5fdc167f1f1519c1b21a690de24d9be5ff0bde19447a5f28958d9256e50",
) as Hash;
const data = encodeAbiParameters(FeeDataAbi, [
{
recipient,
feeRecipient: FEE_RECEIVER_ADDRESS,
relayFeeBPS: 1_000n,
},
]) as Hex;
const withdrawal = { processooor, scope, data };
await depositEth();
// prove
const { proof, publicSignals } = await prove(withdrawal);
const requestBody = {
withdrawal: { ...withdrawal, scope: withdrawal.scope.toString() },
publicSignals,
proof,
};
await request(requestBody);
})();

View File

@@ -0,0 +1,26 @@
/* Based on total-typescript no-dom app config */
/* https://github.com/total-typescript/tsconfig */
{
"compilerOptions": {
/* Base Options: */
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"verbatimModuleSyntax": false,
/* Strictness */
"strict": true,
"noUncheckedIndexedAccess": true,
"useUnknownInCatchVariables": true,
"forceConsistentCasingInFileNames": true,
"noImplicitOverride": true,
/* If transpiling with TypeScript: */
"module": "NodeNext",
"sourceMap": true,
/* If your code doesn't run in the DOM: */
"lib": ["es2022"]
}
}

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.base.json",
"include": ["**/*", ".*.js"],
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"outDir": "dist",
"lib": ["es2022"]
}
}

View File

@@ -0,0 +1,106 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@adraffy/ens-normalize@^1.10.1":
version "1.11.0"
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33"
integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==
"@noble/curves@1.8.1", "@noble/curves@^1.6.0", "@noble/curves@~1.8.1":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.1.tgz#19bc3970e205c99e4bdb1c64a4785706bce497ff"
integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==
dependencies:
"@noble/hashes" "1.7.1"
"@noble/hashes@1.7.1", "@noble/hashes@^1.5.0", "@noble/hashes@~1.7.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f"
integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==
"@scure/base@~1.2.2", "@scure/base@~1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9"
integrity sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==
"@scure/bip32@1.6.2", "@scure/bip32@^1.5.0":
version "1.6.2"
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.2.tgz#093caa94961619927659ed0e711a6e4bf35bffd0"
integrity sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==
dependencies:
"@noble/curves" "~1.8.1"
"@noble/hashes" "~1.7.1"
"@scure/base" "~1.2.2"
"@scure/bip39@1.5.4", "@scure/bip39@^1.4.0":
version "1.5.4"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.4.tgz#07fd920423aa671be4540d59bdd344cc1461db51"
integrity sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==
dependencies:
"@noble/hashes" "~1.7.1"
"@scure/base" "~1.2.4"
"@types/node@^22.13.0":
version "22.13.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.0.tgz#d376dd9a0ee2f9382d86c2d5d7beb4d198b4ea8c"
integrity sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==
dependencies:
undici-types "~6.20.0"
abitype@1.0.8, abitype@^1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba"
integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==
eventemitter3@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
isows@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7"
integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==
ox@0.6.7:
version "0.6.7"
resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.7.tgz#afd53f2ecef68b8526660e9d29dee6e6b599a832"
integrity sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==
dependencies:
"@adraffy/ens-normalize" "^1.10.1"
"@noble/curves" "^1.6.0"
"@noble/hashes" "^1.5.0"
"@scure/bip32" "^1.5.0"
"@scure/bip39" "^1.4.0"
abitype "^1.0.6"
eventemitter3 "5.0.1"
typescript@5.5.4:
version "5.5.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba"
integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
undici-types@~6.20.0:
version "6.20.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
viem@2.22.14:
version "2.22.14"
resolved "https://registry.yarnpkg.com/viem/-/viem-2.22.14.tgz#acf1367704b29f0c00a58260702c9bee05c5dcbd"
integrity sha512-HfWnMmSPHNY+F3+I01ZKvIbwdn8qZUEhV/rzBi094F6gmqHA1KBXdF7P9po5/OYkvBjzxduUlLBgownyZkV+uA==
dependencies:
"@noble/curves" "1.8.1"
"@noble/hashes" "1.7.1"
"@scure/bip32" "1.6.2"
"@scure/bip39" "1.5.4"
abitype "1.0.8"
isows "1.0.6"
ox "0.6.7"
ws "8.18.0"
ws@8.18.0:
version "8.18.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==

View File

@@ -0,0 +1,3 @@
{
"0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9": 100
}

View File

@@ -1,757 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@adraffy/ens-normalize@1.10.1":
version "1.10.1"
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069"
integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==
"@adraffy/ens-normalize@^1.10.1":
version "1.11.0"
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33"
integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==
"@noble/curves@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35"
integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==
dependencies:
"@noble/hashes" "1.3.2"
"@noble/curves@1.8.1", "@noble/curves@^1.6.0", "@noble/curves@~1.8.1":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.1.tgz#19bc3970e205c99e4bdb1c64a4785706bce497ff"
integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==
dependencies:
"@noble/hashes" "1.7.1"
"@noble/hashes@1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==
"@noble/hashes@1.7.1", "@noble/hashes@^1.5.0", "@noble/hashes@~1.7.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f"
integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==
"@scure/base@~1.2.2", "@scure/base@~1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9"
integrity sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==
"@scure/bip32@1.6.2", "@scure/bip32@^1.5.0":
version "1.6.2"
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.2.tgz#093caa94961619927659ed0e711a6e4bf35bffd0"
integrity sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==
dependencies:
"@noble/curves" "~1.8.1"
"@noble/hashes" "~1.7.1"
"@scure/base" "~1.2.2"
"@scure/bip39@1.5.4", "@scure/bip39@^1.4.0":
version "1.5.4"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.4.tgz#07fd920423aa671be4540d59bdd344cc1461db51"
integrity sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==
dependencies:
"@noble/hashes" "~1.7.1"
"@scure/base" "~1.2.4"
"@types/body-parser@*":
version "1.19.5"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4"
integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==
dependencies:
"@types/connect" "*"
"@types/node" "*"
"@types/connect@*":
version "3.4.38"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858"
integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
dependencies:
"@types/node" "*"
"@types/express-serve-static-core@^5.0.0":
version "5.0.5"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.5.tgz#f6a851c7fd512e5da087f6f20d29f44b162a6a95"
integrity sha512-GLZPrd9ckqEBFMcVM/qRFAP0Hg3qiVEojgEFsx/N/zKXsBzbGF6z5FBDpZ0+Xhp1xr+qRZYjfGr1cWHB9oFHSA==
dependencies:
"@types/node" "*"
"@types/qs" "*"
"@types/range-parser" "*"
"@types/send" "*"
"@types/express@5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c"
integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==
dependencies:
"@types/body-parser" "*"
"@types/express-serve-static-core" "^5.0.0"
"@types/qs" "*"
"@types/serve-static" "*"
"@types/http-errors@*":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
"@types/mime@^1":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==
"@types/node@*":
version "22.10.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.10.tgz#85fe89f8bf459dc57dfef1689bd5b52ad1af07e6"
integrity sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==
dependencies:
undici-types "~6.20.0"
"@types/node@22.7.5":
version "22.7.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b"
integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==
dependencies:
undici-types "~6.19.2"
"@types/qs@*":
version "6.9.18"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.18.tgz#877292caa91f7c1b213032b34626505b746624c2"
integrity sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==
"@types/range-parser@*":
version "1.2.7"
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
"@types/send@*":
version "0.17.4"
resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==
dependencies:
"@types/mime" "^1"
"@types/node" "*"
"@types/serve-static@*":
version "1.15.7"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714"
integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==
dependencies:
"@types/http-errors" "*"
"@types/node" "*"
"@types/send" "*"
abitype@1.0.8, abitype@^1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba"
integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==
accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
aes-js@4.0.0-beta.5:
version "4.0.0-beta.5"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873"
integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==
ajv@8.17.1:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
dependencies:
fast-deep-equal "^3.1.3"
fast-uri "^3.0.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
body-parser@1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind-apply-helpers@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840"
integrity sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
call-bound@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.3.tgz#41cfd032b593e39176a71533ab4f384aa04fd681"
integrity sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==
dependencies:
call-bind-apply-helpers "^1.0.1"
get-intrinsic "^1.2.6"
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
cookie@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-errors "^1.3.0"
gopd "^1.2.0"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
encodeurl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-object-atoms@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
dependencies:
es-errors "^1.3.0"
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
ethers@6.13.5:
version "6.13.5"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4"
integrity sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ==
dependencies:
"@adraffy/ens-normalize" "1.10.1"
"@noble/curves" "1.2.0"
"@noble/hashes" "1.3.2"
"@types/node" "22.7.5"
aes-js "4.0.0-beta.5"
tslib "2.7.0"
ws "8.17.1"
eventemitter3@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
express@4.21.2:
version "4.21.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32"
integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.7.1"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.3.1"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.12"
proxy-addr "~2.0.7"
qs "6.13.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.19.0"
serve-static "1.16.2"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-uri@^3.0.1:
version "3.0.6"
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748"
integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==
finalhandler@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==
dependencies:
debug "2.6.9"
encodeurl "~2.0.0"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
get-intrinsic@^1.2.5, get-intrinsic@^1.2.6:
version "1.2.7"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.7.tgz#dcfcb33d3272e15f445d15124bc0a216189b9044"
integrity sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-define-property "^1.0.1"
es-errors "^1.3.0"
es-object-atoms "^1.0.0"
function-bind "^1.1.2"
get-proto "^1.0.0"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
math-intrinsics "^1.1.0"
get-proto@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
isows@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7"
integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
merge-descriptors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
object-inspect@^1.13.3:
version "1.13.3"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a"
integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
ox@0.6.7:
version "0.6.7"
resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.7.tgz#afd53f2ecef68b8526660e9d29dee6e6b599a832"
integrity sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==
dependencies:
"@adraffy/ens-normalize" "^1.10.1"
"@noble/curves" "^1.6.0"
"@noble/hashes" "^1.5.0"
"@scure/bip32" "^1.5.0"
"@scure/bip39" "^1.4.0"
abitype "^1.0.6"
eventemitter3 "5.0.1"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7"
integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
qs@6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
dependencies:
side-channel "^1.0.6"
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
safe-buffer@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
send@0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serve-static@1.16.2:
version "1.16.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296"
integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==
dependencies:
encodeurl "~2.0.0"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.19.0"
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel-list@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad"
integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.3"
side-channel-map@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-weakmap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-map "^1.0.1"
side-channel@^1.0.6:
version "1.1.0"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9"
integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.3"
side-channel-list "^1.0.0"
side-channel-map "^1.0.1"
side-channel-weakmap "^1.0.2"
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
tslib@2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
undici-types@~6.19.2:
version "6.19.8"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
undici-types@~6.20.0:
version "6.20.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
viem@2.22.14:
version "2.22.14"
resolved "https://registry.yarnpkg.com/viem/-/viem-2.22.14.tgz#acf1367704b29f0c00a58260702c9bee05c5dcbd"
integrity sha512-HfWnMmSPHNY+F3+I01ZKvIbwdn8qZUEhV/rzBi094F6gmqHA1KBXdF7P9po5/OYkvBjzxduUlLBgownyZkV+uA==
dependencies:
"@noble/curves" "1.8.1"
"@noble/hashes" "1.7.1"
"@scure/bip32" "1.6.2"
"@scure/bip39" "1.5.4"
abitype "1.0.8"
isows "1.0.6"
ox "0.6.7"
ws "8.18.0"
ws@8.17.1:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
ws@8.18.0:
version "8.18.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==

View File

@@ -12,7 +12,8 @@ const outDirBrowser = path.join(rootOutDir, "esm");
const typescriptConfig = {
tsconfig: path.resolve(`./tsconfig.build.json`),
exclude: ["**/*spec.ts"],
include: ["src/**/*.ts", "src/**/*.js"],
exclude: ["**/*spec.ts", "__mocks__", "tests/*"],
outputToFilesystem: false,
}
@@ -79,7 +80,7 @@ export default [
output: [
{
dir: path.join(rootOutDir, "types"),
sourcemap: true,
sourcemap: false,
},
],
plugins: [
@@ -100,7 +101,7 @@ export default [
},
{
input: path.join(rootOutDir, "types", "src", "index.d.ts"),
input: path.join(rootOutDir, "types", "index.d.ts"),
output: [{ file: path.join(rootOutDir, "index.d.mts"), format: "esm" }],
plugins: [dts()],
}

View File

@@ -54,7 +54,7 @@
"maci-crypto": "2.5.0",
"snarkjs": "0.7.5",
"@types/snarkjs": "0.7.9",
"viem": "2.21.4"
"viem": "2.22.14"
},
"devDependencies": {
"@commitlint/config-conventional": "19.4.1",

View File

@@ -1,532 +0,0 @@
export const IPrivacyPoolSimpleABI = [
{
type: "function",
name: "ASSET",
inputs: [],
outputs: [
{
name: "_asset",
type: "address",
internalType: "address",
},
],
stateMutability: "view",
},
{
type: "function",
name: "ENTRYPOINT",
inputs: [],
outputs: [
{
name: "_entrypoint",
type: "address",
internalType: "contract IEntrypoint",
},
],
stateMutability: "view",
},
{
type: "function",
name: "RAGEQUIT_VERIFIER",
inputs: [],
outputs: [
{
name: "_verifier",
type: "address",
internalType: "contract IVerifier",
},
],
stateMutability: "view",
},
{
type: "function",
name: "ROOT_HISTORY_SIZE",
inputs: [],
outputs: [
{
name: "_size",
type: "uint32",
internalType: "uint32",
},
],
stateMutability: "view",
},
{
type: "function",
name: "SCOPE",
inputs: [],
outputs: [
{
name: "_scope",
type: "uint256",
internalType: "uint256",
},
],
stateMutability: "view",
},
{
type: "function",
name: "VERSION",
inputs: [],
outputs: [
{
name: "_version",
type: "string",
internalType: "string",
},
],
stateMutability: "view",
},
{
type: "function",
name: "WITHDRAWAL_VERIFIER",
inputs: [],
outputs: [
{
name: "_verifier",
type: "address",
internalType: "contract IVerifier",
},
],
stateMutability: "view",
},
{
type: "function",
name: "currentRootIndex",
inputs: [],
outputs: [
{
name: "_index",
type: "uint32",
internalType: "uint32",
},
],
stateMutability: "view",
},
{
type: "function",
name: "dead",
inputs: [],
outputs: [
{
name: "_dead",
type: "bool",
internalType: "bool",
},
],
stateMutability: "view",
},
{
type: "function",
name: "deposit",
inputs: [
{
name: "_depositor",
type: "address",
internalType: "address",
},
{
name: "_value",
type: "uint256",
internalType: "uint256",
},
{
name: "_precommitment",
type: "uint256",
internalType: "uint256",
},
],
outputs: [
{
name: "_commitment",
type: "uint256",
internalType: "uint256",
},
],
stateMutability: "payable",
},
{
type: "function",
name: "deposits",
inputs: [
{
name: "_label",
type: "uint256",
internalType: "uint256",
},
],
outputs: [
{
name: "_depositor",
type: "address",
internalType: "address",
},
{
name: "_amount",
type: "uint256",
internalType: "uint256",
},
{
name: "_whenRagequitteable",
type: "uint256",
internalType: "uint256",
},
],
stateMutability: "view",
},
{
type: "function",
name: "nonce",
inputs: [],
outputs: [
{
name: "_nonce",
type: "uint256",
internalType: "uint256",
},
],
stateMutability: "view",
},
{
type: "function",
name: "nullifierHashes",
inputs: [
{
name: "_nullifierHash",
type: "uint256",
internalType: "uint256",
},
],
outputs: [
{
name: "_spent",
type: "bool",
internalType: "bool",
},
],
stateMutability: "view",
},
{
type: "function",
name: "ragequit",
inputs: [
{
name: "_p",
type: "tuple",
internalType: "struct ProofLib.RagequitProof",
components: [
{
name: "pA",
type: "uint256[2]",
internalType: "uint256[2]",
},
{
name: "pB",
type: "uint256[2][2]",
internalType: "uint256[2][2]",
},
{
name: "pC",
type: "uint256[2]",
internalType: "uint256[2]",
},
{
name: "pubSignals",
type: "uint256[5]",
internalType: "uint256[5]",
},
],
},
],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "roots",
inputs: [
{
name: "_index",
type: "uint256",
internalType: "uint256",
},
],
outputs: [
{
name: "_root",
type: "uint256",
internalType: "uint256",
},
],
stateMutability: "view",
},
{
type: "function",
name: "windDown",
inputs: [],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "function",
name: "withdraw",
inputs: [
{
name: "_w",
type: "tuple",
internalType: "struct IPrivacyPool.Withdrawal",
components: [
{
name: "processooor",
type: "address",
internalType: "address",
},
{
name: "scope",
type: "uint256",
internalType: "uint256",
},
{
name: "data",
type: "bytes",
internalType: "bytes",
},
],
},
{
name: "_p",
type: "tuple",
internalType: "struct ProofLib.WithdrawProof",
components: [
{
name: "pA",
type: "uint256[2]",
internalType: "uint256[2]",
},
{
name: "pB",
type: "uint256[2][2]",
internalType: "uint256[2][2]",
},
{
name: "pC",
type: "uint256[2]",
internalType: "uint256[2]",
},
{
name: "pubSignals",
type: "uint256[8]",
internalType: "uint256[8]",
},
],
},
],
outputs: [],
stateMutability: "nonpayable",
},
{
type: "event",
name: "Deposited",
inputs: [
{
name: "_depositor",
type: "address",
indexed: true,
internalType: "address",
},
{
name: "_commitment",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_label",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_value",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_merkleRoot",
type: "uint256",
indexed: false,
internalType: "uint256",
},
],
anonymous: false,
},
{
type: "event",
name: "LeafInserted",
inputs: [
{
name: "_index",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_leaf",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_root",
type: "uint256",
indexed: false,
internalType: "uint256",
},
],
anonymous: false,
},
{
type: "event",
name: "PoolDied",
inputs: [],
anonymous: false,
},
{
type: "event",
name: "Ragequit",
inputs: [
{
name: "_ragequitter",
type: "address",
indexed: true,
internalType: "address",
},
{
name: "_commitment",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_label",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_value",
type: "uint256",
indexed: false,
internalType: "uint256",
},
],
anonymous: false,
},
{
type: "event",
name: "Withdrawn",
inputs: [
{
name: "_processooor",
type: "address",
indexed: true,
internalType: "address",
},
{
name: "_value",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_spentNullifier",
type: "uint256",
indexed: false,
internalType: "uint256",
},
{
name: "_newCommitment",
type: "uint256",
indexed: false,
internalType: "uint256",
},
],
anonymous: false,
},
{
type: "error",
name: "ContextMismatch",
inputs: [],
},
{
type: "error",
name: "FailedToSendETH",
inputs: [],
},
{
type: "error",
name: "IncorrectASPRoot",
inputs: [],
},
{
type: "error",
name: "InsufficientValue",
inputs: [],
},
{
type: "error",
name: "InvalidCommitment",
inputs: [],
},
{
type: "error",
name: "InvalidProcesooor",
inputs: [],
},
{
type: "error",
name: "InvalidProof",
inputs: [],
},
{
type: "error",
name: "NotYetRagequitteable",
inputs: [],
},
{
type: "error",
name: "NullifierAlreadySpent",
inputs: [],
},
{
type: "error",
name: "OnlyEntrypoint",
inputs: [],
},
{
type: "error",
name: "OnlyOriginalDepositor",
inputs: [],
},
{
type: "error",
name: "PoolIsDead",
inputs: [],
},
{
type: "error",
name: "ScopeMismatch",
inputs: [],
},
{
type: "error",
name: "UnknownStateRoot",
inputs: [],
},
{
type: "error",
name: "ZeroAddress",
inputs: [],
},
] as const;

View File

@@ -21,6 +21,7 @@ import { IPrivacyPoolABI } from "../abi/IPrivacyPool.js";
import { privateKeyToAccount } from "viem/accounts";
import { CommitmentProof } from "../types/commitment.js";
import { bigintToHex } from "../crypto.js";
import { ContractError } from "../errors/base.error.js";
export class ContractInteractionsService implements ContractInteractions {
private publicClient: PublicClient;
@@ -229,7 +230,7 @@ export class ContractInteractionsService implements ContractInteractions {
// if no pool throw error
if (!poolAddress || poolAddress === "0x0000000000000000000000000000000000000000") {
throw new Error(`No pool found for scope ${scope.toString()}`);
throw ContractError.scopeNotFound(scope);
}
// get asset adress from pool
@@ -242,6 +243,8 @@ export class ContractInteractionsService implements ContractInteractions {
return { poolAddress: getAddress(poolAddress as string), assetAddress: getAddress(assetAddress as string) };
} catch (error) {
if (error instanceof ContractError)
throw error;
console.error(`Error resolving scope ${scope.toString()}:`, error);
throw new Error(`Failed to resolve scope ${scope.toString()}: ${error instanceof Error ? error.message : "Unknown error"}`);
}

View File

@@ -87,38 +87,6 @@ export class WithdrawalService {
}
}
/**
* Calculates the context hash for a withdrawal.
*/
protected calculateContext(withdrawal: Withdrawal): string {
const hashString = keccak256(
encodeAbiParameters(
[
{
name: "withdrawal",
type: "tuple",
components: [
{ name: "processooor", type: "address" },
{ name: "scope", type: "uint256" },
{ name: "data", type: "bytes" },
],
},
{ name: "scope", type: "uint256" },
],
[
{
processooor: withdrawal.processooor,
scope: withdrawal.scope,
data: withdrawal.data,
},
withdrawal.scope,
],
),
);
const hashBigint = hexToBigInt(hashString) % SNARK_SCALAR_FIELD;
return numberToHex(hashBigint);
}
/**
* Prepares input signals for the withdrawal circuit.
*/

View File

@@ -7,7 +7,8 @@ import {
PrivacyPoolError,
} from "./exceptions/privacyPool.exception.js";
import { Commitment, Hash, Secret, Withdrawal } from "./types/index.js";
import { encodeAbiParameters, Hex, keccak256 } from "viem";
import { encodeAbiParameters, Hex, keccak256, numberToHex } from "viem";
import { SNARK_SCALAR_FIELD } from "./constants.js";
/**
* Validates that a bigint value is not zero
@@ -126,7 +127,7 @@ export function bigintToHex(num: bigint | string | undefined): Hex {
* Calculates the context hash for a withdrawal.
*/
export function calculateContext(withdrawal: Withdrawal): string {
return keccak256(
const hash = BigInt(keccak256(
encodeAbiParameters(
[
{
@@ -149,5 +150,6 @@ export function calculateContext(withdrawal: Withdrawal): string {
withdrawal.scope,
],
),
);
)) % SNARK_SCALAR_FIELD;
return numberToHex(hash);
}

View File

@@ -103,3 +103,18 @@ export class ProofError extends SDKError {
);
}
}
export class ContractError extends SDKError {
constructor(
message: string,
code: ErrorCode = ErrorCode.CONTRACT_ERROR,
details?: Record<string, unknown>,
) {
super(message, code);
this.name = "ContractError";
}
public static scopeNotFound(scope: bigint): ContractError {
return new ContractError(`No pool found for scope ${scope.toString()}`, ErrorCode.CONTRACT_ERROR);
}
}

View File

@@ -6,6 +6,8 @@ export { BlockchainProvider } from "./internal.js";
export { Circuits } from "./circuits/index.js";
export { ContractInteractionsService } from "./core/contracts.service.js";
// This file is for re-exporting external dependencies that need to be available to consumers
export type { LeanIMTMerkleProof } from "@zk-kit/lean-imt";
export type { Address } from "viem";

View File

@@ -1,2 +1 @@
export { binariesMock, CircuitsMock } from "./circuits.mock.js";
export { WithdrawalServiceMock } from "./withdrawal.service.mock.js";

View File

@@ -1,7 +0,0 @@
import { Withdrawal, WithdrawalService } from "../../src/index.js";
export class WithdrawalServiceMock extends WithdrawalService {
override calculateContext(withdrawal: Withdrawal): string {
return super.calculateContext(withdrawal);
}
}

View File

@@ -4,9 +4,14 @@ import {
hashPrecommitment,
getCommitment,
generateMerkleProof,
calculateContext,
} from "../../src/crypto.js";
import { poseidon } from "maci-crypto/build/ts/hashing.js";
import { Secret } from "../../src/types/commitment.js";
import { Hash, Secret } from "../../src/types/commitment.js";
import { getAddress, Hex, keccak256 } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { SNARK_SCALAR_FIELD } from "../../src/constants.js";
import { Withdrawal } from "../../src/index.js";
describe("Crypto Utilities", () => {
describe("generateSecrets", () => {
@@ -100,4 +105,27 @@ describe("Crypto Utilities", () => {
}).toThrow("Leaf not found in the leaves array.");
});
});
describe("calculateContext", () => {
it("calculates the context correctly", () => {
const withdrawal = {
processooor: getAddress("0xa513E6E4b8f2a923D98304ec87F64353C4D5C853"),
scope: BigInt("0x0555c5fdc167f1f1519c1b21a690de24d9be5ff0bde19447a5f28958d9256e50") as Hash,
data: "0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000c350" as Hex,
};
expect(calculateContext(withdrawal)).toStrictEqual("0x21a25ae329bcce0ede103b5f8279c83b61b647dfd2bc8cfac3836456011cf3b6");
})
it("calculates returns a scalar field bounded value", () => {
const withdrawal: Withdrawal = {
processooor: privateKeyToAccount(generatePrivateKey()).address,
scope: BigInt(keccak256(generatePrivateKey())) as Hash,
data: keccak256(generatePrivateKey()),
};
const result = calculateContext(withdrawal);
expect(BigInt(result) % SNARK_SCALAR_FIELD).toStrictEqual(BigInt(result));
})
})
});

View File

@@ -1,42 +0,0 @@
import { getAddress, hexToBytes, keccak256 } from "viem/utils";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { Hash, Withdrawal } from "../../src/types/index.js";
import { CircuitsMock } from "../mocks/index.js";
import { WithdrawalServiceMock } from "../mocks/withdrawal.service.mock.js";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { SNARK_SCALAR_FIELD } from "../../src/internal.js";
describe("WithdrawalService", () => {
let service: WithdrawalServiceMock;
let circuits: CircuitsMock;
beforeEach(() => {
circuits = new CircuitsMock();
service = new WithdrawalServiceMock(circuits);
});
afterEach(() => {
vi.clearAllMocks();
});
it("calculates the context correctly", () => {
const withdrawal: Withdrawal = {
processooor: getAddress("0x7359Ca02A152f8F466dc9A4cB3ad1ED62792F46A"),
scope: BigInt("12345") as Hash,
data: keccak256("0x123")
};
expect(service.calculateContext(withdrawal)).toStrictEqual(
"0x127096bf8b62b2dea071de6617ca1893e1194d06ca4fb3bd3987f27c5c7c7352",
);
});
it("calculates returns a scalar field bounded value", () => {
const withdrawal: Withdrawal = {
processooor: privateKeyToAccount(generatePrivateKey()).address,
scope: BigInt(keccak256(generatePrivateKey())) as Hash,
data: keccak256(generatePrivateKey()),
};
const result = service.calculateContext(withdrawal);
expect(BigInt(result) % SNARK_SCALAR_FIELD).toStrictEqual(BigInt(result));
});
});

View File

@@ -2,11 +2,6 @@
# yarn lockfile v1
"@adraffy/ens-normalize@1.10.0":
version "1.10.0"
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7"
integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==
"@adraffy/ens-normalize@1.10.1":
version "1.10.1"
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069"
@@ -682,13 +677,6 @@
dependencies:
"@noble/hashes" "1.3.2"
"@noble/curves@1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.0.tgz#f05771ef64da724997f69ee1261b2417a49522d6"
integrity sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==
dependencies:
"@noble/hashes" "1.4.0"
"@noble/curves@1.4.2", "@noble/curves@~1.4.0":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9"
@@ -696,7 +684,7 @@
dependencies:
"@noble/hashes" "1.4.0"
"@noble/curves@1.8.1", "@noble/curves@^1.4.0", "@noble/curves@^1.6.0", "@noble/curves@~1.8.1":
"@noble/curves@1.8.1", "@noble/curves@^1.6.0", "@noble/curves@~1.8.1":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.1.tgz#19bc3970e205c99e4bdb1c64a4785706bce497ff"
integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==
@@ -713,16 +701,11 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==
"@noble/hashes@1.7.1", "@noble/hashes@^1.4.0", "@noble/hashes@^1.5.0", "@noble/hashes@~1.7.1":
"@noble/hashes@1.7.1", "@noble/hashes@^1.5.0", "@noble/hashes@~1.7.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f"
integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==
"@noble/hashes@~1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0"
integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -1044,7 +1027,7 @@
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.0.tgz#64c15d791d9b07ced7e89f735b2bd420a41c2e8e"
integrity sha512-g2ASy1QwHP88y5KWvblUolJz9rN+i4ZOsYzkEwcNfaNooxNUXG+ON6F5xFo0NIItpHqxcdAyls05VXpBnludGw==
"@scure/base@~1.1.6", "@scure/base@~1.1.8":
"@scure/base@~1.1.6":
version "1.1.9"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1"
integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==
@@ -1080,14 +1063,6 @@
"@noble/hashes" "~1.4.0"
"@scure/base" "~1.1.6"
"@scure/bip39@1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.4.0.tgz#664d4f851564e2e1d4bffa0339f9546ea55960a6"
integrity sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==
dependencies:
"@noble/hashes" "~1.5.0"
"@scure/base" "~1.1.8"
"@scure/bip39@1.5.4", "@scure/bip39@^1.4.0":
version "1.5.4"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.4.tgz#07fd920423aa671be4540d59bdd344cc1461db51"
@@ -1561,11 +1536,6 @@ abitype@0.7.1:
resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745"
integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==
abitype@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.5.tgz#29d0daa3eea867ca90f7e4123144c1d1270774b6"
integrity sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==
abitype@1.0.8, abitype@^1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba"
@@ -3718,11 +3688,6 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
isows@1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.4.tgz#810cd0d90cc4995c26395d2aa4cfa4037ebdf061"
integrity sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==
isows@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7"
@@ -5902,21 +5867,6 @@ vary@~1.1.2:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
viem@2.21.4:
version "2.21.4"
resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.4.tgz#b413330f5e05d2ab9ed01bf6e6fe12bcb71788dd"
integrity sha512-4E61XWhErjuXh5ObEoosKSy4iMvYnkuQq9jGLW5Isod68dNrENnyNV0QlVpn0LB3qunJ4ZMFMhYdfTjETqe7cQ==
dependencies:
"@adraffy/ens-normalize" "1.10.0"
"@noble/curves" "1.4.0"
"@noble/hashes" "1.4.0"
"@scure/bip32" "1.4.0"
"@scure/bip39" "1.4.0"
abitype "1.0.5"
isows "1.0.4"
webauthn-p256 "0.0.5"
ws "8.17.1"
viem@2.22.14:
version "2.22.14"
resolved "https://registry.yarnpkg.com/viem/-/viem-2.22.14.tgz#acf1367704b29f0c00a58260702c9bee05c5dcbd"
@@ -6066,14 +6016,6 @@ web3-validator@^2.0.6:
web3-types "^1.6.0"
zod "^3.21.4"
webauthn-p256@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.5.tgz#0baebd2ba8a414b21cc09c0d40f9dd0be96a06bd"
integrity sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg==
dependencies:
"@noble/curves" "^1.4.0"
"@noble/hashes" "^1.4.0"
which-typed-array@^1.1.16, which-typed-array@^1.1.2:
version "1.1.18"
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.18.tgz#df2389ebf3fbb246a71390e90730a9edb6ce17ad"