@kohaku-eth/tornado-cash 0.0.2-alpha.19 → 0.0.2-alpha.21

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
@@ -42,6 +42,14 @@ interface ISecretManager {
42
42
  deriveDelegatorSigner: (params: {
43
43
  path: string;
44
44
  }) => Promise<`0x${string}`>;
45
+ /**
46
+ * Derives the ephemeral signer for an imported legacy note. Legacy secrets
47
+ * have no keystore-derived `depositIndex`, so this is a pure function of
48
+ * the note's own nullifier instead of a BIP-32 derivation.
49
+ */
50
+ deriveLegacyEphemeralSigner: (params: {
51
+ nullifier: Nullifier$1;
52
+ }) => Promise<`0x${string}`>;
45
53
  }
46
54
  interface SecretManagerParams {
47
55
  host: Pick<Host, 'keystore'>;
@@ -361,6 +369,18 @@ declare enum DepositStrategy {
361
369
  MinFee = 1
362
370
  }
363
371
 
372
+ type ImportNoteResult = {
373
+ note: string;
374
+ status: 'imported';
375
+ poolAddress: Address;
376
+ } | {
377
+ note: string;
378
+ status: 'wrong-chain';
379
+ } | {
380
+ note: string;
381
+ status: 'not-found';
382
+ };
383
+
364
384
  /**
365
385
  * How the batch delegator for a tail-call paymaster withdrawal is produced.
366
386
  * Only honored when `tailCalls` are present (see `paymasterWithdrawThunk`).
@@ -545,6 +565,14 @@ interface IStateManager {
545
565
  * @param assets - Optional filter by specific assets
546
566
  */
547
567
  getNotes: (params: IGetNotesParams) => Promise<TCNote[]>;
568
+ /**
569
+ * Parses legacy (pre-SDK) Tornado Cash note strings and, for any that match
570
+ * a synced on-chain deposit on the current chain, registers them as
571
+ * importable user secrets.
572
+ */
573
+ importNotes: (params: {
574
+ notes: string[];
575
+ }) => Promise<ImportNoteResult[]>;
548
576
  dumpState: () => Record<StoreStorageKey, PublicRootState>;
549
577
  }
550
578
 
@@ -558,6 +586,7 @@ interface TCBaseCredential {
558
586
  accountIndex: number;
559
587
  }
