diff --git a/.changeset/red-mice-argue.md b/.changeset/red-mice-argue.md new file mode 100644 index 0000000000..19044b2f22 --- /dev/null +++ b/.changeset/red-mice-argue.md @@ -0,0 +1,11 @@ +--- +"@near-js/providers": minor +"@near-js/utils": minor +--- + +Display user-friendly messages for JSON RPC errors: + +- MethodNotFound +- CodeDoesNotExist +- AccessKeyDoesNotExist +- AccountDoesNotExist diff --git a/packages/accounts/test/account_multisig.test.js b/packages/accounts/test/account_multisig.test.js index b1c1888ca0..51907fc45e 100644 --- a/packages/accounts/test/account_multisig.test.js +++ b/packages/accounts/test/account_multisig.test.js @@ -7,7 +7,7 @@ const BN = require('bn.js'); const fs = require('fs'); const semver = require('semver'); -const { Account2FA, MULTISIG_DEPOSIT, MULTISIG_GAS } = require('../lib'); +const { Account2FA, MULTISIG_DEPOSIT, MULTISIG_GAS, MultisigStateStatus } = require('../lib'); const testUtils = require('./test-utils'); const { functionCall, transfer } = actionCreators; @@ -47,6 +47,7 @@ const getAccount2FA = async (account, keyMapping = ({ public_key: publicKey }) = account2fa.getRecoveryMethods = () => ({ data: keys.map(keyMapping) }); + account2fa.checkMultisigCodeAndStateStatus = () => ({ codeStatus: 1, stateStatus: MultisigStateStatus.STATE_NOT_INITIALIZED }); await account2fa.deployMultisig([...fs.readFileSync('./test/wasm/multisig.wasm')]); return account2fa; }; diff --git a/packages/accounts/test/providers.test.js b/packages/accounts/test/providers.test.js index ca6dec7e71..6355090cea 100644 --- a/packages/accounts/test/providers.test.js +++ b/packages/accounts/test/providers.test.js @@ -2,18 +2,20 @@ const BN = require('bn.js'); const base58 = require('bs58'); const testUtils = require('./test-utils'); +const { KeyPair } = require('@near-js/crypto'); +let ERRORS_JSON = require('../../utils/lib/errors/error_messages.json'); -jest.setTimeout(20000); +jest.setTimeout(60000); -describe('providers', () => { - let provider; - let near; +let provider; +let near; - beforeAll(async () => { - near = await testUtils.setUpTestConnection(); - provider = near.connection.provider; - }); +beforeAll(async () => { + near = await testUtils.setUpTestConnection(); + provider = near.connection.provider; +}); +describe('providers', () => { test('txStatus with string hash and buffer hash', async () => { const sender = await testUtils.createAccount(near); const receiver = await testUtils.createAccount(near); @@ -188,3 +190,94 @@ describe('providers', () => { await expect(provider.lightClientProof(lightClientRequest)).rejects.toThrow(/.+ block .+ is ahead of head block .+/); }); }); + +describe('providers errors', () => { + test('JSON RPC Error - MethodNotFound', async () => { + const account = await testUtils.createAccount(near); + const contract = await testUtils.deployContract( + account, + testUtils.generateUniqueString('test') + ); + + await contract.setValue({ args: { value: 'hello' } }); + + try { + const response = await provider.query({ + request_type: 'call_function', + finality: 'optimistic', + account_id: contract.contractId, + method_name: 'methodNameThatDoesNotExist', + args_base64: '', + }); + expect(response).toBeUndefined(); + } catch (e) { + const errorType = 'MethodNotFound'; + expect(e.type).toEqual(errorType); + expect(e.message).toEqual(ERRORS_JSON[errorType]); + } + }); + + test('JSON RPC Error - CodeDoesNotExist', async () => { + const { accountId } = await testUtils.createAccount(near); + + try { + const response = await provider.query({ + request_type: 'call_function', + finality: 'optimistic', + account_id: accountId, + method_name: 'methodNameThatDoesNotExistOnContractNotDeployed', + args_base64: '', + }); + expect(response).toBeUndefined(); + } catch (e) { + const errorType = 'CodeDoesNotExist'; + expect(e.type).toEqual(errorType); + expect(e.message.split(' ').slice(0, 5)).toEqual( + ERRORS_JSON[errorType].split(' ').slice(0, 5) + ); + } + }); + + test('JSON RPC Error - AccountDoesNotExist', async () => { + const accountName = 'abc.near'; + try { + const response = await provider.query({ + request_type: 'call_function', + finality: 'optimistic', + account_id: accountName, + method_name: 'methodNameThatDoesNotExistOnContractNotDeployed', + args_base64: '', + }); + expect(response).toBeUndefined(); + } catch (e) { + const errorType = 'AccountDoesNotExist'; + expect(e.type).toEqual(errorType); + expect(e.message.split(' ').slice(0, 5)).toEqual( + ERRORS_JSON[errorType].split(' ').slice(0, 5) + ); + } + }); + + test('JSON RPC Error - AccessKeyDoesNotExist', async () => { + const { accountId } = await testUtils.createAccount(near); + + try { + const response = await provider.query({ + request_type: 'view_access_key', + finality: 'optimistic', + account_id: accountId, + public_key: KeyPair.fromRandom('ed25519') + .getPublicKey() + .toString(), + }); + expect(response).toBeUndefined(); + } catch (e) { + const errorType = 'AccessKeyDoesNotExist'; + expect(e.type).toEqual(errorType); + expect(e.message.split(' ').slice(0, 5)).toEqual( + ERRORS_JSON[errorType].split(' ').slice(0, 5) + ); + } + }); +}); + diff --git a/packages/providers/src/json-rpc-provider.ts b/packages/providers/src/json-rpc-provider.ts index 22ccd07281..cc706b7bdc 100644 --- a/packages/providers/src/json-rpc-provider.ts +++ b/packages/providers/src/json-rpc-provider.ts @@ -7,9 +7,11 @@ */ import { baseEncode, + formatError, getErrorTypeFromErrorMessage, Logger, parseRpcError, + ServerError, } from '@near-js/utils'; import { AccessKeyWithPublicKey, @@ -366,7 +368,17 @@ export class JsonRpcProvider extends Provider { throw new TypedError(errorMessage, 'TimeoutError'); } - throw new TypedError(errorMessage, getErrorTypeFromErrorMessage(response.error.data, response.error.name)); + const errorType = getErrorTypeFromErrorMessage(response.error.data, ''); + if (errorType) { + throw new TypedError(formatError(errorType, params), errorType); + } + throw new TypedError(errorMessage, response.error.name); + } + } else if (typeof response.result?.error === 'string') { + const errorType = getErrorTypeFromErrorMessage(response.result.error, ''); + + if (errorType) { + throw new ServerError(formatError(errorType, params), errorType); } } // Success when response.error is not exist diff --git a/packages/utils/fetch_error_schema.js b/packages/utils/fetch_error_schema.js index 0882458e90..c2dd226190 100644 --- a/packages/utils/fetch_error_schema.js +++ b/packages/utils/fetch_error_schema.js @@ -2,8 +2,8 @@ const https = require('https'); const fs = require('fs'); const ERROR_SCHEMA_URL = - 'https://raw.githubusercontent.com/nearprotocol/nearcore/4c1149974ccf899dbcb2253a3e27cbab86dc47be/chain/jsonrpc/res/rpc_errors_schema.json'; -const TARGET_SCHEMA_FILE_PATH = `${process.argv[2] || process.cwd()}/src/errors/rpc_error_schema.json'`; + 'https://raw.githubusercontent.com/near/nearcore/master/chain/jsonrpc/res/rpc_errors_schema.json'; +const TARGET_SCHEMA_FILE_PATH = `${process.argv[2] || process.cwd()}/src/errors/rpc_error_schema.json`; https .get(ERROR_SCHEMA_URL, resp => { diff --git a/packages/utils/src/errors/error_messages.json b/packages/utils/src/errors/error_messages.json index 085313959a..be5bafaf7b 100644 --- a/packages/utils/src/errors/error_messages.json +++ b/packages/utils/src/errors/error_messages.json @@ -56,6 +56,7 @@ "AccountAlreadyExists": "Can't create a new account {{account_id}}, because it already exists", "InvalidChain": "Transaction parent block hash doesn't belong to the current chain", "AccountDoesNotExist": "Can't complete the action because account {{account_id}} doesn't exist", + "AccessKeyDoesNotExist": "Can't complete the action because access key {{public_key}} doesn't exist", "MethodNameMismatch": "Transaction method name {{method_name}} isn't allowed by the access key", "DeleteAccountHasRent": "Account {{account_id}} can't be deleted. It has {{#formatNear}}{{balance}}{{/formatNear}}, which is enough to cover the rent", "DeleteAccountHasEnoughBalance": "Account {{account_id}} can't be deleted. It has {{#formatNear}}{{balance}}{{/formatNear}}, which is enough to cover it's storage", diff --git a/packages/utils/src/errors/rpc_error_schema.json b/packages/utils/src/errors/rpc_error_schema.json index e5ac6cec66..1279c61751 100644 --- a/packages/utils/src/errors/rpc_error_schema.json +++ b/packages/utils/src/errors/rpc_error_schema.json @@ -1,452 +1,5 @@ { "schema": { - "BadUTF16": { - "name": "BadUTF16", - "subtypes": [], - "props": {} - }, - "BadUTF8": { - "name": "BadUTF8", - "subtypes": [], - "props": {} - }, - "BalanceExceeded": { - "name": "BalanceExceeded", - "subtypes": [], - "props": {} - }, - "BreakpointTrap": { - "name": "BreakpointTrap", - "subtypes": [], - "props": {} - }, - "CacheError": { - "name": "CacheError", - "subtypes": [ - "ReadError", - "WriteError", - "DeserializationError", - "SerializationError" - ], - "props": {} - }, - "CallIndirectOOB": { - "name": "CallIndirectOOB", - "subtypes": [], - "props": {} - }, - "CannotAppendActionToJointPromise": { - "name": "CannotAppendActionToJointPromise", - "subtypes": [], - "props": {} - }, - "CannotReturnJointPromise": { - "name": "CannotReturnJointPromise", - "subtypes": [], - "props": {} - }, - "CodeDoesNotExist": { - "name": "CodeDoesNotExist", - "subtypes": [], - "props": { - "account_id": "" - } - }, - "CompilationError": { - "name": "CompilationError", - "subtypes": [ - "CodeDoesNotExist", - "PrepareError", - "WasmerCompileError" - ], - "props": {} - }, - "ContractSizeExceeded": { - "name": "ContractSizeExceeded", - "subtypes": [], - "props": { - "limit": "", - "size": "" - } - }, - "Deprecated": { - "name": "Deprecated", - "subtypes": [], - "props": { - "method_name": "" - } - }, - "Deserialization": { - "name": "Deserialization", - "subtypes": [], - "props": {} - }, - "DeserializationError": { - "name": "DeserializationError", - "subtypes": [], - "props": {} - }, - "EmptyMethodName": { - "name": "EmptyMethodName", - "subtypes": [], - "props": {} - }, - "FunctionCallError": { - "name": "FunctionCallError", - "subtypes": [ - "CompilationError", - "LinkError", - "MethodResolveError", - "WasmTrap", - "WasmUnknownError", - "HostError", - "EvmError" - ], - "props": {} - }, - "GasExceeded": { - "name": "GasExceeded", - "subtypes": [], - "props": {} - }, - "GasInstrumentation": { - "name": "GasInstrumentation", - "subtypes": [], - "props": {} - }, - "GasLimitExceeded": { - "name": "GasLimitExceeded", - "subtypes": [], - "props": {} - }, - "GenericTrap": { - "name": "GenericTrap", - "subtypes": [], - "props": {} - }, - "GuestPanic": { - "name": "GuestPanic", - "subtypes": [], - "props": { - "panic_msg": "" - } - }, - "HostError": { - "name": "HostError", - "subtypes": [ - "BadUTF16", - "BadUTF8", - "GasExceeded", - "GasLimitExceeded", - "BalanceExceeded", - "EmptyMethodName", - "GuestPanic", - "IntegerOverflow", - "InvalidPromiseIndex", - "CannotAppendActionToJointPromise", - "CannotReturnJointPromise", - "InvalidPromiseResultIndex", - "InvalidRegisterId", - "IteratorWasInvalidated", - "MemoryAccessViolation", - "InvalidReceiptIndex", - "InvalidIteratorIndex", - "InvalidAccountId", - "InvalidMethodName", - "InvalidPublicKey", - "ProhibitedInView", - "NumberOfLogsExceeded", - "KeyLengthExceeded", - "ValueLengthExceeded", - "TotalLogLengthExceeded", - "NumberPromisesExceeded", - "NumberInputDataDependenciesExceeded", - "ReturnedValueLengthExceeded", - "ContractSizeExceeded", - "Deprecated" - ], - "props": {} - }, - "IllegalArithmetic": { - "name": "IllegalArithmetic", - "subtypes": [], - "props": {} - }, - "IncorrectCallIndirectSignature": { - "name": "IncorrectCallIndirectSignature", - "subtypes": [], - "props": {} - }, - "Instantiate": { - "name": "Instantiate", - "subtypes": [], - "props": {} - }, - "IntegerOverflow": { - "name": "IntegerOverflow", - "subtypes": [], - "props": {} - }, - "InternalMemoryDeclared": { - "name": "InternalMemoryDeclared", - "subtypes": [], - "props": {} - }, - "InvalidAccountId": { - "name": "InvalidAccountId", - "subtypes": [], - "props": { - "account_id": "" - } - }, - "InvalidIteratorIndex": { - "name": "InvalidIteratorIndex", - "subtypes": [], - "props": { - "iterator_index": "" - } - }, - "InvalidMethodName": { - "name": "InvalidMethodName", - "subtypes": [], - "props": {} - }, - "InvalidPromiseIndex": { - "name": "InvalidPromiseIndex", - "subtypes": [], - "props": { - "promise_idx": "" - } - }, - "InvalidPromiseResultIndex": { - "name": "InvalidPromiseResultIndex", - "subtypes": [], - "props": { - "result_idx": "" - } - }, - "InvalidPublicKey": { - "name": "InvalidPublicKey", - "subtypes": [], - "props": {} - }, - "InvalidReceiptIndex": { - "name": "InvalidReceiptIndex", - "subtypes": [], - "props": { - "receipt_index": "" - } - }, - "InvalidRegisterId": { - "name": "InvalidRegisterId", - "subtypes": [], - "props": { - "register_id": "" - } - }, - "IteratorWasInvalidated": { - "name": "IteratorWasInvalidated", - "subtypes": [], - "props": { - "iterator_index": "" - } - }, - "KeyLengthExceeded": { - "name": "KeyLengthExceeded", - "subtypes": [], - "props": { - "length": "", - "limit": "" - } - }, - "LinkError": { - "name": "LinkError", - "subtypes": [], - "props": { - "msg": "" - } - }, - "Memory": { - "name": "Memory", - "subtypes": [], - "props": {} - }, - "MemoryAccessViolation": { - "name": "MemoryAccessViolation", - "subtypes": [], - "props": {} - }, - "MemoryOutOfBounds": { - "name": "MemoryOutOfBounds", - "subtypes": [], - "props": {} - }, - "MethodEmptyName": { - "name": "MethodEmptyName", - "subtypes": [], - "props": {} - }, - "MethodInvalidSignature": { - "name": "MethodInvalidSignature", - "subtypes": [], - "props": {} - }, - "MethodNotFound": { - "name": "MethodNotFound", - "subtypes": [], - "props": {} - }, - "MethodResolveError": { - "name": "MethodResolveError", - "subtypes": [ - "MethodEmptyName", - "MethodUTF8Error", - "MethodNotFound", - "MethodInvalidSignature" - ], - "props": {} - }, - "MethodUTF8Error": { - "name": "MethodUTF8Error", - "subtypes": [], - "props": {} - }, - "MisalignedAtomicAccess": { - "name": "MisalignedAtomicAccess", - "subtypes": [], - "props": {} - }, - "NumberInputDataDependenciesExceeded": { - "name": "NumberInputDataDependenciesExceeded", - "subtypes": [], - "props": { - "limit": "", - "number_of_input_data_dependencies": "" - } - }, - "NumberOfLogsExceeded": { - "name": "NumberOfLogsExceeded", - "subtypes": [], - "props": { - "limit": "" - } - }, - "NumberPromisesExceeded": { - "name": "NumberPromisesExceeded", - "subtypes": [], - "props": { - "limit": "", - "number_of_promises": "" - } - }, - "PrepareError": { - "name": "PrepareError", - "subtypes": [ - "Serialization", - "Deserialization", - "InternalMemoryDeclared", - "GasInstrumentation", - "StackHeightInstrumentation", - "Instantiate", - "Memory" - ], - "props": {} - }, - "ProhibitedInView": { - "name": "ProhibitedInView", - "subtypes": [], - "props": { - "method_name": "" - } - }, - "ReadError": { - "name": "ReadError", - "subtypes": [], - "props": {} - }, - "ReturnedValueLengthExceeded": { - "name": "ReturnedValueLengthExceeded", - "subtypes": [], - "props": { - "length": "", - "limit": "" - } - }, - "Serialization": { - "name": "Serialization", - "subtypes": [], - "props": {} - }, - "SerializationError": { - "name": "SerializationError", - "subtypes": [], - "props": { - "hash": "" - } - }, - "StackHeightInstrumentation": { - "name": "StackHeightInstrumentation", - "subtypes": [], - "props": {} - }, - "StackOverflow": { - "name": "StackOverflow", - "subtypes": [], - "props": {} - }, - "TotalLogLengthExceeded": { - "name": "TotalLogLengthExceeded", - "subtypes": [], - "props": { - "length": "", - "limit": "" - } - }, - "Unreachable": { - "name": "Unreachable", - "subtypes": [], - "props": {} - }, - "ValueLengthExceeded": { - "name": "ValueLengthExceeded", - "subtypes": [], - "props": { - "length": "", - "limit": "" - } - }, - "WasmTrap": { - "name": "WasmTrap", - "subtypes": [ - "Unreachable", - "IncorrectCallIndirectSignature", - "MemoryOutOfBounds", - "CallIndirectOOB", - "IllegalArithmetic", - "MisalignedAtomicAccess", - "BreakpointTrap", - "StackOverflow", - "GenericTrap" - ], - "props": {} - }, - "WasmUnknownError": { - "name": "WasmUnknownError", - "subtypes": [], - "props": {} - }, - "WasmerCompileError": { - "name": "WasmerCompileError", - "subtypes": [], - "props": { - "msg": "" - } - }, - "WriteError": { - "name": "WriteError", - "subtypes": [], - "props": {} - }, "AccessKeyNotFound": { "name": "AccessKeyNotFound", "subtypes": [], @@ -486,7 +39,14 @@ "InsufficientStake", "FunctionCallError", "NewReceiptValidationError", - "OnlyImplicitAccountCreationAllowed" + "OnlyImplicitAccountCreationAllowed", + "DeleteAccountWithLargeState", + "DelegateActionInvalidSignature", + "DelegateActionSenderDoesNotMatchTxReceiver", + "DelegateActionExpired", + "DelegateActionAccessKeyError", + "DelegateActionInvalidNonce", + "DelegateActionNonceTooLarge" ], "props": { "index": "" @@ -506,7 +66,9 @@ "FunctionCallMethodNameLengthExceeded", "FunctionCallArgumentsLengthExceeded", "UnsuitableStakingKey", - "FunctionCallZeroAttachedGas" + "FunctionCallZeroAttachedGas", + "DelegateActionMustBeOnlyOne", + "UnsupportedProtocolFeature" ], "props": {} }, @@ -542,6 +104,28 @@ "total_number_of_bytes": "" } }, + "AltBn128InvalidInput": { + "name": "AltBn128InvalidInput", + "subtypes": [], + "props": { + "msg": "" + } + }, + "BadUTF16": { + "name": "BadUTF16", + "subtypes": [], + "props": {} + }, + "BadUTF8": { + "name": "BadUTF8", + "subtypes": [], + "props": {} + }, + "BalanceExceeded": { + "name": "BalanceExceeded", + "subtypes": [], + "props": {} + }, "BalanceMismatchError": { "name": "BalanceMismatchError", "subtypes": [], @@ -560,6 +144,45 @@ "tx_burnt_amount": "" } }, + "CallIndirectOOB": { + "name": "CallIndirectOOB", + "subtypes": [], + "props": {} + }, + "CannotAppendActionToJointPromise": { + "name": "CannotAppendActionToJointPromise", + "subtypes": [], + "props": {} + }, + "CannotReturnJointPromise": { + "name": "CannotReturnJointPromise", + "subtypes": [], + "props": {} + }, + "CodeDoesNotExist": { + "name": "CodeDoesNotExist", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "CompilationError": { + "name": "CompilationError", + "subtypes": [ + "CodeDoesNotExist", + "PrepareError", + "WasmerCompileError" + ], + "props": {} + }, + "ContractSizeExceeded": { + "name": "ContractSizeExceeded", + "subtypes": [], + "props": { + "limit": "", + "size": "" + } + }, "CostOverflow": { "name": "CostOverflow", "subtypes": [], @@ -582,6 +205,45 @@ "registrar_account_id": "" } }, + "DelegateActionExpired": { + "name": "DelegateActionExpired", + "subtypes": [], + "props": {} + }, + "DelegateActionInvalidNonce": { + "name": "DelegateActionInvalidNonce", + "subtypes": [], + "props": { + "ak_nonce": "", + "delegate_nonce": "" + } + }, + "DelegateActionInvalidSignature": { + "name": "DelegateActionInvalidSignature", + "subtypes": [], + "props": {} + }, + "DelegateActionMustBeOnlyOne": { + "name": "DelegateActionMustBeOnlyOne", + "subtypes": [], + "props": {} + }, + "DelegateActionNonceTooLarge": { + "name": "DelegateActionNonceTooLarge", + "subtypes": [], + "props": { + "delegate_nonce": "", + "upper_bound": "" + } + }, + "DelegateActionSenderDoesNotMatchTxReceiver": { + "name": "DelegateActionSenderDoesNotMatchTxReceiver", + "subtypes": [], + "props": { + "receiver_id": "", + "sender_id": "" + } + }, "DeleteAccountStaking": { "name": "DeleteAccountStaking", "subtypes": [], @@ -589,6 +251,13 @@ "account_id": "" } }, + "DeleteAccountWithLargeState": { + "name": "DeleteAccountWithLargeState", + "subtypes": [], + "props": { + "account_id": "" + } + }, "DeleteActionMustBeFinal": { "name": "DeleteActionMustBeFinal", "subtypes": [], @@ -607,6 +276,37 @@ "subtypes": [], "props": {} }, + "Deprecated": { + "name": "Deprecated", + "subtypes": [], + "props": { + "method_name": "" + } + }, + "Deserialization": { + "name": "Deserialization", + "subtypes": [], + "props": {} + }, + "ECRecoverError": { + "name": "ECRecoverError", + "subtypes": [], + "props": { + "msg": "" + } + }, + "Ed25519VerifyInvalidInput": { + "name": "Ed25519VerifyInvalidInput", + "subtypes": [], + "props": { + "msg": "" + } + }, + "EmptyMethodName": { + "name": "EmptyMethodName", + "subtypes": [], + "props": {} + }, "Expired": { "name": "Expired", "subtypes": [], @@ -628,8 +328,94 @@ "limit": "" } }, - "FunctionCallZeroAttachedGas": { - "name": "FunctionCallZeroAttachedGas", + "FunctionCallZeroAttachedGas": { + "name": "FunctionCallZeroAttachedGas", + "subtypes": [], + "props": {} + }, + "GasExceeded": { + "name": "GasExceeded", + "subtypes": [], + "props": {} + }, + "GasInstrumentation": { + "name": "GasInstrumentation", + "subtypes": [], + "props": {} + }, + "GasLimitExceeded": { + "name": "GasLimitExceeded", + "subtypes": [], + "props": {} + }, + "GenericTrap": { + "name": "GenericTrap", + "subtypes": [], + "props": {} + }, + "GuestPanic": { + "name": "GuestPanic", + "subtypes": [], + "props": { + "panic_msg": "" + } + }, + "HostError": { + "name": "HostError", + "subtypes": [ + "BadUTF16", + "BadUTF8", + "GasExceeded", + "GasLimitExceeded", + "BalanceExceeded", + "EmptyMethodName", + "GuestPanic", + "IntegerOverflow", + "InvalidPromiseIndex", + "CannotAppendActionToJointPromise", + "CannotReturnJointPromise", + "InvalidPromiseResultIndex", + "InvalidRegisterId", + "IteratorWasInvalidated", + "MemoryAccessViolation", + "InvalidReceiptIndex", + "InvalidIteratorIndex", + "InvalidAccountId", + "InvalidMethodName", + "InvalidPublicKey", + "ProhibitedInView", + "NumberOfLogsExceeded", + "KeyLengthExceeded", + "ValueLengthExceeded", + "TotalLogLengthExceeded", + "NumberPromisesExceeded", + "NumberInputDataDependenciesExceeded", + "ReturnedValueLengthExceeded", + "ContractSizeExceeded", + "Deprecated", + "ECRecoverError", + "AltBn128InvalidInput", + "Ed25519VerifyInvalidInput" + ], + "props": {} + }, + "IllegalArithmetic": { + "name": "IllegalArithmetic", + "subtypes": [], + "props": {} + }, + "IncorrectCallIndirectSignature": { + "name": "IncorrectCallIndirectSignature", + "subtypes": [], + "props": {} + }, + "IndirectCallToNull": { + "name": "IndirectCallToNull", + "subtypes": [], + "props": {} + }, + "Instantiate": { + "name": "Instantiate", "subtypes": [], "props": {} }, @@ -642,6 +428,16 @@ "stake": "" } }, + "IntegerOverflow": { + "name": "IntegerOverflow", + "subtypes": [], + "props": {} + }, + "InternalMemoryDeclared": { + "name": "InternalMemoryDeclared", + "subtypes": [], + "props": {} + }, "InvalidAccessKeyError": { "name": "InvalidAccessKeyError", "subtypes": [ @@ -654,6 +450,11 @@ ], "props": {} }, + "InvalidAccountId": { + "name": "InvalidAccountId", + "subtypes": [], + "props": {} + }, "InvalidChain": { "name": "InvalidChain", "subtypes": [], @@ -666,6 +467,18 @@ "account_id": "" } }, + "InvalidIteratorIndex": { + "name": "InvalidIteratorIndex", + "subtypes": [], + "props": { + "iterator_index": "" + } + }, + "InvalidMethodName": { + "name": "InvalidMethodName", + "subtypes": [], + "props": {} + }, "InvalidNonce": { "name": "InvalidNonce", "subtypes": [], @@ -681,6 +494,32 @@ "account_id": "" } }, + "InvalidPromiseIndex": { + "name": "InvalidPromiseIndex", + "subtypes": [], + "props": { + "promise_idx": "" + } + }, + "InvalidPromiseResultIndex": { + "name": "InvalidPromiseResultIndex", + "subtypes": [], + "props": { + "result_idx": "" + } + }, + "InvalidPublicKey": { + "name": "InvalidPublicKey", + "subtypes": [], + "props": {} + }, + "InvalidReceiptIndex": { + "name": "InvalidReceiptIndex", + "subtypes": [], + "props": { + "receipt_index": "" + } + }, "InvalidReceiverId": { "name": "InvalidReceiverId", "subtypes": [], @@ -688,6 +527,13 @@ "account_id": "" } }, + "InvalidRegisterId": { + "name": "InvalidRegisterId", + "subtypes": [], + "props": { + "register_id": "" + } + }, "InvalidSignature": { "name": "InvalidSignature", "subtypes": [], @@ -707,6 +553,7 @@ "InvalidSignerId", "SignerDoesNotExist", "InvalidNonce", + "NonceTooLarge", "InvalidReceiverId", "InvalidSignature", "NotEnoughBalance", @@ -714,10 +561,26 @@ "CostOverflow", "InvalidChain", "Expired", - "ActionsValidation" + "ActionsValidation", + "TransactionSizeExceeded" ], "props": {} }, + "IteratorWasInvalidated": { + "name": "IteratorWasInvalidated", + "subtypes": [], + "props": { + "iterator_index": "" + } + }, + "KeyLengthExceeded": { + "name": "KeyLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, "LackBalanceForState": { "name": "LackBalanceForState", "subtypes": [], @@ -726,6 +589,31 @@ "amount": "" } }, + "Memory": { + "name": "Memory", + "subtypes": [], + "props": {} + }, + "MemoryAccessViolation": { + "name": "MemoryAccessViolation", + "subtypes": [], + "props": {} + }, + "MemoryOutOfBounds": { + "name": "MemoryOutOfBounds", + "subtypes": [], + "props": {} + }, + "MethodEmptyName": { + "name": "MethodEmptyName", + "subtypes": [], + "props": {} + }, + "MethodInvalidSignature": { + "name": "MethodInvalidSignature", + "subtypes": [], + "props": {} + }, "MethodNameMismatch": { "name": "MethodNameMismatch", "subtypes": [], @@ -733,6 +621,33 @@ "method_name": "" } }, + "MethodNotFound": { + "name": "MethodNotFound", + "subtypes": [], + "props": {} + }, + "MethodResolveError": { + "name": "MethodResolveError", + "subtypes": [ + "MethodEmptyName", + "MethodNotFound", + "MethodInvalidSignature" + ], + "props": {} + }, + "MisalignedAtomicAccess": { + "name": "MisalignedAtomicAccess", + "subtypes": [], + "props": {} + }, + "NonceTooLarge": { + "name": "NonceTooLarge", + "subtypes": [], + "props": { + "tx_nonce": "", + "upper_bound": "" + } + }, "NotEnoughAllowance": { "name": "NotEnoughAllowance", "subtypes": [], @@ -752,6 +667,29 @@ "signer_id": "" } }, + "NumberInputDataDependenciesExceeded": { + "name": "NumberInputDataDependenciesExceeded", + "subtypes": [], + "props": { + "limit": "", + "number_of_input_data_dependencies": "" + } + }, + "NumberOfLogsExceeded": { + "name": "NumberOfLogsExceeded", + "subtypes": [], + "props": { + "limit": "" + } + }, + "NumberPromisesExceeded": { + "name": "NumberPromisesExceeded", + "subtypes": [], + "props": { + "limit": "", + "number_of_promises": "" + } + }, "OnlyImplicitAccountCreationAllowed": { "name": "OnlyImplicitAccountCreationAllowed", "subtypes": [], @@ -759,6 +697,28 @@ "account_id": "" } }, + "PrepareError": { + "name": "PrepareError", + "subtypes": [ + "Serialization", + "Deserialization", + "InternalMemoryDeclared", + "GasInstrumentation", + "StackHeightInstrumentation", + "Instantiate", + "Memory", + "TooManyFunctions", + "TooManyLocals" + ], + "props": {} + }, + "ProhibitedInView": { + "name": "ProhibitedInView", + "subtypes": [], + "props": { + "method_name": "" + } + }, "ReceiptValidationError": { "name": "ReceiptValidationError", "subtypes": [ @@ -785,6 +745,19 @@ "subtypes": [], "props": {} }, + "ReturnedValueLengthExceeded": { + "name": "ReturnedValueLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "Serialization": { + "name": "Serialization", + "subtypes": [], + "props": {} + }, "SignerDoesNotExist": { "name": "SignerDoesNotExist", "subtypes": [], @@ -792,6 +765,34 @@ "signer_id": "" } }, + "StackHeightInstrumentation": { + "name": "StackHeightInstrumentation", + "subtypes": [], + "props": {} + }, + "StackOverflow": { + "name": "StackOverflow", + "subtypes": [], + "props": {} + }, + "TooManyFunctions": { + "name": "TooManyFunctions", + "subtypes": [], + "props": {} + }, + "TooManyLocals": { + "name": "TooManyLocals", + "subtypes": [], + "props": {} + }, + "TotalLogLengthExceeded": { + "name": "TotalLogLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, "TotalNumberOfActionsExceeded": { "name": "TotalNumberOfActionsExceeded", "subtypes": [], @@ -808,6 +809,14 @@ "total_prepaid_gas": "" } }, + "TransactionSizeExceeded": { + "name": "TransactionSizeExceeded", + "subtypes": [], + "props": { + "limit": "", + "size": "" + } + }, "TriesToStake": { "name": "TriesToStake", "subtypes": [], @@ -833,6 +842,11 @@ ], "props": {} }, + "Unreachable": { + "name": "Unreachable", + "subtypes": [], + "props": {} + }, "UnsuitableStakingKey": { "name": "UnsuitableStakingKey", "subtypes": [], @@ -840,13 +854,46 @@ "public_key": "" } }, - "Closed": { - "name": "Closed", + "UnsupportedProtocolFeature": { + "name": "UnsupportedProtocolFeature", "subtypes": [], + "props": { + "protocol_feature": "", + "version": "" + } + }, + "ValueLengthExceeded": { + "name": "ValueLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "WasmTrap": { + "name": "WasmTrap", + "subtypes": [ + "Unreachable", + "IncorrectCallIndirectSignature", + "MemoryOutOfBounds", + "CallIndirectOOB", + "IllegalArithmetic", + "MisalignedAtomicAccess", + "IndirectCallToNull", + "StackOverflow", + "GenericTrap" + ], "props": {} }, - "InternalError": { - "name": "InternalError", + "WasmerCompileError": { + "name": "WasmerCompileError", + "subtypes": [], + "props": { + "msg": "" + } + }, + "Closed": { + "name": "Closed", "subtypes": [], "props": {} }, @@ -855,8 +902,7 @@ "subtypes": [ "TxExecutionError", "Timeout", - "Closed", - "InternalError" + "Closed" ], "props": {} }, diff --git a/packages/utils/src/errors/rpc_errors.ts b/packages/utils/src/errors/rpc_errors.ts index 54605bc16d..57e73a1529 100644 --- a/packages/utils/src/errors/rpc_errors.ts +++ b/packages/utils/src/errors/rpc_errors.ts @@ -96,6 +96,12 @@ export function getErrorTypeFromErrorMessage(errorMessage, errorType) { return 'AccessKeyDoesNotExist'; case /wasm execution failed with error: FunctionCallError\(CompilationError\(CodeDoesNotExist/.test(errorMessage): return 'CodeDoesNotExist'; + case /wasm execution failed with error: CompilationError\(CodeDoesNotExist/.test(errorMessage): + return 'CodeDoesNotExist'; + case /wasm execution failed with error: FunctionCallError\(MethodResolveError\(MethodNotFound/.test(errorMessage): + return 'MethodNotFound'; + case /wasm execution failed with error: MethodResolveError\(MethodNotFound/.test(errorMessage): + return 'MethodNotFound'; case /Transaction nonce \d+ must be larger than nonce of the used access key \d+/.test(errorMessage): return 'InvalidNonce'; default: diff --git a/packages/utils/test/rpc-errors.test.js b/packages/utils/test/rpc-errors.test.js index 2039324529..33c5a6c6a3 100644 --- a/packages/utils/test/rpc-errors.test.js +++ b/packages/utils/test/rpc-errors.test.js @@ -52,9 +52,8 @@ describe('rpc-errors', () => { } }; let error = parseRpcError(rpc_error); - expect(error.type === 'InvalidIteratorIndex').toBe(true); - expect(error.iterator_index).toBe(42); - expect(formatError(error.type, error)).toBe('Iterator index 42 does not exist'); + expect(error.type).toBe('ActionError'); + expect(formatError(error.type, error)).toBe('{\"type\":\"ActionError\",\"kind\":{\"FunctionCallError\":{\"HostError\":{\"InvalidIteratorIndex\":{\"iterator_index\":42}}}}}'); }); test('test ActionError::FunctionCallError::GasLimitExceeded error', async () => { @@ -69,28 +68,34 @@ describe('rpc-errors', () => { } }; let error = parseRpcError(rpc_error); - expect(error.type === 'GasLimitExceeded').toBe(true); + expect(error.type).toBe('ActionError'); - expect(formatError(error.type, error)).toBe('Exceeded the maximum amount of gas allowed to burn per contract'); + expect(formatError(error.type, error)).toBe('{\"type\":\"ActionError\",\"index\":0,\"kind\":{\"index\":0,\"kind\":{\"FunctionCallError\":{\"HostError\":\"GasLimitExceeded\"}}}}'); }); test('test parse error object', async () => { const errorStr = '{"status":{"Failure":{"ActionError":{"index":0,"kind":{"FunctionCallError":{"EvmError":"ArgumentParseError"}}}}},"transaction":{"signer_id":"test.near","public_key":"ed25519:D5HVgBE8KgXkSirDE4UQ8qwieaLAR4wDDEgrPRtbbNep","nonce":110,"receiver_id":"evm","actions":[{"FunctionCall":{"method_name":"transfer","args":"888ZO7SvECKvfSCJ832LrnFXuF/QKrSGztwAAA==","gas":300000000000000,"deposit":"0"}}],"signature":"ed25519:7JtWQ2Ux63ixaKy7bTDJuRTWnv6XtgE84ejFMMjYGKdv2mLqPiCfkMqbAPt5xwLWwFdKjJniTcxWZe7FdiRWpWv","hash":"E1QorKKEh1WLJwRQSQ1pdzQN3f8yeFsQQ8CbJjnz1ZQe"},"transaction_outcome":{"proof":[],"block_hash":"HXXBPjGp65KaFtam7Xr67B8pZVGujZMZvTmVW6Fy9tXf","id":"E1QorKKEh1WLJwRQSQ1pdzQN3f8yeFsQQ8CbJjnz1ZQe","outcome":{"logs":[],"receipt_ids":["ZsKetkrZQGVTtmXr2jALgNjzcRqpoQQsk9HdLmFafeL"],"gas_burnt":2428001493624,"tokens_burnt":"2428001493624000000000","executor_id":"test.near","status":{"SuccessReceiptId":"ZsKetkrZQGVTtmXr2jALgNjzcRqpoQQsk9HdLmFafeL"}}},"receipts_outcome":[{"proof":[],"block_hash":"H6fQCVpxBDv9y2QtmTVHoxHibJvamVsHau7fDi7AmFa2","id":"ZsKetkrZQGVTtmXr2jALgNjzcRqpoQQsk9HdLmFafeL","outcome":{"logs":[],"receipt_ids":["DgRyf1Wv3ZYLFvM8b67k2yZjdmnyUUJtRkTxAwoFi3qD"],"gas_burnt":2428001493624,"tokens_burnt":"2428001493624000000000","executor_id":"evm","status":{"Failure":{"ActionError":{"index":0,"kind":{"FunctionCallError":{"EvmError":"ArgumentParseError"}}}}}}},{"proof":[],"block_hash":"9qNVA235L9XdZ8rZLBAPRNBbiGPyNnMUfpbi9WxbRdbB","id":"DgRyf1Wv3ZYLFvM8b67k2yZjdmnyUUJtRkTxAwoFi3qD","outcome":{"logs":[],"receipt_ids":[],"gas_burnt":0,"tokens_burnt":"0","executor_id":"test.near","status":{"SuccessValue":""}}}]}'; const error = parseRpcError(JSON.parse(errorStr).status.Failure); - expect(error).toEqual(new ServerError('{"index":0,"kind":{"EvmError":"ArgumentParseError"}}')); + expect(error).toEqual(new ServerError('{"index":0,"kind":{"index":0,"kind":{"FunctionCallError":{"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 err4 = 'wasm execution failed with error: 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'; + const err6 = 'wasm execution failed with error: MethodResolveError(MethodNotFound)'; + const err7 = 'wasm execution failed with error: FunctionCallError(CompilationError(CodeDoesNotExist { account_id: "random.testnet" }))'; + const err8 = 'wasm execution failed with error: FunctionCallError(MethodResolveError(MethodNotFound))'; 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'); + expect(getErrorTypeFromErrorMessage(err6)).toEqual('MethodNotFound'); + expect(getErrorTypeFromErrorMessage(err7)).toEqual('CodeDoesNotExist'); + expect(getErrorTypeFromErrorMessage(err8)).toEqual('MethodNotFound'); }); test('test NotEnoughBalance message uses human readable values', () => {