-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor
eth_sendTransaction
method handler
The method handler for `eth_sendTransaction` has been moved to a separate module to simplify unit testing. Unit tests have been added as well. This module was written in TypeScript, which asked for some additional validation of the method parameters. We now throw a more explicit message when the params are missing, and when the transaction parameters are not an object. Previously the method would still throw an error in these scenarios, just with a less helpful message. The change in error message should be the only functional change here. This refactor is intended to make testing PR #5619 easier.
- Loading branch information
Showing
6 changed files
with
277 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// eslint-disable-next-line import/no-nodejs-modules | ||
import { inspect } from 'util'; | ||
import type { JsonRpcRequest, PendingJsonRpcResponse } from 'json-rpc-engine'; | ||
import eth_sendTransaction from './eth_sendTransaction'; | ||
|
||
/** | ||
* Construct a `eth_sendTransaction` JSON-RPC request. | ||
* | ||
* @param params - The request parameters. | ||
* @returns The JSON-RPC request. | ||
*/ | ||
function constructSendTransactionRequest( | ||
params: unknown, | ||
): JsonRpcRequest<unknown> & { method: 'eth_sendTransaction' } { | ||
return { | ||
jsonrpc: '2.0', | ||
id: 1, | ||
method: 'eth_sendTransaction', | ||
params, | ||
}; | ||
} | ||
|
||
/** | ||
* Construct a pending JSON-RPC response. | ||
* | ||
* @returns A pending JSON-RPC response. | ||
*/ | ||
function constructPendingJsonRpcResponse(): PendingJsonRpcResponse<unknown> { | ||
return { | ||
jsonrpc: '2.0', | ||
id: 1, | ||
}; | ||
} | ||
|
||
describe('eth_sendTransaction', () => { | ||
it('invokes next middleware for a valid request', async () => { | ||
const nextMock = jest.fn(); | ||
const minimalValidParams = [{}]; | ||
|
||
await eth_sendTransaction({ | ||
next: nextMock, | ||
req: constructSendTransactionRequest(minimalValidParams), | ||
res: constructPendingJsonRpcResponse(), | ||
validateAccountAndChainId: jest.fn(), | ||
}); | ||
|
||
expect(nextMock).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
const invalidParameters = [null, undefined, '', {}]; | ||
for (const invalidParameter of invalidParameters) { | ||
it(`throws a JSON-RPC invalid parameters error if given "${inspect( | ||
invalidParameter, | ||
)}"`, async () => { | ||
const nextMock = jest.fn(); | ||
|
||
await expect( | ||
async () => | ||
await eth_sendTransaction({ | ||
next: nextMock, | ||
req: constructSendTransactionRequest(invalidParameter), | ||
res: constructPendingJsonRpcResponse(), | ||
validateAccountAndChainId: jest.fn(), | ||
}), | ||
).rejects.toThrow('Invalid parameters: expected an array'); | ||
expect(nextMock).not.toHaveBeenCalled(); | ||
}); | ||
} | ||
|
||
const invalidTransactionParameters = [null, undefined, '', []]; | ||
for (const invalidTransactionParameter of invalidTransactionParameters) { | ||
it(`throws a JSON-RPC invalid parameters error if given "${inspect( | ||
invalidTransactionParameter, | ||
)}" transaction parameters`, async () => { | ||
const nextMock = jest.fn(); | ||
const invalidParameter = [invalidTransactionParameter]; | ||
|
||
await expect( | ||
async () => | ||
await eth_sendTransaction({ | ||
next: nextMock, | ||
req: constructSendTransactionRequest(invalidParameter), | ||
res: constructPendingJsonRpcResponse(), | ||
validateAccountAndChainId: jest.fn(), | ||
}), | ||
).rejects.toThrow( | ||
'Invalid parameters: expected the first parameter to be an object', | ||
); | ||
expect(nextMock).not.toHaveBeenCalled(); | ||
}); | ||
} | ||
|
||
it('throws any validation errors', async () => { | ||
const nextMock = jest.fn(); | ||
const minimalValidParams = [{}]; | ||
|
||
await expect( | ||
async () => | ||
await eth_sendTransaction({ | ||
next: nextMock, | ||
req: constructSendTransactionRequest(minimalValidParams), | ||
res: constructPendingJsonRpcResponse(), | ||
validateAccountAndChainId: jest.fn().mockImplementation(async () => { | ||
throw new Error('test validation error'); | ||
}), | ||
}), | ||
).rejects.toThrow('test validation error'); | ||
expect(nextMock).not.toHaveBeenCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import type { | ||
AsyncJsonRpcEngineNextCallback, | ||
JsonRpcRequest, | ||
PendingJsonRpcResponse, | ||
} from 'json-rpc-engine'; | ||
import { isObject, hasProperty } from '@metamask/utils'; | ||
import { ethErrors } from 'eth-json-rpc-errors'; | ||
|
||
/** | ||
* Handle a `eth_sendTransaction` request. | ||
* | ||
* @param args - Named arguments. | ||
* @param args.checkActiveAccountAndChainId - A function that validates the account and chain ID | ||
* used in the transaction. | ||
* @param args.req - The JSON-RPC request. | ||
* @param args.res - The JSON-RPC response. | ||
*/ | ||
async function eth_sendTransaction({ | ||
next, | ||
req, | ||
res: _res, | ||
validateAccountAndChainId, | ||
}: { | ||
validateAccountAndChainId: (args: { | ||
from: string; | ||
chainId?: number; | ||
}) => Promise<void>; | ||
next: AsyncJsonRpcEngineNextCallback; | ||
req: JsonRpcRequest<unknown> & { method: 'eth_sendTransaction' }; | ||
res: PendingJsonRpcResponse<unknown>; | ||
}) { | ||
if ( | ||
!Array.isArray(req.params) && | ||
!(isObject(req.params) && hasProperty(req.params, 0)) | ||
) { | ||
throw ethErrors.rpc.invalidParams({ | ||
message: `Invalid parameters: expected an array`, | ||
}); | ||
} | ||
const transactionParameters = req.params[0]; | ||
if (!isObject(transactionParameters)) { | ||
throw ethErrors.rpc.invalidParams({ | ||
message: `Invalid parameters: expected the first parameter to be an object`, | ||
}); | ||
} | ||
await validateAccountAndChainId({ | ||
from: req.params[0].from, | ||
chainId: req.params[0].chainId, | ||
}); | ||
|
||
// This is handled later in the network middleware | ||
next(); | ||
} | ||
|
||
export default eth_sendTransaction; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.