@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.
- package/lib/commonjs/providers/index.js +116 -62
- package/lib/commonjs/signers/mpc.js +41 -16
- package/lib/esm/providers/index.js +117 -63
- package/lib/esm/signers/mpc.js +41 -16
- package/package.json +4 -4
- package/src/providers/index.ts +129 -71
- package/src/signers/mpc.ts +54 -24
- package/types.d.ts +6 -3
|
@@ -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 {
|
|
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
|
|
30
|
-
return this.keychain.
|
|
35
|
+
get addresses() {
|
|
36
|
+
return this.keychain.getAddresses();
|
|
31
37
|
}
|
|
32
|
-
get
|
|
33
|
-
return this.
|
|
38
|
+
get address() {
|
|
39
|
+
return this.keychain.getAddress();
|
|
34
40
|
}
|
|
35
41
|
constructor({
|
|
36
42
|
// Required
|
|
37
|
-
apiKey,
|
|
43
|
+
apiKey, keychain, gatewayConfig,
|
|
38
44
|
// Optional
|
|
39
|
-
|
|
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
|
|
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(
|
|
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: ${
|
|
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[
|
|
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: ${
|
|
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
|
-
|
|
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({
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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${
|
|
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
|
-
|
|
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:
|
|
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${
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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:
|
package/lib/esm/signers/mpc.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
52
|
+
const addresses = yield this.keychain.getAddresses();
|
|
53
|
+
const legacyAddress = yield this.keychain.getAddress();
|
|
52
54
|
const apiKey = provider.apiKey;
|
|
53
|
-
const { method,
|
|
55
|
+
const { method, chainId, curve, isRaw } = message;
|
|
54
56
|
switch (method) {
|
|
55
57
|
case 'eth_requestAccounts':
|
|
56
|
-
return [
|
|
58
|
+
return [(_a = addresses.eip155) !== null && _a !== void 0 ? _a : legacyAddress];
|
|
57
59
|
case 'eth_accounts':
|
|
58
|
-
return [
|
|
60
|
+
return [(_b = addresses.eip155) !== null && _b !== void 0 ? _b : legacyAddress];
|
|
59
61
|
default:
|
|
60
62
|
break;
|
|
61
63
|
}
|
|
62
|
-
const
|
|
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:
|
|
76
|
+
optimized: true,
|
|
77
|
+
curve,
|
|
78
|
+
chainId,
|
|
79
|
+
isRaw,
|
|
68
80
|
};
|
|
69
81
|
const stringifiedMetadata = JSON.stringify(metadata);
|
|
70
|
-
|
|
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
|
+
"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": "^
|
|
23
|
-
"@portal-hq/utils": "^
|
|
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": "
|
|
33
|
+
"gitHead": "a1c6f51ff156d3d1922cdaa29ee43fe4cfefd932"
|
|
34
34
|
}
|