@portal-hq/web 3.17.0 → 3.18.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 +1 -1
  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
@@ -3,6 +3,65 @@ export interface PortalApiSuccessEnvelope<T> {
3
3
  metadata?: Record<string, unknown>
4
4
  }
5
5
 
6
+ // -- Shared enums & primitives --
7
+
8
+ export type NoahPaymentMethodType =
9
+ | 'BankAch'
10
+ | 'BankFedNow'
11
+ | 'BankFedwire'
12
+ | 'BankLocal'
13
+ | 'BankSepa'
14
+ | 'BankSortCode'
15
+ | 'BankSwift'
16
+ | 'IdentifierAlias'
17
+ | 'IdentifierMerchantID'
18
+ | 'IdentifierMobileMoney'
19
+ | 'IdentifierPix'
20
+ | 'IdentifierQr'
21
+ | 'TokenizedCard'
22
+
23
+ export type FiatPaymentMethodCategory = 'Bank' | 'Card' | 'Identifier'
24
+
25
+ export type FiatPaymentMethodDisplayType =
26
+ | 'FiatPaymentMethodBankDisplay'
27
+ | 'FiatPaymentMethodCardDisplay'
28
+ | 'FiatPaymentMethodIdentifierDisplay'
29
+
30
+ export type NoahComparisonOperator = 'EQ' | 'LTEQ' | 'GTEQ'
31
+
32
+ export type NoahProcessingTier = 'Standard' | 'Priority'
33
+
34
+ export type TransactionBreakdownType = 'ChannelFee' | 'BusinessFee' | 'Remaining'
35
+
36
+ export interface TransactionBreakdownItem {
37
+ type: TransactionBreakdownType
38
+ amount: string
39
+ }
40
+
41
+ export interface NoahSellQuote {
42
+ signedQuote: string
43
+ expiry: string
44
+ }
45
+
46
+ /** Richer fee object returned by payin and related payment method responses. */
47
+ export interface NoahFeeDetails {
48
+ fiatCurrencyCode: string
49
+ totalFeePct: string
50
+ totalFeeBase: string
51
+ totalFeeMin: string
52
+ }
53
+
54
+ /** Optional business-level fee configuration passed on payout/quote/payin requests. */
55
+ export interface NoahBusinessFee {
56
+ feeBase?: string
57
+ feePct?: string
58
+ fiatCurrency?: string
59
+ }
60
+
61
+ export type BusinessFeesInput = Record<string, NoahBusinessFee>
62
+
63
+ // -- KYC --
64
+
6
65
  export interface NoahInitiateKycRequest {
7
66
  returnUrl: string
8
67
  fiatOptions?: { fiatCurrencyCode: string }[]
@@ -18,26 +77,30 @@ export interface NoahInitiateKycResponseData {
18
77
  export type NoahInitiateKycResponse =
19
78
  PortalApiSuccessEnvelope<NoahInitiateKycResponseData>
20
79
 
80
+ // -- Payin --
81
+
21
82
  export interface NoahInitiatePayinRequest {
22
83
  fiatCurrency: string
23
84
  cryptoCurrency: string
24
85
  network: string
25
86
  destinationAddress: string
87
+ businessFees?: BusinessFeesInput
26
88
  }
27
89
 
28
- export interface StreetAddress {
29
- city?: string
30
- country?: string
31
- line1?: string
32
- line2?: string
33
- postalCode?: string
34
- state?: string
90
+ export interface NoahBankAddress {
91
+ street: string
92
+ street2?: string
93
+ city: string
94
+ postCode: string
95
+ state: string
96
+ country: string
35
97
  }
36
98
 
37
99
  export interface BankToAddressRelatedPaymentMethod {
38
100
  paymentMethodId: string
39
- paymentMethodType: string
40
- details?: {
101
+ paymentMethodType: NoahPaymentMethodType
102
+ fee: NoahFeeDetails
103
+ details: {
41
104
  accountNumber?: string
42
105
  bankCode?: string
43
106
  }
@@ -45,19 +108,23 @@ export interface BankToAddressRelatedPaymentMethod {
45
108
 
46
109
  export interface BankDetails {
47
110
  paymentMethodId: string
48
- paymentMethodType: string
111
+ paymentMethodType: NoahPaymentMethodType
49
112
  accountNumber: string
113
+ cryptoCurrency: string
50
114
  network: string
115
+ fee: NoahFeeDetails
51
116
  accountHolderName?: string
52
117
  bankCode?: string
53
118
  bankName?: string
54
- bankAddress?: StreetAddress
119
+ bankAddress?: NoahBankAddress
55
120
  reference?: string
56
121
  relatedPaymentMethods?: BankToAddressRelatedPaymentMethod[]
57
122
  }
58
123
 
59
124
  export interface NoahInitiatePayinResponseData {
60
125
  payinId: string
126
+ cryptoCurrency: string
127
+ fee: NoahFeeDetails
61
128
  bankDetails: BankDetails
62
129
  }
63
130
 
@@ -72,11 +139,14 @@ export interface NoahSimulatePayinRequest {
72
139
 
73
140
  export interface NoahSimulatePayinResponseData {
74
141
  fiatDepositId: string
142
+ reference?: string
75
143
  }
76
144
 
77
145
  export type NoahSimulatePayinResponse =
78
146
  PortalApiSuccessEnvelope<NoahSimulatePayinResponseData>
79
147
 
148
+ // -- Payout countries --
149
+
80
150
  export interface NoahGetPayoutCountriesResponseData {
81
151
  countries: Record<string, string[]>
82
152
  }
@@ -84,11 +154,16 @@ export interface NoahGetPayoutCountriesResponseData {
84
154
  export type NoahGetPayoutCountriesResponse =
85
155
  PortalApiSuccessEnvelope<NoahGetPayoutCountriesResponseData>
86
156
 
157
+ // -- Payout channels --
158
+
87
159
  export interface NoahGetPayoutChannelsRequest {
88
- country: string
89
160
  cryptoCurrency: string
90
- fiatCurrency: string
161
+ country?: string
162
+ fiatCurrency?: string
91
163
  fiatAmount?: string
164
+ paymentMethodId?: string
165
+ pageSize?: number
166
+ pageToken?: string
92
167
  }
93
168
 
94
169
  export interface ChannelLimits {
@@ -102,13 +177,17 @@ export interface ChannelCalculated {
102
177
 
103
178
  export interface Channel {
104
179
  id: string
105
- paymentMethodType: string
180
+ paymentMethodCategory: FiatPaymentMethodCategory
181
+ paymentMethodType: NoahPaymentMethodType
106
182
  fiatCurrency: string
107
183
  country: string
108
184
  limits: ChannelLimits
109
185
  rate: string
110
186
  processingSeconds: number
111
187
  calculated?: ChannelCalculated
188
+ processingTier?: NoahProcessingTier
189
+ issuer?: string
190
+ paymentMethods?: PaymentMethodDisplay[]
112
191
  }
113
192
 
114
193
  export interface NoahGetPayoutChannelsResponseData {
@@ -129,38 +208,61 @@ export interface NoahGetPayoutChannelFormResponseData {
129
208
  export type NoahGetPayoutChannelFormResponse =
130
209
  PortalApiSuccessEnvelope<NoahGetPayoutChannelFormResponseData>
131
210
 
211
+ // -- Payment methods --
212
+
132
213
  export interface PaymentMethodDetails {
133
- type: string
214
+ type: FiatPaymentMethodDisplayType
134
215
  accountNumber?: string
135
216
  bankCode?: string
217
+ routingNumber?: string
218
+ swiftCode?: string
219
+ bankingSystems?: string[]
220
+ bankName?: string
221
+ bankAddress?: NoahBankAddress
136
222
  last4?: string
137
223
  scheme?: string
138
224
  identifierType?: string
139
225
  identifier?: string
140
226
  }
227
+ export interface PaymentMethodDisplay {
228
+ id: string
229
+ paymentMethodType: NoahPaymentMethodType
230
+ details: PaymentMethodDetails
231
+ accountHolderDetails?: AccountHolderDetails
232
+ issuerDetails?: IssuerDetails
233
+ }
234
+
235
+ /** Name components for the account holder. */
236
+ export interface AccountHolderName {
237
+ firstName: string
238
+ lastName: string
239
+ middleName?: string
240
+ }
141
241
 
142
242
  export interface AccountHolderDetails {
143
- name?: string
144
- taxId?: string
145
- email?: string
146
- phoneNumber?: string
147
- address?: StreetAddress
243
+ /** Structured name; use firstName/lastName rather than a flat string. */
244
+ name?: AccountHolderName
148
245
  }
149
246
 
150
247
  export interface IssuerDetails {
151
248
  name?: string
152
- bankCode?: string
153
- bankName?: string
154
249
  }
155
250
 
156
251
  export interface PaymentMethod {
157
252
  id: string
158
- paymentMethodType: string
253
+ paymentMethodCategory: FiatPaymentMethodCategory
254
+ paymentMethodType: NoahPaymentMethodType
159
255
  details: PaymentMethodDetails
160
256
  accountHolderDetails?: AccountHolderDetails
161
257
  issuerDetails?: IssuerDetails
162
258
  }
163
259
 
260
+ export interface NoahGetPaymentMethodsRequest {
261
+ pageSize?: number
262
+ pageToken?: string
263
+ capability?: 'PayoutFrom' | 'PayinTo' | 'PayoutTo'
264
+ }
265
+
164
266
  export interface NoahGetPaymentMethodsResponseData {
165
267
  paymentMethods: PaymentMethod[]
166
268
  pageToken?: string
@@ -169,17 +271,29 @@ export interface NoahGetPaymentMethodsResponseData {
169
271
  export type NoahGetPaymentMethodsResponse =
170
272
  PortalApiSuccessEnvelope<NoahGetPaymentMethodsResponseData>
171
273
 
172
- export interface NoahGetPayoutQuoteRequest {
274
+ // -- Payout quote --
275
+ export interface BasePayoutQuoteRequest {
173
276
  channelId: string
174
277
  cryptoCurrency: string
175
- fiatAmount: string
176
278
  form?: Record<string, unknown>
177
279
  fiatCurrency?: string
178
280
  paymentMethodId?: string
281
+ quoted?: boolean
282
+ businessFee?: NoahBusinessFee
179
283
  }
180
284
 
285
+ export type NoahGetPayoutQuoteRequest =
286
+ | (BasePayoutQuoteRequest & {
287
+ fiatAmount: string
288
+ cryptoAmount?: never
289
+ })
290
+ | (BasePayoutQuoteRequest & {
291
+ cryptoAmount: string
292
+ fiatAmount?: never
293
+ })
294
+
181
295
  export interface FormNextStep {
182
- stepId: string
296
+ stepId: 'Vop' | 'Cob' | 'PaymentDetails'
183
297
  stepType: 'Ack' | 'DataEntry'
184
298
  schema: Record<string, unknown>
185
299
  }
@@ -188,15 +302,27 @@ export interface NoahGetPayoutQuoteResponseData {
188
302
  payoutId: string
189
303
  totalFee: string
190
304
  cryptoAmountEstimate: string
305
+ cryptoAuthorizedAmount: string
191
306
  formSessionId: string
307
+ rate?: string
308
+ breakdown?: TransactionBreakdownItem[]
309
+ quote?: NoahSellQuote
310
+ /** Disclosure: crypto currency for this quote. */
311
+ cryptoCurrency?: string
312
+ /** Disclosure: fiat currency for this quote. */
313
+ fiatCurrency?: string
314
+ /** Disclosure: fiat amount for this quote. */
315
+ fiatAmount?: string
192
316
  nextStep?: FormNextStep
193
317
  }
194
318
 
195
319
  export type NoahGetPayoutQuoteResponse =
196
320
  PortalApiSuccessEnvelope<NoahGetPayoutQuoteResponseData>
197
321
 
322
+ // -- Payout trigger & initiation --
323
+
198
324
  export interface AmountCondition {
199
- comparisonOperator: string
325
+ comparisonOperator: NoahComparisonOperator
200
326
  value: string
201
327
  }
202
328
 
@@ -204,6 +330,7 @@ export interface DepositSourceTriggerCondition {
204
330
  amountConditions: AmountCondition[]
205
331
  cryptoCurrency: string
206
332
  network: string
333
+ /** Runtime value is always a plain string, not an object. */
207
334
  destinationAddress: string
208
335
  }
209
336
 
@@ -212,10 +339,37 @@ export type NoahSolanaCaipId =
212
339
  | 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1'
213
340
  | 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
214
341
 
342
+ export type NoahOnchainDepositSourceTriggerType =
343
+ | 'SingleOnchainDepositSourceTriggerInput'
344
+ | 'PermanentOnchainDepositSourceTriggerInput'
345
+ | 'QuotedOnchainDepositSourceTriggerInput'
346
+
215
347
  export interface NoahSingleOnchainDepositSourceTriggerInput {
216
348
  Type: 'SingleOnchainDepositSourceTriggerInput'
217
349
  Conditions: {
218
- AmountConditions: { ComparisonOperator: string; Value: string }[]
350
+ AmountConditions: { ComparisonOperator: NoahComparisonOperator; Value: string }[]
351
+ Network: string
352
+ }[]
353
+ SourceAddress: string
354
+ Expiry: string
355
+ Nonce: string
356
+ }
357
+
358
+ export interface NoahPermanentOnchainDepositSourceTriggerInput {
359
+ Type: 'PermanentOnchainDepositSourceTriggerInput'
360
+ Conditions: {
361
+ AmountConditions: { ComparisonOperator: NoahComparisonOperator; Value: string }[]
362
+ Network: string
363
+ }[]
364
+ SourceAddress: string
365
+ Expiry: string
366
+ Nonce: string
367
+ }
368
+
369
+ export interface NoahQuotedOnchainDepositSourceTriggerInput {
370
+ Type: 'QuotedOnchainDepositSourceTriggerInput'
371
+ Conditions: {
372
+ AmountConditions: { ComparisonOperator: NoahComparisonOperator; Value: string }[]
219
373
  Network: string
220
374
  }[]
221
375
  SourceAddress: string
@@ -223,18 +377,26 @@ export interface NoahSingleOnchainDepositSourceTriggerInput {
223
377
  Nonce: string
224
378
  }
225
379
 
380
+ export type NoahOnchainDepositSourceTriggerInput =
381
+ | NoahSingleOnchainDepositSourceTriggerInput
382
+ | NoahPermanentOnchainDepositSourceTriggerInput
383
+ | NoahQuotedOnchainDepositSourceTriggerInput
384
+
226
385
  export interface NoahInitiatePayoutRequest {
227
386
  payoutId: string
228
387
  sourceAddress: string
229
388
  expiry: string
230
389
  nonce: string
231
390
  network: string
232
- trigger?: NoahSingleOnchainDepositSourceTriggerInput
391
+ businessFee?: NoahBusinessFee
392
+ fiatAmount?: string
393
+ trigger?: NoahOnchainDepositSourceTriggerInput
233
394
  }
234
395
 
235
396
  export interface NoahInitiatePayoutResponseData {
236
397
  destinationAddress: string | null
237
398
  conditions: DepositSourceTriggerCondition[]
399
+ ruleId?: string
238
400
  }
239
401
 
240
402
  export type NoahInitiatePayoutResponse =
package/types.d.ts CHANGED
@@ -173,6 +173,12 @@ export interface ClientResponse {
173
173
  wallets: ClientResponseWallet[]
174
174
  }
175
175
 
176
+ export interface WalletNotOnDevicePayload {
177
+ clientId: string
178
+ isBackedUp: boolean
179
+ reason: 'storage_cleared'
180
+ }
181
+
176
182
  interface ClientResponseCustodian {
177
183
  id: string
178
184
  name: string
@@ -269,8 +275,15 @@ export interface PortalApiOptions {
269
275
  }
270
276
 
271
277
  export interface PortalOptions {
272
- // Required options
273
- rpcConfig: RpcConfig
278
+ /**
279
+ * RPC endpoint map (CAIP-2 chainId → URL).
280
+ *
281
+ * Optional — when omitted or set to `{}`, the SDK auto-generates a default
282
+ * gateway config pointing at `https://{gatewayHost}/rpc/v1/{namespace}/{reference}`
283
+ * for 13 built-in chains. An explicit non-empty map is used verbatim (no
284
+ * merging with defaults).
285
+ */
286
+ rpcConfig?: RpcConfig
274
287
 
275
288
  /**
276
289
  * Optional RPC map for iframe-proxied reads only (receipt / Solana polling). Parent signing still
@@ -278,13 +291,62 @@ export interface PortalOptions {
278
291
  */
279
292
  iframeRpcConfig?: RpcConfig
280
293
 
281
- // Optional options
294
+ /**
295
+ * Portal RPC gateway hostname used to construct default RPC URLs when no `rpcConfig`
296
+ * is supplied.
297
+ *
298
+ * Resolution order:
299
+ * 1. This field (explicit override)
300
+ * 2. `host` (derived — Portal deploys both on the same hostname per environment)
301
+ * 3. `'web.portalhq.io'` (compile-time production default)
302
+ *
303
+ * Set this only when the RPC gateway lives on a different hostname than `host`
304
+ * (e.g. a custom CDN). For standard Portal environments, setting `host` is
305
+ * sufficient — the gateway follows automatically.
306
+ *
307
+ * @example
308
+ * // Staging: set host only; gatewayHost derives automatically.
309
+ * new Portal({ host: 'web.portalhq.dev', apiKey: '...' })
310
+ *
311
+ * // Custom CDN: set both independently.
312
+ * new Portal({ host: 'web.portalhq.io', gatewayHost: 'rpc.my-infra.com', apiKey: '...' })
313
+ */
314
+ gatewayHost?: string
315
+
316
+ /**
317
+ * Persistent client-level credential (`ClientApiKey` in the backend).
318
+ *
319
+ * - No expiry. Issued once per client wallet.
320
+ * - Accepted on the Portal RPC gateway (`/rpc`) and on internal service routes (`/service`).
321
+ * - Takes precedence over `authToken` when both are supplied.
322
+ *
323
+ * Use this for server-side or long-lived integrations.
324
+ */
282
325
  apiKey?: string
326
+
327
+ /**
328
+ * Short-lived session credential (`ClientSessionToken` in the backend).
329
+ *
330
+ * - Has an expiry (`expiresAt`) and a maximum refresh window (`maxRefreshAt`).
331
+ * - Accepted on the Portal RPC gateway (`/rpc`). Not accepted on `/service` (MPC layer).
332
+ * - Used as a fallback when `apiKey` is not set.
333
+ *
334
+ * Use this for client-side, user-session-scoped integrations (e.g. browser dApps).
335
+ */
283
336
  authToken?: string
284
337
  authUrl?: string
285
338
  autoApprove?: boolean
286
339
  gdrive?: GDriveConfig
287
340
  passkey?: PasskeyConfig
341
+ /**
342
+ * Portal API + RPC gateway hostname. Defines the environment identity.
343
+ *
344
+ * - Production: `'web.portalhq.io'` (default)
345
+ * - Staging: `'web.portalhq.dev'`
346
+ *
347
+ * When `gatewayHost` is not explicitly set, the RPC gateway derives from this
348
+ * value, ensuring both services stay on the same environment.
349
+ */
288
350
  host?: string
289
351
  mpcHost?: string
290
352
  keychain?: KeychainAdapter