Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for return value indexing in readEventArg and staticCall #462

Merged
merged 5 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 41 additions & 4 deletions packages/core/src/internal/execution/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export function getEventArgumentFromReceipt(
emitterAddress: string,
eventName: string,
eventIndex: number,
argument: string | number
nameOrIndex: string | number
): EvmValue {
const emitterLogs = receipt.logs.filter((l) => l.address === emitterAddress);

Expand All @@ -300,11 +300,11 @@ export function getEventArgumentFromReceipt(

const evmTuple = ethersResultIntoEvmTuple(ethersResult, eventFragment.inputs);

if (typeof argument === "string") {
return evmTuple.named[argument];
if (typeof nameOrIndex === "string") {
return evmTuple.named[nameOrIndex];
}

return evmTuple.positional[argument];
return evmTuple.positional[nameOrIndex];
}

/**
Expand Down 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 @@ -31,7 +31,6 @@ import {
StaticCallResponse,
SuccessfulTransaction,
} from "./types/execution-strategy";
import { convertEvmTupleToSolidityParam } from "./utils/convert-evm-tuple-to-solidity-param";

/**
* Returns true if the given response is an onchain interaction response.
Expand Down Expand Up @@ -205,12 +204,16 @@ export async function* executeStaticCallRequest(
* @returns The value that should be used as the result of the static call execution state.
*/
export function getStaticCallExecutionStateResultValue(
_exState: StaticCallExecutionState,
exState: StaticCallExecutionState,
lastStaticCallResult: SuccessfulEvmExecutionResult
): SolidityParameterType {
const values = convertEvmTupleToSolidityParam(lastStaticCallResult.values);
// TODO: We should have a way to handle other results.
return values[0];
return typeof exState.nameOrIndex === "string"
? (lastStaticCallResult.values.named[
exState.nameOrIndex
] as SolidityParameterType)
: (lastStaticCallResult.values.positional[
exState.nameOrIndex
] as SolidityParameterType);
}

export * from "./abi";
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export async function buildInitializeMessageFor(
deploymentParameters,
accounts
),
nameOrIndex: future.nameOrIndex,
functionName: future.functionName,
contractAddress: resolveAddressForContractFuture(
future.contract,
Expand Down Expand Up @@ -155,7 +156,7 @@ export async function buildInitializeMessageFor(
future.emitter,
future.eventName,
future.eventIndex,
future.argumentName,
future.nameOrIndex,
deploymentState,
deploymentLoader
);
Expand All @@ -168,7 +169,7 @@ export async function buildInitializeMessageFor(
{
artifactId: future.emitter.id,
eventName: future.eventName,
argumentName: future.argumentName,
nameOrIndex: future.nameOrIndex,
eventIndex: future.eventIndex,
txToReadFrom,
emitterAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export async function resolveReadEventArgumentResult(
emitter: ContractFuture<string>,
eventName: string,
eventIndex: number,
argumentName: string,
nameOrIndex: string | number,
deploymentState: DeploymentState,
deploymentLoader: DeploymentLoader
): Promise<{
Expand All @@ -211,7 +211,7 @@ export async function resolveReadEventArgumentResult(
emitterAddress,
eventName,
eventIndex,
argumentName
nameOrIndex
);

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export function initialiseStaticCallExecutionStateFrom(
contractAddress: action.contractAddress,
functionName: action.functionName,
args: action.args,
nameOrIndex: action.nameOrIndex,
from: action.from,
networkInteractions: [],
};
Expand Down Expand Up @@ -94,7 +95,7 @@ export function initialiseReadEventArgumentExecutionStateFrom(
dependencies: new Set<string>(action.dependencies),
artifactId: action.artifactId,
eventName: action.eventName,
argumentName: action.argumentName,
nameOrIndex: action.nameOrIndex,
txToReadFrom: action.txToReadFrom,
emitterAddress: action.emitterAddress,
eventIndex: action.eventIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export interface StaticCallExecutionState
contractAddress: string;
functionName: string;
args: SolidityParameterType[];
nameOrIndex: string | number;
from: string;
networkInteractions: StaticCall[];
result?: StaticCallExecutionResult;
Expand Down Expand Up @@ -170,7 +171,7 @@ export interface ReadEventArgumentExecutionState
> {
artifactId: string;
eventName: string;
argumentName: string;
nameOrIndex: string | number;
txToReadFrom: string;
emitterAddress: string;
eventIndex: number;
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/internal/execution/types/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export interface StaticCallExecutionStateInitializeMessage {
artifactId: string;
contractAddress: string;
functionName: string;
nameOrIndex: string | number;
args: SolidityParameterType[];
from: string;
}
Expand Down Expand Up @@ -165,7 +166,7 @@ export interface ReadEventArgExecutionStateInitializeMessage {
dependencies: string[];
artifactId: string;
eventName: string;
argumentName: string;
nameOrIndex: string | number;
txToReadFrom: string;
emitterAddress: string;
eventIndex: number;
Expand Down
19 changes: 16 additions & 3 deletions packages/core/src/internal/module-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ class IgnitionModuleBuilderImplementation<
contractFuture: CallableContractFuture<ContractNameT>,
functionName: FunctionNameT,
args: ArgumentType[] = [],
nameOrIndex: string | number = 0,
options: StaticCallOptions = {}
): NamedStaticCallFuture<ContractNameT, FunctionNameT> {
const id = options.id ?? functionName;
Expand All @@ -388,6 +389,7 @@ class IgnitionModuleBuilderImplementation<
this._assertUniqueStaticCallId(futureId);
this._assertValidFrom(options.from, this.staticCall);
this._assertValidCallableContract(contractFuture, this.staticCall);
this._assertValidNameOrIndex(nameOrIndex, this.staticCall);
/* validation end */

const future = new NamedStaticCallFutureImplementation(
Expand All @@ -396,6 +398,7 @@ class IgnitionModuleBuilderImplementation<
functionName,
contractFuture,
args,
nameOrIndex,
options.from
);

Expand Down Expand Up @@ -496,7 +499,7 @@ class IgnitionModuleBuilderImplementation<
| SendDataFuture
| NamedContractCallFuture<string, string>,
eventName: string,
argumentName: string,
nameOrIndex: string | number,
options: ReadEventArgumentOptions = {}
): ReadEventArgumentFuture {
const eventIndex = options.eventIndex ?? 0;
Expand All @@ -521,20 +524,21 @@ class IgnitionModuleBuilderImplementation<

const id =
options.id ??
`${emitter.contractName}#${eventName}#${argumentName}#${eventIndex}`;
`${emitter.contractName}#${eventName}#${nameOrIndex}#${eventIndex}`;

const futureId = `${this._module.id}:${id}`;

/* validation start */
this._assertUniqueReadEventArgumentId(futureId);
this._assertValidNameOrIndex(nameOrIndex, this.readEventArgument);
/* validation end */

const future = new ReadEventArgumentFutureImplementation(
futureId,
this._module,
futureToReadFrom,
eventName,
argumentName,
nameOrIndex,
emitter,
eventIndex
);
Expand Down Expand Up @@ -778,6 +782,15 @@ class IgnitionModuleBuilderImplementation<
}
}

private _assertValidNameOrIndex(
nameOrIndex: string | number,
func: (...[]: any[]) => any
) {
if (typeof nameOrIndex !== "string" && typeof nameOrIndex !== "number") {
this._throwErrorWithStackTrace(`Invalid nameOrIndex given`, func);
}
}

private _assertValidAddress(
address:
| string
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/internal/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export class NamedStaticCallFutureImplementation<
public readonly functionName: FunctionNameT,
public readonly contract: ContractFuture<ContractNameT>,
public readonly args: ArgumentType[],
public readonly nameOrIndex: string | number,
public readonly from: string | AccountRuntimeValue | undefined
) {
super(id, FutureType.NAMED_STATIC_CALL, module);
Expand Down Expand Up @@ -211,7 +212,7 @@ export class ReadEventArgumentFutureImplementation
| SendDataFuture
| NamedContractCallFuture<string, string>,
public readonly eventName: string,
public readonly argumentName: string,
public readonly nameOrIndex: string | number,
public readonly emitter: ContractFuture<string>,
public readonly eventIndex: number
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NamedStaticCallFuture } from "../../../types/module";
import { StaticCallExecutionState } from "../../execution/types/execution-state";
import { compare } from "../helpers/compare";
import { reconcileArguments } from "../helpers/reconcile-arguments";
import { reconcileContract } from "../helpers/reconcile-contract";
import { reconcileFrom } from "../helpers/reconcile-from";
Expand Down Expand Up @@ -31,5 +32,15 @@ export function reconcileNamedStaticCall(
return result;
}

result = compare(
future,
"Argument name or index",
executionState.nameOrIndex,
future.nameOrIndex
);
if (result !== undefined) {
return result;
}

return { success: true };
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ export function reconcileReadEventArgument(

result = compare(
future,
"Argument name",
executionState.argumentName,
future.argumentName
"Argument name or index",
executionState.nameOrIndex,
future.nameOrIndex
);
if (result !== undefined) {
return result;
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
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ export async function validateReadEventArgument(
validateArtifactEventArgumentParams(
artifact,
future.eventName,
future.argumentName
future.nameOrIndex
);
}
6 changes: 4 additions & 2 deletions packages/core/src/stored-deployment-serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export class StoredDeploymentSerializer {
contract: this._convertFutureToFutureToken(future.contract),
functionName: future.functionName,
args: future.args.map((arg) => context.argReplacer(arg)),
nameOrIndex: future.nameOrIndex,
from: isRuntimeValue(future.from)
? this._serializeAccountRuntimeValue(future.from)
: future.from,
Expand Down Expand Up @@ -298,7 +299,7 @@ export class StoredDeploymentSerializer {
),
emitter: this._convertFutureToFutureToken(future.emitter),
eventName: future.eventName,
argumentName: future.argumentName,
nameOrIndex: future.nameOrIndex,
eventIndex: future.eventIndex,
};
return serializedReadEventArgumentFuture;
Expand Down Expand Up @@ -742,6 +743,7 @@ export class StoredDeploymentDeserializer {
serializedFuture.args.map((arg) =>
this._deserializeArgument(arg, futuresLookup)
),
serializedFuture.nameOrIndex,
this._isSerializedAccountRuntimeValue(serializedFuture.from)
? this._deserializeAccountRuntimeValue(serializedFuture.from)
: serializedFuture.from
Expand Down Expand Up @@ -795,7 +797,7 @@ export class StoredDeploymentDeserializer {
| ArtifactContractDeploymentFuture
| NamedContractCallFuture<string, string>,
serializedFuture.eventName,
serializedFuture.argumentName,
serializedFuture.nameOrIndex,
this._lookup(
contractFuturesLookup,
serializedFuture.emitter.futureId
Expand Down
Loading
Loading