Commit 590a4084 authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat(ctb): spacer validation task (#3533)

Introduces a new task for validating the correctness of spacer
variables. Prevents future developers from accidentally moving spacers.
Co-authored-by: default avatarmergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
parent c3980e9a
...@@ -134,6 +134,10 @@ jobs: ...@@ -134,6 +134,10 @@ jobs:
name: lint name: lint
command: yarn lint:check command: yarn lint:check
working_directory: packages/contracts-bedrock working_directory: packages/contracts-bedrock
- run:
name: validate spacers
command: yarn validate-spacers
working_directory: packages/contracts-bedrock
- run: - run:
name: slither name: slither
command: | command: |
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
"coverage:lcov": "yarn build:differential && forge coverage --report lcov", "coverage:lcov": "yarn build:differential && forge coverage --report lcov",
"gas-snapshot": "yarn build:differential && forge snapshot", "gas-snapshot": "yarn build:differential && forge snapshot",
"storage-snapshot": "./scripts/storage-snapshot.sh", "storage-snapshot": "./scripts/storage-snapshot.sh",
"validate-spacers": "hardhat validate-spacers",
"slither": "./scripts/slither.sh", "slither": "./scripts/slither.sh",
"clean": "rm -rf ./dist ./artifacts ./forge-artifacts ./cache ./tsconfig.tsbuildinfo ./src/contract-artifacts.ts", "clean": "rm -rf ./dist ./artifacts ./forge-artifacts ./cache ./tsconfig.tsbuildinfo ./src/contract-artifacts.ts",
"lint:ts:check": "eslint . --max-warnings=0", "lint:ts:check": "eslint . --max-warnings=0",
......
...@@ -5,3 +5,4 @@ import './check-op-node' ...@@ -5,3 +5,4 @@ import './check-op-node'
import './check-l2-config' import './check-l2-config'
import './watch' import './watch'
import './forge-verify' import './forge-verify'
import './validate-spacers'
import { task } from 'hardhat/config'
task(
'validate-spacers',
'validates that spacer variables are in the correct positions'
).setAction(async (args, hre) => {
const names = await hre.artifacts.getAllFullyQualifiedNames()
for (const name of names) {
// Script is remarkably slow because of getBuildInfo, so better to skip test files since they
// don't matter for this check.
if (name.includes('.t.sol')) {
continue
}
const buildInfo = await hre.artifacts.getBuildInfo(name)
for (const source of Object.values(buildInfo.output.contracts)) {
for (const [contractName, contract] of Object.entries(source)) {
const storageLayout = (contract as any).storageLayout
for (const variable of storageLayout?.storage || []) {
if (variable.label.startsWith('spacer_')) {
const [, slot, offset, length] = variable.label.split('_')
// Check that the slot is correct.
if (slot !== variable.slot) {
throw new Error(
`${contractName} ${variable.label} is in slot ${variable.slot} but should be in ${slot}`
)
}
// Check that the offset is correct.
if (parseInt(offset, 10) !== variable.offset) {
throw new Error(
`${contractName} ${variable.label} is at offset ${variable.offset} but should be at ${offset}`
)
}
// Figure out the length of the variable.
let variableLength: number
if (variable.type.startsWith('t_mapping')) {
variableLength = 32
} else if (variable.type.startsWith('t_uint')) {
variableLength = variable.type.match(/uint([0-9]+)/)?.[1] / 8
} else if (variable.type.startsWith('t_bytes_')) {
variableLength = 32
} else if (variable.type.startsWith('t_bytes')) {
variableLength = variable.type.match(/uint([0-9]+)/)?.[1]
} else if (variable.type.startsWith('t_address')) {
variableLength = 20
} else {
throw new Error('unsupported type, add it to the script')
}
// Check that the length is correct.
if (parseInt(length, 10) !== variableLength) {
throw new Error(
`${contractName} ${variable.label} is ${variableLength} bytes long but should be ${length}`
)
}
}
}
}
}
}
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment