@portal-hq/web 3.17.0 → 3.18.0-alpha.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 (55) hide show
  1. package/lib/commonjs/index.js +62 -20
  2. package/lib/commonjs/index.test.js +51 -10
  3. package/lib/commonjs/integrations/ramps/index.js +2 -0
  4. package/lib/commonjs/integrations/ramps/meld/index.js +121 -0
  5. package/lib/commonjs/integrations/ramps/meld/index.test.js +162 -0
  6. package/lib/commonjs/integrations/ramps/noah/index.js +2 -2
  7. package/lib/commonjs/integrations/ramps/noah/index.test.js +11 -2
  8. package/lib/commonjs/mpc/index.js +199 -3
  9. package/lib/commonjs/mpc/index.test.js +634 -0
  10. package/lib/commonjs/provider/index.js +44 -2
  11. package/lib/commonjs/provider/index.test.js +186 -0
  12. package/lib/commonjs/rpc.test.js +162 -0
  13. package/lib/commonjs/shared/rpc/auth.js +29 -0
  14. package/lib/commonjs/shared/rpc/defaults.js +40 -0
  15. package/lib/commonjs/shared/types/index.js +1 -0
  16. package/lib/commonjs/shared/types/meld.js +2 -0
  17. package/lib/esm/index.js +59 -19
  18. package/lib/esm/index.test.js +51 -10
  19. package/lib/esm/integrations/ramps/index.js +2 -0
  20. package/lib/esm/integrations/ramps/meld/index.js +118 -0
  21. package/lib/esm/integrations/ramps/meld/index.test.js +157 -0
  22. package/lib/esm/integrations/ramps/noah/index.js +2 -2
  23. package/lib/esm/integrations/ramps/noah/index.test.js +11 -2
  24. package/lib/esm/mpc/index.js +199 -3
  25. package/lib/esm/mpc/index.test.js +635 -1
  26. package/lib/esm/provider/index.js +44 -2
  27. package/lib/esm/provider/index.test.js +186 -0
  28. package/lib/esm/rpc.test.js +157 -0
  29. package/lib/esm/shared/rpc/auth.js +25 -0
  30. package/lib/esm/shared/rpc/defaults.js +36 -0
  31. package/lib/esm/shared/types/index.js +1 -0
  32. package/lib/esm/shared/types/meld.js +1 -0
  33. package/noah-types.d.ts +17 -1
  34. package/package.json +3 -2
  35. package/src/__mocks/constants.ts +68 -0
  36. package/src/__mocks/portal/portal.ts +0 -1
  37. package/src/index.test.ts +90 -0
  38. package/src/index.ts +159 -7
  39. package/src/integrations/ramps/index.ts +3 -0
  40. package/src/integrations/ramps/meld/index.test.ts +189 -0
  41. package/src/integrations/ramps/meld/index.ts +149 -0
  42. package/src/integrations/ramps/noah/index.test.ts +11 -2
  43. package/src/integrations/ramps/noah/index.ts +5 -2
  44. package/src/mpc/index.test.ts +804 -7
  45. package/src/mpc/index.ts +247 -3
  46. package/src/provider/index.test.ts +233 -0
  47. package/src/provider/index.ts +64 -1
  48. package/src/rpc.test.ts +190 -0
  49. package/src/shared/rpc/auth.ts +27 -0
  50. package/src/shared/rpc/defaults.ts +41 -0
  51. package/src/shared/types/common.ts +24 -0
  52. package/src/shared/types/index.ts +1 -0
  53. package/src/shared/types/meld.ts +302 -0
  54. package/src/shared/types/noah.ts +191 -29
  55. package/types.d.ts +65 -3
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.ChainNamespace = exports.PortalCurve = exports.GetTransactionsOrder = exports.BackupMethods = exports.MpcStatuses = exports.RequestMethod = exports.PortalMpcError = exports.MpcErrorCodes = exports.MpcError = void 0;
38
+ exports.ChainNamespace = exports.PortalCurve = exports.GetTransactionsOrder = exports.BackupMethods = exports.MpcStatuses = exports.RequestMethod = exports.DEFAULT_RPC_HOST = exports.buildDefaultRpcConfig = exports.PortalMpcError = exports.MpcErrorCodes = exports.MpcError = void 0;
39
39
  const web3_js_1 = require("@solana/web3.js");
40
40
  const mpc_1 = __importDefault(require("./mpc"));
41
41
  const provider_1 = __importStar(require("./provider"));
@@ -48,6 +48,7 @@ const passkeys_1 = __importDefault(require("./passkeys"));
48
48
  const evmAccountType_1 = require("./namespaces/evmAccountType");
49
49
  const logger_1 = require("./logger");
50
50
  const trace_1 = require("./shared/trace");
