Skip to content

Commit

Permalink
fix: allow undefined froms to pass reconciliation
Browse files Browse the repository at this point in the history
If a `from` is undefined on the first run, allow it to reconcile on the
second run using the account that was selected during initialization of
the future on the first run. This is only valid if the first run from
account (the default sender on the first run) is still in the list of
accounts.

Fixes #413.
  • Loading branch information
kanej committed Sep 20, 2023
1 parent a02a881 commit 54bb8ef
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
ContractCallFuture,
ContractDeploymentFuture,
LibraryDeploymentFuture,
ContractCallFuture,
NamedArtifactContractDeploymentFuture,
NamedArtifactLibraryDeploymentFuture,
StaticCallFuture,
SendDataFuture,
StaticCallFuture,
} from "../../../types/module";
import { resolveFutureFrom } from "../../execution/future-processor/helpers/future-resolvers";
import {
Expand Down Expand Up @@ -37,6 +37,10 @@ export function reconcileFrom(
| StaticCallExecutionState,
context: ReconciliationContext
): ReconciliationFutureResultFailure | undefined {
if (future.from === undefined && context.accounts.includes(exState.from)) {
return undefined;
}

const resolvedFrom = resolveFutureFrom(
future.from,
context.accounts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,40 @@ describe("Reconciliation - named contract call", () => {
);
});

it("should reconcile when the from is undefined but the exState's from is in the accounts list", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");

m.call(contract1, "function1", [1, "a", contract1], { from: undefined });

return { contract1 };
});

await assertSuccessReconciliation(
moduleDefinition,
createDeploymentState(
{
...exampleDeploymentState,
id: "Module#Contract1",
status: ExecutionStatus.SUCCESS,
result: {
type: ExecutionResultType.SUCCESS,
address: differentAddress,
},
},
{
...exampleContractCallState,
id: "Module#Contract1.function1",
futureType: FutureType.CONTRACT_CALL,
status: ExecutionStatus.SUCCESS,
functionName: "function1",
args: [1, "a", differentAddress],
from: exampleAccounts[2],
}
)
);
});

it("should find changes to contract unreconciliable", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,34 @@ describe("Reconciliation - named contract", () => {
);
});

/**
* This test here is in a first run, the from is undefined and the defaultSender is used.
* On the second run the from is undefined but a different defaultSender is now in play.
* We say this should reconcile but the account from the first run should be used, as long
* as it is in the accounts list
*/
it("should reconcile where the future is undefined but the exState's from is in the accounts list", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract", [], {
id: "Example",
from: undefined,
});

return { contract1 };
});

await assertSuccessReconciliation(
moduleDefinition,
createDeploymentState({
...exampleDeploymentState,
id: "Module#Example",
status: ExecutionStatus.STARTED,
contractName: "Contract",
from: exampleAccounts[3],
})
);
});

it("should find changes to contract name unreconciliable", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("ContractChanged", [], {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,43 @@ describe("Reconciliation - named static call", () => {
);
});

it("should reconcile when the from is undefined but the exState's from is in the accounts list", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");

m.staticCall(contract1, "function1", [1, "a"], 0, {
from: undefined,
});

return { contract1 };
});

await assertSuccessReconciliation(
moduleDefinition,
createDeploymentState(
{
...exampleDeploymentState,
id: "Module#Contract1",
status: ExecutionStatus.SUCCESS,
result: {
type: ExecutionResultType.SUCCESS,
address: exampleAddress,
},
},
{
...exampleStaticCallState,
id: "Module#Contract1.function1",
futureType: FutureType.STATIC_CALL,
status: ExecutionStatus.SUCCESS,
contractAddress: exampleAddress,
functionName: "function1",
args: [1, "a"],
from: exampleAccounts[4],
}
)
);
});

it("should find changes to contract unreconciliable", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");
Expand Down
25 changes: 25 additions & 0 deletions packages/core/test/reconciliation/futures/reconcileSendData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,31 @@ describe("Reconciliation - send data", () => {
);
});

/**
* This test here is in a first run, the from is undefined and the defaultSender is used.
* On the second run the from is undefined but a different defaultSender is now in play.
* We say this should reconcile but the account from the first run should be used, as long
* as it is in the accounts list.
*/
it("should reconcile where the future is undefined but the exState's from is in the accounts list", async () => {
const moduleDefinition = buildModule("Module", (m) => {
m.send("test_send", exampleAddress, 0n, "example_data", {
from: undefined,
});

return {};
});

await assertSuccessReconciliation(
moduleDefinition,
createDeploymentState({
...exampleSendState,
id: "Module#test_send",
status: ExecutionStatus.STARTED,
})
);
});

it("should reconcile between undefined and 0x for data", async () => {
const moduleDefinition = buildModule("Module", (m) => {
m.send("test_send", exampleAddress, 0n, undefined);
Expand Down

0 comments on commit 54bb8ef

Please sign in to comment.