customError.js 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. const { config } = require('hardhat');
  2. const { expect } = require('chai');
  3. const optimizationsEnabled = config.solidity.compilers.some(c => c.settings.optimizer.enabled);
  4. /** Revert handler that supports custom errors. */
  5. async function expectRevertCustomError(promise, expectedErrorName, args) {
  6. try {
  7. await promise;
  8. expect.fail("Expected promise to throw but it didn't");
  9. } catch (revert) {
  10. if (!Array.isArray(args)) {
  11. expect.fail('Expected 3rd array parameter for error arguments');
  12. }
  13. if (expectedErrorName) {
  14. if (optimizationsEnabled) {
  15. // Optimizations currently mess with Hardhat's decoding of custom errors
  16. expect(revert.message).to.include.oneOf([expectedErrorName, 'unrecognized return data or custom error']);
  17. } else {
  18. const [, error] = revert.message.match(/'(.*)'/);
  19. if (!/([A-Z])\w+\(.*\)/g.test(error)) {
  20. expect.fail(`Couldn't parse "${error}" as a custom error`);
  21. }
  22. const [, errorName] = error.match(/(\w+)\(.*\)/);
  23. const argMatches = [...error.replace(errorName, '').matchAll(/(0x[0-9A-Fa-f]+|-?\d+|\w+)/g)];
  24. expect(errorName).to.be.equal(
  25. expectedErrorName,
  26. `Unexpected custom error name (with found args: [${argMatches.map(([a]) => a)}])`,
  27. );
  28. // Coerce to string for comparison since `arg` can be either a number or hex.
  29. const sanitizedExpected = args.map(arg => arg.toString().toLowerCase());
  30. const sanitizedActual = argMatches.map(([arg]) => arg.toString().toLowerCase());
  31. expect(sanitizedActual).to.have.members(sanitizedExpected, `Unexpected ${errorName} arguments`);
  32. }
  33. }
  34. }
  35. }
  36. module.exports = {
  37. expectRevertCustomError,
  38. };