51
+ const defaults_1 = require("./shared/rpc/defaults");
51
52
  const waitForEvmOrUserOpConfirmation_1 = require("./internal/waitForEvmOrUserOpConfirmation");
52
53
  const waitForSolanaTxConfirmation_1 = require("./internal/waitForSolanaTxConfirmation");
53
54
  class Portal {
@@ -61,12 +62,14 @@ class Portal {
61
62
  return this._iframeRpcConfig;
62
63
  }
63
64
  constructor({
64
- // Required
65
- rpcConfig,
65
+ // Optional — defaults to Portal-managed RPC gateway when omitted or empty
66
+ rpcConfig, gatewayHost,
66
67
  // Optional
67
- 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, }) {
68
+ iframeRpcConfig, apiKey, authToken, authUrl, autoApprove = false, gdrive, passkey, host = defaults_1.DEFAULT_RPC_HOST, mpcVersion = 'v6', mpcHost = 'mpc-client.portalhq.io', featureFlags = {}, chainId, logLevel = 'none', logger = console, }) {
68
69
  this.errorCallbacks = [];
69
70
  this.readyCallbacks = [];
71
+ this.walletNotOnDeviceCallbacks = [];
72
+ this.lastWalletNotOnDevicePayload = null;
70
73
  this.logger = console;
71
74
  this.sendEth = ({ chainId, to, value, }) => __awaiter(this, void 0, void 0, function* () {
72
75
  return this.provider.request({
@@ -81,11 +84,27 @@ class Portal {
81
84
  ],
82
85
  });
83
86
  });
87
+ // Gateway host resolution — three-level priority:
88
+ // 1. gatewayHost (explicit override)
89
+ // 2. host (environment identity: 'web.portalhq.io' prod / 'web.portalhq.dev' staging)
90
+ // 3. DEFAULT_RPC_HOST (compile-time production fallback)
91
+ //
92
+ // `host` doubles as the RPC gateway domain in Portal deployments — the two are
93
+ // always on the same hostname per environment (confirmed in deploy configs).
94
+ // Deriving from `host` ensures staging callers don't silently RPC to production
95
+ // when only `host` is overridden. Explicit `gatewayHost` takes full precedence
96
+ // for custom domains or non-standard deployments.
97
+ const effectiveGatewayHost = gatewayHost !== null && gatewayHost !== void 0 ? gatewayHost : host;
98
+ // Resolve rpcConfig: undefined or {} → auto-generate from effectiveGatewayHost.
99
+ // An explicit non-empty map is used verbatim — no merging with defaults.
100
+ const resolvedRpcConfig = !rpcConfig || Object.keys(rpcConfig).length === 0
101
+ ? (0, defaults_1.buildDefaultRpcConfig)(effectiveGatewayHost)
102
+ : rpcConfig;
84
103
  this.apiKey = apiKey;
85
104
  this.authToken = authToken;
86
105
  this.authUrl = authUrl;
87
106
  this.autoApprove = autoApprove;
88
- this._rpcConfig = rpcConfig;
107
+ this._rpcConfig = resolvedRpcConfig;
89
108
  this._iframeRpcConfig = iframeRpcConfig;
90
109
  this.host = host;
91
110
  this.mpcHost = mpcHost;
@@ -201,6 +220,27 @@ class Portal {
201
220
  this.readyCallbacks = [];
202
221
  }
203
222
  }
223
+ onWalletNotOnDevice(callback) {
224
+ // Replay to late subscribers: if the event already fired, deliver immediately.
225
+ if (this.lastWalletNotOnDevicePayload !== null) {
226
+ const cached = this.lastWalletNotOnDevicePayload;
227
+ Promise.resolve()
228
+ .then(() => callback(cached))
229
+ .catch((err) => logger_1.sdkLogger.warn('[Portal] onWalletNotOnDevice callback failed:', err));
230
+ }
231
+ this.walletNotOnDeviceCallbacks.push(callback);
232
+ return () => {
233
+ this.walletNotOnDeviceCallbacks = this.walletNotOnDeviceCallbacks.filter((cb) => cb !== callback);
234
+ };
235
+ }
236
+ triggerWalletNotOnDevice(payload) {
237
+ this.lastWalletNotOnDevicePayload = payload;
238
+ this.walletNotOnDeviceCallbacks.forEach((callback) => {
239
+ Promise.resolve()
240
+ .then(() => callback(payload))
241
+ .catch((err) => logger_1.sdkLogger.warn('[Portal] onWalletNotOnDevice callback failed:', err));
242
+ });
243
+ }
204
244
  /*****************************
205
245
  * Wallet Methods
206
246
  *****************************/
@@ -211,20 +251,20 @@ class Portal {
211
251
  }