560
588
  interface TCPluginParameters extends TCBroadcasterParameters, TCBaseCredential, Pick<TCProtocolParams, 'initialState' | 'minExternalSyncBlocksAmount'> {
589
+ secretManagerFactory?: TCProtocolParams['secretManagerFactory'];
561
590
  protocolConfig: TCProtocolConfig;
562
591
  relayerConfig?: IRelayerFeeConfig;
563
592
  stateManagerWorkerUrl?: string;
@@ -602,6 +631,7 @@ type TCInstance = PluginInstance<TCAddress, {
602
631
  prepareShield(asset: TCAssetAmount, options: TCPrepareShieldOptions): Promise<TCPublicOperation>;
603
632
  prepareUnshield(asset: TCAssetAmount, to: Address$1, options: TCRelayerUnshieldOptions): Promise<TCPrivateOperation<'relayer'>>;
604
633
  prepareUnshield(asset: TCAssetAmount, to: Address$1, options: TCPaymasterUnshieldOptions): Promise<TCPrivateOperation<'paymaster'>>;
634
+ importNotes(notes: string | string[]): Promise<ImportNoteResult[]>;
605
635
  };
606
636
  publicOp: TCPublicOperation;
607
637
  privateOp: TCPrivateOperation;
@@ -614,7 +644,7 @@ type RequireOnly<T, Keys extends keyof T> = Partial<T> & Pick<T, Keys>;
614
644
  declare class TornadoCashProtocol implements TCInstance {
615
645
  readonly host: Host;
616
646
  private stateManager;
617
- constructor(host: Host, { accountIndex, initialState, protocolConfig, artifactsLoader, stateManagerWorkerUrl, relayerConfig, paymasterConfig, relayerClientFactory, minExternalSyncBlocksAmount, }: RequireOnly<TCProtocolParams, 'protocolConfig'>);
647
+ constructor(host: Host, { accountIndex, initialState, protocolConfig, artifactsLoader, stateManagerWorkerUrl, relayerConfig, paymasterConfig, relayerClientFactory, minExternalSyncBlocksAmount, secretManagerFactory, }: RequireOnly<TCProtocolParams, 'protocolConfig'>);
618
648
  instanceId: () => Promise<"0x1">;
619
649
  /**
620
650
  * Only process supported assets or error out?
@@ -628,6 +658,11 @@ declare class TornadoCashProtocol implements TCInstance {
628
658
  * @param includeSpent - Include notes with zero balance (default: false)
629
659
  */
630
660
  notes(assets?: ERC20AssetId[], includeSpent?: boolean): Promise<TCNote[]>;
661
+ /**
662
+ * Parses legacy (pre-SDK) Tornado Cash note strings and registers any that
663
+ * match a synced on-chain deposit on the current chain as user secrets.
664
+ */
665
+ importNotes(notes: string | string[]): Promise<ImportNoteResult[]>;
631
666
  prepareShield(assets: TCAssetAmount, options?: TCPrepareShieldOptions | `0x${string}`): Promise<TCPublicOperation>;
632
667
  prepareUnshield(assets: AssetAmount, to: AccountId): Promise<TCPrivateOperation<'relayer'>>;
633
668
  prepareUnshield(assets: AssetAmount, to: AccountId, options?: TCRelayerUnshieldOptions): Promise<TCPrivateOperation<'relayer'>>;
@@ -717,4 +752,4 @@ declare const TornadoCashConfigs: {
717
752
  };
718
753
  };
719
754
 
720
- export { type Commitment, DepositStrategy, E_ADDRESS, type ISecretManager, type Nullifier, SecretManager, type SecretManagerParams, type TCAddress, type TCAssetAmount, type TCAssetBalance, type TCBroadcaster, type TCBroadcasterParameters, type TCInstance, type TCNote, type TCPaymasterUnshieldOptions, type TCPluginParameters, type TCPrepareShieldOptions, type TCPrepareUnshieldOptions, type TCRelayerUnshieldOptions, TornadoCashConfigs, TornadoCashProtocol, TornadoPaymasterConfigs, createTCBroadcaster, createTCPlugin };
755
+ export { type Commitment, DepositStrategy, E_ADDRESS, type ISecretManager, type ImportNoteResult, type Nullifier, SecretManager, type SecretManagerParams, type TCAddress, type TCAssetAmount, type TCAssetBalance, type TCBroadcaster, type TCBroadcasterParameters, type TCInstance, type TCNote, type TCPaymasterUnshieldOptions, type TCPluginParameters, type TCPrepareShieldOptions, type TCPrepareUnshieldOptions, type TCRelayerUnshieldOptions, TornadoCashConfigs, TornadoCashProtocol, TornadoPaymasterConfigs, createTCBroadcaster, createTCPlugin };
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { proxy } from 'comlink';
5
5
  import { loadStateManagerWorker } from '#worker-loader';
6
6
  import 'viem/accounts';
7
7
  import { createBundlerClient } from 'viem/account-abstraction';
8
- import { numberToBytesLE, bytesToNumberLE, concatBytes } from '@noble/curves/utils.js';
8
+ import { asciiToBytes, numberToBytesLE, concatBytes, bytesToHex, bytesToNumberLE } 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
11
 
@@ -216,10 +216,29 @@ createSelector(
216
216
  ([, relayer]) => deserialize(relayer, { ensName: true, hostname: true })
217
217
  )
218
218
  );
