Skip to content

Commit

Permalink
Unit test with namespaced layout for conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
ericglau committed Sep 11, 2023
1 parent 01c4c8e commit ba0314f
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 14 deletions.
40 changes: 40 additions & 0 deletions packages/core/contracts/test/NamespacedConflictsWithFunctions.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DuplicateNamespace {
function foo() public pure returns (uint256) {
return 0;
}

/// @custom:storage-location erc7201:conflicting
struct Conflicting1 {
uint256 b;
}

/// @custom:storage-location erc7201:conflicting
struct Conflicting2 {
uint256 c;
}

function foo2() public pure returns (uint256) {
return 0;
}
}

contract ConflictsWithParent is DuplicateNamespace {
function foo3() public pure returns (uint256) {
return 0;
}

/// @custom:storage-location erc7201:conflicting
struct Conflicting {
uint256 a;
}

function foo4() public pure returns (uint256) {
return 0;
}
}

contract ConflictsInBothParents is DuplicateNamespace, ConflictsWithParent {
}
24 changes: 24 additions & 0 deletions packages/core/contracts/test/NamespacedConflictsWithVariables.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DuplicateNamespace {
/// @custom:storage-location erc7201:conflicting
struct Conflicting1 {
uint256 b;
} Conflicting1 $Conflicting1;

/// @custom:storage-location erc7201:conflicting
struct Conflicting2 {
uint256 c;
} Conflicting2 $Conflicting2;
}

contract ConflictsWithParent is DuplicateNamespace {
/// @custom:storage-location erc7201:conflicting
struct Conflicting {
uint256 a;
} Conflicting $Conflicting;
}