212
252
  createWallet(progress = () => {
213
253
  // Noop
214
- }) {
254
+ }, traceId) {
215
255
  return __awaiter(this, void 0, void 0, function* () {
216
256
  const address = yield this.mpc.generate({
217
257
  host: this.host,
218
258
  mpcVersion: this.mpcVersion,
219
259
  featureFlags: this.featureFlags,
220
- }, progress);
260
+ }, progress, traceId);
221
261
  this.address = address;
222
262
  return address;
223
263
  });
224
264
  }
225
265
  generateBackupShare(progress = () => {
226
266
  // Noop
227
- }) {
267
+ }, traceId) {
228
268
  return __awaiter(this, void 0, void 0, function* () {
229
269
  const response = yield this.mpc.backup({
230
270
  backupMethod: BackupMethods.passkey,
@@ -234,7 +274,7 @@ class Portal {
234
274
  host: this.host,
235
275
  mpcVersion: this.mpcVersion,
236
276
  featureFlags: this.featureFlags,
237
- }, progress);
277
+ }, progress, traceId);
238
278
  if (!response.encryptionKey) {
239
279
  throw new Error('[Portal] Passkey backup did not return an encryption key. Please ensure you are using the latest iframe bundle.');
240
280
  }
@@ -297,17 +337,17 @@ class Portal {
297
337
  }
298
338
  backupWithPasskey(options = {}, progress = () => {
299
339
  // Noop
300
- }) {
340
+ }, traceId) {
301
341
  return __awaiter(this, void 0, void 0, function* () {
302
342
  const { usePopup = true, customDomain, relyingPartyName, backupMethod = BackupMethods.passkey, } = options;
303
343
  if (usePopup) {
304
- yield this.backupWallet(backupMethod, progress, {});
344
+ yield this.backupWallet(backupMethod, progress, {}, traceId);
305
345
  return;
306
346
  }
307
347
  if (backupMethod !== BackupMethods.passkey) {
308
348
  throw new Error(`[Portal] Direct passkey backup currently supports only BackupMethods.passkey (received ${backupMethod}).`);
309
349
  }
310
- const { cipherText, encryptionKey } = yield this.generateBackupShare(progress);
350
+ const { cipherText, encryptionKey } = yield this.generateBackupShare(progress, traceId);
311
351
  yield this.registerPasskeyAndStoreEncryptionKey(cipherText, encryptionKey, {
312
352
  customDomain,
313
353
  relyingPartyName,
@@ -321,7 +361,7 @@ class Portal {
321
361
  }
322
362
  backupWallet(backupMethod, progress = () => {
323
363
  // Noop
324
- }, backupConfigs = {}) {
364
+ }, backupConfigs = {}, traceId) {
325
365
  return __awaiter(this, void 0, void 0, function* () {
326
366
  const response = yield this.mpc.backup({
327
367
  backupMethod,
@@ -329,13 +369,13 @@ class Portal {
329
369
  host: this.host,
330
370
  mpcVersion: this.mpcVersion,
331
371
  featureFlags: this.featureFlags,
332
- }, progress);
372
+ }, progress, traceId);
333
373
  return response;
334
374
  });
335
375
  }
336
376
  recoverWallet(cipherText, backupMethod, backupConfigs = {}, progress = () => {
337
377
  // Noop
338
- }) {
378
+ }, traceId) {
339
379
  return __awaiter(this, void 0, void 0, function* () {
340
380
  const address = yield this.mpc.recover({
341
381
  cipherText,
@@ -344,16 +384,16 @@ class Portal {
344
384
  host: this.host,
345
385
  mpcVersion: this.mpcVersion,
346
386
  featureFlags: this.featureFlags,
347
- }, progress);
387
+ }, progress, traceId);
348
388
  this.address = address;
349
389
  return address;
350
390
  });
351
391
  }
352
392
  provisionWallet(cipherText, backupMethod, backupConfigs, progress = () => {
353
393
  // Noop
354
- }) {
394
+ }, traceId) {
355
395
  return __awaiter(this, void 0, void 0, function* () {
356
- return this.recoverWallet(cipherText, backupMethod, backupConfigs, progress);
396
+ return this.recoverWallet(cipherText, backupMethod, backupConfigs, progress, traceId);
357
397
  });
358
398
  }
359
399
  eject(backupMethod, backupConfigs, orgBackupShare = '', clientBackupCipherText = '') {
@@ -1374,9 +1414,8 @@ class Portal {
1374
1414
  ethereum: 'eip155:1',
1375
1415
  sepolia: 'eip155:11155111',
1376
1416
  base: 'eip155:8453',
1377
- 'base-sepolia': 'eip155:84531',
1417
+ 'base-sepolia': 'eip155:84532',
1378
1418
  polygon: 'eip155:137',
1379
- 'polygon-mumbai': 'eip155:80001',
1380
1419
  'polygon-amoy': 'eip155:80002',
1381
1420
  solana: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
1382
1421
  'solana-devnet': 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
@@ -1444,6 +1483,9 @@ Object.defineProperty(exports, "MpcError", { enumerable: true, get: function ()
1444
1483
  Object.defineProperty(exports, "MpcErrorCodes", { enumerable: true, get: function () { return mpc_2.MpcErrorCodes; } });
1445
1484
  var errors_1 = require("./mpc/errors");
1446
1485
  Object.defineProperty(exports, "PortalMpcError", { enumerable: true, get: function () { return errors_1.PortalMpcError; } });
1486
+ var defaults_2 = require("./shared/rpc/defaults");
1487
+ Object.defineProperty(exports, "buildDefaultRpcConfig", { enumerable: true, get: function () { return defaults_2.buildDefaultRpcConfig; } });
1488
+ Object.defineProperty(exports, "DEFAULT_RPC_HOST", { enumerable: true, get: function () { return defaults_2.DEFAULT_RPC_HOST; } });
1447
1489
  var provider_2 = require("./provider");
1448
1490
  Object.defineProperty(exports, "RequestMethod", { enumerable: true, get: function () { return provider_2.RequestMethod; } });
1449
1491
  var MpcStatuses;
@@ -88,7 +88,7 @@ describe('Portal', () => {
88
88
  host: 'web.portalhq.io',
89
89
  mpcVersion: 'v6',
90
90
  featureFlags: {},
91
- }, mockProgressFn);
91
+ }, mockProgressFn, undefined);
92
92
  }));
