@@ -14,6 +14,7 @@ import {
14
14
SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SLOT_SKIPPED ,
15
15
SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE ,
16
16
SOLANA_ERROR__JSON_RPC__SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION ,
17
+ SOLANA_ERROR__MALFORMED_JSON_RPC_ERROR ,
17
18
SolanaErrorCode ,
18
19
} from './codes' ;
19
20
import { SolanaErrorContext } from './context' ;
@@ -91,46 +92,69 @@ export interface RpcSimulateTransactionResult {
91
92
unitsConsumed : number | null ;
92
93
}
93
94
94
- export function getSolanaErrorFromJsonRpcError ( { code : rawCode , data , message } : RpcErrorResponse ) : SolanaError {
95
+ export function getSolanaErrorFromJsonRpcError ( putativeErrorResponse : unknown ) : SolanaError {
95
96
let out : SolanaError ;
96
- const code = Number ( rawCode ) ;
97
- if ( code === SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE ) {
98
- const { err, ...preflightErrorContext } = data as RpcSimulateTransactionResult ;
99
- const causeObject = err ? { cause : getSolanaErrorFromTransactionError ( err ) } : null ;
100
- out = new SolanaError ( SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE , {
101
- ...preflightErrorContext ,
102
- ...causeObject ,
103
- } ) ;
104
- } else {
105
- let errorContext ;
106
- switch ( code ) {
107
- case SOLANA_ERROR__JSON_RPC__INTERNAL_ERROR :
108
- case SOLANA_ERROR__JSON_RPC__INVALID_PARAMS :
109
- case SOLANA_ERROR__JSON_RPC__INVALID_REQUEST :
110
- case SOLANA_ERROR__JSON_RPC__METHOD_NOT_FOUND :
111
- case SOLANA_ERROR__JSON_RPC__PARSE_ERROR :
112
- case SOLANA_ERROR__JSON_RPC__SCAN_ERROR :
113
- case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_CLEANED_UP :
114
- case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_NOT_AVAILABLE :
115
- case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET :
116
- case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX :
117
- case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED :
118
- case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SLOT_SKIPPED :
119
- case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE :
120
- case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION :
121
- // The server supplies no structured data, but rather a pre-formatted message. Put
122
- // the server message in `context` so as not to completely lose the data. The long
123
- // term fix for this is to add data to the server responses and modify the
124
- // messages in `@solana/errors` to be actual format strings.
125
- errorContext = { __serverMessage : message } ;
126
- break ;
127
- default :
128
- if ( typeof data === 'object' && ! Array . isArray ( data ) ) {
129
- errorContext = data ;
130
- }
97
+ if ( isRpcErrorResponse ( putativeErrorResponse ) ) {
98
+ const { code : rawCode , data, message } = putativeErrorResponse ;
99
+ const code = Number ( rawCode ) ;
100
+ if ( code === SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE ) {
101
+ const { err, ...preflightErrorContext } = data as RpcSimulateTransactionResult ;
102
+ const causeObject = err ? { cause : getSolanaErrorFromTransactionError ( err ) } : null ;
103
+ out = new SolanaError ( SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE , {
104
+ ...preflightErrorContext ,
105
+ ...causeObject ,
106
+ } ) ;
107
+ } else {
108
+ let errorContext ;
109
+ switch ( code ) {
110
+ case SOLANA_ERROR__JSON_RPC__INTERNAL_ERROR :
111
+ case SOLANA_ERROR__JSON_RPC__INVALID_PARAMS :
112
+ case SOLANA_ERROR__JSON_RPC__INVALID_REQUEST :
113
+ case SOLANA_ERROR__JSON_RPC__METHOD_NOT_FOUND :
114
+ case SOLANA_ERROR__JSON_RPC__PARSE_ERROR :
115
+ case SOLANA_ERROR__JSON_RPC__SCAN_ERROR :
116
+ case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_CLEANED_UP :
117
+ case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_NOT_AVAILABLE :
118
+ case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET :
119
+ case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX :
120
+ case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED :
121
+ case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SLOT_SKIPPED :
122
+ case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE :
123
+ case SOLANA_ERROR__JSON_RPC__SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION :
124
+ // The server supplies no structured data, but rather a pre-formatted message. Put
125
+ // the server message in `context` so as not to completely lose the data. The long
126
+ // term fix for this is to add data to the server responses and modify the
127
+ // messages in `@solana/errors` to be actual format strings.
128
+ errorContext = { __serverMessage : message } ;
129
+ break ;
130
+ default :
131
+ if ( typeof data === 'object' && ! Array . isArray ( data ) ) {
132
+ errorContext = data ;
133
+ }
134
+ }
135
+ out = new SolanaError ( code as SolanaErrorCode , errorContext as SolanaErrorContext [ SolanaErrorCode ] ) ;
131
136
}
132
- out = new SolanaError ( code as SolanaErrorCode , errorContext as SolanaErrorContext [ SolanaErrorCode ] ) ;
137
+ } else {
138
+ const message =
139
+ typeof putativeErrorResponse === 'object' &&
140
+ putativeErrorResponse !== null &&
141
+ 'message' in putativeErrorResponse &&
142
+ typeof putativeErrorResponse . message === 'string'
143
+ ? putativeErrorResponse . message
144
+ : 'Malformed JSON-RPC error with no message attribute' ;
145
+ out = new SolanaError ( SOLANA_ERROR__MALFORMED_JSON_RPC_ERROR , { error : putativeErrorResponse , message } ) ;
133
146
}
134
147
safeCaptureStackTrace ( out , getSolanaErrorFromJsonRpcError ) ;
135
148
return out ;
136
149
}
150
+
151
+ function isRpcErrorResponse ( value : unknown ) : value is RpcErrorResponse {
152
+ return (
153
+ typeof value === 'object' &&
154
+ value !== null &&
155
+ 'code' in value &&
156
+ 'message' in value &&
157
+ ( typeof value . code === 'number' || typeof value . code === 'bigint' ) &&
158
+ typeof value . message === 'string'
159
+ ) ;
160
+ }
0 commit comments