@portal-hq/connect 2.0.2 → 2.0.3

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.
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.ConnectionStates = void 0;
12
+ exports.ConnectionStates = exports.ConnectError = void 0;
13
13
  const provider_1 = require("@portal-hq/provider");
14
14
  class PortalConnect {
15
15
  constructor({ apiKey, chainId, keychain, gatewayConfig,
@@ -19,6 +19,7 @@ class PortalConnect {
19
19
  this.apiKey = apiKey;
20
20
  this.events = {};
21
21
  this.websocketServer = webSocketServer;
22
+ this.gatewayConfig = gatewayConfig;
22
23
  this.provider = new provider_1.Provider({
23
24
  apiKey,
24
25
  chainId,
@@ -172,6 +173,36 @@ class PortalConnect {
172
173
  this.events[event] = this.events[event].filter(filterEventHandlers);
173
174
  }
174
175
  }
176
+ /**
177
+ * Adds chains from gateway config to the session proposal
178
+ * @param proposal
179
+ * @returns session proposal with chains from gateway config added
180
+ */
181
+ addChainsToProposal(proposal) {
182
+ if (!('params' in proposal) || !('requiredNamespaces' in proposal.params)) {
183
+ throw new Error('Invalid proposal structure.');
184
+ }
185
+ // Ensure eip155 is present in requiredNamespaces
186
+ if (!proposal.params.requiredNamespaces.eip155) {
187
+ proposal.params.requiredNamespaces.eip155 = {
188
+ chains: [],
189
+ methods: [],
190
+ events: [],
191
+ rpcMap: {},
192
+ };
193
+ }
194
+ // Get chains from gatewayConfig with 'eip155:' prefix, but only if they're not already in the proposal
195
+ const existingChains = proposal.params.requiredNamespaces.eip155.chains;
196
+ const newChains = Object.keys(this.gatewayConfig)
197
+ .map((chain) => `eip155:${chain}`)
198
+ .filter((chain) => !existingChains.includes(chain));
199
+ // Append new chains to the existing chains
200
+ proposal.params.requiredNamespaces.eip155.chains = [
201
+ ...existingChains,
202
+ ...newChains,
203
+ ];
204
+ return proposal;
205
+ }
175
206
  /**
176
207
  * Adds event bindings to a websocket connection
177
208
  * - on open, a `connect` event is dispatched via the socket to establish a bridge connection
@@ -185,6 +216,7 @@ class PortalConnect {
185
216
  * - Fires a subsequent call over the websocket to notify the proxy of the uri to connect to
186
217
  */
187
218
  socket.onopen = () => __awaiter(this, void 0, void 0, function* () {
219
+ this.connectionState = ConnectionStates.CONNECTING;
188
220
  const address = yield this.address;
189
221
  const { chainId } = this.provider;
190
222
  // Tell the proxy server where to connect to downstream
@@ -215,7 +247,12 @@ class PortalConnect {
215
247
  // Check if the event is an ErrorEvent
216
248
  errorMessage = event.message; // Access the error message from the ErrorEvent
217
249
  }
218
- this.emit('portal_connectError', new Error(errorMessage));
250
+ this.emit('portal_connectError', {
251
+ id: '0',
252
+ topic: this.topic,
253
+ params: new ConnectError(errorMessage, 500),
254
+ });
255
+ this.connectionState = ConnectionStates.DISCONNECTED;
219
256
  });
220
257
  /**
221
258
  * Handles all incoming messages over the websocket connection
@@ -244,14 +281,17 @@ class PortalConnect {
244
281
  const request = message.data;
245
282
  const params = request.params;
246
283
  // Sign the transaction and get back a transaction hash
247
- yield this.handleProviderRequest(params.request.method, params.request.params, request);
284
+ yield this.handleProviderRequest(params.request.method, params.request.params, params.chainId, request);
248
285
  break;
249
286
  case 'portal_dappSessionRequested':
250
287
  case 'portal_dappSessionRequestedV1':
251
288
  this.handleSessionRequest(message);
252
289
  break;
290
+ case 'portal_connectError':
291
+ this.emit('portal_connectError', message.data);
292
+ break;
253
293
  default:
254
- console.log(`Recieved unsupported event "${message.event}". Ignoring.`);
294
+ console.log(`Received unsupported event "${message.event}". Ignoring.`);
255
295
  break;
256
296
  }
257
297
  });
@@ -264,7 +304,7 @@ class PortalConnect {
264
304
  *
265
305
  * @returns The result of the provider request
266
306
  */
267
- handleProviderRequest(method, params, request) {
307
+ handleProviderRequest(method, params, chainId, request) {
268
308
  return __awaiter(this, void 0, void 0, function* () {
269
309
  // Bind to potential signing rejection events
270
310
  this.provider.on('portal_signingRejected', this.handleSigningRejected);
@@ -276,7 +316,7 @@ class PortalConnect {
276
316
  }
277
317
  });
278
318
  // Pass the request along to the provider
279
- yield this.provider.request({ method, params, connect: this });
319
+ yield this.provider.request({ method, params, chainId, connect: this });
280
320
  });
281
321
  }
282
322
  handleSessionApproved(data, request) {
@@ -371,6 +411,15 @@ class PortalConnect {
371
411
  }
372
412
  }
373
413
  }
414
+ class ConnectError extends Error {
415
+ constructor(message, code) {
416
+ super(message);
417
+ this.code = code;
418
+ // Set the prototype explicitly.
419
+ Object.setPrototypeOf(this, ConnectError.prototype);
420
+ }
421
+ }
422
+ exports.ConnectError = ConnectError;
374
423
  var ConnectionStates;
375
424
  (function (ConnectionStates) {
376
425
  ConnectionStates["CONNECTED"] = "CONNECTED";
package/lib/esm/index.js CHANGED
@@ -16,6 +16,7 @@ class PortalConnect {
16
16
  this.apiKey = apiKey;
17
17
  this.events = {};
18
18
  this.websocketServer = webSocketServer;
19
+ this.gatewayConfig = gatewayConfig;
19
20
  this.provider = new Provider({
20
21
  apiKey,
21
22
  chainId,
@@ -169,6 +170,36 @@ class PortalConnect {
169
170
  this.events[event] = this.events[event].filter(filterEventHandlers);
170
171
  }
171
172
  }
173
+ /**
174
+ * Adds chains from gateway config to the session proposal
175
+ * @param proposal
176
+ * @returns session proposal with chains from gateway config added
177
+ */
178
+ addChainsToProposal(proposal) {
179
+ if (!('params' in proposal) || !('requiredNamespaces' in proposal.params)) {
180
+ throw new Error('Invalid proposal structure.');
181
+ }
182
+ // Ensure eip155 is present in requiredNamespaces
183
+ if (!proposal.params.requiredNamespaces.eip155) {
184
+ proposal.params.requiredNamespaces.eip155 = {
185
+ chains: [],
186
+ methods: [],
187
+ events: [],
188
+ rpcMap: {},
189
+ };
190
+ }
191
+ // Get chains from gatewayConfig with 'eip155:' prefix, but only if they're not already in the proposal
192
+ const existingChains = proposal.params.requiredNamespaces.eip155.chains;
193
+ const newChains = Object.keys(this.gatewayConfig)
194
+ .map((chain) => `eip155:${chain}`)
195
+ .filter((chain) => !existingChains.includes(chain));
196
+ // Append new chains to the existing chains
197
+ proposal.params.requiredNamespaces.eip155.chains = [
198
+ ...existingChains,
199
+ ...newChains,
200
+ ];
201
+ return proposal;
202
+ }
172
203
  /**
173
204
  * Adds event bindings to a websocket connection
174
205
  * - on open, a `connect` event is dispatched via the socket to establish a bridge connection
@@ -182,6 +213,7 @@ class PortalConnect {
182
213
  * - Fires a subsequent call over the websocket to notify the proxy of the uri to connect to
183
214
  */
184
215
  socket.onopen = () => __awaiter(this, void 0, void 0, function* () {
216
+ this.connectionState = ConnectionStates.CONNECTING;
185
217
  const address = yield this.address;
186
218
  const { chainId } = this.provider;
187
219
  // Tell the proxy server where to connect to downstream
@@ -212,7 +244,12 @@ class PortalConnect {
212
244
  // Check if the event is an ErrorEvent
213
245
  errorMessage = event.message; // Access the error message from the ErrorEvent
214
246
  }
215
- this.emit('portal_connectError', new Error(errorMessage));
247
+ this.emit('portal_connectError', {
248
+ id: '0',
249
+ topic: this.topic,
250
+ params: new ConnectError(errorMessage, 500),
251
+ });
252
+ this.connectionState = ConnectionStates.DISCONNECTED;
216
253
  });
217
254
  /**
218
255
  * Handles all incoming messages over the websocket connection
@@ -241,14 +278,17 @@ class PortalConnect {
241
278
  const request = message.data;
242
279
  const params = request.params;
243
280
  // Sign the transaction and get back a transaction hash
244
- yield this.handleProviderRequest(params.request.method, params.request.params, request);
281
+ yield this.handleProviderRequest(params.request.method, params.request.params, params.chainId, request);
245
282
  break;
246
283
  case 'portal_dappSessionRequested':
247
284
  case 'portal_dappSessionRequestedV1':
248
285
  this.handleSessionRequest(message);
249
286
  break;
287
+ case 'portal_connectError':
288
+ this.emit('portal_connectError', message.data);
289
+ break;
250
290
  default:
251
- console.log(`Recieved unsupported event "${message.event}". Ignoring.`);
291
+ console.log(`Received unsupported event "${message.event}". Ignoring.`);
252
292
  break;
253
293
  }
254
294
  });
@@ -261,7 +301,7 @@ class PortalConnect {
261
301
  *
262
302
  * @returns The result of the provider request
263
303
  */
264
- handleProviderRequest(method, params, request) {
304
+ handleProviderRequest(method, params, chainId, request) {
265
305
  return __awaiter(this, void 0, void 0, function* () {
266
306
  // Bind to potential signing rejection events
267
307
  this.provider.on('portal_signingRejected', this.handleSigningRejected);
@@ -273,7 +313,7 @@ class PortalConnect {
273
313
  }
274
314
  });
275
315
  // Pass the request along to the provider
276
- yield this.provider.request({ method, params, connect: this });
316
+ yield this.provider.request({ method, params, chainId, connect: this });
277
317
  });
278
318
  }
279
319
  handleSessionApproved(data, request) {
@@ -368,6 +408,14 @@ class PortalConnect {
368
408
  }
369
409
  }
370
410
  }
411
+ export class ConnectError extends Error {
412
+ constructor(message, code) {
413
+ super(message);
414
+ this.code = code;
415
+ // Set the prototype explicitly.
416
+ Object.setPrototypeOf(this, ConnectError.prototype);
417
+ }
418
+ }
371
419
  export var ConnectionStates;
372
420
  (function (ConnectionStates) {
373
421
  ConnectionStates["CONNECTED"] = "CONNECTED";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portal-hq/connect",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "main": "lib/commonjs/index",
5
5
  "module": "lib/esm/index",
6
6
  "source": "src/index",
@@ -32,5 +32,5 @@
32
32
  "react": "*",
33
33
  "react-native": "*"
34
34
  },
35
- "gitHead": "8ce39a2867dacd6403db80b5fc51aa40c3b3c205"
35
+ "gitHead": "c333fe5280f16a5c5f70b7ae768f112baf7156ba"
36
36
  }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Provider } from '@portal-hq/provider'
2
2
  import type {
3
+ ErrorResult,
3
4
  EventHandler,
4
5
  PortalConnectOptions,
5
6
  ProviderRequestPayload,
@@ -9,12 +10,16 @@ import type {
9
10
  WebsocketMessage,
10
11
  } from '../types.d'
11
12
  import { IPortalProvider } from '@portal-hq/utils'
12
- import { SwitchEthereumChainParameter } from '@portal-hq/provider/types'
13
+ import {
14
+ GatewayConfig,
15
+ SwitchEthereumChainParameter,
16
+ } from '@portal-hq/provider/types'
13
17
 
14
18
  class PortalConnect {
15
19
  private apiKey: string
16
20
  private connectionState: ConnectionStates = ConnectionStates.DISCONNECTED
17
21
  private provider: IPortalProvider
22
+ private gatewayConfig: GatewayConfig
18
23
  private socket?: WebSocket
19
24
  private topic?: string
20
25
  private uri?: string
@@ -54,7 +59,7 @@ class PortalConnect {
54
59
  this.apiKey = apiKey
55
60
  this.events = {}
56
61
  this.websocketServer = webSocketServer
57
-
62
+ this.gatewayConfig = gatewayConfig
58
63
  this.provider = new Provider({
59
64
  apiKey,
60
65
  chainId,
@@ -224,6 +229,43 @@ class PortalConnect {
224
229
  }
225
230
  }
226
231
 
232
+ /**
233
+ * Adds chains from gateway config to the session proposal
234
+ * @param proposal
235
+ * @returns session proposal with chains from gateway config added
236
+ */
237
+ public addChainsToProposal(
238
+ proposal: SessionProposalOrMetadata,
239
+ ): SessionProposalOrMetadata {
240
+ if (!('params' in proposal) || !('requiredNamespaces' in proposal.params)) {
241
+ throw new Error('Invalid proposal structure.')
242
+ }
243
+
244
+ // Ensure eip155 is present in requiredNamespaces
245
+ if (!proposal.params.requiredNamespaces.eip155) {
246
+ proposal.params.requiredNamespaces.eip155 = {
247
+ chains: [],
248
+ methods: [],
249
+ events: [],
250
+ rpcMap: {},
251
+ }
252
+ }
253
+
254
+ // Get chains from gatewayConfig with 'eip155:' prefix, but only if they're not already in the proposal
255
+ const existingChains = proposal.params.requiredNamespaces.eip155.chains
256
+ const newChains = Object.keys(this.gatewayConfig)
257
+ .map((chain) => `eip155:${chain}`)
258
+ .filter((chain) => !existingChains.includes(chain))
259
+
260
+ // Append new chains to the existing chains
261
+ proposal.params.requiredNamespaces.eip155.chains = [
262
+ ...existingChains,
263
+ ...newChains,
264
+ ]
265
+
266
+ return proposal
267
+ }
268
+
227
269
  /**
228
270
  * Adds event bindings to a websocket connection
229
271
  * - on open, a `connect` event is dispatched via the socket to establish a bridge connection
@@ -237,6 +279,7 @@ class PortalConnect {
237
279
  * - Fires a subsequent call over the websocket to notify the proxy of the uri to connect to
238
280
  */
239
281
  socket.onopen = async () => {
282
+ this.connectionState = ConnectionStates.CONNECTING
240
283
  const address = await this.address
241
284
  const { chainId } = this.provider
242
285
 
@@ -275,7 +318,12 @@ class PortalConnect {
275
318
  errorMessage = event.message // Access the error message from the ErrorEvent
276
319
  }
277
320
 
278
- this.emit('portal_connectError', new Error(errorMessage))
321
+ this.emit('portal_connectError', {
322
+ id: '0',
323
+ topic: this.topic,
324
+ params: new ConnectError(errorMessage, 500),
325
+ } as ErrorResult)
326
+ this.connectionState = ConnectionStates.DISCONNECTED
279
327
  }
280
328
 
281
329
  /**
@@ -310,6 +358,7 @@ class PortalConnect {
310
358
  await this.handleProviderRequest(
311
359
  params.request.method,
312
360
  params.request.params,
361
+ params.chainId,
313
362
  request,
314
363
  )
315
364
  break
@@ -317,9 +366,12 @@ class PortalConnect {
317
366
  case 'portal_dappSessionRequestedV1':
318
367
  this.handleSessionRequest(message)
319
368
  break
369
+ case 'portal_connectError':
370
+ this.emit('portal_connectError', message.data as ErrorResult)
371
+ break
320
372
  default:
321
373
  console.log(
322
- `Recieved unsupported event "${message.event}". Ignoring.`,
374
+ `Received unsupported event "${message.event}". Ignoring.`,
323
375
  )
324
376
  break
325
377
  }
@@ -337,6 +389,7 @@ class PortalConnect {
337
389
  private async handleProviderRequest(
338
390
  method: string,
339
391
  params: any,
392
+ chainId: string,
340
393
  request: SessionRequest,
341
394
  ): Promise<void> {
342
395
  // Bind to potential signing rejection events
@@ -356,7 +409,7 @@ class PortalConnect {
356
409
  )
357
410
 
358
411
  // Pass the request along to the provider
359
- await this.provider.request({ method, params, connect: this })
412
+ await this.provider.request({ method, params, chainId, connect: this })
360
413
  }
361
414
 
362
415
  private async handleSessionApproved(
@@ -476,6 +529,17 @@ class PortalConnect {
476
529
  }
477
530
  }
478
531
 
532
+ export class ConnectError extends Error {
533
+ code: number
534
+
535
+ constructor(message: string, code: number) {
536
+ super(message)
537
+ this.code = code
538
+ // Set the prototype explicitly.
539
+ Object.setPrototypeOf(this, ConnectError.prototype)
540
+ }
541
+ }
542
+
479
543
  export enum ConnectionStates {
480
544
  CONNECTED = 'CONNECTED',
481
545
  CONNECTING = 'CONNECTING',
package/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import Portal from '@portal-hq/core'
2
2
  import { KeychainAdapter } from '@portal-hq/utils'
3
+ import { ConnectError } from 'src'
3
4
 
4
5
  export type EventHandler = (data: any) => void
5
6
  export type SessionProposalOrMetadata = SessionProposal | PeerMetadata
@@ -10,6 +11,12 @@ export interface ConnectRequest {
10
11
  topic: string
11
12
  }
12
13
 
14
+ export interface ErrorResult {
15
+ id: string
16
+ params: ConnectError
17
+ topic: string
18
+ }
19
+
13
20
  export interface ConnectResult {
14
21
  active: boolean
15
22
  expiry: number
@@ -114,5 +121,10 @@ export interface SigningResult {
114
121
 
115
122
  export interface WebsocketMessage {
116
123
  event: string
117
- data: ConnectResult | DisconnectResult | SessionRequest | SigningResult
124
+ data:
125
+ | ConnectResult
126
+ | DisconnectResult
127
+ | SessionRequest
128
+ | SigningResult
129
+ | ErrorResult
118
130
  }