@kohaku-eth/tornado-cash 0.0.2-alpha.12 → 0.0.2-alpha.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { Broadcaster } from '@kohaku-eth/plugins/broadcaster';
2
- import { Host, ChainId, Storage, PrivateOperation, PublicOperation, AssetAmount, ERC20AssetId, PluginInstance, AccountId } from '@kohaku-eth/plugins';
2
+ import { Host, ExternalRawEvent, ChainId, Storage, PrivateOperation, PublicOperation, AccountId, AssetAmount, ERC20AssetId, PluginInstance, UnshieldOptions } from '@kohaku-eth/plugins';
3
+ import { TxData } from '@kohaku-eth/provider';
3
4
  import { combineReducers } from '@reduxjs/toolkit';
4
5
  import { ParseAbiItem, SignedAuthorization, Hash, Address as Address$2 } from 'viem';
5
- import { TxData } from '@kohaku-eth/provider';
6
6
  import { Address as Address$1 } from 'ox/Address';
7
+ import { Hex } from 'ox/Hex';
7
8
 
8
9
  type Nullifier$1 = bigint & {};
9
10
  type NullifierHash = bigint & {};
@@ -190,7 +191,7 @@ declare const INSTANCE_REGISTRY_EVENT_SIGNATURES: {
190
191
  };
191
192
 
192
193
  type IEventsMap = Record<string, ParseAbiItem<string>>;
