Skip to content

Commit

Permalink
reconcile addresses with mismatched casings
Browse files Browse the repository at this point in the history
  • Loading branch information
zoeyTM committed May 14, 2024
1 parent 96f00ae commit a90481b
Show file tree
Hide file tree
Showing 9 changed files with 1,723 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,6 @@ dist

# Webstorm files
.idea/

# npm lockfile since we use pnpm now
package-lock.json
9 changes: 6 additions & 3 deletions packages/core/src/internal/execution/utils/address.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { isAddress as ethersIsAddress, getAddress } from "ethers";

import { assertIgnitionInvariant } from "../../utils/assertions";

/**
* Is the string a valid ethereum address?
*/
export function isAddress(address: string): boolean {
export function isAddress(address: any): address is string {
const { isAddress: ethersIsAddress } =
require("ethers") as typeof import("ethers");

return ethersIsAddress(address);
}

Expand All @@ -21,6 +22,8 @@ export function toChecksumFormat(address: string) {
`Expected ${address} to be an address`
);

const { getAddress } = require("ethers") as typeof import("ethers");

return getAddress(address);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ExecutionSateType,
StaticCallExecutionState,
} from "../../execution/types/execution-state";
import { isAddress, equalAddresses } from "../../execution/utils/address";
import {
ReconciliationContext,
ReconciliationFutureResultFailure,
Expand Down Expand Up @@ -57,7 +58,13 @@ export function reconcileArguments(
for (const [i, futureArg] of futureArgs.entries()) {
const exStateArg = exStateArgs[i];

if (!isEqual(futureArg, exStateArg)) {
// if both args are addresses, we need to compare the checksummed versions
// to ensure case discrepancies are ignored
if (isAddress(futureArg) && isAddress(exStateArg)) {
if (!equalAddresses(futureArg, exStateArg)) {
return fail(future, `Argument at index ${i} has been changed`);
}
} else if (!isEqual(futureArg, exStateArg)) {
return fail(future, `Argument at index ${i} has been changed`);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,55 @@ describe("Reconciliation - artifact contract", () => {
]);
});

it("should reconcile an address arg with entirely different casing", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1", mockArtifact, [
"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65",
]);

return { contract1 };
});

await assertSuccessReconciliation(
moduleDefinition,
createDeploymentState({
...exampleDeploymentState,
id: "Module#Contract1",
futureType: FutureType.CONTRACT_DEPLOYMENT,
status: ExecutionStatus.STARTED,
constructorArgs: ["0x15D34AAF54267DB7D7C367839AAF71A00A2C6A65"],
})
);
});

it("should fail to reconcile an address arg with partially different casing", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1", mockArtifact, [
"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65",
]);

return { contract1 };
});

const reconciliationResult = await reconcile(
moduleDefinition,
createDeploymentState({
...exampleDeploymentState,
id: "Module#Contract1",
futureType: FutureType.CONTRACT_DEPLOYMENT,
status: ExecutionStatus.STARTED,
constructorArgs: ["0x15d34aaf54267db7D7c367839aaf71a00a2c6a65"],
})
);

assert.deepStrictEqual(reconciliationResult.reconciliationFailures, [
{
futureId: "Module#Contract1",
failure: "Argument at index 0 has been changed",
},
]);
});

it("should find changes to libraries unreconciliable", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const safeMath = m.library("SafeMath");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,89 @@ describe("Reconciliation - named contract call", () => {
]);
});

it("should reconcile an address arg with entirely different casing", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");

m.call(
contract1,
"function1",
["0x15d34aaf54267db7d7c367839aaf71a00a2c6a65"],
{}
);

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.STARTED,
functionName: "function1",
args: ["0x15D34AAF54267DB7D7C367839AAF71A00A2C6A65"],
}
)
);
});

it("should fail to reconcile an address arg with partially different casing", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");

m.call(
contract1,
"function1",
["0x15d34aaf54267db7d7c367839aaf71a00a2c6a65"],
{}
);

return { contract1 };
});

const reconiliationResult = await reconcile(
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.STARTED,
functionName: "function1",
args: ["0x15d34aaf54267db7D7c367839aaf71a00a2c6a65"],
}
)
);

assert.deepStrictEqual(reconiliationResult.reconciliationFailures, [
{
futureId: "Module#Contract1.function1",
failure: "Argument at index 0 has been changed",
},
]);
});

it("should find changes to value 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 @@ -190,6 +190,53 @@ describe("Reconciliation - named contract", () => {
]);
});

it("should reconcile an address arg with entirely different casing", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1", [
"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65",
]);

return { contract1 };
});

await assertSuccessReconciliation(
moduleDefinition,
createDeploymentState({
...exampleDeploymentState,
id: "Module#Contract1",
status: ExecutionStatus.STARTED,
constructorArgs: ["0x15D34AAF54267DB7D7C367839AAF71A00A2C6A65"],
})
);
});

it("should fail to reconcile an address arg with partially different casing", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1", [
"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65",
]);

return { contract1 };
});

const reconciliationResult = await reconcile(
moduleDefinition,
createDeploymentState({
...exampleDeploymentState,
id: "Module#Contract1",
status: ExecutionStatus.STARTED,
constructorArgs: ["0x15d34aaf54267db7D7c367839aaf71a00a2c6a65"],
})
);

assert.deepStrictEqual(reconciliationResult.reconciliationFailures, [
{
futureId: "Module#Contract1",
failure: "Argument at index 0 has been changed",
},
]);
});

it("should find changes to libraries unreconciliable", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const safeMath = m.library("SafeMath");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,83 @@ describe("Reconciliation - named static call", () => {
]);
});

it("should reconcile an address arg with entirely different casing", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");

m.staticCall(contract1, "function1", [
"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65",
]);

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.STARTED,
functionName: "function1",
args: ["0x15D34AAF54267DB7D7C367839AAF71A00A2C6A65"],
}
)
);
});

it("should fail to reconcile an address arg with partially different casing", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");

m.staticCall(contract1, "function1", [
"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65",
]);

return { contract1 };
});

const reconiliationResult = await reconcile(
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.STARTED,
functionName: "function1",
args: ["0x15d34aaf54267db7D7c367839aaf71a00a2c6a65"],
}
)
);

assert.deepStrictEqual(reconiliationResult.reconciliationFailures, [
{
futureId: "Module#Contract1.function1",
failure: "Argument at index 0 has been changed",
},
]);
});

it("should find changes to from unreconciliable", async () => {
const moduleDefinition = buildModule("Module", (m) => {
const contract1 = m.contract("Contract1");
Expand Down
Loading

0 comments on commit a90481b

Please sign in to comment.