Skip to content

Commit

Permalink
update all IgnitionValidationErrors to use IgnitionError instead
Browse files Browse the repository at this point in the history
  • Loading branch information
zoeyTM committed Sep 20, 2023
1 parent 3a97486 commit afc1b70
Show file tree
Hide file tree
Showing 23 changed files with 389 additions and 207 deletions.
9 changes: 5 additions & 4 deletions packages/core/src/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IgnitionValidationError } from "./errors";
import { IgnitionError } from "./errors";
import { ERRORS } from "./errors-list";
import {
DEFAULT_AUTOMINE_REQUIRED_CONFIRMATIONS,
defaultConfig,
Expand Down Expand Up @@ -83,9 +84,9 @@ export async function deploy<

if (defaultSender !== undefined) {
if (!accounts.includes(defaultSender)) {
throw new IgnitionValidationError(
`Default sender ${defaultSender} is not part of the provided accounts`
);
throw new IgnitionError(ERRORS.VALIDATION.INVALID_DEFAULT_SENDER, {
defaultSender,
});
}
} else {
defaultSender = getDefaultSender(accounts);
Expand Down
137 changes: 137 additions & 0 deletions packages/core/src/errors-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ export const ERROR_RANGES: {
max: 699,
title: "Wipe errors",
},
VALIDATION: {
min: 700,
max: 799,
title: "Validation errors",
},
};

export const ERRORS = {
Expand All @@ -60,6 +65,10 @@ export const ERRORS = {
number: 1,
message: "Internal Ignition invariant was violated: %description%",
},
UNSUPPORTED_DECODE: {
number: 2,
message: "Ignition can't decode ethers.js value of type %type%: %value%",
},
},
INTERNAL: {
TEMPLATE_INVALID_VARIABLE_NAME: {
Expand Down Expand Up @@ -162,6 +171,134 @@ export const ERRORS = {
message: `Cannot wipe %futureId% as there are dependent futures that have already started: %dependents%`,
},
},
VALIDATION: {
INVALID_DEFAULT_SENDER: {
number: 700,
message:
"Default sender %defaultSender% is not part of the provided accounts",
},
MISSING_EMITTER: {
number: 701,
message:
"`options.emitter` must be provided when reading an event from a SendDataFuture",
},
INVALID_MODULE: {
number: 702,
message: "Module validation failed with reason: %message%",
},
INVALID_CONSTRUCTOR_ARGS_LENGTH: {
number: 703,
message:
"The constructor of the contract '%contractName%' expects %expectedArgsLength% arguments but %argsLength% were given",
},
INVALID_FUNCTION_ARGS_LENGTH: {
number: 704,
message:
"Function %functionName% in contract %contractName% expects %expectedLength% arguments but %argsLength% were given",
},
INVALID_STATIC_CALL: {
number: 705,
message:
"Function %functionName% in contract %contractName% is not 'pure' or 'view' and cannot be statically called",
},
INDEXED_EVENT_ARG: {
number: 706,
message:
"Indexed argument %argument% of event %eventName% of contract %contractName% is not stored in the receipt, but its hash is, so you can't read it.",
},
INVALID_OVERLOAD_NAME: {
number: 707,
message: "Invalid %eventOrFunction% name '%name%'",
},
OVERLOAD_NOT_FOUND: {
number: 708,
message:
"%eventOrFunction% '%name%' not found in contract %contractName%",
},
REQUIRE_BARE_NAME: {
number: 709,
message:
"%eventOrFunction% name '%name%' used for contract %contractName%, but it's not overloaded. Use '%bareName%' instead.",
},
OVERLOAD_NAME_REQUIRED: {
number: 710,
message:
"%eventOrFunction% '%name%' is overloaded in contract %contractName%. Please use one of these names instead: %normalizedNameList%",
},
INVALID_OVERLOAD_GIVEN: {
number: 711,
message:
"%eventOrFunction% '%name%' is not a valid overload of '%bareName%' in contract %contractName%. Please use one of these names instead: %normalizedNameList%",
},
EVENT_ARG_NOT_FOUND: {
number: 712,
message:
"Event %eventName% of contract %contractName% has no argument named %argument%",
},
INVALID_EVENT_ARG_INDEX: {
number: 713,
message:
"Event %eventName% of contract %contractName% has only %expectedLength% arguments, but argument %argument% was requested",
},
FUNCTION_ARG_NOT_FOUND: {
number: 714,
message:
"Function %functionName% of contract %contractName% has no return value named %argument%",
},
INVALID_FUNCTION_ARG_INDEX: {
number: 715,
message:
"Function %functionName% of contract %contractName% has only %expectedLength% return values, but value %argument% was requested",
},
MISSING_LIBRARIES: {
number: 716,
message:
"Invalid libraries for contract %contractName%: The following libraries are missing: %fullyQualifiedNames%",
},
CONFLICTING_LIBRARY_NAMES: {
number: 717,
message:
"Invalid libraries for contract %contractName%: The names '%inputName%' and '%libName%' clash with each other, please use qualified names for both.",
},
INVALID_LIBRARY_NAME: {
number: 718,
message: "Invalid library name %libraryName% for contract %contractName%",
},
LIBRARY_NOT_NEEDED: {
number: 719,
message:
"Invalid library %libraryName% for contract %contractName%: this library is not needed by this contract.",
},
AMBIGUOUS_LIBRARY_NAME: {
number: 720,
message: `Invalid libraries for contract %contractName%: The name "%libraryName%" is ambiguous, please use one of the following fully qualified names: %fullyQualifiedNames%`,
},
INVALID_LIBRARY_ADDRESS: {
number: 721,
message: `Invalid address %address% for library %libraryName% of contract %contractName%`,
},
NEGATIVE_ACCOUNT_INDEX: {
number: 722,
message: "Account index cannot be a negative number",
},
ACCOUNT_INDEX_TOO_HIGH: {
number: 723,
message:
"Requested account index '%accountIndex%' is greater than the total number of available accounts '%accountsLength%'",
},
INVALID_ARTIFACT: {
number: 724,
message: "Artifact for contract '%contractName%' is invalid",
},
MISSING_MODULE_PARAMETER: {
number: 725,
message: "Module parameter '%name%' requires a value but was given none",
},
INVALID_MODULE_PARAMETER_TYPE: {
number: 726,
message: `Module parameter '%name%' must be of type '%expectedType%' but is '%actualType%'`,
},
},
};

/**
Expand Down
128 changes: 76 additions & 52 deletions packages/core/src/internal/execution/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ import type {
Result,
} from "ethers";

import {
IgnitionValidationError,
UnsupportedOperationError,
} from "../../errors";
import { IgnitionError } from "../../errors";
import { ERRORS } from "../../errors-list";
import { Artifact } from "../../types/artifact";
import { ArgumentType, SolidityParameterType } from "../../types/module";
import { assertIgnitionInvariant } from "../utils/assertions";
Expand Down Expand Up @@ -161,9 +159,11 @@ export function validateContractConstructorArgsLength(
const expectedArgsLength = iface.deploy.inputs.length;

if (argsLength !== expectedArgsLength) {
throw new IgnitionValidationError(
`The constructor of the contract '${contractName}' expects ${expectedArgsLength} arguments but ${argsLength} were given`
);
throw new IgnitionError(ERRORS.VALIDATION.INVALID_CONSTRUCTOR_ARGS_LENGTH, {
contractName,
argsLength,
expectedArgsLength,
});
}
}

Expand Down Expand Up @@ -194,16 +194,20 @@ export function validateArtifactFunction(

// Check that the number of arguments is correct
if (fragment.inputs.length !== args.length) {
throw new IgnitionValidationError(
`Function ${functionName} in contract ${contractName} expects ${fragment.inputs.length} arguments but ${args.length} were given`
);
throw new IgnitionError(ERRORS.VALIDATION.INVALID_FUNCTION_ARGS_LENGTH, {
functionName,
contractName,
argsLength: args.length,
expectedLength: fragment.inputs.length,
});
}

// Check that the function is pure or view, which is required for a static call
if (isStaticCall && !fragment.constant) {
throw new IgnitionValidationError(
`Function ${functionName} in contract ${contractName} is not 'pure' or 'view' and cannot be statically called`
);
throw new IgnitionError(ERRORS.VALIDATION.INVALID_STATIC_CALL, {
functionName,
contractName,
});
}
}

Expand Down Expand Up @@ -253,9 +257,11 @@ export function validateArtifactEventArgumentParams(
// as their hash is stored in a topic, and its actual value isn't stored
// anywhere
if (hasDynamicSize(paramType)) {
throw new IgnitionValidationError(
`Indexed argument ${argument} of event ${eventName} of contract ${emitterArtifact.contractName} is not stored in the receipt, but its hash is, so you can't read it.`
);
throw new IgnitionError(ERRORS.VALIDATION.INDEXED_EVENT_ARG, {
eventName,
argument,
contractName: emitterArtifact.contractName,
});
}
}
}
Expand Down Expand Up @@ -473,11 +479,10 @@ function ethersValueIntoEvmValue(
return ethersResultIntoEvmTuple(ethersValue, paramType.components);
}

throw new UnsupportedOperationError(
`Ignition can't decode ethers.js value of type ${
paramType.type
}: ${JSON.stringify(ethersValue, undefined, 2)}`
);
throw new IgnitionError(ERRORS.GENERAL.UNSUPPORTED_DECODE, {
type: paramType.type,
value: JSON.stringify(ethersValue, undefined, 2),
});
}

function ethersResultIntoEvmValueArray(
Expand Down Expand Up @@ -599,9 +604,10 @@ function validateOverloadedName(
const bareName = getBareName(name);

if (bareName === undefined) {
throw new IgnitionValidationError(
`Invalid ${eventOrFunction} name "${name}"`
);
throw new IgnitionError(ERRORS.VALIDATION.INVALID_OVERLOAD_NAME, {
eventOrFunction,
name,
});
}

const { ethers } = require("ethers") as typeof import("ethers");
Expand All @@ -618,19 +624,24 @@ function validateOverloadedName(
.filter((fragment) => fragment.name === bareName);

if (fragments.length === 0) {
throw new IgnitionValidationError(
`${eventOrFunctionCapitalized} "${name}" not found in contract ${artifact.contractName}`
);
throw new IgnitionError(ERRORS.VALIDATION.OVERLOAD_NOT_FOUND, {
name,
eventOrFunction: eventOrFunctionCapitalized,
contractName: artifact.contractName,
});
}

// If it is not overloaded we force the user to use the bare name
// because having a single representation is more friendly with our reconciliation
// process.
if (fragments.length === 1) {
if (bareName !== name) {
throw new IgnitionValidationError(
`${eventOrFunctionCapitalized} name "${name}" used for contract ${artifact.contractName}, but it's not overloaded. Use "${bareName}" instead.`
);
throw new IgnitionError(ERRORS.VALIDATION.REQUIRE_BARE_NAME, {
name,
bareName,
eventOrFunction: eventOrFunctionCapitalized,
contractName: artifact.contractName,
});
}

return;
Expand All @@ -647,19 +658,22 @@ function validateOverloadedName(
const normalizedNameList = normalizedNames.map((nn) => `* ${nn}`).join("\n");

if (bareName === name) {
throw new IgnitionValidationError(
`${eventOrFunctionCapitalized} "${name}" is overloaded in contract ${artifact.contractName}. Please use one of these names instead:
${normalizedNameList}`
);
throw new IgnitionError(ERRORS.VALIDATION.OVERLOAD_NAME_REQUIRED, {
name,
normalizedNameList,
eventOrFunction: eventOrFunctionCapitalized,
contractName: artifact.contractName,
});
}

if (!normalizedNames.includes(name)) {
throw new IgnitionValidationError(
`${eventOrFunctionCapitalized} "${name}" is not a valid overload of "${bareName}" in contract ${artifact.contractName}. Please use one of these names instead:
${normalizedNameList}`
);
throw new IgnitionError(ERRORS.VALIDATION.INVALID_OVERLOAD_GIVEN, {
name,
bareName,
normalizedNameList,
eventOrFunction: eventOrFunctionCapitalized,
contractName: artifact.contractName,
});
}
}

Expand All @@ -681,17 +695,22 @@ function getEventArgumentParamType(
}
}

throw new IgnitionValidationError(
`Event ${eventName} of contract ${contractName} has no argument named ${argument}`
);
throw new IgnitionError(ERRORS.VALIDATION.EVENT_ARG_NOT_FOUND, {
eventName,
argument,
contractName,
});
}

const paramType = eventFragment.inputs[argument];

if (paramType === undefined) {
throw new IgnitionValidationError(
`Event ${eventName} of contract ${contractName} has only ${eventFragment.inputs.length} arguments, but argument ${argument} was requested`
);
throw new IgnitionError(ERRORS.VALIDATION.INVALID_EVENT_ARG_INDEX, {
eventName,
argument,
contractName,
expectedLength: eventFragment.inputs.length,
});
}

return paramType;
Expand Down Expand Up @@ -719,17 +738,22 @@ export function validateFunctionArgumentParamType(
}

if (!hasArg) {
throw new IgnitionValidationError(
`Function ${functionName} of contract ${contractName} has no return value named ${argument}`
);
throw new IgnitionError(ERRORS.VALIDATION.FUNCTION_ARG_NOT_FOUND, {
functionName,
argument,
contractName,
});
}
} else {
const paramType = functionFragment.outputs[argument];

if (paramType === undefined) {
throw new IgnitionValidationError(
`Function ${functionName} of contract ${contractName} has only ${functionFragment.outputs.length} return values, but value ${argument} was requested`
);
throw new IgnitionError(ERRORS.VALIDATION.INVALID_FUNCTION_ARG_INDEX, {
functionName,
argument,
contractName,
expectedLength: functionFragment.outputs.length,
});
}
}
}
Expand Down
Loading

0 comments on commit afc1b70

Please sign in to comment.