93
93
  it('should successfully generate a wallet and call mpc.generate correctly with custom constructor args', () => __awaiter(void 0, void 0, void 0, function* () {
94
94
  portal = new _1.default({
@@ -106,7 +106,13 @@ describe('Portal', () => {
106
106
  host: 'test-host',
107
107
  mpcVersion: 'v6',
108
108
  featureFlags: { isMultiBackupEnabled: true },
109
- }, mockProgressFn);
109
+ }, mockProgressFn, undefined);
110
+ }));
111
+ it('should pass caller-supplied traceId to mpc.generate', () => __awaiter(void 0, void 0, void 0, function* () {
112
+ const mockProgressFn = jest.fn();
113
+ const callerTraceId = 'test-trace-id-create';
114
+ yield portal.createWallet(mockProgressFn, callerTraceId);
115
+ expect(portal.mpc.generate).toHaveBeenCalledWith(expect.any(Object), mockProgressFn, callerTraceId);
110
116
  }));
111
117
  });
112
118
  describe('backupWallet', () => {
@@ -121,7 +127,7 @@ describe('Portal', () => {
121
127
  host: 'web.portalhq.io',
122
128
  mpcVersion: 'v6',
123
129
  featureFlags: {},
124
- }, mockProgressFn);
130
+ }, mockProgressFn, undefined);
125
131
  }));
126
132
  it('should successfully backup a wallet and call mpc.backup correctly with custom constructor args', () => __awaiter(void 0, void 0, void 0, function* () {
127
133
  portal = new _1.default({
@@ -141,7 +147,13 @@ describe('Portal', () => {
141
147
  host: 'test-host',
142
148
  mpcVersion: 'v6',
143
149
  featureFlags: { isMultiBackupEnabled: true },
144
- }, mockProgressFn);
150
+ }, mockProgressFn, undefined);
151
+ }));
152
+ it('should pass caller-supplied traceId to mpc.backup', () => __awaiter(void 0, void 0, void 0, function* () {
153
+ const mockProgressFn = jest.fn();
154
+ const callerTraceId = 'test-trace-id-backup';
155
+ yield portal.backupWallet(_1.BackupMethods.gdrive, mockProgressFn, constants_1.mockBackupConfig, callerTraceId);
156
+ expect(portal.mpc.backup).toHaveBeenCalledWith(expect.any(Object), mockProgressFn, callerTraceId);
145
157
  }));
146
158
  });
147
159
  describe('generateBackupShare', () => {
@@ -165,7 +177,7 @@ describe('Portal', () => {
165
177
  host: 'web.portalhq.io',
166
178
  mpcVersion: 'v6',
167
179
  featureFlags: {},
168
- }, expect.any(Function));
180
+ }, expect.any(Function), undefined);
169
181
  }));
170
182
  it('should throw if the iframe response does not contain an encryption key', () => __awaiter(void 0, void 0, void 0, function* () {
171
183
  ;
@@ -175,6 +187,16 @@ describe('Portal', () => {
175
187
  });
176
188
  yield expect(portal.generateBackupShare()).rejects.toThrow('Passkey backup did not return an encryption key');
177
189
  }));
