Revert broken changes (#93)

This commit is contained in:
Patrick Aljord
2025-07-17 15:00:04 +02:00
committed by GitHub
parent 725c218057
commit 431fb1fd3d
52 changed files with 1135 additions and 6830 deletions

View File

@@ -11,4 +11,4 @@ COPY src/ ./src/
RUN yarn build
EXPOSE 3000
CMD ["node", "dist/index.js"]
CMD ["node", "dist/src/index.js"]

View File

@@ -6,8 +6,8 @@
"license": "Apache-2.0",
"author": "Wonderland",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"directories": {
"src": "src"
},
@@ -18,8 +18,7 @@
],
"scripts": {
"build": "tsc -p tsconfig.build.json",
"build:clean": "rm -r ./tsconfig.build.tsbuildinfo ./dist && tsc -p tsconfig.build.json",
"start": "node ./dist/index.js",
"start": "node ./dist/src/index.js",
"build:start": "yarn build && yarn start",
"start:ts": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/index.ts",
"check-types": "tsc --noEmit -p ./tsconfig.json",
@@ -35,11 +34,7 @@
},
"dependencies": {
"@0xbow/privacy-pools-core-sdk": "0.1.17",
"@ethersproject/bignumber": "5.8.0",
"@ethersproject/bytes": "5.8.0",
"@uniswap/sdk-core": "7.7.2",
"@uniswap/universal-router-sdk": "4.19.5",
"@uniswap/v3-sdk": "3.25.2",
"@uniswap/v3-sdk": "^3.25.2",
"ajv": "8.17.1",
"body-parser": "1.20.3",
"cors": "^2.8.5",

View File

@@ -7,7 +7,7 @@ import {
notFoundMiddleware,
} from "./middlewares/index.js";
import { relayerRouter } from "./routes/index.js";
import { CONFIG, CORS_ALLOW_ALL, ALLOWED_DOMAINS } from "./config/index.js";
import { CONFIG } from "./config/index.js";
// Initialize the express app
const app = express();
@@ -15,25 +15,16 @@ const app = express();
// Middleware functions
const parseJsonMiddleware = bodyParser.json();
// CORS config - allow all origins by default for development and testnet
const isTestnetRelayer = process.env.NODE_ENV === 'production' &&
(process.env.RELAYER_HOST === 'testnet-relayer.privacypools.com' ||
process.env.HOST === 'testnet-relayer.privacypools.com');
const shouldAllowAll = CORS_ALLOW_ALL || isTestnetRelayer;
// CORS config
const corsOptions = {
origin: shouldAllowAll ? '*' : function (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) {
// Allow requests without origin (like mobile apps) or from allowed domains
if (!origin || ALLOWED_DOMAINS.indexOf(origin) !== -1) {
origin: CONFIG.cors_allow_all ? '*' : function (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) {
if (!origin || CONFIG.allowed_domains.indexOf(origin) !== -1) {
callback(null, true);
} else {
console.log(`Request blocked by CORS middleware: ${origin}. Allowed domains: ${ALLOWED_DOMAINS}`);
console.log(`Request blocked by CORS middleware: ${origin}. Allowed domains: ${CONFIG.allowed_domains}`);
callback(new Error("Not allowed by CORS"));
}
},
credentials: true,
optionsSuccessStatus: 200
};

View File

@@ -1,8 +1,8 @@
import fs from "node:fs";
import path from "node:path";
import { ConfigError, RelayerError } from "../exceptions/base.exception.js";
import { ConfigError } from "../exceptions/base.exception.js";
import { zConfig } from "./schemas.js";
import { AssetConfig, ChainConfig } from "./types.js";
import { ChainConfig, AssetConfig } from "./types.js";
/**
* Reads the configuration file from the path specified in the CONFIG_PATH environment variable
@@ -110,26 +110,18 @@ export function getEntrypointAddress(chainId: number): string {
*
* @param {number} chainId - The chain ID
* @param {string} assetAddress - The asset address
* @returns {AssetConfig} The asset configuration, or undefined if not found
* @returns {AssetConfig | undefined} The asset configuration, or undefined if not found
*/
export function getAssetConfig(chainId: number, assetAddress: string): AssetConfig {
export function getAssetConfig(chainId: number, assetAddress: string): AssetConfig | undefined {
const chainConfig = getChainConfig(chainId);
if (!chainConfig.supported_assets) {
throw RelayerError.assetNotSupported();
return undefined;
}
const assetConfig = chainConfig.supported_assets.find(
return chainConfig.supported_assets.find(
asset => asset.asset_address.toLowerCase() === assetAddress.toLowerCase()
);
if (!assetConfig) {
throw RelayerError.assetNotSupported();
}
return assetConfig
}
// Re-export types
export * from "./types.js";
export * from "./types.js";

View File

@@ -64,8 +64,8 @@ export const zChainConfig = z.object({
// Common configuration schema
export const zCommonConfig = z.object({
sqlite_db_path: z.string().transform((p) => path.resolve(p)),
cors_allow_all: z.boolean().default(true),
allowed_domains: z.array(z.string().url()).default(["https://testnet.privacypools.com, https://prod-privacy-pool-ui.vercel.app, https://staging-privacy-pool-ui.vercel.app, https://dev-privacy-pool-ui.vercel.app, http://localhost:3000"]),
cors_allow_all: z.boolean().default(false),
allowed_domains: z.array(z.string().url()),
});
// Default configuration schema

View File

@@ -82,12 +82,6 @@ export class RelayerError extends Error {
public static unknown(message?: string): RelayerError {
return new RelayerError(message || "", ErrorCode.UNKNOWN);
}
public static assetNotSupported(
details?: Record<string, unknown> | string) {
return new RelayerError("Asset is not supported", ErrorCode.ASSET_NOT_SUPPORTED, details);
}
}
export class ValidationError extends RelayerError {
@@ -252,7 +246,7 @@ export class WithdrawalValidationError extends RelayerError {
);
}
public static override assetNotSupported(details: string) {
public static assetNotSupported(details: string) {
return new WithdrawalValidationError(
"Asset not supported on this chain",
ErrorCode.ASSET_NOT_SUPPORTED,
@@ -291,7 +285,7 @@ export class QuoterError extends RelayerError {
this.name = this.constructor.name;
}
public static override assetNotSupported(
public static assetNotSupported(
details?: Record<string, unknown> | string) {
return new QuoterError("Asset is not supported", ErrorCode.ASSET_NOT_SUPPORTED, details);
}

View File

@@ -1,12 +1,11 @@
import { NextFunction, Request, Response } from "express";
import { getAddress } from "viem";
import { getAssetConfig, getFeeReceiverAddress, getSignerPrivateKey } from "../../config/index.js";
import { getAssetConfig, getFeeReceiverAddress } from "../../config/index.js";
import { QuoterError } from "../../exceptions/base.exception.js";
import { web3Provider } from "../../providers/index.js";
import { quoteService } from "../../services/index.js";
import { QuoteMarshall } from "../../types.js";
import { encodeWithdrawalData, isFeeReceiverSameAsSigner, isNative } from "../../utils.js";
import { privateKeyToAccount } from "viem/accounts";
import { encodeWithdrawalData } from "../../utils.js";
const TIME_20_SECS = 20 * 1000;
@@ -18,27 +17,17 @@ export async function relayQuoteHandler(
const chainId = Number(req.body.chainId!);
const amountIn = BigInt(req.body.amount!.toString());
const assetAddress = getAddress(req.body.asset!.toString());
let extraGas = Boolean(req.body.extraGas);
const assetAddress = getAddress(req.body.asset!.toString())
const config = getAssetConfig(chainId, assetAddress);
if (config === undefined)
return next(QuoterError.assetNotSupported(`Asset ${assetAddress} for chain ${chainId} is not supported`));
if (isNative(assetAddress)) {
extraGas = false;
}
const feeBPS = await quoteService.quoteFeeBPSNative({
chainId, amountIn, assetAddress, baseFeeBPS: config.fee_bps, value: 0n
});
let feeBPS;
try {
feeBPS = await quoteService.quoteFeeBPSNative({
chainId, amountIn, assetAddress, baseFeeBPS: config.fee_bps, extraGas: extraGas
});
} catch (e) {
return next(e);
}
const recipient = req.body.recipient ? getAddress(req.body.recipient.toString()) : undefined;
const recipient = req.body.recipient ? getAddress(req.body.recipient.toString()) : undefined
const quoteResponse = new QuoteMarshall({
baseFeeBPS: config.fee_bps,
@@ -46,27 +35,16 @@ export async function relayQuoteHandler(
});
if (recipient) {
let feeReceiverAddress: `0x${string}`;
const finalFeeReceiverAddress = getAddress(getFeeReceiverAddress(chainId));
if (extraGas) {
const signer = privateKeyToAccount(getSignerPrivateKey(chainId) as `0x${string}`);
if (isFeeReceiverSameAsSigner(chainId)) {
feeReceiverAddress = finalFeeReceiverAddress;
} else {
feeReceiverAddress = signer.address;
}
} else {
feeReceiverAddress = finalFeeReceiverAddress;
}
const feeReceiverAddress = getFeeReceiverAddress(chainId);
const withdrawalData = encodeWithdrawalData({
feeRecipient: getAddress(feeReceiverAddress),
recipient,
relayFeeBPS: feeBPS
});
const expiration = Number(new Date()) + TIME_20_SECS;
const relayerCommitment = { withdrawalData, expiration, amount: amountIn, extraGas };
})
const expiration = Number(new Date()) + TIME_20_SECS
const relayerCommitment = { withdrawalData, expiration };
const signedRelayerCommitment = await web3Provider.signRelayerCommitment(chainId, relayerCommitment);
quoteResponse.addFeeCommitment({ expiration, withdrawalData, signedRelayerCommitment, extraGas, amount: amountIn });
quoteResponse.addFeeCommitment({ expiration, withdrawalData, signedRelayerCommitment })
}
res

View File

@@ -1,14 +1,16 @@
import { NextFunction, Request, Response } from "express";
import { CONFIG, getChainConfig } from "../../config/index.js";
import { getAddress } from "viem";
import { ConfigError, ValidationError } from "../../exceptions/base.exception.js";
import {
RelayerResponse,
RelayRequestBody,
WithdrawalPayload,
} from "../../interfaces/relayer/request.js";
import { web3Provider } from "../../providers/index.js";
import { zRelayRequest } from "../../schemes/relayer/request.scheme.js";
import { validateRelayRequestBody } from "../../schemes/relayer/request.scheme.js";
import { privacyPoolRelayer } from "../../services/index.js";
import { RequestMashall } from "../../types.js";
import { CONFIG, getChainConfig } from "../../config/index.js";
import { web3Provider } from "../../providers/index.js";
/**
* Converts a RelayRequestBody into a WithdrawalPayload.
@@ -17,19 +19,25 @@ import { RequestMashall } from "../../types.js";
* @returns {WithdrawalPayload} - The formatted withdrawal payload.
*/
function relayRequestBodyToWithdrawalPayload(
body: ReturnType<typeof zRelayRequest['parse']>,
body: RelayRequestBody,
): WithdrawalPayload {
return {
...body,
proof: {
proof: {
...body.proof,
protocol: "groth16",
curve: "bn128"
},
publicSignals: body.publicSignals,
},
const proof = { ...body.proof, protocol: "groth16", curve: "bn128" };
const publicSignals = body.publicSignals;
const scope = BigInt(body.scope);
const withdrawal = {
processooor: getAddress(body.withdrawal.processooor),
data: body.withdrawal.data as `0x{string}`,
};
const wp = {
proof: {
proof,
publicSignals,
},
withdrawal,
scope,
feeCommitment: body.feeCommitment
};
return wp;
}
/**
@@ -50,23 +58,36 @@ function isChainSupported(chainId: number): boolean {
* @throws {ValidationError} - If the input data is invalid.
* @throws {ConfigError} - If the chain is not supported.
*/
function parseWithdrawal(body: Request["body"]): { payload: WithdrawalPayload, chainId: number; } {
function parseWithdrawal(body: Request["body"]): { payload: WithdrawalPayload, chainId: number } {
if (validateRelayRequestBody(body)) {
try {
const payload = relayRequestBodyToWithdrawalPayload(body);
const chainId = typeof body.chainId === 'string' ? parseInt(body.chainId, 10) : body.chainId;
const { data, error, success } = zRelayRequest.safeParse(body);
if (isNaN(chainId)) {
throw ValidationError.invalidInput({ message: "Invalid chain ID format" });
}
if (!success) {
throw ValidationError.invalidInput({ error, message: "Error parsing payload" });
// Check if the chain is supported early
if (!isChainSupported(chainId)) {
throw ValidationError.invalidInput({ message: `Chain with ID ${chainId} not supported.` });
}
return { payload, chainId };
} catch (error) {
console.error(error);
// Re-throw ConfigError as is
if (error instanceof ConfigError) {
throw error;
}
// TODO: extend this catch to return more details about the issue (viem error, node error, etc)
throw ValidationError.invalidInput({
message: "Can't parse payload into SDK structure",
});
}
} else {
throw ValidationError.invalidInput({ message: "Payload format error" });
}
const payload = relayRequestBodyToWithdrawalPayload(data);
// Check if the chain is supported early
if (!isChainSupported(data.chainId)) {
throw ValidationError.invalidInput({ message: `Chain with ID ${data.chainId} not supported.` });
}
return { payload, chainId: data.chainId };
}
/**
@@ -83,12 +104,10 @@ export async function relayRequestHandler(
) {
try {
const { payload: withdrawalPayload, chainId } = parseWithdrawal(req.body);
const maxGasPrice = getChainConfig(chainId)?.max_gas_price;
const currentGasPrice = await web3Provider.getGasPrice(chainId);
if (maxGasPrice !== undefined && currentGasPrice > maxGasPrice) {
throw ConfigError.maxGasPrice(`Current gas price ${currentGasPrice} is higher than max price ${maxGasPrice}`);
throw ConfigError.maxGasPrice(`Current gas price ${currentGasPrice} is higher than max price ${maxGasPrice}`)
}
const requestResponse: RelayerResponse =

View File

@@ -5,8 +5,6 @@
export interface FeeCommitment {
expiration: number,
withdrawalData: `0x${string}`,
amount: bigint,
extraGas: boolean,
signedRelayerCommitment: `0x${string}`,
}

View File

@@ -1,3 +1,5 @@
import { FeeCommitment } from "./common.js";
export interface QuotetBody {
/** Chain ID to process the request on */
chainId: string | number;
@@ -7,18 +9,10 @@ export interface QuotetBody {
asset: string;
/** Asset address */
recipient?: string;
/** Extra gas flag */
extraGas: boolean;
}
export interface QuoteResponse {
baseFeeBPS: bigint,
feeBPS: bigint,
feeCommitment?: {
expiration: number,
withdrawalData: `0x${string}`,
amount: string,
extraGas: boolean,
signedRelayerCommitment: `0x${string}`,
}
feeCommitment?: FeeCommitment
}

View File

@@ -75,8 +75,6 @@ export interface RelayerResponse {
requestId: string;
/** Optional transaction hash */
txHash?: string;
/** Optional transaction swap hash */
txSwap?: string;
/** Optional error message */
error?: string;
}

View File

@@ -1,7 +1,7 @@
import { NextFunction, Request, Response } from "express";
import { ValidationError } from "../../exceptions/base.exception.js";
import { validateDetailsQuerystring } from "../../schemes/relayer/details.scheme.js";
import { zRelayRequest } from "../../schemes/relayer/request.scheme.js";
import { validateRelayRequestBody } from "../../schemes/relayer/request.scheme.js";
import { validateQuoteBody } from "../../schemes/relayer/quote.scheme.js";
// Middleware to validate the details querying
@@ -26,9 +26,11 @@ export function validateRelayRequestMiddleware(
res: Response,
next: NextFunction,
) {
const { success, error } = zRelayRequest.safeParse(req.body);
if (!success) {
next(ValidationError.invalidInput({ message: error.errors.map(i => `${i.path.join('.')}: ${i.message}`).join("\n") }));
const isValid = validateRelayRequestBody(req.body);
if (!isValid) {
const messages: string[] = [];
validateRelayRequestBody.errors?.forEach(e => e?.message ? messages.push(e.message) : undefined);
next(ValidationError.invalidInput({ message: messages.join("\n") }));
return;
}
next();

View File

@@ -1,11 +1,11 @@
import { Web3Provider } from "./web3.provider.js";
import { UniswapProvider } from "./uniswap/uniswap.provider.js"
import { UniswapProvider } from "./uniswap.provider.js"
import { QuoteProvider } from "./quote.provider.js";
export { db } from "./db.provider.js";
export { SdkProvider } from "./sdk.provider.js";
export { SqliteDatabase } from "./sqlite.provider.js";
export { UniswapProvider } from "./uniswap/uniswap.provider.js"
export { UniswapProvider } from "./uniswap.provider.js"
export const web3Provider = new Web3Provider();
export const uniswapProvider = new UniswapProvider();

View File

@@ -0,0 +1,90 @@
import { Token } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { Address, getContract } from 'viem'
import { web3Provider } from '../providers/index.js'
import { BlockchainError, RelayerError } from '../exceptions/base.exception.js'
import { isViemError } from '../utils.js'
import { QUOTER_CONTRACT_ADDRESS, WRAPPED_NATIVE_TOKEN_ADDRESS } from './uniswap/constants.js'
import { IERC20MinimalABI } from './uniswap/erc20.abi.js'
import { QuoterV2ABI } from './uniswap/quoterV2.abi.js'
export type UniswapQuote = {
chainId: number;
addressIn: string;
addressOut: string;
amountIn: bigint;
};
type QuoteToken = { amount: bigint, decimals: number }
export type Quote = {
in: QuoteToken
out: QuoteToken
};
export class UniswapProvider {
async getTokenInfo(chainId: number, address: Address): Promise<Token> {
const contract = getContract({
address,
abi: IERC20MinimalABI.abi,
client: web3Provider.client(chainId)
});
const [decimals, symbol] = await Promise.all([
contract.read.decimals(),
contract.read.symbol(),
])
return new Token(chainId, address, Number(decimals), symbol);
}
async quoteNativeToken(chainId: number, addressIn: Address, amountIn: bigint): Promise<Quote> {
const addressOut = WRAPPED_NATIVE_TOKEN_ADDRESS[chainId.toString()]!
return this.quote({
chainId,
amountIn,
addressOut,
addressIn
});
}
async quote({ chainId, addressIn, addressOut, amountIn }: UniswapQuote) {
const tokenIn = await this.getTokenInfo(chainId, addressIn as Address);
const tokenOut = await this.getTokenInfo(chainId, addressOut as Address);
const quoterContract = getContract({
address: QUOTER_CONTRACT_ADDRESS[chainId.toString()]!,
abi: QuoterV2ABI.abi,
client: web3Provider.client(chainId)
});
try {
const quotedAmountOut = await quoterContract.simulate.quoteExactInputSingle([{
tokenIn: tokenIn.address as Address,
tokenOut: tokenOut.address as Address,
fee: FeeAmount.MEDIUM,
amountIn,
sqrtPriceLimitX96: 0n,
}])
// amount, sqrtPriceX96After, tickAfter, gasEstimate
const [amount, , , ] = quotedAmountOut.result;
return {
in: {
amount: amountIn, decimals: tokenIn.decimals
},
out: {
amount, decimals: tokenOut.decimals
}
};
} catch (error) {
if (error instanceof Error && isViemError(error)) {
const { metaMessages, shortMessage } = error;
throw BlockchainError.txError((metaMessages ? metaMessages[0] : undefined) || shortMessage)
} else {
throw RelayerError.unknown("Something went wrong while quoting")
}
}
}
}

View File

@@ -1,200 +0,0 @@
export const IERC20MinimalABI = [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
] as const;

View File

@@ -1,901 +0,0 @@
export const Permit2ABI = [
{
"inputs": [
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "AllowanceExpired",
"type": "error"
},
{
"inputs": [],
"name": "ExcessiveInvalidation",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "InsufficientAllowance",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "maxAmount",
"type": "uint256"
}
],
"name": "InvalidAmount",
"type": "error"
},
{
"inputs": [],
"name": "InvalidContractSignature",
"type": "error"
},
{
"inputs": [],
"name": "InvalidNonce",
"type": "error"
},
{
"inputs": [],
"name": "InvalidSignature",
"type": "error"
},
{
"inputs": [],
"name": "InvalidSignatureLength",
"type": "error"
},
{
"inputs": [],
"name": "InvalidSigner",
"type": "error"
},
{
"inputs": [],
"name": "LengthMismatch",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "signatureDeadline",
"type": "uint256"
}
],
"name": "SignatureExpired",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"indexed": false,
"internalType": "uint48",
"name": "expiration",
"type": "uint48"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "Lockdown",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint48",
"name": "newNonce",
"type": "uint48"
},
{
"indexed": false,
"internalType": "uint48",
"name": "oldNonce",
"type": "uint48"
}
],
"name": "NonceInvalidation",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"indexed": false,
"internalType": "uint48",
"name": "expiration",
"type": "uint48"
},
{
"indexed": false,
"internalType": "uint48",
"name": "nonce",
"type": "uint48"
}
],
"name": "Permit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "word",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "mask",
"type": "uint256"
}
],
"name": "UnorderedNonceInvalidation",
"type": "event"
},
{
"inputs": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"internalType": "uint48",
"name": "expiration",
"type": "uint48"
},
{
"internalType": "uint48",
"name": "nonce",
"type": "uint48"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"internalType": "uint48",
"name": "expiration",
"type": "uint48"
}
],
"name": "approve",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint48",
"name": "newNonce",
"type": "uint48"
}
],
"name": "invalidateNonces",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "wordPos",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "mask",
"type": "uint256"
}
],
"name": "invalidateUnorderedNonces",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"internalType": "struct IAllowanceTransfer.TokenSpenderPair[]",
"name": "approvals",
"type": "tuple[]"
}
],
"name": "lockdown",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "nonceBitmap",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"internalType": "uint48",
"name": "expiration",
"type": "uint48"
},
{
"internalType": "uint48",
"name": "nonce",
"type": "uint48"
}
],
"internalType": "struct IAllowanceTransfer.PermitDetails[]",
"name": "details",
"type": "tuple[]"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "sigDeadline",
"type": "uint256"
}
],
"internalType": "struct IAllowanceTransfer.PermitBatch",
"name": "permitBatch",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
}
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"internalType": "uint48",
"name": "expiration",
"type": "uint48"
},
{
"internalType": "uint48",
"name": "nonce",
"type": "uint48"
}
],
"internalType": "struct IAllowanceTransfer.PermitDetails",
"name": "details",
"type": "tuple"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "sigDeadline",
"type": "uint256"
}
],
"internalType": "struct IAllowanceTransfer.PermitSingle",
"name": "permitSingle",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
}
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.TokenPermissions",
"name": "permitted",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.PermitTransferFrom",
"name": "permit",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "requestedAmount",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.SignatureTransferDetails",
"name": "transferDetails",
"type": "tuple"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
}
],
"name": "permitTransferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.TokenPermissions[]",
"name": "permitted",
"type": "tuple[]"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.PermitBatchTransferFrom",
"name": "permit",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "requestedAmount",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.SignatureTransferDetails[]",
"name": "transferDetails",
"type": "tuple[]"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
}
],
"name": "permitTransferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.TokenPermissions",
"name": "permitted",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.PermitTransferFrom",
"name": "permit",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "requestedAmount",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.SignatureTransferDetails",
"name": "transferDetails",
"type": "tuple"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "bytes32",
"name": "witness",
"type": "bytes32"
},
{
"internalType": "string",
"name": "witnessTypeString",
"type": "string"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
}
],
"name": "permitWitnessTransferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.TokenPermissions[]",
"name": "permitted",
"type": "tuple[]"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.PermitBatchTransferFrom",
"name": "permit",
"type": "tuple"
},
{
"components": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "requestedAmount",
"type": "uint256"
}
],
"internalType": "struct ISignatureTransfer.SignatureTransferDetails[]",
"name": "transferDetails",
"type": "tuple[]"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "bytes32",
"name": "witness",
"type": "bytes32"
},
{
"internalType": "string",
"name": "witnessTypeString",
"type": "string"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
}
],
"name": "permitWitnessTransferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"internalType": "struct IAllowanceTransfer.AllowanceTransferDetails[]",
"name": "transferDetails",
"type": "tuple[]"
}
],
"name": "transferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "transferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
] as const;

