diff --git a/package.json b/package.json index a110e937..b6ebeed3 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "test:watch": "jest --watch" }, "dependencies": { - "@metamask/eth-json-rpc-provider": "^4.0.0", + "@metamask/eth-json-rpc-provider": "^4.1.0", "@metamask/safe-event-emitter": "^3.0.0", "@metamask/utils": "^8.1.0", "json-rpc-random-id": "^1.0.1", diff --git a/src/PollingBlockTracker.test.ts b/src/PollingBlockTracker.test.ts index a83d43ac..7a598db2 100644 --- a/src/PollingBlockTracker.test.ts +++ b/src/PollingBlockTracker.test.ts @@ -66,9 +66,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -117,9 +115,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -177,9 +173,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -197,20 +191,17 @@ describe('PollingBlockTracker', () => { await withPollingBlockTracker( { blockTracker: { setSkipCacheFlag: true } }, async ({ provider, blockTracker }) => { - jest.spyOn(provider, 'sendAsync'); + jest.spyOn(provider, 'request'); await blockTracker.getLatestBlock(); - expect(provider.sendAsync).toHaveBeenCalledWith( - { - jsonrpc: '2.0' as const, - id: expect.any(Number), - method: 'eth_blockNumber' as const, - params: [], - skipCache: true, - }, - expect.any(Function), - ); + expect(provider.request).toHaveBeenCalledWith({ + jsonrpc: '2.0' as const, + id: expect.any(Number), + method: 'eth_blockNumber' as const, + params: [], + skipCache: true, + }); }, ); }); @@ -219,14 +210,12 @@ describe('PollingBlockTracker', () => { recordCallsToSetTimeout(); await withPollingBlockTracker(async ({ provider, blockTracker }) => { - const sendAsyncSpy = jest.spyOn(provider, 'sendAsync'); + const requestSpy = jest.spyOn(provider, 'request'); await blockTracker.getLatestBlock(); await blockTracker.getLatestBlock(); - const requestsForLatestBlock = sendAsyncSpy.mock.calls.filter( - (args) => { - return args[0].method === 'eth_blockNumber'; - }, - ); + const requestsForLatestBlock = requestSpy.mock.calls.filter((args) => { + return args[0].method === 'eth_blockNumber'; + }); expect(requestsForLatestBlock).toHaveLength(1); }); }); @@ -244,22 +233,18 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, blockTracker: blockTrackerOptions, }, async ({ provider, blockTracker }) => { - const sendAsyncSpy = jest.spyOn(provider, 'sendAsync'); + const requestSpy = jest.spyOn(provider, 'request'); await blockTracker.getLatestBlock(); // When the block tracker stops, there may be two `setTimeout`s in // play: one to go to the next iteration of the block tracker @@ -269,7 +254,7 @@ describe('PollingBlockTracker', () => { blockTrackerOptions.blockResetDuration, ); await blockTracker.getLatestBlock(); - const requestsForLatestBlock = sendAsyncSpy.mock.calls.filter( + const requestsForLatestBlock = requestSpy.mock.calls.filter( (args) => { return args[0].method === 'eth_blockNumber'; }, @@ -280,45 +265,6 @@ describe('PollingBlockTracker', () => { }); METHODS_TO_ADD_LISTENER.forEach((methodToAddListener) => { - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not throw if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForLatestBlock = blockTracker.getLatestBlock(); - - const caughtError = await promiseForCaughtError; - expect(caughtError.message).toMatch( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ); - const latestBlock = await promiseForLatestBlock; - expect(latestBlock).toBe('0x0'); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not throw if, while making the request for the latest block number, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -334,9 +280,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -373,9 +317,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -410,9 +352,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -435,41 +375,6 @@ describe('PollingBlockTracker', () => { }); }); - it('should log an error if the request for the latest block number returns an error response and there is nothing listening to "error"', async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - jest.spyOn(console, 'error').mockImplementation(EMPTY_FUNCTION); - - blockTracker.getLatestBlock(); - await new Promise((resolve) => { - blockTracker.on('_waitingForNextIteration', resolve); - }); - - expect(console.error).toHaveBeenCalledWith( - expect.objectContaining({ - message: expect.stringMatching( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ), - }), - ); - }, - ); - }); - it('should log an error if, while making a request for the latest block number, the provider throws an Error and there is nothing listening to "error"', async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -582,9 +487,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -610,9 +513,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -646,15 +547,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -687,9 +584,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -710,9 +605,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -731,45 +624,17 @@ describe('PollingBlockTracker', () => { await withPollingBlockTracker( { blockTracker: { setSkipCacheFlag: true } }, async ({ provider, blockTracker }) => { - jest.spyOn(provider, 'sendAsync'); + jest.spyOn(provider, 'request'); await blockTracker.checkForLatestBlock(); - expect(provider.sendAsync).toHaveBeenCalledWith( - { - jsonrpc: '2.0' as const, - id: expect.any(Number), - method: 'eth_blockNumber' as const, - params: [], - skipCache: true, - }, - expect.any(Function), - ); - }, - ); - }); - - it(`should not emit the "error" event, but should throw instead if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForLatestBlock = blockTracker.checkForLatestBlock(); - await expect(promiseForLatestBlock).rejects.toThrow( - 'PollingBlockTracker - encountered error fetching block:\nboom', - ); + expect(provider.request).toHaveBeenCalledWith({ + jsonrpc: '2.0' as const, + id: expect.any(Number), + method: 'eth_blockNumber' as const, + params: [], + skipCache: true, + }); }, ); }); @@ -788,9 +653,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -815,9 +678,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -850,15 +711,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -882,15 +739,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -914,15 +767,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -946,15 +795,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -981,15 +826,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -1013,15 +854,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -1045,15 +882,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1077,15 +910,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1124,9 +953,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1149,9 +976,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1180,15 +1005,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -1246,63 +1067,19 @@ describe('PollingBlockTracker', () => { await withPollingBlockTracker( { blockTracker: { setSkipCacheFlag: true } }, async ({ provider, blockTracker }) => { - jest.spyOn(provider, 'sendAsync'); + jest.spyOn(provider, 'request'); await new Promise((resolve) => { blockTracker[methodToAddListener]('latest', resolve); }); - expect(provider.sendAsync).toHaveBeenCalledWith( - { - jsonrpc: '2.0' as const, - id: expect.any(Number), - method: 'eth_blockNumber' as const, - params: [], - skipCache: true, - }, - expect.any(Function), - ); - }, - ); - }); - - it(`should emit the "error" event and should not kill the block tracker if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForLatestBlock = new Promise((resolve) => { - blockTracker[methodToAddListener]('latest', resolve); + expect(provider.request).toHaveBeenCalledWith({ + jsonrpc: '2.0' as const, + id: expect.any(Number), + method: 'eth_blockNumber' as const, + params: [], + skipCache: true, }); - - const caughtError = await promiseForCaughtError; - expect(caughtError.message).toMatch( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ); - const latestBlock = await promiseForLatestBlock; - expect(latestBlock).toBe('0x0'); }, ); }); @@ -1322,9 +1099,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1363,9 +1138,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1402,9 +1175,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1428,47 +1199,6 @@ describe('PollingBlockTracker', () => { ); }); - it('should log an error if the request for the latest block number returns an error response and there is nothing listening to "error"', async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - jest.spyOn(console, 'error').mockImplementation(EMPTY_FUNCTION); - - blockTracker[methodToAddListener]('latest', EMPTY_FUNCTION); - await new Promise((resolve) => { - blockTracker.on('_waitingForNextIteration', resolve); - }); - - expect(console.error).toHaveBeenCalledWith( - expect.objectContaining({ - message: expect.stringMatching( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ), - }), - ); - }, - ); - }); - it('should log an error if, while making a request for the latest block number, the provider throws an Error and there is nothing listening to "error"', async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -1484,9 +1214,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1525,9 +1253,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1564,9 +1290,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1607,15 +1331,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -1651,15 +1371,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1695,15 +1411,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1742,15 +1454,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -1786,15 +1494,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1830,15 +1534,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1885,9 +1585,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1910,9 +1608,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1941,15 +1637,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -2007,63 +1699,19 @@ describe('PollingBlockTracker', () => { await withPollingBlockTracker( { blockTracker: { setSkipCacheFlag: true } }, async ({ provider, blockTracker }) => { - jest.spyOn(provider, 'sendAsync'); + jest.spyOn(provider, 'request'); await new Promise((resolve) => { blockTracker[methodToAddListener]('sync', resolve); }); - expect(provider.sendAsync).toHaveBeenCalledWith( - { - jsonrpc: '2.0' as const, - id: expect.any(Number), - method: 'eth_blockNumber' as const, - params: [], - skipCache: true, - }, - expect.any(Function), - ); - }, - ); - }); - - it(`should emit the "error" event and should not kill the block tracker if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); + expect(provider.request).toHaveBeenCalledWith({ + jsonrpc: '2.0' as const, + id: expect.any(Number), + method: 'eth_blockNumber' as const, + params: [], + skipCache: true, }); - - const promiseForSync = new Promise((resolve) => { - blockTracker[methodToAddListener]('sync', resolve); - }); - - const caughtError = await promiseForCaughtError; - expect(caughtError.message).toMatch( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ); - const sync = await promiseForSync; - expect(sync).toStrictEqual({ oldBlock: null, newBlock: '0x0' }); }, ); }); @@ -2083,9 +1731,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2124,9 +1770,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2163,9 +1807,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2175,51 +1817,16 @@ describe('PollingBlockTracker', () => { blockTracker[methodToAddListener]('error', resolve); }); - const promiseForSync = new Promise((resolve) => { - blockTracker[methodToAddListener]('sync', resolve); - }); - - const caughtError = await promiseForCaughtError; - expect(caughtError.message).toMatch( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: boom/u, - ); - const sync = await promiseForSync; - expect(sync).toStrictEqual({ oldBlock: null, newBlock: '0x0' }); - }, - ); - }); - - it('should log an error if the request for the latest block number returns an error response and there is nothing listening to "error"', async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - jest.spyOn(console, 'error').mockImplementation(EMPTY_FUNCTION); - - blockTracker[methodToAddListener]('sync', EMPTY_FUNCTION); - await new Promise((resolve) => { - blockTracker.on('_waitingForNextIteration', resolve); + const promiseForSync = new Promise((resolve) => { + blockTracker[methodToAddListener]('sync', resolve); }); - expect(console.error).toHaveBeenCalledWith( - expect.objectContaining({ - message: expect.stringMatching( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ), - }), + const caughtError = await promiseForCaughtError; + expect(caughtError.message).toMatch( + /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: boom/u, ); + const sync = await promiseForSync; + expect(sync).toStrictEqual({ oldBlock: null, newBlock: '0x0' }); }, ); }); @@ -2344,15 +1951,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -2388,15 +1991,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2431,15 +2030,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2477,15 +2072,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -2521,15 +2112,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2565,15 +2152,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2619,9 +2202,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2676,9 +2257,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2724,21 +2303,15 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_blockNumber', - response: { - result: '0x2', - }, + result: '0x2', }, ], }, @@ -2805,9 +2378,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2896,15 +2467,11 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, ], }, @@ -2941,9 +2508,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2968,47 +2533,6 @@ describe('PollingBlockTracker', () => { }); METHODS_TO_ADD_LISTENER.forEach((methodToAddListener) => { - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not throw if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForLatestBlock = new Promise((resolve) => { - blockTracker.once('latest', resolve); - }); - - const caughtError = await promiseForCaughtError; - expect(caughtError.message).toMatch( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ); - const latestBlock = await promiseForLatestBlock; - expect(latestBlock).toBe('0x0'); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not throw if, while making the request for the latest block number, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -3024,9 +2548,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3065,9 +2587,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3104,9 +2624,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3131,47 +2649,6 @@ describe('PollingBlockTracker', () => { }); }); - it('should log an error if the request for the latest block number returns an error response and there is nothing listening to "error"', async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - jest.spyOn(console, 'error').mockImplementation(EMPTY_FUNCTION); - - blockTracker.once('latest', EMPTY_FUNCTION); - await new Promise((resolve) => { - blockTracker.on('_waitingForNextIteration', resolve); - }); - - expect(console.error).toHaveBeenCalledWith( - expect.objectContaining({ - message: expect.stringMatching( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ), - }), - ); - }, - ); - }); - it('should log an error if, while making a request for the latest block number, the provider throws an Error and there is nothing listening to "error"', async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -3187,9 +2664,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3228,9 +2703,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3267,9 +2740,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3324,9 +2795,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3351,47 +2820,6 @@ describe('PollingBlockTracker', () => { }); METHODS_TO_ADD_LISTENER.forEach((methodToAddListener) => { - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not throw if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForSync = new Promise((resolve) => { - blockTracker.once('sync', resolve); - }); - - const caughtError = await promiseForCaughtError; - expect(caughtError.message).toMatch( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ); - const sync = await promiseForSync; - expect(sync).toStrictEqual({ oldBlock: null, newBlock: '0x0' }); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not throw if, while making the request for the latest block number, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -3407,9 +2835,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3448,9 +2874,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3487,9 +2911,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3514,47 +2936,6 @@ describe('PollingBlockTracker', () => { }); }); - it('should log an error if the request for the latest block number returns an error response and there is nothing listening to "error"', async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withPollingBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - jest.spyOn(console, 'error').mockImplementation(EMPTY_FUNCTION); - - blockTracker.once('sync', EMPTY_FUNCTION); - await new Promise((resolve) => { - blockTracker.on('_waitingForNextIteration', resolve); - }); - - expect(console.error).toHaveBeenCalledWith( - expect.objectContaining({ - message: expect.stringMatching( - /^PollingBlockTracker - encountered an error while attempting to update latest block:\nError: PollingBlockTracker - encountered error fetching block:\nboom/u, - ), - }), - ); - }, - ); - }); - it('should log an error if, while making a request for the latest block number, the provider throws an Error and there is nothing listening to "error"', async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -3570,9 +2951,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3611,9 +2990,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3650,9 +3027,7 @@ describe('PollingBlockTracker', () => { }, { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3702,9 +3077,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3747,9 +3120,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3806,9 +3177,7 @@ describe('PollingBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, diff --git a/src/PollingBlockTracker.ts b/src/PollingBlockTracker.ts index 12c82a47..8ab89b99 100644 --- a/src/PollingBlockTracker.ts +++ b/src/PollingBlockTracker.ts @@ -1,8 +1,7 @@ import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider'; import SafeEventEmitter from '@metamask/safe-event-emitter'; -import type { JsonRpcRequest } from '@metamask/utils'; +import { getErrorMessage, type JsonRpcRequest } from '@metamask/utils'; import getCreateRandomId from 'json-rpc-random-id'; -import pify from 'pify'; import type { BlockTracker } from './BlockTracker'; import { projectLogger, createModuleLogger } from './logging-utils'; @@ -272,14 +271,14 @@ export class PollingBlockTracker } log('Making request', req); - const res = await pify((cb) => this._provider.sendAsync(req, cb))(); - log('Got response', res); - if (res.error) { - throw new Error( - `PollingBlockTracker - encountered error fetching block:\n${res.error.message}`, - ); + try { + const result = await this._provider.request<[], string>(req); + log('Got result', result); + return result; + } catch (error) { + log('Encountered error fetching block', getErrorMessage(error)); + throw error; } - return res.result; } /** diff --git a/src/SubscribeBlockTracker.test.ts b/src/SubscribeBlockTracker.test.ts index 47b69911..6d78f230 100644 --- a/src/SubscribeBlockTracker.test.ts +++ b/src/SubscribeBlockTracker.test.ts @@ -70,9 +70,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -106,9 +104,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -164,9 +160,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -184,10 +178,10 @@ describe('SubscribeBlockTracker', () => { recordCallsToSetTimeout(); await withSubscribeBlockTracker(async ({ provider, blockTracker }) => { - const sendAsyncSpy = jest.spyOn(provider, 'sendAsync'); + const requestSpy = jest.spyOn(provider, 'request'); await blockTracker[methodToGetLatestBlock](); await blockTracker[methodToGetLatestBlock](); - const requestsForLatestBlock = sendAsyncSpy.mock.calls.filter( + const requestsForLatestBlock = requestSpy.mock.calls.filter( (args) => { return args[0].method === 'eth_blockNumber'; }, @@ -209,46 +203,34 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_unsubscribe', - response: { - result: true, - }, + result: true, }, { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_subscribe', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_unsubscribe', - response: { - result: true, - }, + result: true, }, ], }, blockTracker: blockTrackerOptions, }, async ({ provider, blockTracker }) => { - const sendAsyncSpy = jest.spyOn(provider, 'sendAsync'); + const requestSpy = jest.spyOn(provider, 'request'); await blockTracker[methodToGetLatestBlock](); // For PollingBlockTracker, there are possibly multiple // `setTimeout`s in play at this point. For SubscribeBlockTracker @@ -258,7 +240,7 @@ describe('SubscribeBlockTracker', () => { blockTrackerOptions.blockResetDuration, ); await blockTracker[methodToGetLatestBlock](); - const requestsForLatestBlock = sendAsyncSpy.mock.calls.filter( + const requestsForLatestBlock = requestSpy.mock.calls.filter( (args) => { return args[0].method === 'eth_blockNumber'; }, @@ -269,37 +251,6 @@ describe('SubscribeBlockTracker', () => { }); METHODS_TO_ADD_LISTENER.forEach((methodToAddListener) => { - it(`should not emit the "error" event (added via \`${methodToAddListener}\`) and should resolve with undefined if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForLatestBlock = - blockTracker[methodToGetLatestBlock](); - - await expect(promiseForCaughtError).toNeverResolve(); - const latestBlock = await promiseForLatestBlock; - expect(latestBlock).toBeUndefined(); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should never resolve if, while making the request for the latest block number, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); const thrownError = new Error('boom'); @@ -393,37 +344,6 @@ describe('SubscribeBlockTracker', () => { ); }); - it(`should not emit the "error" event (added via \`${methodToAddListener}\`) and should still resolve with the latest block number if the request to subscribe returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_subscribe', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForLatestBlock = - blockTracker[methodToGetLatestBlock](); - - await expect(promiseForCaughtError).toNeverResolve(); - const latestBlockNumber = await promiseForLatestBlock; - expect(latestBlockNumber).toBe('0x0'); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should never resolve if, while making the request to subscribe, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); const thrownError = new Error('boom'); @@ -517,34 +437,6 @@ describe('SubscribeBlockTracker', () => { ); }); - it(`should not emit the "error" event (added via \`${methodToAddListener}\`) if the request to unsubscribe returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_unsubscribe', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - await blockTracker[methodToGetLatestBlock](); - - await expect(promiseForCaughtError).toNeverResolve(); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) if, while making the request to unsubscribe, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); const thrownError = new Error('boom'); @@ -642,9 +534,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -670,9 +560,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -719,9 +607,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -744,9 +630,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -770,15 +654,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -822,15 +702,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -887,38 +763,6 @@ describe('SubscribeBlockTracker', () => { }); }); - it(`should not emit the "error" event and should emit "latest" with undefined if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForLatestBlock = new Promise((resolve) => { - blockTracker[methodToAddListener]('latest', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - const latestBlock = await promiseForLatestBlock; - expect(latestBlock).toBeUndefined(); - }, - ); - }); - it(`should emit the "error" event and should not emit "latest" if, while making the request for the latest block number, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -996,9 +840,7 @@ describe('SubscribeBlockTracker', () => { }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1019,43 +861,6 @@ describe('SubscribeBlockTracker', () => { ); }); - it(`should not emit the "error" event and should still emit "latest" with the latest block number if the request to subscribe returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, - }, - { - methodName: 'eth_subscribe', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const latestBlockNumber = await new Promise((resolve) => { - blockTracker[methodToAddListener]('latest', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - expect(latestBlockNumber).toBe('0x0'); - }, - ); - }); - it(`should emit the "error" event and should not emit "latest" if, while making the request to subscribe, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -1165,15 +970,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1219,15 +1020,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1272,15 +1069,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1328,15 +1121,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1382,15 +1171,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1435,15 +1220,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1501,9 +1282,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1526,9 +1305,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -1552,15 +1329,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1603,15 +1376,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1667,41 +1436,6 @@ describe('SubscribeBlockTracker', () => { }); }); - it(`should not emit the "error" event and should emit "sync" with undefined in place of the latest block if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForSync = new Promise((resolve) => { - blockTracker[methodToAddListener]('sync', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - const sync = await promiseForSync; - expect(sync).toStrictEqual({ - oldBlock: null, - newBlock: undefined, - }); - }, - ); - }); - it(`should emit the "error" event and should not emit "sync" if, while making a request for the latest block number, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -1796,41 +1530,6 @@ describe('SubscribeBlockTracker', () => { ); }); - it(`should not emit the "error" event and should still emit "sync" with the latest block number if the request to subscribe returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_subscribe', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForSync = new Promise((resolve) => { - blockTracker[methodToAddListener]('sync', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - const sync = await promiseForSync; - expect(sync).toStrictEqual({ - oldBlock: null, - newBlock: '0x0', - }); - }, - ); - }); - it(`should emit the "error" event and should not emit "sync" if, while making the request to subscribe, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); @@ -1940,15 +1639,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -1994,15 +1689,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -2046,15 +1737,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -2101,15 +1788,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -2155,15 +1838,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x1', - }, + result: '0x1', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -2208,15 +1887,11 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, { methodName: 'eth_subscribe', - response: { - result: '0x64', - }, + result: '0x64', }, ], }, @@ -2273,9 +1948,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2330,9 +2003,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2373,7 +2044,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_unsubscribe', - response: { + result: { error: 'boom', }, }, @@ -2541,9 +2212,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2584,7 +2253,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_unsubscribe', - response: { + result: { error: 'boom', }, }, @@ -2773,9 +2442,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -2800,38 +2467,6 @@ describe('SubscribeBlockTracker', () => { }); METHODS_TO_ADD_LISTENER.forEach((methodToAddListener) => { - it(`should not emit the "error" event (added via \`${methodToAddListener}\`) and should emit "latest" with undefined if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForLatestBlock = new Promise((resolve) => { - blockTracker.once('latest', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - const latestBlockNumber = await promiseForLatestBlock; - expect(latestBlockNumber).toBeUndefined(); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not emit "latest" if, while making the request for the latest block number, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); const thrownError = new Error('boom'); @@ -2928,37 +2563,6 @@ describe('SubscribeBlockTracker', () => { ); }); - it(`should not emit the "error" event (added via \`${methodToAddListener}\`) and should still emit "latest" if the request to subscribe returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_subscribe', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const latestBlockNumber = await new Promise((resolve) => { - blockTracker[methodToAddListener]('latest', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - expect(latestBlockNumber).toBe('0x0'); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not emit "latest" if, while making the request to subscribe, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); const thrownError = new Error('boom'); @@ -3055,36 +2659,6 @@ describe('SubscribeBlockTracker', () => { ); }); - it(`should not emit the "error" event (added via \`${methodToAddListener}\`) if the request to unsubscribe returns an error response`, async () => { - recordCallsToSetTimeout(); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_unsubscribe', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - await new Promise((resolve) => { - blockTracker.once('latest', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) if, while making the request to unsubscribe, the provider throws an Error`, async () => { recordCallsToSetTimeout(); @@ -3208,9 +2782,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3235,41 +2807,6 @@ describe('SubscribeBlockTracker', () => { }); METHODS_TO_ADD_LISTENER.forEach((methodToAddListener) => { - it(`should not emit the "error" event (added via \`${methodToAddListener}\`) and should emit "sync" with undefined in place of the latest block number if the request for the latest block number returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_blockNumber', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const promiseForSync = new Promise((resolve) => { - blockTracker.once('sync', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - const sync = await promiseForSync; - expect(sync).toStrictEqual({ - oldBlock: null, - newBlock: undefined, - }); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not emit "sync" if, while making the request for the latest block number, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); const thrownError = new Error('boom'); @@ -3366,37 +2903,6 @@ describe('SubscribeBlockTracker', () => { ); }); - it(`should not emit the "error" event (added via \`${methodToAddListener}\`) and should still emit "sync" if the request to subscribe returns an error response`, async () => { - recordCallsToSetTimeout({ numAutomaticCalls: 1 }); - - await withSubscribeBlockTracker( - { - provider: { - stubs: [ - { - methodName: 'eth_subscribe', - response: { - error: 'boom', - }, - }, - ], - }, - }, - async ({ blockTracker }) => { - const promiseForCaughtError = new Promise((resolve) => { - blockTracker[methodToAddListener]('error', resolve); - }); - - const sync = await new Promise((resolve) => { - blockTracker[methodToAddListener]('sync', resolve); - }); - - await expect(promiseForCaughtError).toNeverResolve(); - expect(sync).toStrictEqual({ oldBlock: null, newBlock: '0x0' }); - }, - ); - }); - it(`should emit the "error" event (added via \`${methodToAddListener}\`) and should not emit "sync" if, while making the request to subscribe, the provider throws an Error`, async () => { recordCallsToSetTimeout({ numAutomaticCalls: 1 }); const thrownError = new Error('boom'); @@ -3611,9 +3117,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3656,9 +3160,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, @@ -3714,9 +3216,7 @@ describe('SubscribeBlockTracker', () => { stubs: [ { methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }, ], }, diff --git a/src/SubscribeBlockTracker.ts b/src/SubscribeBlockTracker.ts index d0ac90b8..d81a8ea9 100644 --- a/src/SubscribeBlockTracker.ts +++ b/src/SubscribeBlockTracker.ts @@ -1,10 +1,6 @@ import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider'; import SafeEventEmitter from '@metamask/safe-event-emitter'; -import type { - Json, - JsonRpcNotification, - JsonRpcSuccess, -} from '@metamask/utils'; +import type { Json, JsonRpcNotification } from '@metamask/utils'; import getCreateRandomId from 'json-rpc-random-id'; import type { BlockTracker } from './BlockTracker'; @@ -256,22 +252,11 @@ export class SubscribeBlockTracker } private async _call(method: string, ...params: Json[]): Promise { - return new Promise((resolve, reject) => { - this._provider.sendAsync( - { - id: createRandomId(), - method, - params, - jsonrpc: '2.0', - }, - (err, res) => { - if (err) { - reject(err); - } else { - resolve((res as JsonRpcSuccess).result); - } - }, - ); + return this._provider.request({ + id: createRandomId(), + method, + params, + jsonrpc: '2.0', }); } diff --git a/tests/withBlockTracker.ts b/tests/withBlockTracker.ts index e7e1b05d..1e945e80 100644 --- a/tests/withBlockTracker.ts +++ b/tests/withBlockTracker.ts @@ -1,7 +1,10 @@ import { providerFromEngine } from '@metamask/eth-json-rpc-provider'; -import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider'; +import type { + // Eip1193Request, + SafeEventEmitterProvider, +} from '@metamask/eth-json-rpc-provider'; import { JsonRpcEngine } from '@metamask/json-rpc-engine'; -import type { JsonRpcRequest, JsonRpcResponse } from '@metamask/utils'; +import type { Json } from '@metamask/utils'; import util from 'util'; import type { @@ -32,27 +35,21 @@ type WithSubscribeBlockTrackerCallback = (args: { /** * An object that allows specifying the behavior of a specific invocation of - * `sendAsync`. The `methodName` always identifies the stub, but the behavior - * may be specified multiple ways: `sendAsync` can either return a promise or - * throw an error, and if it returns a promise, that promise can either be - * resolved with a response object or reject with an error. + * `request`. The `methodName` always identifies the stub, but the behavior + * may be specified multiple ways: `request` can either return a result + * or reject with an error. * * @property methodName - The RPC method to which this stub will be matched. - * @property response - Instructs `sendAsync` to return a promise that resolves - * with a response object. - * @property response.result - Specifies a successful response, with this as the - * `result`. - * @property response.error - Specifies an error response, with this as the - * `error`. - * @property implementation - Allows overriding `sendAsync` entirely. Useful if + * @property result - Instructs `request` to return a result. + * @property implementation - Allows overriding `request` entirely. Useful if * you want it to throw an error. - * @property error - Instructs `sendAsync` to return a promise that rejects with + * @property error - Instructs `request` to return a promise that rejects with * this error. */ type FakeProviderStub = | { methodName: string; - response: { result: any } | { error: string }; + result: any; } | { methodName: string; @@ -67,7 +64,7 @@ type FakeProviderStub = * The set of options that a new instance of FakeProvider takes. * * @property stubs - A set of objects that allow specifying the behavior - * of specific invocations of `sendAsync` matching a `methodName`. + * of specific invocations of `request` matching a `methodName`. */ interface FakeProviderOptions { stubs?: FakeProviderStub[]; @@ -79,7 +76,7 @@ interface FakeProviderOptions { * * @param options - The options. * @param options.stubs - A set of objects that allow specifying the behavior - * of specific invocations of `sendAsync` matching a `methodName`. + * of specific invocations of `request` matching a `methodName`. * @returns The fake provider. */ function getFakeProvider({ @@ -93,81 +90,53 @@ function getFakeProvider({ if (!stubs.some((stub) => stub.methodName === 'eth_blockNumber')) { stubs.push({ methodName: 'eth_blockNumber', - response: { - result: '0x0', - }, + result: '0x0', }); } if (!stubs.some((stub) => stub.methodName === 'eth_subscribe')) { stubs.push({ methodName: 'eth_subscribe', - response: { - result: '0x0', - }, + result: '0x0', }); } if (!stubs.some((stub) => stub.methodName === 'eth_unsubscribe')) { stubs.push({ methodName: 'eth_unsubscribe', - response: { - result: true, - }, + result: true, }); } const provider = providerFromEngine(new JsonRpcEngine()); jest - .spyOn(provider, 'sendAsync') - .mockImplementation( - ( - request: JsonRpcRequest, - callback: (err: unknown, response?: JsonRpcResponse) => void, - ) => { - const index = stubs.findIndex( - (stub) => stub.methodName === request.method, - ); - - if (index !== -1) { - const stub = stubs[index]; - stubs.splice(index, 1); - if ('implementation' in stub) { - stub.implementation(); - } else if ('response' in stub) { - if ('result' in stub.response) { - callback(null, { - jsonrpc: '2.0', - id: 1, - result: stub.response.result, - }); - } else if ('error' in stub.response) { - callback(null, { - jsonrpc: '2.0', - id: 1, - error: { - code: -999, - message: stub.response.error, - }, - }); - } - } else if ('error' in stub) { - callback(new Error(stub.error)); - } - return; + .spyOn(provider, 'request') + .mockImplementation(async (eip1193Request): Promise => { + const index = stubs.findIndex( + (stub) => stub.methodName === eip1193Request.method, + ); + + if (index !== -1) { + const stub = stubs[index]; + stubs.splice(index, 1); + if ('implementation' in stub) { + stub.implementation(); + } else if ('result' in stub) { + return stub.result; + } else if ('error' in stub) { + throw new Error(stub.error); } - - callback( - new Error( - `Could not find any stubs matching "${request.method}". Perhaps they've already been called?\n\n` + - 'The original set of stubs were:\n\n' + - `${util.inspect(originalStubs, { depth: null })}\n\n` + - 'Current set of stubs:\n\n' + - `${util.inspect(stubs, { depth: null })}\n\n`, - ), - ); - }, - ); + return null; + } + + throw new Error( + `Could not find any stubs matching "${eip1193Request.method}". Perhaps they've already been called?\n\n` + + 'The original set of stubs were:\n\n' + + `${util.inspect(originalStubs, { depth: null })}\n\n` + + 'Current set of stubs:\n\n' + + `${util.inspect(stubs, { depth: null })}\n\n`, + ); + }); return provider; } diff --git a/yarn.lock b/yarn.lock index f8a93d35..4f8134c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -700,14 +700,16 @@ resolved "https://registry.yarnpkg.com/@metamask/eslint-config/-/eslint-config-12.2.0.tgz#6cefc8331e4a34d26ae951882437371ecfe4e3c4" integrity sha512-BurYsht8MKdhvW2itUPPF8NkAhYtDdsCGHTSY7EzVvlmGP4jc9XrRZyfNwlt0zhB6MCMjHB1uNWwchtX7vBFjw== -"@metamask/eth-json-rpc-provider@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-4.0.0.tgz#0ab54548fceda1829f313f2f8ec072ef91cce687" - integrity sha512-HB/I5eNsS67rE5C+px5zASyEuAoK/UFeWe4c4FIm2U4zMo7Y2EED1p10A4zHRHjpctObHdvNDcZQbfu2gHcqsQ== +"@metamask/eth-json-rpc-provider@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-4.1.0.tgz#4191de86c9ace77035135522c42a67a65ab366d0" + integrity sha512-c8aVSZbtJBqFeQFN/4uQz5DsJFsjcwyITXwEOnuqJ1B0dQWu3EWE/VtKjyIMJDmfyvbU2Ihi7SXwRcgCkALI6A== dependencies: "@metamask/json-rpc-engine" "^9.0.0" + "@metamask/rpc-errors" "^6.2.1" "@metamask/safe-event-emitter" "^3.0.0" "@metamask/utils" "^8.3.0" + uuid "^8.3.2" "@metamask/json-rpc-engine@^9.0.0": version "9.0.0" @@ -4197,6 +4199,11 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"