contract ConflictsInBothParents is DuplicateNamespace, ConflictsWithParent {
}
4 changes: 3 additions & 1 deletion packages/core/hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ module.exports = {
],
overrides: {
'contracts/test/Namespaced.sol': { version: '0.8.20', settings },
'contracts/test/NamespacedConflicts.sol': { version: '0.8.20', settings },
'contracts/test/NamespacedConflictsStructsOnly.sol': { version: '0.8.20', settings },
'contracts/test/NamespacedConflictsWithVariables.sol': { version: '0.8.20', settings },
'contracts/test/NamespacedConflictsWithFunctions.sol': { version: '0.8.20', settings },
},
},
etherscan: {
Expand Down
95 changes: 95 additions & 0 deletions packages/core/src/storage-namespaced-conflicts-with-layout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import _test, { TestFn } from 'ava';
import { ContractDefinition } from 'solidity-ast';
import { findAll, astDereferencer } from 'solidity-ast/utils';
import { artifacts } from 'hardhat';

import { SolcOutput } from './solc-api';
import { StorageLayout } from './storage/layout';
import { extractStorageLayout } from './storage/extract';
import { solcInputOutputDecoder } from './src-decoder';

interface Context {
extractStorageLayout: (contract: string) => ReturnType<typeof extractStorageLayout>;
}

const test = _test as TestFn<Context>;

test.before(async t => {
const origBuildInfo = await artifacts.getBuildInfo(
'contracts/test/NamespacedConflictsWithFunctions.sol:DuplicateNamespace',
);
const namespacedBuildInfo = await artifacts.getBuildInfo(
'contracts/test/NamespacedConflictsWithVariables.sol:DuplicateNamespace',
);

if (origBuildInfo === undefined || namespacedBuildInfo === undefined) {
throw new Error('Build info not found');
}

const origSolcOutput: SolcOutput = origBuildInfo.output;
const origContracts: Record<string, ContractDefinition> = {};
const origStorageLayouts: Record<string, StorageLayout> = {};

const namespacedSolcOutput: SolcOutput = namespacedBuildInfo.output;
const namespacedContracts: Record<string, ContractDefinition> = {};
const namespacedStorageLayouts: Record<string, StorageLayout> = {};

const origContractDefs = [];
for (const def of findAll(
'ContractDefinition',
origSolcOutput.sources['contracts/test/NamespacedConflictsWithFunctions.sol'].ast,
)) {
origContractDefs.push(def);
}
const namespacedContractDefs = [];
for (const def of findAll(
'ContractDefinition',
namespacedSolcOutput.sources['contracts/test/NamespacedConflictsWithVariables.sol'].ast,
)) {
namespacedContractDefs.push(def);
}

for (let i = 0; i < origContractDefs.length; i++) {
const origContractDef = origContractDefs[i];
const namespacedContractDef = namespacedContractDefs[i];

origContracts[origContractDef.name] = origContractDef;
namespacedContracts[namespacedContractDef.name] = namespacedContractDef;

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
origStorageLayouts[origContractDef.name] =
origSolcOutput.contracts['contracts/test/NamespacedConflictsWithFunctions.sol'][
origContractDef.name
].storageLayout!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
namespacedStorageLayouts[namespacedContractDef.name] =
namespacedSolcOutput.contracts['contracts/test/NamespacedConflictsWithVariables.sol'][
namespacedContractDef.name
].storageLayout!;
}
const origDeref = astDereferencer(origSolcOutput);
const namespacedDeref = astDereferencer(namespacedSolcOutput);

const decodeSrc = solcInputOutputDecoder(origBuildInfo.input, origSolcOutput);
t.context.extractStorageLayout = name =>
extractStorageLayout(origContracts[name], decodeSrc, origDeref, origStorageLayouts[name], {
deref: namespacedDeref,
contractDef: namespacedContracts[name],
storageLayout: origStorageLayouts[name],
});
});

test('duplicate namespace', t => {
const error = t.throws(() => t.context.extractStorageLayout('DuplicateNamespace'));
t.snapshot(error?.message);
});

test('conflicts with parent', t => {
const error = t.throws(() => t.context.extractStorageLayout('ConflictsWithParent'));
t.snapshot(error?.message);
});

test('conflicts in both parents', t => {
const error = t.throws(() => t.context.extractStorageLayout('ConflictsInBothParents'));
t.snapshot(error?.message);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Snapshot report for `src/storage-namespaced-conflicts-with-layout.test.ts`

The actual snapshot is saved in `storage-namespaced-conflicts-with-layout.test.ts.snap`.

Generated by [AVA](https://avajs.dev).

## duplicate namespace

> Snapshot 1
`Namespace erc7201:conflicting is defined multiple times for contract DuplicateNamespace␊
The namespace erc7201:conflicting was found in structs at the following locations:␊
- contracts/test/NamespacedConflictsWithFunctions.sol:10␊
- contracts/test/NamespacedConflictsWithFunctions.sol:15␊
Use a unique namespace id for each struct annotated with '@custom:storage-location erc7201:<NAMESPACE_ID>' in your contract and its inherited contracts.`

## conflicts with parent

> Snapshot 1
`Namespace erc7201:conflicting is defined multiple times for contract ConflictsWithParent␊
The namespace erc7201:conflicting was found in structs at the following locations:␊
- contracts/test/NamespacedConflictsWithFunctions.sol:30␊
- contracts/test/NamespacedConflictsWithFunctions.sol:10␊
- contracts/test/NamespacedConflictsWithFunctions.sol:15␊
Use a unique namespace id for each struct annotated with '@custom:storage-location erc7201:<NAMESPACE_ID>' in your contract and its inherited contracts.`

## conflicts in both parents

> Snapshot 1
`Namespace erc7201:conflicting is defined multiple times for contract ConflictsInBothParents␊
The namespace erc7201:conflicting was found in structs at the following locations:␊
- contracts/test/NamespacedConflictsWithFunctions.sol:30␊
- contracts/test/NamespacedConflictsWithFunctions.sol:10␊
- contracts/test/NamespacedConflictsWithFunctions.sol:15␊
Use a unique namespace id for each struct annotated with '@custom:storage-location erc7201:<NAMESPACE_ID>' in your contract and its inherited contracts.`
Binary file not shown.
14 changes: 10 additions & 4 deletions packages/core/src/storage-namespaced-conflicts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,23 @@ interface Context {
const test = _test as TestFn<Context>;

test.before(async t => {
const buildInfo = await artifacts.getBuildInfo('contracts/test/NamespacedConflicts.sol:DuplicateNamespace');
const buildInfo = await artifacts.getBuildInfo(
'contracts/test/NamespacedConflictsStructsOnly.sol:DuplicateNamespace',
);
if (buildInfo === undefined) {
throw new Error('Build info not found');
}
const solcOutput: SolcOutput = buildInfo.output;
const contracts: Record<string, ContractDefinition> = {};
const storageLayouts: Record<string, StorageLayout> = {};
for (const def of findAll('ContractDefinition', solcOutput.sources['contracts/test/NamespacedConflicts.sol'].ast)) {
for (const def of findAll(
'ContractDefinition',
solcOutput.sources['contracts/test/NamespacedConflictsStructsOnly.sol'].ast,
)) {
contracts[def.name] = def;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
storageLayouts[def.name] = solcOutput.contracts['contracts/test/NamespacedConflicts.sol'][def.name].storageLayout!;
storageLayouts[def.name] =
solcOutput.contracts['contracts/test/NamespacedConflictsStructsOnly.sol'][def.name].storageLayout!;
}
const deref = astDereferencer(solcOutput);
const decodeSrc = solcInputOutputDecoder(buildInfo.input, solcOutput);
Expand All @@ -46,4 +52,4 @@ test('conflicts with parent', t => {
test('conflicts in both parents', t => {
const error = t.throws(() => t.context.extractStorageLayout('ConflictsInBothParents'));
t.snapshot(error?.message);
});
});
16 changes: 8 additions & 8 deletions packages/core/src/storage-namespaced-conflicts.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ Generated by [AVA](https://avajs.dev).
`Namespace erc7201:conflicting is defined multiple times for contract DuplicateNamespace␊
The namespace erc7201:conflicting was found in structs at the following locations:␊
- contracts/test/NamespacedConflicts.sol:6␊
- contracts/test/NamespacedConflicts.sol:11␊
- contracts/test/NamespacedConflictsStructsOnly.sol:6␊
- contracts/test/NamespacedConflictsStructsOnly.sol:11␊
Use a unique namespace id for each struct annotated with '@custom:storage-location erc7201:<NAMESPACE_ID>' in your contract and its inherited contracts.`

Expand All @@ -23,9 +23,9 @@ Generated by [AVA](https://avajs.dev).
`Namespace erc7201:conflicting is defined multiple times for contract ConflictsWithParent␊
The namespace erc7201:conflicting was found in structs at the following locations:␊
- contracts/test/NamespacedConflicts.sol:18␊
- contracts/test/NamespacedConflicts.sol:6␊
- contracts/test/NamespacedConflicts.sol:11␊
- contracts/test/NamespacedConflictsStructsOnly.sol:18␊
- contracts/test/NamespacedConflictsStructsOnly.sol:6␊
- contracts/test/NamespacedConflictsStructsOnly.sol:11␊
Use a unique namespace id for each struct annotated with '@custom:storage-location erc7201:<NAMESPACE_ID>' in your contract and its inherited contracts.`

Expand All @@ -36,8 +36,8 @@ Generated by [AVA](https://avajs.dev).
`Namespace erc7201:conflicting is defined multiple times for contract ConflictsInBothParents␊
The namespace erc7201:conflicting was found in structs at the following locations:␊
- contracts/test/NamespacedConflicts.sol:18␊
- contracts/test/NamespacedConflicts.sol:6␊
- contracts/test/NamespacedConflicts.sol:11␊
- contracts/test/NamespacedConflictsStructsOnly.sol:18␊
- contracts/test/NamespacedConflictsStructsOnly.sol:6␊
- contracts/test/NamespacedConflictsStructsOnly.sol:11␊
Use a unique namespace id for each struct annotated with '@custom:storage-location erc7201:<NAMESPACE_ID>' in your contract and its inherited contracts.`
Binary file modified packages/core/src/storage-namespaced-conflicts.test.ts.snap
Binary file not shown.
4 changes: 3 additions & 1 deletion packages/core/src/storage/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,9 @@ function getOriginalStruct(structCanonicalName: string, origContractDef: Contrac
}
}
}
throw new Error(`Could not find original source location for namespace struct with name ${structCanonicalName} from contract ${origContractDef.name}`);
throw new Error(
`Could not find original source location for namespace struct with name ${structCanonicalName} from contract ${origContractDef.name}`,
);
}

function getOriginalMemberSrc(structCanonicalName: string, memberLabel: string, origContractDef: ContractDefinition) {
Expand Down

0 comments on commit ba0314f

Please sign in to comment.