@kohaku-eth/tornado-cash 0.0.2-alpha.11 → 0.0.2-alpha.13

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.js CHANGED
@@ -1,15 +1,13 @@
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';
11
- import 'micro-zk-proofs/mimcsponge.js';
12
- import 'fixed-merkle-tree';
13
11
 
14
12
  // src/state/thunks/getDepositPayloadThunk.ts
15
13
 
@@ -350,11 +348,12 @@ var TornadoPaymasterConfigs = {
350
348
  11155111: {
351
349
  bundlerUrl: "https://public.pimlico.io/v2/11155111/rpc",
352
350
  entryPointAddress: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
353
- paymasterAddress: "0xA0e2667AcdECb9bCDa6Fcf87638a962e2E9dB20D",
351
+ paymasterAddress: "0x1c5aCCb9c09D72945b79EC986776136bE01d7B2F",
354
352
  poolsAccountsMap: {
355
- "0x8c4a04d872a6c1be37964a21ba3a138525dff50b": "0x8A70E3dE493B8599447013f08d8b35719C8ED0ec",
356
- "0x8cc930096b4df705a007c4a039bdfa1320ed2508": "0x191e3De3cF7f61de7EbEf4Bf45cfBA4f652d1C6e",
357
- "0x6921fd1a97441dd603a997ed6ddf388658daf754": "0x060d053A6595630851ab873d6C95401b0C964d26"
353
+ // pool -> per-pool TornadoFeeAdapter (eth_0_1, eth_1, dai_100)
354
+ "0x8c4a04d872a6c1be37964a21ba3a138525dff50b": "0xa616aAE443FCCABfc2F1EA2Afe001E5046FFDCe0",
355
+ "0x8cc930096b4df705a007c4a039bdfa1320ed2508": "0x67a898343F32641206d0f30CB3367944a8919A3A",
356
+ "0x6921fd1a97441dd603a997ed6ddf388658daf754": "0xbF0a7969dacF8337716d0F283df0574dF56b0479"
358
357
  }
359
358
  },
360
359
  1: {}
@@ -373,8 +372,7 @@ var TornadoCashConfigs = {
373
372
  aggregator: {
374
373
  address: 0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49n
375
374
  },
376
- weth: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2n,
377
- uniswapQuoterV2: 0x61fFE014bA17989E743c5F6cB21bF9697530B21en
375
+ weth: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2n
378
376
  },
379
377
  11155111: {
380
378
  ensSubdomainKey: "sepolia-tornado",
@@ -389,8 +387,7 @@ var TornadoCashConfigs = {
389
387
  aggregator: {
390
388
  address: 0x4088712AC9fad39ea133cdb9130E465d235e9642n
391
389
  },
392
- weth: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14n,
393
- uniswapQuoterV2: 0xEd1f6473345F45b75F8179591dd5bA1888cf2FB3n
390
+ weth: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14n
394
391
  }
395
392
  };
396
393
 
@@ -423,7 +420,7 @@ var TornadoCashProtocol = class {
423
420
  relayerConfig,
424
421
  paymasterConfig = TornadoPaymasterConfigs,
425
422
  relayerClientFactory = () => new RelayerClient(host),
426
- proverVersion
423
+ minExternalSyncBlocksAmount
427
424
  }) {
428
425
  this.host = host;
429
426
  this.stateManager = (async () => {
@@ -434,6 +431,17 @@ var TornadoCashProtocol = class {
434
431
  reject(err);
435
432
  });
436
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
+ lastCoveredBlock: (params) => externalSyncProvider.lastCoveredBlock(params)
444
+ };
437
445
  await Promise.race([
438
446
  remote.init(
439
447
  proxy(host.provider),
@@ -442,15 +450,26 @@ var TornadoCashProtocol = class {
442
450
  proxy(host.storage),
443
451
  proxy(initialState),
444
452
  proxy(artifactsLoader),
445
- { protocolConfig, accountIndex, relayerConfig, paymasterConfig, proverVersion }
453
+ externalSyncClient ? proxy(externalSyncClient) : void 0,
454
+ { protocolConfig, accountIndex, relayerConfig, paymasterConfig, minExternalSyncBlocksAmount }
446
455
  ),
447
456
  workerReady
448
457
  ]);
449
458
  return {
450
459
  sync: () => remote.sync(),
451
460
  getBalances: ((assets) => remote.getBalances(assets)),
461
+ getNotes: (params) => remote.getNotes(params),
452
462
  getDepositPayload: (params) => remote.getDepositPayload(params),
453
- getWithdrawalPayloads: (params) => remote.getWithdrawalPayloads(params),
463
+ getWithdrawalPayloads: (params) => {
464
+ if (params.mode === "paymaster") {
465
+ const { tailCalls, ...rest } = params;
466
+ return remote.getWithdrawalPayloads(
467
+ rest,
468
+ tailCalls ? proxy(tailCalls) : void 0
469
+ );
470
+ }
471
+ return remote.getWithdrawalPayloads(params);
472
+ },
454
473
  dumpState: (() => remote.dumpState())
455
474
  };
456
475
  })();