219
- var userSecretsSelector = selectEntityMap(
219
+ var derivedUserSecretsSelector = selectEntityMap(
220
220
  (s) => s.userSecrets.byPool,
221
221
  deserialize
222
222
  );
223
+ var legacyUserSecretsSelector = selectEntityMap(
224
+ (s) => s.legacySecrets.byPool,
225
+ deserialize
226
+ );
227
+ createSelector(
228
+ [derivedUserSecretsSelector, legacyUserSecretsSelector],
229
+ (derived, legacy) => {
230
+ const result = /* @__PURE__ */ new Map();
231
+ for (const [poolAddress, records] of legacy) {
232
+ const existing = result.get(poolAddress) ?? [];
233
+ result.set(poolAddress, [...existing, ...records.map((record) => ({ kind: "legacy", ...record }))]);
234
+ }
235
+ for (const [poolAddress, records] of derived) {
236
+ const existing = result.get(poolAddress) ?? [];
237
+ result.set(poolAddress, [...existing, ...records.map((record) => ({ kind: "derived", ...record }))]);
238
+ }
239
+ return result;
240
+ }
241
+ );
223
242
 
224
243
  // src/state/thunks/getDepositPayloadThunk.ts
225
244
  var DepositStrategy = /* @__PURE__ */ ((DepositStrategy2) => {
@@ -266,7 +285,7 @@ createAsyncThunk(
266
285
  const state = getState();
267
286
  const pools = poolsSelector(state);
268
287
  const { chainId } = instanceRegistryInfoSelector(state);
269
- const userSecrets = userSecretsSelector(state);
288
+ const userSecrets = derivedUserSecretsSelector(state);
270
289
  const poolsCandidates = Array.from(pools.values()).filter((p) => p.asset === asset);
271
290
  if (poolsCandidates.length === 0) throw new Error(`No pools available for asset ${addressToHex(asset)}`);
272
291
  const poolsToDepositInto = calculatePoolsDeposits(poolsCandidates, amount, strategy);
@@ -456,7 +475,8 @@ var TornadoCashProtocol = class {
456
475
  relayerConfig,
457
476
  paymasterConfig = TornadoPaymasterConfigs,
458
477
  relayerClientFactory = () => new RelayerClient(host),
459
- minExternalSyncBlocksAmount
478
+ minExternalSyncBlocksAmount,
479
+ secretManagerFactory
460
480
  }) {
461
481
  this.host = host;
462
482
  this.stateManager = (async () => {
@@ -479,6 +499,7 @@ var TornadoCashProtocol = class {
479
499
  firstCoveredBlock: (params) => externalSyncProvider.firstCoveredBlock(params),
480
500
  lastCoveredBlock: (params) => externalSyncProvider.lastCoveredBlock(params)
481
501
  };
502
+ const resolvedSecretManager = secretManagerFactory ? await secretManagerFactory({ host: { keystore: host.keystore }, accountIndex }) : void 0;
482
503
  await Promise.race([
483
504
  remote.init(
484
505
  proxy(host.provider),
@@ -487,6 +508,7 @@ var TornadoCashProtocol = class {
487
508
  proxy(host.storage),
488
509
  proxy(initialState),
489
510
  proxy(artifactsLoader),
511
+ resolvedSecretManager ? proxy(resolvedSecretManager) : void 0,
490
512
  externalSyncClient ? proxy(externalSyncClient) : void 0,
491
513
  { protocolConfig, accountIndex, relayerConfig, paymasterConfig, minExternalSyncBlocksAmount }
492
514
  ),
@@ -497,6 +519,7 @@ var TornadoCashProtocol = class {
497
519
  getBalances: ((assets) => remote.getBalances(assets)),
498
520
  getNotes: (params) => remote.getNotes(params),
499
521
  getDepositPayload: (params) => remote.getDepositPayload(params),
522
+ importNotes: (params) => remote.importNotes(params),
500
523
  getWithdrawalPayloads: (params) => {
501
524
  if (params.mode === "paymaster") {
502
525
  const { tailCalls, ...rest } = params;
@@ -560,6 +583,15 @@ var TornadoCashProtocol = class {
560
583
  assets: assetAddresses.length > 0 ? assetAddresses : void 0
561
584
  });
562
585
  }
586
+ /**
587
+ * Parses legacy (pre-SDK) Tornado Cash note strings and registers any that
588
+ * match a synced on-chain deposit on the current chain as user secrets.
589
+ */
590
+ async importNotes(notes) {
591
+ const stateManager = await this.stateManager;
592
+ await stateManager.sync();
593
+ return stateManager.importNotes({ notes: Array.isArray(notes) ? notes : [notes] });
594
+ }
563
595
  async prepareShield(assets, options) {
564
596
  const { asset, amount } = assets;
565
597
  const strategy = typeof options === "string" ? 1 /* MinFee */ : options?.strategy || 1 /* MinFee */;
@@ -768,10 +800,16 @@ async function SecretManager({
768
800
  const deriveDelegatorSigner = async ({ path }) => {
769
801
  return Promise.resolve(keystore.deriveAt(path));
770
802
  };
803
+ const LEGACY_EPHEMERAL_SALT = asciiToBytes("KOHAKU7702");
804
+ const deriveLegacyEphemeralSigner = async ({ nullifier }) => {
805
+ const hash = keccak_256(concatBytes(numberToBytesLE(nullifier, 31), LEGACY_EPHEMERAL_SALT));
806
+ return `0x${bytesToHex(hash)}`;
807
+ };
771
808
  return {
772
809
  getDepositSecrets: (params) => deriveSecrets(params),
773
810
  deriveEphemeralSigner,
774
- deriveDelegatorSigner
811
+ deriveDelegatorSigner,
812
+ deriveLegacyEphemeralSigner
775
813
  };
776
814
  }
777
815
  function tcPath({ accountIndex, secretType, depositIndex }) {