@portal-hq/web 3.11.0 → 3.12.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/index.js +12 -7
- package/lib/commonjs/index.test.js +1 -1
- package/lib/commonjs/mpc/index.js +58 -10
- package/lib/commonjs/mpc/index.test.js +202 -100
- package/lib/commonjs/namespaces/evmAccountType/index.test.js +25 -12
- package/lib/commonjs/provider/index.js +14 -17
- package/lib/commonjs/provider/index.test.js +25 -2
- package/lib/commonjs/shared/logger.js +59 -0
- package/lib/commonjs/shared/trace/index.js +55 -0
- package/lib/commonjs/shared/types/index.js +2 -1
- package/lib/esm/index.js +12 -7
- package/lib/esm/index.test.js +1 -1
- package/lib/esm/mpc/index.js +58 -10
- package/lib/esm/mpc/index.test.js +202 -100
- package/lib/esm/namespaces/evmAccountType/index.test.js +25 -12
- package/lib/esm/provider/index.js +14 -17
- package/lib/esm/provider/index.test.js +25 -2
- package/lib/esm/shared/logger.js +56 -0
- package/lib/esm/shared/trace/index.js +51 -0
- package/lib/esm/shared/types/index.js +2 -1
- package/package.json +1 -1
- package/src/index.test.ts +1 -0
- package/src/index.ts +14 -3
- package/src/logger/index.ts +1 -8
- package/src/mpc/index.test.ts +202 -100
- package/src/mpc/index.ts +73 -5
- package/src/namespaces/evmAccountType/index.test.ts +25 -12
- package/src/provider/index.test.ts +30 -2
- package/src/provider/index.ts +17 -3
- package/src/shared/logger.ts +69 -0
- package/src/shared/trace/index.ts +59 -0
- package/src/shared/types/api.ts +5 -0
- package/src/shared/types/common.ts +14 -0
- package/src/shared/types/index.ts +2 -1
- package/types.d.ts +1 -0
|
@@ -31,7 +31,8 @@ describe('EvmAccountType', () => {
|
|
|
31
31
|
.mockImplementation((message, origin) => {
|
|
32
32
|
const { type, data } = message;
|
|
33
33
|
expect(type).toEqual('portal:evmAccountType:getStatus');
|
|
34
|
-
expect(data).
|
|
34
|
+
expect(data).toMatchObject(args);
|
|
35
|
+
expect(typeof message.traceId).toBe('string');
|
|
35
36
|
expect(origin).toEqual(mockHostOrigin);
|
|
36
37
|
window.dispatchEvent(new MessageEvent('message', {
|
|
37
38
|
origin: mockHostOrigin,
|
|
@@ -59,7 +60,8 @@ describe('EvmAccountType', () => {
|
|
|
59
60
|
.mockImplementationOnce((message, origin) => {
|
|
60
61
|
const { type, data } = message;
|
|
61
62
|
expect(type).toEqual('portal:evmAccountType:getStatus');
|
|
62
|
-
expect(data).
|
|
63
|
+
expect(data).toMatchObject(args);
|
|
64
|
+
expect(typeof message.traceId).toBe('string');
|
|
63
65
|
expect(origin).toEqual(mockHostOrigin);
|
|
64
66
|
window.dispatchEvent(new MessageEvent('message', {
|
|
65
67
|
origin: mockHostOrigin,
|
|
@@ -99,7 +101,8 @@ describe('EvmAccountType', () => {
|
|
|
99
101
|
.mockImplementation((message, origin) => {
|
|
100
102
|
const { type, data } = message;
|
|
101
103
|
expect(type).toEqual('portal:evmAccountType:getAddresses');
|
|
102
|
-
expect(data).
|
|
104
|
+
expect(data).toMatchObject(args);
|
|
105
|
+
expect(typeof message.traceId).toBe('string');
|
|
103
106
|
expect(origin).toEqual(mockHostOrigin);
|
|
104
107
|
window.dispatchEvent(new MessageEvent('message', {
|
|
105
108
|
origin: mockHostOrigin,
|
|
@@ -127,7 +130,8 @@ describe('EvmAccountType', () => {
|
|
|
127
130
|
.mockImplementationOnce((message, origin) => {
|
|
128
131
|
const { type, data } = message;
|
|
129
132
|
expect(type).toEqual('portal:evmAccountType:getAddresses');
|
|
130
|
-
expect(data).
|
|
133
|
+
expect(data).toMatchObject(args);
|
|
134
|
+
expect(typeof message.traceId).toBe('string');
|
|
131
135
|
expect(origin).toEqual(mockHostOrigin);
|
|
132
136
|
window.dispatchEvent(new MessageEvent('message', {
|
|
133
137
|
origin: mockHostOrigin,
|
|
@@ -149,6 +153,7 @@ describe('EvmAccountType', () => {
|
|
|
149
153
|
.catch((error) => {
|
|
150
154
|
expect(error).toBeInstanceOf(PortalMpcError);
|
|
151
155
|
expect(error.message).toEqual('test');
|
|
156
|
+
expect(error.code).toEqual(1);
|
|
152
157
|
done();
|
|
153
158
|
});
|
|
154
159
|
});
|
|
@@ -166,7 +171,8 @@ describe('EvmAccountType', () => {
|
|
|
166
171
|
.mockImplementation((message, origin) => {
|
|
167
172
|
const { type, data } = message;
|
|
168
173
|
expect(type).toEqual('portal:evmAccountType:buildAuthorizationList');
|
|
169
|
-
expect(data).
|
|
174
|
+
expect(data).toMatchObject(args);
|
|
175
|
+
expect(typeof message.traceId).toBe('string');
|
|
170
176
|
expect(origin).toEqual(mockHostOrigin);
|
|
171
177
|
window.dispatchEvent(new MessageEvent('message', {
|
|
172
178
|
origin: mockHostOrigin,
|
|
@@ -198,7 +204,8 @@ describe('EvmAccountType', () => {
|
|
|
198
204
|
.mockImplementation((message, origin) => {
|
|
199
205
|
const { type, data } = message;
|
|
200
206
|
expect(type).toEqual('portal:evmAccountType:buildAuthorizationList');
|
|
201
|
-
expect(data).
|
|
207
|
+
expect(data).toMatchObject(argsWithoutSubsidize);
|
|
208
|
+
expect(typeof message.traceId).toBe('string');
|
|
202
209
|
expect(origin).toEqual(mockHostOrigin);
|
|
203
210
|
window.dispatchEvent(new MessageEvent('message', {
|
|
204
211
|
origin: mockHostOrigin,
|
|
@@ -226,7 +233,8 @@ describe('EvmAccountType', () => {
|
|
|
226
233
|
.mockImplementationOnce((message, origin) => {
|
|
227
234
|
const { type, data } = message;
|
|
228
235
|
expect(type).toEqual('portal:evmAccountType:buildAuthorizationList');
|
|
229
|
-
expect(data).
|
|
236
|
+
expect(data).toMatchObject(args);
|
|
237
|
+
expect(typeof message.traceId).toBe('string');
|
|
230
238
|
expect(origin).toEqual(mockHostOrigin);
|
|
231
239
|
window.dispatchEvent(new MessageEvent('message', {
|
|
232
240
|
origin: mockHostOrigin,
|
|
@@ -266,7 +274,8 @@ describe('EvmAccountType', () => {
|
|
|
266
274
|
.mockImplementation((message, origin) => {
|
|
267
275
|
const { type, data } = message;
|
|
268
276
|
expect(type).toEqual('portal:evmAccountType:build7702UpgradeTx');
|
|
269
|
-
expect(data).
|
|
277
|
+
expect(data).toMatchObject(args);
|
|
278
|
+
expect(typeof message.traceId).toBe('string');
|
|
270
279
|
expect(origin).toEqual(mockHostOrigin);
|
|
271
280
|
window.dispatchEvent(new MessageEvent('message', {
|
|
272
281
|
origin: mockHostOrigin,
|
|
@@ -299,7 +308,8 @@ describe('EvmAccountType', () => {
|
|
|
299
308
|
.mockImplementation((message, origin) => {
|
|
300
309
|
const { type, data } = message;
|
|
301
310
|
expect(type).toEqual('portal:evmAccountType:build7702UpgradeTx');
|
|
302
|
-
expect(data).
|
|
311
|
+
expect(data).toMatchObject(argsWithoutSubsidize);
|
|
312
|
+
expect(typeof message.traceId).toBe('string');
|
|
303
313
|
expect(origin).toEqual(mockHostOrigin);
|
|
304
314
|
window.dispatchEvent(new MessageEvent('message', {
|
|
305
315
|
origin: mockHostOrigin,
|
|
@@ -327,7 +337,8 @@ describe('EvmAccountType', () => {
|
|
|
327
337
|
.mockImplementationOnce((message, origin) => {
|
|
328
338
|
const { type, data } = message;
|
|
329
339
|
expect(type).toEqual('portal:evmAccountType:build7702UpgradeTx');
|
|
330
|
-
expect(data).
|
|
340
|
+
expect(data).toMatchObject(args);
|
|
341
|
+
expect(typeof message.traceId).toBe('string');
|
|
331
342
|
expect(origin).toEqual(mockHostOrigin);
|
|
332
343
|
window.dispatchEvent(new MessageEvent('message', {
|
|
333
344
|
origin: mockHostOrigin,
|
|
@@ -367,7 +378,8 @@ describe('EvmAccountType', () => {
|
|
|
367
378
|
.mockImplementation((message, _origin) => {
|
|
368
379
|
const { type, data } = message;
|
|
369
380
|
if (type === 'portal:evmAccountType:getStatus') {
|
|
370
|
-
expect(data).
|
|
381
|
+
expect(data).toMatchObject({ chain: args.chain });
|
|
382
|
+
expect(typeof message.traceId).toBe('string');
|
|
371
383
|
window.dispatchEvent(new MessageEvent('message', {
|
|
372
384
|
origin: mockHostOrigin,
|
|
373
385
|
data: {
|
|
@@ -377,7 +389,8 @@ describe('EvmAccountType', () => {
|
|
|
377
389
|
}));
|
|
378
390
|
}
|
|
379
391
|
else if (type === 'portal:evmAccountType:buildAuthorizationList') {
|
|
380
|
-
expect(data).
|
|
392
|
+
expect(data).toMatchObject({ chain: args.chain, subsidize: true });
|
|
393
|
+
expect(typeof message.traceId).toBe('string');
|
|
381
394
|
window.dispatchEvent(new MessageEvent('message', {
|
|
382
395
|
origin: mockHostOrigin,
|
|
383
396
|
data: {
|
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { ProviderRpcError, RpcErrorCodes } from './errors';
|
|
11
11
|
import { sdkLogger } from '../logger';
|
|
12
|
+
import { generateTraceId, X_PORTAL_TRACE_ID_HEADER } from '../shared/trace';
|
|
12
13
|
export var RequestMethod;
|
|
13
14
|
(function (RequestMethod) {
|
|
14
15
|
RequestMethod["eth_accounts"] = "eth_accounts";
|
|
@@ -258,8 +259,10 @@ class Provider {
|
|
|
258
259
|
* @param args The arguments of the request being made
|
|
259
260
|
* @returns Promise<any>
|
|
260
261
|
*/
|
|
261
|
-
request({ chainId: requestChainId, method, params, sponsorGas, signatureApprovalMemo, }) {
|
|
262
|
+
request({ chainId: requestChainId, method, params, sponsorGas, signatureApprovalMemo, traceId: providedTraceId, }) {
|
|
262
263
|
return __awaiter(this, void 0, void 0, function* () {
|
|
264
|
+
const traceId = providedTraceId !== null && providedTraceId !== void 0 ? providedTraceId : generateTraceId();
|
|
265
|
+
sdkLogger.info(`[PortalProvider] request started | method=${method} | traceId=${traceId}`);
|
|
263
266
|
const isSignerMethod = signerMethods.includes(method);
|
|
264
267
|
const chainId = this.getCAIP2ChainId(requestChainId);
|
|
265
268
|
if (!isSignerMethod && !method.startsWith('wallet_')) {
|
|
@@ -268,6 +271,7 @@ class Provider {
|
|
|
268
271
|
chainId,
|
|
269
272
|
method,
|
|
270
273
|
params,
|
|
274
|
+
traceId,
|
|
271
275
|
});
|
|
272
276
|
this.emit('portal_signatureReceived', {
|
|
273
277
|
chainId,
|
|
@@ -288,6 +292,7 @@ class Provider {
|
|
|
288
292
|
params,
|
|
289
293
|
sponsorGas,
|
|
290
294
|
signatureApprovalMemo,
|
|
295
|
+
traceId,
|
|
291
296
|
});
|
|
292
297
|
if (transactionHash) {
|
|
293
298
|
this.emit('portal_signatureReceived', {
|
|
@@ -377,9 +382,8 @@ class Provider {
|
|
|
377
382
|
* @param args The arguments of the request being made
|
|
378
383
|
* @returns Promise<any>
|
|
379
384
|
*/
|
|
380
|
-
handleGatewayRequest({ chainId, method, params, }) {
|
|
385
|
+
handleGatewayRequest({ chainId, method, params, traceId, }) {
|
|
381
386
|
return __awaiter(this, void 0, void 0, function* () {
|
|
382
|
-
// Prepare the request body
|
|
383
387
|
const requestBody = {
|
|
384
388
|
body: JSON.stringify({
|
|
385
389
|
jsonrpc: '2.0',
|
|
@@ -388,11 +392,8 @@ class Provider {
|
|
|
388
392
|
params,
|
|
389
393
|
}),
|
|
390
394
|
method: 'POST',
|
|
391
|
-
headers: {
|
|
392
|
-
'Content-Type': 'application/json',
|
|
393
|
-
},
|
|
395
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, (traceId && { [X_PORTAL_TRACE_ID_HEADER]: traceId })),
|
|
394
396
|
};
|
|
395
|
-
// Pass request off to the gateway
|
|
396
397
|
const result = yield fetch(this.portal.getRpcUrl(chainId), requestBody);
|
|
397
398
|
return result.json();
|
|
398
399
|
});
|
|
@@ -403,7 +404,7 @@ class Provider {
|
|
|
403
404
|
* @param args The arguments of the request being made
|
|
404
405
|
* @returns Promise<any>
|
|
405
406
|
*/
|
|
406
|
-
handleSigningRequest({ chainId, method, params, sponsorGas, signatureApprovalMemo, }) {
|
|
407
|
+
handleSigningRequest({ chainId, method, params, sponsorGas, signatureApprovalMemo, traceId, }) {
|
|
407
408
|
return __awaiter(this, void 0, void 0, function* () {
|
|
408
409
|
const isApproved = passiveSignerMethods.includes(method)
|
|
409
410
|
? true
|
|
@@ -430,9 +431,9 @@ class Provider {
|
|
|
430
431
|
case RequestMethod.eth_signTypedData_v4:
|
|
431
432
|
case RequestMethod.personal_sign: {
|
|
432
433
|
this.enforceEip155ChainId(chainId);
|
|
433
|
-
const result = yield this.portal.mpc.sign(Object.assign({ chainId: chainId, method, params: this.buildParams(method, params), rpcUrl: this.portal.getRpcUrl(chainId), sponsorGas }, (signatureApprovalMemo !== undefined && {
|
|
434
|
+
const result = yield this.portal.mpc.sign(Object.assign(Object.assign({ chainId: chainId, method, params: this.buildParams(method, params), rpcUrl: this.portal.getRpcUrl(chainId), sponsorGas }, (signatureApprovalMemo !== undefined && {
|
|
434
435
|
signatureApprovalMemo,
|
|
435
|
-
})));
|
|
436
|
+
})), { traceId }));
|
|
436
437
|
return result;
|
|
437
438
|
}
|
|
438
439
|
case RequestMethod.sol_signAndConfirmTransaction:
|
|
@@ -440,14 +441,10 @@ class Provider {
|
|
|
440
441
|
case RequestMethod.sol_signMessage:
|
|
441
442
|
case RequestMethod.sol_signTransaction: {
|
|
442
443
|
this.enforceSolanaChainId(chainId);
|
|
443
|
-
const result = yield this.portal.mpc.sign({
|
|
444
|
-
|
|
445
|
-
method,
|
|
446
|
-
params: this.buildParams(method, params),
|
|
447
|
-
rpcUrl: this.portal.getRpcUrl(chainId),
|
|
448
|
-
sponsorGas,
|
|
444
|
+
const result = yield this.portal.mpc.sign(Object.assign({ chainId: chainId, method, params: this.buildParams(method, params), rpcUrl: this.portal.getRpcUrl(chainId), sponsorGas,
|
|
445
|
+
traceId }, (signatureApprovalMemo !== undefined && {
|
|
449
446
|
signatureApprovalMemo,
|
|
450
|
-
});
|
|
447
|
+
})));
|
|
451
448
|
return result;
|
|
452
449
|
}
|
|
453
450
|
default:
|
|
@@ -17,6 +17,8 @@ import { ProviderRpcError, RpcErrorCodes } from './errors';
|
|
|
17
17
|
import { RequestMethod } from '../';
|
|
18
18
|
import mpcMock from '../__mocks/portal/mpc';
|
|
19
19
|
import { sdkLogger } from '../logger';
|
|
20
|
+
import { X_PORTAL_TRACE_ID_HEADER } from '../shared/trace';
|
|
21
|
+
jest.mock('../shared/trace', () => (Object.assign(Object.assign({}, jest.requireActual('../shared/trace')), { generateTraceId: jest.fn(() => 'mock-trace-id-12345'), X_PORTAL_TRACE_ID_HEADER: 'X-Portal-Trace-Id' })));
|
|
20
22
|
describe('Provider', () => {
|
|
21
23
|
beforeEach(() => {
|
|
22
24
|
portal.autoApprove = true;
|
|
@@ -152,6 +154,7 @@ describe('Provider', () => {
|
|
|
152
154
|
method: RequestMethod.eth_sendTransaction,
|
|
153
155
|
params: 'test',
|
|
154
156
|
rpcUrl: mockRpcUrl,
|
|
157
|
+
traceId: 'mock-trace-id-12345',
|
|
155
158
|
});
|
|
156
159
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
157
160
|
chainId: 'eip155:1',
|
|
@@ -188,6 +191,7 @@ describe('Provider', () => {
|
|
|
188
191
|
method: RequestMethod.eth_signTransaction,
|
|
189
192
|
params: 'test',
|
|
190
193
|
rpcUrl: mockRpcUrl,
|
|
194
|
+
traceId: 'mock-trace-id-12345',
|
|
191
195
|
});
|
|
192
196
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
193
197
|
chainId: 'eip155:1',
|
|
@@ -224,6 +228,7 @@ describe('Provider', () => {
|
|
|
224
228
|
method: RequestMethod.eth_sign,
|
|
225
229
|
params: ['test'],
|
|
226
230
|
rpcUrl: mockRpcUrl,
|
|
231
|
+
traceId: 'mock-trace-id-12345',
|
|
227
232
|
});
|
|
228
233
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
229
234
|
chainId: 'eip155:1',
|
|
@@ -260,6 +265,7 @@ describe('Provider', () => {
|
|
|
260
265
|
method: RequestMethod.eth_signTypedData_v3,
|
|
261
266
|
params: ['test'],
|
|
262
267
|
rpcUrl: mockRpcUrl,
|
|
268
|
+
traceId: 'mock-trace-id-12345',
|
|
263
269
|
});
|
|
264
270
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
265
271
|
chainId: 'eip155:1',
|
|
@@ -296,6 +302,7 @@ describe('Provider', () => {
|
|
|
296
302
|
method: RequestMethod.eth_signTypedData_v4,
|
|
297
303
|
params: ['test'],
|
|
298
304
|
rpcUrl: mockRpcUrl,
|
|
305
|
+
traceId: 'mock-trace-id-12345',
|
|
299
306
|
});
|
|
300
307
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
301
308
|
chainId: 'eip155:1',
|
|
@@ -332,6 +339,7 @@ describe('Provider', () => {
|
|
|
332
339
|
method: RequestMethod.personal_sign,
|
|
333
340
|
params: ['test'],
|
|
334
341
|
rpcUrl: mockRpcUrl,
|
|
342
|
+
traceId: 'mock-trace-id-12345',
|
|
335
343
|
});
|
|
336
344
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
337
345
|
chainId: 'eip155:1',
|
|
@@ -368,6 +376,7 @@ describe('Provider', () => {
|
|
|
368
376
|
method: RequestMethod.sol_signAndConfirmTransaction,
|
|
369
377
|
params: ['test'],
|
|
370
378
|
rpcUrl: mockRpcUrl,
|
|
379
|
+
traceId: 'mock-trace-id-12345',
|
|
371
380
|
});
|
|
372
381
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
373
382
|
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
@@ -404,6 +413,7 @@ describe('Provider', () => {
|
|
|
404
413
|
method: RequestMethod.sol_signAndSendTransaction,
|
|
405
414
|
params: ['test'],
|
|
406
415
|
rpcUrl: mockRpcUrl,
|
|
416
|
+
traceId: 'mock-trace-id-12345',
|
|
407
417
|
});
|
|
408
418
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
409
419
|
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
@@ -440,6 +450,7 @@ describe('Provider', () => {
|
|
|
440
450
|
method: RequestMethod.sol_signMessage,
|
|
441
451
|
params: ['test'],
|
|
442
452
|
rpcUrl: mockRpcUrl,
|
|
453
|
+
traceId: 'mock-trace-id-12345',
|
|
443
454
|
});
|
|
444
455
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
445
456
|
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
@@ -476,6 +487,7 @@ describe('Provider', () => {
|
|
|
476
487
|
method: RequestMethod.sol_signTransaction,
|
|
477
488
|
params: ['test'],
|
|
478
489
|
rpcUrl: mockRpcUrl,
|
|
490
|
+
traceId: 'mock-trace-id-12345',
|
|
479
491
|
});
|
|
480
492
|
expect(provider.emit).toHaveBeenCalledWith('portal_signatureReceived', {
|
|
481
493
|
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
@@ -576,6 +588,7 @@ describe('Provider', () => {
|
|
|
576
588
|
method: RequestMethod.eth_sendTransaction,
|
|
577
589
|
params: 'test',
|
|
578
590
|
rpcUrl: mockRpcUrl,
|
|
591
|
+
traceId: 'mock-trace-id-12345',
|
|
579
592
|
});
|
|
580
593
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
581
594
|
chainId: 'eip155:1',
|
|
@@ -627,6 +640,7 @@ describe('Provider', () => {
|
|
|
627
640
|
method: RequestMethod.eth_signTransaction,
|
|
628
641
|
params: 'test',
|
|
629
642
|
rpcUrl: mockRpcUrl,
|
|
643
|
+
traceId: 'mock-trace-id-12345',
|
|
630
644
|
});
|
|
631
645
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
632
646
|
chainId: 'eip155:1',
|
|
@@ -678,6 +692,7 @@ describe('Provider', () => {
|
|
|
678
692
|
method: RequestMethod.eth_sign,
|
|
679
693
|
params: ['test'],
|
|
680
694
|
rpcUrl: mockRpcUrl,
|
|
695
|
+
traceId: 'mock-trace-id-12345',
|
|
681
696
|
});
|
|
682
697
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
683
698
|
chainId: 'eip155:1',
|
|
@@ -729,6 +744,7 @@ describe('Provider', () => {
|
|
|
729
744
|
method: RequestMethod.eth_signTypedData_v3,
|
|
730
745
|
params: ['test'],
|
|
731
746
|
rpcUrl: mockRpcUrl,
|
|
747
|
+
traceId: 'mock-trace-id-12345',
|
|
732
748
|
});
|
|
733
749
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
734
750
|
chainId: 'eip155:1',
|
|
@@ -780,6 +796,7 @@ describe('Provider', () => {
|
|
|
780
796
|
method: RequestMethod.eth_signTypedData_v4,
|
|
781
797
|
params: ['test'],
|
|
782
798
|
rpcUrl: mockRpcUrl,
|
|
799
|
+
traceId: 'mock-trace-id-12345',
|
|
783
800
|
});
|
|
784
801
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
785
802
|
chainId: 'eip155:1',
|
|
@@ -831,6 +848,7 @@ describe('Provider', () => {
|
|
|
831
848
|
method: RequestMethod.personal_sign,
|
|
832
849
|
params: ['test'],
|
|
833
850
|
rpcUrl: mockRpcUrl,
|
|
851
|
+
traceId: 'mock-trace-id-12345',
|
|
834
852
|
});
|
|
835
853
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
836
854
|
chainId: 'eip155:1',
|
|
@@ -882,6 +900,7 @@ describe('Provider', () => {
|
|
|
882
900
|
method: RequestMethod.sol_signAndConfirmTransaction,
|
|
883
901
|
params: ['test'],
|
|
884
902
|
rpcUrl: mockRpcUrl,
|
|
903
|
+
traceId: 'mock-trace-id-12345',
|
|
885
904
|
});
|
|
886
905
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
887
906
|
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
@@ -933,6 +952,7 @@ describe('Provider', () => {
|
|
|
933
952
|
method: RequestMethod.sol_signAndSendTransaction,
|
|
934
953
|
params: ['test'],
|
|
935
954
|
rpcUrl: mockRpcUrl,
|
|
955
|
+
traceId: 'mock-trace-id-12345',
|
|
936
956
|
});
|
|
937
957
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
938
958
|
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
@@ -984,6 +1004,7 @@ describe('Provider', () => {
|
|
|
984
1004
|
method: RequestMethod.sol_signMessage,
|
|
985
1005
|
params: ['test'],
|
|
986
1006
|
rpcUrl: mockRpcUrl,
|
|
1007
|
+
traceId: 'mock-trace-id-12345',
|
|
987
1008
|
});
|
|
988
1009
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
989
1010
|
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
@@ -1035,6 +1056,7 @@ describe('Provider', () => {
|
|
|
1035
1056
|
method: RequestMethod.sol_signTransaction,
|
|
1036
1057
|
params: ['test'],
|
|
1037
1058
|
rpcUrl: mockRpcUrl,
|
|
1059
|
+
traceId: 'mock-trace-id-12345',
|
|
1038
1060
|
});
|
|
1039
1061
|
expect(mockSignatureReceivedHandler).toHaveBeenCalledWith({
|
|
1040
1062
|
chainId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
@@ -1078,9 +1100,10 @@ describe('Provider', () => {
|
|
|
1078
1100
|
expect(result).toEqual('test');
|
|
1079
1101
|
expect(global.fetch).toHaveBeenCalledWith(mockRpcUrl, {
|
|
1080
1102
|
method: 'POST',
|
|
1081
|
-
headers: {
|
|
1103
|
+
headers: expect.objectContaining({
|
|
1082
1104
|
'Content-Type': 'application/json',
|
|
1083
|
-
|
|
1105
|
+
[X_PORTAL_TRACE_ID_HEADER]: 'mock-trace-id-12345',
|
|
1106
|
+
}),
|
|
1084
1107
|
body: JSON.stringify({
|
|
1085
1108
|
jsonrpc: '2.0',
|
|
1086
1109
|
id: '0',
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configurable logger for the iframe SDK. Respects logLevel so that when the
|
|
3
|
+
* parent sets logLevel to 'none', no messages are written to the console.
|
|
4
|
+
*/
|
|
5
|
+
const LOG_LEVEL_PRIORITY = {
|
|
6
|
+
none: 0,
|
|
7
|
+
error: 1,
|
|
8
|
+
warn: 2,
|
|
9
|
+
info: 3,
|
|
10
|
+
debug: 4,
|
|
11
|
+
};
|
|
12
|
+
class SdkLoggerImpl {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.logLevel = 'none';
|
|
15
|
+
}
|
|
16
|
+
configure(logLevel) {
|
|
17
|
+
this.logLevel = logLevel;
|
|
18
|
+
}
|
|
19
|
+
getLogLevel() {
|
|
20
|
+
return this.logLevel;
|
|
21
|
+
}
|
|
22
|
+
shouldLog(level) {
|
|
23
|
+
return LOG_LEVEL_PRIORITY[level] <= LOG_LEVEL_PRIORITY[this.logLevel];
|
|
24
|
+
}
|
|
25
|
+
log(level, message, ...args) {
|
|
26
|
+
if (!this.shouldLog(level))
|
|
27
|
+
return;
|
|
28
|
+
if (typeof console === 'undefined')
|
|
29
|
+
return;
|
|
30
|
+
const prefix = `[PortalMpc] ${message}`;
|
|
31
|
+
try {
|
|
32
|
+
if (level === 'debug' && console.debug) {
|
|
33
|
+
console.debug(prefix, ...args);
|
|
34
|
+
}
|
|
35
|
+
else if (level === 'warn' && console.warn) {
|
|
36
|
+
console.warn(prefix, ...args);
|
|
37
|
+
}
|
|
38
|
+
else if (level === 'error' && console.error) {
|
|
39
|
+
console.error(prefix, ...args);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (_a) {
|
|
43
|
+
// no-op if console is unavailable or throws
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
debug(message, ...args) {
|
|
47
|
+
this.log('debug', message, ...args);
|
|
48
|
+
}
|
|
49
|
+
warn(message, ...args) {
|
|
50
|
+
this.log('warn', message, ...args);
|
|
51
|
+
}
|
|
52
|
+
error(message, ...args) {
|
|
53
|
+
this.log('error', message, ...args);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export const sdkLogger = new SdkLoggerImpl();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request tracing for Portal API calls.
|
|
3
|
+
* Aligns with portal-react-native X-Portal-Trace-Id / traceId behavior.
|
|
4
|
+
*
|
|
5
|
+
* Propagation in the Web SDK:
|
|
6
|
+
* - Browser: High-level methods (backup, recover, sendAsset, provider.request) accept optional
|
|
7
|
+
* traceId or generate one. They pass it to handleRequestToIframeAndPost (postMessage) or
|
|
8
|
+
* to fetch headers (X-Portal-Trace-Id).
|
|
9
|
+
* - Iframe: On each portal:* message, the iframe reads traceId from the message (top-level or
|
|
10
|
+
* data.traceId), calls setRequestTraceId(traceId), which sets it on Api, Mpc, ZeroX, etc.
|
|
11
|
+
* All REST and MPC calls made during that request then use the same trace ID (header or reqId).
|
|
12
|
+
* - After the request completes, clearRequestTraceId() is called so the next message gets a
|
|
13
|
+
* fresh or caller-provided trace ID.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* HTTP header name for request tracing.
|
|
17
|
+
* Used by connect-api (client + custodian APIs) in the Web SDK.
|
|
18
|
+
* Note: Other internal services may use different header names.
|
|
19
|
+
*/
|
|
20
|
+
export const X_PORTAL_TRACE_ID_HEADER = 'X-Portal-Trace-Id';
|
|
21
|
+
/**
|
|
22
|
+
* Generates a UUID v4 trace ID for request correlation.
|
|
23
|
+
* Uses crypto.randomUUID() when available; otherwise crypto.getRandomValues() for better
|
|
24
|
+
* quality than Math.random(); falls back to Math.random() only when no crypto API exists.
|
|
25
|
+
*/
|
|
26
|
+
export function generateTraceId() {
|
|
27
|
+
if (typeof crypto !== 'undefined' &&
|
|
28
|
+
typeof crypto.randomUUID === 'function') {
|
|
29
|
+
return crypto.randomUUID();
|
|
30
|
+
}
|
|
31
|
+
if (typeof crypto !== 'undefined' &&
|
|
32
|
+
typeof crypto.getRandomValues === 'function') {
|
|
33
|
+
return fallbackUuidV4WithCrypto(crypto);
|
|
34
|
+
}
|
|
35
|
+
return fallbackUuidV4WithMathRandom();
|
|
36
|
+
}
|
|
37
|
+
function fallbackUuidV4WithCrypto(crypto) {
|
|
38
|
+
const bytes = new Uint8Array(16);
|
|
39
|
+
crypto.getRandomValues(bytes);
|
|
40
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
41
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
42
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
43
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
44
|
+
}
|
|
45
|
+
function fallbackUuidV4WithMathRandom() {
|
|
46
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
47
|
+
const r = (Math.random() * 16) | 0;
|
|
48
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
49
|
+
return v.toString(16);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared types barrel export
|
|
3
3
|
*
|
|
4
|
-
* This module exports all shared types used by both browser and iframe packages
|
|
4
|
+
* This module exports all shared types used by both browser and iframe packages.
|
|
5
|
+
* For trace ID helpers (X_PORTAL_TRACE_ID_HEADER, generateTraceId), use shared/trace.
|
|
5
6
|
*/
|
|
6
7
|
// Common types
|
|
7
8
|
export * from './common';
|
package/package.json
CHANGED
package/src/index.test.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -56,6 +56,7 @@ import Delegations from './integrations/delegations'
|
|
|
56
56
|
import PasskeyService from './passkeys'
|
|
57
57
|
import { EvmAccountType } from './namespaces/evmAccountType'
|
|
58
58
|
import { sdkLogger } from './logger'
|
|
59
|
+
import { generateTraceId } from './shared/trace'
|
|
59
60
|
import type { LogLevel, ILogger } from '../types'
|
|
60
61
|
|
|
61
62
|
class Portal {
|
|
@@ -627,22 +628,29 @@ class Portal {
|
|
|
627
628
|
chain: string,
|
|
628
629
|
params: SendAssetParams,
|
|
629
630
|
): Promise<string> {
|
|
631
|
+
const traceId = params.traceId ?? generateTraceId()
|
|
632
|
+
sdkLogger.debug(
|
|
633
|
+
'[Portal] sendAsset started (single traceId for build + sign)',
|
|
634
|
+
{ traceId },
|
|
635
|
+
)
|
|
636
|
+
|
|
630
637
|
try {
|
|
631
638
|
// Convert the chain to a chain ID
|
|
632
639
|
const chainId = this.convertChainToChainId(chain)
|
|
633
640
|
|
|
634
|
-
// Build the transaction
|
|
641
|
+
// Build the transaction (same traceId for correlation with backend)
|
|
635
642
|
const buildTxResponse = await this.buildTransaction(
|
|
636
643
|
chainId,
|
|
637
644
|
params.to,
|
|
638
645
|
params.token,
|
|
639
646
|
params.amount,
|
|
647
|
+
traceId,
|
|
640
648
|
)
|
|
641
649
|
|
|
642
650
|
// Get the chain namespace
|
|
643
651
|
const chainNamespace = chainId.split(':')[0] ?? ''
|
|
644
652
|
|
|
645
|
-
// Send the transaction based on chain namespace
|
|
653
|
+
// Send the transaction based on chain namespace (same traceId on request)
|
|
646
654
|
let response
|
|
647
655
|
switch (chainNamespace) {
|
|
648
656
|
case 'eip155': {
|
|
@@ -652,6 +660,7 @@ class Portal {
|
|
|
652
660
|
params: [buildTxResponse.transaction],
|
|
653
661
|
sponsorGas: params.sponsorGas,
|
|
654
662
|
signatureApprovalMemo: params.signatureApprovalMemo,
|
|
663
|
+
traceId,
|
|
655
664
|
})
|
|
656
665
|
break
|
|
657
666
|
}
|
|
@@ -662,6 +671,7 @@ class Portal {
|
|
|
662
671
|
params: [buildTxResponse.transaction],
|
|
663
672
|
sponsorGas: params.sponsorGas,
|
|
664
673
|
signatureApprovalMemo: params.signatureApprovalMemo,
|
|
674
|
+
traceId,
|
|
665
675
|
})
|
|
666
676
|
break
|
|
667
677
|
}
|
|
@@ -1076,8 +1086,9 @@ class Portal {
|
|
|
1076
1086
|
to: string,
|
|
1077
1087
|
token: string,
|
|
1078
1088
|
amount: string,
|
|
1089
|
+
traceId?: string,
|
|
1079
1090
|
): Promise<BuiltTransaction> {
|
|
1080
|
-
return this.mpc?.buildTransaction(chainId, to, token, amount)
|
|
1091
|
+
return this.mpc?.buildTransaction(chainId, to, token, amount, traceId)
|
|
1081
1092
|
}
|
|
1082
1093
|
|
|
1083
1094
|
/*******************************
|
package/src/logger/index.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export interface ILogger {
|
|
4
|
-
error(...args: unknown[]): void
|
|
5
|
-
warn(...args: unknown[]): void
|
|
6
|
-
info(...args: unknown[]): void
|
|
7
|
-
debug(...args: unknown[]): void
|
|
8
|
-
}
|
|
1
|
+
import type { LogLevel, ILogger } from '../../types'
|
|
9
2
|
|
|
10
3
|
const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
|
|
11
4
|
none: 0,
|