signet.js 0.2.0 → 0.3.1-beta.1

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,7 +1,7 @@
1
1
  import { t as __export } from "./chunk-BAz01cYq.js";
2
2
  import { base58 } from "@scure/base";
3
3
  import elliptic from "elliptic";
4
- import { concat, concatHex, createPublicClient, encodeAbiParameters, encodeFunctionData, getAddress, hashMessage, hashTypedData, hexToBigInt, http, isAddress, keccak256, numberToHex, pad, parseAbiParameters, parseTransaction, recoverAddress, serializeTransaction, toBytes, withRetry } from "viem";
4
+ import { concat, concatHex, createPublicClient, encodeAbiParameters, encodeFunctionData, encodePacked, getAddress, hashMessage, hashTypedData, hexToBigInt, http, isAddress, keccak256, numberToHex, pad, parseAbiParameters, parseTransaction, recoverAddress, serializeTransaction, toBytes, withRetry } from "viem";
5
5
  import * as bitcoin from "bitcoinjs-lib";
6
6
  import coinselect from "coinselect";
7
7
  import { encodeSecp256k1Pubkey } from "@cosmjs/amino";
@@ -134,7 +134,7 @@ const EPSILON_DERIVATION_PREFIX_V2 = "sig.network v2.0.0 epsilon derivation";
134
134
  * @param keyVersion - Key version controlling which derivation prefix to use (legacy v1 for 0, CAIP-2 v2 otherwise)
135
135
  * @returns The derived child public key in uncompressed SEC1 format (04 || x || y)
136
136
  */
