@portal-hq/web 2.0.1 → 3.0.1

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.
package/src/mpc/index.ts CHANGED
@@ -4,7 +4,6 @@ import Portal, { BackupMethods, GetTransactionsOrder } from '../index'
4
4
  import type {
5
5
  BackupArgs,
6
6
  BackupResponse,
7
- BackupSharePairMetadata,
8
7
  Balance,
9
8
  ClientResponse,
10
9
  EjectArgs,
@@ -19,14 +18,15 @@ import type {
19
18
  QuoteResponse,
20
19
  RecoverArgs,
21
20
  SignArgs,
22
- SigningSharePairMetadata,
23
21
  SimulateTransactionParam,
24
22
  SimulatedTransaction,
25
23
  Transaction,
24
+ SharesOnDeviceResponse,
26
25
  WorkerResult,
26
+ EjectResult,
27
27
  } from '../../types'
28
28
 
29
- const WEB_SDK_VERSION = '2.0.1'
29
+ const WEB_SDK_VERSION = '3.0.1'
30
30
 
31
31
  class Mpc {
32
32
  public iframe?: HTMLIFrameElement
@@ -87,7 +87,7 @@ class Mpc {
87
87
  const storageCallback = async () => {
88
88
  await this.setBackupStatus(
89
89
  'STORED_CLIENT_BACKUP_SHARE',
90
- result.backupIds,
90
+ result.backupIds as string[],
91
91
  )
92
92
  }
93
93
 
@@ -306,10 +306,10 @@ class Mpc {
306
306
  })
307
307
  }
308
308
 
309
- public async eject(data: EjectArgs): Promise<string> {
309
+ public async eject(data: EjectArgs): Promise<EjectResult> {
310
310
  return new Promise((resolve, reject) => {
311
311
  const handleEject = (message: MessageEvent<WorkerResult>) => {
312
- const { type, data } = message.data
312
+ const { type, data: result } = message.data
313
313
  const { origin } = message
314
314
 
315
315
  // ignore any broadcast postMessages
@@ -321,12 +321,12 @@ class Mpc {
321
321
  // Remove the event listeners
322
322
  window.removeEventListener('message', handleEject)
323
323
 
324
- reject(new PortalMpcError(data as PortalError))
324
+ reject(new PortalMpcError(result as PortalError))
325
325
  } else if (type === 'portal:wasm:ejectResult') {
326
326
  // Remove the event listeners
327
327
  window.removeEventListener('message', handleEject)
328
328
 
329
- resolve(data as string)
329
+ resolve(result as EjectResult)
330
330
  }
331
331
  }
332
332
 
@@ -345,11 +345,6 @@ class Mpc {
345
345
  // Noop
346
346
  },
347
347
  ): Promise<string> {
348
- const message = {
349
- type: 'portal:wasm:sign',
350
- data,
351
- }
352
-
353
348
  return new Promise((resolve, reject) => {
354
349
  // Create a function to be bound when sign is triggered
355
350
  const handleSign = (event: MessageEvent<WorkerResult>) => {
@@ -397,7 +392,47 @@ class Mpc {
397
392
  window.addEventListener('message', handleProgress)
398
393
 
399
394
  // Send the request to the iframe
400
- this.postMessage(message)
395
+ this.postMessage({
396
+ type: 'portal:wasm:sign',
397
+ data,
398
+ })
399
+ })
400
+ }
401
+
402
+ public async checkSharesOnDevice(): Promise<SharesOnDeviceResponse> {
403
+ return new Promise((resolve, reject) => {
404
+ const handleCheckSharesOnDevice = (event: MessageEvent<WorkerResult>) => {
405
+ const { type, data } = event.data
406
+ const { origin } = event
407
+
408
+ // ignore any broadcast postMessages
409
+ if (origin !== this.getOrigin()) {
410
+ return
411
+ }
412
+
413
+ if (type === 'portal:checkSharesOnDeviceError') {
414
+ // Remove the event listener
415
+ window.removeEventListener('message', handleCheckSharesOnDevice)
416
+
417
+ // Reject the promise with the error
418
+ return reject(new PortalMpcError(data as PortalError))
419
+ } else if (type === 'portal:checkSharesOnDeviceResult') {
420
+ // Remove the event listener
421
+ window.removeEventListener('message', handleCheckSharesOnDevice)
422
+
423
+ // Resolve the promise with the result
424
+ resolve(data as SharesOnDeviceResponse)
425
+ }
426
+ }
427
+
428
+ // Bind the function to the message event
429
+ window.addEventListener('message', handleCheckSharesOnDevice)
430
+
431
+ // Send the request to the iframe
432
+ this.postMessage({
433
+ type: 'portal:checkSharesOnDevice',
434
+ data: {},
435
+ })
401
436
  })
402
437
  }
403
438
 
@@ -519,7 +554,7 @@ class Mpc {
519
554
  public async getQuote(
520
555
  apiKey: string,
521
556
  args: QuoteArgs,
522
- chainId?: string,
557
+ chainId: string,
523
558
  ): Promise<QuoteResponse> {
524
559
  return new Promise((resolve, reject) => {
525
560
  const handleGetQuote = (event: MessageEvent<WorkerResult>) => {
@@ -562,7 +597,7 @@ class Mpc {
562
597
 
563
598
  public async getSources(
564
599
  apiKey: string,
565
- chainId?: string,
600
+ chainId: string,
566
601
  ): Promise<Record<string, string>> {
567
602
  return new Promise((resolve, reject) => {
568
603
  const handleGetSources = (event: MessageEvent<WorkerResult>) => {
@@ -603,10 +638,10 @@ class Mpc {
603
638
  }
604
639
 
605
640
  public async getTransactions(
641
+ chainId: string,
606
642
  limit?: number,
607
643
  offset?: number,
608
644
  order?: GetTransactionsOrder,
609
- chainId?: string,
610
645
  ): Promise<Transaction[]> {
611
646
  return new Promise((resolve, reject) => {
612
647
  const handleGetTransactions = (event: MessageEvent<WorkerResult>) => {
@@ -640,10 +675,10 @@ class Mpc {
640
675
  this.postMessage({
641
676
  type: 'portal:getTransactions',
642
677
  data: {
678
+ chainId,
643
679
  limit,
644
680
  offset,
645
681
  order,
646
- chainId,
647
682
  },
648
683
  })
649
684
  })
@@ -674,7 +709,7 @@ class Mpc {
674
709
  window.removeEventListener('message', handleSetBackupStatus)
675
710
 
676
711
  // Resolve the promise with the result
677
- return resolve(data)
712
+ return resolve(data as boolean)
678
713
  }
679
714
  }
680
715
 
@@ -779,84 +814,6 @@ class Mpc {
779
814
  })
780
815
  }
781
816
 
782
- public async getBackupShareMetadata(): Promise<BackupSharePairMetadata[]> {
783
- return new Promise((resolve, reject) => {
784
- const handleGetBackupShareMetadata = (
785
- event: MessageEvent<WorkerResult>,
786
- ) => {
787
- const { type, data } = event.data
788
- const { origin } = event
789
-
790
- // ignore any broadcast postMessages
791
- if (origin !== this.getOrigin()) {
792
- return
793
- }
794
-
795
- if (type === 'portal:getBackupShareMetadataError') {
796
- // Remove the event listener
797
- window.removeEventListener('message', handleGetBackupShareMetadata)
798
-
799
- // Reject the promise with the error
800
- return reject(new PortalMpcError(data as PortalError))
801
- } else if (type === 'portal:getBackupShareMetadataResult') {
802
- // Remove the event listener
803
- window.removeEventListener('message', handleGetBackupShareMetadata)
804
-
805
- // Resolve the promise with the result
806
- resolve(data as BackupSharePairMetadata[])
807
- }
808
- }
809
-
810
- // Bind the function to the message event
811
- window.addEventListener('message', handleGetBackupShareMetadata)
812
-
813
- // Send the request to the iframe
814
- this.postMessage({
815
- type: 'portal:getBackupShareMetadata',
816
- data: {},
817
- })
818
- })
819
- }
820
-
821
- public async getSigningShareMetadata(): Promise<SigningSharePairMetadata[]> {
822
- return new Promise((resolve, reject) => {
823
- const handleGetSigningShareMetadata = (
824
- event: MessageEvent<WorkerResult>,
825
- ) => {
826
- const { type, data } = event.data
827
- const { origin } = event
828
-
829
- // ignore any broadcast postMessages
830
- if (origin !== this.getOrigin()) {
831
- return
832
- }
833
-
834
- if (type === 'portal:getSigningShareMetadataError') {
835
- // Remove the event listener
836
- window.removeEventListener('message', handleGetSigningShareMetadata)
837
-
838
- // Reject the promise with the error
839
- return reject(new PortalMpcError(data as PortalError))
840
- } else if (type === 'portal:getSigningShareMetadataResult') {
841
- // Remove the event listener
842
- window.removeEventListener('message', handleGetSigningShareMetadata)
843
-
844
- // Resolve the promise with the result
845
- resolve(data as SigningSharePairMetadata[])
846
- }
847
- }
848
-
849
- // Bind the function to the message event
850
- window.addEventListener('message', handleGetSigningShareMetadata)
851
-
852
- // Send the request to the iframe
853
- this.postMessage({
854
- type: 'portal:getSigningShareMetadata',
855
- data: {},
856
- })
857
- })
858
- }
859
-
860
817
  /***************************
861
818
  * Private Methods
862
819
  ***************************/
@@ -887,11 +844,9 @@ class Mpc {
887
844
  authToken: this.portal.authToken,
888
845
  authUrl: this.portal.authUrl,
889
846
  autoApprove: this.portal.autoApprove,
890
- chainId: this.portal.chainId,
891
847
  gdrive: this.portal.gDriveConfig,
892
848
  passkey: this.portal.passkeyConfig,
893
849
  host: this.portal.host,
894
- rpcUrl: this.portal.getRpcUrl(),
895
850
  mpcHost: this.portal.mpcHost,
896
851
  mpcVersion: this.portal.mpcVersion,
897
852
  featureFlags: this.portal.featureFlags,
@@ -26,6 +26,10 @@ const signerMethods = [
26
26
  'eth_signTypedData_v3',
27
27
  'eth_signTypedData_v4',
28
28
  'personal_sign',
29
+ 'sol_signAndConfirmTransaction',
30
+ 'sol_signAndSendTransaction',
31
+ 'sol_signMessage',
32
+ 'sol_signTransaction',
29
33
  ]
30
34
 
31
35
  class Provider {
@@ -118,10 +122,6 @@ class Provider {
118
122
  method,
119
123
  params,
120
124
  }: RequestArguments): Promise<any> {
121
- if (method === 'eth_chainId') {
122
- return chainId ? parseInt(chainId.split(':')[1]) : this.portal.chainId
123
- }
124
-
125
125
  const isSignerMethod = signerMethods.includes(method)
126
126
 
127
127
  if (!isSignerMethod && !method.startsWith('wallet_')) {
@@ -264,16 +264,22 @@ class Provider {
264
264
  method,
265
265
  params,
266
266
  }: RequestArguments): Promise<any> {
267
- // Pass request off to the gateway
268
- const result = await fetch(this.portal.getRpcUrl(), {
267
+ // Prepare the request body
268
+ const requestBody = {
269
269
  body: JSON.stringify({
270
270
  jsonrpc: '2.0',
271
- id: chainId ? parseInt(chainId.split(':')[1]) : this.portal.chainId,
271
+ id: '0',
272
272
  method,
273
273
  params,
274
274
  }),
275
275
  method: 'POST',
276
- })
276
+ headers: {
277
+ 'Content-Type': 'application/json',
278
+ },
279
+ }
280
+
281
+ // Pass request off to the gateway
282
+ const result = await fetch(this.portal.getRpcUrl(chainId), requestBody)
277
283
 
278
284
  return result.json()
279
285
  }
@@ -301,26 +307,42 @@ class Provider {
301
307
  }
302
308
 
303
309
  switch (method) {
304
- case 'eth_chainId':
305
- return `0x${this.portal.chainId.toString(16)}`
310
+ case 'eth_chainId': {
311
+ this.enforceEip155ChainId(chainId)
312
+ const chainReferenceId = parseInt((chainId as string).split(':')[1])
313
+ return `0x${chainReferenceId.toString(16)}`
314
+ }
306
315
  case 'eth_accounts':
307
- case 'eth_requestAccounts':
316
+ case 'eth_requestAccounts': {
317
+ this.enforceEip155ChainId(chainId)
308
318
  return [this.portal.address]
319
+ }
309
320
  case 'eth_sendTransaction':
310
321
  case 'eth_sign':
311
322
  case 'eth_signTransaction':
312
323
  case 'eth_signTypedData_v3':
313
324
  case 'eth_signTypedData_v4':
314
325
  case 'personal_sign': {
326
+ this.enforceEip155ChainId(chainId)
315
327
  const result = await this.portal.mpc.sign({
316
- chainId: chainId
317
- ? chainId.split(':')[1]
318
- : this.portal.chainId.toString(),
328
+ chainId: chainId as string,
319
329
  method,
320
330
  params: this.buildParams(method, params),
321
- rpcUrl: this.portal.getRpcUrl(),
331
+ rpcUrl: this.portal.getRpcUrl(chainId),
332
+ })
333
+ return result
334
+ }
335
+ case 'sol_signAndConfirmTransaction':
336
+ case 'sol_signAndSendTransaction':
337
+ case 'sol_signMessage':
338
+ case 'sol_signTransaction': {
339
+ this.enforceSolanaChainId(chainId)
340
+ const result = await this.portal.mpc.sign({
341
+ chainId: chainId as string,
342
+ method,
343
+ params: this.buildParams(method, params),
344
+ rpcUrl: this.portal.getRpcUrl(chainId),
322
345
  })
323
-
324
346
  return result
325
347
  }
326
348
  default:
@@ -330,6 +352,30 @@ class Provider {
330
352
  }
331
353
  }
332
354
 
355
+ private enforceEip155ChainId = (chainId?: string) => {
356
+ if (!chainId) {
357
+ throw new Error('[PortalProvider] Chain ID is required for the operation')
358
+ }
359
+
360
+ if (!chainId.startsWith('eip155:')) {
361
+ throw new Error(
362
+ `[PortalProvider] Chain ID must be prefixed with "eip155:" for the operation, got ${chainId}`,
363
+ )
364
+ }
365
+ }
366
+
367
+ private enforceSolanaChainId = (chainId?: string) => {
368
+ if (!chainId) {
369
+ throw new Error('[PortalProvider] Chain ID is required for the operation')
370
+ }
371
+
372
+ if (!chainId.startsWith('solana:')) {
373
+ throw new Error(
374
+ `[PortalProvider] Chain ID must be prefixed with "solana:" for the operation, got ${chainId}`,
375
+ )
376
+ }
377
+ }
378
+
333
379
  private buildParams = (method: string, txParams: any) => {
334
380
  let params = txParams
335
381
 
@@ -338,6 +384,10 @@ class Provider {
338
384
  case 'personal_sign':
339
385
  case 'eth_signTypedData_v3':
340
386
  case 'eth_signTypedData_v4':
387
+ case 'sol_signAndConfirmTransaction':
388
+ case 'sol_signAndSendTransaction':
389
+ case 'sol_signMessage':
390
+ case 'sol_signTransaction':
341
391
  if (!Array.isArray(txParams)) {
342
392
  params = [txParams]
343
393
  }
package/types.d.ts CHANGED
@@ -1,13 +1,8 @@
1
1
  import { type DkgData, PortalError } from '@portal-hq/utils'
2
2
  import type { MpcErrorCodes } from './src/mpc/errors'
3
3
 
4
- import Api from '../iframe/core/api'
5
- import GDriveStorage from '../iframe/core/storage/gdrive'
6
- import PasskeyStorage from '../iframe/core/storage/passkey'
7
4
  import Portal from './src/index'
8
- import Provider from './src/provider'
9
5
 
10
- export type GatewayLike = GatewayConfig | string
11
6
  export type EventHandler = (event: Event<any>) => void | Promise<void>
12
7
  export type EthereumTransaction = EIP1559Transaction | LegacyTransaction
13
8
  export type MessageData =
@@ -40,19 +35,6 @@ export interface BackupConfigs {
40
35
  passwordStorage?: PasswordConfig
41
36
  }
42
37
 
43
- export interface BackupSharePairMetadata {
44
- backupMethod: BackupMethods
45
- createdAt: string
46
- id: string
47
- status: 'completed' | 'incomplete'
48
- }
49
-
50
- export interface SigningSharePairMetadata {
51
- createdAt: string
52
- id: string
53
- status: 'completed' | 'incomplete'
54
- }
55
-
56
38
  export interface PasswordConfig {
57
39
  password: string
58
40
  }
@@ -87,6 +69,11 @@ export interface Balance {
87
69
  balance: string
88
70
  }
89
71
 
72
+ export interface SharesOnDeviceResponse {
73
+ ED25519: boolean
74
+ SECP256K1: boolean
75
+ }
76
+
90
77
  export interface ClientResponse {
91
78
  id: string
92
79
  address: string
@@ -179,6 +166,11 @@ export interface EIP1559Transaction {
179
166
  value?: string
180
167
  }
181
168
 
169
+ export interface EjectResult {
170
+ ED25519: string
171
+ SECP256K1: string
172
+ }
173
+
182
174
  export interface EncryptArgs {
183
175
  dkgData: string
184
176
  }
@@ -220,8 +212,8 @@ export interface EjectWorkerArgs extends MpcOperationArgs {
220
212
  organizationBackupShare: string
221
213
  }
222
214
 
223
- export interface GatewayConfig {
224
- [key: number]: string
215
+ export interface RpcConfig {
216
+ [key: string]: string
225
217
  }
226
218
 
227
219
  export interface GDriveConfig {
@@ -268,12 +260,10 @@ export interface GenerateResponse {
268
260
 
269
261
  export interface IframeConfigurationOptions {
270
262
  autoApprove: boolean
271
- chainId: number
272
263
  featureFlags?: FeatureFlags
273
264
  gdrive?: GDriveConfig
274
265
  passkey?: PasskeyConfig
275
266
  host: string
276
- rpcUrl: string
277
267
  mpcHost: string
278
268
  mpcVersion: string
279
269
 
@@ -355,7 +345,7 @@ export interface PedersonParams {
355
345
  export interface PortalApiOptions {
356
346
  apiKey: string
357
347
  baseUrl: string
358
- chainId: number
348
+ chainId: string
359
349
  host: string
360
350
  portal: Portal
361
351
  }
@@ -367,14 +357,13 @@ export interface PortalError {
367
357
 
368
358
  export interface PortalOptions {
369
359
  // Required options
370
- gatewayConfig: GatewayLike
360
+ rpcConfig: RpcConfig
371
361
 
372
362
  // Optional options
373
363
  apiKey?: string
374
364
  authToken?: string
375
365
  authUrl?: string
376
366
  autoApprove?: boolean
377
- chainId?: number
378
367
  gdrive?: GDriveConfig
379
368
  passkey?: PasskeyConfig
380
369
  host?: string
@@ -517,7 +506,7 @@ export interface Transaction {
517
506
  asset: string
518
507
  blockNum: string
519
508
  category: string
520
- chainId: number
509
+ chainId: string
521
510
  erc1155Metadata: null | string
522
511
  erc721TokenId: null | string
523
512
  from: string
@@ -537,7 +526,7 @@ export interface Transaction {
537
526
  }
538
527
 
539
528
  export interface SwitchEthereumChainParameter {
540
- chainId: number
529
+ chainId: string
541
530
  }
542
531
 
543
532
  export interface TypedData {
@@ -578,4 +567,5 @@ export interface WorkerResult {
578
567
  | ReadyResult
579
568
  | RecoverResult
580
569
  | SignResult
570
+ | EjectResult
581
571
  }