@portal-hq/provider 3.0.6 → 4.0.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.
@@ -7,7 +7,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { Events, HttpRequester, InvalidApiKeyError, InvalidChainIdError, InvalidGatewayConfigError, ProviderRpcError, RpcErrorCodes, } from '@portal-hq/utils';
10
+ import { PortalCurve } from '@portal-hq/core';
11
+ import { Events, HttpRequester, InvalidApiKeyError, InvalidGatewayConfigError, PortalRequests, ProviderRpcError, RpcErrorCodes, } from '@portal-hq/utils';
11
12
  import { MpcSigner } from '../signers';
12
13
  const passiveSignerMethods = [
13
14
  'eth_accounts',
@@ -24,26 +25,28 @@ const signerMethods = [
24
25
  'eth_signTypedData_v3',
25
26
  'eth_signTypedData_v4',
26
27
  'personal_sign',
28
+ 'sol_signAndConfirmTransaction',
29
+ 'sol_signAndSendTransaction',
30
+ 'sol_signMessage',
31
+ 'sol_signTransaction',
32
+ 'raw_sign',
27
33
  ];
28
34
  class Provider {
29
- get address() {
30
- return this.keychain.getAddress(this.isSimulator);
35
+ get addresses() {
36
+ return this.keychain.getAddresses();
31
37
  }
32
- get gatewayUrl() {
33
- return this.getGatewayUrl();
38
+ get address() {
39
+ return this.keychain.getAddress();
34
40
  }
35
41
  constructor({
36
42
  // Required
37
- apiKey, chainId, keychain, gatewayConfig,
43
+ apiKey, keychain, gatewayConfig,
38
44
  // Optional
39
- isSimulator = false, autoApprove = false, apiHost = 'api.portalhq.io', mpcHost = 'mpc.portalhq.io', version = 'v6', featureFlags = { optimized: false }, }) {
45
+ autoApprove = false, apiHost = 'api.portalhq.io', mpcHost = 'mpc.portalhq.io', version = 'v6', chainId = 'eip155:11155111', featureFlags = {}, }) {
40
46
  // Handle required fields
41
47
  if (!apiKey || apiKey.length === 0) {
42
48
  throw new InvalidApiKeyError();
43
49
  }
44
- if (!chainId) {
45
- throw new InvalidChainIdError();
46
- }
47
50
  if (!gatewayConfig) {
48
51
  throw new InvalidGatewayConfigError();
49
52
  }
@@ -53,7 +56,6 @@ class Provider {
53
56
  this.chainId = chainId;
54
57
  this.events = {};
55
58
  this.gatewayConfig = gatewayConfig;
56
- this.isSimulator = isSimulator;
57
59
  this.keychain = keychain;
58
60
  // Add a logger
59
61
  this.log = console;
@@ -64,9 +66,7 @@ class Provider {
64
66
  : `https://${apiHost}`,
65
67
  });
66
68
  // Initialize Gateway HttpRequester
67
- this.gateway = new HttpRequester({
68
- baseUrl: this.getGatewayUrl(),
69
- });
69
+ this.gateway = new PortalRequests();
70
70
  // Initialize an MpcSigner
71
71
  this.signer = new MpcSigner({
72
72
  mpcHost,
@@ -99,24 +99,28 @@ class Provider {
99
99
  *
100
100
  * @returns string
101
101
  */
102
- getGatewayUrl() {
102
+ getGatewayUrl(chainId) {
103
+ chainId = chainId !== null && chainId !== void 0 ? chainId : this.chainId;
104
+ if (!chainId) {
105
+ throw new Error('[PortalProvider] No chainId provided');
106
+ }
103
107
  if (typeof this.gatewayConfig === 'string') {
104
108
  // If the gatewayConfig is just a static URL, return that
105
109
  return this.gatewayConfig;
106
110
  }
107
111
  else if (typeof this.gatewayConfig === 'object' &&
108
112
  // eslint-disable-next-line no-prototype-builtins
109
- !this.gatewayConfig.hasOwnProperty(this.chainId)) {
113
+ !this.gatewayConfig.hasOwnProperty(chainId)) {
110
114
  // If there's no explicit mapping for the current chainId, error out
111
- throw new Error(`[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`);
115
+ throw new Error(`[PortalProvider] No RPC endpoint configured for chainId: ${chainId}`);
112
116
  }
113
117
  // Get the entry for the current chainId from the gatewayConfig
114
- const config = this.gatewayConfig[this.chainId];
118
+ const config = this.gatewayConfig[chainId];
115
119
  if (typeof config === 'string') {
116
120
  return config;
117
121
  }
118
122
  // If we got this far, there's no way to support the chain with the current config
119
- throw new Error(`[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`);
123
+ throw new Error(`[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${chainId}`);
120
124
  }
121
125
  /**
122
126
  * Registers an event handler for the provided event
@@ -183,14 +187,23 @@ class Provider {
183
187
  */
184
188
  request({ method, params, chainId, connect, }) {
185
189
  return __awaiter(this, void 0, void 0, function* () {
190
+ chainId = chainId !== null && chainId !== void 0 ? chainId : this.chainId;
191
+ if (!chainId) {
192
+ throw new Error('[PortalProvider] No chainId provided');
193
+ }
186
194
  if (method === 'eth_chainId') {
187
- return this.chainId;
195
+ if (typeof chainId === 'string' && chainId.includes(':')) {
196
+ return chainId.split(':')[1];
197
+ }
198
+ else {
199
+ throw new Error("[PortalProvider] Invalid chainId format. Must be 'namespace:reference'");
200
+ }
188
201
  }
189
202
  // Handle changing chains
190
203
  if (method == 'wallet_switchEthereumChain') {
191
204
  const param = params[0];
192
205
  const chainId = parseInt(param.chainId, 16);
193
- this.chainId = chainId;
206
+ this.chainId = `eip155:${String(chainId)}`;
194
207
  this.emit('portal_signatureReceived', {
195
208
  method,
196
209
  params,
@@ -201,7 +214,11 @@ class Provider {
201
214
  let result;
202
215
  if (!isSignerMethod && !method.startsWith('wallet_')) {
203
216
  // Send to Gateway for RPC calls
204
- const response = yield this.handleGatewayRequests({ method, params });
217
+ const response = yield this.handleGatewayRequests({
218
+ method,
219
+ params,
220
+ chainId,
221
+ });
205
222
  this.emit('portal_signatureReceived', {
206
223
  method,
207
224
  params,
@@ -220,6 +237,25 @@ class Provider {
220
237
  chainId,
221
238
  connect,
222
239
  });
240
+ if (transactionHash) {
241
+ try {
242
+ yield this.portalApi.post('/api/v1/analytics/track', {
243
+ headers: {
244
+ Authentication: `Bearer ${this.apiKey}`,
245
+ },
246
+ body: {
247
+ event: Events.TransactionSigned,
248
+ properties: {
249
+ method,
250
+ },
251
+ },
252
+ });
253
+ }
254
+ catch (error) {
255
+ // Do nothing, because we don't want metrics gathering
256
+ // to block the SDK
257
+ }
258
+ }
223
259
  if (transactionHash) {
224
260
  this.emit('portal_signatureReceived', {
225
261
  method,
@@ -251,23 +287,29 @@ class Provider {
251
287
  */
252
288
  setChainId(chainId, connect) {
253
289
  return __awaiter(this, void 0, void 0, function* () {
290
+ if (typeof chainId === 'number') {
291
+ chainId = `eip155:${String(chainId)}`;
292
+ }
254
293
  // Update the chainId
255
294
  this.chainId = chainId;
256
- // Re-initialize the Gateway HttpRequester
257
- this.gateway = new HttpRequester({
258
- baseUrl: this.getGatewayUrl(),
259
- });
260
- // Emit event for update
261
- this.emit('chainChanged', {
262
- chainId: this.chainId,
263
- });
264
- if (connect && connect.connected) {
265
- connect.emit('portalConnect_chainChanged', {
266
- chainId: this.chainId,
295
+ if (chainId.includes(':')) {
296
+ const chainIdParts = chainId.split(':');
297
+ const reference = parseInt(chainIdParts[1]);
298
+ // Emit event for update
299
+ this.emit('chainChanged', {
300
+ chainId: reference,
267
301
  });
302
+ if (connect && connect.connected) {
303
+ connect.emit('portalConnect_chainChanged', {
304
+ chainId: reference,
305
+ });
306
+ }
307
+ // Dispatch 'connect' event
308
+ this.dispatchConnect(chainId);
309
+ }
310
+ else {
311
+ console.error(`[PortalProvider] Invalid chainId format. Must be 'namespace:reference', but got ${chainId}`);
268
312
  }
269
- // Dispatch 'connect' event
270
- this.dispatchConnect();
271
313
  return new Promise((resolve) => resolve(this));
272
314
  });
273
315
  }
@@ -330,9 +372,10 @@ class Provider {
330
372
  });
331
373
  });
332
374
  }
333
- dispatchConnect() {
375
+ dispatchConnect(chainId) {
376
+ const reference = chainId.split(':')[1];
334
377
  this.emit('connect', {
335
- chainId: `0x${this.chainId.toString(16)}`,
378
+ chainId: `0x${Number(reference).toString(16)}`,
336
379
  });
337
380
  }
338
381
  /**
@@ -341,13 +384,15 @@ class Provider {
341
384
  * @param args The arguments of the request being made
342
385
  * @returns Promise<any>
343
386
  */
344
- handleGatewayRequests({ method, params, }) {
387
+ handleGatewayRequests({ method, params, chainId, }) {
345
388
  return __awaiter(this, void 0, void 0, function* () {
389
+ const gatewayUrl = this.getGatewayUrl(chainId);
346
390
  // Pass request off to the gateway
347
- return yield this.gateway.post('', {
391
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
392
+ return yield this.gateway.post(gatewayUrl, '', {
348
393
  body: {
349
394
  jsonrpc: '2.0',
350
- id: this.chainId,
395
+ id: chainId,
351
396
  method,
352
397
  params,
353
398
  },
@@ -361,7 +406,7 @@ class Provider {
361
406
  * @returns Promise<any>
362
407
  */
363
408
  handleSigningRequests({ method, params, chainId, connect, }) {
364
- var _a;
409
+ var _a, _b;
365
410
  return __awaiter(this, void 0, void 0, function* () {
366
411
  const isApproved = passiveSignerMethods.includes(method)
367
412
  ? true
@@ -370,9 +415,17 @@ class Provider {
370
415
  this.log.info(`[PortalProvider] Request for signing method '${method}' could not be completed because it was not approved by the user.`);
371
416
  return;
372
417
  }
418
+ let namespace = 'eip155';
419
+ let reference = chainId;
420
+ if (typeof chainId == 'string' && chainId.includes(':')) {
421
+ const tmp = chainId.split(':');
422
+ namespace = tmp[0];
423
+ reference = tmp[1];
424
+ }
425
+ const curve = namespace == 'eip155' ? PortalCurve.SECP256K1 : PortalCurve.ED25519;
373
426
  switch (method) {
374
427
  case 'eth_chainId':
375
- return `0x${this.chainId.toString(16)}`;
428
+ return `0x${Number(reference).toString(16)}`;
376
429
  case 'eth_accounts':
377
430
  case 'eth_requestAccounts':
378
431
  case 'eth_sendTransaction':
@@ -380,27 +433,28 @@ class Provider {
380
433
  case 'eth_signTransaction':
381
434
  case 'eth_signTypedData_v3':
382
435
  case 'eth_signTypedData_v4':
383
- case 'personal_sign': {
384
- const result = yield ((_a = this.signer) === null || _a === void 0 ? void 0 : _a.sign({ chainId: this.chainId, method, params }, this));
385
- if (result) {
386
- try {
387
- yield this.portalApi.post('/api/v1/analytics/track', {
388
- headers: {
389
- Authentication: `Bearer ${this.apiKey}`,
390
- },
391
- body: {
392
- event: Events.TransactionSigned,
393
- properties: {
394
- method,
395
- },
396
- },
397
- });
398
- }
399
- catch (error) {
400
- // Do nothing, because we don't want metrics gathering
401
- // to block the SDK
402
- }
403
- }
436
+ case 'personal_sign':
437
+ case 'sol_signAndConfirmTransaction':
438
+ case 'sol_signAndSendTransaction':
439
+ case 'sol_signMessage':
440
+ case 'sol_signTransaction': {
441
+ const result = yield ((_a = this.signer) === null || _a === void 0 ? void 0 : _a.sign({
442
+ chainId: `${namespace}:${reference}`,
443
+ method,
444
+ params,
445
+ curve,
446
+ isRaw: false,
447
+ }, this));
448
+ return result;
449
+ }
450
+ case 'raw_sign': {
451
+ const result = yield ((_b = this.signer) === null || _b === void 0 ? void 0 : _b.sign({
452
+ chainId: '',
453
+ method: '',
454
+ params,
455
+ curve,
456
+ isRaw: true,
457
+ }, this));
404
458
  return result;
405
459
  }
406
460
  default:
@@ -7,16 +7,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { PortalCurve } from '@portal-hq/core';
10
11
  import { PortalMpcError, } from '@portal-hq/utils';
11
12
  import { NativeModules } from 'react-native';
12
13
  class MpcSigner {
13
- get address() {
14
- return this.keychain.getAddress(this.isSimulator);
15
- }
16
- get signingShare() {
17
- return this.keychain.getDkgResult(this.isSimulator);
18
- }
19
- constructor({ keychain, isSimulator = false, mpcHost = 'mpc.portalhq.io', version = 'v6', featureFlags = { optimized: false }, }) {
14
+ constructor({ keychain, mpcHost = 'mpc.portalhq.io', version = 'v6', featureFlags = {}, }) {
20
15
  this.version = 'v6';
21
16
  this.buildParams = (method, txParams) => {
22
17
  let params = txParams;
@@ -25,18 +20,23 @@ class MpcSigner {
25
20
  case 'personal_sign':
26
21
  case 'eth_signTypedData_v3':
27
22
  case 'eth_signTypedData_v4':
23
+ case 'sol_signMessage':
24
+ case 'sol_signTransaction':
25
+ case 'sol_signAndSendTransaction':
26
+ case 'sol_signAndConfirmTransaction':
28
27
  if (!Array.isArray(txParams)) {
29
28
  params = [txParams];
30
29
  }
31
30
  break;
32
31
  default:
33
32
  if (Array.isArray(txParams)) {
34
- params = txParams[0];
33
+ if (txParams.length === 1) {
34
+ params = txParams[0];
35
+ }
35
36
  }
36
37
  }
37
38
  return params;
38
39
  };
39
- this.isSimulator = isSimulator;
40
40
  this.keychain = keychain;
41
41
  this.mpc = NativeModules.PortalMobileMpc;
42
42
  this.mpcHost = mpcHost;
@@ -47,27 +47,52 @@ class MpcSigner {
47
47
  }
48
48
  }
49
49
  sign(message, provider) {
50
+ var _a, _b;
50
51
  return __awaiter(this, void 0, void 0, function* () {
51
- const address = yield this.address;
52
+ const addresses = yield this.keychain.getAddresses();
53
+ const legacyAddress = yield this.keychain.getAddress();
52
54
  const apiKey = provider.apiKey;
53
- const { method, params } = message;
55
+ const { method, chainId, curve, isRaw } = message;
54
56
  switch (method) {
55
57
  case 'eth_requestAccounts':
56
- return [address];
58
+ return [(_a = addresses.eip155) !== null && _a !== void 0 ? _a : legacyAddress];
57
59
  case 'eth_accounts':
58
- return [address];
60
+ return [(_b = addresses.eip155) !== null && _b !== void 0 ? _b : legacyAddress];
59
61
  default:
60
62
  break;
61
63
  }
62
- const signingShare = yield this.signingShare;
64
+ const shares = yield this.keychain.getShares();
65
+ let signingShare = shares.secp256k1.share;
66
+ if (curve === PortalCurve.ED25519) {
67
+ if (!shares.ed25519) {
68
+ throw new Error('[Portal.Provider.MpcSigner] The ED25519 share is missing from the keychain.');
69
+ }
70
+ signingShare = shares.ed25519.share;
71
+ }
63
72
  const metadata = {
64
73
  clientPlatform: 'REACT_NATIVE',
65
74
  isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
66
75
  mpcServerVersion: this.version,
67
- optimized: this.featureFlags.optimized,
76
+ optimized: true,
77
+ curve,
78
+ chainId,
79
+ isRaw,
68
80
  };
69
81
  const stringifiedMetadata = JSON.stringify(metadata);
70
- const result = yield this.mpc.sign(apiKey, this.mpcHost, signingShare, message.method, JSON.stringify(this.buildParams(method, params)), provider.gatewayUrl, provider.chainId.toString(), stringifiedMetadata);
82
+ let formattedParams;
83
+ let rpcUrl;
84
+ if (isRaw) {
85
+ formattedParams = this.buildParams(method, message.params);
86
+ rpcUrl = '';
87
+ }
88
+ else {
89
+ formattedParams = JSON.stringify(this.buildParams(method, message.params));
90
+ rpcUrl = provider.getGatewayUrl(chainId);
91
+ }
92
+ if (typeof formattedParams !== 'string') {
93
+ throw new Error(`[Portal.Provider.MpcSigner] The formatted params for the signing request could not be converted to a string. The params were: ${formattedParams}`);
94
+ }
95
+ const result = yield this.mpc.sign(apiKey, this.mpcHost, JSON.stringify(signingShare), message.method, formattedParams, rpcUrl, chainId, stringifiedMetadata);
71
96
  const { data, error } = JSON.parse(String(result));
72
97
  if (error && error.code > 0) {
73
98
  throw new PortalMpcError(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portal-hq/provider",
3
- "version": "3.0.6",
3
+ "version": "4.0.0",
4
4
  "license": "MIT",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/esm/index",
@@ -19,8 +19,8 @@
19
19
  "test": "jest"
20
20
  },
21
21
  "dependencies": {
22
- "@portal-hq/connect": "^3.0.6",
23
- "@portal-hq/utils": "^3.0.6"
22
+ "@portal-hq/connect": "^4.0.0",
23
+ "@portal-hq/utils": "^4.0.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@babel/preset-typescript": "^7.18.6",
@@ -30,5 +30,5 @@
30
30
  "ts-jest": "^29.0.3",
31
31
  "typescript": "^4.8.4"
32
32
  },
33
- "gitHead": "884d505870ff81f79fde92abdcea508619eaf86e"
33
+ "gitHead": "a1c6f51ff156d3d1922cdaa29ee43fe4cfefd932"
34
34
  }