137
- function deriveChildPublicKey(rootUncompressedPubKeySEC1, predecessorId, path = "", chainId, keyVersion) {
137
+ function deriveChildPublicKey(rootUncompressedPubKeySEC1, predecessorId, path = "", chainId, _keyVersion) {
138
138
  const ec = new EC("secp256k1");
139
139
  const derivationPath = `${EPSILON_DERIVATION_PREFIX_V2}:${chainId}:${predecessorId}:${path}`;
140
140
  let scalarHex = "";
@@ -219,7 +219,7 @@ var EVM = class extends ChainAdapter {
219
219
  async attachGasAndNonce(transaction) {
220
220
  const fees = await fetchEVMFeeProperties(this.client, transaction);
221
221
  const nonce = await this.client.getTransactionCount({ address: transaction.from });
222
- const { from, ...rest } = transaction;
222
+ const { from: _from, ...rest } = transaction;
223
223
  return {
224
224
  ...fees,
225
225
  nonce,
@@ -1348,7 +1348,39 @@ const getRootPublicKey = (contractAddress, chain) => {
1348
1348
 
1349
1349
  //#endregion
1350
1350
  //#region src/contracts/evm/utils.ts
1351
- const getRequestId = (request) => {
1351
+ /**
1352
+ * Generates a unique request ID for EVM signature requests using keccak256 hashing.
1353
+ *
1354
+ * The request ID is computed by ABI-encoding the request parameters and hashing
1355
+ * the result. This ID is used to track signature requests and match them with
1356
+ * responses from the MPC network.
1357
+ *
1358
+ * @param request - The signature request parameters
1359
+ * @param request.address - The address of the requester (EVM address format)
1360
+ * @param request.payload - The data payload to be signed as a hex string
1361
+ * @param request.path - The derivation path for the signing key
1362
+ * @param request.keyVersion - The version of the signing key
1363
+ * @param request.chainId - The chain ID where the request originated
1364
+ * @param request.algo - The signing algorithm identifier
1365
+ * @param request.dest - The destination identifier for the signature
1366
+ * @param request.params - Additional parameters for the signing process
1367
+ * @returns The keccak256 hash of the encoded request as a hex string
1368
+ *
1369
+ * @example
1370
+ * ```typescript
1371
+ * const requestId = getRequestIdRespond({
1372
+ * address: '0x1234...abcd',
1373
+ * payload: '0xdeadbeef',
1374
+ * path: 'ethereum,1',
1375
+ * keyVersion: 0,
1376
+ * chainId: 1n,
1377
+ * algo: '',
1378
+ * dest: '',
1379
+ * params: '',
1380
+ * })
1381
+ * ```
1382
+ */
1383
+ const getRequestIdRespond$1 = (request) => {
1352
1384
  return keccak256(encodeAbiParameters([
1353
1385
  { type: "address" },
1354
1386
  { type: "bytes" },
@@ -1555,7 +1587,7 @@ var ChainSignatureContract$2 = class extends ChainSignatureContract {
1555
1587
  }) {
1556
1588
  if (!this.walletClient.account) throw new Error("Wallet client account required to compute requestId");
1557
1589
  if (!this.publicClient.chain?.id) throw new Error("Public client chain required to compute requestId");
1558
- return getRequestId({
1590
+ return getRequestIdRespond$1({
1559
1591
  payload: `0x${Buffer.from(args.payload).toString("hex")}`,
1560
1592
  path: args.path,
1561
1593
  keyVersion: args.key_version,
@@ -1609,6 +1641,7 @@ var ChainSignatureContract$2 = class extends ChainSignatureContract {
1609
1641
  //#region src/contracts/evm/index.ts
1610
1642
  var evm_exports = /* @__PURE__ */ __export({
1611
1643
  ChainSignatureContract: () => ChainSignatureContract$2,
1644
+ getRequestIdRespond: () => getRequestIdRespond$1,
1612
1645
  utils: () => utils$1
1613
1646
  });
1614
1647
  const utils$1 = {
@@ -2652,24 +2685,33 @@ const EMIT_CPI_INSTRUCTION_DISCRIMINATOR = Buffer.from([
2652
2685
  ]);
2653
2686
  var CpiEventParser = class {
2654
2687
  /**
2655
- * Parse CPI events from a transaction (emit_cpi! pattern)
2688
+ * Parse CPI events from an already-fetched transaction (emit_cpi! pattern).
2656
2689
  */
2657
- static async parseCpiEvents(connection, signature, targetProgramId, program) {
2690
+ static parseCpiEventsFromTransaction(tx, targetProgramId, program) {
2658
2691
  const events$1 = [];
2692
+ if (!tx?.meta?.innerInstructions) return events$1;
2693
+ for (const innerIxSet of tx.meta.innerInstructions) for (const instruction of innerIxSet.instructions) {
2694
+ if (!("programId" in instruction) || !("data" in instruction)) continue;
2695
+ if (instruction.programId.toString() !== targetProgramId) continue;
2696
+ const parsedEvent = this.parseInstruction(instruction.data, program);
2697
+ if (parsedEvent) events$1.push(parsedEvent);
2698
+ }
2699
+ return events$1;
2700
+ }
2701
+ /**
2702
+ * Fetch a transaction by signature and parse its CPI events.
2703
+ * Used by the subscription path where only the signature is available.
2704
+ */
2705
+ static async fetchAndParseCpiEvents(connection, signature, targetProgramId, program) {
2659
2706
  try {
2660
2707
  const tx = await connection.getParsedTransaction(signature, {
2661
2708
  commitment: "confirmed",
2662
2709
  maxSupportedTransactionVersion: 0
2663
2710
  });
2664
- if (!tx?.meta?.innerInstructions) return events$1;
2665
- for (const innerIxSet of tx.meta.innerInstructions) for (const instruction of innerIxSet.instructions) {
2666
- if (!("programId" in instruction) || !("data" in instruction)) continue;
2667
- if (instruction.programId.toString() !== targetProgramId) continue;
2668
- const parsedEvent = this.parseInstruction(instruction.data, program);
2669
- if (parsedEvent) events$1.push(parsedEvent);
2670
- }
2671
- } catch {}
2672
- return events$1;
2711
+ return this.parseCpiEventsFromTransaction(tx, targetProgramId, program);
2712
+ } catch {
2713
+ return [];
2714
+ }
2673
2715
  }
2674
2716
  /**
2675
2717
  * Parse CPI event from instruction data
@@ -2699,7 +2741,7 @@ var CpiEventParser = class {
2699
2741
  return connection.onLogs(program.programId, (logs, context) => {
2700
2742
  if (logs.err) return;
2701
2743
  (async () => {
2702
- const events$1 = await this.parseCpiEvents(connection, logs.signature, program.programId.toString(), program);
2744
+ const events$1 = await this.fetchAndParseCpiEvents(connection, logs.signature, program.programId.toString(), program);
2703
2745
  for (const event of events$1) {
2704
2746
  const handler = eventHandlers.get(event.name);
2705
2747
  if (handler) try {
@@ -2713,17 +2755,103 @@ var CpiEventParser = class {
2713
2755
 
2714
2756
  //#endregion
2715
2757
  //#region src/contracts/solana/utils.ts
2716
- function generateRequestIdSolana({ address: address$1, payload, path, keyVersion, chainId, algo, dest, params }) {
2717
- const payloadHex = "0x" + Buffer.from(payload).toString("hex");
2758
+ /**
2759
+ * Generates a unique request ID for Solana signature requests using keccak256 hashing.
2760
+ *
2761
+ * The request ID is computed by ABI-encoding the request parameters and hashing
2762
+ * the result. This ID is used to track signature requests and match them with
2763
+ * responses from the MPC network.
2764
+ *
2765
+ * @param request - The signature request parameters
2766
+ * @param request.address - The sender's address (Solana public key as string)
2767
+ * @param request.payload - The data payload to be signed
2768
+ * @param request.path - The derivation path for the signing key
2769
+ * @param request.keyVersion - The version of the signing key
2770
+ * @param request.chainId - The CAIP-2 chain identifier (e.g., 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
2771
+ * @param request.algo - The signing algorithm identifier
2772
+ * @param request.dest - The destination identifier for the signature
2773
+ * @param request.params - Additional parameters for the signing process
2774
+ * @returns The keccak256 hash of the encoded request as a hex string
2775
+ *
2776
+ * @example
2777
+ * ```typescript
2778
+ * const requestId = getRequestIdRespond({
2779
+ * address: 'So11111111111111111111111111111111111111112',
2780
+ * payload: new Uint8Array([1, 2, 3, 4]),
2781
+ * path: 'solana,1',
2782
+ * keyVersion: 0,
2783
+ * chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
2784
+ * algo: '',
2785
+ * dest: '',
2786
+ * params: '',
2787
+ * })
2788
+ * ```
2789
+ */
2790
+ function getRequestIdRespond(request) {
2791
+ const payloadHex = "0x" + Buffer.from(request.payload).toString("hex");
2718
2792
  return keccak256(encodeAbiParameters(parseAbiParameters("string, bytes, string, uint32, string, string, string, string"), [
2719
- address$1,
2793
+ request.address,
2794
+ payloadHex,
2795
+ request.path,
2796
+ request.keyVersion,
2797
+ request.chainId,
2798
+ request.algo,
2799
+ request.dest,
2800
+ request.params
2801
+ ]));
2802
+ }
2803
+ /**
2804
+ * Generates a unique request ID for bidirectional sign operations using keccak256 hashing.
2805
+ *
2806
+ * Unlike `getRequestIdRespond`, this function uses packed encoding (solidityPacked)
2807
+ * instead of standard ABI encoding. This is used for cross-chain bidirectional
2808
+ * signing flows where the request originates from a different chain.
2809
+ *
2810
+ * @param request - The bidirectional signature request parameters
2811
+ * @param request.sender - The sender's address (Solana public key as string)
2812
+ * @param request.payload - The data payload to be signed
2813
+ * @param request.caip2Id - The CAIP-2 chain identifier (e.g., 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
2814
+ * @param request.keyVersion - The version of the signing key
2815
+ * @param request.path - The derivation path for the signing key
2816
+ * @param request.algo - The signing algorithm identifier
2817
+ * @param request.dest - The destination identifier for the signature
2818
+ * @param request.params - Additional parameters for the signing process
2819
+ * @returns The keccak256 hash of the packed encoded request as a hex string
2820
+ *
2821
+ * @example
2822
+ * ```typescript
2823
+ * const requestId = getRequestIdBidirectional({
2824
+ * sender: 'So11111111111111111111111111111111111111112',
2825
+ * payload: new Uint8Array([1, 2, 3, 4]),
2826
+ * caip2Id: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
2827
+ * keyVersion: 0,
2828
+ * path: 'ethereum,1',
2829
+ * algo: '',
2830
+ * dest: '',
2831
+ * params: '',
2832
+ * })
2833
+ * ```
2834
+ */
2835
+ function getRequestIdBidirectional(request) {
2836
+ const payloadHex = `0x${Buffer.from(request.payload).toString("hex")}`;
2837
+ return keccak256(encodePacked([
2838
+ "string",
2839
+ "bytes",
2840
+ "string",
2841
+ "uint32",
2842
+ "string",
2843
+ "string",
2844
+ "string",
2845
+ "string"
2846
+ ], [
2847
+ request.sender,
2720
2848
  payloadHex,
2721
- path,
2722
- keyVersion,
2723
- chainId,
2724
- algo,
2725
- dest,
2726
- params
2849
+ request.caip2Id,
2850
+ request.keyVersion,
2851
+ request.path,
2852
+ request.algo,
2853
+ request.dest,
2854
+ request.params
2727
2855
  ]));
2728
2856
  }
2729
2857
 
@@ -2799,15 +2927,15 @@ var ChainSignatureContract$1 = class extends ChainSignatureContract {
2799
2927
  }
2800
2928
  /**
2801
2929
  * Sends a transaction to the program to request a signature, then
2802
- * polls for the signature result. If the signature is not found within the retry
2803
- * parameters, it will throw an error.
2930
+ * races a WebSocket listener against polling backfill to find the result.
2931
+ * If the signature is not found within the timeout, it will throw an error.
2804
2932
  */
2805
2933
  async sign(args, options) {
2806
2934
  const algo = options?.sign?.algo ?? "";
2807
2935
  const dest = options?.sign?.dest ?? "";
2808
2936
  const params = options?.sign?.params ?? "";
2809
2937
  const delay = options?.retry?.delay ?? 5e3;
2810
- const retryCount = options?.retry?.retryCount ?? 12;
2938
+ const timeoutMs = delay * (options?.retry?.retryCount ?? 12);
2811
2939
  if (options?.remainingAccounts?.filter((acc) => acc.isSigner)?.some((acc) => !options?.remainingSigners?.some((signer) => signer.publicKey.equals(acc.pubkey)))) throw new Error("All accounts marked as signers must have a corresponding signer");
2812
2940
  const requestId = this.getRequestId(args, {
2813
2941
  algo,
@@ -2825,24 +2953,38 @@ var ChainSignatureContract$1 = class extends ChainSignatureContract {
2825
2953
  const transaction = new Transaction().add(instruction);
2826
2954
  transaction.feePayer = this.provider.wallet.publicKey;
2827
2955
  const hash = await this.sendAndConfirmWithoutWebSocket(transaction, options?.remainingSigners);
2956
+ const controller = new AbortController();
2957
+ const successPromise = this.waitForEvent({
2958
+ eventName: "signatureRespondedEvent",
2959
+ requestId,
2960
+ signer: this.programId,
2961
+ afterSignature: hash,
2962
+ timeoutMs,
2963
+ backfillIntervalMs: delay,
2964
+ signal: controller.signal
2965
+ });
2966
+ const errorPromise = this.waitForEvent({
2967
+ eventName: "signatureErrorEvent",
2968
+ requestId,
2969
+ signer: this.programId,
2970
+ afterSignature: hash,
2971
+ timeoutMs,
2972
+ backfillIntervalMs: delay,
2973
+ signal: controller.signal
2974
+ }).then((err) => {
2975
+ throw new SignatureContractError(err.error, requestId, { hash });
2976
+ });
2977
+ successPromise.catch(() => {});
2978
+ errorPromise.catch(() => {});
2828
2979
  try {
2829
- const pollResult = await this.pollForRequestId({
2830
- requestId,
2831
- payload: args.payload,
2832
- path: args.path,
2833
- afterSignature: hash,
2834
- options: {
2835
- delay,
2836
- retryCount
2837
- }
2838
- });
2839
- if (!pollResult) throw new SignatureNotFoundError(requestId, { hash });
2840
- if ("error" in pollResult) throw new SignatureContractError(pollResult.error, requestId, { hash });
2841
- if (!await verifyRecoveredAddress(pollResult, args.payload, this.requesterAddress, args.path, this, args.key_version)) throw new SigningError(requestId, { hash }, /* @__PURE__ */ new Error("Signature verification failed: recovered address does not match expected address"));
2842
- return pollResult;
2980
+ const result = await Promise.race([successPromise, errorPromise]);
2981
+ if (!await verifyRecoveredAddress(result, args.payload, this.requesterAddress, args.path, this, args.key_version)) throw new SigningError(requestId, { hash }, /* @__PURE__ */ new Error("Signature verification failed: recovered address does not match expected address"));
2982
+ return result;
2843
2983
  } catch (error) {
2844
2984
  if (error instanceof SignatureNotFoundError || error instanceof SignatureContractError) throw error;
2845
2985
  else throw new SigningError(requestId, { hash }, error instanceof Error ? error : void 0);
2986
+ } finally {
2987
+ controller.abort();
2846
2988
  }
2847
2989
  }
2848
2990
  async sendAndConfirmWithoutWebSocket(transaction, signers) {
@@ -2866,70 +3008,147 @@ var ChainSignatureContract$1 = class extends ChainSignatureContract {
2866
3008
  throw new TransactionExpiredTimeoutError(signature, timeout / 1e3);
2867
3009
  }
2868
3010
  /**
2869
- * Polls for signature or error events matching the given requestId starting from the solana transaction with signature afterSignature.
2870
- * Returns a signature, error data, or undefined if nothing is found.
3011
+ * Waits for a specific event matching the given requestId by combining
3012
+ * a WebSocket listener (real-time) with polling backfill (resilience).
2871
3013
  */
2872
- async pollForRequestId({ requestId, payload: _payload, path: _path, afterSignature, options }) {
2873
- const delay = options?.delay ?? 5e3;
2874
- const retryCount = options?.retryCount ?? 12;
2875
- let lastCheckedSignature = afterSignature;
2876
- for (let i = 0; i < retryCount; i++) {
2877
- try {
2878
- const signatures = await this.connection.getSignaturesForAddress(this.programId, {
2879
- until: lastCheckedSignature,
2880
- limit: 50
2881
- }, "confirmed");
2882
- if (signatures.length > 0) lastCheckedSignature = signatures[signatures.length - 1].signature;
2883
- for (const sig of signatures) {
2884
- const tx = await this.connection.getParsedTransaction(sig.signature, {
2885
- commitment: "confirmed",
2886
- maxSupportedTransactionVersion: 0
3014
+ async waitForEvent(options) {
3015
+ const { eventName, requestId, signer, afterSignature, timeoutMs = 6e4, backfillIntervalMs = 5e3, backfillLimit = 50, signal } = options;
3016
+ return await new Promise((resolve, reject) => {
3017
+ let settled = false;
3018
+ const seenSignatures = /* @__PURE__ */ new Set();
3019
+ let lastCheckedSignature = afterSignature;
3020
+ const cleanupFns = [];
3021
+ const cleanup = () => {
3022
+ for (const fn of cleanupFns) try {
3023
+ fn();
3024
+ } catch {}
3025
+ };
3026
+ const settle = (action) => {
3027
+ if (settled) return;
3028
+ settled = true;
3029
+ cleanup();
3030
+ action();
3031
+ };
3032
+ const processEvent = (name, data, txSignature) => {
3033
+ if (settled) return false;
3034
+ if (txSignature && seenSignatures.has(txSignature)) return false;
3035
+ if (txSignature) seenSignatures.add(txSignature);
3036
+ if (name !== eventName) return false;
3037
+ const result = this.mapEventForName(eventName, data, requestId);
3038
+ if (result !== void 0) {
3039
+ settle(() => {
3040
+ resolve(result);
2887
3041
  });
2888
- if (tx?.meta?.logMessages) {
2889
- const result = await this.parseLogsForEvents(tx.meta.logMessages, requestId, sig.signature);
2890
- if (result) return result;
2891
- }
3042
+ return true;
2892
3043
  }
2893
- } catch (error) {
2894
- console.error("Error checking for events:", error);
2895
- }
2896
- if (i < retryCount - 1) {
2897
- console.log(`Retrying get signature: ${i + 1}/${retryCount}`);
2898
- await new Promise((resolve) => setTimeout(resolve, delay));
3044
+ return false;
3045
+ };
3046
+ if (signal) {
3047
+ if (signal.aborted) {
3048
+ settle(() => {
3049
+ reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
3050
+ });
3051
+ return;
3052
+ }
3053
+ const onAbort = () => {
3054
+ settle(() => {
3055
+ reject(signal.reason ?? /* @__PURE__ */ new Error("Aborted"));
3056
+ });
3057
+ };
3058
+ signal.addEventListener("abort", onAbort, { once: true });
3059
+ cleanupFns.push(() => {
3060
+ signal.removeEventListener("abort", onAbort);
3061
+ });
2899
3062
  }
2900
- }
3063
+ const timeoutId = setTimeout(() => {
3064
+ settle(() => {
3065
+ reject(new SignatureNotFoundError(requestId));
3066
+ });
3067
+ }, timeoutMs);
3068
+ cleanupFns.push(() => {
3069
+ clearTimeout(timeoutId);
3070
+ });
3071
+ const parser = new EventParser(this.program.programId, this.program.coder);
3072
+ const subId = this.connection.onLogs(signer, (logs, _context) => {
3073
+ if (settled) return;
3074
+ if (logs.err) return;
3075
+ for (const evt of parser.parseLogs(logs.logs)) {
3076
+ if (!evt) continue;
3077
+ if (processEvent(evt.name, evt.data, logs.signature)) return;
3078
+ }
3079
+ CpiEventParser.fetchAndParseCpiEvents(this.connection, logs.signature, this.programId.toString(), this.program).then((cpiEvents) => {
3080
+ for (const event of cpiEvents) if (processEvent(event.name, event.data, logs.signature)) return;
3081
+ });
3082
+ }, "confirmed");
3083
+ cleanupFns.push(() => {
3084
+ this.connection.removeOnLogsListener(subId);
3085
+ });
3086
+ const runBackfill = async () => {
3087
+ if (settled) return;
3088
+ try {
3089
+ const signatures = await this.connection.getSignaturesForAddress(signer, {
3090
+ until: lastCheckedSignature,
3091
+ limit: backfillLimit
3092
+ }, "confirmed");
3093
+ if (signatures.length > 0) lastCheckedSignature = signatures[0].signature;
3094
+ for (const sig of signatures) {
3095
+ if (settled) return;
3096
+ if (seenSignatures.has(sig.signature)) continue;
3097
+ const tx = await this.connection.getParsedTransaction(sig.signature, {
3098
+ commitment: "confirmed",
3099
+ maxSupportedTransactionVersion: 0
3100
+ });
3101
+ if (!tx) continue;
3102
+ const cpiEvents = CpiEventParser.parseCpiEventsFromTransaction(tx, this.programId.toString(), this.program);
3103
+ for (const event of cpiEvents) if (processEvent(event.name, event.data, sig.signature)) return;
3104
+ const logs = tx.meta?.logMessages;
3105
+ if (logs) for (const evt of parser.parseLogs(logs)) {
3106
+ if (!evt) continue;
3107
+ if (processEvent(evt.name, evt.data, sig.signature)) return;
3108
+ }
3109
+ }
3110
+ } catch {}
3111
+ };
3112
+ runBackfill();
3113
+ const intervalId = setInterval(() => {
3114
+ runBackfill();
3115
+ }, backfillIntervalMs);
3116
+ cleanupFns.push(() => {
3117
+ clearInterval(intervalId);
3118
+ });
3119
+ });
2901
3120
  }
2902
- /**
2903
- * Parses transaction logs for signature or error events.
2904
- */
2905
- async parseLogsForEvents(logs, requestId, signature) {
2906
- const cpiEvents = await CpiEventParser.parseCpiEvents(this.connection, signature, this.programId.toString(), this.program);
2907
- for (const event of cpiEvents) {
2908
- const mapped = this.mapEventToResult(event.name, event.name === "signatureRespondedEvent" ? event.data : event.data, requestId);
2909
- if (mapped) return mapped;
2910
- }
2911
- const parser = new EventParser(this.program.programId, this.program.coder);
2912
- for (const evt of parser.parseLogs(logs)) {
2913
- if (!evt) continue;
2914
- const mapped = this.mapEventToResult(evt.name, evt.name === "signatureRespondedEvent" ? evt.data : evt.data, requestId);
2915
- if (mapped) return mapped;
2916
- }
3121
+ mapRespondToResult(data, requestId) {
3122
+ if ("0x" + Buffer.from(data.requestId).toString("hex") !== requestId) return void 0;
3123
+ return {
3124
+ r: Buffer.from(data.signature.bigR.x).toString("hex"),
3125
+ s: Buffer.from(data.signature.s).toString("hex"),
3126
+ v: data.signature.recoveryId + 27
3127
+ };
2917
3128
  }
2918
- mapEventToResult(name, data, requestId) {
3129
+ mapRespondErrorToResult(data, requestId) {
2919
3130
  const eventRequestIdHex = "0x" + Buffer.from(data.requestId).toString("hex");
2920
- if (name === "signatureRespondedEvent" && eventRequestIdHex === requestId) {
2921
- const d = data;
2922
- return {
2923
- r: Buffer.from(d.signature.bigR.x).toString("hex"),
2924
- s: Buffer.from(d.signature.s).toString("hex"),
2925
- v: d.signature.recoveryId + 27
2926
- };
2927
- }
2928
- if (name === "signatureErrorEvent" && eventRequestIdHex === requestId) return {
3131
+ if (eventRequestIdHex !== requestId) return void 0;
3132
+ return {
2929
3133
  requestId: eventRequestIdHex,
2930
3134
  error: data.error
2931
3135
  };
2932
3136
  }
3137
+ mapRespondBidirectionalToResult(data, requestId) {
3138
+ if ("0x" + Buffer.from(data.requestId).toString("hex") !== requestId) return void 0;
3139
+ return {
3140
+ serializedOutput: data.serializedOutput,
3141
+ signature: data.signature
3142
+ };
3143
+ }
3144
+ mapEventForName(eventName, data, requestId) {
3145
+ switch (eventName) {
3146
+ case "signatureRespondedEvent": return this.mapRespondToResult(data, requestId);
3147
+ case "signatureErrorEvent": return this.mapRespondErrorToResult(data, requestId);
3148
+ case "respondBidirectionalEvent": return this.mapRespondBidirectionalToResult(data, requestId);
3149
+ default: return;
3150
+ }
3151
+ }
2933
3152
  /**
2934
3153
  * Generates the request ID for a signature request allowing to track the response.
2935
3154
  */
@@ -2938,7 +3157,7 @@ var ChainSignatureContract$1 = class extends ChainSignatureContract {
2938
3157
  dest: "",
2939
3158
  params: ""
2940
3159
  }) {
2941
- return generateRequestIdSolana({
3160
+ return getRequestIdRespond({
2942
3161
  payload: args.payload,
2943
3162
  path: args.path,
2944
3163
  keyVersion: args.key_version,
@@ -2949,52 +3168,14 @@ var ChainSignatureContract$1 = class extends ChainSignatureContract {
2949
3168
  chainId: KDF_CHAIN_IDS.SOLANA
2950
3169
  });
2951
3170
  }
2952
- /**
2953
- * Subscribes to program events using Anchor's EventParser for regular events,
2954
- * and CPI parsing for emit_cpi!-emitted events. Returns an unsubscribe fn.
2955
- */
2956
- async subscribeToEvents(handlers) {
2957
- const commitment = "confirmed";
2958
- const cpiHandlers = /* @__PURE__ */ new Map();
2959
- if (handlers.onSignatureResponded) {
2960
- const onSignatureResponded = handlers.onSignatureResponded;
2961
- cpiHandlers.set("signatureRespondedEvent", async (e, s) => {
2962
- await onSignatureResponded(e, s);
2963
- });
2964
- }
2965
- if (handlers.onSignatureError) {
2966
- const onSignatureError = handlers.onSignatureError;
2967
- cpiHandlers.set("signatureErrorEvent", async (e, s) => {
2968
- await onSignatureError(e, s);
2969
- });
2970
- }
2971
- const cpiSubId = CpiEventParser.subscribeToCpiEvents(this.connection, this.program, cpiHandlers);
2972
- const parser = new EventParser(this.program.programId, this.program.coder);
2973
- const subId = this.connection.onLogs(this.program.programId, (logs, context) => {
2974
- if (logs.err) return;
2975
- for (const evt of parser.parseLogs(logs.logs)) {
2976
- if (!evt) continue;
2977
- if (evt.name === "signatureRespondedEvent") {
2978
- const onSignatureResponded = handlers.onSignatureResponded;
2979
- if (onSignatureResponded) onSignatureResponded(evt.data, context.slot);
2980
- }
2981
- if (evt.name === "signatureErrorEvent") {
2982
- const onSignatureError = handlers.onSignatureError;
2983
- if (onSignatureError) onSignatureError(evt.data, context.slot);
2984
- }
2985
- }
2986
- }, commitment);
2987
- return async () => {
2988
- await this.connection.removeOnLogsListener(subId);
2989
- await this.connection.removeOnLogsListener(cpiSubId);
2990
- };
2991
- }
2992
3171
  };
2993
3172
 
2994
3173
  //#endregion
2995
3174
  //#region src/contracts/solana/index.ts
2996
3175
  var solana_exports = /* @__PURE__ */ __export({
2997
3176
  ChainSignatureContract: () => ChainSignatureContract$1,
3177
+ getRequestIdBidirectional: () => getRequestIdBidirectional,
3178
+ getRequestIdRespond: () => getRequestIdRespond,
2998
3179
  utils: () => utils
2999
3180
  });
3000
3181
  const utils = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signet.js",
3
- "version": "0.2.0",
3
+ "version": "0.3.1-beta.1",
4
4
  "description": "A TypeScript library for handling multi-chain transactions and signatures using Signet MPC",
5
5
  "type": "module",
6
6
  "exports": {
@@ -37,7 +37,9 @@
37
37
  "docs:dev": "vocs dev",
38
38
  "docs:build": "vocs build",
39
39
  "docs:preview": "vocs preview",
40
- "format": "prettier --write ."
40
+ "format": "prettier --write .",
41
+ "lint": "eslint 'src/**/*.ts'",
42
+ "typecheck": "tsc --noEmit"
41
43
  },
42
44
  "author": "Sig Network",
43
45
  "license": "MIT",
@@ -59,28 +61,26 @@
59
61
  "@aa-sdk/core": "^4.13.0",
60
62
  "@account-kit/infra": "^4.13.0",
61
63
  "@account-kit/smart-contracts": "^4.13.0",
64
+ "@eslint/js": "^9.18.0",
62
65
  "@noble/curves": "^1.8.1",
63
66
  "@nomicfoundation/hardhat-toolbox": "^5.0.0",
64
67
  "@types/bn.js": "^5.1.5",
65
68
  "@types/elliptic": "^6.4.18",
66
69
  "@types/node": "^22.10.10",
67
- "@typescript-eslint/eslint-plugin": "^6.21.0",
68
- "babel-jest": "^29.7.0",
70
+ "@types/react": "^19.0.0",
69
71
  "dotenv": "^16.4.5",
70
- "eslint": "^8.57.0",
71
- "eslint-config-prettier": "^9.1.0",
72
- "eslint-config-standard-with-typescript": "^43.0.1",
73
- "eslint-plugin-import": "^2.29.1",
74
- "eslint-plugin-n": "^16.6.2",
75
- "eslint-plugin-prettier": "^5.1.3",
76
- "eslint-plugin-promise": "^6.1.1",
72
+ "eslint": "^9.18.0",
73
+ "eslint-config-prettier": "^10.0.0",
74
+ "eslint-plugin-import-x": "^4.6.0",
77
75
  "hardhat": "^2.22.18",
78
- "jest": "^29.7.0",
79
- "prettier": "^3.2.5",
76
+ "prettier": "^3.8.0",
77
+ "react": "^19.0.0",
78
+ "react-dom": "^19.0.0",
80
79
  "tsdown": "^0.16.6",
81
- "typescript": "^5.4.3",
80
+ "typescript": "^5.9.3",
81
+ "typescript-eslint": "^8.21.0",
82
82
  "vitest": "^3.0.4",
83
- "vocs": "1.0.0-alpha.62"
83
+ "vocs": "^1.4.1"
84
84
  },
85
85
  "dependencies": {
86
86
  "@coral-xyz/anchor": "^0.31.0",