Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: error types #1003

Merged
merged 6 commits into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/wild-teachers-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"near-api-js": patch
---

Fix error types. WIth this changes both `JsonRpcProvider.query` and `JsonRpcProvider.sendJsonRpc` methods will return proper error type for these errors: `AccountDoesNotExist`, `AccessKeyDoesNotExist`, `CodeDoesNotExist`, `InvalidNonce`.

An additional fix to `getErrorTypeFromErrorMessage` function. Now `getErrorTypeFromErrorMessage` will not change error type if it already exists.
9 changes: 6 additions & 3 deletions packages/near-api-js/src/providers/json-rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { ConnectionInfo, fetchJson } from '../utils/web';
import { TypedError, ErrorContext } from '../utils/errors';
import { baseEncode } from 'borsh';
import exponentialBackoff from '../utils/exponential-backoff';
import { parseRpcError } from '../utils/rpc_errors';
import { parseRpcError, getErrorTypeFromErrorMessage } from '../utils/rpc_errors';
import { SignedTransaction } from '../transaction';

/** @hidden */
Expand Down Expand Up @@ -147,7 +147,10 @@ export class JsonRpcProvider extends Provider {
result = await this.sendJsonRpc<T>('query', [path, data]);
}
if (result && result.error) {
throw new TypedError(`Querying failed: ${result.error}.\n${JSON.stringify(result, null, 2)}`, result.error.name);
throw new TypedError(
`Querying failed: ${result.error}.\n${JSON.stringify(result, null, 2)}`,
getErrorTypeFromErrorMessage(result.error, result.error.name)
);
}
return result;
}
Expand Down Expand Up @@ -344,7 +347,7 @@ export class JsonRpcProvider extends Provider {
throw new TypedError(errorMessage, 'TimeoutError');
}

throw new TypedError(errorMessage, response.error.name);
throw new TypedError(errorMessage, getErrorTypeFromErrorMessage(response.error.data, response.error.name));
}
}
// Success when response.error is not exist
Expand Down
18 changes: 18 additions & 0 deletions packages/near-api-js/src/utils/rpc_errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@ function walkSubtype(errorObj, schema, result, typeName) {
}
}

export function getErrorTypeFromErrorMessage(errorMessage, errorType) {
// This function should be removed when JSON RPC starts returning typed errors.
switch (true) {
case /^account .*? does not exist while viewing$/.test(errorMessage):
return 'AccountDoesNotExist';
case /^Account .*? doesn't exist$/.test(errorMessage):
return 'AccountDoesNotExist';
case /^access key .*? does not exist while viewing$/.test(errorMessage):
return 'AccessKeyDoesNotExist';
case /wasm execution failed with error: FunctionCallError\(CompilationError\(CodeDoesNotExist/.test(errorMessage):
return 'CodeDoesNotExist';
case /Transaction nonce \d+ must be larger than nonce of the used access key \d+/.test(errorMessage):
return 'InvalidNonce';
default:
return errorType;
}
}

/**
* Helper function determining if the argument is an object
* @param n Value to check
Expand Down
4 changes: 2 additions & 2 deletions packages/near-api-js/test/account.access_key.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ test('remove access key no longer works', async() => {
await contract.setValue({ args: { value: 'test' } });
fail('should throw an error');
} catch (e) {
expect(e.message).toContain(`Querying failed: access key ${publicKey} does not exist while viewing.`);
expect(e.type).toEqual('UntypedError');
expect(e.message).toEqual(`Can not sign transactions for account ${workingAccount.accountId} on network ${testUtils.networkId}, no matching key pair exists for this account`);
expect(e.type).toEqual('KeyNotFound');
}
});

Expand Down
14 changes: 14 additions & 0 deletions packages/near-api-js/test/utils/rpc-errors.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { ServerError } = require('../../src/utils/rpc_errors');
const {
parseRpcError,
formatError,
getErrorTypeFromErrorMessage
} = nearApi.utils.rpc_errors;
describe('rpc-errors', () => {
test('test AccountAlreadyExists error', async () => {
Expand Down Expand Up @@ -84,6 +85,19 @@ describe('rpc-errors', () => {
expect(error).toEqual(new ServerError('{"index":0,"kind":{"EvmError":"ArgumentParseError"}}'));
});

test('test getErrorTypeFromErrorMessage', () => {
const err1 = 'account random.near does not exist while viewing';
const err2 = 'Account random2.testnet doesn\'t exist';
const err3 = 'access key ed25519:DvXowCpBHKdbD2qutgfhG6jvBMaXyUh7DxrDSjkLxMHp does not exist while viewing';
const err4 = 'wasm execution failed with error: FunctionCallError(CompilationError(CodeDoesNotExist { account_id: "random.testnet" }))';
const err5 = '[-32000] Server error: Invalid transaction: Transaction nonce 1 must be larger than nonce of the used access key 1';
expect(getErrorTypeFromErrorMessage(err1)).toEqual('AccountDoesNotExist');
expect(getErrorTypeFromErrorMessage(err2)).toEqual('AccountDoesNotExist');
expect(getErrorTypeFromErrorMessage(err3)).toEqual('AccessKeyDoesNotExist');
expect(getErrorTypeFromErrorMessage(err4)).toEqual('CodeDoesNotExist');
expect(getErrorTypeFromErrorMessage(err5)).toEqual('InvalidNonce');
});

test('test NotEnoughBalance message uses human readable values', () => {
const error = parseRpcError({
NotEnoughBalance: {
Expand Down