Skip to content

Commit

Permalink
add validation rules for static call and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
zoeyTM committed Sep 12, 2023
1 parent 34fee02 commit 32ff72a
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 1 deletion.
37 changes: 37 additions & 0 deletions packages/core/src/internal/execution/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,43 @@ function getEventArgumentParamType(
return paramType;
}

/**
* Validates the param type of a static call return value, throwing a validation error if it's not found.
*/
export function validateFunctionArgumentParamType(
contractName: string,
functionName: string,
artifact: Artifact,
argument: string | number
): void {
const { ethers } = require("ethers") as typeof import("ethers");
const iface = new ethers.Interface(artifact.abi);
const functionFragment = getFunctionFragment(iface, functionName);

if (typeof argument === "string") {
let hasArg = false;
for (const output of functionFragment.outputs) {
if (output.name === argument) {
hasArg = true;
}
}

if (!hasArg) {
throw new IgnitionValidationError(
`Function ${functionName} of contract ${contractName} has no return value named ${argument}`
);
}
} 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`
);
}
}
}

/**
* Returns true if the given param type has a dynamic size.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { IgnitionValidationError } from "../../../errors";
import { isArtifactType } from "../../../type-guards";
import { ArtifactResolver } from "../../../types/artifact";
import { NamedStaticCallFuture } from "../../../types/module";
import { validateArtifactFunction } from "../../execution/abi";
import {
validateArtifactFunction,
validateFunctionArgumentParamType,
} from "../../execution/abi";

export async function validateNamedStaticCall(
future: NamedStaticCallFuture<string, string>,
Expand All @@ -26,4 +29,11 @@ export async function validateNamedStaticCall(
future.args,
true
);

validateFunctionArgumentParamType(
future.contract.contractName,
future.functionName,
artifact,
future.nameOrIndex
);
}
78 changes: 78 additions & 0 deletions packages/core/test/staticCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,84 @@ describe("static call", () => {
/Function inc in contract Another is not 'pure' or 'view' and cannot be statically called/
);
});

it("should not validate a nameOrIndex that is invalid (nonexistent name)", async () => {
const fakeArtifact: Artifact = {
abi: [
{
inputs: [],
name: "inc",
outputs: [
{
internalType: "bool",
name: "b",
type: "bool",
},
],
stateMutability: "pure",
type: "function",
},
],
contractName: "",
bytecode: "",
linkReferences: {},
};

const module = buildModule("Module1", (m) => {
const another = m.contractFromArtifact("Another", fakeArtifact, []);
m.staticCall(another, "inc", [], "a");

return { another };
});

const future = getFuturesFromModule(module).find(
(v) => v.type === FutureType.NAMED_STATIC_CALL
);

await assert.isRejected(
validateNamedStaticCall(future as any, setupMockArtifactResolver()),
/Function inc of contract Another has no return value named a/
);
});

it("should not validate a nameOrIndex that is invalid (out of range)", async () => {
const fakeArtifact: Artifact = {
abi: [
{
inputs: [],
name: "inc",
outputs: [
{
internalType: "bool",
name: "b",
type: "bool",
},
],
stateMutability: "pure",
type: "function",
},
],
contractName: "",
bytecode: "",
linkReferences: {},
};

const module = buildModule("Module1", (m) => {
const another = m.contractFromArtifact("Another", fakeArtifact, []);
m.staticCall(another, "inc", [], 2);

return { another };
});

const future = getFuturesFromModule(module).find(
(v) => v.type === FutureType.NAMED_STATIC_CALL
);

await assert.isRejected(
validateNamedStaticCall(future as any, setupMockArtifactResolver()),
/Function inc of contract Another has only 1 return values, but value 2 was requested/
);
});
});

describe("stage two", () => {
Expand Down

0 comments on commit 32ff72a

Please sign in to comment.