View File

@@ -1,267 +0,0 @@
export const QuoterV2ABI = [
{
"inputs": [
{
"internalType": "address",
"name": "_factory",
"type": "address"
},
{
"internalType": "address",
"name": "_WETH9",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "WETH9",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
}
],
"name": "quoteExactInput",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
},
{
"internalType": "uint160[]",
"name": "sqrtPriceX96AfterList",
"type": "uint160[]"
},
{
"internalType": "uint32[]",
"name": "initializedTicksCrossedList",
"type": "uint32[]"
},
{
"internalType": "uint256",
"name": "gasEstimate",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "tokenIn",
"type": "address"
},
{
"internalType": "address",
"name": "tokenOut",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct IQuoterV2.QuoteExactInputSingleParams",
"name": "params",
"type": "tuple"
}
],
"name": "quoteExactInputSingle",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96After",
"type": "uint160"
},
{
"internalType": "uint32",
"name": "initializedTicksCrossed",
"type": "uint32"
},
{
"internalType": "uint256",
"name": "gasEstimate",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
}
],
"name": "quoteExactOutput",
"outputs": [
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint160[]",
"name": "sqrtPriceX96AfterList",
"type": "uint160[]"
},
{
"internalType": "uint32[]",
"name": "initializedTicksCrossedList",
"type": "uint32[]"
},
{
"internalType": "uint256",
"name": "gasEstimate",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "tokenIn",
"type": "address"
},
{
"internalType": "address",
"name": "tokenOut",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct IQuoterV2.QuoteExactOutputSingleParams",
"name": "params",
"type": "tuple"
}
],
"name": "quoteExactOutputSingle",
"outputs": [
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96After",
"type": "uint160"
},
{
"internalType": "uint32",
"name": "initializedTicksCrossed",
"type": "uint32"
},
{
"internalType": "uint256",
"name": "gasEstimate",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int256",
"name": "amount0Delta",
"type": "int256"
},
{
"internalType": "int256",
"name": "amount1Delta",
"type": "int256"
},
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
}
],
"name": "uniswapV3SwapCallback",
"outputs": [],
"stateMutability": "view",
"type": "function"
}
] as const;

View File

