Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ff865aa
chore: add new RailgunContractError class to separate different kind …
bhflm Jan 13, 2025
553e4ea
chore: add customError class
bhflm Jan 13, 2025
04faafc
chore: add constants.ts to segregate errors acording to categories, m…
bhflm Jan 13, 2025
3df2ce8
fix: export constants for shared usage
bhflm Jan 13, 2025
5052dc8
chore: refactor errors.ts sanitizeError with a more legible approach,…
bhflm Jan 13, 2025
41c9377
fix: test suite correct path
bhflm Jan 13, 2025
729c2e8
chore: EOF
bhflm Jan 13, 2025
a64b53e
chore: move ascii to constants.ts file
bhflm Jan 13, 2025
bb05caf
chore: rename ascii regex to be self documenting
bhflm Jan 13, 2025
b8e3601
fix: correct message and rename for transaction underpriced
bhflm Jan 13, 2025
39a2ba4
chore: remove comments
bhflm Jan 13, 2025
01c1403
chore: match refactor with old tests
bhflm Jan 13, 2025
0c4fe1e
chore: add more tests with edge cases for undefined, and ascii chars
bhflm Jan 13, 2025
2a624f9
fix: remove unnecesary classes, but keep Error class
bhflm Jan 13, 2025
5741d5a
misc: remove unnecesary spaces
bhflm Jan 14, 2025
0e4c319
chore: early return for undefined message case, check just for messag…
bhflm Jan 14, 2025
21b9a12
chore: rename to sanitizeAscii, compress ascii regex
bhflm Jan 14, 2025
4945425
fix: encapsulate catch inside objectwhen throwing Error
bhflm Jan 14, 2025
c9c4a03
fix: rename errors/error.ts to errors/index.ts
bhflm Jan 14, 2025
95af502
fix: errors path for test suite
bhflm Jan 14, 2025
0b206e2
chore: remove unnecesary eslint rules
bhflm Jan 14, 2025
082de73
chore: check for cause message and handle
bhflm Jan 14, 2025
d79855b
chore: rename to sanitizeAscii
bhflm Jan 14, 2025
08db455
fix: do not remove newlines within ascii regex
bhflm Jan 16, 2025
e88a4f4
fix: error directory rename
bhflm Jan 16, 2025
142ef1f
fix: correct error dir test path
bhflm Jan 16, 2025
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
32 changes: 31 additions & 1 deletion src/utils/__tests__/error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,34 @@ describe('error', () => {
sanitizeError(new Error('replacement fee too low')).message,
).to.equal(expectedErrorMessage);
});
});

it('Should sanitize insufficient gas errors', () => {
const expectedErrorMessage = 'Insufficient gas to process transaction.';
expect(
sanitizeError(new Error('insufficient funds for intrinsic')).message,
).to.equal(expectedErrorMessage);
});

it('Should sanitize low gas price errors', () => {
const expectedErrorMessage = 'Gas price rejected. Please select a higher gas price or resubmit.';

expect(
sanitizeError(new Error('intrinsic gas too low')).message,
).to.equal(expectedErrorMessage);
});

it('Should handle undefined error', () => {
const error = sanitizeError(undefined as unknown as Error);
expect(error.message).to.equal('Unknown error. Please try again.');
});

it('Should handle error without message', () => {
const error = sanitizeError(new Error());
expect(error.message).to.equal('Unknown error. Please try again.');
});

it('Should sanitize non-ASCII characters in unknown errors', () => {
const error = sanitizeError(new Error('Unknown error 🚫'));
expect(error.message).to.equal('Unknown error ');
});
});
150 changes: 0 additions & 150 deletions src/utils/error.ts

This file was deleted.

93 changes: 93 additions & 0 deletions src/utils/error/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { CustomErrorMapping } from "./types";

export const STRING_PREFIX_AFTER_UNICODE_REPLACEMENT = 'y %';

// Matches any characters that are NOT in the printable ASCII range (space to tilde)
// Printable ASCII characters are in the range of 32 (space) to 126 (tilde)
export const INVALID_ASCII_REGEX = /[^ -~\n]+/g;

