@portal-hq/provider 1.0.4 → 1.1.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 +46 -48
- package/lib/commonjs/requesters/http.js +52 -0
- package/lib/commonjs/requesters/index.js +8 -0
- package/lib/commonjs/signers/abstract.js +0 -5
- package/lib/commonjs/signers/http.js +56 -0
- package/lib/commonjs/signers/mpc.js +13 -18
- package/lib/esm/providers/index.js +46 -48
- package/lib/esm/requesters/http.js +50 -0
- package/lib/esm/requesters/index.js +1 -0
- package/lib/esm/signers/abstract.js +0 -5
- package/lib/esm/signers/http.js +54 -0
- package/lib/esm/signers/mpc.js +13 -18
- package/package.json +4 -3
- package/src/providers/index.ts +65 -68
- package/src/signers/abstract.ts +0 -5
- package/src/signers/mpc.ts +26 -27
- package/types.d.ts +13 -8
|
@@ -29,10 +29,10 @@ const signerMethods = [
|
|
|
29
29
|
];
|
|
30
30
|
class Provider {
|
|
31
31
|
constructor({
|
|
32
|
-
// Required
|
|
33
|
-
apiKey, chainId, keychain,
|
|
34
|
-
// Optional
|
|
35
|
-
|
|
32
|
+
// Required
|
|
33
|
+
apiKey, chainId, keychain, gatewayConfig,
|
|
34
|
+
// Optional
|
|
35
|
+
isSimulator = false, autoApprove = false, apiHost = 'api.portalhq.io', mpcHost = 'mpc.portalhq.io', version = 'v4', }) {
|
|
36
36
|
// Handle required fields
|
|
37
37
|
if (!apiKey || apiKey.length === 0) {
|
|
38
38
|
throw new utils_1.InvalidApiKeyError();
|
|
@@ -45,39 +45,37 @@ class Provider {
|
|
|
45
45
|
}
|
|
46
46
|
// Handle the stuff we can auto-set
|
|
47
47
|
this.apiKey = apiKey;
|
|
48
|
-
this.apiUrl = apiUrl;
|
|
49
48
|
this.autoApprove = autoApprove;
|
|
50
49
|
this.chainId = chainId;
|
|
51
|
-
this.connected = false;
|
|
52
50
|
this.events = {};
|
|
53
51
|
this.gatewayConfig = gatewayConfig;
|
|
54
|
-
this.
|
|
52
|
+
this.isSimulator = isSimulator;
|
|
55
53
|
this.keychain = keychain;
|
|
56
|
-
this.mpcUrl = mpcUrl;
|
|
57
|
-
this.rpcUrl = this.getRpcUrl();
|
|
58
|
-
this.mpcVersion = mpcVersion;
|
|
59
54
|
// Add a logger
|
|
60
55
|
this.log = console;
|
|
61
56
|
// Initialize Portal API HttpRequester
|
|
62
|
-
this.
|
|
63
|
-
baseUrl:
|
|
57
|
+
this.portalApi = new utils_1.HttpRequester({
|
|
58
|
+
baseUrl: apiHost.startsWith('localhost')
|
|
59
|
+
? `http://${apiHost}`
|
|
60
|
+
: `https://${apiHost}`,
|
|
64
61
|
});
|
|
65
62
|
// Initialize Gateway HttpRequester
|
|
66
|
-
this.
|
|
67
|
-
baseUrl: this.
|
|
63
|
+
this.gateway = new utils_1.HttpRequester({
|
|
64
|
+
baseUrl: this.getGatewayUrl(),
|
|
68
65
|
});
|
|
69
66
|
// Initialize an MpcSigner
|
|
70
67
|
this.signer = new signers_1.MpcSigner({
|
|
71
|
-
|
|
72
|
-
keychain:
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
this.signer.getAddress().then((address) => {
|
|
76
|
-
if (typeof address !== 'undefined' && address.length) {
|
|
77
|
-
this.address = address;
|
|
78
|
-
}
|
|
68
|
+
mpcHost: mpcHost,
|
|
69
|
+
keychain: keychain,
|
|
70
|
+
version: version,
|
|
79
71
|
});
|
|
80
72
|
}
|
|
73
|
+
get address() {
|
|
74
|
+
return this.keychain.getAddress(this.isSimulator);
|
|
75
|
+
}
|
|
76
|
+
get gatewayUrl() {
|
|
77
|
+
return this.getGatewayUrl();
|
|
78
|
+
}
|
|
81
79
|
/**
|
|
82
80
|
* Invokes all registered event handlers with the data provided
|
|
83
81
|
* - If any `once` handlers exist, they are removed after all handlers are invoked
|
|
@@ -102,7 +100,7 @@ class Provider {
|
|
|
102
100
|
*
|
|
103
101
|
* @returns string
|
|
104
102
|
*/
|
|
105
|
-
|
|
103
|
+
getGatewayUrl() {
|
|
106
104
|
if (typeof this.gatewayConfig === 'string') {
|
|
107
105
|
// If the gatewayConfig is just a static URL, return that
|
|
108
106
|
return this.gatewayConfig;
|
|
@@ -183,7 +181,7 @@ class Provider {
|
|
|
183
181
|
* @param args The arguments of the request being made
|
|
184
182
|
* @returns Promise<any>
|
|
185
183
|
*/
|
|
186
|
-
request({ method, params }) {
|
|
184
|
+
request({ method, params, connect, }) {
|
|
187
185
|
return __awaiter(this, void 0, void 0, function* () {
|
|
188
186
|
if (method === 'eth_chainId') {
|
|
189
187
|
return this.chainId;
|
|
@@ -208,6 +206,7 @@ class Provider {
|
|
|
208
206
|
const transactionHash = yield this.handleSigningRequests({
|
|
209
207
|
method,
|
|
210
208
|
params,
|
|
209
|
+
connect,
|
|
211
210
|
});
|
|
212
211
|
if (transactionHash) {
|
|
213
212
|
this.emit('portal_signatureReceived', {
|
|
@@ -231,28 +230,20 @@ class Provider {
|
|
|
231
230
|
return result;
|
|
232
231
|
});
|
|
233
232
|
}
|
|
234
|
-
_setAddress(address) {
|
|
235
|
-
this.address = address;
|
|
236
|
-
if (!this.connected) {
|
|
237
|
-
this.dispatchConnect();
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
233
|
/**
|
|
241
234
|
* Updates the chainId of this instance and builds a new RPC HttpRequester for
|
|
242
235
|
* the gateway used for the new chain
|
|
243
236
|
*
|
|
244
|
-
* @param chainId
|
|
237
|
+
* @param chainId The numerical ID of the chain to switch to
|
|
245
238
|
* @returns BaseProvider
|
|
246
239
|
*/
|
|
247
240
|
setChainId(chainId) {
|
|
248
241
|
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
-
// Disconnect from the current chain
|
|
250
|
-
this.connected = false;
|
|
251
242
|
// Update the chainId
|
|
252
|
-
this.chainId =
|
|
243
|
+
this.chainId = chainId;
|
|
253
244
|
// Re-initialize the Gateway HttpRequester
|
|
254
|
-
this.
|
|
255
|
-
baseUrl: this.
|
|
245
|
+
this.gateway = new utils_1.HttpRequester({
|
|
246
|
+
baseUrl: this.getGatewayUrl(),
|
|
256
247
|
});
|
|
257
248
|
// Emit event for update
|
|
258
249
|
this.emit('chainChanged', {
|
|
@@ -268,15 +259,15 @@ class Provider {
|
|
|
268
259
|
*
|
|
269
260
|
* @param args The arguments of the request being made
|
|
270
261
|
*/
|
|
271
|
-
getApproval({ method, params, }) {
|
|
262
|
+
getApproval({ method, params, connect, }) {
|
|
272
263
|
return __awaiter(this, void 0, void 0, function* () {
|
|
273
264
|
// If autoApprove is enabled, just resolve to true
|
|
274
265
|
if (this.autoApprove) {
|
|
275
266
|
return true;
|
|
276
267
|
}
|
|
277
268
|
const signingHandlers = this.events['portal_signingRequested'];
|
|
278
|
-
if (!signingHandlers || signingHandlers.length === 0) {
|
|
279
|
-
throw new Error(`[PortalProvider] Auto-approve is disabled. Cannot perform signing requests without an event handler for the 'portal_signingRequested' event.`);
|
|
269
|
+
if (!connect && (!signingHandlers || signingHandlers.length === 0)) {
|
|
270
|
+
throw new Error(`[PortalProvider] Auto-approve is disabled. Cannot perform Provider signing requests without an event handler for the 'portal_signingRequested' event.`);
|
|
280
271
|
}
|
|
281
272
|
return new Promise((resolve) => {
|
|
282
273
|
// Remove already used listeners
|
|
@@ -305,16 +296,23 @@ class Provider {
|
|
|
305
296
|
}
|
|
306
297
|
});
|
|
307
298
|
// Tell any listening clients that signing has been requested
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
299
|
+
if (connect) {
|
|
300
|
+
connect.emit('portalConnect_signingRequested', {
|
|
301
|
+
method,
|
|
302
|
+
params,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
this.emit('portal_signingRequested', {
|
|
307
|
+
method,
|
|
308
|
+
params,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
312
311
|
});
|
|
313
312
|
});
|
|
314
313
|
}
|
|
315
314
|
dispatchConnect() {
|
|
316
315
|
return __awaiter(this, void 0, void 0, function* () {
|
|
317
|
-
this.connected = true;
|
|
318
316
|
this.emit('connect', {
|
|
319
317
|
chainId: `0x${this.chainId.toString(16)}`,
|
|
320
318
|
});
|
|
@@ -329,7 +327,7 @@ class Provider {
|
|
|
329
327
|
handleGatewayRequests({ method, params, }) {
|
|
330
328
|
return __awaiter(this, void 0, void 0, function* () {
|
|
331
329
|
// Pass request off to the gateway
|
|
332
|
-
return yield this.
|
|
330
|
+
return yield this.gateway.post('', {
|
|
333
331
|
body: {
|
|
334
332
|
jsonrpc: '2.0',
|
|
335
333
|
id: this.chainId,
|
|
@@ -345,12 +343,12 @@ class Provider {
|
|
|
345
343
|
* @param args The arguments of the request being made
|
|
346
344
|
* @returns Promise<any>
|
|
347
345
|
*/
|
|
348
|
-
handleSigningRequests({ method, params, }) {
|
|
346
|
+
handleSigningRequests({ method, params, connect, }) {
|
|
349
347
|
var _a;
|
|
350
348
|
return __awaiter(this, void 0, void 0, function* () {
|
|
351
349
|
const isApproved = passiveSignerMethods.includes(method)
|
|
352
350
|
? true
|
|
353
|
-
: yield this.getApproval({ method, params });
|
|
351
|
+
: yield this.getApproval({ method, params, connect });
|
|
354
352
|
if (!isApproved) {
|
|
355
353
|
this.log.info(`[PortalProvider] Request for signing method '${method}' could not be completed because it was not approved by the user.`);
|
|
356
354
|
return;
|
|
@@ -369,7 +367,7 @@ class Provider {
|
|
|
369
367
|
const result = yield ((_a = this.signer) === null || _a === void 0 ? void 0 : _a.sign({ chainId: this.chainId, method, params }, this));
|
|
370
368
|
if (result) {
|
|
371
369
|
try {
|
|
372
|
-
yield this.
|
|
370
|
+
yield this.portalApi.post('/api/v1/analytics/track', {
|
|
373
371
|
headers: {
|
|
374
372
|
Authentication: `Bearer ${this.apiKey}`,
|
|
375
373
|
},
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const utils_1 = require("@portal-hq/utils");
|
|
13
|
+
class HttpRequester {
|
|
14
|
+
constructor({ baseUrl }) {
|
|
15
|
+
this.baseUrl = baseUrl.startsWith('https://')
|
|
16
|
+
? baseUrl
|
|
17
|
+
: `https://${baseUrl}`;
|
|
18
|
+
}
|
|
19
|
+
get(path, options) {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
const requestOptions = {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
url: `${this.baseUrl}${path}`,
|
|
24
|
+
};
|
|
25
|
+
if (options && options.headers) {
|
|
26
|
+
requestOptions.headers = this.buildHeaders(options.headers);
|
|
27
|
+
}
|
|
28
|
+
const request = new utils_1.HttpRequest(requestOptions);
|
|
29
|
+
const response = (yield request.send());
|
|
30
|
+
return response;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
post(path, options) {
|
|
34
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
const requestOptions = {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
url: `${this.baseUrl}${path}`,
|
|
38
|
+
};
|
|
39
|
+
requestOptions.headers = this.buildHeaders(options && options.headers ? options.headers : {});
|
|
40
|
+
if (options && options.body) {
|
|
41
|
+
requestOptions.body = options.body;
|
|
42
|
+
}
|
|
43
|
+
const request = new utils_1.HttpRequest(requestOptions);
|
|
44
|
+
const response = (yield request.send());
|
|
45
|
+
return response;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
buildHeaders(headers) {
|
|
49
|
+
return Object.assign({ 'Content-Type': 'application/json' }, headers);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.default = HttpRequester;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HttpRequester = void 0;
|
|
7
|
+
var http_1 = require("./http");
|
|
8
|
+
Object.defineProperty(exports, "HttpRequester", { enumerable: true, get: function () { return __importDefault(http_1).default; } });
|
|
@@ -10,11 +10,6 @@ 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
|
-
}
|
|
18
13
|
sign(_, __) {
|
|
19
14
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
15
|
throw new Error('[Portal] sign() method must be implemented in a subclass of Signer');
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const utils_1 = require("@portal-hq/utils");
|
|
13
|
+
class HttpSigner {
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
if (!opts.portal) {
|
|
16
|
+
throw new utils_1.MissingOptionError({
|
|
17
|
+
className: 'HttpSigner',
|
|
18
|
+
option: 'portal',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
this.keychain = opts.keychain;
|
|
22
|
+
this.portal = opts.portal;
|
|
23
|
+
}
|
|
24
|
+
sign(message, provider) {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
const address = yield this.keychain.getAddress();
|
|
27
|
+
const { chainId, method, params } = message;
|
|
28
|
+
switch (method) {
|
|
29
|
+
case 'eth_requestAccounts':
|
|
30
|
+
return [address];
|
|
31
|
+
case 'eth_accounts':
|
|
32
|
+
return [address];
|
|
33
|
+
default:
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
console.log(`[Portal:HttpSigner] Requesting signature from exchange for:`, JSON.stringify({
|
|
37
|
+
chainId,
|
|
38
|
+
method,
|
|
39
|
+
params: JSON.stringify([params]),
|
|
40
|
+
}, null, 2));
|
|
41
|
+
const signatureResponse = yield this.portal.post('/api/v1/clients/transactions/sign', {
|
|
42
|
+
body: {
|
|
43
|
+
chainId,
|
|
44
|
+
method,
|
|
45
|
+
params: JSON.stringify([params]),
|
|
46
|
+
},
|
|
47
|
+
headers: {
|
|
48
|
+
Authorization: `Bearer ${provider.apiKey}`,
|
|
49
|
+
'Content-Type': 'application/json',
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
return signatureResponse;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.default = HttpSigner;
|
|
@@ -12,8 +12,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const react_native_1 = require("react-native");
|
|
13
13
|
const utils_1 = require("@portal-hq/utils");
|
|
14
14
|
class MpcSigner {
|
|
15
|
-
constructor({ keychain,
|
|
16
|
-
this.
|
|
15
|
+
constructor({ keychain, isSimulator = false, mpcHost = 'mpc.portalhq.io', version = 'v4', }) {
|
|
16
|
+
this.version = 'v4';
|
|
17
17
|
this.buildParams = (method, txParams) => {
|
|
18
18
|
let params = txParams;
|
|
19
19
|
switch (method) {
|
|
@@ -32,29 +32,24 @@ class MpcSigner {
|
|
|
32
32
|
}
|
|
33
33
|
return params;
|
|
34
34
|
};
|
|
35
|
+
this.isSimulator = isSimulator;
|
|
35
36
|
this.keychain = keychain;
|
|
36
37
|
this.mpc = react_native_1.NativeModules.PortalMobileMpc;
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
38
|
+
this.mpcHost = mpcHost;
|
|
39
|
+
this.version = version;
|
|
39
40
|
if (!this.mpc) {
|
|
40
41
|
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.`);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const address = yield this.keychain.getAddress();
|
|
49
|
-
if (typeof address !== 'undefined' && address.length) {
|
|
50
|
-
this.address = address;
|
|
51
|
-
}
|
|
52
|
-
return address;
|
|
53
|
-
});
|
|
44
|
+
get address() {
|
|
45
|
+
return this.keychain.getAddress(this.isSimulator);
|
|
46
|
+
}
|
|
47
|
+
get signingShare() {
|
|
48
|
+
return this.keychain.getDkgResult(this.isSimulator);
|
|
54
49
|
}
|
|
55
50
|
sign(message, provider) {
|
|
56
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
-
const address = yield this.
|
|
52
|
+
const address = yield this.address;
|
|
58
53
|
const apiKey = provider.apiKey;
|
|
59
54
|
const { method, params } = message;
|
|
60
55
|
switch (method) {
|
|
@@ -65,8 +60,8 @@ class MpcSigner {
|
|
|
65
60
|
default:
|
|
66
61
|
break;
|
|
67
62
|
}
|
|
68
|
-
const
|
|
69
|
-
const result = yield this.mpc.sign(apiKey, this.
|
|
63
|
+
const signingShare = yield this.signingShare;
|
|
64
|
+
const result = yield this.mpc.sign(apiKey, this.mpcHost, signingShare, message.method, JSON.stringify(this.buildParams(method, params)), provider.gatewayUrl, provider.chainId.toString(), this.version);
|
|
70
65
|
const { data, error } = JSON.parse(String(result));
|
|
71
66
|
if (error && error.code > 0) {
|
|
72
67
|
throw new utils_1.PortalMpcError(error);
|
|
@@ -27,10 +27,10 @@ const signerMethods = [
|
|
|
27
27
|
];
|
|
28
28
|
class Provider {
|
|
29
29
|
constructor({
|
|
30
|
-
// Required
|
|
31
|
-
apiKey, chainId, keychain,
|
|
32
|
-
// Optional
|
|
33
|
-
|
|
30
|
+
// Required
|
|
31
|
+
apiKey, chainId, keychain, gatewayConfig,
|
|
32
|
+
// Optional
|
|
33
|
+
isSimulator = false, autoApprove = false, apiHost = 'api.portalhq.io', mpcHost = 'mpc.portalhq.io', version = 'v4', }) {
|
|
34
34
|
// Handle required fields
|
|
35
35
|
if (!apiKey || apiKey.length === 0) {
|
|
36
36
|
throw new InvalidApiKeyError();
|
|
@@ -43,39 +43,37 @@ class Provider {
|
|
|
43
43
|
}
|
|
44
44
|
// Handle the stuff we can auto-set
|
|
45
45
|
this.apiKey = apiKey;
|
|
46
|
-
this.apiUrl = apiUrl;
|
|
47
46
|
this.autoApprove = autoApprove;
|
|
48
47
|
this.chainId = chainId;
|
|
49
|
-
this.connected = false;
|
|
50
48
|
this.events = {};
|
|
51
49
|
this.gatewayConfig = gatewayConfig;
|
|
52
|
-
this.
|
|
50
|
+
this.isSimulator = isSimulator;
|
|
53
51
|
this.keychain = keychain;
|
|
54
|
-
this.mpcUrl = mpcUrl;
|
|
55
|
-
this.rpcUrl = this.getRpcUrl();
|
|
56
|
-
this.mpcVersion = mpcVersion;
|
|
57
52
|
// Add a logger
|
|
58
53
|
this.log = console;
|
|
59
54
|
// Initialize Portal API HttpRequester
|
|
60
|
-
this.
|
|
61
|
-
baseUrl:
|
|
55
|
+
this.portalApi = new HttpRequester({
|
|
56
|
+
baseUrl: apiHost.startsWith('localhost')
|
|
57
|
+
? `http://${apiHost}`
|
|
58
|
+
: `https://${apiHost}`,
|
|
62
59
|
});
|
|
63
60
|
// Initialize Gateway HttpRequester
|
|
64
|
-
this.
|
|
65
|
-
baseUrl: this.
|
|
61
|
+
this.gateway = new HttpRequester({
|
|
62
|
+
baseUrl: this.getGatewayUrl(),
|
|
66
63
|
});
|
|
67
64
|
// Initialize an MpcSigner
|
|
68
65
|
this.signer = new MpcSigner({
|
|
69
|
-
|
|
70
|
-
keychain:
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
this.signer.getAddress().then((address) => {
|
|
74
|
-
if (typeof address !== 'undefined' && address.length) {
|
|
75
|
-
this.address = address;
|
|
76
|
-
}
|
|
66
|
+
mpcHost: mpcHost,
|
|
67
|
+
keychain: keychain,
|
|
68
|
+
version: version,
|
|
77
69
|
});
|
|
78
70
|
}
|
|
71
|
+
get address() {
|
|
72
|
+
return this.keychain.getAddress(this.isSimulator);
|
|
73
|
+
}
|
|
74
|
+
get gatewayUrl() {
|
|
75
|
+
return this.getGatewayUrl();
|
|
76
|
+
}
|
|
79
77
|
/**
|
|
80
78
|
* Invokes all registered event handlers with the data provided
|
|
81
79
|
* - If any `once` handlers exist, they are removed after all handlers are invoked
|
|
@@ -100,7 +98,7 @@ class Provider {
|
|
|
100
98
|
*
|
|
101
99
|
* @returns string
|
|
102
100
|
*/
|
|
103
|
-
|
|
101
|
+
getGatewayUrl() {
|
|
104
102
|
if (typeof this.gatewayConfig === 'string') {
|
|
105
103
|
// If the gatewayConfig is just a static URL, return that
|
|
106
104
|
return this.gatewayConfig;
|
|
@@ -181,7 +179,7 @@ class Provider {
|
|
|
181
179
|
* @param args The arguments of the request being made
|
|
182
180
|
* @returns Promise<any>
|
|
183
181
|
*/
|
|
184
|
-
request({ method, params }) {
|
|
182
|
+
request({ method, params, connect, }) {
|
|
185
183
|
return __awaiter(this, void 0, void 0, function* () {
|
|
186
184
|
if (method === 'eth_chainId') {
|
|
187
185
|
return this.chainId;
|
|
@@ -206,6 +204,7 @@ class Provider {
|
|
|
206
204
|
const transactionHash = yield this.handleSigningRequests({
|
|
207
205
|
method,
|
|
208
206
|
params,
|
|
207
|
+
connect,
|
|
209
208
|
});
|
|
210
209
|
if (transactionHash) {
|
|
211
210
|
this.emit('portal_signatureReceived', {
|
|
@@ -229,28 +228,20 @@ class Provider {
|
|
|
229
228
|
return result;
|
|
230
229
|
});
|
|
231
230
|
}
|
|
232
|
-
_setAddress(address) {
|
|
233
|
-
this.address = address;
|
|
234
|
-
if (!this.connected) {
|
|
235
|
-
this.dispatchConnect();
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
231
|
/**
|
|
239
232
|
* Updates the chainId of this instance and builds a new RPC HttpRequester for
|
|
240
233
|
* the gateway used for the new chain
|
|
241
234
|
*
|
|
242
|
-
* @param chainId
|
|
235
|
+
* @param chainId The numerical ID of the chain to switch to
|
|
243
236
|
* @returns BaseProvider
|
|
244
237
|
*/
|
|
245
238
|
setChainId(chainId) {
|
|
246
239
|
return __awaiter(this, void 0, void 0, function* () {
|
|
247
|
-
// Disconnect from the current chain
|
|
248
|
-
this.connected = false;
|
|
249
240
|
// Update the chainId
|
|
250
|
-
this.chainId =
|
|
241
|
+
this.chainId = chainId;
|
|
251
242
|
// Re-initialize the Gateway HttpRequester
|
|
252
|
-
this.
|
|
253
|
-
baseUrl: this.
|
|
243
|
+
this.gateway = new HttpRequester({
|
|
244
|
+
baseUrl: this.getGatewayUrl(),
|
|
254
245
|
});
|
|
255
246
|
// Emit event for update
|
|
256
247
|
this.emit('chainChanged', {
|
|
@@ -266,15 +257,15 @@ class Provider {
|
|
|
266
257
|
*
|
|
267
258
|
* @param args The arguments of the request being made
|
|
268
259
|
*/
|
|
269
|
-
getApproval({ method, params, }) {
|
|
260
|
+
getApproval({ method, params, connect, }) {
|
|
270
261
|
return __awaiter(this, void 0, void 0, function* () {
|
|
271
262
|
// If autoApprove is enabled, just resolve to true
|
|
272
263
|
if (this.autoApprove) {
|
|
273
264
|
return true;
|
|
274
265
|
}
|
|
275
266
|
const signingHandlers = this.events['portal_signingRequested'];
|
|
276
|
-
if (!signingHandlers || signingHandlers.length === 0) {
|
|
277
|
-
throw new Error(`[PortalProvider] Auto-approve is disabled. Cannot perform signing requests without an event handler for the 'portal_signingRequested' event.`);
|
|
267
|
+
if (!connect && (!signingHandlers || signingHandlers.length === 0)) {
|
|
268
|
+
throw new Error(`[PortalProvider] Auto-approve is disabled. Cannot perform Provider signing requests without an event handler for the 'portal_signingRequested' event.`);
|
|
278
269
|
}
|
|
279
270
|
return new Promise((resolve) => {
|
|
280
271
|
// Remove already used listeners
|
|
@@ -303,16 +294,23 @@ class Provider {
|
|
|
303
294
|
}
|
|
304
295
|
});
|
|
305
296
|
// Tell any listening clients that signing has been requested
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
297
|
+
if (connect) {
|
|
298
|
+
connect.emit('portalConnect_signingRequested', {
|
|
299
|
+
method,
|
|
300
|
+
params,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
this.emit('portal_signingRequested', {
|
|
305
|
+
method,
|
|
306
|
+
params,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
310
309
|
});
|
|
311
310
|
});
|
|
312
311
|
}
|
|
313
312
|
dispatchConnect() {
|
|
314
313
|
return __awaiter(this, void 0, void 0, function* () {
|
|
315
|
-
this.connected = true;
|
|
316
314
|
this.emit('connect', {
|
|
317
315
|
chainId: `0x${this.chainId.toString(16)}`,
|
|
318
316
|
});
|
|
@@ -327,7 +325,7 @@ class Provider {
|
|
|
327
325
|
handleGatewayRequests({ method, params, }) {
|
|
328
326
|
return __awaiter(this, void 0, void 0, function* () {
|
|
329
327
|
// Pass request off to the gateway
|
|
330
|
-
return yield this.
|
|
328
|
+
return yield this.gateway.post('', {
|
|
331
329
|
body: {
|
|
332
330
|
jsonrpc: '2.0',
|
|
333
331
|
id: this.chainId,
|
|
@@ -343,12 +341,12 @@ class Provider {
|
|
|
343
341
|
* @param args The arguments of the request being made
|
|
344
342
|
* @returns Promise<any>
|
|
345
343
|
*/
|
|
346
|
-
handleSigningRequests({ method, params, }) {
|
|
344
|
+
handleSigningRequests({ method, params, connect, }) {
|
|
347
345
|
var _a;
|
|
348
346
|
return __awaiter(this, void 0, void 0, function* () {
|
|
349
347
|
const isApproved = passiveSignerMethods.includes(method)
|
|
350
348
|
? true
|
|
351
|
-
: yield this.getApproval({ method, params });
|
|
349
|
+
: yield this.getApproval({ method, params, connect });
|
|
352
350
|
if (!isApproved) {
|
|
353
351
|
this.log.info(`[PortalProvider] Request for signing method '${method}' could not be completed because it was not approved by the user.`);
|
|
354
352
|
return;
|
|
@@ -367,7 +365,7 @@ class Provider {
|
|
|
367
365
|
const result = yield ((_a = this.signer) === null || _a === void 0 ? void 0 : _a.sign({ chainId: this.chainId, method, params }, this));
|
|
368
366
|
if (result) {
|
|
369
367
|
try {
|
|
370
|
-
yield this.
|
|
368
|
+
yield this.portalApi.post('/api/v1/analytics/track', {
|
|
371
369
|
headers: {
|
|
372
370
|
Authentication: `Bearer ${this.apiKey}`,
|
|
373
371
|
},
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { HttpRequest, } from '@portal-hq/utils';
|
|
11
|
+
class HttpRequester {
|
|
12
|
+
constructor({ baseUrl }) {
|
|
13
|
+
this.baseUrl = baseUrl.startsWith('https://')
|
|
14
|
+
? baseUrl
|
|
15
|
+
: `https://${baseUrl}`;
|
|
16
|
+
}
|
|
17
|
+
get(path, options) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
const requestOptions = {
|
|
20
|
+
method: 'GET',
|
|
21
|
+
url: `${this.baseUrl}${path}`,
|
|
22
|
+
};
|
|
23
|
+
if (options && options.headers) {
|
|
24
|
+
requestOptions.headers = this.buildHeaders(options.headers);
|
|
25
|
+
}
|
|
26
|
+
const request = new HttpRequest(requestOptions);
|
|
27
|
+
const response = (yield request.send());
|
|
28
|
+
return response;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
post(path, options) {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
const requestOptions = {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
url: `${this.baseUrl}${path}`,
|
|
36
|
+
};
|
|
37
|
+
requestOptions.headers = this.buildHeaders(options && options.headers ? options.headers : {});
|
|
38
|
+
if (options && options.body) {
|
|
39
|
+
requestOptions.body = options.body;
|
|
40
|
+
}
|
|
41
|
+
const request = new HttpRequest(requestOptions);
|
|
42
|
+
const response = (yield request.send());
|
|
43
|
+
return response;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
buildHeaders(headers) {
|
|
47
|
+
return Object.assign({ 'Content-Type': 'application/json' }, headers);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export default HttpRequester;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as HttpRequester } from './http';
|
|
@@ -8,11 +8,6 @@ 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
|
-
}
|
|
16
11
|
sign(_, __) {
|
|
17
12
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18
13
|
throw new Error('[Portal] sign() method must be implemented in a subclass of Signer');
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { MissingOptionError, } from '@portal-hq/utils';
|
|
11
|
+
class HttpSigner {
|
|
12
|
+
constructor(opts) {
|
|
13
|
+
if (!opts.portal) {
|
|
14
|
+
throw new MissingOptionError({
|
|
15
|
+
className: 'HttpSigner',
|
|
16
|
+
option: 'portal',
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
this.keychain = opts.keychain;
|
|
20
|
+
this.portal = opts.portal;
|
|
21
|
+
}
|
|
22
|
+
sign(message, provider) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const address = yield this.keychain.getAddress();
|
|
25
|
+
const { chainId, method, params } = message;
|
|
26
|
+
switch (method) {
|
|
27
|
+
case 'eth_requestAccounts':
|
|
28
|
+
return [address];
|
|
29
|
+
case 'eth_accounts':
|
|
30
|
+
return [address];
|
|
31
|
+
default:
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
console.log(`[Portal:HttpSigner] Requesting signature from exchange for:`, JSON.stringify({
|
|
35
|
+
chainId,
|
|
36
|
+
method,
|
|
37
|
+
params: JSON.stringify([params]),
|
|
38
|
+
}, null, 2));
|
|
39
|
+
const signatureResponse = yield this.portal.post('/api/v1/clients/transactions/sign', {
|
|
40
|
+
body: {
|
|
41
|
+
chainId,
|
|
42
|
+
method,
|
|
43
|
+
params: JSON.stringify([params]),
|
|
44
|
+
},
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: `Bearer ${provider.apiKey}`,
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
return signatureResponse;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export default HttpSigner;
|
package/lib/esm/signers/mpc.js
CHANGED
|
@@ -10,8 +10,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { NativeModules } from 'react-native';
|
|
11
11
|
import { PortalMpcError, } from '@portal-hq/utils';
|
|
12
12
|
class MpcSigner {
|
|
13
|
-
constructor({ keychain,
|
|
14
|
-
this.
|
|
13
|
+
constructor({ keychain, isSimulator = false, mpcHost = 'mpc.portalhq.io', version = 'v4', }) {
|
|
14
|
+
this.version = 'v4';
|
|
15
15
|
this.buildParams = (method, txParams) => {
|
|
16
16
|
let params = txParams;
|
|
17
17
|
switch (method) {
|
|
@@ -30,29 +30,24 @@ class MpcSigner {
|
|
|
30
30
|
}
|
|
31
31
|
return params;
|
|
32
32
|
};
|
|
33
|
+
this.isSimulator = isSimulator;
|
|
33
34
|
this.keychain = keychain;
|
|
34
35
|
this.mpc = NativeModules.PortalMobileMpc;
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
36
|
+
this.mpcHost = mpcHost;
|
|
37
|
+
this.version = version;
|
|
37
38
|
if (!this.mpc) {
|
|
38
39
|
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.`);
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const address = yield this.keychain.getAddress();
|
|
47
|
-
if (typeof address !== 'undefined' && address.length) {
|
|
48
|
-
this.address = address;
|
|
49
|
-
}
|
|
50
|
-
return address;
|
|
51
|
-
});
|
|
42
|
+
get address() {
|
|
43
|
+
return this.keychain.getAddress(this.isSimulator);
|
|
44
|
+
}
|
|
45
|
+
get signingShare() {
|
|
46
|
+
return this.keychain.getDkgResult(this.isSimulator);
|
|
52
47
|
}
|
|
53
48
|
sign(message, provider) {
|
|
54
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
-
const address = yield this.
|
|
50
|
+
const address = yield this.address;
|
|
56
51
|
const apiKey = provider.apiKey;
|
|
57
52
|
const { method, params } = message;
|
|
58
53
|
switch (method) {
|
|
@@ -63,8 +58,8 @@ class MpcSigner {
|
|
|
63
58
|
default:
|
|
64
59
|
break;
|
|
65
60
|
}
|
|
66
|
-
const
|
|
67
|
-
const result = yield this.mpc.sign(apiKey, this.
|
|
61
|
+
const signingShare = yield this.signingShare;
|
|
62
|
+
const result = yield this.mpc.sign(apiKey, this.mpcHost, signingShare, message.method, JSON.stringify(this.buildParams(method, params)), provider.gatewayUrl, provider.chainId.toString(), this.version);
|
|
68
63
|
const { data, error } = JSON.parse(String(result));
|
|
69
64
|
if (error && error.code > 0) {
|
|
70
65
|
throw new PortalMpcError(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portal-hq/provider",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/esm/index",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"test": "jest"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@portal-hq/
|
|
22
|
+
"@portal-hq/connect": "^1.1.0",
|
|
23
|
+
"@portal-hq/utils": "^1.1.0"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@babel/preset-typescript": "^7.18.6",
|
|
@@ -29,5 +30,5 @@
|
|
|
29
30
|
"ts-jest": "^29.0.3",
|
|
30
31
|
"typescript": "^4.8.4"
|
|
31
32
|
},
|
|
32
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "b65beeaf1dd0423244a099aaa0f3d55734ae676a"
|
|
33
34
|
}
|
package/src/providers/index.ts
CHANGED
|
@@ -38,37 +38,40 @@ const signerMethods = [
|
|
|
38
38
|
]
|
|
39
39
|
|
|
40
40
|
class Provider {
|
|
41
|
-
public readonly mpcUrl: string
|
|
42
|
-
public readonly portal: HttpRequester
|
|
43
|
-
|
|
44
|
-
public address?: string
|
|
45
41
|
public apiKey: string
|
|
46
|
-
public
|
|
47
|
-
public autoApprove?: boolean
|
|
42
|
+
public autoApprove: boolean
|
|
48
43
|
public chainId: number
|
|
49
|
-
public connected: boolean
|
|
50
|
-
public events: Record<string, RegisteredEventHandler[]>
|
|
51
|
-
public gatewayConfig: GatewayLike
|
|
52
|
-
public isMPC: boolean
|
|
53
|
-
public keychain: KeychainAdapter
|
|
54
44
|
public log: Console
|
|
55
|
-
public
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
public gateway: HttpRequester
|
|
46
|
+
|
|
47
|
+
private events: Record<string, RegisteredEventHandler[]>
|
|
48
|
+
private gatewayConfig: GatewayLike
|
|
49
|
+
private isSimulator: boolean
|
|
50
|
+
private keychain: KeychainAdapter
|
|
51
|
+
private readonly portalApi: HttpRequester
|
|
52
|
+
private signer: Signer
|
|
53
|
+
|
|
54
|
+
public get address(): Promise<string | undefined> {
|
|
55
|
+
return this.keychain.getAddress(this.isSimulator)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public get gatewayUrl(): string {
|
|
59
|
+
return this.getGatewayUrl()
|
|
60
|
+
}
|
|
59
61
|
|
|
60
62
|
constructor({
|
|
61
|
-
// Required
|
|
63
|
+
// Required
|
|
62
64
|
apiKey,
|
|
63
65
|
chainId,
|
|
64
66
|
keychain,
|
|
67
|
+
gatewayConfig,
|
|
65
68
|
|
|
66
|
-
// Optional
|
|
67
|
-
|
|
69
|
+
// Optional
|
|
70
|
+
isSimulator = false,
|
|
68
71
|
autoApprove = false,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
apiHost = 'api.portalhq.io',
|
|
73
|
+
mpcHost = 'mpc.portalhq.io',
|
|
74
|
+
version = 'v4',
|
|
72
75
|
}: ProviderOptions) {
|
|
73
76
|
// Handle required fields
|
|
74
77
|
if (!apiKey || apiKey.length === 0) {
|
|
@@ -83,41 +86,33 @@ class Provider {
|
|
|
83
86
|
|
|
84
87
|
// Handle the stuff we can auto-set
|
|
85
88
|
this.apiKey = apiKey
|
|
86
|
-
this.apiUrl = apiUrl
|
|
87
89
|
this.autoApprove = autoApprove
|
|
88
90
|
this.chainId = chainId
|
|
89
|
-
this.connected = false
|
|
90
91
|
this.events = {}
|
|
91
92
|
this.gatewayConfig = gatewayConfig
|
|
92
|
-
this.
|
|
93
|
+
this.isSimulator = isSimulator
|
|
93
94
|
this.keychain = keychain
|
|
94
|
-
|
|
95
|
-
this.rpcUrl = this.getRpcUrl()
|
|
96
|
-
this.mpcVersion = mpcVersion
|
|
95
|
+
|
|
97
96
|
// Add a logger
|
|
98
97
|
this.log = console
|
|
99
98
|
|
|
100
99
|
// Initialize Portal API HttpRequester
|
|
101
|
-
this.
|
|
102
|
-
baseUrl:
|
|
100
|
+
this.portalApi = new HttpRequester({
|
|
101
|
+
baseUrl: apiHost.startsWith('localhost')
|
|
102
|
+
? `http://${apiHost}`
|
|
103
|
+
: `https://${apiHost}`,
|
|
103
104
|
})
|
|
104
105
|
|
|
105
106
|
// Initialize Gateway HttpRequester
|
|
106
|
-
this.
|
|
107
|
-
baseUrl: this.
|
|
107
|
+
this.gateway = new HttpRequester({
|
|
108
|
+
baseUrl: this.getGatewayUrl(),
|
|
108
109
|
})
|
|
109
110
|
|
|
110
111
|
// Initialize an MpcSigner
|
|
111
112
|
this.signer = new MpcSigner({
|
|
112
|
-
|
|
113
|
-
keychain:
|
|
114
|
-
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
this.signer.getAddress().then((address) => {
|
|
118
|
-
if (typeof address !== 'undefined' && address.length) {
|
|
119
|
-
this.address = address
|
|
120
|
-
}
|
|
113
|
+
mpcHost: mpcHost,
|
|
114
|
+
keychain: keychain,
|
|
115
|
+
version: version,
|
|
121
116
|
})
|
|
122
117
|
}
|
|
123
118
|
|
|
@@ -149,7 +144,7 @@ class Provider {
|
|
|
149
144
|
*
|
|
150
145
|
* @returns string
|
|
151
146
|
*/
|
|
152
|
-
public
|
|
147
|
+
public getGatewayUrl(): string {
|
|
153
148
|
if (typeof this.gatewayConfig === 'string') {
|
|
154
149
|
// If the gatewayConfig is just a static URL, return that
|
|
155
150
|
return this.gatewayConfig
|
|
@@ -252,7 +247,11 @@ class Provider {
|
|
|
252
247
|
* @param args The arguments of the request being made
|
|
253
248
|
* @returns Promise<any>
|
|
254
249
|
*/
|
|
255
|
-
public async request({
|
|
250
|
+
public async request({
|
|
251
|
+
method,
|
|
252
|
+
params,
|
|
253
|
+
connect,
|
|
254
|
+
}: RequestArguments): Promise<any> {
|
|
256
255
|
if (method === 'eth_chainId') {
|
|
257
256
|
return this.chainId
|
|
258
257
|
}
|
|
@@ -280,6 +279,7 @@ class Provider {
|
|
|
280
279
|
const transactionHash = await this.handleSigningRequests({
|
|
281
280
|
method,
|
|
282
281
|
params,
|
|
282
|
+
connect,
|
|
283
283
|
})
|
|
284
284
|
|
|
285
285
|
if (transactionHash) {
|
|
@@ -305,31 +305,20 @@ class Provider {
|
|
|
305
305
|
return result
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
public _setAddress(address: string): void {
|
|
309
|
-
this.address = address
|
|
310
|
-
|
|
311
|
-
if (!this.connected) {
|
|
312
|
-
this.dispatchConnect()
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
308
|
/**
|
|
317
309
|
* Updates the chainId of this instance and builds a new RPC HttpRequester for
|
|
318
310
|
* the gateway used for the new chain
|
|
319
311
|
*
|
|
320
|
-
* @param chainId
|
|
312
|
+
* @param chainId The numerical ID of the chain to switch to
|
|
321
313
|
* @returns BaseProvider
|
|
322
314
|
*/
|
|
323
|
-
public async setChainId(chainId:
|
|
324
|
-
// Disconnect from the current chain
|
|
325
|
-
this.connected = false
|
|
326
|
-
|
|
315
|
+
public async setChainId(chainId: number): Promise<Provider> {
|
|
327
316
|
// Update the chainId
|
|
328
|
-
this.chainId =
|
|
317
|
+
this.chainId = chainId
|
|
329
318
|
|
|
330
319
|
// Re-initialize the Gateway HttpRequester
|
|
331
|
-
this.
|
|
332
|
-
baseUrl: this.
|
|
320
|
+
this.gateway = new HttpRequester({
|
|
321
|
+
baseUrl: this.getGatewayUrl(),
|
|
333
322
|
})
|
|
334
323
|
|
|
335
324
|
// Emit event for update
|
|
@@ -351,6 +340,7 @@ class Provider {
|
|
|
351
340
|
protected async getApproval({
|
|
352
341
|
method,
|
|
353
342
|
params,
|
|
343
|
+
connect,
|
|
354
344
|
}: RequestArguments): Promise<boolean> {
|
|
355
345
|
// If autoApprove is enabled, just resolve to true
|
|
356
346
|
if (this.autoApprove) {
|
|
@@ -359,9 +349,9 @@ class Provider {
|
|
|
359
349
|
|
|
360
350
|
const signingHandlers = this.events['portal_signingRequested']
|
|
361
351
|
|
|
362
|
-
if (!signingHandlers || signingHandlers.length === 0) {
|
|
352
|
+
if (!connect && (!signingHandlers || signingHandlers.length === 0)) {
|
|
363
353
|
throw new Error(
|
|
364
|
-
`[PortalProvider] Auto-approve is disabled. Cannot perform signing requests without an event handler for the 'portal_signingRequested' event.`,
|
|
354
|
+
`[PortalProvider] Auto-approve is disabled. Cannot perform Provider signing requests without an event handler for the 'portal_signingRequested' event.`,
|
|
365
355
|
)
|
|
366
356
|
}
|
|
367
357
|
|
|
@@ -407,15 +397,21 @@ class Provider {
|
|
|
407
397
|
)
|
|
408
398
|
|
|
409
399
|
// Tell any listening clients that signing has been requested
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
400
|
+
if (connect) {
|
|
401
|
+
connect.emit('portalConnect_signingRequested', {
|
|
402
|
+
method,
|
|
403
|
+
params,
|
|
404
|
+
})
|
|
405
|
+
} else {
|
|
406
|
+
this.emit('portal_signingRequested', {
|
|
407
|
+
method,
|
|
408
|
+
params,
|
|
409
|
+
})
|
|
410
|
+
}
|
|
414
411
|
})
|
|
415
412
|
}
|
|
416
413
|
|
|
417
414
|
private async dispatchConnect(): Promise<void> {
|
|
418
|
-
this.connected = true
|
|
419
415
|
this.emit('connect', {
|
|
420
416
|
chainId: `0x${this.chainId.toString(16)}`,
|
|
421
417
|
})
|
|
@@ -432,7 +428,7 @@ class Provider {
|
|
|
432
428
|
params,
|
|
433
429
|
}: RequestArguments): Promise<any> {
|
|
434
430
|
// Pass request off to the gateway
|
|
435
|
-
return await this.
|
|
431
|
+
return await this.gateway.post<any>('', {
|
|
436
432
|
body: {
|
|
437
433
|
jsonrpc: '2.0',
|
|
438
434
|
id: this.chainId,
|
|
@@ -451,10 +447,11 @@ class Provider {
|
|
|
451
447
|
private async handleSigningRequests({
|
|
452
448
|
method,
|
|
453
449
|
params,
|
|
450
|
+
connect,
|
|
454
451
|
}: RequestArguments): Promise<any> {
|
|
455
452
|
const isApproved = passiveSignerMethods.includes(method)
|
|
456
453
|
? true
|
|
457
|
-
: await this.getApproval({ method, params })
|
|
454
|
+
: await this.getApproval({ method, params, connect })
|
|
458
455
|
|
|
459
456
|
if (!isApproved) {
|
|
460
457
|
this.log.info(
|
|
@@ -481,7 +478,7 @@ class Provider {
|
|
|
481
478
|
|
|
482
479
|
if (result) {
|
|
483
480
|
try {
|
|
484
|
-
await this.
|
|
481
|
+
await this.portalApi.post('/api/v1/analytics/track', {
|
|
485
482
|
headers: {
|
|
486
483
|
Authentication: `Bearer ${this.apiKey}`,
|
|
487
484
|
},
|
package/src/signers/abstract.ts
CHANGED
|
@@ -2,11 +2,6 @@ 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
|
-
}
|
|
10
5
|
public async sign(_: SigningRequestArguments, __?: any): Promise<SignResult> {
|
|
11
6
|
throw new Error(
|
|
12
7
|
'[Portal] sign() method must be implemented in a subclass of Signer',
|
package/src/signers/mpc.ts
CHANGED
|
@@ -15,18 +15,31 @@ import {
|
|
|
15
15
|
} from '../../types'
|
|
16
16
|
|
|
17
17
|
class MpcSigner implements Signer {
|
|
18
|
+
private isSimulator: boolean
|
|
19
|
+
private keychain: KeychainAdapter
|
|
18
20
|
private mpc: PortalMobileMpc
|
|
21
|
+
private mpcHost: string // should we add a default here mpc.portalhq.io
|
|
22
|
+
private version: string = 'v4'
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
private get address(): Promise<string | undefined> {
|
|
25
|
+
return this.keychain.getAddress(this.isSimulator)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private get signingShare(): Promise<string> {
|
|
29
|
+
return this.keychain.getDkgResult(this.isSimulator)
|
|
30
|
+
}
|
|
24
31
|
|
|
25
|
-
constructor({
|
|
32
|
+
constructor({
|
|
33
|
+
keychain,
|
|
34
|
+
isSimulator = false,
|
|
35
|
+
mpcHost = 'mpc.portalhq.io',
|
|
36
|
+
version = 'v4',
|
|
37
|
+
}: MpcSignerOptions) {
|
|
38
|
+
this.isSimulator = isSimulator
|
|
26
39
|
this.keychain = keychain
|
|
27
40
|
this.mpc = NativeModules.PortalMobileMpc
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
41
|
+
this.mpcHost = mpcHost
|
|
42
|
+
this.version = version
|
|
30
43
|
|
|
31
44
|
if (!this.mpc) {
|
|
32
45
|
throw new Error(
|
|
@@ -35,25 +48,11 @@ class MpcSigner implements Signer {
|
|
|
35
48
|
}
|
|
36
49
|
}
|
|
37
50
|
|
|
38
|
-
public async getAddress(): Promise<string> {
|
|
39
|
-
if (this.address) {
|
|
40
|
-
return this.address
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const address = await this.keychain.getAddress()
|
|
44
|
-
|
|
45
|
-
if (typeof address !== 'undefined' && address.length) {
|
|
46
|
-
this.address = address
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return address
|
|
50
|
-
}
|
|
51
|
-
|
|
52
51
|
public async sign(
|
|
53
52
|
message: SigningRequestArguments,
|
|
54
53
|
provider: Provider,
|
|
55
54
|
): Promise<any> {
|
|
56
|
-
const address = await this.
|
|
55
|
+
const address = await this.address
|
|
57
56
|
const apiKey = provider.apiKey
|
|
58
57
|
|
|
59
58
|
const { method, params } = message
|
|
@@ -67,17 +66,17 @@ class MpcSigner implements Signer {
|
|
|
67
66
|
break
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
const
|
|
69
|
+
const signingShare = await this.signingShare
|
|
71
70
|
|
|
72
71
|
const result = await this.mpc.sign(
|
|
73
72
|
apiKey,
|
|
74
|
-
this.
|
|
75
|
-
|
|
73
|
+
this.mpcHost,
|
|
74
|
+
signingShare,
|
|
76
75
|
message.method,
|
|
77
76
|
JSON.stringify(this.buildParams(method, params)),
|
|
78
|
-
provider.
|
|
77
|
+
provider.gatewayUrl,
|
|
79
78
|
provider.chainId.toString(),
|
|
80
|
-
this.
|
|
79
|
+
this.version,
|
|
81
80
|
)
|
|
82
81
|
|
|
83
82
|
const { data, error } = JSON.parse(String(result)) as SigningResponse
|
package/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Types
|
|
2
2
|
|
|
3
|
+
import PortalConnect from '@portal-hq/connect'
|
|
3
4
|
import { KeychainAdapter, type PortalError } from '@portal-hq/utils'
|
|
4
5
|
|
|
5
6
|
export type EventHandler = (data: any) => void
|
|
@@ -16,8 +17,11 @@ export interface GatewayConfig {
|
|
|
16
17
|
|
|
17
18
|
export interface MpcSignerOptions extends SignerOptions {
|
|
18
19
|
keychain: KeychainAdapter
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
// Optional
|
|
22
|
+
isSimulator?: boolean
|
|
23
|
+
mpcHost?: string
|
|
24
|
+
version?: string
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export interface PortalMobileMpc {
|
|
@@ -46,18 +50,18 @@ export interface PortalMobileMpc {
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
export interface ProviderOptions {
|
|
49
|
-
// Required
|
|
53
|
+
// Required
|
|
50
54
|
apiKey: string
|
|
51
55
|
chainId: number
|
|
52
56
|
keychain: KeychainAdapter
|
|
53
57
|
gatewayConfig: GatewayLike
|
|
54
58
|
|
|
55
|
-
// Optional
|
|
56
|
-
|
|
59
|
+
// Optional
|
|
60
|
+
isSimulator?: boolean
|
|
57
61
|
autoApprove?: boolean
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
apiHost?: string
|
|
63
|
+
mpcHost?: string
|
|
64
|
+
version?: string
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
export interface RegisteredEventHandler {
|
|
@@ -68,6 +72,7 @@ export interface RegisteredEventHandler {
|
|
|
68
72
|
export interface RequestArguments {
|
|
69
73
|
method: string
|
|
70
74
|
params?: unknown[] | SigningRequestParams
|
|
75
|
+
connect?: PortalConnect
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
export interface SignResult {
|