@portal-hq/provider 0.2.16 → 0.2.18

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.
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const utils_1 = require("@portal-hq/utils");
13
- const requesters_1 = require("../requesters");
14
13
  const signers_1 = require("../signers");
15
14
  const passiveSignerMethods = [
16
15
  'eth_accounts',
@@ -32,8 +31,7 @@ class Provider {
32
31
  // Required options
33
32
  apiKey, chainId, keychain,
34
33
  // Optional options
35
- apiUrl = 'api.portalhq.io', autoApprove = false, enableMpc = true, mpcUrl = 'mpc.portalhq.io', gatewayConfig = {}, }) {
36
- this._address = '';
34
+ apiUrl = 'api.portalhq.io', autoApprove = false, mpcUrl = 'mpc.portalhq.io', gatewayConfig = {}, }) {
37
35
  // Handle required fields
38
36
  if (!apiKey || apiKey.length === 0) {
39
37
  throw new utils_1.InvalidApiKeyError();
@@ -49,47 +47,33 @@ class Provider {
49
47
  this.apiUrl = apiUrl;
50
48
  this.autoApprove = autoApprove;
51
49
  this.chainId = chainId;
50
+ this.connected = false;
52
51
  this.events = {};
53
- this.isMPC = enableMpc;
52
+ this.gatewayConfig = gatewayConfig;
53
+ this.isMPC = true;
54
54
  this.keychain = keychain;
55
- this.log = console;
56
55
  this.mpcUrl = mpcUrl;
57
- this.portal = new requesters_1.HttpRequester({
56
+ this.rpcUrl = this.getRpcUrl();
57
+ // Add a logger
58
+ this.log = console;
59
+ // Initialize Portal API HttpRequester
60
+ this.portal = new utils_1.HttpRequester({
58
61
  baseUrl: this.apiUrl,
59
62
  });
60
- // Handle RPC Initialization
61
- this.gatewayConfig = gatewayConfig;
62
- this.rpc = new requesters_1.HttpRequester({
63
- baseUrl: this.getRpcUrl(),
63
+ // Initialize Gateway HttpRequester
64
+ this.rpc = new utils_1.HttpRequester({
65
+ baseUrl: this.rpcUrl,
66
+ });
67
+ // Initialize an MpcSigner
68
+ this.signer = new signers_1.MpcSigner({
69
+ mpcUrl: this.mpcUrl,
70
+ keychain: this.keychain,
71
+ });
72
+ this.signer.getAddress().then((address) => {
73
+ if (typeof address !== 'undefined' && address.length) {
74
+ this.address = address;
75
+ }
64
76
  });
65
- if (this.isMPC) {
66
- // If MPC is enabled, initialize an MpcSigner
67
- this.signer = new signers_1.MpcSigner({
68
- mpcUrl: this.mpcUrl,
69
- keychain: this.keychain,
70
- });
71
- }
72
- else {
73
- // If MPC is disabled, initialize an HttpSigner, talking to whatever httpHost was provided
74
- this.signer = new signers_1.HttpSigner({
75
- keychain: this.keychain,
76
- portal: this.portal,
77
- });
78
- }
79
- this.dispatchConnect();
80
- }
81
- get address() {
82
- return this._address;
83
- }
84
- set address(value) {
85
- this._address = value;
86
- if (this.signer && this.isMPC) {
87
- ;
88
- this.signer._address = value;
89
- }
90
- }
91
- get rpcUrl() {
92
- return this.getRpcUrl();
93
77
  }
94
78
  /**
95
79
  * Invokes all registered event handlers with the data provided
@@ -110,6 +94,29 @@ class Provider {
110
94
  this.events[event] = handlers.filter((handler) => !handler.once);
111
95
  return this;
112
96
  }
97
+ /**
98
+ * Determines the RPC URL to be used for the current chain
99
+ *
100
+ * @returns string
101
+ */
102
+ getRpcUrl() {
103
+ if (typeof this.gatewayConfig === 'string') {
104
+ // If the gatewayConfig is just a static URL, return that
105
+ return this.gatewayConfig;
106
+ }
107
+ else if (typeof this.gatewayConfig === 'object' &&
108
+ !this.gatewayConfig.hasOwnProperty(this.chainId)) {
109
+ // If there's no explicit mapping for the current chainId, error out
110
+ throw new Error(`[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`);
111
+ }
112
+ // Get the entry for the current chainId from the gatewayConfig
113
+ const config = this.gatewayConfig[this.chainId];
114
+ if (typeof config === 'string') {
115
+ return config;
116
+ }
117
+ // If we got this far, there's no way to support the chain with the current config
118
+ throw new Error(`[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`);
119
+ }
113
120
  /**
114
121
  * Registers an event handler for the provided event
115
122
  *
@@ -154,7 +161,6 @@ class Provider {
154
161
  }
155
162
  removeEventListener(event, listenerToRemove) {
156
163
  if (!this.events[event]) {
157
- this.log.info(`[PortalProvider] Attempted to remove a listener from unregistered event '${event}'. Ignoring.`);
158
164
  return;
159
165
  }
160
166
  if (!listenerToRemove) {
@@ -201,7 +207,6 @@ class Provider {
201
207
  params,
202
208
  });
203
209
  if (transactionHash) {
204
- console.log(`Received transaction hash: `, transactionHash);
205
210
  this.emit('portal_signatureReceived', {
206
211
  method,
207
212
  params,
@@ -223,6 +228,12 @@ class Provider {
223
228
  return result;
224
229
  });
225
230
  }
231
+ setAddress(address) {
232
+ this.address = address;
233
+ if (!this.connected) {
234
+ this.dispatchConnect();
235
+ }
236
+ }
226
237
  /**
227
238
  * Updates the chainId of this instance and builds a new RPC HttpRequester for
228
239
  * the gateway used for the new chain
@@ -230,13 +241,22 @@ class Provider {
230
241
  * @param chainId A hex string of the chainId to use for this connection
231
242
  * @returns BaseProvider
232
243
  */
233
- updateChainId(chainId) {
244
+ setChainId(chainId) {
234
245
  return __awaiter(this, void 0, void 0, function* () {
246
+ // Disconnect from the current chain
247
+ this.connected = false;
248
+ // Update the chainId
235
249
  this.chainId = Number(`${chainId}`);
236
- this.rpc = new requesters_1.HttpRequester({
250
+ // Re-initialize the Gateway HttpRequester
251
+ this.rpc = new utils_1.HttpRequester({
237
252
  baseUrl: this.getRpcUrl(),
238
253
  });
239
- this.emit('chainChanged', { chainId });
254
+ // Emit event for update
255
+ this.emit('chainChanged', {
256
+ chainId: this.chainId,
257
+ });
258
+ // Dispatch 'connect' event
259
+ this.dispatchConnect();
240
260
  return this;
241
261
  });
242
262
  }
@@ -251,7 +271,8 @@ class Provider {
251
271
  if (this.autoApprove) {
252
272
  return true;
253
273
  }
254
- if (!this.events['portal_signingRequested']) {
274
+ const signingHandlers = this.events['portal_signingRequested'];
275
+ if (!signingHandlers || signingHandlers.length === 0) {
255
276
  throw new Error(`[PortalProvider] Auto-approve is disabled. Cannot perform signing requests without an event handler for the 'portal_signingRequested' event.`);
256
277
  }
257
278
  return new Promise((resolve) => {
@@ -260,7 +281,6 @@ class Provider {
260
281
  this.removeEventListener('portal_signingRejected');
261
282
  // If the signing has been approved, resolve to true
262
283
  this.once('portal_signingApproved', ({ method: approvedMethod, params: approvedParams }) => {
263
- console.log(`[PortalProvider] Signing Approved`, method, params);
264
284
  // Remove already used listeners
265
285
  this.removeEventListener('portal_signingApproved');
266
286
  this.removeEventListener('portal_signingRejected');
@@ -272,7 +292,6 @@ class Provider {
272
292
  });
273
293
  // If the signing request has been rejected, resolve to false
274
294
  this.once('portal_signingRejected', ({ method: rejectedMethod, params: rejectedParams }) => {
275
- console.log(`[PortalProvider] Signing Approved`, method, params);
276
295
  // Remove already used listeners
277
296
  this.removeEventListener('portal_signingApproved');
278
297
  this.removeEventListener('portal_signingRejected');
@@ -292,35 +311,12 @@ class Provider {
292
311
  }
293
312
  dispatchConnect() {
294
313
  return __awaiter(this, void 0, void 0, function* () {
295
- console.log(`[PortalProvider] Connected on chainId: 0x${this.chainId.toString(16)}`);
314
+ this.connected = true;
296
315
  this.emit('connect', {
297
316
  chainId: `0x${this.chainId.toString(16)}`,
298
317
  });
299
318
  });
300
319
  }
301
- /**
302
- * Determines the RPC URL to be used for the current chain
303
- *
304
- * @returns string
305
- */
306
- getRpcUrl() {
307
- if (typeof this.gatewayConfig === 'string') {
308
- // If the gatewayConfig is just a static URL, return that
309
- return this.gatewayConfig;
310
- }
311
- else if (typeof this.gatewayConfig === 'object' &&
312
- !this.gatewayConfig.hasOwnProperty(this.chainId)) {
313
- // If there's no explicit mapping for the current chainId, error out
314
- throw new Error(`[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`);
315
- }
316
- // Get the entry for the current chainId from the gatewayConfig
317
- const config = this.gatewayConfig[this.chainId];
318
- if (typeof config === 'string') {
319
- return config;
320
- }
321
- // If we got this far, there's no way to support the chain with the current config
322
- throw new Error(`[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`);
323
- }
324
320
  /**
325
321
  * Sends the provided request payload along to the RPC HttpRequester
326
322
  *
@@ -10,9 +10,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  class Signer {
13
+ getAddress() {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ throw new Error(`[Portal] getAddress() method must be implemented as a subclass of Signer`);
16
+ });
17
+ }
13
18
  sign(_, __) {
14
19
  return __awaiter(this, void 0, void 0, function* () {
15
- throw new Error('[Portal] sign() method must be implemented in a child of BaseSigner');
20
+ throw new Error('[Portal] sign() method must be implemented in a subclass of Signer');
16
21
  });
17
22
  }
18
23
  }
@@ -3,10 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.MpcSigner = exports.HttpSigner = exports.Signer = void 0;
6
+ exports.MpcSigner = exports.Signer = void 0;
7
7
  var abstract_1 = require("./abstract");
8
8
  Object.defineProperty(exports, "Signer", { enumerable: true, get: function () { return __importDefault(abstract_1).default; } });
9
- var http_1 = require("./http");
10
- Object.defineProperty(exports, "HttpSigner", { enumerable: true, get: function () { return __importDefault(http_1).default; } });
11
9
  var mpc_1 = require("./mpc");
12
10
  Object.defineProperty(exports, "MpcSigner", { enumerable: true, get: function () { return __importDefault(mpc_1).default; } });
@@ -13,7 +13,6 @@ const react_native_1 = require("react-native");
13
13
  const utils_1 = require("@portal-hq/utils");
14
14
  class MpcSigner {
15
15
  constructor(opts) {
16
- this._address = '';
17
16
  this.buildParams = (method, txParams) => {
18
17
  let params = txParams;
19
18
  switch (method) {
@@ -37,15 +36,17 @@ class MpcSigner {
37
36
  throw new Error(`[Portal.Provider.MpcSigner] The MPC module could not be found by the signer. This is usually an issue with React Native linking. Please verify that the 'PortalReactNative' module is properly linked to this project.`);
38
37
  }
39
38
  }
40
- get address() {
41
- return (() => __awaiter(this, void 0, void 0, function* () {
42
- if (this._address) {
43
- return this._address;
39
+ getAddress() {
40
+ return __awaiter(this, void 0, void 0, function* () {
41
+ if (this.address) {
42
+ return this.address;
44
43
  }
45
44
  const address = yield this.keychain.getAddress();
46
- this._address = address;
45
+ if (typeof address !== 'undefined' && address.length) {
46
+ this.address = address;
47
+ }
47
48
  return address;
48
- }))();
49
+ });
49
50
  }
50
51
  sign(message, provider) {
51
52
  return __awaiter(this, void 0, void 0, function* () {
@@ -60,11 +61,8 @@ class MpcSigner {
60
61
  default:
61
62
  break;
62
63
  }
63
- console.log(`[Portal:MpcSigner] Requesting signature from PortalMobileMpc for:`, JSON.stringify(message, null, 2));
64
64
  const dkg = yield this.keychain.getDkgResult();
65
- console.log(`[PortalMpcSigner] RPC URL: ${provider.rpcUrl}`);
66
65
  const result = yield this.mpc.sign(apiKey, this.mpcUrl, dkg, message.method, JSON.stringify(this.buildParams(method, params)), provider.rpcUrl, provider.chainId.toString());
67
- console.log(`[PortalMpcSigner] Result: `, result);
68
66
  const { data, error } = JSON.parse(String(result));
69
67
  if (error && error.length) {
70
68
  throw new utils_1.MpcSigningError(error);
@@ -7,9 +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 { InvalidApiKeyError, InvalidChainIdError, InvalidGatewayConfigError, ProviderRpcError, RpcErrorCodes, } from '@portal-hq/utils';
11
- import { HttpRequester } from '../requesters';
12
- import { HttpSigner, MpcSigner } from '../signers';
10
+ import { HttpRequester, InvalidApiKeyError, InvalidChainIdError, InvalidGatewayConfigError, ProviderRpcError, RpcErrorCodes, } from '@portal-hq/utils';
11
+ import { MpcSigner } from '../signers';
13
12
  const passiveSignerMethods = [
14
13
  'eth_accounts',
15
14
  'eth_chainId',
@@ -30,8 +29,7 @@ class Provider {
30
29
  // Required options
31
30
  apiKey, chainId, keychain,
32
31
  // Optional options
33
- apiUrl = 'api.portalhq.io', autoApprove = false, enableMpc = true, mpcUrl = 'mpc.portalhq.io', gatewayConfig = {}, }) {
34
- this._address = '';
32
+ apiUrl = 'api.portalhq.io', autoApprove = false, mpcUrl = 'mpc.portalhq.io', gatewayConfig = {}, }) {
35
33
  // Handle required fields
36
34
  if (!apiKey || apiKey.length === 0) {
37
35
  throw new InvalidApiKeyError();
@@ -47,47 +45,33 @@ class Provider {
47
45
  this.apiUrl = apiUrl;
48
46
  this.autoApprove = autoApprove;
49
47
  this.chainId = chainId;
48
+ this.connected = false;
50
49
  this.events = {};
51
- this.isMPC = enableMpc;
50
+ this.gatewayConfig = gatewayConfig;
51
+ this.isMPC = true;
52
52
  this.keychain = keychain;
53
- this.log = console;
54
53
  this.mpcUrl = mpcUrl;
54
+ this.rpcUrl = this.getRpcUrl();
55
+ // Add a logger
56
+ this.log = console;
57
+ // Initialize Portal API HttpRequester
55
58
  this.portal = new HttpRequester({
56
59
  baseUrl: this.apiUrl,
57
60
  });
58
- // Handle RPC Initialization
59
- this.gatewayConfig = gatewayConfig;
61
+ // Initialize Gateway HttpRequester
60
62
  this.rpc = new HttpRequester({
61
- baseUrl: this.getRpcUrl(),
63
+ baseUrl: this.rpcUrl,
64
+ });
65
+ // Initialize an MpcSigner
66
+ this.signer = new MpcSigner({
67
+ mpcUrl: this.mpcUrl,
68
+ keychain: this.keychain,
69
+ });
70
+ this.signer.getAddress().then((address) => {
71
+ if (typeof address !== 'undefined' && address.length) {
72
+ this.address = address;
73
+ }
62
74
  });
63
- if (this.isMPC) {
64
- // If MPC is enabled, initialize an MpcSigner
65
- this.signer = new MpcSigner({
66
- mpcUrl: this.mpcUrl,
67
- keychain: this.keychain,
68
- });
69
- }
70
- else {
71
- // If MPC is disabled, initialize an HttpSigner, talking to whatever httpHost was provided
72
- this.signer = new HttpSigner({
73
- keychain: this.keychain,
74
- portal: this.portal,
75
- });
76
- }
77
- this.dispatchConnect();
78
- }
79
- get address() {
80
- return this._address;
81
- }
82
- set address(value) {
83
- this._address = value;
84
- if (this.signer && this.isMPC) {
85
- ;
86
- this.signer._address = value;
87
- }
88
- }
89
- get rpcUrl() {
90
- return this.getRpcUrl();
91
75
  }
92
76
  /**
93
77
  * Invokes all registered event handlers with the data provided
@@ -108,6 +92,29 @@ class Provider {
108
92
  this.events[event] = handlers.filter((handler) => !handler.once);
109
93
  return this;
110
94
  }
95
+ /**
96
+ * Determines the RPC URL to be used for the current chain
97
+ *
98
+ * @returns string
99
+ */
100
+ getRpcUrl() {
101
+ if (typeof this.gatewayConfig === 'string') {
102
+ // If the gatewayConfig is just a static URL, return that
103
+ return this.gatewayConfig;
104
+ }
105
+ else if (typeof this.gatewayConfig === 'object' &&
106
+ !this.gatewayConfig.hasOwnProperty(this.chainId)) {
107
+ // If there's no explicit mapping for the current chainId, error out
108
+ throw new Error(`[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`);
109
+ }
110
+ // Get the entry for the current chainId from the gatewayConfig
111
+ const config = this.gatewayConfig[this.chainId];
112
+ if (typeof config === 'string') {
113
+ return config;
114
+ }
115
+ // If we got this far, there's no way to support the chain with the current config
116
+ throw new Error(`[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`);
117
+ }
111
118
  /**
112
119
  * Registers an event handler for the provided event
113
120
  *
@@ -152,7 +159,6 @@ class Provider {
152
159
  }
153
160
  removeEventListener(event, listenerToRemove) {
154
161
  if (!this.events[event]) {
155
- this.log.info(`[PortalProvider] Attempted to remove a listener from unregistered event '${event}'. Ignoring.`);
156
162
  return;
157
163
  }
158
164
  if (!listenerToRemove) {
@@ -199,7 +205,6 @@ class Provider {
199
205
  params,
200
206
  });
201
207
  if (transactionHash) {
202
- console.log(`Received transaction hash: `, transactionHash);
203
208
  this.emit('portal_signatureReceived', {
204
209
  method,
205
210
  params,
@@ -221,6 +226,12 @@ class Provider {
221
226
  return result;
222
227
  });
223
228
  }
229
+ setAddress(address) {
230
+ this.address = address;
231
+ if (!this.connected) {
232
+ this.dispatchConnect();
233
+ }
234
+ }
224
235
  /**
225
236
  * Updates the chainId of this instance and builds a new RPC HttpRequester for
226
237
  * the gateway used for the new chain
@@ -228,13 +239,22 @@ class Provider {
228
239
  * @param chainId A hex string of the chainId to use for this connection
229
240
  * @returns BaseProvider
230
241
  */
231
- updateChainId(chainId) {
242
+ setChainId(chainId) {
232
243
  return __awaiter(this, void 0, void 0, function* () {
244
+ // Disconnect from the current chain
245
+ this.connected = false;
246
+ // Update the chainId
233
247
  this.chainId = Number(`${chainId}`);
248
+ // Re-initialize the Gateway HttpRequester
234
249
  this.rpc = new HttpRequester({
235
250
  baseUrl: this.getRpcUrl(),
236
251
  });
237
- this.emit('chainChanged', { chainId });
252
+ // Emit event for update
253
+ this.emit('chainChanged', {
254
+ chainId: this.chainId,
255
+ });
256
+ // Dispatch 'connect' event
257
+ this.dispatchConnect();
238
258
  return this;
239
259
  });
240
260
  }
@@ -249,7 +269,8 @@ class Provider {
249
269
  if (this.autoApprove) {
250
270
  return true;
251
271
  }
252
- if (!this.events['portal_signingRequested']) {
272
+ const signingHandlers = this.events['portal_signingRequested'];
273
+ if (!signingHandlers || signingHandlers.length === 0) {
253
274
  throw new Error(`[PortalProvider] Auto-approve is disabled. Cannot perform signing requests without an event handler for the 'portal_signingRequested' event.`);
254
275
  }
255
276
  return new Promise((resolve) => {
@@ -258,7 +279,6 @@ class Provider {
258
279
  this.removeEventListener('portal_signingRejected');
259
280
  // If the signing has been approved, resolve to true
260
281
  this.once('portal_signingApproved', ({ method: approvedMethod, params: approvedParams }) => {
261
- console.log(`[PortalProvider] Signing Approved`, method, params);
262
282
  // Remove already used listeners
263
283
  this.removeEventListener('portal_signingApproved');
264
284
  this.removeEventListener('portal_signingRejected');
@@ -270,7 +290,6 @@ class Provider {
270
290
  });
271
291
  // If the signing request has been rejected, resolve to false
272
292
  this.once('portal_signingRejected', ({ method: rejectedMethod, params: rejectedParams }) => {
273
- console.log(`[PortalProvider] Signing Approved`, method, params);
274
293
  // Remove already used listeners
275
294
  this.removeEventListener('portal_signingApproved');
276
295
  this.removeEventListener('portal_signingRejected');
@@ -290,35 +309,12 @@ class Provider {
290
309
  }
291
310
  dispatchConnect() {
292
311
  return __awaiter(this, void 0, void 0, function* () {
293
- console.log(`[PortalProvider] Connected on chainId: 0x${this.chainId.toString(16)}`);
312
+ this.connected = true;
294
313
  this.emit('connect', {
295
314
  chainId: `0x${this.chainId.toString(16)}`,
296
315
  });
297
316
  });
298
317
  }
299
- /**
300
- * Determines the RPC URL to be used for the current chain
301
- *
302
- * @returns string
303
- */
304
- getRpcUrl() {
305
- if (typeof this.gatewayConfig === 'string') {
306
- // If the gatewayConfig is just a static URL, return that
307
- return this.gatewayConfig;
308
- }
309
- else if (typeof this.gatewayConfig === 'object' &&
310
- !this.gatewayConfig.hasOwnProperty(this.chainId)) {
311
- // If there's no explicit mapping for the current chainId, error out
312
- throw new Error(`[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`);
313
- }
314
- // Get the entry for the current chainId from the gatewayConfig
315
- const config = this.gatewayConfig[this.chainId];
316
- if (typeof config === 'string') {
317
- return config;
318
- }
319
- // If we got this far, there's no way to support the chain with the current config
320
- throw new Error(`[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`);
321
- }
322
318
  /**
323
319
  * Sends the provided request payload along to the RPC HttpRequester
324
320
  *
@@ -8,9 +8,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  class Signer {
11
+ getAddress() {
12
+ return __awaiter(this, void 0, void 0, function* () {
13
+ throw new Error(`[Portal] getAddress() method must be implemented as a subclass of Signer`);
14
+ });
15
+ }
11
16
  sign(_, __) {
12
17
  return __awaiter(this, void 0, void 0, function* () {
13
- throw new Error('[Portal] sign() method must be implemented in a child of BaseSigner');
18
+ throw new Error('[Portal] sign() method must be implemented in a subclass of Signer');
14
19
  });
15
20
  }
16
21
  }
@@ -1,3 +1,2 @@
1
1
  export { default as Signer } from './abstract';
2
- export { default as HttpSigner } from './http';
3
2
  export { default as MpcSigner } from './mpc';
@@ -11,7 +11,6 @@ import { NativeModules } from 'react-native';
11
11
  import { MpcSigningError, } from '@portal-hq/utils';
12
12
  class MpcSigner {
13
13
  constructor(opts) {
14
- this._address = '';
15
14
  this.buildParams = (method, txParams) => {
16
15
  let params = txParams;
17
16
  switch (method) {
@@ -35,15 +34,17 @@ class MpcSigner {
35
34
  throw new Error(`[Portal.Provider.MpcSigner] The MPC module could not be found by the signer. This is usually an issue with React Native linking. Please verify that the 'PortalReactNative' module is properly linked to this project.`);
36
35
  }
37
36
  }
38
- get address() {
39
- return (() => __awaiter(this, void 0, void 0, function* () {
40
- if (this._address) {
41
- return this._address;
37
+ getAddress() {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ if (this.address) {
40
+ return this.address;
42
41
  }
43
42
  const address = yield this.keychain.getAddress();
44
- this._address = address;
43
+ if (typeof address !== 'undefined' && address.length) {
44
+ this.address = address;
45
+ }
45
46
  return address;
46
- }))();
47
+ });
47
48
  }
48
49
  sign(message, provider) {
49
50
  return __awaiter(this, void 0, void 0, function* () {
@@ -58,11 +59,8 @@ class MpcSigner {
58
59
  default:
59
60
  break;
60
61
  }
61
- console.log(`[Portal:MpcSigner] Requesting signature from PortalMobileMpc for:`, JSON.stringify(message, null, 2));
62
62
  const dkg = yield this.keychain.getDkgResult();
63
- console.log(`[PortalMpcSigner] RPC URL: ${provider.rpcUrl}`);
64
63
  const result = yield this.mpc.sign(apiKey, this.mpcUrl, dkg, message.method, JSON.stringify(this.buildParams(method, params)), provider.rpcUrl, provider.chainId.toString());
65
- console.log(`[PortalMpcSigner] Result: `, result);
66
64
  const { data, error } = JSON.parse(String(result));
67
65
  if (error && error.length) {
68
66
  throw new MpcSigningError(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portal-hq/provider",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "license": "MIT",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/esm/index",
@@ -14,12 +14,18 @@
14
14
  "scripts": {
15
15
  "prepare": "yarn prepare:cjs && yarn prepare:esm",
16
16
  "prepare:cjs": "tsc --outDir lib/commonjs --module commonjs",
17
- "prepare:esm": "tsc --outDir lib/esm --module es2015 --target es2015"
17
+ "prepare:esm": "tsc --outDir lib/esm --module es2015 --target es2015",
18
+ "test": "jest"
18
19
  },
19
20
  "dependencies": {
20
- "@portal-hq/utils": "^0.2.16"
21
+ "@portal-hq/utils": "^0.2.17"
21
22
  },
22
23
  "devDependencies": {
24
+ "@babel/preset-typescript": "^7.18.6",
25
+ "@types/jest": "^29.2.0",
26
+ "jest": "^29.2.1",
27
+ "jest-environment-jsdom": "^29.2.2",
28
+ "ts-jest": "^29.0.3",
23
29
  "typescript": "^4.8.4"
24
30
  }
25
31
  }
@@ -1,4 +1,5 @@
1
1
  import {
2
+ HttpRequester,
2
3
  InvalidApiKeyError,
3
4
  InvalidChainIdError,
4
5
  InvalidGatewayConfigError,
@@ -7,8 +8,7 @@ import {
7
8
  RpcErrorCodes,
8
9
  } from '@portal-hq/utils'
9
10
 
10
- import { HttpRequester } from '../requesters'
11
- import { HttpSigner, MpcSigner, Signer } from '../signers'
11
+ import { MpcSigner, Signer } from '../signers'
12
12
  import {
13
13
  type EventHandler,
14
14
  type GatewayLike,
@@ -38,21 +38,21 @@ const signerMethods = [
38
38
  class Provider {
39
39
  public readonly mpcUrl: string
40
40
  public readonly portal: HttpRequester
41
- public apiKey: string
42
41
 
42
+ public address?: string
43
+ public apiKey: string
44
+ public apiUrl: string
45
+ public autoApprove?: boolean
43
46
  public chainId: number
44
- public isMPC?: boolean
45
-
46
- private _address: string = ''
47
- private apiUrl: string
48
- private autoApprove?: boolean
49
- private events: Record<string, RegisteredEventHandler[]>
50
- private keychain: KeychainAdapter
51
- private log: Console
52
- private gatewayConfig: GatewayLike
53
- private rpc: HttpRequester
54
-
55
- public signer?: Signer
47
+ public connected: boolean
48
+ public events: Record<string, RegisteredEventHandler[]>
49
+ public gatewayConfig: GatewayLike
50
+ public isMPC: boolean
51
+ public keychain: KeychainAdapter
52
+ public log: Console
53
+ public rpc: HttpRequester
54
+ public rpcUrl: string
55
+ public signer: Signer
56
56
 
57
57
  constructor({
58
58
  // Required options
@@ -63,7 +63,6 @@ class Provider {
63
63
  // Optional options
64
64
  apiUrl = 'api.portalhq.io',
65
65
  autoApprove = false,
66
- enableMpc = true,
67
66
  mpcUrl = 'mpc.portalhq.io',
68
67
  gatewayConfig = {},
69
68
  }: ProviderOptions) {
@@ -83,53 +82,38 @@ class Provider {
83
82
  this.apiUrl = apiUrl
84
83
  this.autoApprove = autoApprove
85
84
  this.chainId = chainId
85
+ this.connected = false
86
86
  this.events = {}
87
- this.isMPC = enableMpc
87
+ this.gatewayConfig = gatewayConfig
88
+ this.isMPC = true
88
89
  this.keychain = keychain
89
- this.log = console
90
90
  this.mpcUrl = mpcUrl
91
+ this.rpcUrl = this.getRpcUrl()
92
+
93
+ // Add a logger
94
+ this.log = console
95
+
96
+ // Initialize Portal API HttpRequester
91
97
  this.portal = new HttpRequester({
92
98
  baseUrl: this.apiUrl,
93
99
  })
94
100
 
95
- // Handle RPC Initialization
96
- this.gatewayConfig = gatewayConfig
97
-
101
+ // Initialize Gateway HttpRequester
98
102
  this.rpc = new HttpRequester({
99
- baseUrl: this.getRpcUrl(),
103
+ baseUrl: this.rpcUrl,
100
104
  })
101
105
 
102
- if (this.isMPC) {
103
- // If MPC is enabled, initialize an MpcSigner
104
- this.signer = new MpcSigner({
105
- mpcUrl: this.mpcUrl,
106
- keychain: this.keychain,
107
- })
108
- } else {
109
- // If MPC is disabled, initialize an HttpSigner, talking to whatever httpHost was provided
110
- this.signer = new HttpSigner({
111
- keychain: this.keychain,
112
- portal: this.portal,
113
- })
114
- }
115
-
116
- this.dispatchConnect()
117
- }
118
-
119
- get address(): string {
120
- return this._address
121
- }
122
-
123
- set address(value: string) {
124
- this._address = value
125
-
126
- if (this.signer && this.isMPC) {
127
- ;(this.signer as MpcSigner)._address = value
128
- }
129
- }
106
+ // Initialize an MpcSigner
107
+ this.signer = new MpcSigner({
108
+ mpcUrl: this.mpcUrl,
109
+ keychain: this.keychain,
110
+ })
130
111
 
131
- get rpcUrl(): string {
132
- return this.getRpcUrl()
112
+ this.signer.getAddress().then((address) => {
113
+ if (typeof address !== 'undefined' && address.length) {
114
+ this.address = address
115
+ }
116
+ })
133
117
  }
134
118
 
135
119
  /**
@@ -155,6 +139,38 @@ class Provider {
155
139
  return this
156
140
  }
157
141
 
142
+ /**
143
+ * Determines the RPC URL to be used for the current chain
144
+ *
145
+ * @returns string
146
+ */
147
+ public getRpcUrl(): string {
148
+ if (typeof this.gatewayConfig === 'string') {
149
+ // If the gatewayConfig is just a static URL, return that
150
+ return this.gatewayConfig
151
+ } else if (
152
+ typeof this.gatewayConfig === 'object' &&
153
+ !this.gatewayConfig.hasOwnProperty(this.chainId)
154
+ ) {
155
+ // If there's no explicit mapping for the current chainId, error out
156
+ throw new Error(
157
+ `[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`,
158
+ )
159
+ }
160
+
161
+ // Get the entry for the current chainId from the gatewayConfig
162
+ const config = this.gatewayConfig[this.chainId]
163
+
164
+ if (typeof config === 'string') {
165
+ return config
166
+ }
167
+
168
+ // If we got this far, there's no way to support the chain with the current config
169
+ throw new Error(
170
+ `[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`,
171
+ )
172
+ }
173
+
158
174
  /**
159
175
  * Registers an event handler for the provided event
160
176
  *
@@ -208,9 +224,6 @@ class Provider {
208
224
  listenerToRemove?: EventHandler,
209
225
  ): void {
210
226
  if (!this.events[event]) {
211
- this.log.info(
212
- `[PortalProvider] Attempted to remove a listener from unregistered event '${event}'. Ignoring.`,
213
- )
214
227
  return
215
228
  }
216
229
 
@@ -265,8 +278,6 @@ class Provider {
265
278
  })
266
279
 
267
280
  if (transactionHash) {
268
- console.log(`Received transaction hash: `, transactionHash)
269
-
270
281
  this.emit('portal_signatureReceived', {
271
282
  method,
272
283
  params,
@@ -289,6 +300,14 @@ class Provider {
289
300
  return result
290
301
  }
291
302
 
303
+ public setAddress(address: string): void {
304
+ this.address = address
305
+
306
+ if (!this.connected) {
307
+ this.dispatchConnect()
308
+ }
309
+ }
310
+
292
311
  /**
293
312
  * Updates the chainId of this instance and builds a new RPC HttpRequester for
294
313
  * the gateway used for the new chain
@@ -296,14 +315,25 @@ class Provider {
296
315
  * @param chainId A hex string of the chainId to use for this connection
297
316
  * @returns BaseProvider
298
317
  */
299
- public async updateChainId(chainId: string): Promise<Provider> {
318
+ public async setChainId(chainId: string): Promise<Provider> {
319
+ // Disconnect from the current chain
320
+ this.connected = false
321
+
322
+ // Update the chainId
300
323
  this.chainId = Number(`${chainId}`)
301
324
 
325
+ // Re-initialize the Gateway HttpRequester
302
326
  this.rpc = new HttpRequester({
303
327
  baseUrl: this.getRpcUrl(),
304
328
  })
305
329
 
306
- this.emit('chainChanged', { chainId } as SwitchEthereumChainParameter)
330
+ // Emit event for update
331
+ this.emit('chainChanged', {
332
+ chainId: this.chainId,
333
+ } as SwitchEthereumChainParameter)
334
+
335
+ // Dispatch 'connect' event
336
+ this.dispatchConnect()
307
337
 
308
338
  return this
309
339
  }
@@ -322,7 +352,9 @@ class Provider {
322
352
  return true
323
353
  }
324
354
 
325
- if (!this.events['portal_signingRequested']) {
355
+ const signingHandlers = this.events['portal_signingRequested']
356
+
357
+ if (!signingHandlers || signingHandlers.length === 0) {
326
358
  throw new Error(
327
359
  `[PortalProvider] Auto-approve is disabled. Cannot perform signing requests without an event handler for the 'portal_signingRequested' event.`,
328
360
  )
@@ -337,7 +369,6 @@ class Provider {
337
369
  this.once(
338
370
  'portal_signingApproved',
339
371
  ({ method: approvedMethod, params: approvedParams }) => {
340
- console.log(`[PortalProvider] Signing Approved`, method, params)
341
372
  // Remove already used listeners
342
373
  this.removeEventListener('portal_signingApproved')
343
374
  this.removeEventListener('portal_signingRejected')
@@ -356,7 +387,6 @@ class Provider {
356
387
  this.once(
357
388
  'portal_signingRejected',
358
389
  ({ method: rejectedMethod, params: rejectedParams }) => {
359
- console.log(`[PortalProvider] Signing Approved`, method, params)
360
390
  // Remove already used listeners
361
391
  this.removeEventListener('portal_signingApproved')
362
392
  this.removeEventListener('portal_signingRejected')
@@ -380,46 +410,12 @@ class Provider {
380
410
  }
381
411
 
382
412
  private async dispatchConnect(): Promise<void> {
383
- console.log(
384
- `[PortalProvider] Connected on chainId: 0x${this.chainId.toString(16)}`,
385
- )
413
+ this.connected = true
386
414
  this.emit('connect', {
387
415
  chainId: `0x${this.chainId.toString(16)}`,
388
416
  })
389
417
  }
390
418
 
391
- /**
392
- * Determines the RPC URL to be used for the current chain
393
- *
394
- * @returns string
395
- */
396
- private getRpcUrl(): string {
397
- if (typeof this.gatewayConfig === 'string') {
398
- // If the gatewayConfig is just a static URL, return that
399
- return this.gatewayConfig
400
- } else if (
401
- typeof this.gatewayConfig === 'object' &&
402
- !this.gatewayConfig.hasOwnProperty(this.chainId)
403
- ) {
404
- // If there's no explicit mapping for the current chainId, error out
405
- throw new Error(
406
- `[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`,
407
- )
408
- }
409
-
410
- // Get the entry for the current chainId from the gatewayConfig
411
- const config = this.gatewayConfig[this.chainId]
412
-
413
- if (typeof config === 'string') {
414
- return config
415
- }
416
-
417
- // If we got this far, there's no way to support the chain with the current config
418
- throw new Error(
419
- `[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`,
420
- )
421
- }
422
-
423
419
  /**
424
420
  * Sends the provided request payload along to the RPC HttpRequester
425
421
  *
@@ -2,9 +2,14 @@ import { SigningRequestArguments } from '@portal-hq/utils'
2
2
  import { type SignResult } from '../../types'
3
3
 
4
4
  abstract class Signer {
5
+ public async getAddress(): Promise<string> {
6
+ throw new Error(
7
+ `[Portal] getAddress() method must be implemented as a subclass of Signer`,
8
+ )
9
+ }
5
10
  public async sign(_: SigningRequestArguments, __?: any): Promise<SignResult> {
6
11
  throw new Error(
7
- '[Portal] sign() method must be implemented in a child of BaseSigner',
12
+ '[Portal] sign() method must be implemented in a subclass of Signer',
8
13
  )
9
14
  }
10
15
  }
@@ -1,3 +1,2 @@
1
1
  export { default as Signer } from './abstract'
2
- export { default as HttpSigner } from './http'
3
2
  export { default as MpcSigner } from './mpc'
@@ -15,10 +15,11 @@ import {
15
15
  } from '../../types'
16
16
 
17
17
  class MpcSigner implements Signer {
18
- public _address: string = ''
19
- private keychain: KeychainAdapter
20
18
  private mpc: PortalMobileMpc
21
- private mpcUrl: string
19
+
20
+ public address?: string
21
+ public keychain: KeychainAdapter
22
+ public mpcUrl: string
22
23
 
23
24
  constructor(opts: MpcSignerOptions) {
24
25
  this.keychain = opts.keychain
@@ -32,18 +33,18 @@ class MpcSigner implements Signer {
32
33
  }
33
34
  }
34
35
 
35
- public get address() {
36
- return (async () => {
37
- if (this._address) {
38
- return this._address
39
- }
36
+ public async getAddress(): Promise<string> {
37
+ if (this.address) {
38
+ return this.address
39
+ }
40
40
 
41
- const address = await this.keychain.getAddress()
41
+ const address = await this.keychain.getAddress()
42
42
 
43
- this._address = address
43
+ if (typeof address !== 'undefined' && address.length) {
44
+ this.address = address
45
+ }
44
46
 
45
- return address
46
- })()
47
+ return address
47
48
  }
48
49
 
49
50
  public async sign(
@@ -64,15 +65,8 @@ class MpcSigner implements Signer {
64
65
  break
65
66
  }
66
67
 
67
- console.log(
68
- `[Portal:MpcSigner] Requesting signature from PortalMobileMpc for:`,
69
- JSON.stringify(message, null, 2),
70
- )
71
-
72
68
  const dkg = await this.keychain.getDkgResult()
73
69
 
74
- console.log(`[PortalMpcSigner] RPC URL: ${provider.rpcUrl}`)
75
-
76
70
  const result = await this.mpc.sign(
77
71
  apiKey,
78
72
  this.mpcUrl,
@@ -83,8 +77,6 @@ class MpcSigner implements Signer {
83
77
  provider.chainId.toString(),
84
78
  )
85
79
 
86
- console.log(`[PortalMpcSigner] Result: `, result)
87
-
88
80
  const { data, error } = JSON.parse(String(result)) as SigningResponse
89
81
 
90
82
  if (error && error.length) {
package/types.d.ts CHANGED
@@ -14,15 +14,6 @@ export interface GatewayConfig {
14
14
  [key: number]: string
15
15
  }
16
16
 
17
- export interface HttpRequesterOptions {
18
- baseUrl: string
19
- }
20
-
21
- export interface HttpSignerOptions extends SignerOptions {
22
- keychain: KeychainAdapter
23
- portal: HttpRequester
24
- }
25
-
26
17
  export interface MpcSignerOptions extends SignerOptions {
27
18
  keychain: KeychainAdapter
28
19
  mpcUrl: string
@@ -88,5 +79,5 @@ export interface SigningResponse {
88
79
  }
89
80
 
90
81
  export interface SwitchEthereumChainParameter {
91
- chainId: string
82
+ chainId: number
92
83
  }
@@ -1,61 +0,0 @@
1
- import {
2
- HttpRequest,
3
- type HttpRequestOptions,
4
- type HttpOptions,
5
- type HttpRequesterOptions,
6
- } from '@portal-hq/utils'
7
-
8
- class HttpRequester {
9
- private baseUrl: string
10
-
11
- constructor({ baseUrl }: HttpRequesterOptions) {
12
- this.baseUrl = baseUrl.startsWith('https://')
13
- ? baseUrl
14
- : `https://${baseUrl}`
15
- }
16
-
17
- public async get<T>(path: string, options?: HttpOptions): Promise<T> {
18
- const requestOptions = {
19
- method: 'GET',
20
- url: `${this.baseUrl}${path}`,
21
- } as HttpRequestOptions
22
-
23
- if (options && options.headers) {
24
- requestOptions.headers = this.buildHeaders(options.headers)
25
- }
26
-
27
- const request = new HttpRequest(requestOptions)
28
- const response = (await request.send()) as T
29
-
30
- return response
31
- }
32
-
33
- public async post<T>(path: string, options?: HttpOptions): Promise<T> {
34
- const requestOptions = {
35
- method: 'POST',
36
- url: `${this.baseUrl}${path}`,
37
- } as HttpRequestOptions
38
-
39
- requestOptions.headers = this.buildHeaders(
40
- options && options.headers ? options.headers : {},
41
- )
42
-
43
- if (options && options.body) {
44
- requestOptions.body = options.body
45
- }
46
-
47
- const request = new HttpRequest(requestOptions)
48
- const response = (await request.send()) as T
49
-
50
- return response
51
- }
52
-
53
- private buildHeaders(headers: Record<string, any>): Record<string, any> {
54
- return {
55
- 'Content-Type': 'application/json',
56
- ...headers,
57
- }
58
- }
59
- }
60
-
61
- export default HttpRequester
@@ -1 +0,0 @@
1
- export { default as HttpRequester } from './http'
@@ -1,77 +0,0 @@
1
- import {
2
- KeychainAdapter,
3
- MissingOptionError,
4
- SigningRequestArguments,
5
- } from '@portal-hq/utils'
6
-
7
- import { type HttpSignerOptions, type SignResult } from '../../types'
8
-
9
- import Signer from './abstract'
10
- import { Provider } from '../index'
11
- import HttpRequester from '../requesters/http'
12
-
13
- class HttpSigner implements Signer {
14
- private portal: HttpRequester
15
- private keychain: KeychainAdapter
16
-
17
- constructor(opts: HttpSignerOptions) {
18
- if (!opts.portal) {
19
- throw new MissingOptionError({
20
- className: 'HttpSigner',
21
- option: 'portal',
22
- })
23
- }
24
-
25
- this.keychain = opts.keychain
26
- this.portal = opts.portal
27
- }
28
-
29
- public async sign(
30
- message: SigningRequestArguments,
31
- provider: Provider,
32
- ): Promise<any> {
33
- const address = await this.keychain.getAddress()
34
- const { chainId, method, params } = message
35
-
36
- switch (method) {
37
- case 'eth_requestAccounts':
38
- return [address]
39
- case 'eth_accounts':
40
- return [address]
41
- default:
42
- break
43
- }
44
-
45
- console.log(
46
- `[Portal:HttpSigner] Requesting signature from exchange for:`,
47
- JSON.stringify(
48
- {
49
- chainId,
50
- method,
51
- params: JSON.stringify([params]),
52
- },
53
- null,
54
- 2,
55
- ),
56
- )
57
-
58
- const signatureResponse = await this.portal.post<SignResult>(
59
- '/api/v1/clients/transactions/sign',
60
- {
61
- body: {
62
- chainId,
63
- method,
64
- params: JSON.stringify([params]),
65
- },
66
- headers: {
67
- Authorization: `Bearer ${provider.apiKey}`,
68
- 'Content-Type': 'application/json',
69
- },
70
- },
71
- )
72
-
73
- return signatureResponse
74
- }
75
- }
76
-
77
- export default HttpSigner