@@ -1,443 +0,0 @@
export const UniversalRouterABI = [
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "permit2",
"type": "address"
},
{
"internalType": "address",
"name": "weth9",
"type": "address"
},
{
"internalType": "address",
"name": "v2Factory",
"type": "address"
},
{
"internalType": "address",
"name": "v3Factory",
"type": "address"
},
{
"internalType": "bytes32",
"name": "pairInitCodeHash",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "poolInitCodeHash",
"type": "bytes32"
},
{
"internalType": "address",
"name": "v4PoolManager",
"type": "address"
},
{
"internalType": "address",
"name": "v3NFTPositionManager",
"type": "address"
},
{
"internalType": "address",
"name": "v4PositionManager",
"type": "address"
}
],
"internalType": "struct RouterParameters",
"name": "params",
"type": "tuple"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "BalanceTooLow",
"type": "error"
},
{
"inputs": [],
"name": "ContractLocked",
"type": "error"
},
{
"inputs": [
{
"internalType": "Currency",
"name": "currency",
"type": "address"
}
],
"name": "DeltaNotNegative",
"type": "error"
},
{
"inputs": [
{
"internalType": "Currency",
"name": "currency",
"type": "address"
}
],
"name": "DeltaNotPositive",
"type": "error"
},
{
"inputs": [],
"name": "ETHNotAccepted",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "commandIndex",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "message",
"type": "bytes"
}
],
"name": "ExecutionFailed",
"type": "error"
},
{
"inputs": [],
"name": "FromAddressIsNotOwner",
"type": "error"
},
{
"inputs": [],
"name": "InputLengthMismatch",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientBalance",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientETH",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientToken",
"type": "error"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "action",
"type": "bytes4"
}
],
"name": "InvalidAction",
"type": "error"
},
{
"inputs": [],
"name": "InvalidBips",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "commandType",
"type": "uint256"
}
],
"name": "InvalidCommandType",
"type": "error"
},
{
"inputs": [],
"name": "InvalidEthSender",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPath",
"type": "error"
},
{
"inputs": [],
"name": "InvalidReserves",
"type": "error"
},
{
"inputs": [],
"name": "LengthMismatch",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "NotAuthorizedForToken",
"type": "error"
},
{
"inputs": [],
"name": "NotPoolManager",
"type": "error"
},
{
"inputs": [],
"name": "OnlyMintAllowed",
"type": "error"
},
{
"inputs": [],
"name": "SliceOutOfBounds",
"type": "error"
},
{
"inputs": [],
"name": "TransactionDeadlinePassed",
"type": "error"
},
{
"inputs": [],
"name": "UnsafeCast",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "action",
"type": "uint256"
}
],
"name": "UnsupportedAction",
"type": "error"
},
{
"inputs": [],
"name": "V2InvalidPath",
"type": "error"
},
{
"inputs": [],
"name": "V2TooLittleReceived",
"type": "error"
},
{
"inputs": [],
"name": "V2TooMuchRequested",
"type": "error"
},
{
"inputs": [],
"name": "V3InvalidAmountOut",
"type": "error"
},
{
"inputs": [],
"name": "V3InvalidCaller",
"type": "error"
},
{
"inputs": [],
"name": "V3InvalidSwap",
"type": "error"
},
{
"inputs": [],
"name": "V3TooLittleReceived",
"type": "error"
},
{
"inputs": [],
"name": "V3TooMuchRequested",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minAmountOutReceived",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountReceived",
"type": "uint256"
}
],
"name": "V4TooLittleReceived",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "maxAmountInRequested",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountRequested",
"type": "uint256"
}
],
"name": "V4TooMuchRequested",
"type": "error"
},
{
"inputs": [],
"name": "V3_POSITION_MANAGER",
"outputs": [
{
"internalType": "contract INonfungiblePositionManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "V4_POSITION_MANAGER",
"outputs": [
{
"internalType": "contract IPositionManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "commands",
"type": "bytes"
},
{
"internalType": "bytes[]",
"name": "inputs",
"type": "bytes[]"
}
],
"name": "execute",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "commands",
"type": "bytes"
},
{
"internalType": "bytes[]",
"name": "inputs",
"type": "bytes[]"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "execute",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "msgSender",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"outputs": [
{
"internalType": "contract IPoolManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "int256",
"name": "amount0Delta",
"type": "int256"
},
{
"internalType": "int256",
"name": "amount1Delta",
"type": "int256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "uniswapV3SwapCallback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "unlockCallback",
"outputs": [
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
] as const;

View File

@@ -1,983 +0,0 @@
export const v3PoolABI = [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount",
"type": "uint128"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"name": "Burn",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount0",
"type": "uint128"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount1",
"type": "uint128"
}
],
"name": "Collect",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount0",
"type": "uint128"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount1",
"type": "uint128"
}
],
"name": "CollectProtocol",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "paid0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "paid1",
"type": "uint256"
}
],
"name": "Flash",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint16",
"name": "observationCardinalityNextOld",
"type": "uint16"
},
{
"indexed": false,
"internalType": "uint16",
"name": "observationCardinalityNextNew",
"type": "uint16"
}
],
"name": "IncreaseObservationCardinalityNext",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"indexed": false,
"internalType": "int24",
"name": "tick",
"type": "int24"
}
],
"name": "Initialize",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount",
"type": "uint128"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"name": "Mint",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint8",
"name": "feeProtocol0Old",
"type": "uint8"
},
{
"indexed": false,
"internalType": "uint8",
"name": "feeProtocol1Old",
"type": "uint8"
},
{
"indexed": false,
"internalType": "uint8",
"name": "feeProtocol0New",
"type": "uint8"
},
{
"indexed": false,
"internalType": "uint8",
"name": "feeProtocol1New",
"type": "uint8"
}
],
"name": "SetFeeProtocol",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "int256",
"name": "amount0",
"type": "int256"
},
{
"indexed": false,
"internalType": "int256",
"name": "amount1",
"type": "int256"
},
{
"indexed": false,
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"indexed": false,
"internalType": "uint128",
"name": "liquidity",
"type": "uint128"
},
{
"indexed": false,
"internalType": "int24",
"name": "tick",
"type": "int24"
}
],
"name": "Swap",
"type": "event"
},
{
"inputs": [
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint128",
"name": "amount",
"type": "uint128"
}
],
"name": "burn",
"outputs": [
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint128",
"name": "amount0Requested",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "amount1Requested",
"type": "uint128"
}
],
"name": "collect",
"outputs": [
{
"internalType": "uint128",
"name": "amount0",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "amount1",
"type": "uint128"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint128",
"name": "amount0Requested",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "amount1Requested",
"type": "uint128"
}
],
"name": "collectProtocol",
"outputs": [
{
"internalType": "uint128",
"name": "amount0",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "amount1",
"type": "uint128"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "fee",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeGrowthGlobal0X128",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeGrowthGlobal1X128",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "flash",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "observationCardinalityNext",
"type": "uint16"
}
],
"name": "increaseObservationCardinalityNext",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "liquidity",
"outputs": [
{
"internalType": "uint128",
"name": "",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxLiquidityPerTick",
"outputs": [
{
"internalType": "uint128",
"name": "",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint128",
"name": "amount",
"type": "uint128"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "mint",
"outputs": [
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "observations",
"outputs": [
{
"internalType": "uint32",
"name": "blockTimestamp",
"type": "uint32"
},
{
"internalType": "int56",
"name": "tickCumulative",
"type": "int56"
},
{
"internalType": "uint160",
"name": "secondsPerLiquidityCumulativeX128",
"type": "uint160"
},
{
"internalType": "bool",
"name": "initialized",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32[]",
"name": "secondsAgos",
"type": "uint32[]"
}
],
"name": "observe",
"outputs": [
{
"internalType": "int56[]",
"name": "tickCumulatives",
"type": "int56[]"
},
{
"internalType": "uint160[]",
"name": "secondsPerLiquidityCumulativeX128s",
"type": "uint160[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "key",
"type": "bytes32"
}
],
"name": "positions",
"outputs": [
{
"internalType": "uint128",
"name": "_liquidity",
"type": "uint128"
},
{
"internalType": "uint256",
"name": "feeGrowthInside0LastX128",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "feeGrowthInside1LastX128",
"type": "uint256"
},
{
"internalType": "uint128",
"name": "tokensOwed0",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "tokensOwed1",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "protocolFees",
"outputs": [
{
"internalType": "uint128",
"name": "token0",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "token1",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint8",
"name": "feeProtocol0",
"type": "uint8"
},
{
"internalType": "uint8",
"name": "feeProtocol1",
"type": "uint8"
}
],
"name": "setFeeProtocol",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "slot0",
"outputs": [
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"internalType": "int24",
"name": "tick",
"type": "int24"
},
{
"internalType": "uint16",
"name": "observationIndex",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "observationCardinality",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "observationCardinalityNext",
"type": "uint16"
},
{
"internalType": "uint8",
"name": "feeProtocol",
"type": "uint8"
},
{
"internalType": "bool",
"name": "unlocked",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
}
],
"name": "snapshotCumulativesInside",
"outputs": [
{
"internalType": "int56",
"name": "tickCumulativeInside",
"type": "int56"
},
{
"internalType": "uint160",
"name": "secondsPerLiquidityInsideX128",
"type": "uint160"
},
{
"internalType": "uint32",
"name": "secondsInside",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "bool",
"name": "zeroForOne",
"type": "bool"
},
{
"internalType": "int256",
"name": "amountSpecified",
"type": "int256"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "swap",
"outputs": [
{
"internalType": "int256",
"name": "amount0",
"type": "int256"
},
{
"internalType": "int256",
"name": "amount1",
"type": "int256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int16",
"name": "wordPosition",
"type": "int16"
}
],
"name": "tickBitmap",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tickSpacing",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "int24",
"name": "tick",
"type": "int24"
}
],
"name": "ticks",
"outputs": [
{
"internalType": "uint128",
"name": "liquidityGross",
"type": "uint128"
},
{
"internalType": "int128",
"name": "liquidityNet",
"type": "int128"
},
{
"internalType": "uint256",
"name": "feeGrowthOutside0X128",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "feeGrowthOutside1X128",
"type": "uint256"
},
{
"internalType": "int56",
"name": "tickCumulativeOutside",
"type": "int56"
},
{
"internalType": "uint160",
"name": "secondsPerLiquidityOutsideX128",
"type": "uint160"
},
{
"internalType": "uint32",
"name": "secondsOutside",
"type": "uint32"
},
{
"internalType": "bool",
"name": "initialized",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "token0",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "token1",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
] as const;

View File

@@ -1,198 +0,0 @@
import { encodeAbiParameters, getAddress } from "viem";
const V3_SWAP_EXACT_IN = 0x00;
const UNWRAP_WETH = 0x0c;
const PERMIT2_TRANSFER_FROM = 0x02;
const SWEEP = 0x04;
const TRANSFER = 0x05;
const PERMIT2_PERMIT = 0x0a;
export type CommandPair = [number, `0x${string}`];
export interface IPermitSingle {
details: {
token: `0x${string}`;
amount: bigint;
expiration: number;
nonce: number;
};
spender: `0x${string}`;
sigDeadline: bigint;
}
export interface Permit2Params {
permit: IPermitSingle;
signature: string;
}
export function permit2({ permit, signature }: Permit2Params): CommandPair {
const encodedInput = encodeAbiParameters(
[
{
name: 'PermitSingle', type: 'tuple',
components: [
{
name: "details", type: "tuple", components: [
{ name: "token", type: "address" },
{ name: "amount", type: "uint160" },
{ name: "expiration", type: "uint48" },
{ name: "nonce", type: "uint48" },
]
},
{ name: "spender", type: "address" },
{ name: "sigDeadline", type: "uint256" }
]
},
{ name: 'signature', type: 'bytes' },
],
[
permit,
signature as `0x${string}`
]
);
return [PERMIT2_PERMIT, encodedInput];
}
interface TransferParams {
token: string;
recipient: `0x${string}`;
amount: bigint;
}
export function transferWithPermit({ token, recipient, amount }: TransferParams): CommandPair {
const encodedInput = encodeAbiParameters(
[
{ name: 'token', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
[
getAddress(token), getAddress(recipient), amount
]
);
return [PERMIT2_TRANSFER_FROM, encodedInput];
}
export function transfer({ token, recipient, amount }: TransferParams): CommandPair {
const encodedInput = encodeAbiParameters(
[
{ name: 'token', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
[
getAddress(token), getAddress(recipient), amount
]
);
return [TRANSFER, encodedInput];
}
interface V3SwapExactInParams {
recipient: string;
amountIn: bigint;
minAmountOut: bigint;
path: `0x${string}`;
payerIsUser: boolean;
}
export function swapV3ExactIn({
recipient,
amountIn,
minAmountOut,
path,
payerIsUser
}: V3SwapExactInParams): CommandPair {
const encodedInput = encodeAbiParameters(
[
{ name: 'recipient', type: 'address' },
{ name: 'amountIn', type: 'uint256' },
{ name: 'minAmountOut', type: 'uint256' },
{ name: 'path', type: 'bytes' },
{ name: 'payerIsUser', type: 'bool' },
],
[
getAddress(recipient), amountIn, minAmountOut, path, payerIsUser
]
);
return [V3_SWAP_EXACT_IN, encodedInput];
}
interface UnwrapWethParams {
recipient: string;
minAmountOut: bigint;
}
export function unwrapWeth({ recipient, minAmountOut }: UnwrapWethParams): CommandPair {
const encodedInput = encodeAbiParameters(
[
{ name: 'recipient', type: 'address' },
{ name: 'minAmountOut', type: 'uint256' },
],
[
getAddress(recipient), minAmountOut
]
);
return [UNWRAP_WETH, encodedInput];
}
interface SweepParams {
token: string;
recipient: string;
minAmountOut: bigint;
}
export function sweep({ token, recipient, minAmountOut }: SweepParams): CommandPair {
const encodedInput = encodeAbiParameters(
[
{ name: 'token', type: 'address' },
{ name: 'recipient', type: 'address' },
{ name: 'minAmountOut', type: 'uint256' },
],
[
getAddress(token), getAddress(recipient), minAmountOut
]
);
return [SWEEP, encodedInput];
}
export const enum Command {
permit2 = "permit2",
transfer = "transfer",
transferWithPermit = "transferWithPermit",
swapV3ExactIn = "swapV3ExactIn",
unrwapWeth = "unrwapWeth",
sweep = "sweep",
};
interface CommandParams {
[Command.permit2]: Permit2Params;
[Command.transfer]: TransferParams;
[Command.transferWithPermit]: TransferParams;
[Command.swapV3ExactIn]: V3SwapExactInParams;
[Command.unrwapWeth]: UnwrapWethParams;
[Command.sweep]: SweepParams;
};
type CommandFnMap = {
[K in keyof CommandParams]: (params: CommandParams[K]) => CommandPair;
};
export type Instruction = {
[K in keyof CommandParams]: {
command: K;
params: CommandParams[K];
}
}[keyof CommandParams];
export const commandFnMap: CommandFnMap = {
[Command.permit2]: permit2,
[Command.transfer]: transfer,
[Command.transferWithPermit]: transferWithPermit,
[Command.sweep]: sweep,
[Command.swapV3ExactIn]: swapV3ExactIn,
[Command.unrwapWeth]: unwrapWeth,
};
export function encodeInstruction<K extends Command>(ins: { command: K, params: CommandParams[K]; }): CommandPair {
return commandFnMap[ins.command](ins.params);
}

View File

@@ -1,32 +1,16 @@
import { QUOTER_ADDRESSES, V3_CORE_FACTORY_ADDRESSES } from "@uniswap/sdk-core";
import { UNIVERSAL_ROUTER_ADDRESS, UniversalRouterVersion } from "@uniswap/universal-router-sdk";
import { Address, getAddress } from "viem";
export { WETH9 as WRAPPED_NATIVE_TOKEN_ADDRESS } from "@uniswap/sdk-core";
export const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3'
export function permit2Address(chainId?: number): `0x${string}` {
switch (chainId) {
case 324:
return '0x0000000000225e31D15943971F47aD3022F714Fa'
default:
return PERMIT2_ADDRESS
}
}
import { Address } from "viem";
/**
* Mainnet (1), Polygon (137), Optimism (10), Arbitrum (42161), Testnets Address (11155111)
* Mainnet, Polygon, Optimism, Arbitrum, Testnets Address
* source: https://github.com/Uniswap/v3-periphery/blob/main/deploys.md
*/
// export const QUOTER_CONTRACT_ADDRESS: Record<string, Address> = {
// "1": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", // Ethereum
// "137": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", // polygon
// "10": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", // Optimism
// "42161": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", // Arbitrum
// "11155111": "0xEd1f6473345F45b75F8179591dd5bA1888cf2FB3", // Sepolia
// };
export const QUOTER_CONTRACT_ADDRESS: Record<string, Address> = {
"1": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", // Ethereum
"137": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", // polygon
"10": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", // Optimism
"42161": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", // Arbitrum
"11155111": "0xEd1f6473345F45b75F8179591dd5bA1888cf2FB3", // Sepolia
};
export const FACTORY_CONTRACT_ADDRESS: Record<string, Address> = {
"1": "0x1F98431c8aD98523631AE4a59f267346ea31F984", // Ethereum
@@ -36,19 +20,11 @@ export const FACTORY_CONTRACT_ADDRESS: Record<string, Address> = {
"11155111": "0x0227628f3f023bb0b980b67d528571c95c6dac1c", // Sepolia
}
export function getRouterAddress(chainId: number) {
return getAddress(UNIVERSAL_ROUTER_ADDRESS(UniversalRouterVersion.V2_0, chainId));
}
export function getPermit2Address(chainId: number) {
return getAddress(permit2Address(chainId));
}
export function getV3Factory(chainId: number) {
return getAddress(V3_CORE_FACTORY_ADDRESSES[chainId]!)
}
export function getQuoterAddress(chainId: number) {
const mainnetQuoter = "0x61fFE014bA17989E743c5F6cB21bF9697530B21e";
return getAddress(chainId !== 1 ? QUOTER_ADDRESSES[chainId]! : mainnetQuoter)
export const WRAPPED_NATIVE_TOKEN_ADDRESS: Record<string, Address> = {
"1": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // mainnet (WETH)
"137": "0x0000000000000000000000000000000000001010", // polygon (POL)
// "137": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", // (WPOL) TODO: compare which token to use
"10": "0x4200000000000000000000000000000000000006", // Optimism (WETH)
"42161": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // Arbitrum (WETH)
"11155111": "0xfff9976782d46cc05630d1f6ebab18b2324d6b14", // sepolia (WETH)
}

View File

@@ -1,58 +0,0 @@
import { Address, CustomSource, getContract } from "viem";
import { web3Provider } from "../index.js";
import { Permit2ABI } from "./abis/permit2.abi.js";
import { createPermitSingleData, PermitSingle } from "./permit2.js";
export async function createPermit2<Signer extends CustomSource>({
signer,
chainId,
permit2Address,
routerAddress,
assetAddress,
allowanceAmount,
}: {
signer: Signer,
chainId: number,
permit2Address: Address,
routerAddress: Address,
assetAddress: Address,
allowanceAmount: bigint,
}): Promise<[PermitSingle, `0x${string}`]> {
const deadline = Math.floor(3600 + Number(new Date()) / 1000); // one hour
const permitContract = getContract({
abi: Permit2ABI,
address: permit2Address,
client: web3Provider.client(chainId)
});
const allowance = await permitContract.read.allowance([
signer.address,
assetAddress,
routerAddress
]);
const [, , nonce] = allowance;
const permitSingle = {
spender: routerAddress,
details: {
token: assetAddress,
amount: allowanceAmount,
expiration: deadline,
nonce
},
sigDeadline: BigInt(deadline)
};
const permitData = createPermitSingleData(permitSingle, permit2Address, chainId);
const signature = await signer.signTypedData({
domain: permitData.domain,
types: permitData.types,
message: permitSingle,
primaryType: "PermitSingle"
});
return [permitSingle, signature];
}

View File

@@ -0,0 +1,202 @@
export const IERC20MinimalABI = {
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
} as const;

View File

@@ -1,60 +0,0 @@
const PERMIT2_DOMAIN_NAME = 'Permit2';
export function permit2Domain(permit2Address: `0x${string}`, chainId: number) {
return {
name: PERMIT2_DOMAIN_NAME,
chainId,
verifyingContract: permit2Address,
};
}
export const PERMIT_DETAILS = [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint160' },
{ name: 'expiration', type: 'uint48' },
{ name: 'nonce', type: 'uint48' },
];
export const PERMIT_TYPES = {
PermitSingle: [
{ name: 'details', type: 'PermitDetails' },
{ name: 'spender', type: 'address' },
{ name: 'sigDeadline', type: 'uint256' },
],
PermitDetails: PERMIT_DETAILS,
};
export interface PermitDetails {
token: `0x${string}`,
amount: bigint,
expiration: number,
nonce: number;
}
export interface PermitSingle {
details: PermitDetails;
spender: `0x${string}`;
sigDeadline: bigint;
}
const MaxUint48 = BigInt('0xffffffffffff');
const MaxUint160 = BigInt('0xffffffffffffffffffffffffffffffffffffffff');
const MaxAllowanceTransferAmount = MaxUint160;
const MaxAllowanceExpiration = MaxUint48;
const MaxOrderedNonce = MaxUint48;
export function validatePermitDetails(details: PermitDetails) {
if (MaxOrderedNonce <= details.nonce) throw new Error('NONCE_OUT_OF_RANGE');
if (MaxAllowanceTransferAmount <= details.amount) throw new Error('AMOUNT_OUT_OF_RANGE');
if (MaxAllowanceExpiration <= details.expiration) throw new Error('EXPIRATION_OUT_OF_RANGE');
}
export function createPermitSingleData(permit: PermitSingle, permit2Address: `0x${string}`, chainId: number) {
const domain = permit2Domain(permit2Address, chainId);
validatePermitDetails(permit.details);
return {
domain,
types: PERMIT_TYPES,
values: permit,
};
}

View File

@@ -1,49 +0,0 @@
import { Token, WETH9 } from "@uniswap/sdk-core";
import { encodeRouteToPath, FeeAmount, Pool, Route as V3Route } from '@uniswap/v3-sdk';
import { getAddress, getContract, toHex } from "viem";
import { getV3Factory } from "./constants.js";
import { web3Provider } from "../index.js";
import { v3PoolABI } from "./abis/v3pool.abi.js";
function sortTokens(tokenA: Token, tokenB: Token): [Token, Token] {
return tokenA.address.toLowerCase() < tokenB.address.toLowerCase()
? [tokenA, tokenB]
: [tokenB, tokenA];
}
export async function getPool(chainId: number, tokenA: Token, tokenB: Token, fee: FeeAmount) {
const V3_FACTORY = getV3Factory(chainId);
const [token0, token1] = sortTokens(tokenA, tokenB);
const poolAddress = Pool.getAddress(token0, token1, fee, undefined, V3_FACTORY);
const poolContract = getContract({
abi: v3PoolABI,
address: getAddress(poolAddress),
client: web3Provider.client(chainId),
});
const [liquidity, slot0] = await Promise.all([
poolContract.read.liquidity!(),
poolContract.read.slot0!(),
]);
const [sqrtPriceX96, tick, , , , ,] = slot0;
const pool = new Pool(token0, token1, fee, toHex(sqrtPriceX96), toHex(liquidity), tick);
return pool;
}
export async function getPoolPath(tokenIn: `0x${string}`, chainId: number) {
const weth = WETH9[chainId]!;
const TokenIn = new Token(chainId, tokenIn, 1);
const pool = await getPool(chainId, TokenIn, weth, FeeAmount.LOW); // 0.05% fee tier
const route = new V3Route([pool], TokenIn, weth);
const pathParams = encodeRouteToPath(route, false) as `0x${string}`;
return pathParams;
}

View File

@@ -0,0 +1,269 @@
export const QuoterV2ABI = {
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_factory",
"type": "address"
},
{
"internalType": "address",
"name": "_WETH9",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "WETH9",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
}
],
"name": "quoteExactInput",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
},
{
"internalType": "uint160[]",
"name": "sqrtPriceX96AfterList",
"type": "uint160[]"
},
{
"internalType": "uint32[]",
"name": "initializedTicksCrossedList",
"type": "uint32[]"
},
{
"internalType": "uint256",
"name": "gasEstimate",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "tokenIn",
"type": "address"
},
{
"internalType": "address",
"name": "tokenOut",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct IQuoterV2.QuoteExactInputSingleParams",
"name": "params",
"type": "tuple"
}
],
"name": "quoteExactInputSingle",
"outputs": [
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96After",
"type": "uint160"
},
{
"internalType": "uint32",
"name": "initializedTicksCrossed",
"type": "uint32"
},
{
"internalType": "uint256",
"name": "gasEstimate",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "amountOut",
"type": "uint256"
}
],
"name": "quoteExactOutput",
"outputs": [
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint160[]",
"name": "sqrtPriceX96AfterList",
"type": "uint160[]"
},
{
"internalType": "uint32[]",
"name": "initializedTicksCrossedList",
"type": "uint32[]"
},
{
"internalType": "uint256",
"name": "gasEstimate",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "tokenIn",
"type": "address"
},
{
"internalType": "address",
"name": "tokenOut",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct IQuoterV2.QuoteExactOutputSingleParams",
"name": "params",
"type": "tuple"
}
],
"name": "quoteExactOutputSingle",
"outputs": [
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96After",
"type": "uint160"
},
{
"internalType": "uint32",
"name": "initializedTicksCrossed",
"type": "uint32"
},
{
"internalType": "uint256",
"name": "gasEstimate",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int256",
"name": "amount0Delta",
"type": "int256"
},
{
"internalType": "int256",
"name": "amount1Delta",
"type": "int256"
},
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
}
],
"name": "uniswapV3SwapCallback",
"outputs": [],
"stateMutability": "view",
"type": "function"
}
]
} as const;

View File

@@ -1,336 +0,0 @@
import { Token } from '@uniswap/sdk-core';
import { FeeAmount } from '@uniswap/v3-sdk';
import { Account, Address, getAddress, getContract, WriteContractParameters } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { getSignerPrivateKey } from "../../config/index.js";
import { BlockchainError, RelayerError } from '../../exceptions/base.exception.js';
import { web3Provider } from '../../providers/index.js';
import { isFeeReceiverSameAsSigner, isViemError } from '../../utils.js';
import { IERC20MinimalABI } from './abis/erc20.abi.js';
import { QuoterV2ABI } from './abis/quoterV2.abi.js';
import { UniversalRouterABI } from './abis/universalRouter.abi.js';
import { Command, CommandPair, encodeInstruction, Instruction, Permit2Params } from './commands.js';
import { getPermit2Address, getQuoterAddress, getRouterAddress, WRAPPED_NATIVE_TOKEN_ADDRESS } from './constants.js';
import { createPermit2 } from './createPermit.js';
import { getPoolPath } from './pools.js';
export type UniswapQuote = {
chainId: number;
addressIn: string;
addressOut: string;
amountIn: bigint;
};
type QuoteToken = { amount: bigint, decimals: number; };
export type Quote = {
in: QuoteToken;
out: QuoteToken;
};
interface SwapWithRefundParams {
feeReceiver: `0x${string}`;
nativeRecipient: `0x${string}`;
tokenIn: `0x${string}`;
feeGross: bigint;
refundAmount: bigint;
chainId: number;
feeBase: bigint;
}
interface CreateInstructionsFeeReceiveerIsRelayer {
router: { address: Address; };
relayer: Account;
nativeRecipient: Address;
amountToSwap: bigint;
minAmountOut: bigint;
permitParmas: Permit2Params;
pathParams: `0x${string}`;
refundAmount: bigint;
}
interface CreateInstructionsFeeReceiveerIsNotRelayer extends CreateInstructionsFeeReceiveerIsRelayer {
tokenIn: Address;
feeReceiver: Address;
feeBase: bigint;
}
export class UniswapProvider {
static readonly ZERO_ADDRESS = getAddress("0x0000000000000000000000000000000000000000");
async getTokenInfo(chainId: number, address: Address): Promise<Token> {
const contract = getContract({
address,
abi: IERC20MinimalABI,
client: web3Provider.client(chainId)
});
const [decimals, symbol] = await Promise.all([
contract.read.decimals(),
contract.read.symbol(),
]);
return new Token(chainId, address, Number(decimals), symbol);
}
async quoteNativeToken(chainId: number, addressIn: Address, amountIn: bigint): Promise<Quote> {
const weth = WRAPPED_NATIVE_TOKEN_ADDRESS[chainId]!;
return this.quote({
chainId,
amountIn,
addressOut: weth.address,
addressIn
});
}
async quote({ chainId, addressIn, addressOut, amountIn }: UniswapQuote) {
const tokenIn = await this.getTokenInfo(chainId, addressIn as Address);
const tokenOut = await this.getTokenInfo(chainId, addressOut as Address);
const quoterContract = getContract({
address: getQuoterAddress(chainId),
abi: QuoterV2ABI,
client: web3Provider.client(chainId)
});
try {
const quotedAmountOut = await quoterContract.simulate.quoteExactInputSingle([{
tokenIn: tokenIn.address as Address,
tokenOut: tokenOut.address as Address,
fee: FeeAmount.LOW,
amountIn,
sqrtPriceLimitX96: 0n,
}]);
// amount, sqrtPriceX96After, tickAfter, gasEstimate
const [amount, , ,] = quotedAmountOut.result;
return {
in: {
amount: amountIn, decimals: tokenIn.decimals
},
out: {
amount, decimals: tokenOut.decimals
}
};
} catch (error) {
if (error instanceof Error && isViemError(error)) {
const { metaMessages, shortMessage } = error;
throw BlockchainError.txError((metaMessages ? metaMessages[0] : undefined) || shortMessage);
} else {
throw RelayerError.unknown("Something went wrong while quoting");
}
}
}
async approvePermit2forERC20(tokenIn: `0x${string}`, chainId: number) {
// 0) - (this is done only once) - Approve Permit2 to move Relayer's ERC20
const relayer = privateKeyToAccount(getSignerPrivateKey(chainId) as `0x${string}`);
const PERMIT2_ADDRESS = getPermit2Address(chainId);
const client = web3Provider.client(chainId);
const erc20 = getContract({
abi: IERC20MinimalABI,
address: tokenIn,
client,
});
const allowance = await erc20.read.allowance([relayer.address, PERMIT2_ADDRESS]);
if (allowance < 2n ** 128n) {
const hash = await erc20.write.approve(
[PERMIT2_ADDRESS, 2n ** 256n - 1n],
{ chain: client.chain, account: relayer }
);
await client.waitForTransactionReceipt({ hash });
}
}
static createInstructionsIfFeeReceiverIsNotRelayer({
permitParmas, router, pathParams, relayer,
tokenIn, feeReceiver, feeBase,
refundAmount, amountToSwap, minAmountOut, nativeRecipient
}: CreateInstructionsFeeReceiveerIsNotRelayer): Instruction[] {
// OPERATIONS:
// 1) Send permit for Router to move Gross Fees in Token from Relayer
// 2) AllowanceTransfer from Relayer to feeReceiver for Base Fees
// 3) Swap ERC20 for WETH consuming (Gross-Base) Fees, destination Router, setting the payerIsUser=true flag, meaning to use permit2 (Relayer has the tokens)
// 4) Unwrap WETH to Router
// 5) Transfer native Refund value to Relayer
// 6) Sweep whatever is left to Recipient
return [
// This is used to authorize the router to move our tokens
{ command: Command.permit2, params: permitParmas },
// We send relaying fees to feeReceiver
{ command: Command.transferWithPermit, params: { token: tokenIn, recipient: feeReceiver, amount: feeBase } },
// Swap consuming all
{
command: Command.swapV3ExactIn, params: {
// we're going to unwrap weth from here
recipient: router.address,
amountIn: amountToSwap,
minAmountOut,
// USDC-WETH
path: pathParams,
// The relayer is the tx initiator
payerIsUser: true,
}
},
// the router will hold the value for further splitting
{ command: Command.unrwapWeth, params: { recipient: router.address, minAmountOut } },
// gas refund to relayer
// 0 address means moving native
{ command: Command.transfer, params: { token: this.ZERO_ADDRESS, recipient: relayer.address, amount: refundAmount } },
// 0 address means moving native
// sweep reminder to the withdrawal address
{ command: Command.sweep, params: { token: this.ZERO_ADDRESS, recipient: nativeRecipient, minAmountOut } }
];
}
static createInstructionsIfFeeReceiverIsRelayer({
permitParmas, router, pathParams, relayer,
refundAmount, amountToSwap, minAmountOut, nativeRecipient
}: CreateInstructionsFeeReceiveerIsRelayer): Instruction[] {
// OPERATIONS:
// 1) Send permit for Router to move Gross Fees in Token from Relayer
// 2) Swap ERC20 for WETH, destination Router, setting the payerIsUser=true flag
// 3) Unwrap WETH to Router
// 4) Transfer native Refund value to Relayer
// 5) Sweep whatever is left to Recipient
return [
// This is used to authorize the router to move our tokens
{ command: Command.permit2, params: permitParmas },
// Swap consuming all
{
command: Command.swapV3ExactIn, params: {
// we're going to unwrap weth from here
recipient: router.address,
amountIn: amountToSwap,
minAmountOut,
// USDC-WETH
path: pathParams,
// The relayer is the tx initiator
payerIsUser: true,
}
},
// the router will hold the value for further splitting
{ command: Command.unrwapWeth, params: { recipient: router.address, minAmountOut } },
// gas refund to relayer
// 0 address means moving native
{ command: Command.transfer, params: { token: this.ZERO_ADDRESS, recipient: relayer.address, amount: refundAmount } },
// 0 address means moving native
// sweep reminder to the withdrawal address
{ command: Command.sweep, params: { token: this.ZERO_ADDRESS, recipient: nativeRecipient, minAmountOut } }
];
}
async simulateSwapExactInputSingleForWeth({
nativeRecipient,
feeReceiver,
feeBase,
feeGross,
tokenIn,
refundAmount,
chainId
}: SwapWithRefundParams): Promise<WriteContractParameters> {
await this.approvePermit2forERC20(tokenIn, chainId);
const minAmountOut = refundAmount;
const ROUTER_ADDRESS = getRouterAddress(chainId);
const PERMIT2_ADDRESS = getPermit2Address(chainId);
const relayer = privateKeyToAccount(getSignerPrivateKey(chainId) as `0x${string}`);
const client = web3Provider.client(chainId);
const router = getContract({
abi: UniversalRouterABI,
address: ROUTER_ADDRESS,
client
});
const amountToSwap = feeGross - feeBase;
const [permit, signature] = await createPermit2({
signer: relayer,
chainId,
allowanceAmount: feeGross,
permit2Address: PERMIT2_ADDRESS,
routerAddress: ROUTER_ADDRESS,
assetAddress: tokenIn
});
const pathParams = await getPoolPath(tokenIn, chainId);
let instructions;
if (isFeeReceiverSameAsSigner(chainId)) {
// If feeReceiver is the same as signer, moving coins around is easier
instructions = UniswapProvider.createInstructionsIfFeeReceiverIsRelayer({
relayer,
router,
amountToSwap,
permitParmas: { permit, signature },
refundAmount,
minAmountOut,
pathParams,
nativeRecipient
});
} else {
instructions = UniswapProvider.createInstructionsIfFeeReceiverIsNotRelayer({
relayer,
router,
amountToSwap,
permitParmas: { permit, signature },
refundAmount,
minAmountOut,
pathParams,
nativeRecipient,
// we need to know receiver and how much to take
feeReceiver,
feeBase,
tokenIn
});
}
const commandPairs: CommandPair[] = [];
instructions.forEach((ins) => commandPairs.push(encodeInstruction(ins)));
const commands = "0x" + commandPairs.map(x => x[0].toString(16).padStart(2, "0")).join("") as `0x${string}`;
const params = commandPairs.map(x => x[1]);
try {
const { request: simulation } = await router.simulate.execute([commands, params], { account: relayer });
const estimateGas = await client.estimateContractGas(simulation);
const {
address,
abi,
functionName,
args,
chain,
nonce,
} = simulation;
return {
functionName,
account: relayer,
address,
abi,
args,
chain,
nonce,
gas: estimateGas * 11n / 10n
};
} catch (e) {
console.error(e);
throw e;
}
}
async swapExactInputSingleForWeth(params: SwapWithRefundParams) {
const { chainId } = params;
const writeContractParams = await this.simulateSwapExactInputSingleForWeth(params);
const relayer = web3Provider.signer(chainId);
const txHash = await relayer.writeContract(writeContractParams);
return txHash;
}
}

View File

@@ -1,11 +1,11 @@
import { Chain, createPublicClient, createWalletClient, Hex, http, PublicClient, verifyTypedData, WalletClient } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { Chain, createPublicClient, Hex, http, PublicClient, verifyTypedData } from "viem";
import {
CONFIG,
getSignerPrivateKey
} from "../config/index.js";
import { FeeCommitment } from "../interfaces/relayer/common.js";
import { createChainObject } from "../utils.js";
import { privateKeyToAccount } from "viem/accounts";
import { FeeCommitment } from "../interfaces/relayer/common.js";
interface IWeb3Provider {
client(chainId: number): PublicClient;
@@ -16,14 +16,12 @@ const domain = (chainId: number) => ({
name: "Privacy Pools Relayer",
version: "1",
chainId,
} as const);
} as const)
const RelayerCommitmentTypes = {
RelayerCommitment: [
{ name: "withdrawalData", type: "bytes" },
{ name: "expiration", type: "uint256" },
{ name: "amount", type: "uint256" },
{ name: "extraGas", type: "bool" },
]
} as const;
@@ -31,9 +29,8 @@ const RelayerCommitmentTypes = {
* Class representing the provider for interacting with several chains
*/
export class Web3Provider implements IWeb3Provider {
chains: { [key: number]: Chain; };
clients: { [key: number]: PublicClient; };
signers: { [key: number]: WalletClient; };
chains: { [key: number]: Chain };
clients: { [key: number]: PublicClient };
constructor() {
this.chains = Object.fromEntries(CONFIG.chains.map(chainConfig => {
@@ -46,59 +43,38 @@ export class Web3Provider implements IWeb3Provider {
chain,
transport: http(chain.rpcUrls.default.http[0])
})];
}));
this.signers = Object.fromEntries(Object.entries(this.chains).map(([chainId, chain]) => {
const account = privateKeyToAccount(getSignerPrivateKey(Number(chainId)) as `0x${string}`);
return [
Number(chainId),
createWalletClient({
account,
chain,
transport: http(chain.rpcUrls.default.http[0])
})];
}));
}))
}
client(chainId: number): PublicClient {
const client = this.clients[chainId];
if (client === undefined) {
throw Error(`Web3ProviderError::UnsupportedChainId(${chainId})`);
throw Error(`Web3ProviderError::UnsupportedChainId(${chainId})`)
}
else return client;
}
signer(chainId: number): WalletClient {
const signer = this.signers[chainId];
if (signer === undefined) {
throw Error(`Web3ProviderError::UnsupportedChainId(${chainId})`);
}
else return signer;
else return client
}
async getGasPrice(chainId: number): Promise<bigint> {
return await this.client(chainId).getGasPrice();
return await this.client(chainId).getGasPrice()
}
async signRelayerCommitment(chainId: number, commitment: Omit<FeeCommitment, 'signedRelayerCommitment'>) {
const signer = privateKeyToAccount(getSignerPrivateKey(chainId) as Hex);
const { withdrawalData, expiration, extraGas, amount } = commitment;
const { withdrawalData, expiration } = commitment;
return signer.signTypedData({
domain: domain(chainId),
types: RelayerCommitmentTypes,
primaryType: 'RelayerCommitment',
message: {
withdrawalData,
amount,
extraGas,
expiration: BigInt(expiration)
}
});
})
}
async verifyRelayerCommitment(chainId: number, commitment: FeeCommitment): Promise<boolean> {
const signer = privateKeyToAccount(getSignerPrivateKey(chainId) as Hex);
const { withdrawalData, expiration, amount, extraGas, signedRelayerCommitment } = commitment;
const { withdrawalData, expiration, signedRelayerCommitment } = commitment;
return verifyTypedData({
address: signer.address,
domain: domain(chainId),
@@ -106,12 +82,10 @@ export class Web3Provider implements IWeb3Provider {
primaryType: 'RelayerCommitment',
message: {
withdrawalData,
amount,
extraGas,
expiration: BigInt(expiration)
},
signature: signedRelayerCommitment
});
})
}
}

