@portal-hq/provider 4.1.1 → 4.1.2
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 +12 -11
- package/lib/commonjs/signers/mpc.js +133 -45
- package/lib/esm/providers/index.js +12 -11
- package/lib/esm/signers/mpc.js +133 -45
- package/package.json +4 -4
- package/src/providers/index.ts +1 -0
- package/src/signers/mpc.ts +162 -60
- package/types.d.ts +12 -2
|
@@ -74,6 +74,7 @@ class Provider {
|
|
|
74
74
|
mpcHost,
|
|
75
75
|
keychain,
|
|
76
76
|
version,
|
|
77
|
+
portalApi: this.portalApi,
|
|
77
78
|
featureFlags,
|
|
78
79
|
});
|
|
79
80
|
}
|
|
@@ -187,8 +188,8 @@ class Provider {
|
|
|
187
188
|
* @param args The arguments of the request being made
|
|
188
189
|
* @returns Promise<any>
|
|
189
190
|
*/
|
|
190
|
-
request(
|
|
191
|
-
return __awaiter(this,
|
|
191
|
+
request({ method, params, chainId, connect, }) {
|
|
192
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
192
193
|
chainId = chainId !== null && chainId !== void 0 ? chainId : this.chainId;
|
|
193
194
|
if (!chainId) {
|
|
194
195
|
throw new Error('[PortalProvider] No chainId provided');
|
|
@@ -320,8 +321,8 @@ class Provider {
|
|
|
320
321
|
*
|
|
321
322
|
* @param args The arguments of the request being made
|
|
322
323
|
*/
|
|
323
|
-
getApproval(
|
|
324
|
-
return __awaiter(this,
|
|
324
|
+
getApproval({ method, params, chainId, connect, }) {
|
|
325
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
325
326
|
// If autoApprove is enabled, just resolve to true
|
|
326
327
|
if (this.autoApprove) {
|
|
327
328
|
return true;
|
|
@@ -386,8 +387,8 @@ class Provider {
|
|
|
386
387
|
* @param args The arguments of the request being made
|
|
387
388
|
* @returns Promise<any>
|
|
388
389
|
*/
|
|
389
|
-
handleGatewayRequests(
|
|
390
|
-
return __awaiter(this,
|
|
390
|
+
handleGatewayRequests({ method, params, chainId, }) {
|
|
391
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
391
392
|
const gatewayUrl = this.getGatewayUrl(chainId);
|
|
392
393
|
// Pass request off to the gateway
|
|
393
394
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
@@ -407,9 +408,9 @@ class Provider {
|
|
|
407
408
|
* @param args The arguments of the request being made
|
|
408
409
|
* @returns Promise<any>
|
|
409
410
|
*/
|
|
410
|
-
handleSigningRequests(
|
|
411
|
-
|
|
412
|
-
|
|
411
|
+
handleSigningRequests({ method, params, chainId, connect, }) {
|
|
412
|
+
var _a, _b;
|
|
413
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
413
414
|
const isApproved = passiveSignerMethods.includes(method)
|
|
414
415
|
? true
|
|
415
416
|
: yield this.getApproval({ method, params, chainId, connect });
|
|
@@ -440,7 +441,7 @@ class Provider {
|
|
|
440
441
|
case 'sol_signAndSendTransaction':
|
|
441
442
|
case 'sol_signMessage':
|
|
442
443
|
case 'sol_signTransaction': {
|
|
443
|
-
const result = yield ((
|
|
444
|
+
const result = yield ((_a = this.signer) === null || _a === void 0 ? void 0 : _a.sign({
|
|
444
445
|
chainId: `${namespace}:${reference}`,
|
|
445
446
|
method,
|
|
446
447
|
params,
|
|
@@ -450,7 +451,7 @@ class Provider {
|
|
|
450
451
|
return result;
|
|
451
452
|
}
|
|
452
453
|
case 'raw_sign': {
|
|
453
|
-
const result = yield ((
|
|
454
|
+
const result = yield ((_b = this.signer) === null || _b === void 0 ? void 0 : _b.sign({
|
|
454
455
|
chainId: '',
|
|
455
456
|
method: '',
|
|
456
457
|
params,
|
|
@@ -12,8 +12,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const core_1 = require("@portal-hq/core");
|
|
13
13
|
const utils_1 = require("@portal-hq/utils");
|
|
14
14
|
const react_native_1 = require("react-native");
|
|
15
|
+
var Operation;
|
|
16
|
+
(function (Operation) {
|
|
17
|
+
Operation["SIGN"] = "sign";
|
|
18
|
+
Operation["RAW_SIGN"] = "raw_sign";
|
|
19
|
+
})(Operation || (Operation = {}));
|
|
15
20
|
class MpcSigner {
|
|
16
|
-
constructor({ keychain, mpcHost = 'mpc.portalhq.io', version = 'v6', featureFlags = {}, }) {
|
|
21
|
+
constructor({ keychain, mpcHost = 'mpc.portalhq.io', version = 'v6', portalApi, featureFlags = {}, }) {
|
|
17
22
|
this.version = 'v6';
|
|
18
23
|
this.buildParams = (method, txParams) => {
|
|
19
24
|
let params = txParams;
|
|
@@ -44,61 +49,144 @@ class MpcSigner {
|
|
|
44
49
|
this.mpc = react_native_1.NativeModules.PortalMobileMpc;
|
|
45
50
|
this.mpcHost = mpcHost;
|
|
46
51
|
this.version = version;
|
|
52
|
+
this.portalApi = portalApi;
|
|
47
53
|
if (!this.mpc) {
|
|
48
54
|
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.`);
|
|
49
55
|
}
|
|
50
56
|
}
|
|
51
57
|
sign(message, provider) {
|
|
52
58
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return [eip155Address];
|
|
61
|
-
default:
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
const shares = yield this.keychain.getShares();
|
|
65
|
-
let signingShare = shares.secp256k1.share;
|
|
66
|
-
if (curve === core_1.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
|
-
}
|
|
72
|
-
const metadata = {
|
|
73
|
-
clientPlatform: 'REACT_NATIVE',
|
|
74
|
-
clientPlatformVersion: (0, utils_1.getClientPlatformVersion)(),
|
|
75
|
-
isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
|
|
76
|
-
mpcServerVersion: this.version,
|
|
77
|
-
optimized: true,
|
|
78
|
-
curve,
|
|
79
|
-
chainId,
|
|
80
|
-
isRaw,
|
|
59
|
+
// Always track metrics, but only send if feature flag is enabled
|
|
60
|
+
const shouldSendMetrics = this.featureFlags.enableSdkPerformanceMetrics === true;
|
|
61
|
+
const signStartTime = performance.now();
|
|
62
|
+
const preOperationStartTime = performance.now();
|
|
63
|
+
const metrics = {
|
|
64
|
+
operation: Operation.SIGN,
|
|
65
|
+
hasError: false,
|
|
81
66
|
};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
67
|
+
try {
|
|
68
|
+
const eip155Address = yield this.keychain.getEip155Address();
|
|
69
|
+
const apiKey = provider.apiKey;
|
|
70
|
+
const { method, chainId, curve, isRaw } = message;
|
|
71
|
+
// Add chainId to metrics
|
|
72
|
+
if (chainId) {
|
|
73
|
+
metrics.chainId = chainId;
|
|
74
|
+
}
|
|
75
|
+
switch (method) {
|
|
76
|
+
case 'eth_requestAccounts':
|
|
77
|
+
return [eip155Address];
|
|
78
|
+
case 'eth_accounts':
|
|
79
|
+
return [eip155Address];
|
|
80
|
+
default:
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
const shares = yield this.keychain.getShares();
|
|
84
|
+
let signingShare = shares.secp256k1.share;
|
|
85
|
+
if (curve === core_1.PortalCurve.ED25519) {
|
|
86
|
+
if (!shares.ed25519) {
|
|
87
|
+
throw new Error('[Portal.Provider.MpcSigner] The ED25519 share is missing from the keychain.');
|
|
88
|
+
}
|
|
89
|
+
signingShare = shares.ed25519.share;
|
|
90
|
+
}
|
|
91
|
+
const metadata = {
|
|
92
|
+
clientPlatform: 'REACT_NATIVE',
|
|
93
|
+
clientPlatformVersion: (0, utils_1.getClientPlatformVersion)(),
|
|
94
|
+
isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
|
|
95
|
+
mpcServerVersion: this.version,
|
|
96
|
+
optimized: true,
|
|
97
|
+
curve,
|
|
98
|
+
chainId,
|
|
99
|
+
isRaw,
|
|
100
|
+
};
|
|
101
|
+
const stringifiedMetadata = JSON.stringify(metadata);
|
|
102
|
+
let formattedParams;
|
|
103
|
+
let rpcUrl;
|
|
104
|
+
if (isRaw) {
|
|
105
|
+
formattedParams = this.buildParams(method, message.params);
|
|
106
|
+
rpcUrl = '';
|
|
107
|
+
metrics.operation = Operation.RAW_SIGN;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
formattedParams = JSON.stringify(this.buildParams(method, message.params));
|
|
111
|
+
rpcUrl = provider.getGatewayUrl(chainId);
|
|
112
|
+
}
|
|
113
|
+
if (typeof formattedParams !== 'string') {
|
|
114
|
+
throw new Error(`[Portal.Provider.MpcSigner] The formatted params for the signing request could not be converted to a string. The params were: ${formattedParams}`);
|
|
115
|
+
}
|
|
116
|
+
// Record pre-operation time
|
|
117
|
+
metrics.sdkPreOperationMs = performance.now() - preOperationStartTime;
|
|
118
|
+
// Measure MPC signing operation time
|
|
119
|
+
const mpcSignStartTime = performance.now();
|
|
120
|
+
const result = yield this.mpc.sign(apiKey, this.mpcHost, JSON.stringify(signingShare), message.method, formattedParams, rpcUrl, chainId, stringifiedMetadata);
|
|
121
|
+
// Post-operation processing time starts
|
|
122
|
+
const postOperationStartTime = performance.now();
|
|
123
|
+
// Record native call time
|
|
124
|
+
metrics.mpcNativeCallMs = performance.now() - mpcSignStartTime;
|
|
125
|
+
// Parse result and extract binary metrics if available
|
|
126
|
+
const parsedResponse = JSON.parse(String(result));
|
|
127
|
+
const { data, error, meta } = parsedResponse;
|
|
128
|
+
// Add binary metrics to our metrics object
|
|
129
|
+
if (meta === null || meta === void 0 ? void 0 : meta.metrics) {
|
|
130
|
+
if (meta.metrics.wsConnectDurationMs) {
|
|
131
|
+
metrics.sdkBinaryWSConnectMs = meta.metrics.wsConnectDurationMs;
|
|
132
|
+
}
|
|
133
|
+
if (meta.metrics.operationDurationMs) {
|
|
134
|
+
metrics.sdkBinaryOperationMs = meta.metrics.operationDurationMs;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if ((error === null || error === void 0 ? void 0 : error.code) > 0) {
|
|
138
|
+
throw new utils_1.PortalMpcError(error);
|
|
139
|
+
}
|
|
140
|
+
// Record post-operation time
|
|
141
|
+
metrics.sdkPostOperationMs = performance.now() - postOperationStartTime;
|
|
142
|
+
// Calculate total SDK signing time
|
|
143
|
+
metrics.sdkOperationMs = performance.now() - signStartTime;
|
|
144
|
+
// Only send metrics if the feature flag is enabled
|
|
145
|
+
if (shouldSendMetrics && this.portalApi) {
|
|
146
|
+
try {
|
|
147
|
+
yield this.sendMetrics(metrics, apiKey);
|
|
148
|
+
}
|
|
149
|
+
catch (_a) {
|
|
150
|
+
// No-op
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return data;
|
|
88
154
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
155
|
+
catch (error) {
|
|
156
|
+
// Calculate total time even in error case
|
|
157
|
+
metrics.sdkOperationMs = performance.now() - signStartTime;
|
|
158
|
+
// Only send metrics if the feature flag is enabled
|
|
159
|
+
if (shouldSendMetrics) {
|
|
160
|
+
const apiKey = provider.apiKey;
|
|
161
|
+
metrics.hasError = true;
|
|
162
|
+
try {
|
|
163
|
+
yield this.sendMetrics(metrics, apiKey);
|
|
164
|
+
}
|
|
165
|
+
catch (_b) {
|
|
166
|
+
// No-op
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
92
170
|
}
|
|
93
|
-
|
|
94
|
-
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// Add debug logs to sendMetrics method too
|
|
174
|
+
sendMetrics(metrics, apiKey) {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
try {
|
|
177
|
+
if (this.portalApi) {
|
|
178
|
+
yield this.portalApi.post('/api/v3/clients/me/sdk/metrics', {
|
|
179
|
+
headers: {
|
|
180
|
+
Authorization: `Bearer ${apiKey}`,
|
|
181
|
+
'Content-Type': 'application/json',
|
|
182
|
+
},
|
|
183
|
+
body: metrics,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
95
186
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (error && error.code > 0) {
|
|
99
|
-
throw new utils_1.PortalMpcError(error);
|
|
187
|
+
catch (_a) {
|
|
188
|
+
// No-op
|
|
100
189
|
}
|
|
101
|
-
return data;
|
|
102
190
|
});
|
|
103
191
|
}
|
|
104
192
|
}
|
|
@@ -72,6 +72,7 @@ class Provider {
|
|
|
72
72
|
mpcHost,
|
|
73
73
|
keychain,
|
|
74
74
|
version,
|
|
75
|
+
portalApi: this.portalApi,
|
|
75
76
|
featureFlags,
|
|
76
77
|
});
|
|
77
78
|
}
|
|
@@ -185,8 +186,8 @@ class Provider {
|
|
|
185
186
|
* @param args The arguments of the request being made
|
|
186
187
|
* @returns Promise<any>
|
|
187
188
|
*/
|
|
188
|
-
request(
|
|
189
|
-
return __awaiter(this,
|
|
189
|
+
request({ method, params, chainId, connect, }) {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
190
191
|
chainId = chainId !== null && chainId !== void 0 ? chainId : this.chainId;
|
|
191
192
|
if (!chainId) {
|
|
192
193
|
throw new Error('[PortalProvider] No chainId provided');
|
|
@@ -318,8 +319,8 @@ class Provider {
|
|
|
318
319
|
*
|
|
319
320
|
* @param args The arguments of the request being made
|
|
320
321
|
*/
|
|
321
|
-
getApproval(
|
|
322
|
-
return __awaiter(this,
|
|
322
|
+
getApproval({ method, params, chainId, connect, }) {
|
|
323
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
323
324
|
// If autoApprove is enabled, just resolve to true
|
|
324
325
|
if (this.autoApprove) {
|
|
325
326
|
return true;
|
|
@@ -384,8 +385,8 @@ class Provider {
|
|
|
384
385
|
* @param args The arguments of the request being made
|
|
385
386
|
* @returns Promise<any>
|
|
386
387
|
*/
|
|
387
|
-
handleGatewayRequests(
|
|
388
|
-
return __awaiter(this,
|
|
388
|
+
handleGatewayRequests({ method, params, chainId, }) {
|
|
389
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
389
390
|
const gatewayUrl = this.getGatewayUrl(chainId);
|
|
390
391
|
// Pass request off to the gateway
|
|
391
392
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
@@ -405,9 +406,9 @@ class Provider {
|
|
|
405
406
|
* @param args The arguments of the request being made
|
|
406
407
|
* @returns Promise<any>
|
|
407
408
|
*/
|
|
408
|
-
handleSigningRequests(
|
|
409
|
-
|
|
410
|
-
|
|
409
|
+
handleSigningRequests({ method, params, chainId, connect, }) {
|
|
410
|
+
var _a, _b;
|
|
411
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
411
412
|
const isApproved = passiveSignerMethods.includes(method)
|
|
412
413
|
? true
|
|
413
414
|
: yield this.getApproval({ method, params, chainId, connect });
|
|
@@ -438,7 +439,7 @@ class Provider {
|
|
|
438
439
|
case 'sol_signAndSendTransaction':
|
|
439
440
|
case 'sol_signMessage':
|
|
440
441
|
case 'sol_signTransaction': {
|
|
441
|
-
const result = yield ((
|
|
442
|
+
const result = yield ((_a = this.signer) === null || _a === void 0 ? void 0 : _a.sign({
|
|
442
443
|
chainId: `${namespace}:${reference}`,
|
|
443
444
|
method,
|
|
444
445
|
params,
|
|
@@ -448,7 +449,7 @@ class Provider {
|
|
|
448
449
|
return result;
|
|
449
450
|
}
|
|
450
451
|
case 'raw_sign': {
|
|
451
|
-
const result = yield ((
|
|
452
|
+
const result = yield ((_b = this.signer) === null || _b === void 0 ? void 0 : _b.sign({
|
|
452
453
|
chainId: '',
|
|
453
454
|
method: '',
|
|
454
455
|
params,
|
package/lib/esm/signers/mpc.js
CHANGED
|
@@ -10,8 +10,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { PortalCurve } from '@portal-hq/core';
|
|
11
11
|
import { PortalMpcError, getClientPlatformVersion, } from '@portal-hq/utils';
|
|
12
12
|
import { NativeModules } from 'react-native';
|
|
13
|
+
var Operation;
|
|
14
|
+
(function (Operation) {
|
|
15
|
+
Operation["SIGN"] = "sign";
|
|
16
|
+
Operation["RAW_SIGN"] = "raw_sign";
|
|
17
|
+
})(Operation || (Operation = {}));
|
|
13
18
|
class MpcSigner {
|
|
14
|
-
constructor({ keychain, mpcHost = 'mpc.portalhq.io', version = 'v6', featureFlags = {}, }) {
|
|
19
|
+
constructor({ keychain, mpcHost = 'mpc.portalhq.io', version = 'v6', portalApi, featureFlags = {}, }) {
|
|
15
20
|
this.version = 'v6';
|
|
16
21
|
this.buildParams = (method, txParams) => {
|
|
17
22
|
let params = txParams;
|
|
@@ -42,61 +47,144 @@ class MpcSigner {
|
|
|
42
47
|
this.mpc = NativeModules.PortalMobileMpc;
|
|
43
48
|
this.mpcHost = mpcHost;
|
|
44
49
|
this.version = version;
|
|
50
|
+
this.portalApi = portalApi;
|
|
45
51
|
if (!this.mpc) {
|
|
46
52
|
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.`);
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
55
|
sign(message, provider) {
|
|
50
56
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return [eip155Address];
|
|
59
|
-
default:
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
const shares = yield this.keychain.getShares();
|
|
63
|
-
let signingShare = shares.secp256k1.share;
|
|
64
|
-
if (curve === PortalCurve.ED25519) {
|
|
65
|
-
if (!shares.ed25519) {
|
|
66
|
-
throw new Error('[Portal.Provider.MpcSigner] The ED25519 share is missing from the keychain.');
|
|
67
|
-
}
|
|
68
|
-
signingShare = shares.ed25519.share;
|
|
69
|
-
}
|
|
70
|
-
const metadata = {
|
|
71
|
-
clientPlatform: 'REACT_NATIVE',
|
|
72
|
-
clientPlatformVersion: getClientPlatformVersion(),
|
|
73
|
-
isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
|
|
74
|
-
mpcServerVersion: this.version,
|
|
75
|
-
optimized: true,
|
|
76
|
-
curve,
|
|
77
|
-
chainId,
|
|
78
|
-
isRaw,
|
|
57
|
+
// Always track metrics, but only send if feature flag is enabled
|
|
58
|
+
const shouldSendMetrics = this.featureFlags.enableSdkPerformanceMetrics === true;
|
|
59
|
+
const signStartTime = performance.now();
|
|
60
|
+
const preOperationStartTime = performance.now();
|
|
61
|
+
const metrics = {
|
|
62
|
+
operation: Operation.SIGN,
|
|
63
|
+
hasError: false,
|
|
79
64
|
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
65
|
+
try {
|
|
66
|
+
const eip155Address = yield this.keychain.getEip155Address();
|
|
67
|
+
const apiKey = provider.apiKey;
|
|
68
|
+
const { method, chainId, curve, isRaw } = message;
|
|
69
|
+
// Add chainId to metrics
|
|
70
|
+
if (chainId) {
|
|
71
|
+
metrics.chainId = chainId;
|
|
72
|
+
}
|
|
73
|
+
switch (method) {
|
|
74
|
+
case 'eth_requestAccounts':
|
|
75
|
+
return [eip155Address];
|
|
76
|
+
case 'eth_accounts':
|
|
77
|
+
return [eip155Address];
|
|
78
|
+
default:
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
const shares = yield this.keychain.getShares();
|
|
82
|
+
let signingShare = shares.secp256k1.share;
|
|
83
|
+
if (curve === PortalCurve.ED25519) {
|
|
84
|
+
if (!shares.ed25519) {
|
|
85
|
+
throw new Error('[Portal.Provider.MpcSigner] The ED25519 share is missing from the keychain.');
|
|
86
|
+
}
|
|
87
|
+
signingShare = shares.ed25519.share;
|
|
88
|
+
}
|
|
89
|
+
const metadata = {
|
|
90
|
+
clientPlatform: 'REACT_NATIVE',
|
|
91
|
+
clientPlatformVersion: getClientPlatformVersion(),
|
|
92
|
+
isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
|
|
93
|
+
mpcServerVersion: this.version,
|
|
94
|
+
optimized: true,
|
|
95
|
+
curve,
|
|
96
|
+
chainId,
|
|
97
|
+
isRaw,
|
|
98
|
+
};
|
|
99
|
+
const stringifiedMetadata = JSON.stringify(metadata);
|
|
100
|
+
let formattedParams;
|
|
101
|
+
let rpcUrl;
|
|
102
|
+
if (isRaw) {
|
|
103
|
+
formattedParams = this.buildParams(method, message.params);
|
|
104
|
+
rpcUrl = '';
|
|
105
|
+
metrics.operation = Operation.RAW_SIGN;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
formattedParams = JSON.stringify(this.buildParams(method, message.params));
|
|
109
|
+
rpcUrl = provider.getGatewayUrl(chainId);
|
|
110
|
+
}
|
|
111
|
+
if (typeof formattedParams !== 'string') {
|
|
112
|
+
throw new Error(`[Portal.Provider.MpcSigner] The formatted params for the signing request could not be converted to a string. The params were: ${formattedParams}`);
|
|
113
|
+
}
|
|
114
|
+
// Record pre-operation time
|
|
115
|
+
metrics.sdkPreOperationMs = performance.now() - preOperationStartTime;
|
|
116
|
+
// Measure MPC signing operation time
|
|
117
|
+
const mpcSignStartTime = performance.now();
|
|
118
|
+
const result = yield this.mpc.sign(apiKey, this.mpcHost, JSON.stringify(signingShare), message.method, formattedParams, rpcUrl, chainId, stringifiedMetadata);
|
|
119
|
+
// Post-operation processing time starts
|
|
120
|
+
const postOperationStartTime = performance.now();
|
|
121
|
+
// Record native call time
|
|
122
|
+
metrics.mpcNativeCallMs = performance.now() - mpcSignStartTime;
|
|
123
|
+
// Parse result and extract binary metrics if available
|
|
124
|
+
const parsedResponse = JSON.parse(String(result));
|
|
125
|
+
const { data, error, meta } = parsedResponse;
|
|
126
|
+
// Add binary metrics to our metrics object
|
|
127
|
+
if (meta === null || meta === void 0 ? void 0 : meta.metrics) {
|
|
128
|
+
if (meta.metrics.wsConnectDurationMs) {
|
|
129
|
+
metrics.sdkBinaryWSConnectMs = meta.metrics.wsConnectDurationMs;
|
|
130
|
+
}
|
|
131
|
+
if (meta.metrics.operationDurationMs) {
|
|
132
|
+
metrics.sdkBinaryOperationMs = meta.metrics.operationDurationMs;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if ((error === null || error === void 0 ? void 0 : error.code) > 0) {
|
|
136
|
+
throw new PortalMpcError(error);
|
|
137
|
+
}
|
|
138
|
+
// Record post-operation time
|
|
139
|
+
metrics.sdkPostOperationMs = performance.now() - postOperationStartTime;
|
|
140
|
+
// Calculate total SDK signing time
|
|
141
|
+
metrics.sdkOperationMs = performance.now() - signStartTime;
|
|
142
|
+
// Only send metrics if the feature flag is enabled
|
|
143
|
+
if (shouldSendMetrics && this.portalApi) {
|
|
144
|
+
try {
|
|
145
|
+
yield this.sendMetrics(metrics, apiKey);
|
|
146
|
+
}
|
|
147
|
+
catch (_a) {
|
|
148
|
+
// No-op
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return data;
|
|
86
152
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
153
|
+
catch (error) {
|
|
154
|
+
// Calculate total time even in error case
|
|
155
|
+
metrics.sdkOperationMs = performance.now() - signStartTime;
|
|
156
|
+
// Only send metrics if the feature flag is enabled
|
|
157
|
+
if (shouldSendMetrics) {
|
|
158
|
+
const apiKey = provider.apiKey;
|
|
159
|
+
metrics.hasError = true;
|
|
160
|
+
try {
|
|
161
|
+
yield this.sendMetrics(metrics, apiKey);
|
|
162
|
+
}
|
|
163
|
+
catch (_b) {
|
|
164
|
+
// No-op
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
90
168
|
}
|
|
91
|
-
|
|
92
|
-
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
// Add debug logs to sendMetrics method too
|
|
172
|
+
sendMetrics(metrics, apiKey) {
|
|
173
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
174
|
+
try {
|
|
175
|
+
if (this.portalApi) {
|
|
176
|
+
yield this.portalApi.post('/api/v3/clients/me/sdk/metrics', {
|
|
177
|
+
headers: {
|
|
178
|
+
Authorization: `Bearer ${apiKey}`,
|
|
179
|
+
'Content-Type': 'application/json',
|
|
180
|
+
},
|
|
181
|
+
body: metrics,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
93
184
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (error && error.code > 0) {
|
|
97
|
-
throw new PortalMpcError(error);
|
|
185
|
+
catch (_a) {
|
|
186
|
+
// No-op
|
|
98
187
|
}
|
|
99
|
-
return data;
|
|
100
188
|
});
|
|
101
189
|
}
|
|
102
190
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portal-hq/provider",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.2",
|
|
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": "^4.1.
|
|
23
|
-
"@portal-hq/utils": "^4.1.
|
|
22
|
+
"@portal-hq/connect": "^4.1.2",
|
|
23
|
+
"@portal-hq/utils": "^4.1.2"
|
|
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": "4504ed5bdfde36df2b9c1acab9916ad412ba6fd9"
|
|
34
34
|
}
|
package/src/providers/index.ts
CHANGED
package/src/signers/mpc.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PortalCurve } from '@portal-hq/core'
|
|
2
2
|
import { FeatureFlags } from '@portal-hq/core/types'
|
|
3
3
|
import {
|
|
4
|
+
HttpRequester,
|
|
4
5
|
IPortalProvider,
|
|
5
6
|
KeychainAdapter,
|
|
6
7
|
PortalMpcError,
|
|
@@ -17,17 +18,24 @@ import {
|
|
|
17
18
|
} from '../../types'
|
|
18
19
|
import Signer from './abstract'
|
|
19
20
|
|
|
21
|
+
enum Operation {
|
|
22
|
+
SIGN = 'sign',
|
|
23
|
+
RAW_SIGN = 'raw_sign',
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
class MpcSigner implements Signer {
|
|
21
27
|
private featureFlags: FeatureFlags
|
|
22
28
|
private keychain: KeychainAdapter
|
|
23
29
|
private mpc: PortalMobileMpc
|
|
24
|
-
private mpcHost: string
|
|
30
|
+
private mpcHost: string
|
|
25
31
|
private version = 'v6'
|
|
32
|
+
private portalApi?: HttpRequester
|
|
26
33
|
|
|
27
34
|
constructor({
|
|
28
35
|
keychain,
|
|
29
36
|
mpcHost = 'mpc.portalhq.io',
|
|
30
37
|
version = 'v6',
|
|
38
|
+
portalApi,
|
|
31
39
|
featureFlags = {},
|
|
32
40
|
}: MpcSignerOptions) {
|
|
33
41
|
this.featureFlags = featureFlags
|
|
@@ -35,6 +43,7 @@ class MpcSigner implements Signer {
|
|
|
35
43
|
this.mpc = NativeModules.PortalMobileMpc
|
|
36
44
|
this.mpcHost = mpcHost
|
|
37
45
|
this.version = version
|
|
46
|
+
this.portalApi = portalApi
|
|
38
47
|
|
|
39
48
|
if (!this.mpc) {
|
|
40
49
|
throw new Error(
|
|
@@ -47,82 +56,175 @@ class MpcSigner implements Signer {
|
|
|
47
56
|
message: SigningRequestArguments,
|
|
48
57
|
provider: IPortalProvider,
|
|
49
58
|
): Promise<any> {
|
|
50
|
-
|
|
59
|
+
// Always track metrics, but only send if feature flag is enabled
|
|
60
|
+
const shouldSendMetrics =
|
|
61
|
+
this.featureFlags.enableSdkPerformanceMetrics === true
|
|
62
|
+
const signStartTime = performance.now()
|
|
63
|
+
const preOperationStartTime = performance.now()
|
|
64
|
+
const metrics: Record<string, number | string | boolean> = {
|
|
65
|
+
operation: Operation.SIGN,
|
|
66
|
+
hasError: false,
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const eip155Address = await this.keychain.getEip155Address()
|
|
51
70
|
|
|
52
|
-
|
|
71
|
+
const apiKey = provider.apiKey
|
|
53
72
|
|
|
54
|
-
|
|
73
|
+
const { method, chainId, curve, isRaw } = message
|
|
74
|
+
// Add chainId to metrics
|
|
75
|
+
if (chainId) {
|
|
76
|
+
metrics.chainId = chainId
|
|
77
|
+
}
|
|
55
78
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
switch (method) {
|
|
80
|
+
case 'eth_requestAccounts':
|
|
81
|
+
return [eip155Address]
|
|
82
|
+
case 'eth_accounts':
|
|
83
|
+
return [eip155Address]
|
|
84
|
+
default:
|
|
85
|
+
break
|
|
86
|
+
}
|
|
64
87
|
|
|
65
|
-
|
|
88
|
+
const shares = await this.keychain.getShares()
|
|
66
89
|
|
|
67
|
-
|
|
90
|
+
let signingShare = shares.secp256k1.share
|
|
91
|
+
|
|
92
|
+
if (curve === PortalCurve.ED25519) {
|
|
93
|
+
if (!shares.ed25519) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
'[Portal.Provider.MpcSigner] The ED25519 share is missing from the keychain.',
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
signingShare = shares.ed25519.share
|
|
99
|
+
}
|
|
68
100
|
|
|
69
|
-
|
|
70
|
-
|
|
101
|
+
const metadata: PortalMobileMpcMetadata = {
|
|
102
|
+
clientPlatform: 'REACT_NATIVE',
|
|
103
|
+
clientPlatformVersion: getClientPlatformVersion(),
|
|
104
|
+
isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
|
|
105
|
+
mpcServerVersion: this.version,
|
|
106
|
+
optimized: true,
|
|
107
|
+
curve,
|
|
108
|
+
chainId,
|
|
109
|
+
isRaw,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const stringifiedMetadata = JSON.stringify(metadata)
|
|
113
|
+
|
|
114
|
+
let formattedParams: string
|
|
115
|
+
let rpcUrl: string
|
|
116
|
+
|
|
117
|
+
if (isRaw) {
|
|
118
|
+
formattedParams = this.buildParams(method, message.params)
|
|
119
|
+
rpcUrl = ''
|
|
120
|
+
metrics.operation = Operation.RAW_SIGN
|
|
121
|
+
} else {
|
|
122
|
+
formattedParams = JSON.stringify(
|
|
123
|
+
this.buildParams(method, message.params),
|
|
124
|
+
)
|
|
125
|
+
rpcUrl = provider.getGatewayUrl(chainId)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (typeof formattedParams !== 'string') {
|
|
71
129
|
throw new Error(
|
|
72
|
-
|
|
130
|
+
`[Portal.Provider.MpcSigner] The formatted params for the signing request could not be converted to a string. The params were: ${formattedParams}`,
|
|
73
131
|
)
|
|
74
132
|
}
|
|
75
|
-
signingShare = shares.ed25519.share
|
|
76
|
-
}
|
|
77
133
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
clientPlatformVersion: getClientPlatformVersion(),
|
|
81
|
-
isMultiBackupEnabled: this.featureFlags.isMultiBackupEnabled,
|
|
82
|
-
mpcServerVersion: this.version,
|
|
83
|
-
optimized: true,
|
|
84
|
-
curve,
|
|
85
|
-
chainId,
|
|
86
|
-
isRaw,
|
|
87
|
-
}
|
|
134
|
+
// Record pre-operation time
|
|
135
|
+
metrics.sdkPreOperationMs = performance.now() - preOperationStartTime
|
|
88
136
|
|
|
89
|
-
|
|
137
|
+
// Measure MPC signing operation time
|
|
138
|
+
const mpcSignStartTime = performance.now()
|
|
90
139
|
|
|
91
|
-
|
|
92
|
-
|
|
140
|
+
const result = await this.mpc.sign(
|
|
141
|
+
apiKey,
|
|
142
|
+
this.mpcHost,
|
|
143
|
+
JSON.stringify(signingShare),
|
|
144
|
+
message.method,
|
|
145
|
+
formattedParams,
|
|
146
|
+
rpcUrl,
|
|
147
|
+
chainId,
|
|
148
|
+
stringifiedMetadata,
|
|
149
|
+
)
|
|
93
150
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
rpcUrl = ''
|
|
97
|
-
} else {
|
|
98
|
-
formattedParams = JSON.stringify(this.buildParams(method, message.params))
|
|
99
|
-
rpcUrl = provider.getGatewayUrl(chainId)
|
|
100
|
-
}
|
|
151
|
+
// Post-operation processing time starts
|
|
152
|
+
const postOperationStartTime = performance.now()
|
|
101
153
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
154
|
+
// Record native call time
|
|
155
|
+
metrics.mpcNativeCallMs = performance.now() - mpcSignStartTime
|
|
156
|
+
|
|
157
|
+
// Parse result and extract binary metrics if available
|
|
158
|
+
const parsedResponse = JSON.parse(String(result)) as SigningResponse
|
|
159
|
+
const { data, error, meta } = parsedResponse
|
|
160
|
+
|
|
161
|
+
// Add binary metrics to our metrics object
|
|
162
|
+
if (meta?.metrics) {
|
|
163
|
+
if (meta.metrics.wsConnectDurationMs) {
|
|
164
|
+
metrics.sdkBinaryWSConnectMs = meta.metrics.wsConnectDurationMs
|
|
165
|
+
}
|
|
166
|
+
if (meta.metrics.operationDurationMs) {
|
|
167
|
+
metrics.sdkBinaryOperationMs = meta.metrics.operationDurationMs
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (error?.code > 0) {
|
|
172
|
+
throw new PortalMpcError(error)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Record post-operation time
|
|
176
|
+
metrics.sdkPostOperationMs = performance.now() - postOperationStartTime
|
|
107
177
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
178
|
+
// Calculate total SDK signing time
|
|
179
|
+
metrics.sdkOperationMs = performance.now() - signStartTime
|
|
180
|
+
|
|
181
|
+
// Only send metrics if the feature flag is enabled
|
|
182
|
+
if (shouldSendMetrics && this.portalApi) {
|
|
183
|
+
try {
|
|
184
|
+
await this.sendMetrics(metrics, apiKey)
|
|
185
|
+
} catch {
|
|
186
|
+
// No-op
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return data
|
|
191
|
+
} catch (error) {
|
|
192
|
+
// Calculate total time even in error case
|
|
193
|
+
metrics.sdkOperationMs = performance.now() - signStartTime
|
|
194
|
+
|
|
195
|
+
// Only send metrics if the feature flag is enabled
|
|
196
|
+
if (shouldSendMetrics) {
|
|
197
|
+
const apiKey = provider.apiKey
|
|
198
|
+
metrics.hasError = true
|
|
199
|
+
try {
|
|
200
|
+
await this.sendMetrics(metrics, apiKey)
|
|
201
|
+
} catch {
|
|
202
|
+
// No-op
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
throw error
|
|
123
207
|
}
|
|
208
|
+
}
|
|
124
209
|
|
|
125
|
-
|
|
210
|
+
// Add debug logs to sendMetrics method too
|
|
211
|
+
private async sendMetrics(
|
|
212
|
+
metrics: Record<string, number | string | boolean>,
|
|
213
|
+
apiKey: string,
|
|
214
|
+
): Promise<void> {
|
|
215
|
+
try {
|
|
216
|
+
if (this.portalApi) {
|
|
217
|
+
await this.portalApi.post('/api/v3/clients/me/sdk/metrics', {
|
|
218
|
+
headers: {
|
|
219
|
+
Authorization: `Bearer ${apiKey}`,
|
|
220
|
+
'Content-Type': 'application/json',
|
|
221
|
+
},
|
|
222
|
+
body: metrics,
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
// No-op
|
|
227
|
+
}
|
|
126
228
|
}
|
|
127
229
|
|
|
128
230
|
private buildParams = (method: string, txParams: any) => {
|
package/types.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// Types
|
|
2
2
|
import PortalConnect from '@portal-hq/connect'
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
KeychainAdapter,
|
|
5
|
+
type PortalError,
|
|
6
|
+
HttpRequester,
|
|
7
|
+
} from '@portal-hq/utils'
|
|
5
8
|
|
|
6
9
|
export type EventHandler = (data: any) => void
|
|
7
10
|
|
|
@@ -21,6 +24,7 @@ export interface MpcSignerOptions extends SignerOptions {
|
|
|
21
24
|
// Optional
|
|
22
25
|
isSimulator?: boolean
|
|
23
26
|
mpcHost?: string
|
|
27
|
+
portalApi?: HttpRequester
|
|
24
28
|
version?: string
|
|
25
29
|
featureFlags?: FeatureFlags
|
|
26
30
|
}
|
|
@@ -120,6 +124,12 @@ export type SigningRequestParams = Eip1559 | LegacyTx
|
|
|
120
124
|
export interface SigningResponse {
|
|
121
125
|
data: string
|
|
122
126
|
error: PortalError
|
|
127
|
+
meta?: {
|
|
128
|
+
metrics?: {
|
|
129
|
+
wsConnectDurationMs?: number
|
|
130
|
+
operationDurationMs?: number
|
|
131
|
+
}
|
|
132
|
+
}
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
export interface SwitchEthereumChainParameter {
|