Skip to content

Commit 2deecf4

Browse files
authored
refactor: remove timeout for messages requiring user actions (#279)
<!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? Are there any issues or other links reviewers should consult to understand this pull request better? For instance: * Fixes #12345 * See: #67890 --> #271 introduced timeouts for all messages between the Ledger bridge and the iframe. Though a 20-second timeout may not be enough for the user to grab the device and accept the request - especially if the user wants to look closely at the signature request on the hardware device. For this reason, the timeout is being removed for messages requiring user intervention, while keeping it for messages that do not expect user actions (i.e. `updateTransportMethod` and `attemptMakeApp`)
1 parent a0bee4c commit 2deecf4

File tree

3 files changed

+32
-83
lines changed

3 files changed

+32
-83
lines changed

packages/keyring-eth-ledger-bridge/jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module.exports = merge(baseConfig, {
2323
// An object that configures minimum threshold enforcement for coverage results
2424
coverageThreshold: {
2525
global: {
26-
branches: 91.83,
26+
branches: 91.89,
2727
functions: 97.87,
2828
lines: 97.16,
2929
statements: 97.19,

packages/keyring-eth-ledger-bridge/src/ledger-iframe-bridge.test.ts

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -433,18 +433,6 @@ describe('LedgerIframeBridge', function () {
433433
// eslint-disable-next-line @typescript-eslint/unbound-method
434434
expect(bridge.iframe?.contentWindow?.postMessage).toHaveBeenCalled();
435435
});
436-
437-
it('throws a timeout error when the message does not receive a response', async function () {
438-
const params = { hdPath: "m/44'/60'/0'/0", tx: '' };
439-
440-
stubKeyringIFramePostMessage(bridge, () => {
441-
jest.advanceTimersByTime(20_000);
442-
});
443-
444-
await expect(bridge.deviceSignTransaction(params)).rejects.toThrow(
445-
'Ledger iframe message timeout',
446-
);
447-
});
448436
});
449437

450438
describe('deviceSignMessage', function () {
@@ -507,18 +495,6 @@ describe('LedgerIframeBridge', function () {
507495
// eslint-disable-next-line @typescript-eslint/unbound-method
508496
expect(bridge.iframe?.contentWindow?.postMessage).toHaveBeenCalled();
509497
});
510-
511-
it('throws a timeout error when the message does not receive a response', async function () {
512-
const params = { hdPath: "m/44'/60'/0'/0", message: '' };
513-
514-
stubKeyringIFramePostMessage(bridge, () => {
515-
jest.advanceTimersByTime(20_000);
516-
});
517-
518-
await expect(bridge.deviceSignMessage(params)).rejects.toThrow(
519-
'Ledger iframe message timeout',
520-
);
521-
});
522498
});
523499

524500
describe('deviceSignTypedData', function () {
@@ -595,16 +571,6 @@ describe('LedgerIframeBridge', function () {
595571
// eslint-disable-next-line @typescript-eslint/unbound-method
596572
expect(bridge.iframe?.contentWindow?.postMessage).toHaveBeenCalled();
597573
});
598-
599-
it('throws a timeout error when the message does not receive a response', async function () {
600-
stubKeyringIFramePostMessage(bridge, () => {
601-
jest.advanceTimersByTime(20_000);
602-
});
603-
604-
await expect(bridge.deviceSignTypedData(params)).rejects.toThrow(
605-
'Ledger iframe message timeout',
606-
);
607-
});
608574
});
609575

610576
describe('setOption', function () {

packages/keyring-eth-ledger-bridge/src/ledger-iframe-bridge.ts

Lines changed: 31 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ const LEDGER_IFRAME_ID = 'LEDGER-IFRAME';
1616

1717
const IFRAME_MESSAGE_TIMEOUT = 4000;
1818

19-
const USER_ACTION_REQUEST_TIMEOUT = 20_000;
20-
2119
export enum IFrameMessageAction {
2220
LedgerIsIframeReady = 'ledger-is-iframe-ready',
2321
LedgerConnectionChange = 'ledger-connection-change',
@@ -158,9 +156,12 @@ export class LedgerIframeBridge
158156
}
159157

160158
async attemptMakeApp(): Promise<boolean> {
161-
const response = await this.#sendMessage({
162-
action: IFrameMessageAction.LedgerMakeApp,
163-
});
159+
const response = await this.#sendMessage(
160+
{
161+
action: IFrameMessageAction.LedgerMakeApp,
162+
},
163+
{ timeout: IFRAME_MESSAGE_TIMEOUT },
164+
);
164165

165166
if ('success' in response && response.success) {
166167
return true;
@@ -177,10 +178,13 @@ export class LedgerIframeBridge
177178
throw new Error('The iframe is not loaded yet');
178179
}
179180

180-
const response = await this.#sendMessage({
181-
action: IFrameMessageAction.LedgerUpdateTransport,
182-
params: { transportType },
183-
});
181+
const response = await this.#sendMessage(
182+
{
183+
action: IFrameMessageAction.LedgerUpdateTransport,
184+
params: { transportType },
185+
},
186+
{ timeout: IFRAME_MESSAGE_TIMEOUT },
187+
);
184188