193
- interface IGetEventsParams<T> {
194
+ interface IGetEventsParams$1<T> {
194
195
  events: T | T[];
195
196
  fromBlock: bigint;
196
197
  toBlock?: bigint;
@@ -211,7 +212,7 @@ type IGroupedEvents<NamesTable extends Record<string, unknown>> = {
211
212
  };
212
213
  type GetEventsFn<EventsMap extends IEventsMap, ParsedEvents extends {
213
214
  [key in keyof EventsMap]: unknown;
214
- }> = <const T extends keyof ParsedEvents = never>(params: IGetEventsParams<T>) => Promise<Pick<IGroupedEvents<ParsedEvents>, T> & {
215
+ }> = <const T extends keyof ParsedEvents = never>(params: IGetEventsParams$1<T>) => Promise<Pick<IGroupedEvents<ParsedEvents>, T> & {
215
216
  fromBlock: bigint;
216
217
  toBlock: bigint;
217
218
  }>;
@@ -220,7 +221,6 @@ interface IPoolConfig {
220
221
  isERC20: boolean;
221
222
  token: Address;
222
223
  state: 0 | 1;
223
- uniswapPoolSwappingFee: number;
224
224
  protocolFeePercentage: number;
225
225
  denomination: bigint;
226
226
  rootHistorySize: number;
@@ -233,6 +233,14 @@ interface IRelayerAggregatorData {
233
233
  }
234
234
  interface IDataService {
235
235
  getPoolEvents: GetEventsFn<typeof POOL_EVENTS_SIGNATURES, IPoolEvents>;
236
+ getBlockNumber(): Promise<bigint>;
237
+ parsePoolEvents(events: ExternalRawEvent[]): {
238
+ Deposited: IRawDepositEvent[];
239
+ Withdrawn: IRawWithdrawalEvent[];
240
+ };
241
+ parseRelayerRegistryEvents(events: ExternalRawEvent[]): {
242
+ RelayerRegistered: IRelayerRegisteredEvent[];
243
+ };
236
244
  getRelayerRegistryEvents: GetEventsFn<typeof RELAYER_REGISTRY_EVENTS_SIGNATURES, IRelayerRegistryEvents>;
237
245
  getInstanceRegistryEvents: GetEventsFn<typeof INSTANCE_REGISTRY_EVENT_SIGNATURES, IInstanceRegistryEvents>;
238
246
  getAsset(assetAddress: Address): Promise<IAsset>;
@@ -249,7 +257,47 @@ interface IDataService {
249
257
  getLatestBlockTimestamp(): Promise<bigint>;
250
258
  getRelayerData(aggregatorAddress: Address, relayerNameHashes: bigint[], subdomains: string[]): Promise<IRelayerAggregatorData[]>;
251
259
  getAccountNonce(accountAddress: Address): Promise<number>;
252
- quoteEthToToken(amountInWei: bigint, tokenAddress: Address, poolFee: number): Promise<bigint>;
260
+ quoteWeiInToken(paymasterAddress: Address, feeToken: Address, weiAmount: bigint): Promise<bigint>;
261
+ }
262
+
263
+ /** Identifies one contract to sync from a given block. */
264
+ interface IGetEventsParams {
265
+ chainId: bigint;
266
+ address: Address;
267
+ fromBlock: bigint;
268
+ }
269
+ interface IGetPoolEventsResult {
270
+ Deposited: IRawDepositEvent[];
271
+ Withdrawn: IRawWithdrawalEvent[];
272
+ /** Highest block the returned events are synced up to. */
273
+ toBlock: bigint;
274
+ }
275
+ interface IGetRelayerRegistryEventsResult {
276
+ RelayerRegistered: IRelayerRegisteredEvent[];
277
+ /** Highest block the returned events are synced up to. */
278
+ toBlock: bigint;
279
+ }
280
+ interface ISyncService {
281
+ /**
282
+ * Fetches a single pool's `Deposited`/`Withdrawn` events from `fromBlock`.
283
+ * Uses the external sync provider for the bulk of a large range and the chain
284
+ * for the remaining tail; falls back to a chain-only fetch otherwise.
285
+ */
286
+ getPoolEvents(params: IGetEventsParams): Promise<IGetPoolEventsResult>;
287
+ /**
288
+ * Fetches the relayer registry's `RelayerRegistered` events from `fromBlock`,
289
+ * with the same external-then-chain strategy as {@link getPoolEvents}.
290
+ */
291
+ getRelayerRegistryEvents(params: IGetEventsParams): Promise<IGetRelayerRegistryEventsResult>;
292
+ /**
293
+ * Resolves a pool's scan-start block. Uses the external provider's first
294
+ * covered block when available (an O(1) lookup), else falls back to the
295
+ * on-chain deployment-block binary search.
296
+ */
297
+ getPoolDeploymentBlock(params: {
298
+ chainId: bigint;
299
+ address: Address;
300
+ }): Promise<bigint>;
253
301
  }
254
302
 
255
303
  type SpecificAssetBalanceFn<isAsync extends boolean = false> = <const Adresses extends Address | Address[] = Address[], ReturnType = Adresses extends unknown[] ? Map<Address, bigint> : bigint>(assetAddress?: Adresses) => isAsync extends true ? Promise<ReturnType> : ReturnType;
@@ -289,6 +337,7 @@ interface IRelayerFeeConfig {
289
337
  interface StoreFactoryParams {
290
338
  secretManagerFactory: () => Promise<ISecretManager>;
291
339
  dataService: IDataService;
340
+ syncService: ISyncService;
292
341
  relayerClient: IRelayerClient;
293
342
  paymasterConfig: IChainsPaymastersConfig;
294
343
  storageToSyncTo?: Storage;
@@ -303,6 +352,36 @@ declare enum DepositStrategy {
303
352
  MinFee = 1
304
353
  }
305
354
 
355
+ interface SerializedAuth {
356
+ address: Address$1;
357
+ chainId: Hex;
358
+ nonce: Hex;
359
+ r: Hex;
360
+ s: Hex;
361
+ yParity: Hex;
362
+ }
363
+ /**
364
+ * A userOp serialized to the hex shape expected by `eth_sendUserOperation`, so
365
+ * it can be carried as plain (JSON-serializable) data from the prepare phase
366
+ * (thunk) to the broadcast phase.
367
+ */
368
+ interface SerializedUserOperation {
369
+ sender: `0x${string}`;
370
+ nonce: `0x${string}`;
371
+ callData: `0x${string}`;
372
+ callGasLimit: `0x${string}`;
373
+ verificationGasLimit: `0x${string}`;
374
+ preVerificationGas: `0x${string}`;
375
+ maxFeePerGas: `0x${string}`;
376
+ maxPriorityFeePerGas: `0x${string}`;
377
+ paymaster?: `0x${string}`;
378
+ paymasterVerificationGasLimit?: `0x${string}`;
379
+ paymasterPostOpGasLimit?: `0x${string}`;
380
+ paymasterData?: `0x${string}`;
381
+ signature: `0x${string}`;
382
+ eip7702Auth?: SerializedAuth;
383
+ }
384
+
306
385
  interface SignedDelegation {
307
386
  senderAddress: `0x${string}`;
308
387
  authorization: SignedAuthorization;
@@ -322,8 +401,7 @@ interface IGenericPaymasterWithdrawalPayload {
322
401
  paymasterAddress: `0x${string}`;
323
402
  entryPointAddress: `0x${string}`;
324
403
  bundlerUrl: string;
325
- accountAddress: `0x${string}`;
326
- delegation?: SignedDelegation;
404
+ userOperation: SerializedUserOperation;
327
405
  }
328
406
  interface PaymasterBroadcastResult {
329
407
  userOpHash: Hash;
@@ -377,6 +455,12 @@ interface TCProtocolParams {
377
455
  artifactsLoader?: () => Promise<ITornadoArtifacts>;
378
456
  initialState?: () => Promise<Record<string, PublicRootState>>;
379
457
  stateManagerWorkerUrl?: string;
458
+ /**
459
+ * When a pool is at least this many blocks behind, use the host's
460
+ * `externalSyncProvider` (if any) for the bulk of the sync instead of the
461
+ * chain. Omit to always sync from the chain.
462
+ */
463
+ minExternalSyncBlocksAmount?: number;
380
464
  }
381
465
  interface IBaseOperationParams {
382
466
  }
@@ -396,6 +480,7 @@ interface IRelayerWithdrawParams extends IWithdrawBaseParams {
396
480
  interface IPaymasterWithdrawParams extends IWithdrawBaseParams {
397
481
  mode: 'paymaster';
398
482
  delegation?: DelegationConfig;
483
+ tailCalls?: (address: AccountId) => Promise<TxData[]>;
399
484
  }
400
485
  type IWithdrawapOperationParams = IRelayerWithdrawParams | IPaymasterWithdrawParams;
401
486
  interface IGetNotesParams extends IBaseOperationParams {
@@ -445,7 +530,7 @@ type TCBroadcaster = Broadcaster<TCPrivateOperation, ITornadoWithdrawResponse[]>
445
530
  interface TCBaseCredential {
446
531
  accountIndex: number;
447
532
  }
448
- interface TCPluginParameters extends TCBroadcasterParameters, TCBaseCredential, Pick<TCProtocolParams, 'initialState'> {
533
+ interface TCPluginParameters extends TCBroadcasterParameters, TCBaseCredential, Pick<TCProtocolParams, 'initialState' | 'minExternalSyncBlocksAmount'> {
449
534
  protocolConfig: TCProtocolConfig;
450
535
  relayerConfig?: IRelayerFeeConfig;
451
536
  stateManagerWorkerUrl?: string;
@@ -454,11 +539,11 @@ interface TCPluginParameters extends TCBroadcasterParameters, TCBaseCredential,
454
539
  type TCAddress = Address$1;
455
540
  type TCAssetAmount<Tag extends string | undefined = undefined> = AssetAmount<ERC20AssetId, bigint, Tag>;
456
541
  type TCAssetBalance = TCAssetAmount;
457
- interface TCRelayerUnshieldOptions {
542
+ interface TCRelayerUnshieldOptions extends UnshieldOptions {
458
543
  mode: 'relayer';
459
544
  preferredRelayersEns?: string[];
460
545
  }
461
- interface TCPaymasterUnshieldOptions {
546
+ interface TCPaymasterUnshieldOptions extends UnshieldOptions {
462
547
  mode: 'paymaster';
463
548
  delegation?: DelegationConfig;
464
549
  }
@@ -495,7 +580,7 @@ type RequireOnly<T, Keys extends keyof T> = Partial<T> & Pick<T, Keys>;
495
580
  declare class TornadoCashProtocol implements TCInstance {
496
581
  readonly host: Host;
497
582
  private stateManager;
498
- constructor(host: Host, { accountIndex, initialState, protocolConfig, artifactsLoader, stateManagerWorkerUrl, relayerConfig, paymasterConfig, relayerClientFactory, }: RequireOnly<TCProtocolParams, 'protocolConfig'>);
583
+ constructor(host: Host, { accountIndex, initialState, protocolConfig, artifactsLoader, stateManagerWorkerUrl, relayerConfig, paymasterConfig, relayerClientFactory, minExternalSyncBlocksAmount, }: RequireOnly<TCProtocolParams, 'protocolConfig'>);
499
584
  instanceId: () => Promise<"0x1">;
500
585
  /**
501
586
  * Only process supported assets or error out?
@@ -510,6 +595,7 @@ declare class TornadoCashProtocol implements TCInstance {
510
595
  */
511
596
  notes(assets?: ERC20AssetId[], includeSpent?: boolean): Promise<TCNote[]>;
512
597
  prepareShield(assets: TCAssetAmount, options?: TCPrepareShieldOptions | `0x${string}`): Promise<TCPublicOperation>;
598
+ prepareUnshield(assets: AssetAmount, to: AccountId): Promise<TCPrivateOperation<'relayer'>>;
513
599
  prepareUnshield(assets: AssetAmount, to: AccountId, options?: TCRelayerUnshieldOptions): Promise<TCPrivateOperation<'relayer'>>;
514
600
  prepareUnshield(assets: AssetAmount, to: AccountId, options?: TCPaymasterUnshieldOptions): Promise<TCPrivateOperation<'paymaster'>>;
515
601
  sync(): Promise<void>;
@@ -534,11 +620,11 @@ declare const TornadoPaymasterConfigs: {
534
620
  readonly 11155111: {
535
621
  readonly bundlerUrl: "https://public.pimlico.io/v2/11155111/rpc";
536
622
  readonly entryPointAddress: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108";
537
- readonly paymasterAddress: "0xA0e2667AcdECb9bCDa6Fcf87638a962e2E9dB20D";
623
+ readonly paymasterAddress: "0x1c5aCCb9c09D72945b79EC986776136bE01d7B2F";
538
624
  readonly poolsAccountsMap: {
539
- readonly '0x8c4a04d872a6c1be37964a21ba3a138525dff50b': "0x8A70E3dE493B8599447013f08d8b35719C8ED0ec";
540
- readonly '0x8cc930096b4df705a007c4a039bdfa1320ed2508': "0x191e3De3cF7f61de7EbEf4Bf45cfBA4f652d1C6e";
541
- readonly '0x6921fd1a97441dd603a997ed6ddf388658daf754': "0x060d053A6595630851ab873d6C95401b0C964d26";
625
+ readonly '0x8c4a04d872a6c1be37964a21ba3a138525dff50b': "0xa616aAE443FCCABfc2F1EA2Afe001E5046FFDCe0";
626
+ readonly '0x8cc930096b4df705a007c4a039bdfa1320ed2508': "0x67a898343F32641206d0f30CB3367944a8919A3A";
627
+ readonly '0x6921fd1a97441dd603a997ed6ddf388658daf754': "0xbF0a7969dacF8337716d0F283df0574dF56b0479";
542
628
  };
543
629
  };
544
630
  readonly 1: never;
@@ -558,7 +644,6 @@ declare const TornadoCashConfigs: {
558
644
  readonly address: 1329937909414098670091808248270266272594403334985n;
559
645
  };
560
646
  readonly weth: 1097077688018008265106216665536940668749033598146n;
561
- readonly uniswapQuoterV2: 559478315000592720989748010348495519536007655966n;
562
647
  };
563
648
  readonly 11155111: {
564
649
  readonly ensSubdomainKey: "sepolia-tornado";
@@ -574,7 +659,6 @@ declare const TornadoCashConfigs: {
574
659
  readonly address: 368418168928372787409224158240737522620871644738n;
575
660
  };
576
661
  readonly weth: 1461358721292675304620367034492138750323989506836n;
577
- readonly uniswapQuoterV2: 1353730886217084326490181093487957540173111046067n;
578
662
  };
579
663
  };
580
664
 
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { createSelector, createAsyncThunk } from '@reduxjs/toolkit';
2
2
  import { createTx } from '@kohaku-eth/provider';
3
- import { encodeFunctionData, toHex, erc20Abi } from 'viem';
3
+ import { encodeFunctionData, toHex, erc20Abi, http } from 'viem';
4
4
  import { proxy } from 'comlink';
5
5
  import { loadStateManagerWorker } from '#worker-loader';
6
- import { BundlerClient, TornadoBuilder } from '@privacy-paymasters/sdk';
7
- import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
6
+ import 'viem/accounts';
7
+ import { createBundlerClient } from 'viem/account-abstraction';
8
8
  import { numberToBytesLE, bytesToNumberLE, concatBytes } from '@noble/curves/utils.js';
9
9
  import { keccak_256 } from '@noble/hashes/sha3.js';
10
10
  import { pedersenHash as pedersenHash$1, Point } from 'micro-zk-proofs/pedersen.js';
@@ -348,11 +348,12 @@ var TornadoPaymasterConfigs = {
348
348
  11155111: {
349
349
  bundlerUrl: "https://public.pimlico.io/v2/11155111/rpc",
350
350
  entryPointAddress: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
351
- paymasterAddress: "0xA0e2667AcdECb9bCDa6Fcf87638a962e2E9dB20D",
351
+ paymasterAddress: "0x1c5aCCb9c09D72945b79EC986776136bE01d7B2F",
352
352
  poolsAccountsMap: {
353
- "0x8c4a04d872a6c1be37964a21ba3a138525dff50b": "0x8A70E3dE493B8599447013f08d8b35719C8ED0ec",
354
- "0x8cc930096b4df705a007c4a039bdfa1320ed2508": "0x191e3De3cF7f61de7EbEf4Bf45cfBA4f652d1C6e",
355
- "0x6921fd1a97441dd603a997ed6ddf388658daf754": "0x060d053A6595630851ab873d6C95401b0C964d26"
353
+ // pool -> per-pool TornadoFeeAdapter (eth_0_1, eth_1, dai_100)
354
+ "0x8c4a04d872a6c1be37964a21ba3a138525dff50b": "0xa616aAE443FCCABfc2F1EA2Afe001E5046FFDCe0",
355
+ "0x8cc930096b4df705a007c4a039bdfa1320ed2508": "0x67a898343F32641206d0f30CB3367944a8919A3A",
356
+ "0x6921fd1a97441dd603a997ed6ddf388658daf754": "0xbF0a7969dacF8337716d0F283df0574dF56b0479"
356
357
  }
357
358
  },
358
359
  1: {}
@@ -371,8 +372,7 @@ var TornadoCashConfigs = {
371
372
  aggregator: {
372
373
  address: 0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49n
373
374
  },
374
- weth: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2n,
375
- uniswapQuoterV2: 0x61fFE014bA17989E743c5F6cB21bF9697530B21en
375
+ weth: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2n
376
376
  },
377
377
  11155111: {
378
378
  ensSubdomainKey: "sepolia-tornado",
@@ -387,8 +387,7 @@ var TornadoCashConfigs = {
387
387
  aggregator: {
388
388
  address: 0x4088712AC9fad39ea133cdb9130E465d235e9642n
389
389
  },
390
- weth: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14n,
391
- uniswapQuoterV2: 0xEd1f6473345F45b75F8179591dd5bA1888cf2FB3n
390
+ weth: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14n
392
391
  }
393
392
  };
394
393
 
@@ -420,7 +419,8 @@ var TornadoCashProtocol = class {
420
419
  stateManagerWorkerUrl,
421
420
  relayerConfig,
422
421
  paymasterConfig = TornadoPaymasterConfigs,
423
- relayerClientFactory = () => new RelayerClient(host)
422
+ relayerClientFactory = () => new RelayerClient(host),
423
+ minExternalSyncBlocksAmount
424
424
  }) {
425
425
  this.host = host;
426
426
  this.stateManager = (async () => {
@@ -431,6 +431,18 @@ var TornadoCashProtocol = class {
431
431
  reject(err);
432
432
  });
433
433
  });
434
+ const { externalSyncProvider } = host;
435
+ const externalSyncClient = externalSyncProvider && {
436
+ getEvents: async (params) => {
437
+ const events = [];
438
+ for await (const event of externalSyncProvider.streamEvents(params)) {
439
+ events.push(event);
440
+ }
441
+ return events;
442
+ },
443
+ firstCoveredBlock: (params) => externalSyncProvider.firstCoveredBlock(params),
444
+ lastCoveredBlock: (params) => externalSyncProvider.lastCoveredBlock(params)
445
+ };
434
446
  await Promise.race([
435
447
  remote.init(
436
448
  proxy(host.provider),
@@ -439,7 +451,8 @@ var TornadoCashProtocol = class {
439
451
  proxy(host.storage),
440
452
  proxy(initialState),
441
453
  proxy(artifactsLoader),
442
- { protocolConfig, accountIndex, relayerConfig, paymasterConfig }
454
+ externalSyncClient ? proxy(externalSyncClient) : void 0,
455
+ { protocolConfig, accountIndex, relayerConfig, paymasterConfig, minExternalSyncBlocksAmount }
443
456
  ),
444
457
  workerReady
445
458
  ]);
@@ -448,7 +461,16 @@ var TornadoCashProtocol = class {
448
461
  getBalances: ((assets) => remote.getBalances(assets)),
449
462
  getNotes: (params) => remote.getNotes(params),
450
463
  getDepositPayload: (params) => remote.getDepositPayload(params),
451
- getWithdrawalPayloads: (params) => remote.getWithdrawalPayloads(params),
464
+ getWithdrawalPayloads: (params) => {
465
+ if (params.mode === "paymaster") {
466
+ const { tailCalls, ...rest } = params;
467
+ return remote.getWithdrawalPayloads(
468
+ rest,
469
+ tailCalls ? proxy(tailCalls) : void 0
470
+ );
471
+ }
472
+ return remote.getWithdrawalPayloads(params);
473
+ },
452
474
  dumpState: (() => remote.dumpState())
453
475
  };
454
476
  })();
@@ -515,7 +537,10 @@ var TornadoCashProtocol = class {
515
537
  });
516
538
  return { txns: tx };
517
539
  }
518
- async prepareUnshield(assets, to, options) {
540
+ async prepareUnshield(assets, to, options = { mode: "relayer" }) {
541
+ if (options.mode === "relayer" && options.tailCalls !== void 0) {
542
+ throw new Error("Tail Calls are only supported when using paymaster mode.");
543
+ }
519
544
  const { asset, amount } = assets;
520
545
  const parsedAsset = BigInt(asset.contract || E_ADDRESS_BIGINT);
521
546
  const stateManager = await this.stateManager;
@@ -530,7 +555,8 @@ var TornadoCashProtocol = class {
530
555
  withdrawals = await stateManager.getWithdrawalPayloads({
531
556
  ...baseParams,
532
557
  mode: "paymaster",
533
- delegation: options.delegation
558
+ delegation: options.delegation,
559
+ tailCalls: options.tailCalls
534
560
  });
535
561
  } else {
536
562
  withdrawals = await stateManager.getWithdrawalPayloads({
@@ -553,120 +579,51 @@ var TornadoCashProtocol = class {
553
579
  return stateManager.dumpState();
554
580
  }
555
581
  };
556
-
557
- // src/paymaster/fee.ts
558
- var ERC20_TRANSFER_GAS = 100000n;
559
- var baseGasUnits = {
560
- preVerificationGas: 80000n,
561
- verificationGasLimit: 50000n,
562
- callGasLimit: 300000n,
563
- paymasterVerificationGasLimit: 350000n,
564
- paymasterPostOpGasLimit: 10000n
565
- };
566
- function reasonableGasUnits(isERC20) {
567
- if (!isERC20) return baseGasUnits;
568
- return {
569
- ...baseGasUnits,
570
- callGasLimit: baseGasUnits.callGasLimit + ERC20_TRANSFER_GAS
571
- };
582
+ function createPaymasterBundlerClient(bundlerUrl) {
583
+ return createBundlerClient({ transport: http(bundlerUrl) });
572
584
  }
573
- async function signDelegationAuthorization({
574
- privateKey,
575
- accountAddress,
576
- chainId,
577
- nonce
578
- }) {
579
- const account = privateKeyToAccount(privateKey);
580
- const authorization = await account.signAuthorization({
581
- contractAddress: accountAddress,
582
- chainId,
583
- nonce
585
+ async function sendSerializedUserOperation(client, op, entryPoint) {
586
+ return client.request({
587
+ method: "eth_sendUserOperation",
588
+ params: [op, entryPoint]
584
589
  });
585
- return { authorization, senderAddress: account.address };
586
590
  }
587
591
 
588
592
  // src/paymaster/paymaster-broadcaster.ts
589
593
  var PaymasterBroadcaster = class _PaymasterBroadcaster {
590
- constructor(provider, options) {
591
- this.provider = provider;
592
- this.options = options;
593
- }
594
- provider;
595
- options;
596
594
  async broadcast(withdrawals) {
597
- const chainId = Number(await this.provider.getChainId());
598
- const {
599
- poolsAccountsMap: rawPoolsAccountsMap
600
- } = this.options[chainId];
601
- const poolAcountsMap = new Map(
602
- Object.entries(rawPoolsAccountsMap).map(([poolAccount, tornadoAccount]) => [
603
- BigInt(poolAccount),
604
- tornadoAccount
605
- ])
606
- );
607
- const results = await Promise.allSettled(
608
- withdrawals.map((w) => _PaymasterBroadcaster.broadcastOne({
609
- ...w,
610
- accountAddress: poolAcountsMap.get(w.poolAddress)
611
- }, this.provider))
595
+ const bySender = /* @__PURE__ */ new Map();
596
+ for (const w of withdrawals) {
597
+ const key = w.userOperation.sender.toLowerCase();
598
+ const group = bySender.get(key) ?? [];
599
+ group.push(w);
600
+ bySender.set(key, group);
601
+ }
602
+ const groupResults = await Promise.allSettled(
603
+ [...bySender.values()].map(async (group) => {
604
+ const out = [];
605
+ for (const w of group) {
606
+ out.push(await _PaymasterBroadcaster.broadcastOne(w));
607
+ }
608
+ return out;
609
+ })
612
610
  );
613
- const failed = results.filter((r) => r.status === "rejected");
611
+ const failed = groupResults.filter((r) => r.status === "rejected");
614
612
  if (failed.length > 0) {
615
613
  console.warn(
616
614
  `Some paymaster withdrawals failed.`,
617
615
  failed.map((e) => e.reason).join("\n")
618
616
  );
619
617
  }
620
- return results.filter((r) => r.status === "fulfilled").map((r) => r.value);
618
+ return groupResults.filter((r) => r.status === "fulfilled").flatMap((r) => r.value);
621
619
  }
622
- static async broadcastOne(withdrawal, provider) {
623
- const {
624
- proof: { proof: proofHex, args: proofArgs },
625
- paymasterAddress,
626
- entryPointAddress,
627
- bundlerUrl,
628
- accountAddress,
629
- isERC20
630
- } = withdrawal;
631
- const [root, nullifierHash, recipient, _paymasterAddress, feeHex] = proofArgs;
632
- if (BigInt(paymasterAddress) !== BigInt(_paymasterAddress)) {
633
- throw new Error(`relayer must be paymaster when using the 4337 paymaster flow: ${paymasterAddress} != ${_paymasterAddress}`);
634
- }
635
- const delegation = withdrawal.delegation ?? await _PaymasterBroadcaster.generateRandomDelegation(accountAddress, provider);
636
- const { senderAddress, authorization } = delegation;
637
- const bundlerClient = new BundlerClient(bundlerUrl, entryPointAddress);
638
- const { standard: { maxFeePerGas, maxPriorityFeePerGas } } = await bundlerClient.getUserOperationGasPrice();
639
- let gas;
640
- {
641
- gas = {
642
- type: "manual",
643
- ...reasonableGasUnits(isERC20),
644
- maxFeePerGas,
645
- maxPriorityFeePerGas
646
- };
647
- }
648
- const op = await new TornadoBuilder(senderAddress).withPaymaster(paymasterAddress).withAuthorization(authorization).withWithdraw(
649
- proofHex,
650
- root,
651
- nullifierHash,
652
- recipient,
653
- _paymasterAddress,
654
- BigInt(feeHex)
655
- ).withGas(gas).build(provider, bundlerClient);
656
- const userOpHash = await bundlerClient.sendUserOperation(op);
657
- await bundlerClient.waitForUserOperationReceipt(userOpHash);
620
+ static async broadcastOne(withdrawal) {
621
+ const { bundlerUrl, entryPointAddress, userOperation } = withdrawal;
622
+ const bundlerClient = createPaymasterBundlerClient(bundlerUrl);
623
+ const userOpHash = await sendSerializedUserOperation(bundlerClient, userOperation, entryPointAddress);
624
+ await bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
658
625
  return { userOpHash };
659
626
  }
660
- static async generateRandomDelegation(accountAddress, provider) {
661
- const privateKey = generatePrivateKey();
662
- const chainId = Number(await provider.getChainId());
663
- return signDelegationAuthorization({
664
- privateKey,
665
- accountAddress,
666
- chainId,
667
- nonce: 0
668
- });
669
- }
670
627
  };
671
628
 
672
629
  // src/plugin/broadcaster.ts
@@ -678,7 +635,7 @@ var TornadoCashBroadcaster = class {
678
635
  host,
679
636
  relayerClientFactory = () => new RelayerClient({ network: host.network }),
680
637
  paymasterConfig,
681
- paymasterClientFactory = () => new PaymasterBroadcaster(host.provider, paymasterConfig)
638
+ paymasterClientFactory = () => new PaymasterBroadcaster()
682
639
  }) {
683
640
  this.provider = host.provider;
684
641
  this.relayerClient = relayerClientFactory();
@@ -1,5 +1,5 @@
1
1
  // src/utils/merkle-tree/merkle-tree.util.browser.ts
2
- import { generateMimcMerkleProof, createMimcMerkleTreeParallel } from "mimc-tree";
2
+ import { generateMimcMerkleProof, createMimcMerkleTreeParallel } from "@kohaku-eth/mimc-tree";
3
3
  var buildTree = async (leaves) => {
4
4
  return createMimcMerkleTreeParallel(leaves, { workerUrl: "./merkle-tree-worker.browser.js" });
5
5
  };
@@ -1,5 +1,5 @@
1
1
  // src/utils/merkle-tree/merkle-tree.util.node.ts
2
- import { generateMimcMerkleProof, createMimcMerkleTreeNodejsParallel } from "mimc-tree";
2
+ import { generateMimcMerkleProof, createMimcMerkleTreeNodejsParallel } from "@kohaku-eth/mimc-tree";
3
3
  var buildTree = async (leaves) => {
4
4
  return createMimcMerkleTreeNodejsParallel(leaves);
5
5
  };