@@ -487,6 +506,23 @@ var TornadoCashProtocol = class {
487
506
  };
488
507
  });
489
508
  }
509
+ /**
510
+ * Returns all notes for the account.
511
+ * @param assets - Filter by specific assets (optional, if empty returns all)
512
+ * @param includeSpent - Include notes with zero balance (default: false)
513
+ */
514
+ async notes(assets = [], includeSpent = false) {
515
+ const stateManager = await this.stateManager;
516
+ await stateManager.sync();
517
+ const assetAddresses = assets.map(({ contract }) => {
518
+ const parsedAddress = BigInt(contract);
519
+ return parsedAddress === E_ADDRESS_BIGINT ? 0n : parsedAddress;
520
+ });
521
+ return stateManager.getNotes({
522
+ includeSpent,
523
+ assets: assetAddresses.length > 0 ? assetAddresses : void 0
524
+ });
525
+ }
490
526
  async prepareShield(assets, options) {
491
527
  const { asset, amount } = assets;
492
528
  const strategy = typeof options === "string" ? 1 /* MinFee */ : options?.strategy || 1 /* MinFee */;
@@ -500,7 +536,10 @@ var TornadoCashProtocol = class {
500
536
  });
501
537
  return { txns: tx };
502
538
  }
503
- async prepareUnshield(assets, to, options) {
539
+ async prepareUnshield(assets, to, options = { mode: "relayer" }) {
540
+ if (options.mode === "relayer" && options.tailCalls !== void 0) {
541
+ throw new Error("Tail Calls are only supported when using paymaster mode.");
542
+ }
504
543
  const { asset, amount } = assets;
505
544
  const parsedAsset = BigInt(asset.contract || E_ADDRESS_BIGINT);
506
545
  const stateManager = await this.stateManager;
@@ -515,7 +554,8 @@ var TornadoCashProtocol = class {
515
554
  withdrawals = await stateManager.getWithdrawalPayloads({
516
555
  ...baseParams,
517
556
  mode: "paymaster",
518
- delegation: options.delegation
557
+ delegation: options.delegation,
558
+ tailCalls: options.tailCalls
519
559
  });
520
560
  } else {
521
561
  withdrawals = await stateManager.getWithdrawalPayloads({
@@ -538,120 +578,51 @@ var TornadoCashProtocol = class {
538
578
  return stateManager.dumpState();
539
579
  }
540
580
  };
541
-
542
- // src/paymaster/fee.ts
543
- var ERC20_TRANSFER_GAS = 100000n;
544
- var baseGasUnits = {
545
- preVerificationGas: 80000n,
546
- verificationGasLimit: 50000n,
547
- callGasLimit: 300000n,
548
- paymasterVerificationGasLimit: 350000n,
549
- paymasterPostOpGasLimit: 10000n
550
- };
551
- function reasonableGasUnits(isERC20) {
552
- if (!isERC20) return baseGasUnits;
553
- return {
554
- ...baseGasUnits,
555
- callGasLimit: baseGasUnits.callGasLimit + ERC20_TRANSFER_GAS
556
- };
581
+ function createPaymasterBundlerClient(bundlerUrl) {
582
+ return createBundlerClient({ transport: http(bundlerUrl) });
557
583
  }
558
- async function signDelegationAuthorization({
559
- privateKey,
560
- accountAddress,
561
- chainId,
562
- nonce
563
- }) {
564
- const account = privateKeyToAccount(privateKey);
565
- const authorization = await account.signAuthorization({
566
- contractAddress: accountAddress,
567
- chainId,
568
- nonce
584
+ async function sendSerializedUserOperation(client, op, entryPoint) {
585
+ return client.request({
586
+ method: "eth_sendUserOperation",
587
+ params: [op, entryPoint]
569
588
  });
570
- return { authorization, senderAddress: account.address };
571
589
  }
572
590
 
573
591
  // src/paymaster/paymaster-broadcaster.ts
574
592
  var PaymasterBroadcaster = class _PaymasterBroadcaster {
575
- constructor(provider, options) {
576
- this.provider = provider;
577
- this.options = options;
578
- }
579
- provider;
580
- options;
581
593
  async broadcast(withdrawals) {
582
- const chainId = Number(await this.provider.getChainId());
583
- const {
584
- poolsAccountsMap: rawPoolsAccountsMap
585
- } = this.options[chainId];
586
- const poolAcountsMap = new Map(
587
- Object.entries(rawPoolsAccountsMap).map(([poolAccount, tornadoAccount]) => [
588
- BigInt(poolAccount),
589
- tornadoAccount
590
- ])
591
- );
592
- const results = await Promise.allSettled(
593
- withdrawals.map((w) => _PaymasterBroadcaster.broadcastOne({
594
- ...w,
595
- accountAddress: poolAcountsMap.get(w.poolAddress)
596
- }, this.provider))
594
+ const bySender = /* @__PURE__ */ new Map();
595
+ for (const w of withdrawals) {
596
+ const key = w.userOperation.sender.toLowerCase();
597
+ const group = bySender.get(key) ?? [];
598
+ group.push(w);
599
+ bySender.set(key, group);
600
+ }
601
+ const groupResults = await Promise.allSettled(
602
+ [...bySender.values()].map(async (group) => {
603
+ const out = [];
604
+ for (const w of group) {
605
+ out.push(await _PaymasterBroadcaster.broadcastOne(w));
606
+ }
607
+ return out;
608
+ })
597
609
  );
598
- const failed = results.filter((r) => r.status === "rejected");
610
+ const failed = groupResults.filter((r) => r.status === "rejected");
599
611
  if (failed.length > 0) {
600
612
  console.warn(
601
613
  `Some paymaster withdrawals failed.`,
602
614
  failed.map((e) => e.reason).join("\n")
603
615
  );
604
616
  }
605
- return results.filter((r) => r.status === "fulfilled").map((r) => r.value);
617
+ return groupResults.filter((r) => r.status === "fulfilled").flatMap((r) => r.value);
606
618
  }
607
- static async broadcastOne(withdrawal, provider) {
608
- const {
609
- proof: { proof: proofHex, args: proofArgs },
610
- paymasterAddress,
611
- entryPointAddress,
612
- bundlerUrl,
613
- accountAddress,
614
- isERC20
615
- } = withdrawal;
616
- const [root, nullifierHash, recipient, _paymasterAddress, feeHex] = proofArgs;
617
- if (BigInt(paymasterAddress) !== BigInt(_paymasterAddress)) {
618
- throw new Error(`relayer must be paymaster when using the 4337 paymaster flow: ${paymasterAddress} != ${_paymasterAddress}`);
619
- }
620
- const delegation = withdrawal.delegation ?? await _PaymasterBroadcaster.generateRandomDelegation(accountAddress, provider);
621
- const { senderAddress, authorization } = delegation;
622
- const bundlerClient = new BundlerClient(bundlerUrl, entryPointAddress);
623
- const { standard: { maxFeePerGas, maxPriorityFeePerGas } } = await bundlerClient.getUserOperationGasPrice();
624
- let gas;
625
- {
626
- gas = {
627
- type: "manual",
628
- ...reasonableGasUnits(isERC20),
629
- maxFeePerGas,
630
- maxPriorityFeePerGas
631
- };
632
- }
633
- const op = await new TornadoBuilder(senderAddress).withPaymaster(paymasterAddress).withAuthorization(authorization).withWithdraw(
634
- proofHex,
635
- root,
636
- nullifierHash,
637
- recipient,
638
- _paymasterAddress,
639
- BigInt(feeHex)
640
- ).withGas(gas).build(provider, bundlerClient);
641
- const userOpHash = await bundlerClient.sendUserOperation(op);
642
- await bundlerClient.waitForUserOperationReceipt(userOpHash);
619
+ static async broadcastOne(withdrawal) {
620
+ const { bundlerUrl, entryPointAddress, userOperation } = withdrawal;
621
+ const bundlerClient = createPaymasterBundlerClient(bundlerUrl);
622
+ const userOpHash = await sendSerializedUserOperation(bundlerClient, userOperation, entryPointAddress);
623
+ await bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
643
624
  return { userOpHash };
644
625
  }
645
- static async generateRandomDelegation(accountAddress, provider) {
646
- const privateKey = generatePrivateKey();
647
- const chainId = Number(await provider.getChainId());
648
- return signDelegationAuthorization({
649
- privateKey,
650
- accountAddress,
651
- chainId,
652
- nonce: 0
653
- });
654
- }
655
626
  };
656
627
 
657
628
  // src/plugin/broadcaster.ts
@@ -663,7 +634,7 @@ var TornadoCashBroadcaster = class {
663
634
  host,
664
635
  relayerClientFactory = () => new RelayerClient({ network: host.network }),
665
636
  paymasterConfig,
666
- paymasterClientFactory = () => new PaymasterBroadcaster(host.provider, paymasterConfig)
637
+ paymasterClientFactory = () => new PaymasterBroadcaster()
667
638
  }) {
668
639
  this.provider = host.provider;
669
640
  this.relayerClient = relayerClientFactory();