190
+ it('should pass caller-supplied traceId to mpc.backup', () => __awaiter(void 0, void 0, void 0, function* () {
191
+ const callerTraceId = 'test-trace-id-gen-backup';
192
+ portal.mpc.backup.mockResolvedValueOnce({
193
+ cipherText: constants_1.mockCipherText,
194
+ encryptionKey: 'manual-key',
195
+ storageCallback: jest.fn(),
196
+ });
197
+ yield portal.generateBackupShare(undefined, callerTraceId);
198
+ expect(portal.mpc.backup).toHaveBeenCalledWith(expect.any(Object), expect.any(Function), callerTraceId);
199
+ }));
178
200
  });
179
201
  describe('registerPasskeyAndStoreEncryptionKey', () => {
180
202
  it('should delegate to the passkey service with computed relying party data', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -260,7 +282,7 @@ describe('Portal', () => {
260
282
  host: 'web.portalhq.io',
261
283
  mpcVersion: 'v6',
262
284
  featureFlags: {},
263
- }, progress);
285
+ }, progress, undefined);
264
286
  }));
265
287
  });
266
288
  describe('recoverWallet', () => {
@@ -276,7 +298,7 @@ describe('Portal', () => {
276
298
  host: 'web.portalhq.io',
277
299
  mpcVersion: 'v6',
278
300
  featureFlags: {},
279
- }, mockProgressFn);
301
+ }, mockProgressFn, undefined);
280
302
  }));
281
303
  it('should successfully recover a wallet and call mpc.recover correctly with custom constructor args', () => __awaiter(void 0, void 0, void 0, function* () {
282
304
  portal = new _1.default({
@@ -297,7 +319,13 @@ describe('Portal', () => {
297
319
  host: 'test-host',
298
320
  mpcVersion: 'v6',
299
321
  featureFlags: { isMultiBackupEnabled: true },
300
- }, mockProgressFn);
322
+ }, mockProgressFn, undefined);
323
+ }));
324
+ it('should pass caller-supplied traceId to mpc.recover', () => __awaiter(void 0, void 0, void 0, function* () {
325
+ const mockProgressFn = jest.fn();
326
+ const callerTraceId = 'test-trace-id-recover';
327
+ yield portal.recoverWallet(constants_1.mockCipherText, _1.BackupMethods.password, constants_1.mockBackupConfig, mockProgressFn, callerTraceId);
328
+ expect(portal.mpc.recover).toHaveBeenCalledWith(expect.any(Object), mockProgressFn, callerTraceId);
301
329
  }));
302
330
  });
303
331
  describe('provisionWallet', () => {
@@ -313,7 +341,7 @@ describe('Portal', () => {
313
341
  host: 'web.portalhq.io',
314
342
  mpcVersion: 'v6',
315
343
  featureFlags: {},
316
- }, mockProgressFn);
344
+ }, mockProgressFn, undefined);
317
345
  }));
318
346
  it('should successfully generate a wallet and call mpc.recover correctly with custom constructor args', () => __awaiter(void 0, void 0, void 0, function* () {
319
347
  portal = new _1.default({
@@ -334,7 +362,7 @@ describe('Portal', () => {
334
362
  host: 'test-host',
335
363
  mpcVersion: 'v6',
336
364
  featureFlags: { isMultiBackupEnabled: true },
337
- }, mockProgressFn);
365
+ }, mockProgressFn, undefined);
338
366
  }));
339
367
  });
340
368
  describe('eject', () => {
@@ -1029,4 +1057,17 @@ describe('Portal', () => {
1029
1057
  expect(portal.iframeRpcConfig).toEqual(iframeRpcConfig);
1030
1058
  });
1031
1059
  });
1060
+ describe('onWalletNotOnDevice', () => {
1061
+ it('returns an unsubscribe function that stops the callback from firing', () => {
1062
+ const callback = jest.fn();
1063
+ const unsubscribe = portal.onWalletNotOnDevice(callback);
1064
+ unsubscribe();
1065
+ portal.triggerWalletNotOnDevice({
1066
+ clientId: 'test-client',
1067
+ isBackedUp: false,
1068
+ reason: 'storage_cleared',
1069
+ });
1070
+ expect(callback).not.toHaveBeenCalled();
1071
+ });
1072
+ });
1032
1073
  });
@@ -3,9 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const meld_1 = __importDefault(require("./meld"));
6
7
  const noah_1 = __importDefault(require("./noah"));
