1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-12 12:47:14 +01:00
metamask-extension/app/scripts/lib/metaRPCClientFactory.js
Olusegun Akintayo 4fa4930c8a
Add friendly error handling when background throws an error before listening for connection (#14461)
* When background port closes, UI should display a user friendly error.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Remove console.log

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

A couple of fixes
    1. Use timeout in metaRPCClientFactory to check if UI can't
       communicate with bg
    2. Refactor locale setup
    3. Fixed wording/capitalization
    4. Fix locales usage so that linting works
    5. Refactor CSS

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

do not simulate errorwq

Refactor loading css

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Remove the onDisconnect event handler in ui as this is handled in
metarpcclientfactory

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Do not throw in bg

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Fix PR comments

Remove unused message 'failedToLoadMessage'

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Move usage of locales to shared/** so that linter can see it.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Do not simulate error.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

metarpc can handle multiple requests, responseHandled should be a map.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

reload metamask button on critical error

Use metamask state (if available) to the locale, else read locale files
manually.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

use constant and numeric separator

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

refactor error utils

remove error simulation

Memoize setupLocale function

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

test cases

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Do not simulate error

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

1. store should be metamask state
2. code refactorings.
Tests: mock setupLocale

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Mock fetchLocale instead
Test setup locale

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

UI/CSS changes.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

Do not simulate failure

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* spell MetaMask correctly

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Rename state to mockStore

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* we should clean up this.responseHandled[id] in the error case.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* Fixed PR comments.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>

* clean up response handled.

Signed-off-by: Akintayo A. Olusegun <akintayo.segun@gmail.com>
2022-06-08 00:37:15 +04:00

115 lines
3.0 KiB
JavaScript

import { EthereumRpcError } from 'eth-rpc-errors';
import SafeEventEmitter from 'safe-event-emitter';
import createRandomId from '../../../shared/modules/random-id';
import { TEN_SECONDS_IN_MILLISECONDS } from '../../../ui/helpers/constants/critical-error';
class MetaRPCClient {
constructor(connectionStream) {
this.connectionStream = connectionStream;
this.notificationChannel = new SafeEventEmitter();
this.uncaughtErrorChannel = new SafeEventEmitter();
this.requests = new Map();
this.connectionStream.on('data', this.handleResponse.bind(this));
this.connectionStream.on('end', this.close.bind(this));
this.responseHandled = {};
}
send(id, payload, cb) {
this.requests.set(id, cb);
this.connectionStream.write(payload);
this.responseHandled[id] = false;
setTimeout(() => {
if (!this.responseHandled[id] && cb) {
delete this.responseHandled[id];
return cb(new Error('No response from RPC'), null);
}
delete this.responseHandled[id];
// needed for linter to pass
return true;
}, TEN_SECONDS_IN_MILLISECONDS);
}
onNotification(handler) {
this.notificationChannel.addListener('notification', (data) => {
handler(data);
});
}
onUncaughtError(handler) {
this.uncaughtErrorChannel.addListener('error', (error) => {
handler(error);
});
}
close() {
this.notificationChannel.removeAllListeners();
this.uncaughtErrorChannel.removeAllListeners();
}
handleResponse(data) {
const { id, result, error, method, params } = data;
const isNotification = id === undefined && error === undefined;
const cb = this.requests.get(id);
this.responseHandled[id] = true;
if (method && params && !isNotification) {
// dont handle server-side to client-side requests
return;
}
if (method && params && isNotification) {
// handle servier-side to client-side notification
this.notificationChannel.emit('notification', data);
return;
}
if (error) {
const e = new EthereumRpcError(error.code, error.message, error.data);
// preserve the stack from serializeError
e.stack = error.stack;
if (cb) {
this.requests.delete(id);
cb(e);
return;
}
this.uncaughtErrorChannel.emit('error', e);
return;
}
if (!cb) {
// not found in request list
return;
}
this.requests.delete(id);
cb(null, result);
}
}
const metaRPCClientFactory = (connectionStream) => {
const metaRPCClient = new MetaRPCClient(connectionStream);
return new Proxy(metaRPCClient, {
get: (object, property) => {
if (object[property]) {
return object[property];
}
return (...p) => {
const cb = p[p.length - 1];
const params = p.slice(0, -1);
const id = createRandomId();
const payload = {
jsonrpc: '2.0',
method: property,
params,
id,
};
object.send(id, payload, cb);
};
},
});
};
export default metaRPCClientFactory;