@portal-hq/web 3.4.0 → 3.4.1-beta

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.
@@ -333,6 +333,52 @@ class Portal {
333
333
  return this.getUniqueBackupMethod(allMethods);
334
334
  });
335
335
  }
336
+ receiveTestnetAsset(chainId, params) {
337
+ var _a;
338
+ return __awaiter(this, void 0, void 0, function* () {
339
+ return yield ((_a = this.mpc) === null || _a === void 0 ? void 0 : _a.fund(chainId, params));
340
+ });
341
+ }
342
+ sendAsset(chain, params) {
343
+ var _a;
344
+ return __awaiter(this, void 0, void 0, function* () {
345
+ try {
346
+ // Convert the chain to a chain ID
347
+ const chainId = this.convertChainToChainId(chain);
348
+ // Build the transaction
349
+ const buildTxResponse = yield this.buildTransaction(chainId, params.to, params.token, params.amount);
350
+ // Get the chain namespace
351
+ const chainNamespace = (_a = chainId.split(':')[0]) !== null && _a !== void 0 ? _a : '';
352
+ // Send the transaction based on chain namespace
353
+ let response;
354
+ switch (chainNamespace) {
355
+ case 'eip155': {
356
+ response = yield this.request({
357
+ chainId,
358
+ method: 'eth_sendTransaction',
359
+ params: [buildTxResponse.transaction],
360
+ });
361
+ break;
362
+ }
363
+ case 'solana': {
364
+ response = yield this.request({
365
+ chainId,
366
+ method: 'sol_signAndSendTransaction',
367
+ params: [buildTxResponse.transaction],
368
+ });
369
+ break;
370
+ }
371
+ default: {
372
+ throw new Error(`Unsupported chain namespace: ${chainNamespace}`);
373
+ }
374
+ }
375
+ return response;
376
+ }
377
+ catch (error) {
378
+ throw new Error(`Failed to send asset: ${error.message}`);
379
+ }
380
+ });
381
+ }
336
382
  /****************************
337
383
  * Provider Methods
338
384
  ****************************/
@@ -718,6 +764,40 @@ class Portal {
718
764
  getUniqueBackupMethod(methods) {
719
765
  return Array.from(new Set(methods));
720
766
  }
767
+ convertChainToChainId(chain) {
768
+ // If the chain is not provided, throw an error.
769
+ if (!chain) {
770
+ throw new Error('You did not provide "chain" in the function call. Please provide a chain ID or friendly chain name.');
771
+ }
772
+ // If the chain is already in CAIP-2 format (e.g., "eip155:1"), return it as-is.
773
+ if (chain.includes(':')) {
774
+ return chain;
775
+ }
776
+ // A mapping of friendly chain names to CAIP-2 chain IDs.
777
+ const friendlyChainToChainId = {
778
+ ethereum: 'eip155:1',
779
+ sepolia: 'eip155:11155111',
780
+ base: 'eip155:8453',
781
+ 'base-sepolia': 'eip155:84531',
782
+ polygon: 'eip155:137',
783
+ 'polygon-mumbai': 'eip155:80001',
784
+ 'polygon-amoy': 'eip155:80002',
785
+ solana: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
786
+ 'solana-devnet': 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
787
+ optimism: 'eip155:10',
788
+ arbitrum: 'eip155:42161',
789
+ avalanche: 'eip155:43114',
790
+ };
791
+ // Convert the chain to lowercase to match the friendly chain names.
792
+ const lowerCaseChain = chain.toLowerCase();
793
+ // Get the CAIP-2 chain ID from the mapping, if it exists.
794
+ const chainId = friendlyChainToChainId[lowerCaseChain];
795
+ // If the chain is not supported, throw an error.
796
+ if (!chainId) {
797
+ throw new Error(`Unsupported chain: "${chain}". Please provide a supported chain, such as ${Object.keys(friendlyChainToChainId).join(', ')}`);
798
+ }
799
+ return chainId;
800
+ }
721
801
  }
722
802
  var mpc_2 = require("./mpc");
723
803
  Object.defineProperty(exports, "MpcError", { enumerable: true, get: function () { return mpc_2.MpcError; } });
