@portal-hq/web 3.13.1 → 3.13.2-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.
Files changed (96) hide show
  1. package/lib/commonjs/index.js +127 -9
  2. package/lib/commonjs/index.test.js +13 -0
  3. package/lib/commonjs/integrations/delegations/index.js +109 -2
  4. package/lib/commonjs/integrations/delegations/index.test.js +171 -0
  5. package/lib/commonjs/integrations/ramps/noah/index.test.js +18 -5
  6. package/lib/commonjs/integrations/trading/index.js +16 -5
  7. package/lib/commonjs/integrations/trading/lifi/index.js +297 -25
  8. package/lib/commonjs/integrations/trading/lifi/lifi.tradeAsset.test.js +360 -0
  9. package/lib/commonjs/integrations/trading/lifi/lifiStatusPoll.js +118 -0
  10. package/lib/commonjs/integrations/trading/lifi/lifiStatusPoll.test.js +66 -0
  11. package/lib/commonjs/integrations/trading/zero-x/index.js +129 -26
  12. package/lib/commonjs/integrations/trading/zero-x/index.test.js +163 -1
  13. package/lib/commonjs/integrations/yield/index.js +18 -4
  14. package/lib/commonjs/integrations/yield/yieldxyz.getValidators.test.js +71 -0
  15. package/lib/commonjs/integrations/yield/yieldxyz.highLevel.test.js +438 -0
  16. package/lib/commonjs/integrations/yield/yieldxyz.js +541 -1
  17. package/lib/commonjs/internal/pollLoop.js +64 -0
  18. package/lib/commonjs/internal/pollLoop.test.js +100 -0
  19. package/lib/commonjs/internal/stripStalePlanningNonce.js +65 -0
  20. package/lib/commonjs/internal/stripStalePlanningNonce.test.js +35 -0
  21. package/lib/commonjs/internal/waitForEvmOrUserOpConfirmation.js +155 -0
  22. package/lib/commonjs/internal/waitForEvmOrUserOpConfirmation.test.js +33 -0
  23. package/lib/commonjs/internal/waitForEvmTxConfirmation.js +104 -0
  24. package/lib/commonjs/internal/waitForSolanaTxConfirmation.js +106 -0
  25. package/lib/commonjs/internal/yieldEvmNetwork.js +60 -0
  26. package/lib/commonjs/mpc/index.js +116 -1
  27. package/lib/commonjs/provider/index.js +17 -0
  28. package/lib/commonjs/shared/trace/index.js +0 -1
  29. package/lib/esm/index.js +127 -9
  30. package/lib/esm/index.test.js +13 -0
  31. package/lib/esm/integrations/delegations/index.js +109 -2
  32. package/lib/esm/integrations/delegations/index.test.js +171 -0
  33. package/lib/esm/integrations/ramps/noah/index.test.js +18 -5
  34. package/lib/esm/integrations/trading/index.js +16 -5
  35. package/lib/esm/integrations/trading/lifi/index.js +292 -25
  36. package/lib/esm/integrations/trading/lifi/lifi.tradeAsset.test.js +332 -0
  37. package/lib/esm/integrations/trading/lifi/lifiStatusPoll.js +113 -0
  38. package/lib/esm/integrations/trading/lifi/lifiStatusPoll.test.js +64 -0
  39. package/lib/esm/integrations/trading/zero-x/index.js +129 -26
  40. package/lib/esm/integrations/trading/zero-x/index.test.js +141 -2
  41. package/lib/esm/integrations/yield/index.js +18 -4
  42. package/lib/esm/integrations/yield/yieldxyz.getValidators.test.js +66 -0
  43. package/lib/esm/integrations/yield/yieldxyz.highLevel.test.js +433 -0
  44. package/lib/esm/integrations/yield/yieldxyz.js +541 -1
  45. package/lib/esm/internal/pollLoop.js +59 -0
  46. package/lib/esm/internal/pollLoop.test.js +98 -0
  47. package/lib/esm/internal/stripStalePlanningNonce.js +61 -0
  48. package/lib/esm/internal/stripStalePlanningNonce.test.js +33 -0
  49. package/lib/esm/internal/waitForEvmOrUserOpConfirmation.js +151 -0
  50. package/lib/esm/internal/waitForEvmOrUserOpConfirmation.test.js +31 -0
  51. package/lib/esm/internal/waitForEvmTxConfirmation.js +100 -0
  52. package/lib/esm/internal/waitForSolanaTxConfirmation.js +102 -0
  53. package/lib/esm/internal/yieldEvmNetwork.js +55 -0
  54. package/lib/esm/mpc/index.js +116 -1
  55. package/lib/esm/provider/index.js +17 -0
  56. package/lib/esm/shared/trace/index.js +0 -1
  57. package/noah-types.d.ts +16 -2
  58. package/package.json +3 -2
  59. package/src/index.test.ts +15 -0
  60. package/src/index.ts +203 -14
  61. package/src/integrations/delegations/index.test.ts +251 -0
  62. package/src/integrations/delegations/index.ts +202 -4
  63. package/src/integrations/ramps/noah/index.test.ts +18 -5
  64. package/src/integrations/trading/index.ts +10 -7
  65. package/src/integrations/trading/lifi/index.ts +388 -28
  66. package/src/integrations/trading/lifi/lifi.tradeAsset.test.ts +436 -0
  67. package/src/integrations/trading/lifi/lifiStatusPoll.test.ts +74 -0
  68. package/src/integrations/trading/lifi/lifiStatusPoll.ts +158 -0
  69. package/src/integrations/trading/zero-x/index.test.ts +297 -1
  70. package/src/integrations/trading/zero-x/index.ts +181 -27
  71. package/src/integrations/yield/index.ts +24 -4
  72. package/src/integrations/yield/yieldxyz.getValidators.test.ts +70 -0
  73. package/src/integrations/yield/yieldxyz.highLevel.test.ts +536 -0
  74. package/src/integrations/yield/yieldxyz.ts +762 -8
  75. package/src/internal/pollLoop.test.ts +109 -0
  76. package/src/internal/pollLoop.ts +87 -0
  77. package/src/internal/stripStalePlanningNonce.test.ts +38 -0
  78. package/src/internal/stripStalePlanningNonce.ts +66 -0
  79. package/src/internal/waitForEvmOrUserOpConfirmation.test.ts +31 -0
  80. package/src/internal/waitForEvmOrUserOpConfirmation.ts +194 -0
  81. package/src/internal/waitForEvmTxConfirmation.ts +155 -0
  82. package/src/internal/waitForSolanaTxConfirmation.ts +135 -0
  83. package/src/internal/yieldEvmNetwork.ts +57 -0
  84. package/src/mpc/index.ts +142 -1
  85. package/src/provider/index.ts +25 -0
  86. package/src/shared/trace/index.ts +0 -1
  87. package/src/shared/types/README.md +6 -0
  88. package/src/shared/types/api.ts +12 -1
  89. package/src/shared/types/common.ts +332 -20
  90. package/src/shared/types/delegations.ts +10 -0
  91. package/src/shared/types/index.ts +1 -0
  92. package/src/shared/types/lifi.ts +82 -0
  93. package/src/shared/types/noah.ts +124 -33
  94. package/src/shared/types/yieldxyz.ts +193 -0
  95. package/src/shared/types/zero-x.ts +66 -0
  96. package/types.d.ts +6 -0
