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
This commit is contained in:
Steven Luscher 2022-06-30 13:08:10 -07:00 committed by GitHub
parent 54cd31e9e2
commit e17ed6b2b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 143 additions and 99 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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';
}
}