@portal-hq/provider 4.4.0 → 4.6.0

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.
@@ -1,21 +1,23 @@
1
1
  import { PortalCurve } from '@portal-hq/core'
2
2
  import { FeatureFlags } from '@portal-hq/core/types'
3
3
  import {
4
+ DEFAULT_HOSTS,
4
5
  HttpRequester,
5
6
  IPortalProvider,
6
7
  KeychainAdapter,
7
8
  PortalErrorCodes,
8
9
  PortalMpcError,
9
10
  type SigningRequestArguments,
11
+ generateTraceId,
10
12
  getClientPlatformVersion,
11
- DEFAULT_HOSTS,
13
+ sdkLogger,
12
14
  } from '@portal-hq/utils'
13
- import UUID from 'react-native-uuid'
14
15
 
15
16
  import {
17
+ type EnclaveSignResponse,
16
18
  type MpcSignerOptions,
17
19
  PortalMobileMpcMetadata,
18
- type EnclaveSignResponse,
20
+ type PresignatureSource,
19
21
  } from '../../types'
20
22
  import Signer from './abstract'
21
23
 
@@ -31,6 +33,7 @@ class EnclaveSigner implements Signer {
31
33
  private version = 'v6'
32
34
  private portalApi?: HttpRequester
33
35
  private requests: HttpRequester
36
+ private presignatureSource?: PresignatureSource
34
37
 
35
38
  constructor({
36
39
  keychain,
@@ -38,12 +41,14 @@ class EnclaveSigner implements Signer {
38
41
  version = 'v6',
39
42
  portalApi,
40
43
  featureFlags = {},
44
+ presignatureSource,
41
45
  }: MpcSignerOptions & { enclaveMPCHost?: string }) {
42
46
  this.featureFlags = featureFlags
43
47
  this.keychain = keychain
44
48
  this.enclaveMPCHost = enclaveMPCHost ?? DEFAULT_HOSTS.ENCLAVE_MPC
45
49
  this.version = version
46
50
  this.portalApi = portalApi
51
+ this.presignatureSource = presignatureSource
47
52
  this.requests = new HttpRequester({
48
53
  baseUrl: `https://${this.enclaveMPCHost}`,
49
54
  })
@@ -59,9 +64,11 @@ class EnclaveSigner implements Signer {
59
64
  const signStartTime = performance.now()
60
65
  const preOperationStartTime = performance.now()
61
66
 
62
- // Generate a traceId for this operation
63
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
64
- const traceId = (UUID as any).v4() as string
67
+ // Use traceId from message if available (e.g. options.traceId), otherwise generate a new UUID
68
+ const traceId = message.traceId ?? generateTraceId()
69
+ sdkLogger.info(
70
+ `[Portal MPC] enclave sign started | method=${message.method} | traceId=${traceId} | chainId=${message.chainId} | curve=${message.curve}`,
71
+ )
65
72
  const metrics: Record<string, number | string | boolean> = {
66
73
  hasError: false,
67
74
  operation: Operation.SIGN,
@@ -127,6 +134,34 @@ class EnclaveSigner implements Signer {
127
134
  connectionTracingEnabled: shouldSendMetrics,
128
135
  }
129
136
 
137
+ // Presignature path (SECP256K1 only): try consume before building params; fallback to normal sign.
138
+ if (
139
+ curve === PortalCurve.SECP256K1 &&
140
+ this.featureFlags.usePresignatures === true &&
141
+ this.presignatureSource
142
+ ) {
143
+ const presignature = await this.presignatureSource.consumePresignature(
144
+ PortalCurve.SECP256K1,
145
+ )
146
+ if (presignature?.data) {
147
+ sdkLogger.debug(
148
+ '[Portal] Signing with presignature (EnclaveSigner)',
149
+ { method: message.method, chainId: message.chainId },
150
+ )
151
+ try {
152
+ return await this.signWithPresignature(
153
+ { ...message, presignatureData: presignature.data, traceId },
154
+ provider,
155
+ )
156
+ } catch (presignError) {
157
+ sdkLogger.warn(
158
+ '[Portal.Provider.EnclaveSigner] signWithPresignature failed, falling back to normal sign:',
159
+ presignError,
160
+ )
161
+ }
162
+ }
163
+ }
164
+
130
165
  // Build params
131
166
  // Avoid double JSON encoding: if params is already a string (e.g. a hex message), pass it directly; otherwise stringify objects/arrays.
132
167
  const params = this.buildParams(method, message.params)
@@ -172,6 +207,7 @@ class EnclaveSigner implements Signer {
172
207
  requestBody = {
173
208
  params: formattedParams,
174
209
  share: JSON.stringify(signingShare),
210
+ metadataStr: JSON.stringify(metadata),
175
211
  }
176
212
  } else {
177
213
  // Standard sign endpoint and body
@@ -195,6 +231,7 @@ class EnclaveSigner implements Signer {
195
231
  endpoint,
196
232
  apiKey,
197
233
  requestBody,
234
+ traceId,
198
235
  )
199
236
  result = this.processEnclaveResponse(response)
200
237
  } catch (error) {
@@ -310,6 +347,7 @@ class EnclaveSigner implements Signer {
310
347
  endpoint: string,
311
348
  apiKey: string,
312
349
  body: Record<string, any>,
350
+ traceId?: string,
313
351
  ): Promise<EnclaveSignResponse> {
314
352
  return await this.requests.post<EnclaveSignResponse>(endpoint, {
315
353
  headers: {
@@ -317,6 +355,7 @@ class EnclaveSigner implements Signer {
317
355
  'Content-Type': 'application/json',
318
356
  },
319
357
  body,
358
+ ...(traceId ? { traceId } : {}),
320
359
  })
321
360
  }
322
361
 
@@ -456,6 +495,165 @@ class EnclaveSigner implements Signer {
456
495
  }
457
496
  return params
458
497
  }
498
+
499
+ /**
500
+ * Sign using a pre-generated presignature via the Enclave REST API.
501
+ * Uses the same endpoints as normal signing (/v1/sign, /v1/raw/sign/:curve)
502
+ * but includes the optional `presignature` field in the request body.
503
+ * NOTE: Only supports SECP256K1 curve.
504
+ */
505
+ public async signWithPresignature(
506
+ message: SigningRequestArguments & { presignatureData: string },
507
+ provider: IPortalProvider,
508
+ ): Promise<string> {
509
+ const { method, chainId, isRaw = false, curve, presignatureData } = message
510
+
511
+ const targetCurve = curve || PortalCurve.SECP256K1
512
+ if (targetCurve !== PortalCurve.SECP256K1) {
513
+ throw new Error(
514
+ '[Portal.Provider.EnclaveSigner] Presignatures are only supported for SECP256K1 curve',
515
+ )
516
+ }
517
+
518
+ const shouldSendMetrics = this.featureFlags.enableSdkPerformanceMetrics
519
+ const traceId = message.traceId ?? generateTraceId()
520
+ const metrics: Record<string, number | string | boolean> = {
521
+ hasError: false,
522
+ operation: 'signWithPresignature',
523
+ signingMethod: method,
524
+ traceId,
525
+ }
526
+
527
+ if (chainId) {
528
+ metrics.chainId = chainId
529
+ }
530
+
531
+ const requestStartTime = performance.now()
532
+
533
+ try {
534
+ const preOperationStartTime = performance.now()
535
+
536
+ const apiKey = provider.apiKey
537
+ if (!apiKey) {
538
+ throw new Error(
539
+ '[Portal.Provider.EnclaveSigner] The API key is missing.',
540
+ )
541
+ }
542
+
543
+ const shares = await this.keychain.getShares()
544
+ const signingShare = shares.secp256k1.share
545
+
546
+ if (!signingShare) {
547
+ throw new Error(
548
+ '[Portal.Provider.EnclaveSigner] The SECP256K1 share is missing from the keychain.',
549
+ )
550
+ }
551
+
552
+ const metadata: PortalMobileMpcMetadata = {
553
+ clientPlatform: 'REACT_NATIVE',
554
+ clientPlatformVersion: getClientPlatformVersion(),
555
+ isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
556
+ mpcServerVersion: this.version,
557
+ optimized: true,
558
+ curve: PortalCurve.SECP256K1,
559
+ chainId,
560
+ isRaw,
561
+ reqId: traceId,
562
+ connectionTracingEnabled: shouldSendMetrics,
563
+ }
564
+
565
+ let endpoint: string
566
+ let requestBody: Record<string, any>
567
+
568
+ if (isRaw) {
569
+ const params = this.buildParams(method, message.params)
570
+ if (typeof params !== 'string') {
571
+ throw new Error(
572
+ '[Portal.Provider.EnclaveSigner] For raw signing with presignature, params must be a string (e.g., a hex-encoded message).',
573
+ )
574
+ }
575
+ endpoint = `/v1/raw/sign/${targetCurve}`
576
+ requestBody = {
577
+ params,
578
+ share: JSON.stringify(signingShare),
579
+ metadataStr: JSON.stringify(metadata),
580
+ presignature: presignatureData,
581
+ }
582
+ metrics.operation = 'raw_signWithPresignature'
583
+ } else {
584
+ const params = this.buildParams(method, message.params)
585
+ const formattedParams =
586
+ typeof params === 'string' ? params : JSON.stringify(params)
587
+ const rpcUrl = provider.getGatewayUrl(chainId)
588
+ endpoint = '/v1/sign'
589
+ requestBody = {
590
+ method: message.method,
591
+ params: formattedParams,
592
+ share: JSON.stringify(signingShare),
593
+ chainId: chainId || '',
594
+ rpcUrl,
595
+ metadataStr: JSON.stringify(metadata),
596
+ clientPlatform: 'REACT_NATIVE',
597
+ clientPlatformVersion: getClientPlatformVersion(),
598
+ presignature: presignatureData,
599
+ }
600
+ }
601
+
602
+ metrics.sdkPreOperationMs = performance.now() - preOperationStartTime
603
+
604
+ const enclaveSignStartTime = performance.now()
605
+
606
+ let result: string
607
+ try {
608
+ const response = await this.makeEnclaveRequest(
609
+ endpoint,
610
+ apiKey,
611
+ requestBody,
612
+ traceId,
613
+ )
614
+ result = this.processEnclaveResponse(response)
615
+ } catch (error) {
616
+ this.handleEnclaveError(error)
617
+ }
618
+
619
+ const postOperationStartTime = performance.now()
620
+
621
+ metrics.enclaveHttpCallMs = performance.now() - enclaveSignStartTime
622
+
623
+ metrics.sdkPostOperationMs = performance.now() - postOperationStartTime
624
+
625
+ metrics.sdkOperationMs = performance.now() - requestStartTime
626
+
627
+ if (shouldSendMetrics && this.portalApi) {
628
+ try {
629
+ await this.sendMetrics(metrics, apiKey)
630
+ } catch (error) {
631
+ // No-op
632
+ }
633
+ }
634
+
635
+ return result
636
+ } catch (error) {
637
+ sdkLogger.error(
638
+ '[Portal.Provider.EnclaveSigner] signWithPresignature error:',
639
+ error,
640
+ )
641
+
642
+ metrics.sdkOperationMs = performance.now() - requestStartTime
643
+ metrics.hasError = true
644
+
645
+ if (shouldSendMetrics && this.portalApi) {
646
+ const apiKey = provider.apiKey
647
+ try {
648
+ await this.sendMetrics(metrics, apiKey)
649
+ } catch (metricsError) {
650
+ // No-op
651
+ }
652
+ }
653
+
654
+ throw error
655
+ }
656
+ }
459
657
  }
460
658
 
461
659
  export default EnclaveSigner
@@ -1,21 +1,23 @@
1
1
  import { PortalCurve } from '@portal-hq/core'
2
2
  import { FeatureFlags } from '@portal-hq/core/types'
3
3
  import {
4
+ DEFAULT_HOSTS,
4
5
  HttpRequester,
5
6
  IPortalProvider,
6
7
  KeychainAdapter,
7
8
  PortalMpcError,
8
9
  type SigningRequestArguments,
10
+ generateTraceId,
9
11
  getClientPlatformVersion,
10
- DEFAULT_HOSTS,
12
+ sdkLogger,
11
13
  } from '@portal-hq/utils'
12
14
  import { NativeModules } from 'react-native'
13
- import UUID from 'react-native-uuid'
14
15
 
15
16
  import {
16
17
  type MpcSignerOptions,
17
18
  type PortalMobileMpc,
18
19
  PortalMobileMpcMetadata,
20
+ type PresignatureSource,
19
21
  type SigningResponse,
20
22
  } from '../../types'
21
23
  import Signer from './abstract'
@@ -32,6 +34,7 @@ class MpcSigner implements Signer {
32
34
  private mpcHost: string
33
35
  private version = 'v6'
34
36
  private portalApi?: HttpRequester
37
+ private presignatureSource?: PresignatureSource
35
38
 
36
39
  constructor({
37
40
  keychain,
@@ -39,6 +42,7 @@ class MpcSigner implements Signer {
39
42
  version = 'v6',
40
43
  portalApi,
41
44
  featureFlags = {},
45
+ presignatureSource,
42
46
  }: MpcSignerOptions) {
43
47
  this.featureFlags = featureFlags
44
48
  this.keychain = keychain
@@ -46,6 +50,7 @@ class MpcSigner implements Signer {
46
50
  this.mpcHost = mpcHost
47
51
  this.version = version
48
52
  this.portalApi = portalApi
53
+ this.presignatureSource = presignatureSource
49
54
 
50
55
  if (!this.mpc) {
51
56
  throw new Error(
@@ -63,9 +68,11 @@ class MpcSigner implements Signer {
63
68
  this.featureFlags.enableSdkPerformanceMetrics === true
64
69
  const signStartTime = performance.now()
65
70
  const preOperationStartTime = performance.now()
66
- // Generate a traceId for this operation
67
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
68
- const traceId = (UUID as any).v4() as string
71
+ // Use traceId from message if available (e.g. options.traceId), otherwise generate a new UUID
72
+ const traceId = message.traceId ?? generateTraceId()
73
+ sdkLogger.info(
74
+ `[Portal MPC] sign started | method=${message.method} | traceId=${traceId} | chainId=${message.chainId} | curve=${message.curve}`,
75
+ )
69
76
  const metrics: Record<string, number | string | boolean> = {
70
77
  hasError: false,
71
78
  operation: Operation.SIGN,
@@ -149,6 +156,39 @@ class MpcSigner implements Signer {
149
156
  )
150
157
  }
151
158
 
159
+ // Internal presignature handling (gated by feature flag): when usePresignatures is true and a
160
+ // presignature is available for SECP256K1, use signWithPresignature; otherwise normal sign().
161
+ if (
162
+ curve === PortalCurve.SECP256K1 &&
163
+ this.featureFlags.usePresignatures === true &&
164
+ this.presignatureSource
165
+ ) {
166
+ const presignature = await this.presignatureSource.consumePresignature(
167
+ PortalCurve.SECP256K1,
168
+ )
169
+ if (presignature?.data) {
170
+ sdkLogger.debug(
171
+ '[Portal] Signing with presignature (portal.request/sendAsset path)',
172
+ { method: message.method, chainId: message.chainId },
173
+ )
174
+ try {
175
+ return await this.signWithPresignature(
176
+ {
177
+ ...message,
178
+ presignatureData: presignature.data,
179
+ traceId,
180
+ },
181
+ provider,
182
+ )
183
+ } catch (presignError) {
184
+ sdkLogger.warn(
185
+ '[Portal.Provider.MpcSigner] signWithPresignature failed, falling back to normal sign:',
186
+ presignError,
187
+ )
188
+ }
189
+ }
190
+ }
191
+
152
192
  // Record pre-operation time
153
193
  metrics.sdkPreOperationMs = performance.now() - preOperationStartTime
154
194
 
@@ -283,6 +323,188 @@ class MpcSigner implements Signer {
283
323
  }
284
324
  return params
285
325
  }
326
+
327
+ /**
328
+ * Sign a transaction using a pre-generated presignature
329
+ * NOTE: Only supports SECP256K1 curve
330
+ *
331
+ * @param message - Signing request with presignatureData
332
+ * @param provider - Portal provider instance
333
+ * @returns Signed transaction hash
334
+ */
335
+ public async signWithPresignature(
336
+ message: SigningRequestArguments & { presignatureData: string },
337
+ provider: IPortalProvider,
338
+ ): Promise<string> {
339
+ const {
340
+ method,
341
+ chainId,
342
+ isRaw = false,
343
+ curve,
344
+ sponsorGas,
345
+ signatureApprovalMemo,
346
+ presignatureData,
347
+ } = message
348
+
349
+ const targetCurve = curve || PortalCurve.SECP256K1
350
+ if (targetCurve !== PortalCurve.SECP256K1) {
351
+ throw new Error(
352
+ '[Portal.Provider.MpcSigner] Presignatures are only supported for SECP256K1 curve',
353
+ )
354
+ }
355
+
356
+ const shouldSendMetrics = this.featureFlags.enableSdkPerformanceMetrics
357
+ const traceId = message.traceId ?? generateTraceId()
358
+ const metrics: Record<string, number | string | boolean> = {
359
+ hasError: false,
360
+ operation: 'signWithPresignature',
361
+ signingMethod: method,
362
+ traceId,
363
+ }
364
+
365
+ if (chainId) {
366
+ metrics.chainId = chainId
367
+ }
368
+
369
+ const requestStartTime = performance.now()
370
+
371
+ try {
372
+ const preOperationStartTime = performance.now()
373
+
374
+ const apiKey = provider.apiKey
375
+ if (!apiKey) {
376
+ throw new Error('[Portal.Provider.MpcSigner] The API key is missing.')
377
+ }
378
+
379
+ const shares = await this.keychain.getShares()
380
+ const signingShare = shares.secp256k1.share
381
+
382
+ if (!signingShare) {
383
+ throw new Error(
384
+ '[Portal.Provider.MpcSigner] The SECP256K1 share is missing from the keychain.',
385
+ )
386
+ }
387
+
388
+ const metadata: PortalMobileMpcMetadata = {
389
+ clientPlatform: 'REACT_NATIVE',
390
+ clientPlatformVersion: getClientPlatformVersion(),
391
+ isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
392
+ mpcServerVersion: this.version,
393
+ optimized: true,
394
+ curve: PortalCurve.SECP256K1,
395
+ chainId,
396
+ isRaw,
397
+ reqId: traceId,
398
+ connectionTracingEnabled: shouldSendMetrics,
399
+ sponsorGas,
400
+ signatureApprovalMemo,
401
+ }
402
+
403
+ const stringifiedMetadata = JSON.stringify(metadata)
404
+
405
+ let formattedParams: string
406
+ let rpcUrl: string
407
+
408
+ if (isRaw) {
409
+ formattedParams = this.buildParams(method, message.params)
410
+ rpcUrl = ''
411
+ metrics.operation = 'raw_signWithPresignature'
412
+ } else {
413
+ formattedParams = JSON.stringify(
414
+ this.buildParams(method, message.params),
415
+ )
416
+ rpcUrl = provider.getGatewayUrl(chainId)
417
+ }
418
+
419
+ if (typeof formattedParams !== 'string') {
420
+ throw new Error(
421
+ `[Portal.Provider.MpcSigner] The formatted params for the signing request could not be converted to a string. The params were: ${formattedParams}`,
422
+ )
423
+ }
424
+
425
+ metrics.sdkPreOperationMs = performance.now() - preOperationStartTime
426
+
427
+ const mpcSignStartTime = performance.now()
428
+
429
+ const result = await this.mpc.signWithPresignature(
430
+ apiKey,
431
+ this.mpcHost,
432
+ JSON.stringify(signingShare),
433
+ presignatureData,
434
+ method,
435
+ formattedParams,
436
+ rpcUrl,
437
+ chainId,
438
+ stringifiedMetadata,
439
+ )
440
+
441
+ const postOperationStartTime = performance.now()
442
+
443
+ metrics.mpcNativeCallMs = performance.now() - mpcSignStartTime
444
+
445
+ const parsedResponse = JSON.parse(String(result)) as SigningResponse
446
+ const { data, error, meta } = parsedResponse
447
+
448
+ if (meta?.metrics) {
449
+ const binaryMetrics = meta.metrics
450
+ if (binaryMetrics.wsConnectDurationMs) {
451
+ metrics.sdkBinaryWSConnectMs = binaryMetrics.wsConnectDurationMs
452
+ }
453
+ if (binaryMetrics.operationDurationMs) {
454
+ metrics.sdkBinaryOperationMs = binaryMetrics.operationDurationMs
455
+ }
456
+ if (binaryMetrics.tlsHandshakeMs) {
457
+ metrics.sdkBinaryTlsHandshakeMs = binaryMetrics.tlsHandshakeMs
458
+ }
459
+ if (binaryMetrics.firstResponseMs) {
460
+ metrics.sdkBinaryFirstResponseMs = binaryMetrics.firstResponseMs
461
+ }
462
+ if (binaryMetrics.dnsLookupMs) {
463
+ metrics.sdkBinaryDnsLookupMs = binaryMetrics.dnsLookupMs
464
+ }
465
+ if (binaryMetrics.connectMs) {
466
+ metrics.sdkBinaryConnectMs = binaryMetrics.connectMs
467
+ }
468
+ }
469
+
470
+ if (error?.id) {
471
+ throw new PortalMpcError(error)
472
+ }
473
+
474
+ metrics.sdkPostOperationMs = performance.now() - postOperationStartTime
475
+
476
+ metrics.sdkOperationMs = performance.now() - requestStartTime
477
+
478
+ if (shouldSendMetrics && this.portalApi) {
479
+ try {
480
+ await this.sendMetrics(metrics, apiKey)
481
+ } catch (error) {
482
+ // No-op
483
+ }
484
+ }
485
+
486
+ return data
487
+ } catch (error) {
488
+ sdkLogger.error(
489
+ '[Portal.Provider.MpcSigner] signWithPresignature error:',
490
+ error,
491
+ )
492
+
493
+ metrics.sdkOperationMs = performance.now() - requestStartTime
494
+ metrics.hasError = true
495
+
496
+ if (shouldSendMetrics && this.portalApi) {
497
+ const apiKey = provider.apiKey
498
+ try {
499
+ await this.sendMetrics(metrics, apiKey)
500
+ } catch (metricsError) {
501
+ // No-op
502
+ }
503
+ }
504
+
505
+ throw error
506
+ }
507
+ }
286
508
  }
287
509
 
288
510
  export default MpcSigner
package/types.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  // Types
2
2
  import PortalConnect from '@portal-hq/connect'
3
+ import type { PortalCurve } from '@portal-hq/core'
3
4
  import {
5
+ HttpRequester,
4
6
  KeychainAdapter,
5
7
  type PortalError,
6
- HttpRequester,
7
8
  } from '@portal-hq/utils'
8
9
 
9
10
  export type EventHandler = (data: any) => void
@@ -18,6 +19,11 @@ export interface GatewayConfig {
18
19
  [key: string]: string
19
20
  }
20
21
 
22
+ /** Internal SDK only: used by MpcSigner to consume a presignature when signing (SECP256K1). Not exposed to consumers. */
23
+ export interface PresignatureSource {
24
+ consumePresignature(curve: PortalCurve): Promise<{ data: string } | null>
25
+ }
26
+
21
27
  export interface MpcSignerOptions extends SignerOptions {
22
28
  keychain: KeychainAdapter
23
29
 
@@ -28,6 +34,8 @@ export interface MpcSignerOptions extends SignerOptions {
28
34
  enclaveMPCHost?: string
29
35
  version?: string
30
36
  featureFlags?: FeatureFlags
37
+ /** Internal: presignature source for SECP256K1 signing (portal.request() uses this automatically). */
38
+ presignatureSource?: PresignatureSource
31
39
  }
32
40
 
33
41
  export interface PortalMobileMpcMetadata {
@@ -47,9 +55,11 @@ export interface PortalMobileMpcMetadata {
47
55
  optimized: true
48
56
  isRaw?: boolean
49
57
  reqId?: string
58
+ traceId?: string
50
59
  connectionTracingEnabled?: boolean
51
60
  sponsorGas?: boolean
52
61
  signatureApprovalMemo?: string
62
+ presignatureExpirationTs?: number
53
63
  }
54
64
 
55
65
  export interface PortalMobileMpc {
@@ -75,6 +85,17 @@ export interface PortalMobileMpc {
75
85
  chainId: string,
76
86
  metadata: string,
77
87
  ) => Promise<string>
88
+ signWithPresignature: (
89
+ clientApiKey: string,
90
+ mpcApiUrl: string,
91
+ dkgResult: string,
92
+ presignatureData: string,
93
+ method: string,
94
+ params: string,
95
+ rpcUrl: string,
96
+ chainId: string,
97
+ metadata: string,
98
+ ) => Promise<string>
78
99
  }
79
100
 
80
101
  export interface ProviderOptions {
@@ -92,6 +113,8 @@ export interface ProviderOptions {
92
113
  enclaveMPCHost?: string
93
114
  version?: string
94
115
  featureFlags?: FeatureFlags
116
+ /** Internal: presignature source for SECP256K1 signing (portal.request() uses this automatically). */
117
+ presignatureSource?: PresignatureSource
95
118
  }
96
119
 
97
120
  export interface RegisteredEventHandler {
@@ -132,6 +155,7 @@ export interface ProviderOptions {
132
155
  version?: string
133
156
  chainId?: string
134
157
  featureFlags?: FeatureFlags
158
+ presignatureSource?: PresignatureSource
135
159
  }
136
160
 
137
161
  export interface SignerOptions {}