@@ -14,7 +14,7 @@ const errors_1 = require("./errors");
14
14
  const logger_1 = require("../logger");
15
15
  const index_1 = require("../index");
16
16
  const trace_1 = require("../shared/trace");
17
- const WEB_SDK_VERSION = '3.13.1';
17
+ const WEB_SDK_VERSION = '3.13.2-0';
18
18
  class Mpc {
19
19
  get ready() {
20
20
  return this._ready;
@@ -38,6 +38,8 @@ class Mpc {
38
38
  mpcVersion: this.portal.mpcVersion,
39
39
  featureFlags: this.portal.featureFlags,
40
40
  logLevel: this.portal.getLogLevel(),
41
+ rpcConfig: this.portal.rpcConfig,
42
+ iframeRpcConfig: this.portal.iframeRpcConfig,
41
43
  };
42
44
  const message = {
43
45
  type: 'portal:configure',
@@ -311,8 +313,18 @@ class Mpc {
311
313
  });
312
314
  });
313
315
  }
316
+ /**
317
+ * @deprecated This method is deprecated and will be removed in a future version.
318
+ * Please use `getTransactionHistory()` instead, which uses the new Portal v3 API
319
+ * endpoint and returns the unified transaction format across all chains.
320
+ */
314
321
  getTransactions(chainId, limit, offset, order) {
315
322
  return __awaiter(this, void 0, void 0, function* () {
323
+ // Log deprecation warning
324
+ logger_1.sdkLogger.warn('[DEPRECATED] getTransactions() is deprecated and will be removed in a future version. ' +
325
+ 'Please use getTransactionHistory() instead, which provides improved type safety with ' +
326
+ 'discriminated unions for regular transactions and UserOperations, and proper polymorphic ' +
327
+ 'response types for Solana vs unified formats.');
316
328
  return this.handleRequestToIframeAndPost({
317
329
  methodMessage: 'portal:getTransactions',
318
330
  errorMessage: 'portal:getTransactionsError',
@@ -326,6 +338,31 @@ class Mpc {
326
338
  });
327
339
  });
328
340
  }
341
+ /**
342
+ * Retrieves transaction history for the client's wallet on the specified chain.
343
+ *
344
+ * This method uses the new Portal v3 API endpoint and returns the unified
345
+ * transaction format. Supports EVM (EIP-155), Solana, Bitcoin, Tron, and Stellar chains.
346
+ *
347
+ * @param params - Request parameters
348
+ * @param params.chainId - Chain ID in CAIP-2 format (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
349
+ * @param params.limit - Maximum number of transactions to return (default: 50)
350
+ * @param params.offset - Number of transactions to skip (default: 0)
351
+ * @param params.order - Sort order ('asc' or 'desc')
352
+ * @param params.address - Override wallet address (EVM only)
353
+ * @param params.userOperations - Filter for ERC-4337 UserOperations (EVM only)
354
+ * @returns Promise resolving to transaction history response
355
+ */
356
+ getTransactionHistory(params) {
357
+ return __awaiter(this, void 0, void 0, function* () {
358
+ return this.handleRequestToIframeAndPost({
359
+ methodMessage: 'portal:getTransactionHistory',
360
+ errorMessage: 'portal:getTransactionHistoryError',
361
+ resultMessage: 'portal:getTransactionHistoryResult',
362
+ data: params,
363
+ });
364
+ });
365
+ }
329
366
  setBackupStatus(status, backupIds) {
330
367
  return __awaiter(this, void 0, void 0, function* () {
331
368
  return this.handleRequestToIframeAndPost({
@@ -478,6 +515,26 @@ class Mpc {
478
515
  });
479
516
  });
480
517
  }
518
+ getYieldXyzDefaults(data) {
519
+ return __awaiter(this, void 0, void 0, function* () {
520
+ return this.handleRequestToIframeAndPost({
521
+ methodMessage: 'portal:yieldxyz:getDefaults',
522
+ errorMessage: 'portal:yieldxyz:getDefaultsError',
523
+ resultMessage: 'portal:yieldxyz:getDefaultsResult',
524
+ data: data !== null && data !== void 0 ? data : {},
525
+ });
526
+ });
527
+ }
528
+ getYieldXyzValidators(yieldId) {
529
+ return __awaiter(this, void 0, void 0, function* () {
530
+ return this.handleRequestToIframeAndPost({
531
+ methodMessage: 'portal:yieldxyz:getValidators',
532
+ errorMessage: 'portal:yieldxyz:getValidatorsError',
533
+ resultMessage: 'portal:yieldxyz:getValidatorsResult',
534
+ data: yieldId,
535
+ });
536
+ });
537
+ }
481
538
  getLifiRoutes(data) {
482
539
  return __awaiter(this, void 0, void 0, function* () {
483
540
  return this.handleRequestToIframeAndPost({
@@ -953,6 +1010,64 @@ class Mpc {
953
1010
  });
954
1011
  });
955
1012
  }
1013
+ rpcRequest(data, options) {
1014
+ var _a, _b;
1015
+ return __awaiter(this, void 0, void 0, function* () {
1016
+ const { timeoutMs = 30000, traceId } = options !== null && options !== void 0 ? options : {};
1017
+ const requestId = (_b = (_a = crypto.randomUUID) === null || _a === void 0 ? void 0 : _a.call(crypto)) !== null && _b !== void 0 ? _b : `${Date.now()}-${Math.random()}`;
1018
+ const resolvedTraceId = traceId !== null && traceId !== void 0 ? traceId : (0, trace_1.generateTraceId)();
1019
+ logger_1.sdkLogger.debug('[Portal] rpcRequest', {
1020
+ requestId,
1021
+ method: data.method,
1022
+ chainId: data.chainId,
1023
+ traceId: resolvedTraceId,
1024
+ timeoutMs,
1025
+ });
1026
+ return new Promise((resolve, reject) => {
1027
+ let timeoutId;
1028
+ const cleanup = () => {
1029
+ window.removeEventListener('message', handleResponse);
1030
+ if (timeoutId !== undefined) {
1031
+ clearTimeout(timeoutId);
1032
+ }
1033
+ };
1034
+ const handleResponse = (event) => {
1035
+ var _a;
1036
+ const { origin } = event;
1037
+ if (origin !== this.getOrigin())
1038
+ return;
1039
+ const { type, data: result } = event.data;
1040
+ if (type === 'portal:rpc:requestResult' &&
1041
+ (result === null || result === void 0 ? void 0 : result.requestId) === requestId) {
1042
+ cleanup();
1043
+ resolve(result);
1044
+ }
1045
+ else if (type === 'portal:rpc:requestError' &&
1046
+ (result === null || result === void 0 ? void 0 : result.requestId) === requestId) {
1047
+ cleanup();
1048
+ reject(new Error((_a = result.message) !== null && _a !== void 0 ? _a : 'RPC proxy error'));
1049
+ }
1050
+ };
1051
+ timeoutId = setTimeout(() => {
1052
+ cleanup();
1053
+ const msg = `RPC request ${requestId} (${data.method}) timed out after ${timeoutMs}ms`;
1054
+ logger_1.sdkLogger.error('[Portal] rpcRequest timeout', {
1055
+ requestId,
1056
+ method: data.method,
1057
+ chainId: data.chainId,
1058
+ timeoutMs,
1059
+ });
1060
+ reject(new Error(msg));
1061
+ }, timeoutMs);
1062
+ window.addEventListener('message', handleResponse);
1063
+ this.postMessage({
1064
+ type: 'portal:rpc:request',
1065
+ data: Object.assign(Object.assign({}, data), { requestId }),
1066
+ traceId: resolvedTraceId,
1067
+ });
1068
+ });
1069
+ });
1070
+ }
956
1071
  postMessage(event) {
957
1072
  var _a, _b;
958
1073
  (_b = (_a = this.iframe) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.postMessage(event, this.getOrigin());
@@ -152,6 +152,11 @@ const signerMethods = [
152
152
  RequestMethod.sol_signMessage,
153
153
  RequestMethod.sol_signTransaction,
154
154
  ];
155
+ const iframeProxiedMethodStrings = [
156
+ 'eth_getTransactionReceipt',
157
+ 'eth_getUserOperationReceipt',
158
+ 'getSignatureStatuses',
159
+ ];
155
160
  class Provider {
156
161
  constructor({ portal, chainId }) {
157
162
  this.enforceEip155ChainId = (chainId) => {
@@ -387,6 +392,18 @@ class Provider {
387
392
  */
388
393
  handleGatewayRequest({ chainId, method, params, traceId, }) {
389
394
  return __awaiter(this, void 0, void 0, function* () {
395
+ if (iframeProxiedMethodStrings.includes(method)) {
396
+ logger_1.sdkLogger.info(`[PortalProvider] routing ${method} through iframe (chainId=${String(chainId)})`, { traceId });
397
+ return this.portal.mpc.rpcRequest({
398
+ method,
399
+ params: Array.isArray(params)
400
+ ? params
401
+ : params != null
402
+ ? [params]
403
+ : [],
404
+ chainId: chainId,
405
+ }, { traceId });
406
+ }
390
407
  const requestBody = {
391
408
  body: JSON.stringify({
392
409
  jsonrpc: '2.0',
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Request tracing for Portal API calls.
4
- * Aligns with portal-react-native X-Portal-Trace-Id / traceId behavior.
5
4
  *
6
5
  * Propagation in the Web SDK:
7
6
  * - Browser: High-level methods (backup, recover, sendAsset, provider.request) accept optional
package/lib/esm/index.js CHANGED
@@ -19,15 +19,23 @@ import PasskeyService from './passkeys';
19
19
  import { EvmAccountType } from './namespaces/evmAccountType';
20
20
  import { sdkLogger } from './logger';
21
21
  import { generateTraceId } from './shared/trace';
22
+ import { waitForEvmOrUserOpConfirmation } from './internal/waitForEvmOrUserOpConfirmation';
23
+ import { waitForSolanaTxConfirmation } from './internal/waitForSolanaTxConfirmation';
22
24
  class Portal {
23
25
  get ready() {
24
26
  return this.mpc.ready;
25
27
  }
28
+ get rpcConfig() {
29
+ return this._rpcConfig;
30
+ }
31
+ get iframeRpcConfig() {
32
+ return this._iframeRpcConfig;
33
+ }
26
34
  constructor({
27
35
  // Required
28
36
  rpcConfig,
29
37
  // Optional
30
- apiKey, authToken, authUrl, autoApprove = false, gdrive, passkey, host = 'web.portalhq.io', mpcVersion = 'v6', mpcHost = 'mpc-client.portalhq.io', featureFlags = {}, chainId, logLevel = 'none', logger = console, }) {
38
+ iframeRpcConfig, apiKey, authToken, authUrl, autoApprove = false, gdrive, passkey, host = 'web.portalhq.io', mpcVersion = 'v6', mpcHost = 'mpc-client.portalhq.io', featureFlags = {}, chainId, logLevel = 'none', logger = console, }) {
31
39
  this.errorCallbacks = [];
32
40
  this.readyCallbacks = [];
33
41
  this.logger = console;
@@ -48,7 +56,8 @@ class Portal {
48
56
  this.authToken = authToken;
49
57
  this.authUrl = authUrl;
50
58
  this.autoApprove = autoApprove;
51
- this.rpcConfig = rpcConfig;
59
+ this._rpcConfig = rpcConfig;
60
+ this._iframeRpcConfig = iframeRpcConfig;
52
61
  this.host = host;
53
62
  this.mpcHost = mpcHost;
54
63
  this.mpcVersion = mpcVersion;
@@ -66,15 +75,43 @@ class Portal {
66
75
  this.mpc = new Mpc({
67
76
  portal: this,
68
77
  });
69
- this.yield = new Yield({ mpc: this.mpc });
70
78
  this.ramps = new Ramps({ mpc: this.mpc });
71
- this.trading = new Trading({ mpc: this.mpc });
72
79
  this.security = new Security({ mpc: this.mpc });
73
- this.delegations = new Delegations({ mpc: this.mpc });
74
80
  this.provider = new Provider({
75
81
  portal: this,
76
82
  chainId: chainId ? Number(chainId) : undefined,
77
83
  });
84
+ const signAndSendTransaction = (transaction, network) => __awaiter(this, void 0, void 0, function* () {
85
+ const method = network.startsWith('solana')
86
+ ? RequestMethod.sol_signAndSendTransaction
87
+ : RequestMethod.eth_sendTransaction;
88
+ const hash = yield this.provider.request({
89
+ chainId: network,
90
+ method,
91
+ params: [transaction],
92
+ });
93
+ if (typeof hash !== 'string' || hash.length === 0) {
94
+ throw new Error('[Portal] Signing request did not return a transaction hash. The user may have rejected the request, or the provider did not complete signing.');
95
+ }
96
+ return hash;
97
+ });
98
+ const evmRequestFn = (method, params, network) => this.provider.request({ chainId: network, method, params });
99
+ this.yield = new Yield({
100
+ mpc: this.mpc,
101
+ waitForConfirmation: this.waitForConfirmation.bind(this),
102
+ evmRequestFn,
103
+ });
104
+ this.trading = new Trading({
105
+ mpc: this.mpc,
106
+ signAndSendTransaction,
107
+ waitForConfirmation: this.waitForConfirmation.bind(this),
108
+ evmRequestFn,
109
+ });
110
+ this.yield.yieldXyz.setSignAndSendTransaction(signAndSendTransaction);
111
+ this.delegations = new Delegations({
112
+ mpc: this.mpc,
113
+ signAndSendTransaction,
114
+ });
78
115
  this.evmAccountType = new EvmAccountType({ mpc: this.mpc });
79
116
  }
80
117
  /***********************************
@@ -493,6 +530,44 @@ class Portal {
493
530
  }
494
531
  });
495
532
  }
533
+ /**
534
+ * Wait until a transaction is confirmed on-chain, or until timeout.
535
+ *
536
+ * - **EVM (`eip155:*`):** Polls both `eth_getTransactionReceipt` and
537
+ * `eth_getUserOperationReceipt` (auto-detect, locks after first hit).
538
+ * - **Solana (`solana:*`):** Polls `getSignatureStatuses` until the
539
+ * commitment level from `options.commitment`
540
+ * is met (default `confirmed`).
541
+ * - **Other networks:** Returns `false` (unsupported for polling).
542
+ *
543
+ * Optional `options` tune poll/timeout for all polled chains; EVM also accepts
544
+ * `onTimeout` (default `resolve_false`) and `lockModeAfterDetection` (default `true`) for dual-receipt polling and timeout handling.
545
+ *
546
+ * All RPC calls are routed through the iframe proxy to avoid CORS issues.
547
+ */
548
+ waitForConfirmation(txHash, network, options) {
549
+ var _a, _b, _c, _d, _e, _f, _g;
550
+ return __awaiter(this, void 0, void 0, function* () {
551
+ const requestFn = (method, params, chainId) => this.provider.request({ chainId, method, params });
552
+ if (network.startsWith('eip155:')) {
553
+ return waitForEvmOrUserOpConfirmation(txHash, network, requestFn, {
554
+ pollIntervalMs: (_a = options === null || options === void 0 ? void 0 : options.pollIntervalMs) !== null && _a !== void 0 ? _a : 4000,
555
+ timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : 900000,
556
+ onTimeout: (_c = options === null || options === void 0 ? void 0 : options.onTimeout) !== null && _c !== void 0 ? _c : 'resolve_false',
557
+ lockModeAfterDetection: (_d = options === null || options === void 0 ? void 0 : options.lockModeAfterDetection) !== null && _d !== void 0 ? _d : true,
558
+ });
559
+ }
560
+ if (network.toLowerCase().startsWith('solana:')) {
561
+ return waitForSolanaTxConfirmation(txHash, network, requestFn, {
562
+ pollIntervalMs: (_e = options === null || options === void 0 ? void 0 : options.pollIntervalMs) !== null && _e !== void 0 ? _e : 4000,
563
+ timeoutMs: (_f = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _f !== void 0 ? _f : 900000,
564
+ commitment: (_g = options === null || options === void 0 ? void 0 : options.commitment) !== null && _g !== void 0 ? _g : 'confirmed',
565
+ });
566
+ }
567
+ sdkLogger.warn(`[Portal.waitForConfirmation] Unsupported network: "${network}". Returning false (cannot verify confirmation).`, { txHash, network });
568
+ return false;
569
+ });
570
+ }
496
571
  /****************************
497
572
  * Provider Methods
498
573
  ****************************/
@@ -782,12 +857,57 @@ class Portal {
782
857
  return (_a = this.mpc) === null || _a === void 0 ? void 0 : _a.getNFTs(chainId);
783
858
  });
784
859
  }
860
+ /**
861
+ * @deprecated This method is deprecated and will be removed in a future version.
862
+ * Please use `getTransactionHistory()` instead, which uses the new Portal v3 API
863
+ * endpoint and returns the unified transaction format across all chains.
864
+ *
865
+ * Legacy endpoint: /api/v3/clients/me/transactions?chainId={chainId}
866
+ * New endpoint: /api/v3/clients/me/chains/{chain}/transactions
867
+ */
785
868
  getTransactions(chainId, limit, offset, order) {
786
869
  var _a;
787
870
  return __awaiter(this, void 0, void 0, function* () {
788
871
  return (_a = this.mpc) === null || _a === void 0 ? void 0 : _a.getTransactions(chainId, limit, offset, order);
789
872
  });
790
873
  }
874
+ /**
875
+ * Retrieves transaction history for the client's wallet on the specified chain.
876
+ *
877
+ * This method uses the new Portal v3 API endpoint and returns the unified
878
+ * transaction format. Supports EVM (EIP-155), Solana, Bitcoin, Tron, and Stellar chains.
879
+ *
880
+ * Response format varies by chain:
881
+ * - Solana returns the legacy format (will be migrated in a future release)
882
+ * - All other chains return the unified TransactionHistoryItem format
883
+ *
884
+ * @param params - Request parameters
885
+ * @param params.chainId - Chain ID in CAIP-2 format (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
886
+ * @param params.limit - Maximum number of transactions to return (default: 50, max: 1000 for EVM, 15 for Solana)
887
+ * @param params.offset - Number of transactions to skip (default: 0)
888
+ * @param params.order - Sort order ('asc' or 'desc')
889
+ * @param params.address - Override wallet address (EVM only, must match client's known addresses)
890
+ * @param params.userOperations - Filter for ERC-4337 UserOperations (EVM only): 'include', 'only', or 'exclude'
891
+ * @returns Promise resolving to transaction history response
892
+ *
893
+ * @example
894
+ * ```typescript
895
+ * // Fetch latest 100 transactions for Ethereum mainnet
896
+ * const response = await portal.getTransactionHistory({
897
+ * chainId: 'eip155:1',
898
+ * limit: 100,
899
+ * order: 'desc'
900
+ * });
901
+ *
902
+ * console.log(response.data.transactions); // Array of TransactionHistoryItem
903
+ * ```
904
+ */
905
+ getTransactionHistory(params) {
906
+ var _a;
907
+ return __awaiter(this, void 0, void 0, function* () {
908
+ return (_a = this.mpc) === null || _a === void 0 ? void 0 : _a.getTransactionHistory(params);
909
+ });
910
+ }
791
911
  /**
792
912
  * @deprecated This method is deprecated. Use `portal.evaluateTransaction` instead.
793
913
  */
@@ -859,13 +979,11 @@ class Portal {
859
979
  if (!chainId) {
860
980
  throw new Error('[Portal] No chainId provided. Please provide a chainId to get the RPC endpoint');
861
981
  }
862
- // Ensure the chainId is configured in the rpcConfig.
863
982
  // eslint-disable-next-line no-prototype-builtins
864
- if (!this.rpcConfig.hasOwnProperty(chainId)) {
983
+ if (!this._rpcConfig.hasOwnProperty(chainId)) {
865
984
  throw new Error(`[Portal] No RPC endpoint configured for chainId: ${chainId}`);
866
985
  }
867
- // Derive the RPC endpoint from the rpcConfig.
868
- const gatewayUrl = this.rpcConfig[chainId];
986
+ const gatewayUrl = this._rpcConfig[chainId];
869
987
  // If the RPC endpoint is a string, return it as-is.
870
988
  if (typeof gatewayUrl === 'string') {
871
989
  return gatewayUrl;
@@ -703,5 +703,18 @@ describe('Portal', () => {
703
703
  it('should throw an error if the chainId is not present in the rpc config', () => {
704
704
  expect(() => portal.getRpcUrl('incorrect-config')).toThrow(new Error('[Portal] Could not find a valid rpcConfig entry for chainId: incorrect-config'));
705
705
  });
706
+ it('should use rpcConfig for getRpcUrl when iframeRpcConfig is also set', () => {
707
+ const iframeRpcConfig = {
708
+ 'eip155:1': 'http://localhost:9999/rpc/v1/eip155/1',
709
+ };
710
+ portal = new Portal({
711
+ rpcConfig: mockRpcConfig,
712
+ iframeRpcConfig,
713
+ });
714
+ portal.mpc = mpcMock;
715
+ portal.provider = providerMock;
716
+ expect(portal.getRpcUrl('eip155:1')).toEqual(mockEthRpcUrl);
717
+ expect(portal.iframeRpcConfig).toEqual(iframeRpcConfig);
718
+ });
706
719
  });
707
720
  });
@@ -7,9 +7,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ const DELEGATIONS_SUBMIT_CONFIG_ERROR = '[Delegations] No signer configured. Call setSignAndSendTransaction() on the instance or pass signAndSendTransaction in options.';
10
11
  export default class Delegations {
11
- constructor({ mpc }) {
12
- this.mpc = mpc;
12
+ constructor(options) {
13
+ this.mpc = options.mpc;
14
+ this.signAndSendTransaction = options.signAndSendTransaction;
15
+ }
16
+ setSignAndSendTransaction(fn) {
17
+ this.signAndSendTransaction = fn;
13
18
  }
14
19
  /**
15
20
  * Approves a delegation for a specified token on a given chain.
@@ -59,4 +64,106 @@ export default class Delegations {
59
64
  return (_a = this.mpc) === null || _a === void 0 ? void 0 : _a.delegationsTransferFrom(params);
60
65
  });
61
66
  }
67
+ /**
68
+ * Approves a delegation, then signs and broadcasts each returned transaction via
69
+ * {@link DelegationsOptions.signAndSendTransaction}.
70
+ *
71
+ * Requires `signAndSendTransaction` (provided by default on `Portal.delegations`).
72
+ * Confirming transactions on-chain remains the application's responsibility.
73
+ *
74
+ * @param params - Same as {@link Delegations.approve}
75
+ * @param options - Optional progress callbacks
76
+ * @returns Transaction hashes in submission order
77
+ */
78
+ approveAndSubmit(params, options) {
79
+ var _a;
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ const signAndSend = (_a = options === null || options === void 0 ? void 0 : options.signAndSendTransaction) !== null && _a !== void 0 ? _a : this.signAndSendTransaction;
82
+ if (!signAndSend) {
83
+ throw new Error(DELEGATIONS_SUBMIT_CONFIG_ERROR);
84
+ }
85
+ const response = yield this.approve(params);
86
+ return this.executeAndTrack(response, params.chain, signAndSend, options === null || options === void 0 ? void 0 : options.onProgress);
87
+ });
88
+ }
89
+ /**
90
+ * Revokes a delegation, then signs and broadcasts each returned transaction.
91
+ *
92
+ * Requires `signAndSendTransaction` (provided by default on `Portal.delegations`).
93
+ *
94
+ * @param params - Same as {@link Delegations.revoke}
95
+ * @param options - Optional progress callbacks
96
+ * @returns Transaction hashes in submission order
97
+ */
98
+ revokeAndSubmit(params, options) {
99
+ var _a;
100
+ return __awaiter(this, void 0, void 0, function* () {
101
+ const signAndSend = (_a = options === null || options === void 0 ? void 0 : options.signAndSendTransaction) !== null && _a !== void 0 ? _a : this.signAndSendTransaction;
102
+ if (!signAndSend) {
103
+ throw new Error(DELEGATIONS_SUBMIT_CONFIG_ERROR);
104
+ }
105
+ const response = yield this.revoke(params);
106
+ return this.executeAndTrack(response, params.chain, signAndSend, options === null || options === void 0 ? void 0 : options.onProgress);
107
+ });
108
+ }
109
+ /**
110
+ * Executes a delegated transfer, then signs and broadcasts each returned transaction.
111
+ *
112
+ * Requires `signAndSendTransaction` (provided by default on `Portal.delegations`).
113
+ *
114
+ * @param params - Same as {@link Delegations.transferFrom}
115
+ * @param options - Optional progress callbacks
116
+ * @returns Transaction hashes in submission order
117
+ */
118
+ transferAndSubmit(params, options) {
119
+ var _a;
120
+ return __awaiter(this, void 0, void 0, function* () {
121
+ const signAndSend = (_a = options === null || options === void 0 ? void 0 : options.signAndSendTransaction) !== null && _a !== void 0 ? _a : this.signAndSendTransaction;
122
+ if (!signAndSend) {
123
+ throw new Error(DELEGATIONS_SUBMIT_CONFIG_ERROR);
124
+ }
125
+ const response = yield this.transferFrom(params);
126
+ return this.executeAndTrack(response, params.chain, signAndSend, options === null || options === void 0 ? void 0 : options.onProgress);
127
+ });
128
+ }
129
+ normalizeToTransactionList(response) {
130
+ const transactions = Array.isArray(response.transactions)
131
+ ? response.transactions
132
+ : [];
133
+ const encodedTransactions = Array.isArray(response.encodedTransactions)
134
+ ? response.encodedTransactions
135
+ : [];
136
+ if (transactions.length > 0) {
137
+ return transactions;
138
+ }
139
+ if (encodedTransactions.length > 0) {
140
+ return encodedTransactions;
141
+ }
142
+ return [];
143
+ }
144
+ executeAndTrack(response, chainId, signAndSend, onProgress) {
145
+ return __awaiter(this, void 0, void 0, function* () {
146
+ const transactions = this.normalizeToTransactionList(response);
147
+ if (transactions.length === 0) {
148
+ throw new Error('No transactions in delegation response.');
149
+ }
150
+ const total = transactions.length;
151
+ const hashes = [];
152
+ for (let index = 0; index < transactions.length; index++) {
153
+ const tx = transactions[index];
154
+ // Validate transaction exists (defensive check for sparse arrays)
155
+ if (!tx) {
156
+ throw new Error(`Transaction at index ${index} is undefined or null. This indicates a malformed API response.`);
157
+ }
158
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress({ step: 'signing', index, total });
159
+ const hash = yield signAndSend(tx, chainId);
160
+ if (typeof hash !== 'string' || hash.trim().length === 0) {
161
+ throw new Error(`Invalid transaction hash returned from signAndSendTransaction at index ${index} for chain ${chainId}.`);
162
+ }
163
+ hashes.push(hash);
164
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress({ step: 'submitted', index, total, hash });
165
+ }
166
+ return { hashes };
167
+ });
168
+ }
62
169
  }