diff --git a/.changeset/nasty-ligers-retire.md b/.changeset/nasty-ligers-retire.md new file mode 100644 index 0000000000..aa908ddb26 --- /dev/null +++ b/.changeset/nasty-ligers-retire.md @@ -0,0 +1,5 @@ +--- +"viem": patch +--- + +Added a static `code` property to RPC Error classes. diff --git a/src/errors/rpc.ts b/src/errors/rpc.ts index e105fd3fda..588263d291 100644 --- a/src/errors/rpc.ts +++ b/src/errors/rpc.ts @@ -95,10 +95,11 @@ export class ProviderRpcError< */ export class ParseRpcError extends RpcError { override name = 'ParseRpcError' + static code = -32700 as const constructor(cause: Error) { super(cause, { - code: -32700, + code: ParseRpcError.code, shortMessage: 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.', }) @@ -112,10 +113,11 @@ export class ParseRpcError extends RpcError { */ export class InvalidRequestRpcError extends RpcError { override name = 'InvalidRequestRpcError' + static code = -32600 as const constructor(cause: Error) { super(cause, { - code: -32600, + code: InvalidRequestRpcError.code, shortMessage: 'JSON is not a valid request object.', }) } @@ -128,10 +130,11 @@ export class InvalidRequestRpcError extends RpcError { */ export class MethodNotFoundRpcError extends RpcError { override name = 'MethodNotFoundRpcError' + static code = -32601 as const constructor(cause: Error) { super(cause, { - code: -32601, + code: MethodNotFoundRpcError.code, shortMessage: 'The method does not exist / is not available.', }) } @@ -144,10 +147,11 @@ export class MethodNotFoundRpcError extends RpcError { */ export class InvalidParamsRpcError extends RpcError { override name = 'InvalidParamsRpcError' + static code = -32602 as const constructor(cause: Error) { super(cause, { - code: -32602, + code: InvalidParamsRpcError.code, shortMessage: [ 'Invalid parameters were provided to the RPC method.', 'Double check you have provided the correct parameters.', @@ -163,10 +167,11 @@ export class InvalidParamsRpcError extends RpcError { */ export class InternalRpcError extends RpcError { override name = 'InternalRpcError' + static code = -32603 as const constructor(cause: Error) { super(cause, { - code: -32603, + code: InternalRpcError.code, shortMessage: 'An internal error was received.', }) } @@ -179,10 +184,11 @@ export class InternalRpcError extends RpcError { */ export class InvalidInputRpcError extends RpcError { override name = 'InvalidInputRpcError' + static code = -32000 as const constructor(cause: Error) { super(cause, { - code: -32000, + code: InvalidInputRpcError.code, shortMessage: [ 'Missing or invalid parameters.', 'Double check you have provided the correct parameters.', @@ -198,10 +204,11 @@ export class InvalidInputRpcError extends RpcError { */ export class ResourceNotFoundRpcError extends RpcError { override name = 'ResourceNotFoundRpcError' + static code = -32001 as const constructor(cause: Error) { super(cause, { - code: -32001, + code: ResourceNotFoundRpcError.code, shortMessage: 'Requested resource not found.', }) } @@ -214,10 +221,11 @@ export class ResourceNotFoundRpcError extends RpcError { */ export class ResourceUnavailableRpcError extends RpcError { override name = 'ResourceUnavailableRpcError' + static code = -32002 as const constructor(cause: Error) { super(cause, { - code: -32002, + code: ResourceUnavailableRpcError.code, shortMessage: 'Requested resource not available.', }) } @@ -230,9 +238,13 @@ export class ResourceUnavailableRpcError extends RpcError { */ export class TransactionRejectedRpcError extends RpcError { override name = 'TransactionRejectedRpcError' + static code = -32003 as const constructor(cause: Error) { - super(cause, { code: -32003, shortMessage: 'Transaction creation failed.' }) + super(cause, { + code: TransactionRejectedRpcError.code, + shortMessage: 'Transaction creation failed.', + }) } } @@ -243,9 +255,13 @@ export class TransactionRejectedRpcError extends RpcError { */ export class MethodNotSupportedRpcError extends RpcError { override name = 'MethodNotSupportedRpcError' + static code = -32004 as const constructor(cause: Error) { - super(cause, { code: -32004, shortMessage: 'Method is not implemented.' }) + super(cause, { + code: MethodNotSupportedRpcError.code, + shortMessage: 'Method is not implemented.', + }) } } @@ -256,10 +272,11 @@ export class MethodNotSupportedRpcError extends RpcError { */ export class LimitExceededRpcError extends RpcError { override name = 'LimitExceededRpcError' + static code = -32005 as const constructor(cause: Error) { super(cause, { - code: -32005, + code: LimitExceededRpcError.code, shortMessage: 'Request exceeds defined limit.', }) } @@ -272,10 +289,11 @@ export class LimitExceededRpcError extends RpcError { */ export class JsonRpcVersionUnsupportedError extends RpcError { override name = 'JsonRpcVersionUnsupportedError' + static code = -32006 as const constructor(cause: Error) { super(cause, { - code: -32006, + code: JsonRpcVersionUnsupportedError.code, shortMessage: 'Version of JSON-RPC protocol is not supported.', }) } @@ -288,10 +306,11 @@ export class JsonRpcVersionUnsupportedError extends RpcError { */ export class UserRejectedRequestError extends ProviderRpcError { override name = 'UserRejectedRequestError' + static code = 4001 as const constructor(cause: Error) { super(cause, { - code: 4001, + code: UserRejectedRequestError.code, shortMessage: 'User rejected the request.', }) } @@ -304,10 +323,11 @@ export class UserRejectedRequestError extends ProviderRpcError { */ export class UnauthorizedProviderError extends ProviderRpcError { override name = 'UnauthorizedProviderError' + static code = 4100 as const constructor(cause: Error) { super(cause, { - code: 4100, + code: UnauthorizedProviderError.code, shortMessage: 'The requested method and/or account has not been authorized by the user.', }) @@ -321,10 +341,11 @@ export class UnauthorizedProviderError extends ProviderRpcError { */ export class UnsupportedProviderMethodError extends ProviderRpcError { override name = 'UnsupportedProviderMethodError' + static code = 4200 as const constructor(cause: Error) { super(cause, { - code: 4200, + code: UnsupportedProviderMethodError.code, shortMessage: 'The Provider does not support the requested method.', }) } @@ -337,10 +358,11 @@ export class UnsupportedProviderMethodError extends ProviderRpcError { */ export class ProviderDisconnectedError extends ProviderRpcError { override name = 'ProviderDisconnectedError' + static code = 4900 as const constructor(cause: Error) { super(cause, { - code: 4900, + code: ProviderDisconnectedError.code, shortMessage: 'The Provider is disconnected from all chains.', }) } @@ -353,10 +375,11 @@ export class ProviderDisconnectedError extends ProviderRpcError { */ export class ChainDisconnectedError extends ProviderRpcError { override name = 'ChainDisconnectedError' + static code = 4901 as const constructor(cause: Error) { super(cause, { - code: 4901, + code: ChainDisconnectedError.code, shortMessage: 'The Provider is not connected to the requested chain.', }) } @@ -369,10 +392,11 @@ export class ChainDisconnectedError extends ProviderRpcError { */ export class SwitchChainError extends ProviderRpcError { override name = 'SwitchChainError' + static code = 4902 as const constructor(cause: Error) { super(cause, { - code: 4902, + code: SwitchChainError.code, shortMessage: 'An error occurred when attempting to switch chain.', }) } diff --git a/src/utils/buildRequest.test.ts b/src/utils/buildRequest.test.ts index af6f812b5d..d5eeeaafb3 100644 --- a/src/utils/buildRequest.test.ts +++ b/src/utils/buildRequest.test.ts @@ -8,10 +8,25 @@ import { TimeoutError, } from '../errors/request.js' import { + ChainDisconnectedError, InternalRpcError, + InvalidInputRpcError, + InvalidParamsRpcError, + InvalidRequestRpcError, + JsonRpcVersionUnsupportedError, LimitExceededRpcError, + MethodNotFoundRpcError, + MethodNotSupportedRpcError, ParseRpcError, + ProviderDisconnectedError, + ResourceNotFoundRpcError, + ResourceUnavailableRpcError, + SwitchChainError, + TransactionRejectedRpcError, + UnauthorizedProviderError, UnknownRpcError, + UnsupportedProviderMethodError, + UserRejectedRequestError, } from '../errors/rpc.js' import { buildRequest, isDeterministicError } from './buildRequest.js' @@ -56,7 +71,11 @@ describe('args', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32603, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InternalRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -76,7 +95,11 @@ describe('args', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32603, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InternalRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -108,7 +131,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32700, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: ParseRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -129,7 +156,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32600, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InvalidRequestRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -150,7 +181,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32601, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: MethodNotFoundRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -171,7 +206,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32602, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InvalidParamsRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -193,7 +232,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32603, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InternalRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -214,7 +257,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32000, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InvalidInputRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -236,7 +283,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32001, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: ResourceNotFoundRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -257,7 +308,14 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32002, message: 'message' } })) + res.end( + JSON.stringify({ + error: { + code: ResourceUnavailableRpcError.code, + message: 'message', + }, + }), + ) }) await expect(() => @@ -278,7 +336,14 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32003, message: 'message' } })) + res.end( + JSON.stringify({ + error: { + code: TransactionRejectedRpcError.code, + message: 'message', + }, + }), + ) }) await expect(() => @@ -301,7 +366,14 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32004, message: 'message' } })) + res.end( + JSON.stringify({ + error: { + code: MethodNotSupportedRpcError.code, + message: 'message', + }, + }), + ) }) await expect(() => @@ -322,7 +394,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32005, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: LimitExceededRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -343,7 +419,14 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32006, message: 'message' } })) + res.end( + JSON.stringify({ + error: { + code: JsonRpcVersionUnsupportedError.code, + message: 'message', + }, + }), + ) }) await expect(() => @@ -364,7 +447,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: 4001, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: UserRejectedRequestError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -385,7 +472,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: 4100, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: UnauthorizedProviderError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -406,7 +497,14 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: 4200, message: 'message' } })) + res.end( + JSON.stringify({ + error: { + code: UnsupportedProviderMethodError.code, + message: 'message', + }, + }), + ) }) await expect(() => @@ -427,7 +525,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: 4900, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: ProviderDisconnectedError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -448,7 +550,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: 4901, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: ChainDisconnectedError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -469,7 +575,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: 4902, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: SwitchChainError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -490,7 +600,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32602, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InvalidParamsRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -507,27 +621,6 @@ describe('behavior', () => { `) }) - test('MethodNotSupportedRpcError', async () => { - const server = await createHttpServer((_req, res) => { - res.writeHead(200, { - 'Content-Type': 'application/json', - }) - res.end(JSON.stringify({ error: { code: -32042, message: 'message' } })) - }) - - await expect(() => - buildRequest(request(server.url))({ method: 'eth_blockNumber' }), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - "Method is not implemented. - - URL: http://localhost - Request body: {\\"method\\":\\"eth_blockNumber\\"} - - Details: message - Version: viem@1.0.2" - `) - }) - test('UnknownRpcError', async () => { await expect(() => buildRequest(() => Promise.reject(new Error('wat')))(), @@ -569,7 +662,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32603, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InternalRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -585,7 +682,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32005, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: LimitExceededRpcError.code, message: 'message' }, + }), + ) }) await expect(() => @@ -779,7 +880,11 @@ describe('behavior', () => { res.writeHead(200, { 'Content-Type': 'application/json', }) - res.end(JSON.stringify({ error: { code: -32602, message: 'message' } })) + res.end( + JSON.stringify({ + error: { code: InvalidParamsRpcError.code, message: 'message' }, + }), + ) }) await expect(() => diff --git a/src/utils/buildRequest.ts b/src/utils/buildRequest.ts index 9a4ba9eb4f..8b9acdd592 100644 --- a/src/utils/buildRequest.ts +++ b/src/utils/buildRequest.ts @@ -71,27 +71,65 @@ export function buildRequest Promise>( const err = err_ as unknown as RpcError< RpcErrorCode | ProviderRpcErrorCode > - if (err.code === -32700) throw new ParseRpcError(err) - if (err.code === -32600) throw new InvalidRequestRpcError(err) - if (err.code === -32601) throw new MethodNotFoundRpcError(err) - if (err.code === -32602) throw new InvalidParamsRpcError(err) - if (err.code === -32603) throw new InternalRpcError(err) - if (err.code === -32000) throw new InvalidInputRpcError(err) - if (err.code === -32001) throw new ResourceNotFoundRpcError(err) - if (err.code === -32002) throw new ResourceUnavailableRpcError(err) - if (err.code === -32003) throw new TransactionRejectedRpcError(err) - if (err.code === -32004) throw new MethodNotSupportedRpcError(err) - if (err.code === -32005) throw new LimitExceededRpcError(err) - if (err.code === -32006) throw new JsonRpcVersionUnsupportedError(err) - if (err.code === -32042) throw new MethodNotSupportedRpcError(err) - if (err.code === 4001) throw new UserRejectedRequestError(err) - if (err.code === 4100) throw new UnauthorizedProviderError(err) - if (err.code === 4200) throw new UnsupportedProviderMethodError(err) - if (err.code === 4900) throw new ProviderDisconnectedError(err) - if (err.code === 4901) throw new ChainDisconnectedError(err) - if (err.code === 4902) throw new SwitchChainError(err) - if (err_ instanceof BaseError) throw err_ - throw new UnknownRpcError(err as Error) + switch (err.code) { + // -32700 + case ParseRpcError.code: + throw new ParseRpcError(err) + // -32600 + case InvalidRequestRpcError.code: + throw new InvalidRequestRpcError(err) + // -32601 + case MethodNotFoundRpcError.code: + throw new MethodNotFoundRpcError(err) + // -32602 + case InvalidParamsRpcError.code: + throw new InvalidParamsRpcError(err) + // -32603 + case InternalRpcError.code: + throw new InternalRpcError(err) + // -32000 + case InvalidInputRpcError.code: + throw new InvalidInputRpcError(err) + // -32001 + case ResourceNotFoundRpcError.code: + throw new ResourceNotFoundRpcError(err) + // -32002 + case ResourceUnavailableRpcError.code: + throw new ResourceUnavailableRpcError(err) + // -32003 + case TransactionRejectedRpcError.code: + throw new TransactionRejectedRpcError(err) + // -32004 + case MethodNotSupportedRpcError.code: + throw new MethodNotSupportedRpcError(err) + // -32005 + case LimitExceededRpcError.code: + throw new LimitExceededRpcError(err) + // -32006 + case JsonRpcVersionUnsupportedError.code: + throw new JsonRpcVersionUnsupportedError(err) + // 4001 + case UserRejectedRequestError.code: + throw new UserRejectedRequestError(err) + // 4100 + case UnauthorizedProviderError.code: + throw new UnauthorizedProviderError(err) + // 4200 + case UnsupportedProviderMethodError.code: + throw new UnsupportedProviderMethodError(err) + // 4900 + case ProviderDisconnectedError.code: + throw new ProviderDisconnectedError(err) + // 4901 + case ChainDisconnectedError.code: + throw new ChainDisconnectedError(err) + // 4902 + case SwitchChainError.code: + throw new SwitchChainError(err) + default: + if (err_ instanceof BaseError) throw err_ + throw new UnknownRpcError(err as Error) + } } }, {