@@ -12,7 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.MpcErrorCodes = exports.MpcError = void 0;
13
13
  const errors_1 = require("./errors");
14
14
  const index_1 = require("../index");
15
- const WEB_SDK_VERSION = '3.4.0';
15
+ const WEB_SDK_VERSION = '3.4.1-beta';
16
16
  class Mpc {
17
17
  get ready() {
18
18
  return this._ready;
@@ -475,6 +475,39 @@ class Mpc {
475
475
  });
476
476
  });
477
477
  }
478
+ fund(chainId, params) {
479
+ return __awaiter(this, void 0, void 0, function* () {
480
+ return new Promise((resolve, reject) => {
481
+ const handleFund = (event) => {
482
+ const { type, data } = event.data;
483
+ const { origin } = event;
484
+ // Ignore any broadcast postMessages
485
+ if (origin !== this.getOrigin()) {
486
+ return;
487
+ }
488
+ if (type === 'portal:fundError') {
489
+ // Remove the event listener
490
+ window.removeEventListener('message', handleFund);
491
+ // Reject the promise with the error
492
+ return reject(new errors_1.PortalMpcError(data));
493
+ }
494
+ else if (type === 'portal:fundResult') {
495
+ // Remove the event listener
496
+ window.removeEventListener('message', handleFund);
497
+ // Resolve the promise with the result
498
+ resolve(data);
499
+ }
500
+ };
501
+ // Bind the function to the message event
502
+ window.addEventListener('message', handleFund);
503
+ // Send the request to the iframe
504
+ this.postMessage({
505
+ type: 'portal:fund',
506
+ data: { chainId, params },
507
+ });
508
+ });
509
+ });
510
+ }
478
511
  getClient() {
479
512
  return __awaiter(this, void 0, void 0, function* () {
480
513
  return new Promise((resolve, reject) => {
package/lib/esm/index.js CHANGED
@@ -304,6 +304,52 @@ class Portal {
304
304
  return this.getUniqueBackupMethod(allMethods);
305
305
  });
306
306
  }
307
+ receiveTestnetAsset(chainId, params) {
308
+ var _a;
309
+ return __awaiter(this, void 0, void 0, function* () {
310
+ return yield ((_a = this.mpc) === null || _a === void 0 ? void 0 : _a.fund(chainId, params));
311
+ });
312
+ }
313
+ sendAsset(chain, params) {
314
+ var _a;
315
+ return __awaiter(this, void 0, void 0, function* () {
316
+ try {
317
+ // Convert the chain to a chain ID
318
+ const chainId = this.convertChainToChainId(chain);
319
+ // Build the transaction
320
+ const buildTxResponse = yield this.buildTransaction(chainId, params.to, params.token, params.amount);
321
+ // Get the chain namespace
322
+ const chainNamespace = (_a = chainId.split(':')[0]) !== null && _a !== void 0 ? _a : '';
323
+ // Send the transaction based on chain namespace
324
+ let response;
325
+ switch (chainNamespace) {
326
+ case 'eip155': {
327
+ response = yield this.request({
328
+ chainId,
329
+ method: 'eth_sendTransaction',
330
+ params: [buildTxResponse.transaction],
331
+ });
332
+ break;
333
+ }
334
+ case 'solana': {
335
+ response = yield this.request({
336
+ chainId,
337
+ method: 'sol_signAndSendTransaction',
338
+ params: [buildTxResponse.transaction],
339
+ });
340
+ break;
341
+ }
342
+ default: {
343
+ throw new Error(`Unsupported chain namespace: ${chainNamespace}`);
344
+ }
345
+ }
346
+ return response;
347
+ }
348
+ catch (error) {
349
+ throw new Error(`Failed to send asset: ${error.message}`);
350
+ }
351
+ });
352
+ }
307
353
  /****************************
308
354
  * Provider Methods
309
355
  ****************************/
@@ -689,6 +735,40 @@ class Portal {
689
735
  getUniqueBackupMethod(methods) {
690
736
  return Array.from(new Set(methods));
691
737
  }
738
+ convertChainToChainId(chain) {
739
+ // If the chain is not provided, throw an error.
740
+ if (!chain) {
741
+ throw new Error('You did not provide "chain" in the function call. Please provide a chain ID or friendly chain name.');
742
+ }
743
+ // If the chain is already in CAIP-2 format (e.g., "eip155:1"), return it as-is.
744
+ if (chain.includes(':')) {
745
+ return chain;
746
+ }
747
+ // A mapping of friendly chain names to CAIP-2 chain IDs.
748
+ const friendlyChainToChainId = {
749
+ ethereum: 'eip155:1',
750
+ sepolia: 'eip155:11155111',
751
+ base: 'eip155:8453',
752
+ 'base-sepolia': 'eip155:84531',
753
+ polygon: 'eip155:137',
754
+ 'polygon-mumbai': 'eip155:80001',
755
+ 'polygon-amoy': 'eip155:80002',
756
+ solana: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
757
+ 'solana-devnet': 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
758
+ optimism: 'eip155:10',
759
+ arbitrum: 'eip155:42161',
760
+ avalanche: 'eip155:43114',
761
+ };
762
+ // Convert the chain to lowercase to match the friendly chain names.
763
+ const lowerCaseChain = chain.toLowerCase();
764
+ // Get the CAIP-2 chain ID from the mapping, if it exists.
765
+ const chainId = friendlyChainToChainId[lowerCaseChain];
766
+ // If the chain is not supported, throw an error.
767
+ if (!chainId) {
768
+ throw new Error(`Unsupported chain: "${chain}". Please provide a supported chain, such as ${Object.keys(friendlyChainToChainId).join(', ')}`);
769
+ }
770
+ return chainId;
771
+ }
692
772
  }
693
773
  export { MpcError, MpcErrorCodes } from './mpc';
694
774
  export { RequestMethod } from './provider';
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { PortalMpcError } from './errors';
11
11
  import { BackupMethods, } from '../index';
12
- const WEB_SDK_VERSION = '3.4.0';
12
+ const WEB_SDK_VERSION = '3.4.1-beta';
13
13
  class Mpc {
14
14
  get ready() {
15
15
  return this._ready;
@@ -472,6 +472,39 @@ class Mpc {
472
472
  });
473
473
  });
474
474
  }
475
+ fund(chainId, params) {
476
+ return __awaiter(this, void 0, void 0, function* () {
477
+ return new Promise((resolve, reject) => {
478
+ const handleFund = (event) => {
479
+ const { type, data } = event.data;
480
+ const { origin } = event;
481
+ // Ignore any broadcast postMessages
482
+ if (origin !== this.getOrigin()) {
483
+ return;
484
+ }
485
+ if (type === 'portal:fundError') {
486
+ // Remove the event listener
487
+ window.removeEventListener('message', handleFund);
488
+ // Reject the promise with the error
489
+ return reject(new PortalMpcError(data));
490
+ }
491
+ else if (type === 'portal:fundResult') {
492
+ // Remove the event listener
493
+ window.removeEventListener('message', handleFund);
494
+ // Resolve the promise with the result
495
+ resolve(data);
496
+ }
497
+ };
498
+ // Bind the function to the message event
499
+ window.addEventListener('message', handleFund);
500
+ // Send the request to the iframe
501
+ this.postMessage({
502
+ type: 'portal:fund',
503
+ data: { chainId, params },
504
+ });
505
+ });
506
+ });
507
+ }
475
508
  getClient() {
476
509
  return __awaiter(this, void 0, void 0, function* () {
477
510
  return new Promise((resolve, reject) => {
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.4.0",
6
+ "version": "3.4.1-beta",
7
7
  "license": "MIT",
8
8
  "main": "lib/commonjs/index",
9
9
  "module": "lib/esm/index",
@@ -49,5 +49,6 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@solana/web3.js": "^1.91.8"
52
- }
52
+ },
53
+ "stableVersion": "3.4.0"
53
54
  }
package/src/index.ts CHANGED
@@ -12,8 +12,12 @@ import {
12
12
  EvaluateTransactionOperationType,
13
13
  EvaluateTransactionParam,
14
14
  EvaluatedTransaction,
15
+ FundParams,
16
+ FundResponse,
15
17
  NFTAsset,
16
18
  OrgBackupShares,
19
+ SendAssetParams,
20
+ SendAssetResponse,
17
21
  type BackupConfigs,
18
22
  type BackupResponse,
19
23
  type Balance,
@@ -419,6 +423,62 @@ class Portal {
419
423
  return this.getUniqueBackupMethod(allMethods)
420
424
  }
421
425
 
426
+ public async receiveTestnetAsset(
427
+ chainId: string,
428
+ params: FundParams,
429
+ ): Promise<FundResponse> {
430
+ return await this.mpc?.fund(chainId, params)
431
+ }
432
+
433
+ public async sendAsset(
434
+ chain: string,
435
+ params: SendAssetParams,
436
+ ): Promise<SendAssetResponse> {
437
+ try {
438
+ // Convert the chain to a chain ID
439
+ const chainId = this.convertChainToChainId(chain)
440
+
441
+ // Build the transaction
442
+ const buildTxResponse = await this.buildTransaction(
443
+ chainId,
444
+ params.to,
445
+ params.token,
446
+ params.amount,
447
+ )
448
+
449
+ // Get the chain namespace
450
+ const chainNamespace = chainId.split(':')[0] ?? ''
451
+
452
+ // Send the transaction based on chain namespace
453
+ let response
454
+ switch (chainNamespace) {
455
+ case 'eip155': {
456
+ response = await this.request({
457
+ chainId,
458
+ method: 'eth_sendTransaction',
459
+ params: [buildTxResponse.transaction],
460
+ })
461
+ break
462
+ }
463
+ case 'solana': {
464
+ response = await this.request({
465
+ chainId,
466
+ method: 'sol_signAndSendTransaction',
467
+ params: [buildTxResponse.transaction],
468
+ })
469
+ break
470
+ }
471
+ default: {
472
+ throw new Error(`Unsupported chain namespace: ${chainNamespace}`)
473
+ }
474
+ }
475
+
476
+ return response
477
+ } catch (error: any) {
478
+ throw new Error(`Failed to send asset: ${error.message}`)
479
+ }
480
+ }
481
+
422
482
  /****************************
423
483
  * Provider Methods
424
484
  ****************************/
@@ -908,6 +968,54 @@ class Portal {
908
968
  private getUniqueBackupMethod(methods: BackupMethods[]): BackupMethods[] {
909
969
  return Array.from(new Set(methods))
910
970
  }
971
+
972
+ private convertChainToChainId(chain: string): string {
973
+ // If the chain is not provided, throw an error.
974
+ if (!chain) {
975
+ throw new Error(
976
+ 'You did not provide "chain" in the function call. Please provide a chain ID or friendly chain name.',
977
+ )
978
+ }
979
+
980
+ // If the chain is already in CAIP-2 format (e.g., "eip155:1"), return it as-is.
981
+ if (chain.includes(':')) {
982
+ return chain
983
+ }
984
+
985
+ // A mapping of friendly chain names to CAIP-2 chain IDs.
986
+ const friendlyChainToChainId = {
987
+ ethereum: 'eip155:1',
988
+ sepolia: 'eip155:11155111',
989
+ base: 'eip155:8453',
990
+ 'base-sepolia': 'eip155:84531',
991
+ polygon: 'eip155:137',
992
+ 'polygon-mumbai': 'eip155:80001',
993
+ 'polygon-amoy': 'eip155:80002',
994
+ solana: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
995
+ 'solana-devnet': 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
996
+ optimism: 'eip155:10',
997
+ arbitrum: 'eip155:42161',
998
+ avalanche: 'eip155:43114',
999
+ }
1000
+
1001
+ // Convert the chain to lowercase to match the friendly chain names.
1002
+ const lowerCaseChain =
1003
+ chain.toLowerCase() as keyof typeof friendlyChainToChainId
1004
+
1005
+ // Get the CAIP-2 chain ID from the mapping, if it exists.
1006
+ const chainId = friendlyChainToChainId[lowerCaseChain]
1007
+
1008
+ // If the chain is not supported, throw an error.
1009
+ if (!chainId) {
1010
+ throw new Error(
1011
+ `Unsupported chain: "${chain}". Please provide a supported chain, such as ${Object.keys(
1012
+ friendlyChainToChainId,
1013
+ ).join(', ')}`,
1014
+ )
1015
+ }
1016
+
1017
+ return chainId
1018
+ }
911
1019
  }
912
1020
 
913
1021
  export { MpcError, MpcErrorCodes } from './mpc'
package/src/mpc/index.ts CHANGED
@@ -37,9 +37,11 @@ import type {
37
37
  BuiltTransaction,
38
38
  EjectPrivateKeysArgs,
39
39
  EjectPrivateKeysResult,
40
+ FundParams,
41
+ FundResponse,
40
42
  } from '../../types'
41
43
 
42
- const WEB_SDK_VERSION = '3.4.0'
44
+ const WEB_SDK_VERSION = '3.4.1-beta'
43
45
 
44
46
  class Mpc {
45
47
  public iframe?: HTMLIFrameElement
@@ -574,6 +576,46 @@ class Mpc {
574
576
  })
575
577
  }
576
578
 
579
+ public async fund(
580
+ chainId: string,
581
+ params: FundParams,
582
+ ): Promise<FundResponse> {
583
+ return new Promise((resolve, reject) => {
584
+ const handleFund = (event: MessageEvent<WorkerResult>) => {
585
+ const { type, data } = event.data
586
+ const { origin } = event
587
+
588
+ // Ignore any broadcast postMessages
589
+ if (origin !== this.getOrigin()) {
590
+ return
591
+ }
592
+
593
+ if (type === 'portal:fundError') {
594
+ // Remove the event listener
595
+ window.removeEventListener('message', handleFund)
596
+
597
+ // Reject the promise with the error
598
+ return reject(new PortalMpcError(data as PortalError))
599
+ } else if (type === 'portal:fundResult') {
600
+ // Remove the event listener
601
+ window.removeEventListener('message', handleFund)
602
+
603
+ // Resolve the promise with the result
604
+ resolve(data as FundResponse)
605
+ }
606
+ }
607
+
608
+ // Bind the function to the message event
609
+ window.addEventListener('message', handleFund)
610
+
611
+ // Send the request to the iframe
612
+ this.postMessage({
613
+ type: 'portal:fund',
614
+ data: { chainId, params },
615
+ })
616
+ })
617
+ }
618
+
577
619
  public async getClient(): Promise<ClientResponse> {
578
620
  return new Promise((resolve, reject) => {
579
621
  const handleGetClient = (event: MessageEvent<WorkerResult>) => {
package/types.d.ts CHANGED
@@ -69,6 +69,49 @@ export interface Balance {
69
69
  balance: string
70
70
  }
71
71
 
72
+ export interface SendAssetParams {
73
+ amount: string
74
+ to: string
75
+ token: string
76
+ }
77
+
78
+ export interface SendAssetResponse {
79
+ transactionHash: string
80
+ metadata: {
81
+ amount?: string
82
+ lastValidBlockHeight?: string
83
+ rawAmount?: string
84
+ tokenAddress?: string
85
+ tokenDecimals?: number
86
+ tokenSymbol?: string
87
+ }
88
+ }
89
+
90
+ export interface FundParams {
91
+ amount: string
92
+ token: string
93
+ }
94
+
95
+ export interface FundResponseData {
96
+ explorerUrl: string
97
+ txHash: string
98
+ }
99
+
100
+ export interface FundResponseMetadata {
101
+ amount: string
102
+ chainId: string
103
+ clientId: string
104
+ custodianId: string
105
+ environmentId: string
106
+ token: string
107
+ }
108
+
109
+ export interface FundResponse {
110
+ data?: FundResponseData
111
+ metadata: FundResponseMetadata
112
+ error?: string
113
+ }
114
+
72
115
  export interface SharesOnDeviceResponse {
73
116
  ED25519: boolean
74
117
  SECP256K1: boolean