7
8
  class Ramps {
8
9
  constructor({ mpc }) {
10
+ this.meld = new meld_1.default({ mpc });
9
11
  this.noah = new noah_1.default({ mpc });
10
12
  }
11
13
  }
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ /**
13
+ * Meld fiat on-ramp API on `portal.ramps.meld`.
14
+ * Each method forwards to the embedded Portal iframe, which calls connect-api.
15
+ */
16
+ class Meld {
17
+ constructor({ mpc }) {
18
+ this.mpc = mpc;
19
+ }
20
+ /** Register a new Meld customer for the authenticated client. */
21
+ createCustomer(data) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ return this.mpc.meldCreateCustomer(data);
24
+ });
25
+ }
26
+ /** Retrieve the Meld customer record for the authenticated client. */
27
+ searchCustomer() {
28
+ return __awaiter(this, void 0, void 0, function* () {
29
+ return this.mpc.meldSearchCustomer();
30
+ });
31
+ }
32
+ /** Get fiat-to-crypto purchase quotes from Meld service providers. */
33
+ getRetailQuote(data) {
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ return this.mpc.meldGetRetailQuote(data);
36
+ });
37
+ }
38
+ /**
39
+ * Create a Meld-hosted retail widget session.
40
+ * The response contains `data.widgetUrl` — open this URL in a new browser tab
41
+ * to let the user complete the purchase or KYC flow on the Meld-hosted page.
42
+ */
43
+ createRetailWidget(data) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ return this.mpc.meldCreateRetailWidget(data);
46
+ });
47
+ }
48
+ /** Search retail transactions for the authenticated client. */
49
+ searchRetailTransactions(data) {
50
+ return __awaiter(this, void 0, void 0, function* () {
51
+ return this.mpc.meldSearchRetailTransactions(data);
52
+ });
53
+ }
54
+ /** Get a single retail transaction by session ID. */
55
+ getRetailTransactionBySession(sessionId) {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ return this.mpc.meldGetRetailTransactionBySession(sessionId);
58
+ });
59
+ }
60
+ /** Get a single retail transaction by transaction ID. */
61
+ getRetailTransaction(id) {
62
+ return __awaiter(this, void 0, void 0, function* () {
63
+ return this.mpc.meldGetRetailTransaction(id);
64
+ });
65
+ }
66
+ /** Discover available Meld service providers. */
67
+ getServiceProviders(params) {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ return this.mpc.meldGetServiceProviders(params);
70
+ });
71
+ }
72
+ /** Discover supported countries. */
73
+ getCountries(params) {
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ return this.mpc.meldGetCountries(params);
76
+ });
77
+ }
78
+ /** Discover supported fiat currencies. */
79
+ getFiatCurrencies(params) {
80
+ return __awaiter(this, void 0, void 0, function* () {
81
+ return this.mpc.meldGetFiatCurrencies(params);
82
+ });
83
+ }
84
+ /** Discover supported crypto currencies. */
85
+ getCryptoCurrencies(params) {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ return this.mpc.meldGetCryptoCurrencies(params);
88
+ });
89
+ }
90
+ /** Discover supported payment methods. */
91
+ getPaymentMethods(params) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ return this.mpc.meldGetPaymentMethods(params);
94
+ });
95
+ }
96
+ /** Discover default currency and payment method settings by country. */
97
+ getDefaults(params) {
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ return this.mpc.meldGetDefaults(params);
100
+ });
101
+ }
102
+ /** Discover fiat currency purchase limits. */
103
+ getBuyLimits(params) {
104
+ return __awaiter(this, void 0, void 0, function* () {
105
+ return this.mpc.meldGetBuyLimits(params);
106
+ });
107
+ }
108
+ /** Discover crypto currency sell limits. */
109
+ getSellLimits(params) {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ return this.mpc.meldGetSellLimits(params);
112
+ });
113
+ }
114
+ /** Discover KYC fiat level limits. */
115
+ getKycLimits(params) {
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ return this.mpc.meldGetKycLimits(params);
118
+ });
119
+ }
120
+ }
121
+ exports.default = Meld;
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const _1 = __importDefault(require("."));
16
+ const mockMeldCreateCustomerResponse = {
17
+ data: { id: 'cust-1', accountId: 'acc-1', externalId: 'ext-1' },
18
+ };
19
+ const mockMeldSearchCustomerResponse = {
20
+ data: { customers: [], count: 0, remaining: 0 },
21
+ };
22
+ const mockMeldGetRetailQuoteResponse = {
23
+ data: { quotes: [] },
24
+ };
25
+ const mockMeldCreateRetailWidgetResponse = {
26
+ data: { id: 'sess-1', token: 'tok-1', customerId: 'cust-1', externalCustomerId: 'ext-1', externalSessionId: 'ses-1', widgetUrl: 'https://widget.meld.io' },
27
+ };
28
+ const mockMeldSearchRetailTransactionsResponse = {
29
+ data: { transactions: [], count: 0, remaining: 0, totalCount: 0 },
30
+ };
31
+ const mockMeldGetRetailTransactionResponse = {
32
+ data: { transaction: { id: 'tx-1', sessionId: 'ses-1', status: 'COMPLETED', transactionType: 'BUY', serviceProvider: 'STRIPE', createdAt: '2024-01-01', updatedAt: '2024-01-01' } },
33
+ };
34
+ const mockMeldDiscoveryResponse = { data: [] };
35
+ const buildMpcMock = () => ({
36
+ meldCreateCustomer: jest.fn().mockResolvedValue(mockMeldCreateCustomerResponse),
37
+ meldSearchCustomer: jest.fn().mockResolvedValue(mockMeldSearchCustomerResponse),
38
+ meldGetRetailQuote: jest.fn().mockResolvedValue(mockMeldGetRetailQuoteResponse),
39
+ meldCreateRetailWidget: jest.fn().mockResolvedValue(mockMeldCreateRetailWidgetResponse),
40
+ meldSearchRetailTransactions: jest.fn().mockResolvedValue(mockMeldSearchRetailTransactionsResponse),
41
+ meldGetRetailTransactionBySession: jest.fn().mockResolvedValue(mockMeldGetRetailTransactionResponse),
42
+ meldGetRetailTransaction: jest.fn().mockResolvedValue(mockMeldGetRetailTransactionResponse),
43
+ meldGetServiceProviders: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
44
+ meldGetCountries: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
45
+ meldGetFiatCurrencies: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
46
+ meldGetCryptoCurrencies: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
47
+ meldGetPaymentMethods: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
48
+ meldGetDefaults: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
49
+ meldGetBuyLimits: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
50
+ meldGetSellLimits: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
51
+ meldGetKycLimits: jest.fn().mockResolvedValue(mockMeldDiscoveryResponse),
52
+ });
53
+ describe('Meld', () => {
54
+ let mpcMock;
55
+ let meld;
56
+ beforeEach(() => {
57
+ mpcMock = buildMpcMock();
58
+ meld = new _1.default({ mpc: mpcMock });
59
+ });
60
+ it('createCustomer delegates to mpc.meldCreateCustomer', () => __awaiter(void 0, void 0, void 0, function* () {
61
+ const req = { email: 'user@example.com', type: 'INDIVIDUAL' };
62
+ const result = yield meld.createCustomer(req);
63
+ expect(mpcMock.meldCreateCustomer).toHaveBeenCalledWith(req);
64
+ expect(result).toEqual(mockMeldCreateCustomerResponse);
65
+ }));
66
+ it('searchCustomer delegates to mpc.meldSearchCustomer', () => __awaiter(void 0, void 0, void 0, function* () {
67
+ const result = yield meld.searchCustomer();
68
+ expect(mpcMock.meldSearchCustomer).toHaveBeenCalled();
69
+ expect(result).toEqual(mockMeldSearchCustomerResponse);
70
+ }));
71
+ it('getRetailQuote delegates to mpc.meldGetRetailQuote', () => __awaiter(void 0, void 0, void 0, function* () {
72
+ const req = { countryCode: 'US', sourceCurrencyCode: 'USD', destinationCurrencyCode: 'ETH', sourceAmount: 100 };
73
+ const result = yield meld.getRetailQuote(req);
74
+ expect(mpcMock.meldGetRetailQuote).toHaveBeenCalledWith(req);
75
+ expect(result).toEqual(mockMeldGetRetailQuoteResponse);
76
+ }));
77
+ it('createRetailWidget delegates to mpc.meldCreateRetailWidget', () => __awaiter(void 0, void 0, void 0, function* () {
78
+ const req = {
79
+ sessionType: 'BUY',
80
+ sessionData: {
81
+ countryCode: 'US',
82
+ serviceProvider: 'STRIPE',
83
+ sourceCurrencyCode: 'USD',
84
+ sourceAmount: '100',
85
+ destinationCurrencyCode: 'ETH',
86
+ },
87
+ };
88
+ const result = yield meld.createRetailWidget(req);
89
+ expect(mpcMock.meldCreateRetailWidget).toHaveBeenCalledWith(req);
90
+ expect(result).toEqual(mockMeldCreateRetailWidgetResponse);
91
+ }));
92
+ it('searchRetailTransactions delegates to mpc.meldSearchRetailTransactions without params', () => __awaiter(void 0, void 0, void 0, function* () {
93
+ const result = yield meld.searchRetailTransactions();
94
+ expect(mpcMock.meldSearchRetailTransactions).toHaveBeenCalledWith(undefined);
95
+ expect(result).toEqual(mockMeldSearchRetailTransactionsResponse);
96
+ }));
97
+ it('searchRetailTransactions delegates with params when provided', () => __awaiter(void 0, void 0, void 0, function* () {
98
+ const params = { status: 'COMPLETED' };
99
+ yield meld.searchRetailTransactions(params);
100
+ expect(mpcMock.meldSearchRetailTransactions).toHaveBeenCalledWith(params);
101
+ }));
102
+ it('getRetailTransactionBySession delegates to mpc.meldGetRetailTransactionBySession', () => __awaiter(void 0, void 0, void 0, function* () {
103
+ const result = yield meld.getRetailTransactionBySession('ses-1');
104
+ expect(mpcMock.meldGetRetailTransactionBySession).toHaveBeenCalledWith('ses-1');
105
+ expect(result).toEqual(mockMeldGetRetailTransactionResponse);
106
+ }));
107
+ it('getRetailTransaction delegates to mpc.meldGetRetailTransaction', () => __awaiter(void 0, void 0, void 0, function* () {
108
+ const result = yield meld.getRetailTransaction('tx-1');
109
+ expect(mpcMock.meldGetRetailTransaction).toHaveBeenCalledWith('tx-1');
110
+ expect(result).toEqual(mockMeldGetRetailTransactionResponse);
111
+ }));
112
+ it('getServiceProviders delegates to mpc.meldGetServiceProviders', () => __awaiter(void 0, void 0, void 0, function* () {
113
+ const params = { countries: 'US' };
114
+ const result = yield meld.getServiceProviders(params);
115
+ expect(mpcMock.meldGetServiceProviders).toHaveBeenCalledWith(params);
116
+ expect(result).toEqual(mockMeldDiscoveryResponse);
117
+ }));
118
+ it('getServiceProviders without params delegates undefined', () => __awaiter(void 0, void 0, void 0, function* () {
119
+ yield meld.getServiceProviders();
120
+ expect(mpcMock.meldGetServiceProviders).toHaveBeenCalledWith(undefined);
121
+ }));
122
+ it('getCountries delegates to mpc.meldGetCountries', () => __awaiter(void 0, void 0, void 0, function* () {
123
+ const result = yield meld.getCountries();
124
+ expect(mpcMock.meldGetCountries).toHaveBeenCalledWith(undefined);
125
+ expect(result).toEqual(mockMeldDiscoveryResponse);
126
+ }));
127
+ it('getFiatCurrencies delegates to mpc.meldGetFiatCurrencies', () => __awaiter(void 0, void 0, void 0, function* () {
128
+ const result = yield meld.getFiatCurrencies();
129
+ expect(mpcMock.meldGetFiatCurrencies).toHaveBeenCalledWith(undefined);
130
+ expect(result).toEqual(mockMeldDiscoveryResponse);
131
+ }));
132
+ it('getCryptoCurrencies delegates to mpc.meldGetCryptoCurrencies', () => __awaiter(void 0, void 0, void 0, function* () {
133
+ const result = yield meld.getCryptoCurrencies();
134
+ expect(mpcMock.meldGetCryptoCurrencies).toHaveBeenCalledWith(undefined);
135
+ expect(result).toEqual(mockMeldDiscoveryResponse);
136
+ }));
137
+ it('getPaymentMethods delegates to mpc.meldGetPaymentMethods', () => __awaiter(void 0, void 0, void 0, function* () {
138
+ const result = yield meld.getPaymentMethods();
139
+ expect(mpcMock.meldGetPaymentMethods).toHaveBeenCalledWith(undefined);
140
+ expect(result).toEqual(mockMeldDiscoveryResponse);
141
+ }));
142
+ it('getDefaults delegates to mpc.meldGetDefaults', () => __awaiter(void 0, void 0, void 0, function* () {
143
+ const result = yield meld.getDefaults();
144
+ expect(mpcMock.meldGetDefaults).toHaveBeenCalledWith(undefined);
145
+ expect(result).toEqual(mockMeldDiscoveryResponse);
146
+ }));
147
+ it('getBuyLimits delegates to mpc.meldGetBuyLimits', () => __awaiter(void 0, void 0, void 0, function* () {
148
+ const result = yield meld.getBuyLimits();
149
+ expect(mpcMock.meldGetBuyLimits).toHaveBeenCalledWith(undefined);
150
+ expect(result).toEqual(mockMeldDiscoveryResponse);
151
+ }));
152
+ it('getSellLimits delegates to mpc.meldGetSellLimits', () => __awaiter(void 0, void 0, void 0, function* () {
153
+ const result = yield meld.getSellLimits();
154
+ expect(mpcMock.meldGetSellLimits).toHaveBeenCalledWith(undefined);
155
+ expect(result).toEqual(mockMeldDiscoveryResponse);
156
+ }));
157
+ it('getKycLimits delegates to mpc.meldGetKycLimits', () => __awaiter(void 0, void 0, void 0, function* () {
158
+ const result = yield meld.getKycLimits();
159
+ expect(mpcMock.meldGetKycLimits).toHaveBeenCalledWith(undefined);
160
+ expect(result).toEqual(mockMeldDiscoveryResponse);
161
+ }));
162
+ });
@@ -66,9 +66,9 @@ class Noah {
66
66
  });
67
67
  }
68
68
  /** Pay-in payment methods available to the customer. */
69
- getPaymentMethods() {
69
+ getPaymentMethods(data) {
70
70
  return __awaiter(this, void 0, void 0, function* () {
71
- return this.mpc.getPaymentMethods();
71
+ return this.mpc.getPaymentMethods(data);
72
72
  });
73
73
  }
74
74
  }