185189
if ('success' in response && response.success) {
186190
return true;
@@ -192,11 +196,7 @@ export class LedgerIframeBridge
192196
async getPublicKey(
193197
params: GetPublicKeyParams,
194198
): Promise<GetPublicKeyResponse> {
195-
return this.#deviceActionMessage(
196-
IFrameMessageAction.LedgerUnlock,
197-
params,
198-
USER_ACTION_REQUEST_TIMEOUT,
199-
);
199+
return this.#deviceActionMessage(IFrameMessageAction.LedgerUnlock, params);
200200
}
201201

202202
async deviceSignTransaction(
@@ -205,7 +205,6 @@ export class LedgerIframeBridge
205205
return this.#deviceActionMessage(
206206
IFrameMessageAction.LedgerSignTransaction,
207207
params,
208-
USER_ACTION_REQUEST_TIMEOUT,
209208
);
210209
}
211210

@@ -215,7 +214,6 @@ export class LedgerIframeBridge
215214
return this.#deviceActionMessage(
216215
IFrameMessageAction.LedgerSignPersonalMessage,
217216
params,
218-
USER_ACTION_REQUEST_TIMEOUT,
219217
);
220218
}
221219

@@ -225,59 +223,42 @@ export class LedgerIframeBridge
225223
return this.#deviceActionMessage(
226224
IFrameMessageAction.LedgerSignTypedData,
227225
params,
228-
USER_ACTION_REQUEST_TIMEOUT,
229226
);
230227
}
231228

232229
async #deviceActionMessage(
233230
action: IFrameMessageAction.LedgerUnlock,
234231
params: GetPublicKeyParams,
235-
timeout: number,
236232
): Promise<GetPublicKeyResponse>;
237233

238234
async #deviceActionMessage(
239235
action: IFrameMessageAction.LedgerSignTransaction,
240236
params: LedgerSignTransactionParams,
241-
timeout: number,
242237
): Promise<LedgerSignTransactionResponse>;
243238

244239
async #deviceActionMessage(
245240
action: IFrameMessageAction.LedgerSignPersonalMessage,
246241
params: LedgerSignMessageParams,
247-
timeout: number,
248242
): Promise<LedgerSignMessageResponse>;
249243

250244
async #deviceActionMessage(
251245
action: IFrameMessageAction.LedgerSignTypedData,
252246
params: LedgerSignTypedDataParams,
253-
timeout: number,
254247
): Promise<LedgerSignTypedDataResponse>;
255248

256249
async #deviceActionMessage(
257-
...[action, params, timeout]:
258-
| [IFrameMessageAction.LedgerUnlock, GetPublicKeyParams, number]
259-
| [
260-
IFrameMessageAction.LedgerSignTransaction,
261-
LedgerSignTransactionParams,
262-
number,
263-
]
264-
| [
265-
IFrameMessageAction.LedgerSignPersonalMessage,
266-
LedgerSignMessageParams,
267-
number,
268-
]
269-
| [
270-
IFrameMessageAction.LedgerSignTypedData,
271-
LedgerSignTypedDataParams,
272-
number,
273-
]
250+
...[action, params]:
251+
| [IFrameMessageAction.LedgerUnlock, GetPublicKeyParams]
252+
| [IFrameMessageAction.LedgerSignTransaction, LedgerSignTransactionParams]
253+
| [IFrameMessageAction.LedgerSignPersonalMessage, LedgerSignMessageParams]
254+
| [IFrameMessageAction.LedgerSignTypedData, LedgerSignTypedDataParams]
274255
): Promise<
275256
| GetPublicKeyResponse
276257
| LedgerSignTransactionResponse
277258
| LedgerSignMessageResponse
278259
| LedgerSignTypedDataResponse
279260
> {
280-
const response = await this.#sendMessage({ action, params }, timeout);
261+
const response = await this.#sendMessage({ action, params });
281262

282263
if ('payload' in response && response.payload) {
283264
if ('success' in response && response.success) {
@@ -338,7 +319,7 @@ export class LedgerIframeBridge
338319

339320
async #sendMessage<TAction extends IFrameMessageAction>(
340321
message: IFrameMessage<TAction>,
341-
timeout = IFRAME_MESSAGE_TIMEOUT,
322+
{ timeout }: { timeout?: number } = {},
342323
): Promise<IFrameMessageResponse> {
343324
this.currentMessageId += 1;
344325

@@ -360,14 +341,16 @@ export class LedgerIframeBridge
360341
throw new Error('The iframe is not loaded yet');
361342
}
362343

363-
setTimeout(() => {
364-
if (this.#messageResponseHandles.has(postMsg.messageId)) {
365-
this.#messageResponseHandles.delete(postMsg.messageId);
366-
messageResponseHandle.reject(
367-
new Error('Ledger iframe message timeout'),
368-
);
369-
}
370-
}, timeout);
344+
if (timeout) {
345+
setTimeout(() => {
346+
if (this.#messageResponseHandles.has(postMsg.messageId)) {
347+
this.#messageResponseHandles.delete(postMsg.messageId);
348+
messageResponseHandle.reject(
349+
new Error('Ledger iframe message timeout'),
350+
);
351+
}
352+
}, timeout);
353+
}
371354

372355
this.iframe.contentWindow.postMessage(postMsg, '*');
373356

0 commit comments

Comments
 (0)