From e17ed6b2b9a2dcee07f18e7afbbb8a0c352da243 Mon Sep 17 00:00:00 2001 From: Steven Luscher Date: Thu, 30 Jun 2022 13:08:10 -0700 Subject: [PATCH] feat: web3.js RPC errors now hold the error `code` and `data` on the error object (#26318) feat: web3.js RPC errors now hold the error code on the error object --- client/src/rpc_custom_error.rs | 1 + web3.js/src/connection.ts | 200 +++++++++++++++++---------------- web3.js/src/errors.ts | 41 +++++++ 3 files changed, 143 insertions(+), 99 deletions(-) diff --git a/client/src/rpc_custom_error.rs b/client/src/rpc_custom_error.rs index 1a6a6b84ce..95ef60293c 100644 --- a/client/src/rpc_custom_error.rs +++ b/client/src/rpc_custom_error.rs @@ -7,6 +7,7 @@ use { thiserror::Error, }; +// Keep in sync with web3.js/src/errors.ts pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001; pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002; pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003; diff --git a/web3.js/src/connection.ts b/web3.js/src/connection.ts index 34fa228649..3f20a81f0e 100644 --- a/web3.js/src/connection.ts +++ b/web3.js/src/connection.ts @@ -26,7 +26,7 @@ import RpcClient from 'jayson/lib/client/browser'; import {AgentManager} from './agent-manager'; import {EpochSchedule} from './epoch-schedule'; -import {SendTransactionError} from './errors'; +import {SendTransactionError, SolanaJSONRPCError} from './errors'; import fetchImpl, {Response} from './fetch-impl'; import {NonceAccount} from './nonce-account'; import {PublicKey} from './publickey'; @@ -2532,11 +2532,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getBalance', args); const res = create(unsafeRes, jsonRpcResultAndContext(number())); if ('error' in res) { - throw new Error( - 'failed to get balance for ' + - publicKey.toBase58() + - ': ' + - res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get balance for ${publicKey.toBase58()}`, ); } return res.result; @@ -2565,8 +2563,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getBlockTime', [slot]); const res = create(unsafeRes, jsonRpcResult(nullable(number()))); if ('error' in res) { - throw new Error( - 'failed to get block time for slot ' + slot + ': ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get block time for slot ${slot}`, ); } return res.result; @@ -2580,8 +2579,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('minimumLedgerSlot', []); const res = create(unsafeRes, jsonRpcResult(number())); if ('error' in res) { - throw new Error( - 'failed to get minimum ledger slot: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get minimum ledger slot', ); } return res.result; @@ -2594,8 +2594,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getFirstAvailableBlock', []); const res = create(unsafeRes, SlotRpcResult); if ('error' in res) { - throw new Error( - 'failed to get first available block: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get first available block', ); } return res.result; @@ -2624,7 +2625,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getSupply', [configArg]); const res = create(unsafeRes, GetSupplyRpcResult); if ('error' in res) { - throw new Error('failed to get supply: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get supply'); } return res.result; } @@ -2640,7 +2641,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getTokenSupply', args); const res = create(unsafeRes, jsonRpcResultAndContext(TokenAmountResult)); if ('error' in res) { - throw new Error('failed to get token supply: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get token supply'); } return res.result; } @@ -2656,8 +2657,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getTokenAccountBalance', args); const res = create(unsafeRes, jsonRpcResultAndContext(TokenAmountResult)); if ('error' in res) { - throw new Error( - 'failed to get token account balance: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get token account balance', ); } return res.result; @@ -2690,11 +2692,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getTokenAccountsByOwner', args); const res = create(unsafeRes, GetTokenAccountsByOwner); if ('error' in res) { - throw new Error( - 'failed to get token accounts owned by account ' + - ownerAddress.toBase58() + - ': ' + - res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get token accounts owned by account ${ownerAddress.toBase58()}`, ); } return res.result; @@ -2725,11 +2725,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getTokenAccountsByOwner', args); const res = create(unsafeRes, GetParsedTokenAccountsByOwner); if ('error' in res) { - throw new Error( - 'failed to get token accounts owned by account ' + - ownerAddress.toBase58() + - ': ' + - res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get token accounts owned by account ${ownerAddress.toBase58()}`, ); } return res.result; @@ -2749,7 +2747,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getLargestAccounts', args); const res = create(unsafeRes, GetLargestAccountsRpcResult); if ('error' in res) { - throw new Error('failed to get largest accounts: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get largest accounts'); } return res.result; } @@ -2766,8 +2764,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getTokenLargestAccounts', args); const res = create(unsafeRes, GetTokenLargestAccountsResult); if ('error' in res) { - throw new Error( - 'failed to get token largest accounts: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get token largest accounts', ); } return res.result; @@ -2794,11 +2793,9 @@ export class Connection { jsonRpcResultAndContext(nullable(AccountInfoResult)), ); if ('error' in res) { - throw new Error( - 'failed to get info about account ' + - publicKey.toBase58() + - ': ' + - res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get info about account ${publicKey.toBase58()}`, ); } return res.result; @@ -2824,11 +2821,9 @@ export class Connection { jsonRpcResultAndContext(nullable(ParsedAccountInfoResult)), ); if ('error' in res) { - throw new Error( - 'failed to get info about account ' + - publicKey.toBase58() + - ': ' + - res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get info about account ${publicKey.toBase58()}`, ); } return res.result; @@ -2871,8 +2866,9 @@ export class Connection { jsonRpcResultAndContext(array(nullable(AccountInfoResult))), ); if ('error' in res) { - throw new Error( - 'failed to get info for accounts ' + keys + ': ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get info for accounts ${keys}`, ); } return res.result; @@ -2915,10 +2911,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getStakeActivation', args); const res = create(unsafeRes, jsonRpcResult(StakeActivationResult)); if ('error' in res) { - throw new Error( - `failed to get Stake Activation ${publicKey.toBase58()}: ${ - res.error.message - }`, + throw new SolanaJSONRPCError( + res.error, + `failed to get Stake Activation ${publicKey.toBase58()}`, ); } return res.result; @@ -2945,11 +2940,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getProgramAccounts', args); const res = create(unsafeRes, jsonRpcResult(array(KeyedAccountInfoResult))); if ('error' in res) { - throw new Error( - 'failed to get accounts owned by program ' + - programId.toBase58() + - ': ' + - res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get accounts owned by program ${programId.toBase58()}`, ); } return res.result; @@ -2983,11 +2976,9 @@ export class Connection { jsonRpcResult(array(KeyedParsedAccountInfoResult)), ); if ('error' in res) { - throw new Error( - 'failed to get accounts owned by program ' + - programId.toBase58() + - ': ' + - res.error.message, + throw new SolanaJSONRPCError( + res.error, + `failed to get accounts owned by program ${programId.toBase58()}`, ); } return res.result; @@ -3142,7 +3133,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getClusterNodes', []); const res = create(unsafeRes, jsonRpcResult(array(ContactInfoResult))); if ('error' in res) { - throw new Error('failed to get cluster nodes: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get cluster nodes'); } return res.result; } @@ -3155,7 +3146,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getVoteAccounts', args); const res = create(unsafeRes, GetVoteAccounts); if ('error' in res) { - throw new Error('failed to get vote accounts: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get vote accounts'); } return res.result; } @@ -3177,7 +3168,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getSlot', args); const res = create(unsafeRes, jsonRpcResult(number())); if ('error' in res) { - throw new Error('failed to get slot: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get slot'); } return res.result; } @@ -3199,7 +3190,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getSlotLeader', args); const res = create(unsafeRes, jsonRpcResult(string())); if ('error' in res) { - throw new Error('failed to get slot leader: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get slot leader'); } return res.result; } @@ -3218,7 +3209,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getSlotLeaders', args); const res = create(unsafeRes, jsonRpcResult(array(PublicKeyFromString))); if ('error' in res) { - throw new Error('failed to get slot leaders: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get slot leaders'); } return res.result; } @@ -3253,7 +3244,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getSignatureStatuses', params); const res = create(unsafeRes, GetSignatureStatusesRpcResult); if ('error' in res) { - throw new Error('failed to get signature status: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get signature status'); } return res.result; } @@ -3275,7 +3266,10 @@ export class Connection { const unsafeRes = await this._rpcRequest('getTransactionCount', args); const res = create(unsafeRes, jsonRpcResult(number())); if ('error' in res) { - throw new Error('failed to get transaction count: ' + res.error.message); + throw new SolanaJSONRPCError( + res.error, + 'failed to get transaction count', + ); } return res.result; } @@ -3303,7 +3297,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getInflationGovernor', args); const res = create(unsafeRes, GetInflationGovernorRpcResult); if ('error' in res) { - throw new Error('failed to get inflation: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get inflation'); } return res.result; } @@ -3330,7 +3324,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getInflationReward', args); const res = create(unsafeRes, GetInflationRewardResult); if ('error' in res) { - throw new Error('failed to get inflation reward: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get inflation reward'); } return res.result; } @@ -3352,7 +3346,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getEpochInfo', args); const res = create(unsafeRes, GetEpochInfoRpcResult); if ('error' in res) { - throw new Error('failed to get epoch info: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get epoch info'); } return res.result; } @@ -3364,7 +3358,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getEpochSchedule', []); const res = create(unsafeRes, GetEpochScheduleRpcResult); if ('error' in res) { - throw new Error('failed to get epoch schedule: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get epoch schedule'); } const epochSchedule = res.result; return new EpochSchedule( @@ -3384,7 +3378,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getLeaderSchedule', []); const res = create(unsafeRes, GetLeaderScheduleRpcResult); if ('error' in res) { - throw new Error('failed to get leader schedule: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get leader schedule'); } return res.result; } @@ -3425,7 +3419,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getRecentBlockhash', args); const res = create(unsafeRes, GetRecentBlockhashAndContextRpcResult); if ('error' in res) { - throw new Error('failed to get recent blockhash: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get recent blockhash'); } return res.result; } @@ -3444,8 +3438,9 @@ export class Connection { ); const res = create(unsafeRes, GetRecentPerformanceSamplesRpcResult); if ('error' in res) { - throw new Error( - 'failed to get recent performance samples: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get recent performance samples', ); } @@ -3469,7 +3464,7 @@ export class Connection { const res = create(unsafeRes, GetFeeCalculatorRpcResult); if ('error' in res) { - throw new Error('failed to get fee calculator: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get fee calculator'); } const {context, value} = res.result; return { @@ -3491,7 +3486,7 @@ export class Connection { const res = create(unsafeRes, jsonRpcResultAndContext(nullable(number()))); if ('error' in res) { - throw new Error('failed to get slot: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get slot'); } if (res.result === null) { throw new Error('invalid blockhash'); @@ -3549,7 +3544,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getLatestBlockhash', args); const res = create(unsafeRes, GetLatestBlockhashRpcResult); if ('error' in res) { - throw new Error('failed to get latest blockhash: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get latest blockhash'); } return res.result; } @@ -3561,7 +3556,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getVersion', []); const res = create(unsafeRes, jsonRpcResult(VersionResult)); if ('error' in res) { - throw new Error('failed to get version: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get version'); } return res.result; } @@ -3573,7 +3568,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getGenesisHash', []); const res = create(unsafeRes, jsonRpcResult(string())); if ('error' in res) { - throw new Error('failed to get genesis hash: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get genesis hash'); } return res.result; } @@ -3593,7 +3588,7 @@ export class Connection { const res = create(unsafeRes, GetBlockRpcResult); if ('error' in res) { - throw new Error('failed to get confirmed block: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get confirmed block'); } const result = res.result; @@ -3631,8 +3626,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getBlockHeight', args); const res = create(unsafeRes, jsonRpcResult(number())); if ('error' in res) { - throw new Error( - 'failed to get block height information: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get block height information', ); } @@ -3660,8 +3656,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getBlockProduction', args); const res = create(unsafeRes, BlockProductionResponseStruct); if ('error' in res) { - throw new Error( - 'failed to get block production information: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get block production information', ); } @@ -3682,7 +3679,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getTransaction', args); const res = create(unsafeRes, GetTransactionRpcResult); if ('error' in res) { - throw new Error('failed to get transaction: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get transaction'); } const result = res.result; @@ -3712,7 +3709,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getTransaction', args); const res = create(unsafeRes, GetParsedTransactionRpcResult); if ('error' in res) { - throw new Error('failed to get transaction: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get transaction'); } return res.result; } @@ -3740,7 +3737,7 @@ export class Connection { const res = unsafeRes.map((unsafeRes: any) => { const res = create(unsafeRes, GetParsedTransactionRpcResult); if ('error' in res) { - throw new Error('failed to get transactions: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get transactions'); } return res.result; }); @@ -3768,7 +3765,7 @@ export class Connection { const res = unsafeRes.map((unsafeRes: any) => { const res = create(unsafeRes, GetTransactionRpcResult); if ('error' in res) { - throw new Error('failed to get transactions: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get transactions'); } const result = res.result; if (!result) return result; @@ -3800,7 +3797,7 @@ export class Connection { const res = create(unsafeRes, GetConfirmedBlockRpcResult); if ('error' in res) { - throw new Error('failed to get confirmed block: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get confirmed block'); } const result = res.result; @@ -3851,7 +3848,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getBlocks', args); const res = create(unsafeRes, jsonRpcResult(array(number()))); if ('error' in res) { - throw new Error('failed to get blocks: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get blocks'); } return res.result; } @@ -3875,7 +3872,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getBlock', args); const res = create(unsafeRes, GetBlockSignaturesRpcResult); if ('error' in res) { - throw new Error('failed to get block: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get block'); } const result = res.result; if (!result) { @@ -3905,7 +3902,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getConfirmedBlock', args); const res = create(unsafeRes, GetBlockSignaturesRpcResult); if ('error' in res) { - throw new Error('failed to get confirmed block: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get confirmed block'); } const result = res.result; if (!result) { @@ -3927,7 +3924,7 @@ export class Connection { const unsafeRes = await this._rpcRequest('getConfirmedTransaction', args); const res = create(unsafeRes, GetTransactionRpcResult); if ('error' in res) { - throw new Error('failed to get transaction: ' + res.error.message); + throw new SolanaJSONRPCError(res.error, 'failed to get transaction'); } const result = res.result; @@ -3958,8 +3955,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getConfirmedTransaction', args); const res = create(unsafeRes, GetParsedTransactionRpcResult); if ('error' in res) { - throw new Error( - 'failed to get confirmed transaction: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get confirmed transaction', ); } return res.result; @@ -3990,8 +3988,9 @@ export class Connection { const res = unsafeRes.map((unsafeRes: any) => { const res = create(unsafeRes, GetParsedTransactionRpcResult); if ('error' in res) { - throw new Error( - 'failed to get confirmed transactions: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get confirmed transactions', ); } return res.result; @@ -4096,8 +4095,9 @@ export class Connection { ); const res = create(unsafeRes, GetConfirmedSignaturesForAddress2RpcResult); if ('error' in res) { - throw new Error( - 'failed to get confirmed signatures for address: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get confirmed signatures for address', ); } return res.result; @@ -4125,8 +4125,9 @@ export class Connection { const unsafeRes = await this._rpcRequest('getSignaturesForAddress', args); const res = create(unsafeRes, GetSignaturesForAddressRpcResult); if ('error' in res) { - throw new Error( - 'failed to get signatures for address: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + 'failed to get signatures for address', ); } return res.result; @@ -4198,8 +4199,9 @@ export class Connection { ]); const res = create(unsafeRes, RequestAirdropRpcResult); if ('error' in res) { - throw new Error( - 'airdrop to ' + to.toBase58() + ' failed: ' + res.error.message, + throw new SolanaJSONRPCError( + res.error, + `airdrop to ${to.toBase58()} failed`, ); } return res.result; diff --git a/web3.js/src/errors.ts b/web3.js/src/errors.ts index c1155137b3..24ac09b661 100644 --- a/web3.js/src/errors.ts +++ b/web3.js/src/errors.ts @@ -7,3 +7,44 @@ export class SendTransactionError extends Error { this.logs = logs; } } + +// Keep in sync with client/src/rpc_custom_errors.rs +// Typescript `enums` thwart tree-shaking. See https://bargsten.org/jsts/enums/ +export const SolanaJSONRPCErrorCode = { + JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: -32001, + JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: -32002, + JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: -32003, + JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: -32004, + JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY: -32005, + JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: -32006, + JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: -32007, + JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: -32008, + JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED: -32009, + JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX: -32010, + JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: -32011, + JSON_RPC_SCAN_ERROR: -32012, + JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH: -32013, + JSON_RPC_SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET: -32014, + JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION: -32015, + JSON_RPC_SERVER_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED: -32016, +} as const; +export type SolanaJSONRPCErrorCodeEnum = + typeof SolanaJSONRPCErrorCode[keyof typeof SolanaJSONRPCErrorCode]; + +export class SolanaJSONRPCError extends Error { + code: SolanaJSONRPCErrorCodeEnum | unknown; + data?: any; + constructor( + { + code, + message, + data, + }: Readonly<{code: unknown; message: string; data?: any}>, + customMessage?: string, + ) { + super(customMessage != null ? `${customMessage}: ${message}` : message); + this.code = code; + this.data = data; + this.name = 'SolanaJSONRPCError'; + } +}