View File

@@ -11,7 +11,6 @@ const quoteSchema: JSONSchemaType<QuotetBody> = {
amount: { type: ["string"] },
asset: { type: ["string"] },
recipient: { type: ["string"], nullable: true },
extraGas: { type: "boolean" }
},
required: ["chainId", "amount", "asset"],
} as const;

View File

@@ -1,55 +1,59 @@
import { getAddress } from "viem";
import { z } from "zod";
import { Ajv, JSONSchemaType } from "ajv";
import { RelayRequestBody } from "../../interfaces/relayer/request.js";
const zNonNegativeBigInt = z
.string()
.or(z.number())
.pipe(z.coerce.bigint().nonnegative());
// AJV schema for validation
const ajv = new Ajv();
// Address validation schema
export const zAddress = z
.string()
.regex(/^0x[0-9a-fA-F]+/)
.length(42)
.transform((v) => getAddress(v));
const relayRequestSchema: JSONSchemaType<RelayRequestBody> = {
type: "object",
properties: {
withdrawal: {
type: "object",
properties: {
processooor: { type: "string" },
data: { type: "string", pattern: "0x[0-9a-fA-F]+" },
},
required: ["processooor", "data"],
},
publicSignals: {
type: "array",
items: { type: "string" },
minItems: 8,
maxItems: 8,
},
proof: {
type: "object",
properties: {
protocol: { type: "string" },
curve: { type: "string" },
pi_a: { type: "array", items: { type: "string" }, minItems: 1 },
pi_b: {
type: "array",
items: {
type: "array",
items: { type: "string" },
minItems: 1,
},
minItems: 1,
},
pi_c: { type: "array", items: { type: "string" }, minItems: 1 },
},
required: ["pi_a", "pi_b", "pi_c"],
},
scope: { type: "string" },
chainId: { type: ["string", "number"] },
feeCommitment: {
type: "object",
properties: {
expiration: { type: "number" },
withdrawalData: { type: "string", pattern: "0x[0-9a-fA-F]+" },
signedRelayerCommitment: { type: "string", pattern: "0x[0-9a-fA-F]+" }
},
nullable: true,
required: ["expiration", "signedRelayerCommitment"]
}
},
required: ["withdrawal", "proof", "publicSignals", "scope", "chainId"],
} as const;
export const zHex = z
.string()
.regex(/^0x[0-9a-fA-F]+/)
.transform(x => x as `0x${string}`);
export const zWithdrawal = z.object({
processooor: zAddress,
data: zHex
});
export const zProof = z.object({
protocol: z.string().optional(),
curve: z.string().optional(),
pi_a: z.tuple([z.string(), z.string(), z.string()]),
pi_b: z.tuple([
z.tuple([z.string(), z.string()]),
z.tuple([z.string(), z.string()]),
z.tuple([z.string(), z.string()]),
]),
pi_c: z.tuple([z.string(), z.string(), z.string()]),
});
export const zFeeCommitment = z.object({
expiration: z.number().nonnegative().int(),
withdrawalData: zHex,
signedRelayerCommitment: zHex,
extraGas: z.boolean(),
amount: zNonNegativeBigInt
});
export const zRelayRequest = z.object({
withdrawal: zWithdrawal,
publicSignals: z.array(z.string()).length(8),
proof: zProof,
scope: zNonNegativeBigInt,
chainId: z.string().or(z.number()).pipe(z.coerce.number().positive()),
feeCommitment: zFeeCommitment.optional()
})
.strict()
.readonly();
export const validateRelayRequestBody = ajv.compile(relayRequestSchema);

