Better ABI Validation: check type of constructor/call args against abi
Add to the general validation phase a check that each argument in the call/constructor args of the relevant futures matches the type expected by the abi.
TODO
- [ ] Draw up a test plan that will effectively act as a specification
- [ ] Spike type validation for single future (i.e. named contract deploy)
The types of futures that accept arguments and need validation are:
- contract deploys (not libraries)
- function calls
- static calls
For arguments, these are the types of inputs we need to be able to validate:
- raw primitives (number, bigint, string, boolean)
- contract futures (resolve to
addresstype) - static call futures (resolves to any solidity param type)
- read argument event futures (resolves to any solidity param type)
- runtime value
- account (resolves to
addresstype) - module parameter (resolves to number, bigint, string, boolean)
- account (resolves to
- a record of any of the above
- an array of any of the above
For raw primitives, we can let ethers do the heavy lifting for us with something like:
const iface = new ethers.Interface(artifact.abi);
try {
iface.encodeDeploy([future.constructorArgs])
} catch {
throw new Error('failed validation')
}
however, this approach would only work for primitive values, so I think primitives should instead just be accounted for within our custom loop that could look something like this (pseudocode):
function validateArgs(artifact: Artifact, functionName: string, args: ArgumentType[]): IgnitionError[] {
// loop through, probably recursively, and resolve to primitive type strings
const argTypes = args.map((arg) => resolveTypeForArg(artifact, arg));
// get an array of primitive type strings representing the valid type for each arg, derived from the ABI and likely with help from ethers
const validTypes = resolveValidTypesForFunctionArgs(artifact, functionName);
// compare each argument against the valid type
let errors = [];
for (let i = 0; i < argTypes.length; i++) {
if (argTypes[i] !== validTypes[i]) {
errors.push(new IgnitionError("invalid arg type"));
}
}
return errors;
}
@zoeyTM if I am following. During validation we add additional checks to args (either call or constructor) that recursively cross checks the types of the passed values with the ABI's type.
Does ethers v6 give us any util functions to help with this?
It seems viem has parseAbi, but I wouldn't want to switch to viem in just his case (and a full viem switch is likely out of scope).
Do you think cross checking the types is extensive custom code? Or is leveraging either ethers/viem the way to go?