export const CUSTOM_ERRORS: CustomErrorMapping = {
CONNECTION_ERROR: {
matches: ['quorum', 'could not connect to'],
message: 'Could not connect.'
},
RPC_ERROR: {
matches: ['call revert exception', 'error while dialing dial tcp'],
message: 'Failed to connect to RPC.'
},
RPC_CONNECTION_ERROR: {
matches: ['missing revert data'],
message: 'RPC connection error.'
},
KNOWN_TRANSACTION: {
matches: ['already known'],
message: 'Transaction successful but ethers request for TXID failed.'
},
LOW_REPLACEMENT_FEE: {
matches: ['replacement fee too low'],
message: 'Nonce is used in a pending transaction, and replacement fee is too low. Please increase your network fee to replace the pending transaction.'
},
LOW_GAS: {
matches: ['intrinsic gas too low'],
message: 'Gas price rejected. Please select a higher gas price or resubmit.'
},
UNDERPRICED_TRANSACTION: {
matches: ['transaction underpriced'],
message: 'Gas price rejected. Please select a higher gas price and resubmit.'
},
INSUFFICIENT_GAS: {
matches: ['insufficient funds for intrinsic'],
message: 'Insufficient gas to process transaction.'
},
NONCE_USED: {
matches: ['nonce has already been used'],
message: 'Nonce already used: the transaction was already completed.'
},
LOW_PRIVATE_BALANCE: {
matches: ['private balance too low', 'broadcaster fee'],
message: 'Private balance too low to pay broadcaster fee.'
},
TRANSACTION_SIMULATION_FAILED: {
matches: ['transaction may fail or may require manual gas limit'],
message: 'Unknown error. Transaction failed.'
}
};

export const RAILGUN_ERRORS: CustomErrorMapping = {
INVALID_NFT_NOTE: {
matches: ['invalid nft note value'],
message: 'RailgunSmartWallet: Invalid NFT Note Value.'
},
UNSUPPORTED_TOKEN: {
matches: ['unsupported token'],
message: 'RailgunSmartWallet: Unsupported Token. This token cannot interact with the RAILGUN contract.'
},
INVALID_NOTE_VALUE: {
matches: ['invalid note value'],
message: 'RailgunSmartWallet: Invalid Note Value. Please submit transaction with a corrected amount.'
},
INVALID_ADAPT_CONTRACT_SENDER: {
matches: ['invalid adapt contract as sender'],
message: 'RailgunSmartWallet: Invalid Adapt Contract as Sender. Please update your frontend to current Adapt module versions.'
},
INVALID_MERKLE_ROOT: {
matches: ['invalid merkle root'],
message: 'RailgunSmartWallet: Invalid Merkle Root. Please sync your balances and try again.'
},
NOTE_SPENT: {
matches: ['note already spent'],
message: 'RailgunSmartWallet: Note Already Spent. Please sync your balances and try again.'
},
INVALID_NOTE_CIPHERTEXT_ARRAY_LENGTH: {
matches: ['invalid note ciphertext array length'],
message: 'RailgunSmartWallet: Invalid Note Ciphertext Array Length. Please sync balances and re-prove your transaction.'
},
INVALID_WITHDRAW_NOTE: {
matches: ['invalid withdraw note'],
message: 'RailgunSmartWallet: Invalid Unshield Note. Please sync balances and re-prove your transaction.'
},
INVALID_SNARK_PROOF: {
matches: ['invalid snark proof'],
message: 'RailgunSmartWallet: Invalid Snark Proof. Please re-prove your transaction.'
}
};
48 changes: 48 additions & 0 deletions src/utils/error/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { CustomErrorMapping, ErrorDefinition } from './types';
import { STRING_PREFIX_AFTER_UNICODE_REPLACEMENT, RAILGUN_ERRORS, CUSTOM_ERRORS, INVALID_ASCII_REGEX } from './constants';

const sanitizeAscii = (str: string) => str.replace(INVALID_ASCII_REGEX, '');

const findMatchingError = (errorMessage: string, errorMapping: CustomErrorMapping): ErrorDefinition | null => {
const lowercaseMsg = errorMessage.toLowerCase();

for (const [, errorDef] of Object.entries(errorMapping)) {
if (errorDef.matches.some(match => lowercaseMsg.includes(match))) {
return errorDef;
}
}
return null;
}

const isRailgunError = (cause: Error): boolean => cause.message.toLowerCase().includes('railgunsmartwallet')

export const sanitizeError = (cause: Error): Error => {
if (!cause?.message) {
return new Error('Unknown error. Please try again.', { cause });
}

if (isRailgunError(cause)) {
const matchedRailgunError = findMatchingError(cause.message, RAILGUN_ERRORS);
if (matchedRailgunError) {
return new Error(matchedRailgunError.message, { cause });
}
return new Error('Uknown Railgun Smart Wallet Error.', { cause });
}

const matchedCustomError = findMatchingError(cause.message, CUSTOM_ERRORS);

if (matchedCustomError) {
return new Error(matchedCustomError.message, { cause });
}

// If no error is matched we return the original sanitized error
const errorMessage = sanitizeAscii(cause.message).replace(
`:${STRING_PREFIX_AFTER_UNICODE_REPLACEMENT}`,
': ',
);

return new Error(
errorMessage,
{ cause }
);
};
8 changes: 8 additions & 0 deletions src/utils/error/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type ErrorDefinition = {
matches: string[];
message: string;
};

export type CustomErrorMapping = {
[key: string]: ErrorDefinition;
};
Loading