@portal-hq/web 3.13.2 → 3.14.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 +318 -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 +117 -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 +313 -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 +117 -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 +1 -1
  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 +421 -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 +144 -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 +83 -1
  96. package/types.d.ts +6 -0
@@ -11,7 +11,7 @@ import { PortalMpcError } from './errors';
11
11
  import { sdkLogger } from '../logger';
12
12
  import { BackupMethods, } from '../index';
13
13
  import { generateTraceId } from '../shared/trace';
14
- const WEB_SDK_VERSION = '3.13.2';
14
+ const WEB_SDK_VERSION = '3.14.0';
15
15
  class Mpc {
16
16
  get ready() {
17
17
  return this._ready;
@@ -35,6 +35,8 @@ class Mpc {
35
35
  mpcVersion: this.portal.mpcVersion,
36
36
  featureFlags: this.portal.featureFlags,
37
37
  logLevel: this.portal.getLogLevel(),
38
+ rpcConfig: this.portal.rpcConfig,
39
+ iframeRpcConfig: this.portal.iframeRpcConfig,
38
40
  };
39
41
  const message = {
40
42
  type: 'portal:configure',
@@ -308,8 +310,18 @@ class Mpc {
308
310
  });
309
311
  });
310
312
  }
313
+ /**
314
+ * @deprecated This method is deprecated and will be removed in a future version.
315
+ * Please use `getTransactionHistory()` instead, which uses the new Portal v3 API
316
+ * endpoint and returns the unified transaction format across all chains.
317
+ */
311
318
  getTransactions(chainId, limit, offset, order) {
312
319
  return __awaiter(this, void 0, void 0, function* () {
320
+ // Log deprecation warning
321
+ sdkLogger.warn('[DEPRECATED] getTransactions() is deprecated and will be removed in a future version. ' +
322
+ 'Please use getTransactionHistory() instead, which provides improved type safety with ' +
323
+ 'discriminated unions for regular transactions and UserOperations, and proper polymorphic ' +
324
+ 'response types for Solana vs unified formats.');
313
325
  return this.handleRequestToIframeAndPost({
314
326
  methodMessage: 'portal:getTransactions',
315
327
  errorMessage: 'portal:getTransactionsError',
@@ -323,6 +335,31 @@ class Mpc {
323
335
  });
324
336
  });
325
337
  }
338
+ /**
339
+ * Retrieves transaction history for the client's wallet on the specified chain.
340
+ *
341
+ * This method uses the new Portal v3 API endpoint and returns the unified
342
+ * transaction format. Supports EVM (EIP-155), Solana, Bitcoin, Tron, and Stellar chains.
343
+ *
344
+ * @param params - Request parameters
345
+ * @param params.chainId - Chain ID in CAIP-2 format (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
346
+ * @param params.limit - Maximum number of transactions to return (default: 50)
347
+ * @param params.offset - Number of transactions to skip (default: 0)
348
+ * @param params.order - Sort order ('asc' or 'desc')
349
+ * @param params.address - Override wallet address (EVM only)
350
+ * @param params.userOperations - Filter for ERC-4337 UserOperations (EVM only)
351
+ * @returns Promise resolving to transaction history response
352
+ */
353
+ getTransactionHistory(params) {
354
+ return __awaiter(this, void 0, void 0, function* () {
355
+ return this.handleRequestToIframeAndPost({
356
+ methodMessage: 'portal:getTransactionHistory',
357
+ errorMessage: 'portal:getTransactionHistoryError',
358
+ resultMessage: 'portal:getTransactionHistoryResult',
359
+ data: params,
360
+ });
361
+ });
362
+ }
326
363
  setBackupStatus(status, backupIds) {
327
364
  return __awaiter(this, void 0, void 0, function* () {
328
365
  return this.handleRequestToIframeAndPost({
@@ -475,6 +512,26 @@ class Mpc {
475
512
  });
476
513
  });
477
514
  }
515
+ getYieldXyzDefaults(data) {
516
+ return __awaiter(this, void 0, void 0, function* () {
517
+ return this.handleRequestToIframeAndPost({
518
+ methodMessage: 'portal:yieldxyz:getDefaults',
519
+ errorMessage: 'portal:yieldxyz:getDefaultsError',
520
+ resultMessage: 'portal:yieldxyz:getDefaultsResult',
521
+ data: data !== null && data !== void 0 ? data : {},
522
+ });
523
+ });
524
+ }
525
+ getYieldXyzValidators(yieldId) {
526
+ return __awaiter(this, void 0, void 0, function* () {
527
+ return this.handleRequestToIframeAndPost({
528
+ methodMessage: 'portal:yieldxyz:getValidators',
529
+ errorMessage: 'portal:yieldxyz:getValidatorsError',
530
+ resultMessage: 'portal:yieldxyz:getValidatorsResult',
531
+ data: yieldId,
532
+ });
533
+ });
534
+ }
478
535
  getLifiRoutes(data) {
479
536
  return __awaiter(this, void 0, void 0, function* () {
480
537
  return this.handleRequestToIframeAndPost({
@@ -950,6 +1007,65 @@ class Mpc {
950
1007
  });
951
1008
  });
952
1009
  }
1010
+ rpcRequest(data, options) {
1011
+ return __awaiter(this, void 0, void 0, function* () {
1012
+ const { timeoutMs = 30000, traceId } = options !== null && options !== void 0 ? options : {};
1013
+ const requestId = typeof crypto !== 'undefined' && crypto.randomUUID
1014
+ ? crypto.randomUUID()
1015
+ : `${Date.now()}-${Math.random()}`;
1016
+ const resolvedTraceId = traceId !== null && traceId !== void 0 ? traceId : generateTraceId();
1017
+ sdkLogger.debug('[Portal] rpcRequest', {
1018
+ requestId,
1019
+ method: data.method,
1020
+ chainId: data.chainId,
1021
+ traceId: resolvedTraceId,
1022
+ timeoutMs,
1023
+ });
1024
+ return new Promise((resolve, reject) => {
1025
+ let timeoutId;
1026
+ const cleanup = () => {
1027
+ window.removeEventListener('message', handleResponse);
1028
+ if (timeoutId !== undefined) {
1029
+ clearTimeout(timeoutId);
1030
+ }
1031
+ };
1032
+ const handleResponse = (event) => {
1033
+ var _a;
1034
+ const { origin } = event;
1035
+ if (origin !== this.getOrigin())
1036
+ return;
1037
+ const { type, data: result } = event.data;
1038
+ if (type === 'portal:rpc:requestResult' &&
1039
+ (result === null || result === void 0 ? void 0 : result.requestId) === requestId) {
1040
+ cleanup();
1041
+ resolve(result);
1042
+ }
1043
+ else if (type === 'portal:rpc:requestError' &&
1044
+ (result === null || result === void 0 ? void 0 : result.requestId) === requestId) {
1045
+ cleanup();
1046
+ reject(new Error((_a = result.message) !== null && _a !== void 0 ? _a : 'RPC proxy error'));
1047
+ }
1048
+ };
1049
+ timeoutId = setTimeout(() => {
1050
+ cleanup();
1051
+ const msg = `RPC request ${requestId} (${data.method}) timed out after ${timeoutMs}ms`;
1052
+ sdkLogger.error('[Portal] rpcRequest timeout', {
1053
+ requestId,
1054
+ method: data.method,
1055
+ chainId: data.chainId,
1056
+ timeoutMs,
1057
+ });
1058
+ reject(new Error(msg));
1059
+ }, timeoutMs);
1060
+ window.addEventListener('message', handleResponse);
1061
+ this.postMessage({
1062
+ type: 'portal:rpc:request',
1063
+ data: Object.assign(Object.assign({}, data), { requestId }),
1064
+ traceId: resolvedTraceId,
1065
+ });
1066
+ });
1067
+ });
1068
+ }
953
1069
  postMessage(event) {
954
1070
  var _a, _b;
955
1071
  (_b = (_a = this.iframe) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.postMessage(event, this.getOrigin());
@@ -149,6 +149,11 @@ const signerMethods = [
149
149
  RequestMethod.sol_signMessage,
150
150
  RequestMethod.sol_signTransaction,
151
151
  ];
152
+ const iframeProxiedMethodStrings = [
153
+ 'eth_getTransactionReceipt',
154
+ 'eth_getUserOperationReceipt',
155
+ 'getSignatureStatuses',
156
+ ];
152
157
  class Provider {
153
158
  constructor({ portal, chainId }) {
154
159
  this.enforceEip155ChainId = (chainId) => {
@@ -384,6 +389,18 @@ class Provider {
384
389
  */
385
390
  handleGatewayRequest({ chainId, method, params, traceId, }) {
386
391
  return __awaiter(this, void 0, void 0, function* () {
392
+ if (iframeProxiedMethodStrings.includes(method)) {
393
+ sdkLogger.info(`[PortalProvider] routing ${method} through iframe (chainId=${String(chainId)})`, { traceId });
394
+ return this.portal.mpc.rpcRequest({
395
+ method,
396
+ params: Array.isArray(params)
397
+ ? params
398
+ : params != null
399
+ ? [params]
400
+ : [],
401
+ chainId: chainId,
402
+ }, { traceId });
403
+ }
387
404
  const requestBody = {
388
405
  body: JSON.stringify({
389
406
  jsonrpc: '2.0',
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * Request tracing for Portal API calls.
3
- * Aligns with portal-react-native X-Portal-Trace-Id / traceId behavior.
4
3
  *
5
4
  * Propagation in the Web SDK:
6
5
  * - Browser: High-level methods (backup, recover, sendAsset, provider.request) accept optional
package/noah-types.d.ts CHANGED
@@ -1,11 +1,23 @@
1
1
  export type {
2
- NoahAmountConditionRow,
2
+ AccountHolderDetails,
3
+ AmountCondition,
4
+ BankDetails,
5
+ BankToAddressRelatedPaymentMethod,
6
+ Channel,
7
+ ChannelCalculated,
8
+ ChannelLimits,
9
+ DepositSourceTriggerCondition,
10
+ FormNextStep,
11
+ IssuerDetails,
3
12
  NoahGetPaymentMethodsResponse,
4
13
  NoahGetPaymentMethodsResponseData,
5
14
  NoahGetPayoutChannelFormResponse,
15
+ NoahGetPayoutChannelFormResponseData,
6
16
  NoahGetPayoutChannelsRequest,
7
17
  NoahGetPayoutChannelsResponse,
18
+ NoahGetPayoutChannelsResponseData,
8
19
  NoahGetPayoutCountriesResponse,
20
+ NoahGetPayoutCountriesResponseData,
9
21
  NoahGetPayoutQuoteRequest,
10
22
  NoahGetPayoutQuoteResponse,
11
23
  NoahGetPayoutQuoteResponseData,
@@ -18,11 +30,13 @@ export type {
18
30
  NoahInitiatePayoutRequest,
19
31
  NoahInitiatePayoutResponse,
20
32
  NoahInitiatePayoutResponseData,
21
- NoahPayoutConditionBlock,
22
33
  NoahSingleOnchainDepositSourceTriggerInput,
23
34
  NoahSimulatePayinRequest,
24
35
  NoahSimulatePayinResponse,
25
36
  NoahSimulatePayinResponseData,
26
37
  NoahSolanaCaipId,
38
+ PaymentMethod,
39
+ PaymentMethodDetails,
27
40
  PortalApiSuccessEnvelope,
41
+ StreetAddress,
28
42
  } from './src/shared/types/noah'
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Portal MPC Support for Web",
4
4
  "author": "Portal Labs, Inc.",
5
5
  "homepage": "https://portalhq.io/",
6
- "version": "3.13.2",
6
+ "version": "3.14.0",
7
7
  "license": "MIT",
8
8
  "main": "lib/commonjs/index",
9
9
  "module": "lib/esm/index",
package/src/index.test.ts CHANGED
@@ -1138,5 +1138,20 @@ describe('Portal', () => {
1138
1138
  ),
1139
1139
  )
1140
1140
  })
1141
+
1142
+ it('should use rpcConfig for getRpcUrl when iframeRpcConfig is also set', () => {
1143
+ const iframeRpcConfig = {
1144
+ 'eip155:1': 'http://localhost:9999/rpc/v1/eip155/1',
1145
+ }
1146
+ portal = new Portal({
1147
+ rpcConfig: mockRpcConfig,
1148
+ iframeRpcConfig,
1149
+ })
1150
+ portal.mpc = mpcMock
1151
+ portal.provider = providerMock
1152
+
1153
+ expect(portal.getRpcUrl('eip155:1')).toEqual(mockEthRpcUrl)
1154
+ expect(portal.iframeRpcConfig).toEqual(iframeRpcConfig)
1155
+ })
1141
1156
  })
1142
1157
  })
package/src/index.ts CHANGED
@@ -16,6 +16,8 @@ import {
16
16
  NFTAsset,
17
17
  OrgBackupShares,
18
18
  SendAssetParams,
19
+ GetTransactionHistoryParams,
20
+ GetTransactionHistoryResponse,
19
21
  type BackupConfigs,
20
22
  type BackupResponse,
21
23
  type Balance,
@@ -58,6 +60,8 @@ import PasskeyService from './passkeys'
58
60
  import { EvmAccountType } from './namespaces/evmAccountType'
59
61
  import { sdkLogger } from './logger'
60
62
  import { generateTraceId } from './shared/trace'
63
+ import { waitForEvmOrUserOpConfirmation } from './internal/waitForEvmOrUserOpConfirmation'
64
+ import { waitForSolanaTxConfirmation } from './internal/waitForSolanaTxConfirmation'
61
65
  import type { LogLevel, ILogger } from '../types'
62
66
 
63
67
  class Portal {
@@ -82,7 +86,8 @@ class Portal {
82
86
  public featureFlags: FeatureFlags
83
87
 
84
88
  private errorCallbacks: ((reason: string) => any | Promise<any>)[] = []
85
- private rpcConfig: RpcConfig
89
+ private _rpcConfig: RpcConfig
90
+ private _iframeRpcConfig?: RpcConfig
86
91
  private readyCallbacks: (() => any | Promise<any>)[] = []
87
92
  private passkeyService?: PasskeyService
88
93
  private passkeyServiceDefaultDomain?: string
@@ -92,11 +97,20 @@ class Portal {
92
97
  return this.mpc.ready
93
98
  }
94
99
 
100
+ public get rpcConfig(): RpcConfig {
101
+ return this._rpcConfig
102
+ }
103
+
104
+ public get iframeRpcConfig(): RpcConfig | undefined {
105
+ return this._iframeRpcConfig
106
+ }
107
+
95
108
  constructor({
96
109
  // Required
97
110
  rpcConfig,
98
111
 
99
112
  // Optional
113
+ iframeRpcConfig,
100
114
  apiKey,
101
115
  authToken,
102
116
  authUrl,
@@ -115,7 +129,8 @@ class Portal {
115
129
  this.authToken = authToken
116
130
  this.authUrl = authUrl
117
131
  this.autoApprove = autoApprove
118
- this.rpcConfig = rpcConfig
132
+ this._rpcConfig = rpcConfig
133
+ this._iframeRpcConfig = iframeRpcConfig
119
134
  this.host = host
120
135
  this.mpcHost = mpcHost
121
136
  this.mpcVersion = mpcVersion
@@ -136,21 +151,58 @@ class Portal {
136
151
  portal: this,
137
152
  })
138
153
 
139
- this.yield = new Yield({ mpc: this.mpc })
140
-
141
154
  this.ramps = new Ramps({ mpc: this.mpc })
142
155
 
143
- this.trading = new Trading({ mpc: this.mpc })
144
-
145
156
  this.security = new Security({ mpc: this.mpc })
146
157
 
147
- this.delegations = new Delegations({ mpc: this.mpc })
148
-
149
158
  this.provider = new Provider({
150
159
  portal: this,
151
160
  chainId: chainId ? Number(chainId) : undefined,
152
161
  })
153
162
 
163
+ const signAndSendTransaction = async (
164
+ transaction: unknown,
165
+ network: string,
166
+ ): Promise<string> => {
167
+ const method = network.startsWith('solana')
168
+ ? RequestMethod.sol_signAndSendTransaction
169
+ : RequestMethod.eth_sendTransaction
170
+ const hash = await this.provider.request({
171
+ chainId: network,
172
+ method,
173
+ params: [transaction],
174
+ })
175
+ if (typeof hash !== 'string' || hash.length === 0) {
176
+ throw new Error(
177
+ '[Portal] Signing request did not return a transaction hash. The user may have rejected the request, or the provider did not complete signing.',
178
+ )
179
+ }
180
+ return hash
181
+ }
182
+
183
+ const evmRequestFn = (method: string, params: unknown[], network: string) =>
184
+ this.provider.request({ chainId: network, method, params })
185
+
186
+ this.yield = new Yield({
187
+ mpc: this.mpc,
188
+ waitForConfirmation: this.waitForConfirmation.bind(this),
189
+ evmRequestFn,
190
+ })
191
+
192
+ this.trading = new Trading({
193
+ mpc: this.mpc,
194
+ signAndSendTransaction,
195
+ waitForConfirmation: this.waitForConfirmation.bind(this),
196
+ evmRequestFn,
197
+ })
198
+
199
+ this.yield.yieldXyz.setSignAndSendTransaction(signAndSendTransaction)
200
+
201
+ this.delegations = new Delegations({
202
+ mpc: this.mpc,
203
+ signAndSendTransaction,
204
+ })
205
+
154
206
  this.evmAccountType = new EvmAccountType({ mpc: this.mpc })
155
207
  }
156
208
 
@@ -690,6 +742,62 @@ class Portal {
690
742
  }
691
743
  }
692
744
 
745
+ /**
746
+ * Wait until a transaction is confirmed on-chain, or until timeout.
747
+ *
748
+ * - **EVM (`eip155:*`):** Polls both `eth_getTransactionReceipt` and
749
+ * `eth_getUserOperationReceipt` (auto-detect, locks after first hit).
750
+ * - **Solana (`solana:*`):** Polls `getSignatureStatuses` until the
751
+ * commitment level from `options.commitment`
752
+ * is met (default `confirmed`).
753
+ * - **Other networks:** Returns `false` (unsupported for polling).
754
+ *
755
+ * Optional `options` tune poll/timeout for all polled chains; EVM also accepts
756
+ * `onTimeout` (default `resolve_false`) and `lockModeAfterDetection` (default `true`) for dual-receipt polling and timeout handling.
757
+ *
758
+ * All RPC calls are routed through the iframe proxy to avoid CORS issues.
759
+ */
760
+ public async waitForConfirmation(
761
+ txHash: string,
762
+ network: string,
763
+ options?: {
764
+ pollIntervalMs?: number
765
+ timeoutMs?: number
766
+ /** EVM / dual-mode only. @default 'resolve_false' */
767
+ onTimeout?: 'resolve_false' | 'throw'
768
+ /** EVM / dual-mode only. @default true */
769
+ lockModeAfterDetection?: boolean
770
+ /** Solana only. @default 'confirmed' */
771
+ commitment?: 'processed' | 'confirmed' | 'finalized'
772
+ },
773
+ ): Promise<boolean> {
774
+ const requestFn = (method: string, params: unknown[], chainId: string) =>
775
+ this.provider.request({ chainId, method, params })
776
+
777
+ if (network.startsWith('eip155:')) {
778
+ return waitForEvmOrUserOpConfirmation(txHash, network, requestFn, {
779
+ pollIntervalMs: options?.pollIntervalMs ?? 4_000,
780
+ timeoutMs: options?.timeoutMs ?? 900_000,
781
+ onTimeout: options?.onTimeout ?? 'resolve_false',
782
+ lockModeAfterDetection: options?.lockModeAfterDetection ?? true,
783
+ })
784
+ }
785
+
786
+ if (network.toLowerCase().startsWith('solana:')) {
787
+ return waitForSolanaTxConfirmation(txHash, network, requestFn, {
788
+ pollIntervalMs: options?.pollIntervalMs ?? 4_000,
789
+ timeoutMs: options?.timeoutMs ?? 900_000,
790
+ commitment: options?.commitment ?? 'confirmed',
791
+ })
792
+ }
793
+
794
+ sdkLogger.warn(
795
+ `[Portal.waitForConfirmation] Unsupported network: "${network}". Returning false (cannot verify confirmation).`,
796
+ { txHash, network },
797
+ )
798
+ return false
799
+ }
800
+
693
801
  /****************************
694
802
  * Provider Methods
695
803
  ****************************/
@@ -1054,6 +1162,14 @@ class Portal {
1054
1162
  return this.mpc?.getNFTs(chainId)
1055
1163
  }
1056
1164
 
1165
+ /**
1166
+ * @deprecated This method is deprecated and will be removed in a future version.
1167
+ * Please use `getTransactionHistory()` instead, which uses the new Portal v3 API
1168
+ * endpoint and returns the unified transaction format across all chains.
1169
+ *
1170
+ * Legacy endpoint: /api/v3/clients/me/transactions?chainId={chainId}
1171
+ * New endpoint: /api/v3/clients/me/chains/{chain}/transactions
1172
+ */
1057
1173
  public async getTransactions(
1058
1174
  chainId: string,
1059
1175
  limit?: number,
@@ -1063,6 +1179,43 @@ class Portal {
1063
1179
  return this.mpc?.getTransactions(chainId, limit, offset, order)
1064
1180
  }
1065
1181
 
1182
+ /**
1183
+ * Retrieves transaction history for the client's wallet on the specified chain.
1184
+ *
1185
+ * This method uses the new Portal v3 API endpoint and returns the unified
1186
+ * transaction format. Supports EVM (EIP-155), Solana, Bitcoin, Tron, and Stellar chains.
1187
+ *
1188
+ * Response format varies by chain:
1189
+ * - Solana returns the legacy format (will be migrated in a future release)
1190
+ * - All other chains return the unified TransactionHistoryItem format
1191
+ *
1192
+ * @param params - Request parameters
1193
+ * @param params.chainId - Chain ID in CAIP-2 format (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp')
1194
+ * @param params.limit - Maximum number of transactions to return (default: 50, max: 1000 for EVM, 15 for Solana)
1195
+ * @param params.offset - Number of transactions to skip (default: 0)
1196
+ * @param params.order - Sort order ('asc' or 'desc')
1197
+ * @param params.address - Override wallet address (EVM only, must match client's known addresses)
1198
+ * @param params.userOperations - Filter for ERC-4337 UserOperations (EVM only): 'include', 'only', or 'exclude'
1199
+ * @returns Promise resolving to transaction history response
1200
+ *
1201
+ * @example
1202
+ * ```typescript
1203
+ * // Fetch latest 100 transactions for Ethereum mainnet
1204
+ * const response = await portal.getTransactionHistory({
1205
+ * chainId: 'eip155:1',
1206
+ * limit: 100,
1207
+ * order: 'desc'
1208
+ * });
1209
+ *
1210
+ * console.log(response.data.transactions); // Array of TransactionHistoryItem
1211
+ * ```
1212
+ */
1213
+ public async getTransactionHistory(
1214
+ params: GetTransactionHistoryParams,
1215
+ ): Promise<GetTransactionHistoryResponse> {
1216
+ return this.mpc?.getTransactionHistory(params)
1217
+ }
1218
+
1066
1219
  /**
1067
1220
  * @deprecated This method is deprecated. Use `portal.evaluateTransaction` instead.
1068
1221
  */
@@ -1150,16 +1303,14 @@ class Portal {
1150
1303
  )
1151
1304
  }
1152
1305
 
1153
- // Ensure the chainId is configured in the rpcConfig.
1154
1306
  // eslint-disable-next-line no-prototype-builtins
1155
- if (!this.rpcConfig.hasOwnProperty(chainId)) {
1307
+ if (!this._rpcConfig.hasOwnProperty(chainId)) {
1156
1308
  throw new Error(
1157
1309
  `[Portal] No RPC endpoint configured for chainId: ${chainId}`,
1158
1310
  )
1159
1311
  }
1160
1312
 
1161
- // Derive the RPC endpoint from the rpcConfig.
1162
- const gatewayUrl = this.rpcConfig[chainId]
1313
+ const gatewayUrl = this._rpcConfig[chainId]
1163
1314
 
1164
1315
  // If the RPC endpoint is a string, return it as-is.
1165
1316
  if (typeof gatewayUrl === 'string') {
@@ -1307,6 +1458,13 @@ class Portal {
1307
1458
  }
1308
1459
  }
1309
1460
 
1461
+ export type {
1462
+ IDelegations,
1463
+ IPortalDelegationsApi,
1464
+ DelegationsOptions,
1465
+ DelegationSubmitOptions,
1466
+ } from './integrations/delegations'
1467
+
1310
1468
  export {
1311
1469
  type YieldXyzEnterRequest,
1312
1470
  type YieldXyzEnterYieldResponse,
@@ -1324,6 +1482,22 @@ export {
1324
1482
  type YieldXyzManageYieldResponse,
1325
1483
  type YieldXyzTrackTransactionRequest,
1326
1484
  type YieldXyzTrackTransactionResponse,
1485
+ type YieldXyzGetYieldDefaultsRequest,
1486
+ type YieldXyzGetYieldDefaultsResponse,
1487
+ type YieldXyzGetYieldValidatorsResponse,
1488
+ type YieldDepositParams,
1489
+ type YieldDepositResult,
1490
+ type YieldSubmitOptions,
1491
+ type YieldSubmitProgress,
1492
+ type YieldWithdrawParams,
1493
+ type YieldWithdrawResult,
1494
+ type LifiTradeAssetParams,
1495
+ type LifiTradeAssetOptions,
1496
+ type LifiTradeAssetResult,
1497
+ type LifiPollStatusOptions,
1498
+ type ZeroXTradeAssetParams,
1499
+ type ZeroXTradeAssetOptions,
1500
+ type ZeroXTradeAssetResult,
1327
1501
  type ApproveDelegationRequest,
1328
1502
  type ApproveDelegationResponse,
1329
1503
  type RevokeDelegationRequest,
@@ -1332,6 +1506,7 @@ export {
1332
1506
  type DelegationStatusResponse,
1333
1507
  type TransferFromRequest,
1334
1508
  type TransferFromResponse,
1509
+ type DelegationSubmitProgress,
1335
1510
  type EvmAccountTypeGetStatusRequest,
1336
1511
  type EvmAccountTypeGetStatusResponse,
1337
1512
  type BuildAuthorizationListRequest,
@@ -1341,16 +1516,23 @@ export {
1341
1516
  type UpgradeTo7702Request,
1342
1517
  type UpgradeTo7702Response,
1343
1518
  type GetAddressesResponse,
1519
+ type GetTransactionHistoryParams,
1520
+ type GetTransactionHistoryResponse,
1521
+ type TransactionHistoryItem,
1522
+ type SolanaTransactionDetails,
1523
+ type Transaction,
1344
1524
  } from './shared/types'
1345
1525
 
1346
1526
  export type {
1347
- NoahAmountConditionRow,
1348
1527
  NoahGetPaymentMethodsResponse,
1349
1528
  NoahGetPaymentMethodsResponseData,
1350
1529
  NoahGetPayoutChannelFormResponse,
1530
+ NoahGetPayoutChannelFormResponseData,
1351
1531
  NoahGetPayoutChannelsRequest,
1352
1532
  NoahGetPayoutChannelsResponse,
1533
+ NoahGetPayoutChannelsResponseData,
1353
1534
  NoahGetPayoutCountriesResponse,
1535
+ NoahGetPayoutCountriesResponseData,
1354
1536
  NoahGetPayoutQuoteRequest,
1355
1537
  NoahGetPayoutQuoteResponse,
1356
1538
  NoahGetPayoutQuoteResponseData,
@@ -1363,7 +1545,6 @@ export type {
1363
1545
  NoahInitiatePayoutRequest,
1364
1546
  NoahInitiatePayoutResponse,
1365
1547
  NoahInitiatePayoutResponseData,
1366
- NoahPayoutConditionBlock,
1367
1548
  NoahSingleOnchainDepositSourceTriggerInput,
1368
1549
  NoahSimulatePayinRequest,
1369
1550
  NoahSimulatePayinResponse,
@@ -1372,6 +1553,14 @@ export type {
1372
1553
  PortalApiSuccessEnvelope,
1373
1554
  } from './shared/types'
1374
1555
 
1556
+ export type {
1557
+ TradingOptions,
1558
+ LifiOptions,
1559
+ ZeroXOptions,
1560
+ ILiFi,
1561
+ IZeroX,
1562
+ } from './integrations/trading'
1563
+
1375
1564
  export { MpcError, MpcErrorCodes } from './mpc'
1376
1565
 
1377
1566
  export { PortalMpcError } from './mpc/errors'