View File

@@ -1,12 +1,11 @@
/**
* Handles withdrawal requests within the Privacy Pool relayer.
*/
import { Address, getAddress } from "viem";
import { getAddress } from "viem";
import {
getAssetConfig,
getEntrypointAddress,
getFeeReceiverAddress,
getSignerPrivateKey
getFeeReceiverAddress
} from "../config/index.js";
import {
BlockchainError,
@@ -18,17 +17,13 @@ import {
RelayerResponse,
WithdrawalPayload,
} from "../interfaces/relayer/request.js";
import { db, SdkProvider, UniswapProvider, web3Provider } from "../providers/index.js";
import { db, SdkProvider, web3Provider } from "../providers/index.js";
import { RelayerDatabase } from "../types/db.types.js";
import { SdkProviderInterface } from "../types/sdk.types.js";
import { decodeWithdrawalData, isFeeReceiverSameAsSigner, isNative, isViemError, parseSignals } from "../utils.js";
import { decodeWithdrawalData, isViemError, parseSignals } from "../utils.js";
import { quoteService } from "./index.js";
import { Web3Provider } from "../providers/web3.provider.js";
import { FeeCommitment } from "../interfaces/relayer/common.js";
import { uniswapProvider } from "../providers/index.js";
import { WRAPPED_NATIVE_TOKEN_ADDRESS } from "../providers/uniswap/constants.js";
import { Withdrawal, WithdrawalProof } from "@0xbow/privacy-pools-core-sdk";
import { privateKeyToAccount } from "viem/accounts";
/**
* Class representing the Privacy Pool Relayer, responsible for processing withdrawal requests.
@@ -40,7 +35,6 @@ export class PrivacyPoolRelayer {
protected sdkProvider: SdkProviderInterface;
/** Web3 provider for handling blockchain interactions. */
protected web3Provider: Web3Provider;
protected uniswapProvider: UniswapProvider;
/**
* Initializes a new instance of the Privacy Pool Relayer.
@@ -49,7 +43,6 @@ export class PrivacyPoolRelayer {
this.db = db;
this.sdkProvider = new SdkProvider();
this.web3Provider = web3Provider;
this.uniswapProvider = uniswapProvider;
}
/**
@@ -67,33 +60,17 @@ export class PrivacyPoolRelayer {
await this.db.createNewRequest(requestId, timestamp, req);
await this.validateWithdrawal(req, chainId);
const extraGas = req.feeCommitment?.extraGas ?? false;
const isValidWithdrawalProof = await this.verifyProof(req.proof);
if (!isValidWithdrawalProof) {
throw ZkError.invalidProof();
}
// We do early check, before relaying
if (extraGas) {
if (!WRAPPED_NATIVE_TOKEN_ADDRESS[chainId])
throw RelayerError.unknown(`Missing wrapped native token for chain ${chainId}`);
}
const response = await this.broadcastWithdrawal(req, chainId);
// const response = { hash: "0x" }
let txSwap;
if (extraGas) {
txSwap = await this.swapForNativeAndFund(req.scope, req.withdrawal, req.proof, chainId, response.hash);
}
await this.db.updateBroadcastedRequest(requestId, response.hash);
return {
success: true,
txHash: response.hash,
txSwap,
timestamp,
requestId,
};
@@ -139,42 +116,6 @@ export class PrivacyPoolRelayer {
}
}
async swapForNativeAndFund(scope: bigint, withdrawal: Withdrawal, proof: WithdrawalProof, chainId: number, relayTx: string) {
const { assetAddress } = await this.sdkProvider.scopeData(scope, chainId);
if (isNative(assetAddress)) {
// we shouldn't be here
return
}
const relayReceipt = await web3Provider.client(chainId).waitForTransactionReceipt({ hash: relayTx as `0x${string}` });
const { gasUsed: relayGasUsed, effectiveGasPrice: relayGasPrice } = relayReceipt;
const assetConfig = getAssetConfig(chainId, assetAddress);
const feeReceiver = getFeeReceiverAddress(chainId) as Address;
const { recipient, relayFeeBPS } = decodeWithdrawalData(withdrawal.data);
const withdrawnValue = parseSignals(proof.publicSignals).withdrawnValue;
const gasPrice = await web3Provider.getGasPrice(chainId);
const feeGross = withdrawnValue * relayFeeBPS / 10_000n;
const feeBase = withdrawnValue * assetConfig.fee_bps / 10_000n;
const relayerGasRefundValue = gasPrice * quoteService.extraGasTxCost + relayGasPrice * relayGasUsed;
const txHash = await this.uniswapProvider.swapExactInputSingleForWeth({
chainId,
feeGross,
feeBase,
refundAmount: relayerGasRefundValue,
tokenIn: assetAddress,
nativeRecipient: recipient,
feeReceiver
});
return txHash;
}
/**
* Verifies a withdrawal proof.
*
@@ -197,15 +138,15 @@ export class PrivacyPoolRelayer {
protected async broadcastWithdrawal(
withdrawal: WithdrawalPayload,
chainId: number,
): Promise<{ hash: string; }> {
): Promise<{ hash: string }> {
try {
return await this.sdkProvider.broadcastWithdrawal(withdrawal, chainId);
} catch (error) {
if (isViemError(error)) {
const { metaMessages, shortMessage } = error;
throw BlockchainError.txError((metaMessages ? metaMessages[0] : undefined) || shortMessage);
throw BlockchainError.txError((metaMessages ? metaMessages[0] : undefined) || shortMessage)
} else {
throw RelayerError.unknown("Something went wrong while broadcasting Tx");
throw RelayerError.unknown("Something went wrong while broadcasting Tx")
}
}
}
@@ -221,9 +162,6 @@ export class PrivacyPoolRelayer {
protected async validateWithdrawal(wp: WithdrawalPayload, chainId: number) {
const entrypointAddress = getEntrypointAddress(chainId);
const feeReceiverAddress = getFeeReceiverAddress(chainId);
const signerAddress = privateKeyToAccount(getSignerPrivateKey(chainId) as `0x${string}`).address;
const extraGas = wp.feeCommitment?.extraGas ?? false;
const { feeRecipient, relayFeeBPS } = decodeWithdrawalData(
wp.withdrawal.data,
@@ -236,18 +174,10 @@ export class PrivacyPoolRelayer {
);
}
if (extraGas && !isFeeReceiverSameAsSigner(chainId)) {
if (getAddress(feeRecipient) !== getAddress(signerAddress)) {
throw WithdrawalValidationError.feeReceiverMismatch(
`Fee recipient with extraGas mismatch: expected "${signerAddress}", got "${feeRecipient}".`,
);
}
} else {
if (getAddress(feeRecipient) !== feeReceiverAddress) {
throw WithdrawalValidationError.feeReceiverMismatch(
`Fee recipient mismatch: expected "${feeReceiverAddress}", got "${feeRecipient}".`,
);
}
if (getAddress(feeRecipient) !== feeReceiverAddress) {
throw WithdrawalValidationError.feeReceiverMismatch(
`Fee recipient mismatch: expected "${feeReceiverAddress}", got "${feeRecipient}".`,
);
}
const withdrawalContext = BigInt(
@@ -287,11 +217,7 @@ export class PrivacyPoolRelayer {
} else {
const currentFeeBPS = await quoteService.quoteFeeBPSNative({
chainId,
amountIn: proofSignals.withdrawnValue,
assetAddress,
baseFeeBPS: assetConfig.fee_bps,
extraGas
chainId, amountIn: proofSignals.withdrawnValue, assetAddress, baseFeeBPS: assetConfig.fee_bps, value: 0n
});
if (relayFeeBPS < currentFeeBPS) {
@@ -313,9 +239,9 @@ export class PrivacyPoolRelayer {
}
function commitmentExpired(feeCommitment: FeeCommitment): boolean {
return feeCommitment.expiration < Number(new Date());
return feeCommitment.expiration < Number(new Date())
}
async function validFeeCommitment(chainId: number, feeCommitment: FeeCommitment): Promise<boolean> {
return web3Provider.verifyRelayerCommitment(chainId, feeCommitment);
return web3Provider.verifyRelayerCommitment(chainId, feeCommitment)
}

View File

@@ -5,49 +5,39 @@ interface QuoteFeeBPSParams {
chainId: number,
assetAddress: Address,
amountIn: bigint,
baseFeeBPS: bigint,
extraGas: boolean;
value: bigint,
baseFeeBPS: bigint
};
const NativeAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
export class QuoteService {
readonly relayTxCost: bigint;
readonly extraGasTxCost: bigint;
readonly extraGasFundAmount: bigint;
readonly txCost: bigint;
constructor() {
// a typical withdrawal costs between 450k-650k gas
this.relayTxCost = 650_000n;
// approximate value of a uniswap Router call
this.extraGasTxCost = 200_000n;
// this gas will be transformed into equivalent native units at the time of the fund swap.
this.extraGasFundAmount = 600_000n;
this.txCost = 700_000n;
}
async netFeeBPSNative(baseFee: bigint, balance: bigint, nativeQuote: { num: bigint, den: bigint; }, gasPrice: bigint, extraGasUnits: bigint): Promise<bigint> {
const totalGasUnits = this.relayTxCost + extraGasUnits;
const nativeCosts = (1n * gasPrice * totalGasUnits);
async netFeeBPSNative(baseFee: bigint, balance: bigint, nativeQuote: { num: bigint, den: bigint }, gasPrice: bigint, value: bigint): Promise<bigint> {
const nativeCosts = (1n * gasPrice * this.txCost + value)
return baseFee + nativeQuote.den * 10_000n * nativeCosts / balance / nativeQuote.num;
}
async quoteFeeBPSNative(quoteParams: QuoteFeeBPSParams): Promise<bigint> {
const { chainId, assetAddress, amountIn, baseFeeBPS, extraGas } = quoteParams;
const { chainId, assetAddress, amountIn, baseFeeBPS, value } = quoteParams;
const gasPrice = await web3Provider.getGasPrice(chainId);
const EXTRA_GAS_AMOUNT = this.extraGasTxCost + this.extraGasFundAmount;
const extraGasUnits = extraGas ? EXTRA_GAS_AMOUNT : 0n;
let quote: { num: bigint, den: bigint; };
let quote: { num: bigint, den: bigint };
if (assetAddress.toLowerCase() === NativeAddress.toLowerCase()) {
quote = { num: 1n, den: 1n };
} else {
quote = await quoteProvider.quoteNativeTokenInERC20(chainId, assetAddress, amountIn);
}
const feeBPS = await this.netFeeBPSNative(baseFeeBPS, amountIn, quote, gasPrice, extraGasUnits);
return feeBPS;
const feeBPS = await this.netFeeBPSNative(baseFeeBPS, amountIn, quote, gasPrice, value);
return feeBPS
}
}

View File

@@ -1,7 +1,6 @@
import { Address } from "viem/accounts";
import { RelayerResponse } from "./interfaces/relayer/request.js";
import { QuoteResponse } from "./interfaces/relayer/quote.js";
import { FeeCommitment } from "./interfaces/relayer/common.js";
export abstract class RelayerMarshall {
abstract toJSON(): object;
@@ -53,11 +52,12 @@ export class QuoteMarshall extends RelayerMarshall {
super();
}
addFeeCommitment(feeCommitment: FeeCommitment) {
this.response.feeCommitment = {
...feeCommitment,
amount: feeCommitment.amount.toString()
};
addFeeCommitment(feeCommitment: {
expiration: number
withdrawalData: `0x${string}`,
signedRelayerCommitment: `0x${string}`
}) {
this.response.feeCommitment = feeCommitment;
}
override toJSON(): object {

View File

@@ -17,13 +17,11 @@ import {
WithdrawPublicSignals,
} from "./interfaces/relayer/request.js";
import { FeeDataAbi } from "./types/abi.types.js";
import { getFeeReceiverAddress, getSignerPrivateKey } from "./config/index.js";
import { privateKeyToAccount } from "viem/accounts";
interface WithdrawalData {
recipient: Address,
feeRecipient: Address,
relayFeeBPS: bigint;
relayFeeBPS: bigint
}
export function decodeWithdrawalData(data: `0x${string}`): WithdrawalData {
@@ -44,7 +42,7 @@ export function decodeWithdrawalData(data: `0x${string}`): WithdrawalData {
export function encodeWithdrawalData(withdrawalData: WithdrawalData): `0x${string}` {
try {
return encodeAbiParameters(FeeDataAbi, [withdrawalData]);
return encodeAbiParameters(FeeDataAbi, [withdrawalData])
} catch (e) {
const error = e as EncodeAbiParametersErrorType;
throw WithdrawalValidationError.invalidWithdrawalAbi({
@@ -88,7 +86,7 @@ export function createChainObject(chainConfig: {
chain_id: number;
chain_name: string;
rpc_url: string;
native_currency?: { name: string; symbol: string; decimals: number; };
native_currency?: { name: string; symbol: string; decimals: number };
}): Chain {
return {
id: chainConfig.chain_id,
@@ -109,16 +107,6 @@ export function isViemError(error: unknown): error is ViemError {
const viemErrorNames = [
ContractFunctionExecutionError.prototype.constructor.name,
ContractFunctionRevertedError.prototype.constructor.name,
];
]
return viemErrorNames.includes(error?.constructor?.name || "");
}
export function isFeeReceiverSameAsSigner(chainId: number) {
const feeReceiverAddress = getFeeReceiverAddress(chainId);
const signerAddress = privateKeyToAccount(getSignerPrivateKey(chainId) as `0x${string}`).address;
return feeReceiverAddress.toLowerCase() === signerAddress.toLowerCase();
}
export function isNative(asset: `0x${string}`) {
return asset.toLowerCase() === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
}

View File

@@ -4,18 +4,15 @@
"main": "index.js",
"license": "Apache-2.0",
"type": "module",
"scripts": {
"build": "tsc -p ./tsconfig.json",
"tree-cache": "node ./dist/main.js tree --fromBlock $(echo $(cast bn) - 50 | bc) --output ./tree-cache",
"main": "node dist/main.js"
},
"dependencies": {
"minimist": "1.2.8",
"viem": "2.22.14"
},
"scripts": {
"build": "tsc -p ./tsconfig.json",
"main": "node dist/main.js"
},
"devDependencies": {
"@types/minimist": "1.2.5",
"@0xbow/privacy-pools-core-sdk": "0.1.21",
"@0xbow/privacy-pools-core-sdk": "0.1.5",
"@types/node": "^22.13.0",
"typescript": "5.5.4"
}

View File

@@ -1,185 +0,0 @@
export const abi = [
{
"type": "function",
"name": "allowance",
"inputs": [
{
"name": "owner",
"type": "address",
"internalType": "address"
},
{
"name": "spender",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "approve",
"inputs": [
{
"name": "spender",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "balanceOf",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "totalSupply",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "transfer",
"inputs": [
{
"name": "to",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "transferFrom",
"inputs": [
{
"name": "from",
"type": "address",
"internalType": "address"
},
{
"name": "to",
"type": "address",
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"type": "event",
"name": "Approval",
"inputs": [
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "spender",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "Transfer",
"inputs": [
{
"name": "from",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "to",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "value",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
}
] as const;

View File

@@ -1,681 +0,0 @@
export const abi = [
{
"type": "function",
"name": "assetConfig",
"inputs": [
{
"name": "_asset",
"type": "address",
"internalType": "contract IERC20"
}
],
"outputs": [
{
"name": "_pool",
"type": "address",
"internalType": "contract IPrivacyPool"
},
{
"name": "_minimumDepositAmount",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_vettingFeeBPS",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_maxRelayFeeBPS",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "associationSets",
"inputs": [
{
"name": "_index",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "_root",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_ipfsCID",
"type": "string",
"internalType": "string"
},
{
"name": "_timestamp",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "deposit",
"inputs": [
{
"name": "_asset",
"type": "address",
"internalType": "contract IERC20"
},
{
"name": "_value",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_precommitment",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "_commitment",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "deposit",
"inputs": [
{
"name": "_precommitment",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "_commitment",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "payable"
},
{
"type": "function",
"name": "initialize",
"inputs": [
{
"name": "_owner",
"type": "address",
"internalType": "address"
},
{
"name": "_postman",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "latestRoot",
"inputs": [],
"outputs": [
{
"name": "_root",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "registerPool",
"inputs": [
{
"name": "_asset",
"type": "address",
"internalType": "contract IERC20"
},
{
"name": "_pool",
"type": "address",
"internalType": "contract IPrivacyPool"
},
{
"name": "_minimumDepositAmount",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_vettingFeeBPS",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_maxRelayFeeBPS",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "relay",
"inputs": [
{
"name": "_withdrawal",
"type": "tuple",
"internalType": "struct IPrivacyPool.Withdrawal",
"components": [
{
"name": "processooor",
"type": "address",
"internalType": "address"
},
{
"name": "data",
"type": "bytes",
"internalType": "bytes"
}
]
},
{
"name": "_proof",
"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]"
}
]
},
{
"name": "_scope",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "removePool",
"inputs": [
{
"name": "_asset",
"type": "address",
"internalType": "contract IERC20"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "rootByIndex",
"inputs": [
{
"name": "_index",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "_root",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "scopeToPool",
"inputs": [
{
"name": "_scope",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "_pool",
"type": "address",
"internalType": "contract IPrivacyPool"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "updatePoolConfiguration",
"inputs": [
{
"name": "_asset",
"type": "address",
"internalType": "contract IERC20"
},
{
"name": "_minimumDepositAmount",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_vettingFeeBPS",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_maxRelayFeeBPS",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "updateRoot",
"inputs": [
{
"name": "_root",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_ipfsCID",
"type": "string",
"internalType": "string"
}
],
"outputs": [
{
"name": "_index",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "windDownPool",
"inputs": [
{
"name": "_pool",
"type": "address",
"internalType": "contract IPrivacyPool"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "withdrawFees",
"inputs": [
{
"name": "_asset",
"type": "address",
"internalType": "contract IERC20"
},
{
"name": "_recipient",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "event",
"name": "Deposited",
"inputs": [
{
"name": "_depositor",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "_pool",
"type": "address",
"indexed": true,
"internalType": "contract IPrivacyPool"
},
{
"name": "_commitment",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "_amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "FeesWithdrawn",
"inputs": [
{
"name": "_asset",
"type": "address",
"indexed": false,
"internalType": "contract IERC20"
},
{
"name": "_recipient",
"type": "address",
"indexed": false,
"internalType": "address"
},
{
"name": "_amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "PoolConfigurationUpdated",
"inputs": [
{
"name": "_pool",
"type": "address",
"indexed": false,
"internalType": "contract IPrivacyPool"
},
{
"name": "_asset",
"type": "address",
"indexed": false,
"internalType": "contract IERC20"
},
{
"name": "_newMinimumDepositAmount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "_newVettingFeeBPS",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "_newMaxRelayFeeBPS",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "PoolRegistered",
"inputs": [
{
"name": "_pool",
"type": "address",
"indexed": false,
"internalType": "contract IPrivacyPool"
},
{
"name": "_asset",
"type": "address",
"indexed": false,
"internalType": "contract IERC20"
},
{
"name": "_scope",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "PoolRemoved",
"inputs": [
{
"name": "_pool",
"type": "address",
"indexed": false,
"internalType": "contract IPrivacyPool"
},
{
"name": "_asset",
"type": "address",
"indexed": false,
"internalType": "contract IERC20"
},
{
"name": "_scope",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "PoolWindDown",
"inputs": [
{
"name": "_pool",
"type": "address",
"indexed": false,
"internalType": "contract IPrivacyPool"
}
],
"anonymous": false
},
{
"type": "event",
"name": "RootUpdated",
"inputs": [
{
"name": "_root",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "_ipfsCID",
"type": "string",
"indexed": false,
"internalType": "string"
},
{
"name": "_timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "WithdrawalRelayed",
"inputs": [
{
"name": "_relayer",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "_recipient",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "_asset",
"type": "address",
"indexed": true,
"internalType": "contract IERC20"
},
{
"name": "_amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "_feeAmount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "error",
"name": "AssetMismatch",
"inputs": []
},
{
"type": "error",
"name": "AssetPoolAlreadyRegistered",
"inputs": []
},
{
"type": "error",
"name": "EmptyRoot",
"inputs": []
},
{
"type": "error",
"name": "InvalidEntrypointForPool",
"inputs": []
},
{
"type": "error",
"name": "InvalidFeeBPS",
"inputs": []
},
{
"type": "error",
"name": "InvalidIPFSCIDLength",
"inputs": []
},
{
"type": "error",
"name": "InvalidIndex",
"inputs": []
},
{
"type": "error",
"name": "InvalidPoolState",
"inputs": []
},
{
"type": "error",
"name": "InvalidProcessooor",
"inputs": []
},
{
"type": "error",
"name": "InvalidWithdrawalAmount",
"inputs": []
},
{
"type": "error",
"name": "MinimumDepositAmount",
"inputs": []
},
{
"type": "error",
"name": "NativeAssetNotAccepted",
"inputs": []
},
{
"type": "error",
"name": "NativeAssetTransferFailed",
"inputs": []
},
{
"type": "error",
"name": "NoRootsAvailable",
"inputs": []
},
{
"type": "error",
"name": "PoolIsDead",
"inputs": []
},
{
"type": "error",
"name": "PoolNotFound",
"inputs": []
},
{
"type": "error",
"name": "RelayFeeGreaterThanMax",
"inputs": []
},
{
"type": "error",
"name": "ScopePoolAlreadyRegistered",
"inputs": []
},
{
"type": "error",
"name": "ZeroAddress",
"inputs": []
}
] as const;

View File

@@ -1,561 +0,0 @@
export const abi = [
{
"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": "MAX_TREE_DEPTH",
"inputs": [],
"outputs": [
{
"name": "_maxDepth",
"type": "uint32",
"internalType": "uint32"
}
],
"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": "WITHDRAWAL_VERIFIER",
"inputs": [],
"outputs": [
{
"name": "_verifier",
"type": "address",
"internalType": "contract IVerifier"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "currentRoot",
"inputs": [],
"outputs": [
{
"name": "_root",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "currentRootIndex",
"inputs": [],
"outputs": [
{
"name": "_index",
"type": "uint32",
"internalType": "uint32"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "currentTreeDepth",
"inputs": [],
"outputs": [
{
"name": "_depth",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "currentTreeSize",
"inputs": [],
"outputs": [
{
"name": "_size",
"type": "uint256",
"internalType": "uint256"
}
],
"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": "depositors",
"inputs": [
{
"name": "_label",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "_depositor",
"type": "address",
"internalType": "address"
}
],
"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[4]",
"internalType": "uint256[4]"
}
]
}
],
"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": "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": "_precommitmentHash",
"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": "IncorrectASPRoot",
"inputs": []
},
{
"type": "error",
"name": "InvalidCommitment",
"inputs": []
},
{
"type": "error",
"name": "InvalidDepositValue",
"inputs": []
},
{
"type": "error",
"name": "InvalidProcessooor",
"inputs": []
},
{
"type": "error",
"name": "InvalidProof",
"inputs": []
},
{
"type": "error",
"name": "InvalidTreeDepth",
"inputs": []
},
{
"type": "error",
"name": "MaxTreeDepthReached",
"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

@@ -1,26 +1,26 @@
export const ping = async () => {
const r = await fetch("http://localhost:3000/ping", {
let r = await fetch("http://localhost:3000/ping", {
method: "get",
});
console.log(JSON.stringify(await r.text(), null, 2));
};
export const details = async () => {
const r = await fetch("http://localhost:3000/relayer/details", {
let r = await fetch("http://localhost:3000/relayer/details", {
method: "get",
});
console.log(JSON.stringify(await r.json(), null, 2));
};
export const notFound = async () => {
const r = await fetch("http://localhost:3000/HOLA", {
let r = await fetch("http://localhost:3000/HOLA", {
method: "get",
});
console.log(JSON.stringify(await r.json(), null, 2));
};
export const request = async (requestBody: object) => {
const r = await fetch("http://localhost:3000/relayer/request", {
export const request = async (requestBody) => {
let r = await fetch("http://localhost:3000/relayer/request", {
method: "post",
headers: {
"Content-Type": "application/json",
@@ -30,18 +30,8 @@ export const request = async (requestBody: object) => {
console.log(JSON.stringify(await r.json(), null, 2));
};
interface QuoteResponse {
baseFeeBPS: bigint,
feeBPS: bigint,
feeCommitment?: {
expiration: number,
withdrawalData: `0x${string}`,
signedRelayerCommitment: `0x${string}`,
};
}
export const quote = async (quoteBody: object) => {
const r = await fetch("http://localhost:3000/relayer/quote", {
export const quote = async (quoteBody) => {
let r = await fetch("http://localhost:3000/relayer/quote", {
method: "post",
headers: {
'Content-Type': 'application/json'
@@ -50,5 +40,5 @@ export const quote = async (quoteBody: object) => {
})
const quoteResponse = await r.json();
console.log(JSON.stringify(quoteResponse, null, 2))
return quoteResponse as QuoteResponse;
return quoteResponse;
}

View File

@@ -1,86 +1,16 @@
import { Account, createPublicClient, defineChain, getContract, GetContractReturnType, http, PublicClient } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { createPublicClient, defineChain, getContract, http, parseAbi } from "viem";
import { localhost } from "viem/chains";
import { abi as EntrypointAbi } from "./abis/Entrypoint.abi.js";
import { abi as Erc20Abi } from "./abis/ERC20.abi.js";
import { abi as PoolAbi } from "./abis/Pool.abi.js";
import { ENTRYPOINT_ADDRESS, LOCAL_ANVIL_RPC } from "./constants.js";
import { ETH_POOL_ADDRESS, LOCAL_ANVIL_RPC } from "./constants.js";
type PoolContract = GetContractReturnType<typeof PoolAbi, PublicClient, `0x${string}`>;
export const anvilChain = defineChain({ ...localhost, id: 31337 });
export interface IChainContext {
account: Account,
chain: ReturnType<typeof defineChain>;
client: PublicClient;
entrypoint: GetContractReturnType<typeof EntrypointAbi, PublicClient, `0x${string}`>,
getPoolContract: (asset: `0x${string}`) => Promise<PoolContract>;
getPoolContractByScope: (scope: bigint) => PoolContract;
getErc20Contract: (asset: `0x${string}`) => GetContractReturnType<typeof Erc20Abi, PublicClient, `0x${string}`>;
}
export function ChainContext(chainId: number, privateKey: `0x${string}`): IChainContext {
const _poolCacheByAsset: { [key: `0x${string}`]: PoolContract; } = {};
const _poolCacheByScope: { [key: string]: PoolContract; } = {};
const anvilChain = defineChain({ ...localhost, id: chainId });
const publicClient = createPublicClient({
chain: anvilChain,
transport: http(LOCAL_ANVIL_RPC),
});
const entrypoint = getContract({
address: ENTRYPOINT_ADDRESS,
abi: EntrypointAbi,
client: publicClient,
});
async function getPoolContract(asset: `0x${string}`) {
const cachedPool = _poolCacheByAsset[asset];
if (cachedPool !== undefined)
return cachedPool;
const [
poolAddress,
_minimumDepositAmount, // eslint-disable-line @typescript-eslint/no-unused-vars
_vettingFeeBPS, // eslint-disable-line @typescript-eslint/no-unused-vars
_maxRelayFeeBPS // eslint-disable-line @typescript-eslint/no-unused-vars
] = await entrypoint.read.assetConfig([asset]);
const pool = getContract({
address: poolAddress,
abi: PoolAbi,
client: publicClient,
});
const scope = await pool.read.SCOPE();
_poolCacheByAsset[asset] = pool;
_poolCacheByScope[scope.toString()] = pool;
return pool;
}
function getPoolContractByScope(scope: bigint) {
const cachedPool = _poolCacheByScope[scope.toString()];
if (cachedPool !== undefined)
return cachedPool;
throw Error("Pool is not instantiated")
}
function getErc20Contract(asset: `0x${string}`) {
return getContract({
address: asset,
client: publicClient,
abi: Erc20Abi
});
}
return {
account: privateKeyToAccount(privateKey),
chain: anvilChain,
client: publicClient,
entrypoint,
getPoolContract,
getErc20Contract,
getPoolContractByScope
};
}
export const publicClient = createPublicClient({
chain: anvilChain,
transport: http(LOCAL_ANVIL_RPC),
});
export const pool = getContract({
address: ETH_POOL_ADDRESS,
abi: parseAbi(["function SCOPE() view returns (uint256)"]),
client: publicClient,
})

View File

@@ -1,270 +0,0 @@
import minimist from 'minimist';
import { getAddress } from 'viem';
import { quote, request } from "./api-test.js";
import { ChainContext } from "./chain.js";
import { feeRecipient, PRIVATE_KEY, processooor, recipient } from "./constants.js";
import { encodeFeeData, isNative } from "./util.js";
import { SdkWrapper } from './sdk-wrapper.js';
import * as fs from "fs";
interface Context {
chainId: number;
privateKey: `0x${string}`;
}
interface DepositCli {
context: Context;
accNonce: bigint;
amount: bigint;
asset: `0x${string}`;
}
export async function depositCli({ accNonce, amount, asset, context }: DepositCli) {
const { chainId, privateKey } = context;
const sdkWrapper = new SdkWrapper(ChainContext(chainId, privateKey));
let r;
if (isNative(asset)) {
r = await sdkWrapper.deposit(accNonce, amount);
} else {
r = await sdkWrapper.depositAsset(accNonce, amount, asset);
}
await r.wait();
console.log(`Successful deposit, hash := ${r.hash}`);
}
interface QuoteCli {
context: Context;
asset: `0x${string}`;
amount: bigint;
extraGas: boolean;
}
export async function quoteCli({ context, asset, amount, extraGas }: QuoteCli) {
return quote({
chainId: context.chainId,
amount: amount.toString(),
asset,
recipient,
extraGas
});
}
interface RelayCli {
context: Context;
asset: `0x${string}`;
withQuote: boolean;
extraGas: boolean;
amount: bigint;
fromDeposit: boolean;
fromLabel?: bigint;
accNonce: bigint;
value: bigint;
leaves: {
index: bigint;
leaf: bigint;
root: bigint;
block: bigint;
}[];
}
export async function relayCli({ asset, withQuote, amount, extraGas, fromDeposit, fromLabel, accNonce, value, context, leaves }: RelayCli) {
const { chainId, privateKey } = context;
const sdkWrapper = new SdkWrapper(ChainContext(chainId, privateKey));
const pool = await sdkWrapper.chainContext.getPoolContract(asset);
const scope = await pool.read.SCOPE();
let note: { nullifier: bigint, secret: bigint; };
let newNote: { nullifier: bigint, secret: bigint; } | undefined;
let label: bigint;
if (fromDeposit) {
note = sdkWrapper.depositSecret(scope, accNonce);
const noteLabel = await sdkWrapper.findLabelFromDepositNote(asset, note);
label = noteLabel;
newNote = sdkWrapper.withdrawSecret(noteLabel, 0n);
} else if (fromLabel !== undefined) {
note = sdkWrapper.withdrawSecret(fromLabel, accNonce);
newNote = sdkWrapper.withdrawSecret(fromLabel, accNonce + 1n);
label = fromLabel;
} else {
throw new Error("No deposit or label");
}
console.log("note", note);
console.log("newnote", newNote);
// 0.1 ETH or 1.5 dollars
const withdrawAmount = amount;
let data;
let feeCommitment = undefined;
if (withQuote) {
const quoteRes = await quote({
chainId,
amount: withdrawAmount.toString(),
asset,
recipient,
extraGas
});
data = quoteRes.feeCommitment!.withdrawalData as `0x${string}`;
feeCommitment = {
...quoteRes.feeCommitment,
};
} else {
data = encodeFeeData({ recipient, feeRecipient, relayFeeBPS: 100n });
}
const withdrawal = { processooor, data };
// prove
const { proof, publicSignals } = await sdkWrapper.proveWithdrawal(withdrawAmount, withdrawal, scope, label, value, note, newNote, leaves);
const requestBody = {
scope: scope.toString(),
chainId: sdkWrapper.chainContext.chain.id,
withdrawal,
publicSignals,
proof,
feeCommitment
};
await request(requestBody);
}
interface DefArgs {
_: string[],
chainId: number;
privateKey: `0x${string}`;
}
export async function cli() {
let args = minimist(process.argv.slice(2), {
string: ["asset", "fromLabel", "accNonce", "output"],
boolean: ["quote", "extraGas", "fromDeposit"],
alias: {
"private-key": "privateKey",
"chain-id": "chainId",
"from-deposit": "fromDeposit",
"acc-nonce": "accNonce",
"from-label": "fromLabel",
"cache-file": "cacheFile"
},
default: {
"chainId": process.env["CHAIN_ID"] || 1115511,
"privateKey": process.env["PRIVATE_KEY"] || PRIVATE_KEY,
"asset": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"extraGas": true,
"quote": false,
"fromDeposit": false,
}
});
const action = process.argv[2]!;
const actions = [
"deposit",
"quote",
"relay",
"tree"
];
if (!actions.includes(action)) {
console.log("No action selected");
process.exit(0);
}
const context = { chainId: Number.parseInt(args.chainId), privateKey: args.privateKey };
switch (action) {
case "deposit": {
args = args as DefArgs & { amount: string, asset?: string; note: string; };
const r = await depositCli({ accNonce: BigInt(args.accNonce), amount: BigInt(args.amount), asset: args.asset, context });
console.log(r);
break;
}
case "quote": {
if (args.length < 3) {
throw Error("Not enough args");
}
args = args as DefArgs & { amount: string, asset: string; extraGas: boolean; };
await quoteCli({
context,
asset: getAddress(args.asset),
amount: BigInt(args.amount),
extraGas: args.extraGas
});
break;
}
case "relay": {
args = args as DefArgs & {
amount: string;
asset: string;
quote: boolean;
extraGas: boolean;
fromDeposit: boolean;
fromLabel: string;
accNonce: string;
value: string;
cacheFile: string;
};
await relayCli({
context,
asset: getAddress(args.asset),
amount: BigInt(args.amount),
extraGas: args.extraGas,
withQuote: args.quote,
fromDeposit: args.fromDeposit,
fromLabel: args.fromLabel ? BigInt(args.fromLabel) : undefined,
accNonce: BigInt(args.accNonce),
value: BigInt(args.value),
leaves: readLeavesFromFile(args.cacheFile),
});
break;
}
case "tree": {
args = args as DefArgs & { fromBlock: string; asset: string; output?: string; };
buildTreeCache({ context, asset: args.asset, fromBlock: args.fromBlock, output: args.output });
break;
}
case undefined: {
console.log("No action selected");
break;
}
}
}
async function buildTreeCache({ context, asset, fromBlock, output }: { fromBlock: string; asset: string; output?: string; } & { context: Context; }) {
console.log("Building tree");
const { chainId, privateKey } = context;
const sdkWrapper = new SdkWrapper(ChainContext(chainId, privateKey));
const pool = await sdkWrapper.chainContext.getPoolContract(asset as `0x${string}`);
const leavesRaw = await pool.getEvents.LeafInserted({ fromBlock: BigInt(fromBlock) });
const leaves = leavesRaw.map(l => ({
index: l.args._index!.toString(),
leaf: l.args._leaf!.toString(),
root: l.args._root!.toString(),
block: l.blockNumber.toString()
}));
const timestamp = (new Date()).toISOString().replaceAll(":", "_").replace(new RegExp(".[0-9]{3}Z"), "");
const treeFileName = output || `./tree-cache-${timestamp}.json`;
fs.writeFileSync(treeFileName, JSON.stringify(leaves, null, 2));
console.log(`Wrote ${leaves.length} leaves to file ${treeFileName}`);
}
function readLeavesFromFile(filePath: string) {
const rawLeaves = JSON.parse(fs.readFileSync(filePath, { encoding: 'utf-8' })) as { index: string, leaf: string, root: string, block: string; }[];
return rawLeaves.map(l => ({
index: BigInt(l.index),
leaf: BigInt(l.leaf),
root: BigInt(l.root),
block: BigInt(l.block),
}));
}

View File

@@ -1,18 +1,12 @@
import { Address, getAddress, Hex } from "viem";
import { Address, Hex } from "viem";
// // mainnet
// export const ENTRYPOINT_ADDRESS: Address = "0x6818809EefCe719E480a7526D76bD3e561526b46";
// export const ETH_POOL_ADDRESS: Address = "0xF241d57C6DebAe225c0F2e6eA1529373C9A9C9fB";
// sepolia (localnet)
export const ENTRYPOINT_ADDRESS: Address = "0x1fF2EA3C98E22d5589d66829a1599cB74b566E94";
// localnet
export const ENTRYPOINT_ADDRESS: Address = "0xd6DB18A83F9eE4e2d0FC8D6BEd075A2905A83FDA";
export const ETH_POOL_ADDRESS: Address = "0x4Cb503503047b66aA5e64b9BDC8148E769ac52f6";
export const LOCAL_ANVIL_RPC = "http://127.0.0.1:8545";
// export const PRIVATE_KEY: Hex = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
export const PRIVATE_KEY: Hex = "0xa278275fee36ebb6f4689e79bf1a8b4650c9aec0fc39e03c111461e5b08730eb"; // 0xb9edc9DD585C13891F5B2dE85f182d3Ea4AaEa09
export const processooor = ENTRYPOINT_ADDRESS;
export const feeRecipient = getAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
export const recipient = getAddress("0xabA6aeB1bCFF1096f4b8148085C4231FED9FE8E4");
export const PRIVATE_KEY: Hex = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";

View File

@@ -0,0 +1,170 @@
import {
bigintToHash,
calculateContext,
Circuits,
getCommitment,
hashPrecommitment,
LeanIMTMerkleProof,
PrivacyPoolSDK,
Secret,
Withdrawal,
WithdrawalProof,
WithdrawalProofInput,
Hash,
} from "@0xbow/privacy-pools-core-sdk";
import {
ENTRYPOINT_ADDRESS,
LOCAL_ANVIL_RPC,
PRIVATE_KEY,
} from "./constants.js";
import { anvilChain } from "./chain.js";
/*
TestToken deployed at: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
Withdrawal Verifier deployed at: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
Ragequit Verifier deployed at: 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9
Entrypoint deployed at: 0x0165878A594ca255338adfa4d48449f69242Eb8F
ETH Pool deployed at: 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
TST Pool deployed at: 0x8A791620dd6260079BF849Dc5567aDC3F2FdC318
*/
const sdk = new PrivacyPoolSDK(new Circuits({ browser: false }));
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,
scope: bigint,
): Promise<WithdrawalProof> {
try {
console.log("🚀 Initializing PrivacyPoolSDK...");
// **Retrieve On-Chain Scope**
console.log(
"🔹 Retrieved Scope from Withdrawal:",
`0x${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, scope as Hash);
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

@@ -1,7 +1,138 @@
import { cli } from "./cli.js";
import { Hash, Withdrawal } from "@0xbow/privacy-pools-core-sdk";
import { encodeAbiParameters, getAddress, Hex } from "viem";
import { request, quote } from "./api-test.js";
import { anvilChain, pool } from "./chain.js";
import { ENTRYPOINT_ADDRESS } from "./constants.js";
import { deposit, proveWithdrawal } from "./create-withdrawal.js";
interface QuoteResponse {
baseFeeBPS: bigint,
feeBPS: bigint,
feeCommitment?: {
expiration: number,
withdrawalData: `0x${string}`,
signedRelayerCommitment: `0x${string}`,
}
}
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 = ENTRYPOINT_ADDRESS;
const FEE_RECEIVER_ADDRESS = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
async function prove(w: Withdrawal, scope: bigint) {
return proveWithdrawal(w, scope);
}
async function depositCli() {
const r = await deposit();
await r.wait();
console.log(`Successful deposit, hash := ${r.hash}`);
}
async function quoteReq(chainId: number, asset: string, recipient: string, amount: string) {
return (await quote({
chainId,
amount,
asset,
recipient
}) as QuoteResponse);
}
async function quoteCli(chainId: string, asset: string, amount?: string) {
const _amount = amount ? Number(amount) : 100_000_000_000_000_000n
quoteReq(Number(chainId), asset, recipient, _amount.toString())
}
async function relayCli(chainId: string, asset: string, withQuote: boolean) {
const scope = await pool.read.SCOPE() as Hash;
let data;
let feeCommitment = undefined;
if (withQuote) {
const amount = "100000000000000000"; // 0.1 ETH
const quoteRes = await quoteReq(Number(chainId), asset, recipient, amount);
data = quoteRes.feeCommitment!.withdrawalData as Hex
feeCommitment = {
...quoteRes.feeCommitment,
};
} else {
data = encodeAbiParameters(FeeDataAbi, [
{
recipient,
feeRecipient: FEE_RECEIVER_ADDRESS,
relayFeeBPS: 100n,
},
]) as Hex;
}
const withdrawal = { processooor, data };
// prove
const { proof, publicSignals } = await prove(withdrawal, scope);
const requestBody = {
scope: scope.toString(),
chainId: anvilChain.id,
withdrawal,
publicSignals,
proof,
feeCommitment
};
await request(requestBody);
}
async function cli() {
const args = process.argv.slice(2)
const action = args[0];
switch (action) {
case "deposit": {
console.log(action)
await depositCli();
break;
}
case "quote": {
console.log(action)
if (args.length < 3) {
throw Error("Not enough args")
}
await quoteCli(args[1]!, args[2]!, args[3])
break;
}
case "relay": {
console.log(...args)
const withQuote = args.includes("--with-quote")
const noFlags = args.slice(1).filter(a => a !== "--with-quote")
if (noFlags.length < 2) {
throw Error("Not enough args")
}
await relayCli(noFlags[0]!, noFlags[1]!, withQuote);
break;
}
case undefined: {
console.log("No action selected")
break;
}
}
}
(async () => {
await cli();
cli();
})();

View File

@@ -1,272 +0,0 @@
import {
bigintToHash,
calculateContext,
Circuits,
ContractInteractionsService,
generateDepositSecrets,
generateMasterKeys,
generateMerkleProof,
getCommitment,
Hash,
hashPrecommitment,
LeanIMTMerkleProof,
MasterKeys,
PrivacyPoolSDK,
Secret,
Withdrawal,
WithdrawalProof,
WithdrawalProofInput
} from "@0xbow/privacy-pools-core-sdk";
import { IChainContext } from "./chain.js";
import {
ENTRYPOINT_ADDRESS,
PRIVATE_KEY
} from "./constants.js";
type Note = { nullifier: bigint, secret: bigint; };
/*
TestToken deployed at: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
Withdrawal Verifier deployed at: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
Ragequit Verifier deployed at: 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9
Entrypoint deployed at: 0x0165878A594ca255338adfa4d48449f69242Eb8F
ETH Pool deployed at: 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
TST Pool deployed at: 0x8A791620dd6260079BF849Dc5567aDC3F2FdC318
*/
export class SdkWrapper {
chainContext: IChainContext;
sdk: PrivacyPoolSDK;
contracts: ContractInteractionsService;
mnemonic: string;
masterKeys: MasterKeys;
constructor(chainContext: IChainContext) {
this.chainContext = chainContext;
this.sdk = new PrivacyPoolSDK(new Circuits({ browser: false }));
this.contracts = this.sdk.createContractInstance(
this.chainContext.client.transport.url,
this.chainContext.chain,
ENTRYPOINT_ADDRESS,
PRIVATE_KEY,
);
this.mnemonic = "muscle horse fly praise focus mixed annual disorder false black bottom uncover";
this.masterKeys = generateMasterKeys(this.mnemonic);
}
depositSecret(scope: bigint, index: bigint) {
return generateDepositSecrets(this.masterKeys, scope as Hash, index);
}
withdrawSecret(label: bigint, index: bigint) {
return generateDepositSecrets(this.masterKeys, label as Hash, index);
}
async findLabelFromDepositNote(asset: `0x${string}`, note: { nullifier: bigint; secret: bigint; }): Promise<bigint> {
const pool = await this.chainContext.getPoolContract(asset);
const depositEvents = await pool.getEvents.Deposited(undefined, { fromBlock: (await this.chainContext.client.getBlockNumber()) - 50n });
const preCommitment = hashPrecommitment(note.nullifier as Secret, note.secret as Secret);
const event = depositEvents.filter(de => de.args._precommitmentHash === preCommitment).pop();
if (event && event?.args?._label !== undefined) {
return event.args._label;
} else {
throw Error("Can't find matching label");
}
}
async deposit(accNonce: bigint, amount: bigint) {
const pool = await this.chainContext.getPoolContract("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE");
const scope = await pool.read.SCOPE() as Hash;
const { secret, nullifier } = this.depositSecret(scope, accNonce);
const precommitment = {
hash: hashPrecommitment(nullifier!, secret!),
nullifier: secret,
secret: nullifier,
};
const tx = await this.contracts.depositETH(amount, precommitment.hash);
await tx.wait();
const depositEvents = await pool.getEvents.Deposited({ _depositor: this.chainContext.account.address });
depositEvents.forEach(e => {
console.log("Deposited<", {
...e.args,
blockNumber: e.blockNumber,
blockHash: e.blockHash
}, ">");
});
return tx;
}
async depositAsset(accNonce: bigint, amount: bigint, assetAddress: `0x${string}`) {
const pool = await this.chainContext.getPoolContract(assetAddress);
const scope = await pool.read.SCOPE() as Hash;
const { secret, nullifier } = this.depositSecret(scope, accNonce);
const precommitment = {
hash: hashPrecommitment(nullifier!, secret!),
nullifier: secret,
secret: nullifier,
};
const erc20 = this.chainContext.getErc20Contract(assetAddress);
await erc20.write.approve([ENTRYPOINT_ADDRESS, 2n ** 256n - 1n], {
account: this.chainContext.account,
chain: this.chainContext.chain
});
const tx = await this.contracts.depositERC20(assetAddress, amount, precommitment.hash);
await tx.wait();
const depositEvents = await pool.getEvents.Deposited({ _depositor: this.chainContext.account.address });
depositEvents.forEach(e => {
console.log("Deposited<", {
...e.args,
blockNumber: e.blockNumber,
blockHash: e.blockHash
}, ">");
});
return tx;
}
async proveWithdrawal(
withdrawAmount: bigint,
w: Withdrawal,
scope: bigint,
label: bigint,
oldNoteValue: bigint,
oldNote: Note,
newNote: Note,
leaves: {
index: bigint;
leaf: bigint;
root: bigint;
block: bigint;
}[]
): Promise<WithdrawalProof> {
try {
console.log("🚀 Initializing PrivacyPoolSDK...");
// **Retrieve On-Chain Scope**
console.log(
"🔹 Retrieved Scope from Withdrawal:",
`0x${scope.toString(16)}`,
);
const pool = this.chainContext.getPoolContractByScope(scope);
// **Load Valid Input Values**
const stateTreeDepth = await pool.read.currentTreeDepth();
// pool.read.currentTreeSize();
const stateRoot = await pool.read.currentRoot();
// const stateRoot = BigInt(
// "11647068014638404411083963959916324311405860401109309104995569418439086324505",
// );
// const stateTreeDepth = BigInt("2");
const { secret: existingSecret, nullifier: existingNullifier } = oldNote;
const { secret: newSecret, nullifier: newNullifier } = newNote;
console.log("🛠️ Generating commitments...");
const commitment = getCommitment(
oldNoteValue,
label,
existingNullifier as Secret,
existingSecret as Secret,
);
const sortedLeaves = leaves.sort((a, b) => Number(a.index - b.index)).map(x => x.leaf);
// **State Merkle Proof**
const stateMerkleProof: LeanIMTMerkleProof = generateMerkleProof(sortedLeaves, commitment.hash);
stateMerkleProof.index = Number.isNaN(stateMerkleProof.index) ? 0 : stateMerkleProof.index;
if (stateMerkleProof.siblings.length < 32) {
const N = 32 - stateMerkleProof.siblings.length;
const siblings = [...stateMerkleProof.siblings, ...Array(N).fill(0n)];
stateMerkleProof.siblings = siblings;
}
stateMerkleProof.siblings = stateMerkleProof.siblings.length === 0 ? [stateRoot, ...Array(31).fill(0n)] : stateMerkleProof.siblings;
console.log(stateMerkleProof);
// const stateMerkleProof: LeanIMTMerkleProof = {
// root: stateRoot,
// leaf: commitment.hash,
// index: 3,
// siblings: [
// BigInt("6398878698952029"),
// BigInt(
// "13585012987205807684735841540436202984635744455909835202346884556845854938903",
// ),
// ...Array(30).fill(BigInt(0)),
// ],
// };
// const aspRoot = BigInt(
// "17509119559942543382744731935952318540675152427220720285867932301410542597330",
// );
// const aspTreeDepth = BigInt("2");
const firstSib = 1n;
const aspRoot = hashPrecommitment(label as Secret, firstSib as Secret);
const aspTreeDepth = 2n;
// **ASP Merkle Proof**
const aspMerkleProof: LeanIMTMerkleProof = {
root: aspRoot,
leaf: label,
index: 0,
siblings: [
firstSib,
...Array(31).fill(BigInt(0)),
// 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, scope as Hash);
console.log("🔹 Computed Context:", computedContext.toString());
// **Create Withdrawal Proof Input**
const proofInput: WithdrawalProofInput = {
context: BigInt(computedContext),
withdrawalAmount: withdrawAmount,
stateMerkleProof: stateMerkleProof,
aspMerkleProof: aspMerkleProof,
stateRoot: bigintToHash(stateRoot),
stateTreeDepth: stateTreeDepth,
aspRoot: bigintToHash(aspRoot),
aspTreeDepth: aspTreeDepth,
newSecret: newSecret as Secret,
newNullifier: newNullifier as Secret,
};
console.log("🚀 Generating withdrawal proof...");
const proofPayload: WithdrawalProof = await this.sdk.proveWithdrawal(
commitment,
proofInput,
);
console.log(proofPayload)
return proofPayload;
} catch (error) {
console.error("❌ **Error running testWithdraw script**:", error);
process.exit(1);
}
}
}

View File

@@ -1,31 +0,0 @@
import { encodeAbiParameters } from "viem";
const FeeDataAbi = [
{
name: "FeeData",
type: "tuple",
components: [
{ name: "recipient", type: "address" },
{ name: "feeRecipient", type: "address" },
{ name: "relayFeeBPS", type: "uint256" },
],
},
];
export function encodeFeeData({
recipient, feeRecipient, relayFeeBPS
}: {
recipient: `0x${string}`; feeRecipient: `0x${string}`; relayFeeBPS: bigint;
}) {
return encodeAbiParameters(FeeDataAbi, [
{
recipient,
feeRecipient,
relayFeeBPS,
},
]);
}
export function isNative(asset: `0x${string}`) {
return asset.toLowerCase() === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
}

View File

@@ -2,48 +2,11 @@
# yarn lockfile v1
"@0xbow/privacy-pools-core-sdk@0.1.21":
version "0.1.21"
resolved "https://registry.yarnpkg.com/@0xbow/privacy-pools-core-sdk/-/privacy-pools-core-sdk-0.1.21.tgz#8811694288525c7a12f14150fbea085c6da88b59"
integrity sha512-UTNPHSkdR1XdQoU2u7XLaGcRCh09WP9C9yIlXrUgF0SUkiaaE2ZnyUIrhtT/oW7/zreYNZArWv8v647uVSNCzw==
dependencies:
"@types/snarkjs" "0.7.9"
"@zk-kit/lean-imt" "2.2.2"
maci-crypto "2.5.0"
snarkjs "0.7.5"
typescript "^5.7.3"
viem "2.22.14"
"@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==
"@iden3/bigarray@0.0.2":
version "0.0.2"
resolved "https://registry.yarnpkg.com/@iden3/bigarray/-/bigarray-0.0.2.tgz#6fc4ba5be18daf8a26ee393f2fb62b80d98c05e9"
integrity sha512-Xzdyxqm1bOFF6pdIsiHLLl3HkSLjbhqJHVyqaTxXt3RqXBEnmsUmEW47H7VOi/ak7TdkRpNkxjyK5Zbkm+y52g==
"@iden3/binfileutils@0.0.12":
version "0.0.12"
resolved "https://registry.yarnpkg.com/@iden3/binfileutils/-/binfileutils-0.0.12.tgz#3772552f57551814ff606fa68ea1e0ef52795ce3"
integrity sha512-naAmzuDufRIcoNfQ1d99d7hGHufLA3wZSibtr4dMe6ZeiOPV1KwOZWTJ1YVz4HbaWlpDuzVU72dS4ATQS4PXBQ==
dependencies:
fastfile "0.0.20"
ffjavascript "^0.3.0"
"@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"
@@ -51,11 +14,6 @@
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"
@@ -83,18 +41,6 @@
"@noble/hashes" "~1.7.1"
"@scure/base" "~1.2.4"
"@types/minimist@1.2.8":
version "1.2.5"
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e"
integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==
"@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/node@^22.13.0":
version "22.13.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.0.tgz#d376dd9a0ee2f9382d86c2d5d7beb4d198b4ea8c"
@@ -102,377 +48,21 @@
dependencies:
undici-types "~6.20.0"
"@types/snarkjs@0.7.9":
version "0.7.9"
resolved "https://registry.yarnpkg.com/@types/snarkjs/-/snarkjs-0.7.9.tgz#7a3b99bd86009133a74dcb215a475382c772c37c"
integrity sha512-pb4Bq3GI2YQOQOG0dR/YuQs/mqcuL6k/vnz68LIPtpA2frrUL3twf69a3AUK9eUmNNeW0RIKkq6scDlC75Is+g==
"@zk-kit/baby-jubjub@1.0.3", "@zk-kit/baby-jubjub@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@zk-kit/baby-jubjub/-/baby-jubjub-1.0.3.tgz#8d2eccd20d729f1dbd39203dbff9a245a61dea76"
integrity sha512-Wl+QfV6XGOMk1yU2JTqHXeKWfJVXp83is0+dtqfj9wx4wsAPpb+qzYvwAxW5PBx5/Nu71Bh7jp/5vM+6QgHSwA==
dependencies:
"@zk-kit/utils" "1.2.1"
"@zk-kit/eddsa-poseidon@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@zk-kit/eddsa-poseidon/-/eddsa-poseidon-1.1.0.tgz#08ef95ccbb2fbb5260617b6b5120dfb25a25229b"
integrity sha512-Djc+zOZjd73FpLLf32/XeVZi8GX4ShPQJGS4Iig1QMAR/2CggEi++6Jrkr9N2FM3M4MRCH1qxz2u22DjOLtASg==
dependencies:
"@zk-kit/baby-jubjub" "1.0.3"
"@zk-kit/utils" "1.2.1"
blakejs "^1.2.1"
buffer "6.0.3"
poseidon-lite "0.3.0"
"@zk-kit/lean-imt@2.2.2":
version "2.2.2"
resolved "https://registry.yarnpkg.com/@zk-kit/lean-imt/-/lean-imt-2.2.2.tgz#79c8bd70fc0d444638328cb4781479b14c69a9dd"
integrity sha512-rscIPEgBBcu9vP/DJ3J+3187G/ObKETl343G5enPawNT81oeQSdHx3e2ZapTC+GfrZ/AS2AHHUOpRS1FfdSwjg==
dependencies:
"@zk-kit/utils" "1.2.1"
"@zk-kit/poseidon-cipher@^0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@zk-kit/poseidon-cipher/-/poseidon-cipher-0.3.2.tgz#a2afcc1e4fcfa9db3b245d584183cd7fb7fcd4ab"
integrity sha512-Ezz1e0mj/GRDlHdU5m0uhj5iHY72zWJU0BP8DsCCvPubU7LPI2tVaPpxrAjT4JQqatbVRQHLIhixW7F7BPzaFg==
dependencies:
"@zk-kit/baby-jubjub" "1.0.3"
"@zk-kit/utils" "1.2.1"
"@zk-kit/utils@1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@zk-kit/utils/-/utils-1.2.1.tgz#6cb38120535c73ab68cd0f09684882af148f256d"
integrity sha512-H2nTsyWdicVOyvqC5AjgU7tsTgmR6PDrruFJNmlmdhKp7RxEia/E1B1swMZjaasYa2QMp4Zc6oB7cWchty7B2Q==
dependencies:
buffer "^6.0.3"
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==
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==
ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
async@^3.2.3:
version "3.2.6"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
b4a@^1.0.1:
version "1.6.7"
resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4"
integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
bfj@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.1.0.tgz#c5177d522103f9040e1b12980fe8c38cf41d3f8b"
integrity sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==
dependencies:
bluebird "^3.7.2"
check-types "^11.2.3"
hoopy "^0.1.4"
jsonpath "^1.1.1"
tryer "^1.0.1"
blake2b-wasm@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz#9115649111edbbd87eb24ce7c04b427e4e2be5be"
integrity sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w==
dependencies:
b4a "^1.0.1"
nanoassert "^2.0.0"
blakejs@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814"
integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==
bluebird@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
buffer@6.0.3, buffer@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
chalk@^4.0.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
check-types@^11.2.3:
version "11.2.3"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.3.tgz#1ffdf68faae4e941fce252840b1787b8edc93b71"
integrity sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==
circom_runtime@0.1.28:
version "0.1.28"
resolved "https://registry.yarnpkg.com/circom_runtime/-/circom_runtime-0.1.28.tgz#4ea4606956eeac4499f71f65354f45b54faa93fe"
integrity sha512-ACagpQ7zBRLKDl5xRZ4KpmYIcZDUjOiNRuxvXLqhnnlLSVY1Dbvh73TI853nqoR0oEbihtWmMSjgc5f+pXf/jQ==
dependencies:
ffjavascript "0.3.1"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
deep-is@~0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
ejs@^3.1.6:
version "3.1.10"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b"
integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==
dependencies:
jake "^10.8.5"
escodegen@^1.8.1:
version "1.14.3"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503"
integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==
dependencies:
esprima "^4.0.1"
estraverse "^4.2.0"
esutils "^2.0.2"
optionator "^0.8.1"
optionalDependencies:
source-map "~0.6.1"
esprima@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.2.tgz#76a0fd66fcfe154fd292667dc264019750b1657b"
integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==
esprima@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
estraverse@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
ethers@^6.13.4:
version "6.14.3"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.14.3.tgz#7c4443c165ee59b2964e691600fd4586004b2000"
integrity sha512-qq7ft/oCJohoTcsNPFaXSQUm457MA5iWqkf1Mb11ujONdg7jBI6sAOrHaTi3j0CBqIGFSCeR/RMc+qwRRub7IA==
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==
fast-levenshtein@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fastfile@0.0.20:
version "0.0.20"
resolved "https://registry.yarnpkg.com/fastfile/-/fastfile-0.0.20.tgz#794a143d58cfda2e24c298e5ef619c748c8a1879"
integrity sha512-r5ZDbgImvVWCP0lA/cGNgQcZqR+aYdFx3u+CtJqUE510pBUVGMn4ulL/iRTI4tACTYsNJ736uzFxEBXesPAktA==
ffjavascript@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.3.0.tgz#442cd8fbb1ee4cbb1be9d26fd7b2951a1ea45d6a"
integrity sha512-l7sR5kmU3gRwDy8g0Z2tYBXy5ttmafRPFOqY7S6af5cq51JqJWt5eQ/lSR/rs2wQNbDYaYlQr5O+OSUf/oMLoQ==
dependencies:
wasmbuilder "0.0.16"
wasmcurves "0.2.2"
web-worker "1.2.0"
ffjavascript@0.3.1, ffjavascript@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.3.1.tgz#3761bbb3f4a67b58a94a463080272bf6b5877b03"
integrity sha512-4PbK1WYodQtuF47D4pRI5KUg3Q392vuP5WjE1THSnceHdXwU3ijaoS0OqxTzLknCtz4Z2TtABzkBdBdMn3B/Aw==
dependencies:
wasmbuilder "0.0.16"
wasmcurves "0.2.2"
web-worker "1.2.0"
filelist@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
dependencies:
minimatch "^5.0.1"
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
hoopy@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d"
integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==
ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
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==
jake@^10.8.5:
version "10.9.2"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f"
integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==
dependencies:
async "^3.2.3"
chalk "^4.0.2"
filelist "^1.0.4"
minimatch "^3.1.2"
js-sha3@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
jsonpath@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/jsonpath/-/jsonpath-1.1.1.tgz#0ca1ed8fb65bb3309248cc9d5466d12d5b0b9901"
integrity sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==
dependencies:
esprima "1.2.2"
static-eval "2.0.2"
underscore "1.12.1"
levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==
dependencies:
prelude-ls "~1.1.2"
type-check "~0.3.2"
logplease@^1.2.15:
version "1.2.15"
resolved "https://registry.yarnpkg.com/logplease/-/logplease-1.2.15.tgz#3da442e93751a5992cc19010a826b08d0293c48a"
integrity sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==
maci-crypto@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/maci-crypto/-/maci-crypto-2.5.0.tgz#89a99921517b79564af8a356863c446b49e4cb0f"
integrity sha512-ozrLDH6kaK62TomNr5tnVgrUs6szXHCwcRyPzsQy07Wg2ZX61nyY0EFgWKAuU8kXqvYRdTtQdgflw6qpVz/4+w==
dependencies:
"@zk-kit/baby-jubjub" "^1.0.3"
"@zk-kit/eddsa-poseidon" "^1.1.0"
"@zk-kit/poseidon-cipher" "^0.3.2"
ethers "^6.13.4"
minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minimatch@^5.0.1:
version "5.1.6"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
dependencies:
brace-expansion "^2.0.1"
minimist@1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
nanoassert@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-2.0.0.tgz#a05f86de6c7a51618038a620f88878ed1e490c09"
integrity sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
dependencies:
deep-is "~0.1.3"
fast-levenshtein "~2.0.6"
levn "~0.3.0"
prelude-ls "~1.1.2"
type-check "~0.3.2"
word-wrap "~1.2.3"
ox@0.6.7:
version "0.6.7"
resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.7.tgz#afd53f2ecef68b8526660e9d29dee6e6b599a832"
@@ -486,98 +76,11 @@ ox@0.6.7:
abitype "^1.0.6"
eventemitter3 "5.0.1"
poseidon-lite@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/poseidon-lite/-/poseidon-lite-0.3.0.tgz#93c42f6f9b870f154f2722dfd686b909c4285765"
integrity sha512-ilJj4MIve4uBEG7SrtPqUUNkvpJ/pLVbndxa0WvebcQqeIhe+h72JR4g0EvwchUzm9sOQDlOjiDNmRAgxNZl4A==
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
r1csfile@0.0.48:
version "0.0.48"
resolved "https://registry.yarnpkg.com/r1csfile/-/r1csfile-0.0.48.tgz#a317fc75407a9da92631666c75bdfc13f0a7835a"
integrity sha512-kHRkKUJNaor31l05f2+RFzvcH5XSa7OfEfd/l4hzjte6NL6fjRkSMfZ4BjySW9wmfdwPOtq3mXurzPvPGEf5Tw==
dependencies:
"@iden3/bigarray" "0.0.2"
"@iden3/binfileutils" "0.0.12"
fastfile "0.0.20"
ffjavascript "0.3.0"
snarkjs@0.7.5:
version "0.7.5"
resolved "https://registry.yarnpkg.com/snarkjs/-/snarkjs-0.7.5.tgz#334d83b61468bdffbbf922b20734ca47be50b8ab"
integrity sha512-h+3c4rXZKLhLuHk4LHydZCk/h5GcNvk5GjVKRRkHmfb6Ntf8gHOA9zea3g656iclRuhqQ3iKDWFgiD9ypLrKiA==
dependencies:
"@iden3/binfileutils" "0.0.12"
bfj "^7.0.2"
blake2b-wasm "^2.4.0"
circom_runtime "0.1.28"
ejs "^3.1.6"
fastfile "0.0.20"
ffjavascript "0.3.1"
js-sha3 "^0.8.0"
logplease "^1.2.15"
r1csfile "0.0.48"
source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
static-eval@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42"
integrity sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==
dependencies:
escodegen "^1.8.1"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
tryer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
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-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==
dependencies:
prelude-ls "~1.1.2"
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==
typescript@^5.7.3:
version "5.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
underscore@1.12.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e"
integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==
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"
@@ -597,33 +100,6 @@ viem@2.22.14:
ox "0.6.7"
ws "8.18.0"
wasmbuilder@0.0.16:
version "0.0.16"
resolved "https://registry.yarnpkg.com/wasmbuilder/-/wasmbuilder-0.0.16.tgz#f34c1f2c047d2f6e1065cbfec5603988f16d8549"
integrity sha512-Qx3lEFqaVvp1cEYW7Bfi+ebRJrOiwz2Ieu7ZG2l7YyeSJIok/reEQCQCuicj/Y32ITIJuGIM9xZQppGx5LrQdA==
wasmcurves@0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/wasmcurves/-/wasmcurves-0.2.2.tgz#ca444f6a6f6e2a5cbe6629d98ff478a62b4ccb2b"
integrity sha512-JRY908NkmKjFl4ytnTu5ED6AwPD+8VJ9oc94kdq7h5bIwbj0L4TDJ69mG+2aLs2SoCmGfqIesMWTEJjtYsoQXQ==
dependencies:
wasmbuilder "0.0.16"
web-worker@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da"
integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==
word-wrap@~1.2.3:
version "1.2.5"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
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"

View File

@@ -58,13 +58,12 @@ const withdrawalPayload = {
data: "0xfeeMismatch",
},
proof: {
pi_a: ["0", "0", "0"],
pi_a: ["0", "0"],
pi_b: [
["0", "0"],
["0", "0"],
["0", "0"],
],
pi_c: ["0", "0", "0"],
pi_c: ["0", "0"],
protocol: "groth16",
curve: "bn128",
},
@@ -120,7 +119,6 @@ describe("relayRequestHandler", () => {
vi.spyOn(privacyPoolRelayer, "handleRequest").mockResolvedValue(undefined);
await relayRequestHandler(req, resMock, nextMock);
const error = nextMock.mock.calls[0][0]
console.log(error)
expect(error).toBeInstanceOf(ConfigError)
expect(error.code).toEqual(ErrorCode.MAX_GAS_PRICE);
});

View File

@@ -4,7 +4,6 @@
"declarationMap": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src",
/* Base Options: */
"esModuleInterop": true,
"skipLibCheck": true,
@@ -27,5 +26,5 @@
"lib": ["es2022"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "test", "coverage"]
"exclude": ["node_modules", "dist", "test"]
}