@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.
@@ -8,15 +8,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { PortalCurve } from '@portal-hq/core';
11
- import { HttpRequester, PortalErrorCodes, PortalMpcError, getClientPlatformVersion, DEFAULT_HOSTS, } from '@portal-hq/utils';
12
- import UUID from 'react-native-uuid';
11
+ import { DEFAULT_HOSTS, HttpRequester, PortalErrorCodes, PortalMpcError, generateTraceId, getClientPlatformVersion, sdkLogger, } from '@portal-hq/utils';
13
12
  var Operation;
14
13
  (function (Operation) {
15
14
  Operation["SIGN"] = "sign";
16
15
  Operation["RAW_SIGN"] = "raw_sign";
17
16
  })(Operation || (Operation = {}));
18
17
  class EnclaveSigner {
19
- constructor({ keychain, enclaveMPCHost, version = 'v6', portalApi, featureFlags = {}, }) {
18
+ constructor({ keychain, enclaveMPCHost, version = 'v6', portalApi, featureFlags = {}, presignatureSource, }) {
20
19
  this.version = 'v6';
21
20
  this.buildParams = (method, txParams) => {
22
21
  let params = txParams;
@@ -47,19 +46,21 @@ class EnclaveSigner {
47
46
  this.enclaveMPCHost = enclaveMPCHost !== null && enclaveMPCHost !== void 0 ? enclaveMPCHost : DEFAULT_HOSTS.ENCLAVE_MPC;
48
47
  this.version = version;
49
48
  this.portalApi = portalApi;
49
+ this.presignatureSource = presignatureSource;
50
50
  this.requests = new HttpRequester({
51
51
  baseUrl: `https://${this.enclaveMPCHost}`,
52
52
  });
53
53
  }
54
54
  sign(message, provider) {
55
+ var _a;
55
56
  return __awaiter(this, void 0, void 0, function* () {
56
57
  // Always track metrics, but only send if feature flag is enabled
57
58
  const shouldSendMetrics = this.featureFlags.enableSdkPerformanceMetrics === true;
58
59
  const signStartTime = performance.now();
59
60
  const preOperationStartTime = performance.now();
60
- // Generate a traceId for this operation
61
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
62
- const traceId = UUID.v4();
61
+ // Use traceId from message if available (e.g. options.traceId), otherwise generate a new UUID
62
+ const traceId = (_a = message.traceId) !== null && _a !== void 0 ? _a : generateTraceId();
63
+ sdkLogger.info(`[Portal MPC] enclave sign started | method=${message.method} | traceId=${traceId} | chainId=${message.chainId} | curve=${message.curve}`);
63
64
  const metrics = {
64
65
  hasError: false,
65
66
  operation: Operation.SIGN,
@@ -114,6 +115,21 @@ class EnclaveSigner {
114
115
  reqId: traceId,
115
116
  connectionTracingEnabled: shouldSendMetrics,
116
117
  };
118
+ // Presignature path (SECP256K1 only): try consume before building params; fallback to normal sign.
119
+ if (curve === PortalCurve.SECP256K1 &&
120
+ this.featureFlags.usePresignatures === true &&
121
+ this.presignatureSource) {
122
+ const presignature = yield this.presignatureSource.consumePresignature(PortalCurve.SECP256K1);
123
+ if (presignature === null || presignature === void 0 ? void 0 : presignature.data) {
124
+ sdkLogger.debug('[Portal] Signing with presignature (EnclaveSigner)', { method: message.method, chainId: message.chainId });
125
+ try {
126
+ return yield this.signWithPresignature(Object.assign(Object.assign({}, message), { presignatureData: presignature.data, traceId }), provider);
127
+ }
128
+ catch (presignError) {
129
+ sdkLogger.warn('[Portal.Provider.EnclaveSigner] signWithPresignature failed, falling back to normal sign:', presignError);
130
+ }
131
+ }
132
+ }
117
133
  // Build params
118
134
  // Avoid double JSON encoding: if params is already a string (e.g. a hex message), pass it directly; otherwise stringify objects/arrays.
119
135
  const params = this.buildParams(method, message.params);
@@ -148,6 +164,7 @@ class EnclaveSigner {
148
164
  requestBody = {
149
165
  params: formattedParams,
150
166
  share: JSON.stringify(signingShare),
167
+ metadataStr: JSON.stringify(metadata),
151
168
  };
152
169
  }
153
170
  else {
@@ -167,7 +184,7 @@ class EnclaveSigner {
167
184
  // Make API request and process response
168
185
  let result;
169
186
  try {
170
- const response = yield this.makeEnclaveRequest(endpoint, apiKey, requestBody);
187
+ const response = yield this.makeEnclaveRequest(endpoint, apiKey, requestBody, traceId);
171
188
  result = this.processEnclaveResponse(response);
172
189
  }
173
190
  catch (error) {
@@ -218,7 +235,7 @@ class EnclaveSigner {
218
235
  try {
219
236
  yield this.sendMetrics(metrics, apiKey);
220
237
  }
221
- catch (_a) {
238
+ catch (_b) {
222
239
  // No-op
223
240
  }
224
241
  }
@@ -263,15 +280,12 @@ class EnclaveSigner {
263
280
  * Make HTTP request to Enclave API
264
281
  * Returns raw response without processing
265
282
  */
266
- makeEnclaveRequest(endpoint, apiKey, body) {
283
+ makeEnclaveRequest(endpoint, apiKey, body, traceId) {
267
284
  return __awaiter(this, void 0, void 0, function* () {
268
- return yield this.requests.post(endpoint, {
269
- headers: {
285
+ return yield this.requests.post(endpoint, Object.assign({ headers: {
270
286
  Authorization: `Bearer ${apiKey}`,
271
287
  'Content-Type': 'application/json',
272
- },
273
- body,
274
- });
288
+ }, body }, (traceId ? { traceId } : {})));
275
289
  });
276
290
  }
277
291
  /**
@@ -369,5 +383,128 @@ class EnclaveSigner {
369
383
  }
370
384
  });
371
385
  }
386
+ /**
387
+ * Sign using a pre-generated presignature via the Enclave REST API.
388
+ * Uses the same endpoints as normal signing (/v1/sign, /v1/raw/sign/:curve)
389
+ * but includes the optional `presignature` field in the request body.
390
+ * NOTE: Only supports SECP256K1 curve.
391
+ */
392
+ signWithPresignature(message, provider) {
393
+ var _a;
394
+ return __awaiter(this, void 0, void 0, function* () {
395
+ const { method, chainId, isRaw = false, curve, presignatureData } = message;
396
+ const targetCurve = curve || PortalCurve.SECP256K1;
397
+ if (targetCurve !== PortalCurve.SECP256K1) {
398
+ throw new Error('[Portal.Provider.EnclaveSigner] Presignatures are only supported for SECP256K1 curve');
399
+ }
400
+ const shouldSendMetrics = this.featureFlags.enableSdkPerformanceMetrics;
401
+ const traceId = (_a = message.traceId) !== null && _a !== void 0 ? _a : generateTraceId();
402
+ const metrics = {
403
+ hasError: false,
404
+ operation: 'signWithPresignature',
405
+ signingMethod: method,
406
+ traceId,
407
+ };
408
+ if (chainId) {
409
+ metrics.chainId = chainId;
410
+ }
411
+ const requestStartTime = performance.now();
412
+ try {
413
+ const preOperationStartTime = performance.now();
414
+ const apiKey = provider.apiKey;
415
+ if (!apiKey) {
416
+ throw new Error('[Portal.Provider.EnclaveSigner] The API key is missing.');
417
+ }
418
+ const shares = yield this.keychain.getShares();
419
+ const signingShare = shares.secp256k1.share;
420
+ if (!signingShare) {
421
+ throw new Error('[Portal.Provider.EnclaveSigner] The SECP256K1 share is missing from the keychain.');
422
+ }
423
+ const metadata = {
424
+ clientPlatform: 'REACT_NATIVE',
425
+ clientPlatformVersion: getClientPlatformVersion(),
426
+ isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
427
+ mpcServerVersion: this.version,
428
+ optimized: true,
429
+ curve: PortalCurve.SECP256K1,
430
+ chainId,
431
+ isRaw,
432
+ reqId: traceId,
433
+ connectionTracingEnabled: shouldSendMetrics,
434
+ };
435
+ let endpoint;
436
+ let requestBody;
437
+ if (isRaw) {
438
+ const params = this.buildParams(method, message.params);
439
+ if (typeof params !== 'string') {
440
+ throw new Error('[Portal.Provider.EnclaveSigner] For raw signing with presignature, params must be a string (e.g., a hex-encoded message).');
441
+ }
442
+ endpoint = `/v1/raw/sign/${targetCurve}`;
443
+ requestBody = {
444
+ params,
445
+ share: JSON.stringify(signingShare),
446
+ metadataStr: JSON.stringify(metadata),
447
+ presignature: presignatureData,
448
+ };
449
+ metrics.operation = 'raw_signWithPresignature';
450
+ }
451
+ else {
452
+ const params = this.buildParams(method, message.params);
453
+ const formattedParams = typeof params === 'string' ? params : JSON.stringify(params);
454
+ const rpcUrl = provider.getGatewayUrl(chainId);
455
+ endpoint = '/v1/sign';
456
+ requestBody = {
457
+ method: message.method,
458
+ params: formattedParams,
459
+ share: JSON.stringify(signingShare),
460
+ chainId: chainId || '',
461
+ rpcUrl,
462
+ metadataStr: JSON.stringify(metadata),
463
+ clientPlatform: 'REACT_NATIVE',
464
+ clientPlatformVersion: getClientPlatformVersion(),
465
+ presignature: presignatureData,
466
+ };
467
+ }
468
+ metrics.sdkPreOperationMs = performance.now() - preOperationStartTime;
469
+ const enclaveSignStartTime = performance.now();
470
+ let result;
471
+ try {
472
+ const response = yield this.makeEnclaveRequest(endpoint, apiKey, requestBody, traceId);
473
+ result = this.processEnclaveResponse(response);
474
+ }
475
+ catch (error) {
476
+ this.handleEnclaveError(error);
477
+ }
478
+ const postOperationStartTime = performance.now();
479
+ metrics.enclaveHttpCallMs = performance.now() - enclaveSignStartTime;
480
+ metrics.sdkPostOperationMs = performance.now() - postOperationStartTime;
481
+ metrics.sdkOperationMs = performance.now() - requestStartTime;
482
+ if (shouldSendMetrics && this.portalApi) {
483
+ try {
484
+ yield this.sendMetrics(metrics, apiKey);
485
+ }
486
+ catch (error) {
487
+ // No-op
488
+ }
489
+ }
490
+ return result;
491
+ }
492
+ catch (error) {
493
+ sdkLogger.error('[Portal.Provider.EnclaveSigner] signWithPresignature error:', error);
494
+ metrics.sdkOperationMs = performance.now() - requestStartTime;
495
+ metrics.hasError = true;
496
+ if (shouldSendMetrics && this.portalApi) {
497
+ const apiKey = provider.apiKey;
498
+ try {
499
+ yield this.sendMetrics(metrics, apiKey);
500
+ }
501
+ catch (metricsError) {
502
+ // No-op
503
+ }
504
+ }
505
+ throw error;
506
+ }
507
+ });
508
+ }
372
509
  }
373
510
  export default EnclaveSigner;
@@ -8,16 +8,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { PortalCurve } from '@portal-hq/core';
11
- import { PortalMpcError, getClientPlatformVersion, DEFAULT_HOSTS, } from '@portal-hq/utils';
11
+ import { DEFAULT_HOSTS, PortalMpcError, generateTraceId, getClientPlatformVersion, sdkLogger, } from '@portal-hq/utils';
12
12
  import { NativeModules } from 'react-native';
13
- import UUID from 'react-native-uuid';
14
13
  var Operation;
15
14
  (function (Operation) {
16
15
  Operation["SIGN"] = "sign";
17
16
  Operation["RAW_SIGN"] = "raw_sign";
18
17
  })(Operation || (Operation = {}));
19
18
  class MpcSigner {
20
- constructor({ keychain, mpcHost = DEFAULT_HOSTS.MPC, version = 'v6', portalApi, featureFlags = {}, }) {
19
+ constructor({ keychain, mpcHost = DEFAULT_HOSTS.MPC, version = 'v6', portalApi, featureFlags = {}, presignatureSource, }) {
21
20
  this.version = 'v6';
22
21
  this.buildParams = (method, txParams) => {
23
22
  let params = txParams;
@@ -49,19 +48,21 @@ class MpcSigner {
49
48
  this.mpcHost = mpcHost;
50
49
  this.version = version;
51
50
  this.portalApi = portalApi;
51
+ this.presignatureSource = presignatureSource;
52
52
  if (!this.mpc) {
53
53
  throw new Error(`[Portal.Provider.MpcSigner] The MPC module could not be found by the signer. This is usually an issue with React Native linking. Please verify that the 'PortalReactNative' module is properly linked to this project.`);
54
54
  }
55
55
  }
56
56
  sign(message, provider) {
57
+ var _a;
57
58
  return __awaiter(this, void 0, void 0, function* () {
58
59
  // Always track metrics, but only send if feature flag is enabled
59
60
  const shouldSendMetrics = this.featureFlags.enableSdkPerformanceMetrics === true;
60
61
  const signStartTime = performance.now();
61
62
  const preOperationStartTime = performance.now();
62
- // Generate a traceId for this operation
63
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
64
- const traceId = UUID.v4();
63
+ // Use traceId from message if available (e.g. options.traceId), otherwise generate a new UUID
64
+ const traceId = (_a = message.traceId) !== null && _a !== void 0 ? _a : generateTraceId();
65
+ sdkLogger.info(`[Portal MPC] sign started | method=${message.method} | traceId=${traceId} | chainId=${message.chainId} | curve=${message.curve}`);
65
66
  const metrics = {
66
67
  hasError: false,
67
68
  operation: Operation.SIGN,
@@ -121,6 +122,22 @@ class MpcSigner {
121
122
  if (typeof formattedParams !== 'string') {
122
123
  throw new Error(`[Portal.Provider.MpcSigner] The formatted params for the signing request could not be converted to a string. The params were: ${formattedParams}`);
123
124
  }
125
+ // Internal presignature handling (gated by feature flag): when usePresignatures is true and a
126
+ // presignature is available for SECP256K1, use signWithPresignature; otherwise normal sign().
127
+ if (curve === PortalCurve.SECP256K1 &&
128
+ this.featureFlags.usePresignatures === true &&
129
+ this.presignatureSource) {
130
+ const presignature = yield this.presignatureSource.consumePresignature(PortalCurve.SECP256K1);
131
+ if (presignature === null || presignature === void 0 ? void 0 : presignature.data) {
132
+ sdkLogger.debug('[Portal] Signing with presignature (portal.request/sendAsset path)', { method: message.method, chainId: message.chainId });
133
+ try {
134
+ return yield this.signWithPresignature(Object.assign(Object.assign({}, message), { presignatureData: presignature.data, traceId }), provider);
135
+ }
136
+ catch (presignError) {
137
+ sdkLogger.warn('[Portal.Provider.MpcSigner] signWithPresignature failed, falling back to normal sign:', presignError);
138
+ }
139
+ }
140
+ }
124
141
  // Record pre-operation time
125
142
  metrics.sdkPreOperationMs = performance.now() - preOperationStartTime;
126
143
  // Measure MPC signing operation time
@@ -183,7 +200,7 @@ class MpcSigner {
183
200
  try {
184
201
  yield this.sendMetrics(metrics, apiKey);
185
202
  }
186
- catch (_a) {
203
+ catch (_b) {
187
204
  // No-op
188
205
  }
189
206
  }
@@ -210,5 +227,133 @@ class MpcSigner {
210
227
  }
211
228
  });
212
229
  }
230
+ /**
231
+ * Sign a transaction using a pre-generated presignature
232
+ * NOTE: Only supports SECP256K1 curve
233
+ *
234
+ * @param message - Signing request with presignatureData
235
+ * @param provider - Portal provider instance
236
+ * @returns Signed transaction hash
237
+ */
238
+ signWithPresignature(message, provider) {
239
+ var _a;
240
+ return __awaiter(this, void 0, void 0, function* () {
241
+ const { method, chainId, isRaw = false, curve, sponsorGas, signatureApprovalMemo, presignatureData, } = message;
242
+ const targetCurve = curve || PortalCurve.SECP256K1;
243
+ if (targetCurve !== PortalCurve.SECP256K1) {
244
+ throw new Error('[Portal.Provider.MpcSigner] Presignatures are only supported for SECP256K1 curve');
245
+ }
246
+ const shouldSendMetrics = this.featureFlags.enableSdkPerformanceMetrics;
247
+ const traceId = (_a = message.traceId) !== null && _a !== void 0 ? _a : generateTraceId();
248
+ const metrics = {
249
+ hasError: false,
250
+ operation: 'signWithPresignature',
251
+ signingMethod: method,
252
+ traceId,
253
+ };
254
+ if (chainId) {
255
+ metrics.chainId = chainId;
256
+ }
257
+ const requestStartTime = performance.now();
258
+ try {
259
+ const preOperationStartTime = performance.now();
260
+ const apiKey = provider.apiKey;
261
+ if (!apiKey) {
262
+ throw new Error('[Portal.Provider.MpcSigner] The API key is missing.');
263
+ }
264
+ const shares = yield this.keychain.getShares();
265
+ const signingShare = shares.secp256k1.share;
266
+ if (!signingShare) {
267
+ throw new Error('[Portal.Provider.MpcSigner] The SECP256K1 share is missing from the keychain.');
268
+ }
269
+ const metadata = {
270
+ clientPlatform: 'REACT_NATIVE',
271
+ clientPlatformVersion: getClientPlatformVersion(),
272
+ isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
273
+ mpcServerVersion: this.version,
274
+ optimized: true,
275
+ curve: PortalCurve.SECP256K1,
276
+ chainId,
277
+ isRaw,
278
+ reqId: traceId,
279
+ connectionTracingEnabled: shouldSendMetrics,
280
+ sponsorGas,
281
+ signatureApprovalMemo,
282
+ };
283
+ const stringifiedMetadata = JSON.stringify(metadata);
284
+ let formattedParams;
285
+ let rpcUrl;
286
+ if (isRaw) {
287
+ formattedParams = this.buildParams(method, message.params);
288
+ rpcUrl = '';
289
+ metrics.operation = 'raw_signWithPresignature';
290
+ }
291
+ else {
292
+ formattedParams = JSON.stringify(this.buildParams(method, message.params));
293
+ rpcUrl = provider.getGatewayUrl(chainId);
294
+ }
295
+ if (typeof formattedParams !== 'string') {
296
+ throw new Error(`[Portal.Provider.MpcSigner] The formatted params for the signing request could not be converted to a string. The params were: ${formattedParams}`);
297
+ }
298
+ metrics.sdkPreOperationMs = performance.now() - preOperationStartTime;
299
+ const mpcSignStartTime = performance.now();
300
+ const result = yield this.mpc.signWithPresignature(apiKey, this.mpcHost, JSON.stringify(signingShare), presignatureData, method, formattedParams, rpcUrl, chainId, stringifiedMetadata);
301
+ const postOperationStartTime = performance.now();
302
+ metrics.mpcNativeCallMs = performance.now() - mpcSignStartTime;
303
+ const parsedResponse = JSON.parse(String(result));
304
+ const { data, error, meta } = parsedResponse;
305
+ if (meta === null || meta === void 0 ? void 0 : meta.metrics) {
306
+ const binaryMetrics = meta.metrics;
307
+ if (binaryMetrics.wsConnectDurationMs) {
308
+ metrics.sdkBinaryWSConnectMs = binaryMetrics.wsConnectDurationMs;
309
+ }
310
+ if (binaryMetrics.operationDurationMs) {
311
+ metrics.sdkBinaryOperationMs = binaryMetrics.operationDurationMs;
312
+ }
313
+ if (binaryMetrics.tlsHandshakeMs) {
314
+ metrics.sdkBinaryTlsHandshakeMs = binaryMetrics.tlsHandshakeMs;
315
+ }
316
+ if (binaryMetrics.firstResponseMs) {
317
+ metrics.sdkBinaryFirstResponseMs = binaryMetrics.firstResponseMs;
318
+ }
319
+ if (binaryMetrics.dnsLookupMs) {
320
+ metrics.sdkBinaryDnsLookupMs = binaryMetrics.dnsLookupMs;
321
+ }
322
+ if (binaryMetrics.connectMs) {
323
+ metrics.sdkBinaryConnectMs = binaryMetrics.connectMs;
324
+ }
325
+ }
326
+ if (error === null || error === void 0 ? void 0 : error.id) {
327
+ throw new PortalMpcError(error);
328
+ }
329
+ metrics.sdkPostOperationMs = performance.now() - postOperationStartTime;
330
+ metrics.sdkOperationMs = performance.now() - requestStartTime;
331
+ if (shouldSendMetrics && this.portalApi) {
332
+ try {
333
+ yield this.sendMetrics(metrics, apiKey);
334
+ }
335
+ catch (error) {
336
+ // No-op
337
+ }
338
+ }
339
+ return data;
340
+ }
341
+ catch (error) {
342
+ sdkLogger.error('[Portal.Provider.MpcSigner] signWithPresignature error:', error);
343
+ metrics.sdkOperationMs = performance.now() - requestStartTime;
344
+ metrics.hasError = true;
345
+ if (shouldSendMetrics && this.portalApi) {
346
+ const apiKey = provider.apiKey;
347
+ try {
348
+ yield this.sendMetrics(metrics, apiKey);
349
+ }
350
+ catch (metricsError) {
351
+ // No-op
352
+ }
353
+ }
354
+ throw error;
355
+ }
356
+ });
357
+ }
213
358
  }
214
359
  export default MpcSigner;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portal-hq/provider",
3
- "version": "4.4.0",
3
+ "version": "4.6.0",
4
4
  "license": "MIT",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/esm/index",
@@ -19,8 +19,8 @@
19
19
  "test": "jest"
20
20
  },
21
21
  "dependencies": {
22
- "@portal-hq/connect": "^4.4.0",
23
- "@portal-hq/utils": "^4.4.0",
22
+ "@portal-hq/connect": "^4.6.0",
23
+ "@portal-hq/utils": "^4.6.0",
24
24
  "@types/react-native-uuid": "^2.0.0",
25
25
  "react-native-uuid": "^2.0.3"
26
26
  },
@@ -32,5 +32,5 @@
32
32
  "ts-jest": "^29.0.3",
33
33
  "typescript": "^4.8.4"
34
34
  },
35
- "gitHead": "76ce311809d877d0f8aa5373b0f4e9d85ee963f0"
35
+ "gitHead": "81e3ade6164a87aa8fc8942af4c770d04f73bc55"
36
36
  }
@@ -1,6 +1,9 @@
1
1
  import PortalConnect from '@portal-hq/connect'
2
2
  import { PortalCurve } from '@portal-hq/core'
3
3
  import {
4
+ CHAIN_NAMESPACES,
5
+ DEFAULT_CHAIN_ID_CAIP2,
6
+ DEFAULT_HOSTS,
4
7
  Events,
5
8
  HttpRequester,
6
9
  IPortalProvider,
@@ -11,9 +14,7 @@ import {
11
14
  ProviderRpcError,
12
15
  type RequestArguments,
13
16
  RpcErrorCodes,
14
- DEFAULT_CHAIN_ID_CAIP2,
15
- CHAIN_NAMESPACES,
16
- DEFAULT_HOSTS,
17
+ generateTraceId,
17
18
  sdkLogger,
18
19
  } from '@portal-hq/utils'
19
20
  import { AddressesByNamespace, RpcErrorOptions } from '@portal-hq/utils/types'
@@ -25,7 +26,8 @@ import {
25
26
  type RegisteredEventHandler,
26
27
  SwitchEthereumChainParameter,
27
28
  } from '../../types'
28
- import { MpcSigner, Signer, EnclaveSigner } from '../signers'
29
+ import { EnclaveSigner, MpcSigner, Signer } from '../signers'
30
+
29
31
  const passiveSignerMethods = [
30
32
  'eth_accounts',
31
33
  'eth_chainId',
@@ -84,6 +86,7 @@ class Provider implements IPortalProvider {
84
86
  version = 'v6',
85
87
  chainId = DEFAULT_CHAIN_ID_CAIP2,
86
88
  featureFlags = {},
89
+ presignatureSource,
87
90
  }: ProviderOptions) {
88
91
  const finalApiHost = apiHost ?? DEFAULT_HOSTS.API
89
92
  const finalMpcHost = mpcHost ?? DEFAULT_HOSTS.MPC
@@ -128,6 +131,11 @@ class Provider implements IPortalProvider {
128
131
  version,
129
132
  portalApi: this.portalApi,
130
133
  featureFlags,
134
+ // Only pass presignatureSource when usePresignatures is enabled (same as MpcSigner).
135
+ presignatureSource:
136
+ featureFlags.usePresignatures === true
137
+ ? presignatureSource
138
+ : undefined,
131
139
  })
132
140
  } else {
133
141
  this.signer = new MpcSigner({
@@ -136,6 +144,11 @@ class Provider implements IPortalProvider {
136
144
  version,
137
145
  portalApi: this.portalApi,
138
146
  featureFlags,
147
+ // Only pass presignatureSource when usePresignatures is enabled; otherwise signer always uses normal sign().
148
+ presignatureSource:
149
+ featureFlags.usePresignatures === true
150
+ ? presignatureSource
151
+ : undefined,
139
152
  })
140
153
  }
141
154
  }
@@ -281,6 +294,7 @@ class Provider implements IPortalProvider {
281
294
  params,
282
295
  chainId,
283
296
  connect,
297
+ options,
284
298
  }: RequestArguments): Promise<any> {
285
299
  chainId = chainId ?? this.chainId
286
300
  if (!chainId) {
@@ -339,6 +353,7 @@ class Provider implements IPortalProvider {
339
353
  params,
340
354
  chainId,
341
355
  connect,
356
+ options,
342
357
  })
343
358
 
344
359
  if (transactionHash) {
@@ -545,6 +560,7 @@ class Provider implements IPortalProvider {
545
560
  params,
546
561
  chainId,
547
562
  connect,
563
+ options,
548
564
  }: RequestArguments): Promise<any> {
549
565
  const isApproved = passiveSignerMethods.includes(method)
550
566
  ? true
@@ -556,6 +572,7 @@ class Provider implements IPortalProvider {
556
572
  )
557
573
  return
558
574
  }
575
+ const traceId = options?.traceId ?? generateTraceId()
559
576
  let namespace: string = CHAIN_NAMESPACES.EIP155
560
577
  let reference: string | number | undefined = chainId
561
578
  if (typeof chainId == 'string' && chainId.includes(':')) {
@@ -591,6 +608,9 @@ class Provider implements IPortalProvider {
591
608
  params,
592
609
  curve,
593
610
  isRaw: false,
611
+ sponsorGas: options?.sponsorGas,
612
+ signatureApprovalMemo: options?.signatureApprovalMemo,
613
+ traceId,
594
614
  },
595
615
  this,
596
616
  )
@@ -600,11 +620,14 @@ class Provider implements IPortalProvider {
600
620
  case 'raw_sign': {
601
621
  const result = await this.signer?.sign(
602
622
  {
603
- chainId: '',
604
- method: '',
623
+ chainId: chainId ?? '',
624
+ method: 'raw_sign',
605
625
  params,
606
626
  curve,
607
627
  isRaw: true,
628
+ sponsorGas: options?.sponsorGas,
629
+ signatureApprovalMemo: options?.signatureApprovalMemo,
630
+ traceId,
608
631
  },
609
632
  this,
610
633
  )