From d30144b4df85b2057c5f18b177ce57a3802c19fc Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 18 Mar 2024 19:09:51 +0100 Subject: [PATCH 01/23] Add storage slot derivation tooling and UDVT typed slot representation. --- .changeset/kind-planets-cough.md | 5 + contracts/mocks/StorageSlotMock.sol | 16 + contracts/utils/Arrays.sol | 24 +- contracts/utils/StorageSlot.sol | 416 +++++++++++++++++++++- foundry.toml | 2 + hardhat.config.js | 12 +- scripts/checks/compareGasReports.js | 2 +- scripts/generate/templates/StorageSlot.js | 147 +++++++- scripts/helpers.js | 7 +- test/helpers/random.js | 2 + test/helpers/strings.js | 5 + test/utils/StorageSlot.t.sol | 84 +++++ test/utils/StorageSlot.test.js | 21 +- 13 files changed, 689 insertions(+), 54 deletions(-) create mode 100644 .changeset/kind-planets-cough.md create mode 100644 test/helpers/strings.js create mode 100644 test/utils/StorageSlot.t.sol diff --git a/.changeset/kind-planets-cough.md b/.changeset/kind-planets-cough.md new file mode 100644 index 00000000000..65e79706017 --- /dev/null +++ b/.changeset/kind-planets-cough.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': minor +--- + +`StorageSlot`: Add storage slot derivation tooling and typed-slot representation with transient storage support. diff --git a/contracts/mocks/StorageSlotMock.sol b/contracts/mocks/StorageSlotMock.sol index 36f0f5af022..2417a171f60 100644 --- a/contracts/mocks/StorageSlotMock.sol +++ b/contracts/mocks/StorageSlotMock.sol @@ -7,6 +7,14 @@ import {StorageSlot} from "../utils/StorageSlot.sol"; contract StorageSlotMock { using StorageSlot for *; + function erc1967slot(string memory path) public pure returns (bytes32) { + return path.erc1967slot(); + } + + function erc7201slot(string memory path) public pure returns (bytes32) { + return path.erc7201slot(); + } + function setBooleanSlot(bytes32 slot, bool value) public { slot.getBooleanSlot().value = value; } @@ -23,6 +31,10 @@ contract StorageSlotMock { slot.getUint256Slot().value = value; } + function setInt256Slot(bytes32 slot, int256 value) public { + slot.getInt256Slot().value = value; + } + function getBooleanSlot(bytes32 slot) public view returns (bool) { return slot.getBooleanSlot().value; } @@ -39,6 +51,10 @@ contract StorageSlotMock { return slot.getUint256Slot().value; } + function getInt256Slot(bytes32 slot) public view returns (int256) { + return slot.getInt256Slot().value; + } + mapping(uint256 key => string) public stringMap; function setStringSlot(bytes32 slot, string calldata value) public { diff --git a/contracts/utils/Arrays.sol b/contracts/utils/Arrays.sol index 02252059898..92d8dcbf626 100644 --- a/contracts/utils/Arrays.sol +++ b/contracts/utils/Arrays.sol @@ -361,15 +361,11 @@ library Arrays { */ function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) { bytes32 slot; - // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` - // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. - /// @solidity memory-safe-assembly assembly { - mstore(0, arr.slot) - slot := add(keccak256(0, 0x20), pos) + slot := arr.slot } - return slot.getAddressSlot(); + return slot.deriveArray().offset(pos).getAddressSlot(); } /** @@ -379,15 +375,11 @@ library Arrays { */ function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) { bytes32 slot; - // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` - // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. - /// @solidity memory-safe-assembly assembly { - mstore(0, arr.slot) - slot := add(keccak256(0, 0x20), pos) + slot := arr.slot } - return slot.getBytes32Slot(); + return slot.deriveArray().offset(pos).getBytes32Slot(); } /** @@ -397,15 +389,11 @@ library Arrays { */ function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) { bytes32 slot; - // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` - // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. - /// @solidity memory-safe-assembly assembly { - mstore(0, arr.slot) - slot := add(keccak256(0, 0x20), pos) + slot := arr.slot } - return slot.getUint256Slot(); + return slot.deriveArray().offset(pos).getUint256Slot(); } /** diff --git a/contracts/utils/StorageSlot.sol b/contracts/utils/StorageSlot.sol index 4e02bfe746d..46ff2d5f832 100644 --- a/contracts/utils/StorageSlot.sol +++ b/contracts/utils/StorageSlot.sol @@ -2,7 +2,7 @@ // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; /** * @dev Library for reading and writing primitive types to specific storage slots. @@ -29,28 +29,123 @@ pragma solidity ^0.8.20; * ``` */ library StorageSlot { - struct AddressSlot { - address value; + /// Derivation tooling + /** + * @dev Derive an ERC-1967 slot from a string (path). + */ + function erc1967slot(string memory path) internal pure returns (bytes32 slot) { + /// @solidity memory-safe-assembly + assembly { + slot := sub(keccak256(add(path, 0x20), mload(path)), 1) + } } - struct BooleanSlot { - bool value; + /** + * @dev Derive an ERC-7201 slot from a string (path). + */ + function erc7201slot(string memory path) internal pure returns (bytes32 slot) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, sub(keccak256(add(path, 0x20), mload(path)), 1)) + slot := and(keccak256(0x00, 0x20), not(0xff)) + } } - struct Bytes32Slot { - bytes32 value; + /** + * @dev Add an offset to a slot to get the n-th element of a structure or an array. + */ + function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) { + unchecked { + return bytes32(uint256(slot) + pos); + } } - struct Uint256Slot { - uint256 value; + /** + * @dev Derive the location of the first element in an array from the slot where the length is stored. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ + function deriveArray(bytes32 slot) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, slot) + result := keccak256(0x00, 0x20) + } } - struct StringSlot { - string value; + /** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ + function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } } - struct BytesSlot { - bytes value; + /** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ + function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ + function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ + function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ + function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /// Storage slots as structs + struct AddressSlot { + address value; } /** @@ -63,6 +158,10 @@ library StorageSlot { } } + struct BooleanSlot { + bool value; + } + /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ @@ -73,6 +172,10 @@ library StorageSlot { } } + struct Bytes32Slot { + bytes32 value; + } + /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ @@ -83,6 +186,10 @@ library StorageSlot { } } + struct Uint256Slot { + uint256 value; + } + /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ @@ -93,6 +200,24 @@ library StorageSlot { } } + struct Int256Slot { + int256 value; + } + + /** + * @dev Returns an `Int256Slot` with member `value` located at `slot`. + */ + function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + struct StringSlot { + string value; + } + /** * @dev Returns an `StringSlot` with member `value` located at `slot`. */ @@ -113,6 +238,10 @@ library StorageSlot { } } + struct BytesSlot { + bytes value; + } + /** * @dev Returns an `BytesSlot` with member `value` located at `slot`. */ @@ -132,4 +261,265 @@ library StorageSlot { r.slot := store.slot } } + + /// Storage slots as udvt + /** + * @dev UDVT that represent a slot holding a address. + */ + type AddressSlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a AddressSlotType. + */ + function asAddressSlot(bytes32 slot) internal pure returns (AddressSlotType) { + return AddressSlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in (normal) storage. + */ + function sload(AddressSlotType slot) internal view returns (address value) { + /// @solidity memory-safe-assembly + assembly { + value := sload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in (normal) storage. + */ + function sstore(AddressSlotType slot, address value) internal { + /// @solidity memory-safe-assembly + assembly { + sstore(slot, value) + } + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(AddressSlotType slot) internal view returns (address value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(AddressSlotType slot, address value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } + + /** + * @dev UDVT that represent a slot holding a bool. + */ + type BooleanSlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a BooleanSlotType. + */ + function asBooleanSlot(bytes32 slot) internal pure returns (BooleanSlotType) { + return BooleanSlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in (normal) storage. + */ + function sload(BooleanSlotType slot) internal view returns (bool value) { + /// @solidity memory-safe-assembly + assembly { + value := sload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in (normal) storage. + */ + function sstore(BooleanSlotType slot, bool value) internal { + /// @solidity memory-safe-assembly + assembly { + sstore(slot, value) + } + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(BooleanSlotType slot) internal view returns (bool value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(BooleanSlotType slot, bool value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } + + /** + * @dev UDVT that represent a slot holding a bytes32. + */ + type Bytes32SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Bytes32SlotType. + */ + function asBytes32Slot(bytes32 slot) internal pure returns (Bytes32SlotType) { + return Bytes32SlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in (normal) storage. + */ + function sload(Bytes32SlotType slot) internal view returns (bytes32 value) { + /// @solidity memory-safe-assembly + assembly { + value := sload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in (normal) storage. + */ + function sstore(Bytes32SlotType slot, bytes32 value) internal { + /// @solidity memory-safe-assembly + assembly { + sstore(slot, value) + } + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(Bytes32SlotType slot) internal view returns (bytes32 value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(Bytes32SlotType slot, bytes32 value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } + + /** + * @dev UDVT that represent a slot holding a uint256. + */ + type Uint256SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Uint256SlotType. + */ + function asUint256Slot(bytes32 slot) internal pure returns (Uint256SlotType) { + return Uint256SlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in (normal) storage. + */ + function sload(Uint256SlotType slot) internal view returns (uint256 value) { + /// @solidity memory-safe-assembly + assembly { + value := sload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in (normal) storage. + */ + function sstore(Uint256SlotType slot, uint256 value) internal { + /// @solidity memory-safe-assembly + assembly { + sstore(slot, value) + } + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(Uint256SlotType slot) internal view returns (uint256 value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(Uint256SlotType slot, uint256 value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } + + /** + * @dev UDVT that represent a slot holding a int256. + */ + type Int256SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Int256SlotType. + */ + function asInt256Slot(bytes32 slot) internal pure returns (Int256SlotType) { + return Int256SlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in (normal) storage. + */ + function sload(Int256SlotType slot) internal view returns (int256 value) { + /// @solidity memory-safe-assembly + assembly { + value := sload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in (normal) storage. + */ + function sstore(Int256SlotType slot, int256 value) internal { + /// @solidity memory-safe-assembly + assembly { + sstore(slot, value) + } + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(Int256SlotType slot) internal view returns (int256 value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(Int256SlotType slot, int256 value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } } diff --git a/foundry.toml b/foundry.toml index cf207ffe984..391c40e6f11 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,4 +1,6 @@ [profile.default] +solc_version = '0.8.24' +evm_version = 'cancun' src = 'contracts' out = 'out' libs = ['node_modules', 'lib'] diff --git a/hardhat.config.js b/hardhat.config.js index b91a8865836..4bcdb8e5ead 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -18,7 +18,7 @@ const { argv } = require('yargs/yargs')() compiler: { alias: 'compileVersion', type: 'string', - default: '0.8.20', + default: '0.8.24', }, src: { alias: 'source', @@ -36,6 +36,11 @@ const { argv } = require('yargs/yargs')() type: 'boolean', default: false, }, + evm: { + alias: 'evmVersion', + type: 'string', + default: 'cancun', + }, // Extra modules coverage: { type: 'boolean', @@ -78,6 +83,7 @@ module.exports = { enabled: withOptimizations, runs: 200, }, + evmVersion: argv.evm, viaIR: withOptimizations && argv.ir, outputSelection: { '*': { '*': ['storageLayout'] } }, }, @@ -90,11 +96,13 @@ module.exports = { '*': { 'code-size': withOptimizations, 'unused-param': !argv.coverage, // coverage causes unused-param warnings + 'transient-storage': false, default: 'error', }, }, networks: { hardhat: { + hardfork: argv.evm, allowUnlimitedContractSize, initialBaseFeePerGas: argv.coverage ? 0 : undefined, }, @@ -102,7 +110,7 @@ module.exports = { exposed: { imports: true, initializers: true, - exclude: ['vendor/**/*', '**/*WithInit.sol'], + exclude: ['vendor/**/*', '**/*WithInit.sol', 'utils/StorageSlot.sol'], }, gasReporter: { enabled: argv.gas, diff --git a/scripts/checks/compareGasReports.js b/scripts/checks/compareGasReports.js index 1afe1d3686e..fd94dc266dc 100755 --- a/scripts/checks/compareGasReports.js +++ b/scripts/checks/compareGasReports.js @@ -50,7 +50,7 @@ class Report { // Compare two reports static compare(update, ref, opts = { hideEqual: true, strictTesting: false }) { if (JSON.stringify(update.config.metadata) !== JSON.stringify(ref.config.metadata)) { - throw new Error('Reports produced with non matching metadata'); + console.warn('WARNING: Reports produced with non matching metadata'); } const deployments = update.info.deployments diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 3d2a62a9230..0591bd61c92 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -6,12 +6,13 @@ const TYPES = [ { type: 'bool', isValueType: true, name: 'Boolean' }, { type: 'bytes32', isValueType: true }, { type: 'uint256', isValueType: true }, + { type: 'int256', isValueType: true }, { type: 'string', isValueType: false }, { type: 'bytes', isValueType: false }, -].map(type => Object.assign(type, { struct: (type.name ?? capitalize(type.type)) + 'Slot' })); +].map(type => Object.assign(type, { name: (type.name ?? capitalize(type.type)) + 'Slot' })); const header = `\ -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; /** * @dev Library for reading and writing primitive types to specific storage slots. @@ -39,17 +40,76 @@ pragma solidity ^0.8.20; */ `; -const struct = type => `\ -struct ${type.struct} { - ${type.type} value; +const tooling = `\ +/** + * @dev Derive an ERC-1967 slot from a string (path). + */ +function erc1967slot(string memory path) internal pure returns (bytes32 slot) { + /// @solidity memory-safe-assembly + assembly { + slot := sub(keccak256(add(path, 0x20), mload(path)), 1) + } +} + +/** + * @dev Derive an ERC-7201 slot from a string (path). + */ +function erc7201slot(string memory path) internal pure returns (bytes32 slot) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, sub(keccak256(add(path, 0x20), mload(path)), 1)) + slot := and(keccak256(0x00, 0x20), not(0xff)) + } +} + +/** + * @dev Add an offset to a slot to get the n-th element of a structure or an array. + */ +function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) { + unchecked { + return bytes32(uint256(slot) + pos); + } +} + +/** + * @dev Derive the location of the first element in an array from the slot where the length is stored. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ +function deriveArray(bytes32 slot) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, slot) + result := keccak256(0x00, 0x20) + } +} +`; + +const derive = ({ type }) => `\ +/** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ +function deriveMapping(bytes32 slot, ${type} key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } } `; -const get = type => `\ +const struct = ({ type, name }) => `\ +struct ${name} { + ${type} value; +} + /** - * @dev Returns an \`${type.struct}\` with member \`value\` located at \`slot\`. + * @dev Returns an \`${name}\` with member \`value\` located at \`slot\`. */ -function get${type.struct}(bytes32 slot) internal pure returns (${type.struct} storage r) { +function get${name}(bytes32 slot) internal pure returns (${name} storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot @@ -57,11 +117,11 @@ function get${type.struct}(bytes32 slot) internal pure returns (${type.struct} s } `; -const getStorage = type => `\ +const getStorage = ({ type, name }) => `\ /** - * @dev Returns an \`${type.struct}\` representation of the ${type.type} storage pointer \`store\`. + * @dev Returns an \`${name}\` representation of the ${type} storage pointer \`store\`. */ -function get${type.struct}(${type.type} storage store) internal pure returns (${type.struct} storage r) { +function get${name}(${type} storage store) internal pure returns (${name} storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot @@ -69,10 +129,73 @@ function get${type.struct}(${type.type} storage store) internal pure returns (${ } `; +const udvt = ({ type, name }) => `\ +/** + * @dev UDVT that represent a slot holding a ${type}. + */ +type ${name}Type is bytes32; + +/** + * @dev Cast an arbitrary slot to a ${name}Type. + */ +function as${name}(bytes32 slot) internal pure returns (${name}Type) { + return ${name}Type.wrap(slot); +} + +/** + * @dev Load the value held at location \`slot\` in (normal) storage. + */ +function sload(${name}Type slot) internal view returns (${type} value) { + /// @solidity memory-safe-assembly + assembly { + value := sload(slot) + } +} + +/** + * @dev Store \`value\` at location \`slot\` in (normal) storage. + */ +function sstore(${name}Type slot, ${type} value) internal { + /// @solidity memory-safe-assembly + assembly { + sstore(slot, value) + } +} + +/** + * @dev Load the value held at location \`slot\` in transient storage. + */ +function tload(${name}Type slot) internal view returns (${type} value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } +} + +/** + * @dev Store \`value\` at location \`slot\` in transient storage. + */ +function tstore(${name}Type slot, ${type} value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } +} +`; + // GENERATE module.exports = format( header.trimEnd(), 'library StorageSlot {', - [...TYPES.map(struct), ...TYPES.flatMap(type => [get(type), type.isValueType ? '' : getStorage(type)])], + '/// Derivation tooling', + tooling, + TYPES.filter(type => type.isValueType).flatMap(type => derive(type)), // TODO support non-value type + '/// Storage slots as structs', + TYPES.flatMap(type => [ + struct(type), + type.isValueType ? '' : getStorage(type) + ]), + '/// Storage slots as udvt', + TYPES.filter(type => type.isValueType).map(type => udvt(type)), '}', ); diff --git a/scripts/helpers.js b/scripts/helpers.js index 52102bdaf1b..d28c0866d1c 100644 --- a/scripts/helpers.js +++ b/scripts/helpers.js @@ -1,10 +1,7 @@ const iterate = require('../test/helpers/iterate'); +const strings = require('../test/helpers/strings'); module.exports = { - // Capitalize the first char of a string - // Example: capitalize('uint256') → 'Uint256' - capitalize: str => str.charAt(0).toUpperCase() + str.slice(1), - - // Iterate tools for the test helpers ...iterate, + ...strings, }; diff --git a/test/helpers/random.js b/test/helpers/random.js index 48b97768b6e..3adeed0a447 100644 --- a/test/helpers/random.js +++ b/test/helpers/random.js @@ -4,12 +4,14 @@ const generators = { address: () => ethers.Wallet.createRandom().address, bytes32: () => ethers.hexlify(ethers.randomBytes(32)), uint256: () => ethers.toBigInt(ethers.randomBytes(32)), + int256: () => ethers.toBigInt(ethers.randomBytes(32)) + ethers.MinInt256, hexBytes: length => ethers.hexlify(ethers.randomBytes(length)), }; generators.address.zero = ethers.ZeroAddress; generators.bytes32.zero = ethers.ZeroHash; generators.uint256.zero = 0n; +generators.int256.zero = 0n; generators.hexBytes.zero = '0x'; module.exports = { diff --git a/test/helpers/strings.js b/test/helpers/strings.js new file mode 100644 index 00000000000..4f34099d7df --- /dev/null +++ b/test/helpers/strings.js @@ -0,0 +1,5 @@ +module.exports = { + // Capitalize the first char of a string + // Example: capitalize('uint256') → 'Uint256' + capitalize: str => str.charAt(0).toUpperCase() + str.slice(1), +}; diff --git a/test/utils/StorageSlot.t.sol b/test/utils/StorageSlot.t.sol new file mode 100644 index 00000000000..4ce1e30c455 --- /dev/null +++ b/test/utils/StorageSlot.t.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; + +contract StorageSlotTest is Test { + using StorageSlot for *; + + // Variable declarations + uint256 private _variable; + uint256[] private _array; + mapping(address => uint256) private _mapping; + + // Tests + function testValue1(uint256 value) public { + // set in solidity + _variable = value; + // read using Slots + assertEq(_getVariableSlot().asUint256Slot().sload(), value); + } + + function testValue2(uint256 value) public { + // set using Slots + _getVariableSlot().asUint256Slot().sstore(value); + // read in solidity + assertEq(_variable, value); + } + + function testArray1(uint256[] calldata values) public { + // set in solidity + _array = values; + // read using Slots + assertEq(_getArraySlot().asUint256Slot().sload(), values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(_getArraySlot().deriveArray().offset(i).asUint256Slot().sload(), values[i]); + } + } + + function testArray2(uint256[] calldata values) public { + // set using Slots + _getArraySlot().asUint256Slot().sstore(values.length); + for (uint256 i = 0; i < values.length; ++i) { + _getArraySlot().deriveArray().offset(i).asUint256Slot().sstore(values[i]); + } + // read in solidity + assertEq(_array, values); + } + + function testMapping1(address key, uint256 value) public { + // set in solidity + _mapping[key] = value; + // read using Slots + assertEq(_getMappingSlot().deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMapping2(address key, uint256 value) public { + // set using Slots + _getMappingSlot().deriveMapping(key).asUint256Slot().sstore(value); + // read in solidity + assertEq(_mapping[key], value); + } + + // Slot extraction + function _getVariableSlot() public pure returns (bytes32 slot) { + assembly { + slot := _variable.slot + } + } + + function _getArraySlot() public pure returns (bytes32 slot) { + assembly { + slot := _array.slot + } + } + + function _getMappingSlot() public pure returns (bytes32 slot) { + assembly { + slot := _mapping.slot + } + } +} diff --git a/test/utils/StorageSlot.test.js b/test/utils/StorageSlot.test.js index ab237b700a0..fdd3eb01500 100644 --- a/test/utils/StorageSlot.test.js +++ b/test/utils/StorageSlot.test.js @@ -1,6 +1,7 @@ const { ethers } = require('hardhat'); const { expect } = require('chai'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +const { erc1967slot, erc7201slot } = require('../helpers/storage'); const { generators } = require('../helpers/random'); const slot = ethers.id('some.storage.slot'); @@ -17,12 +18,26 @@ describe('StorageSlot', function () { Object.assign(this, await loadFixture(fixture)); }); + describe('slot derivation', function () { + const path = 'example.main'; + + it('erc-1967', async function () { + expect(await this.mock.erc1967slot(path)).to.equal(erc1967slot(path)); + }); + + it('erc-7201', async function () { + expect(await this.mock.erc7201slot(path)).to.equal(erc7201slot(path)); + }); + }); + for (const { type, value, zero } of [ { type: 'Boolean', value: true, zero: false }, - { type: 'Address', value: generators.address(), zero: ethers.ZeroAddress }, - { type: 'Bytes32', value: generators.bytes32(), zero: ethers.ZeroHash }, + { type: 'Address', value: generators.address(), zero: generators.address.zero }, + { type: 'Bytes32', value: generators.bytes32(), zero: generators.bytes32.zero }, + { type: 'Uint256', value: generators.uint256(), zero: generators.uint256.zero }, + { type: 'Int256', value: generators.int256(), zero: generators.int256.zero }, + { type: 'Bytes', value: generators.hexBytes(128), zero: generators.hexBytes.zero }, { type: 'String', value: 'lorem ipsum', zero: '' }, - { type: 'Bytes', value: generators.hexBytes(128), zero: '0x' }, ]) { describe(`${type} storage slot`, function () { it('set', async function () { From e282a33fb0527772b9c2780057c8b9fa78598f0f Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 18 Mar 2024 21:04:16 +0100 Subject: [PATCH 02/23] generate fuzzing tests --- scripts/generate/run.js | 1 + scripts/generate/templates/StorageSlot.js | 17 +- .../generate/templates/StorageSlot.opts.js | 13 + scripts/generate/templates/StorageSlot.t.js | 127 ++++++ test/utils/StorageSlot.t.sol | 387 ++++++++++++++++-- 5 files changed, 498 insertions(+), 47 deletions(-) create mode 100644 scripts/generate/templates/StorageSlot.opts.js create mode 100644 scripts/generate/templates/StorageSlot.t.js diff --git a/scripts/generate/run.js b/scripts/generate/run.js index 53589455ab5..2f4cbdc119d 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -44,6 +44,7 @@ for (const [file, template] of Object.entries({ // Tests for (const [file, template] of Object.entries({ 'utils/structs/Checkpoints.t.sol': './templates/Checkpoints.t.js', + 'utils/StorageSlot.t.sol': './templates/StorageSlot.t.js', })) { generateFromTemplate(file, template, './test/'); } diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 0591bd61c92..5b17862e7a3 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -1,15 +1,5 @@ const format = require('../format-lines'); -const { capitalize } = require('../../helpers'); - -const TYPES = [ - { type: 'address', isValueType: true }, - { type: 'bool', isValueType: true, name: 'Boolean' }, - { type: 'bytes32', isValueType: true }, - { type: 'uint256', isValueType: true }, - { type: 'int256', isValueType: true }, - { type: 'string', isValueType: false }, - { type: 'bytes', isValueType: false }, -].map(type => Object.assign(type, { name: (type.name ?? capitalize(type.type)) + 'Slot' })); +const { TYPES } = require('./StorageSlot.opts'); const header = `\ pragma solidity ^0.8.24; @@ -191,10 +181,7 @@ module.exports = format( tooling, TYPES.filter(type => type.isValueType).flatMap(type => derive(type)), // TODO support non-value type '/// Storage slots as structs', - TYPES.flatMap(type => [ - struct(type), - type.isValueType ? '' : getStorage(type) - ]), + TYPES.flatMap(type => [struct(type), type.isValueType ? '' : getStorage(type)]), '/// Storage slots as udvt', TYPES.filter(type => type.isValueType).map(type => udvt(type)), '}', diff --git a/scripts/generate/templates/StorageSlot.opts.js b/scripts/generate/templates/StorageSlot.opts.js new file mode 100644 index 00000000000..ef1bd915cff --- /dev/null +++ b/scripts/generate/templates/StorageSlot.opts.js @@ -0,0 +1,13 @@ +const { capitalize } = require('../../helpers'); + +const TYPES = [ + { type: 'address', isValueType: true }, + { type: 'bool', isValueType: true, name: 'Boolean' }, + { type: 'bytes32', isValueType: true }, + { type: 'uint256', isValueType: true }, + { type: 'int256', isValueType: true }, + { type: 'string', isValueType: false }, + { type: 'bytes', isValueType: false }, +].map(type => Object.assign(type, { name: (type.name ?? capitalize(type.type)) + 'Slot' })); + +module.exports = { TYPES }; diff --git a/scripts/generate/templates/StorageSlot.t.js b/scripts/generate/templates/StorageSlot.t.js new file mode 100644 index 00000000000..e4551c61af1 --- /dev/null +++ b/scripts/generate/templates/StorageSlot.t.js @@ -0,0 +1,127 @@ +const format = require('../format-lines'); +const { TYPES } = require('./StorageSlot.opts'); + +const header = `\ +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; +`; + +const variable = ({ type, name }) => `\ +${type} private _${type}Variable; + +function testValue_${type}1(${type} value) public { + bytes32 slot; + assembly { + slot := _${type}Variable.slot + } + + // set in solidity + _${type}Variable = value; + + // read using Slots + assertEq(slot.as${name}().sload(), value); +} + +function testValue_${type}2(${type} value) public { + bytes32 slot; + assembly { + slot := _${type}Variable.slot + } + + // set using Slots + slot.as${name}().sstore(value); + + // read in solidity + assertEq(_${type}Variable, value); +} +`; + +const array = ({ type, name }) => `\ +${type}[] private _${type}Array; + +function testArray_${type}1(${type}[] calldata values) public { + bytes32 slot; + assembly { + slot := _${type}Array.slot + } + + // set in solidity + _${type}Array = values; + + // read using Slots + assertEq(slot.asUint256Slot().sload(), values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(slot.deriveArray().offset(i).as${name}().sload(), values[i]); + } +} + +function testArray_${type}2(${type}[] calldata values) public { + bytes32 slot; + assembly { + slot := _${type}Array.slot + } + + // set using Slots + slot.asUint256Slot().sstore(values.length); + for (uint256 i = 0; i < values.length; ++i) { + slot.deriveArray().offset(i).as${name}().sstore(values[i]); + } + + // read in solidity + assertEq(_${type}Array.length, values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(_${type}Array[i], values[i]); + } +} +`; + +const mapping = ({ type }) => `\ +mapping(${type} => uint256) private _${type}Mapping; + +function testMapping_${type}1(${type} key, uint256 value) public { + bytes32 slot; + assembly { + slot := _${type}Mapping.slot + } + + // set in solidity + _${type}Mapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); +} + +function testMapping_${type}2(${type} key, uint256 value) public { + bytes32 slot; + assembly { + slot := _${type}Mapping.slot + } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_${type}Mapping[key], value); +} +`; + +// GENERATE +module.exports = format( + header.trimEnd(), + 'contract StorageSlotTest is Test {', + 'using StorageSlot for *;', + '', + // bool is not using a full word, solidity allocation in storage is not right aligned + TYPES.filter(type => type.isValueType && type.type !== 'bool').map(type => variable(type)), + // bool is not using a full word, solidity allocation in storage is not right aligned + TYPES.filter(type => type.isValueType && type.type !== 'bool').map(type => array(type)), + TYPES.filter(type => type.isValueType).flatMap(type => + [].concat( + mapping(type), + (type.variant ?? []).map(variant => mapping({ type: variant })), + ), + ), + '}', +); diff --git a/test/utils/StorageSlot.t.sol b/test/utils/StorageSlot.t.sol index 4ce1e30c455..d5409fcbb5c 100644 --- a/test/utils/StorageSlot.t.sol +++ b/test/utils/StorageSlot.t.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MIT +// This file was procedurally generated from scripts/generate/templates/StorageSlot.t.js. pragma solidity ^0.8.20; @@ -9,76 +10,398 @@ import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; contract StorageSlotTest is Test { using StorageSlot for *; - // Variable declarations - uint256 private _variable; - uint256[] private _array; - mapping(address => uint256) private _mapping; + address private _addressVariable; + + function testValue_address1(address value) public { + bytes32 slot; + assembly { + slot := _addressVariable.slot + } + + // set in solidity + _addressVariable = value; + + // read using Slots + assertEq(slot.asAddressSlot().sload(), value); + } + + function testValue_address2(address value) public { + bytes32 slot; + assembly { + slot := _addressVariable.slot + } + + // set using Slots + slot.asAddressSlot().sstore(value); + + // read in solidity + assertEq(_addressVariable, value); + } + + bytes32 private _bytes32Variable; + + function testValue_bytes321(bytes32 value) public { + bytes32 slot; + assembly { + slot := _bytes32Variable.slot + } + + // set in solidity + _bytes32Variable = value; + + // read using Slots + assertEq(slot.asBytes32Slot().sload(), value); + } + + function testValue_bytes322(bytes32 value) public { + bytes32 slot; + assembly { + slot := _bytes32Variable.slot + } + + // set using Slots + slot.asBytes32Slot().sstore(value); + + // read in solidity + assertEq(_bytes32Variable, value); + } + + uint256 private _uint256Variable; + + function testValue_uint2561(uint256 value) public { + bytes32 slot; + assembly { + slot := _uint256Variable.slot + } + + // set in solidity + _uint256Variable = value; + + // read using Slots + assertEq(slot.asUint256Slot().sload(), value); + } + + function testValue_uint2562(uint256 value) public { + bytes32 slot; + assembly { + slot := _uint256Variable.slot + } + + // set using Slots + slot.asUint256Slot().sstore(value); + + // read in solidity + assertEq(_uint256Variable, value); + } + + int256 private _int256Variable; + + function testValue_int2561(int256 value) public { + bytes32 slot; + assembly { + slot := _int256Variable.slot + } - // Tests - function testValue1(uint256 value) public { // set in solidity - _variable = value; + _int256Variable = value; + // read using Slots - assertEq(_getVariableSlot().asUint256Slot().sload(), value); + assertEq(slot.asInt256Slot().sload(), value); } - function testValue2(uint256 value) public { + function testValue_int2562(int256 value) public { + bytes32 slot; + assembly { + slot := _int256Variable.slot + } + // set using Slots - _getVariableSlot().asUint256Slot().sstore(value); + slot.asInt256Slot().sstore(value); + // read in solidity - assertEq(_variable, value); + assertEq(_int256Variable, value); } - function testArray1(uint256[] calldata values) public { + address[] private _addressArray; + + function testArray_address1(address[] calldata values) public { + bytes32 slot; + assembly { + slot := _addressArray.slot + } + // set in solidity - _array = values; + _addressArray = values; + // read using Slots - assertEq(_getArraySlot().asUint256Slot().sload(), values.length); + assertEq(slot.asUint256Slot().sload(), values.length); for (uint256 i = 0; i < values.length; ++i) { - assertEq(_getArraySlot().deriveArray().offset(i).asUint256Slot().sload(), values[i]); + assertEq(slot.deriveArray().offset(i).asAddressSlot().sload(), values[i]); } } - function testArray2(uint256[] calldata values) public { + function testArray_address2(address[] calldata values) public { + bytes32 slot; + assembly { + slot := _addressArray.slot + } + // set using Slots - _getArraySlot().asUint256Slot().sstore(values.length); + slot.asUint256Slot().sstore(values.length); for (uint256 i = 0; i < values.length; ++i) { - _getArraySlot().deriveArray().offset(i).asUint256Slot().sstore(values[i]); + slot.deriveArray().offset(i).asAddressSlot().sstore(values[i]); } + // read in solidity - assertEq(_array, values); + assertEq(_addressArray.length, values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(_addressArray[i], values[i]); + } } - function testMapping1(address key, uint256 value) public { + bytes32[] private _bytes32Array; + + function testArray_bytes321(bytes32[] calldata values) public { + bytes32 slot; + assembly { + slot := _bytes32Array.slot + } + // set in solidity - _mapping[key] = value; + _bytes32Array = values; + // read using Slots - assertEq(_getMappingSlot().deriveMapping(key).asUint256Slot().sload(), value); + assertEq(slot.asUint256Slot().sload(), values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(slot.deriveArray().offset(i).asBytes32Slot().sload(), values[i]); + } } - function testMapping2(address key, uint256 value) public { + function testArray_bytes322(bytes32[] calldata values) public { + bytes32 slot; + assembly { + slot := _bytes32Array.slot + } + // set using Slots - _getMappingSlot().deriveMapping(key).asUint256Slot().sstore(value); + slot.asUint256Slot().sstore(values.length); + for (uint256 i = 0; i < values.length; ++i) { + slot.deriveArray().offset(i).asBytes32Slot().sstore(values[i]); + } + // read in solidity - assertEq(_mapping[key], value); + assertEq(_bytes32Array.length, values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(_bytes32Array[i], values[i]); + } } - // Slot extraction - function _getVariableSlot() public pure returns (bytes32 slot) { + uint256[] private _uint256Array; + + function testArray_uint2561(uint256[] calldata values) public { + bytes32 slot; assembly { - slot := _variable.slot + slot := _uint256Array.slot + } + + // set in solidity + _uint256Array = values; + + // read using Slots + assertEq(slot.asUint256Slot().sload(), values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(slot.deriveArray().offset(i).asUint256Slot().sload(), values[i]); } } - function _getArraySlot() public pure returns (bytes32 slot) { + function testArray_uint2562(uint256[] calldata values) public { + bytes32 slot; assembly { - slot := _array.slot + slot := _uint256Array.slot + } + + // set using Slots + slot.asUint256Slot().sstore(values.length); + for (uint256 i = 0; i < values.length; ++i) { + slot.deriveArray().offset(i).asUint256Slot().sstore(values[i]); + } + + // read in solidity + assertEq(_uint256Array.length, values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(_uint256Array[i], values[i]); } } - function _getMappingSlot() public pure returns (bytes32 slot) { + int256[] private _int256Array; + + function testArray_int2561(int256[] calldata values) public { + bytes32 slot; + assembly { + slot := _int256Array.slot + } + + // set in solidity + _int256Array = values; + + // read using Slots + assertEq(slot.asUint256Slot().sload(), values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(slot.deriveArray().offset(i).asInt256Slot().sload(), values[i]); + } + } + + function testArray_int2562(int256[] calldata values) public { + bytes32 slot; + assembly { + slot := _int256Array.slot + } + + // set using Slots + slot.asUint256Slot().sstore(values.length); + for (uint256 i = 0; i < values.length; ++i) { + slot.deriveArray().offset(i).asInt256Slot().sstore(values[i]); + } + + // read in solidity + assertEq(_int256Array.length, values.length); + for (uint256 i = 0; i < values.length; ++i) { + assertEq(_int256Array[i], values[i]); + } + } + + mapping(address => uint256) private _addressMapping; + + function testMapping_address1(address key, uint256 value) public { + bytes32 slot; + assembly { + slot := _addressMapping.slot + } + + // set in solidity + _addressMapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMapping_address2(address key, uint256 value) public { + bytes32 slot; assembly { - slot := _mapping.slot + slot := _addressMapping.slot } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_addressMapping[key], value); + } + + mapping(bool => uint256) private _boolMapping; + + function testMapping_bool1(bool key, uint256 value) public { + bytes32 slot; + assembly { + slot := _boolMapping.slot + } + + // set in solidity + _boolMapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMapping_bool2(bool key, uint256 value) public { + bytes32 slot; + assembly { + slot := _boolMapping.slot + } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_boolMapping[key], value); + } + + mapping(bytes32 => uint256) private _bytes32Mapping; + + function testMapping_bytes321(bytes32 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _bytes32Mapping.slot + } + + // set in solidity + _bytes32Mapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMapping_bytes322(bytes32 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _bytes32Mapping.slot + } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_bytes32Mapping[key], value); + } + + mapping(uint256 => uint256) private _uint256Mapping; + + function testMapping_uint2561(uint256 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _uint256Mapping.slot + } + + // set in solidity + _uint256Mapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMapping_uint2562(uint256 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _uint256Mapping.slot + } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_uint256Mapping[key], value); + } + + mapping(int256 => uint256) private _int256Mapping; + + function testMapping_int2561(int256 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _int256Mapping.slot + } + + // set in solidity + _int256Mapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMapping_int2562(int256 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _int256Mapping.slot + } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_int256Mapping[key], value); } } From 8705253226949842257cbb96bb45d8ad0bc956cb Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 18 Mar 2024 21:06:57 +0100 Subject: [PATCH 03/23] update hardhat to support cancun --- package-lock.json | 1097 +++++++++++++++++++-------------------------- package.json | 2 +- 2 files changed, 452 insertions(+), 647 deletions(-) diff --git a/package-lock.json b/package-lock.json index 773e1028dd7..6a8cfc7d81d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openzeppelin-solidity", - "version": "5.0.1", + "version": "5.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openzeppelin-solidity", - "version": "5.0.1", + "version": "5.0.2", "license": "MIT", "devDependencies": { "@changesets/changelog-github": "^0.5.0", @@ -26,7 +26,7 @@ "ethers": "^6.7.1", "glob": "^10.3.5", "graphlib": "^2.1.8", - "hardhat": "^2.17.4", + "hardhat": "^2.22.1", "hardhat-exposed": "^0.3.14-0", "hardhat-gas-reporter": "^1.0.9", "hardhat-ignore-warnings": "^0.2.0", @@ -109,32 +109,6 @@ "node": ">=6.9.0" } }, - "node_modules/@chainsafe/as-sha256": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz", - "integrity": "sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==", - "dev": true - }, - "node_modules/@chainsafe/persistent-merkle-tree": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz", - "integrity": "sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1" - } - }, - "node_modules/@chainsafe/ssz": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.9.4.tgz", - "integrity": "sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/persistent-merkle-tree": "^0.4.2", - "case": "^1.6.3" - } - }, "node_modules/@changesets/apply-release-plan": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-6.1.4.tgz", @@ -1779,304 +1753,233 @@ "node": ">= 8" } }, - "node_modules/@nomicfoundation/ethereumjs-block": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.2.tgz", - "integrity": "sha512-hSe6CuHI4SsSiWWjHDIzWhSiAVpzMUcDRpWYzN0T9l8/Rz7xNn3elwVOJ/tAyS0LqL6vitUD78Uk7lQDXZun7Q==", + "node_modules/@nomicfoundation/edr": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.3.2.tgz", + "integrity": "sha512-HGWtjibAK1mo4I2A7nJ/fXqe/J9G54OrSPJnnkY2K8TiXotYLShGd9GvHkae3PuFjTJKm6ZgBy7tveJj5yrCfw==", "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-common": "4.0.2", - "@nomicfoundation/ethereumjs-rlp": "5.0.2", - "@nomicfoundation/ethereumjs-trie": "6.0.2", - "@nomicfoundation/ethereumjs-tx": "5.0.2", - "@nomicfoundation/ethereumjs-util": "9.0.2", - "ethereum-cryptography": "0.1.3", - "ethers": "^5.7.1" + "engines": { + "node": ">= 18" }, + "optionalDependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.3.2", + "@nomicfoundation/edr-darwin-x64": "0.3.2", + "@nomicfoundation/edr-linux-arm64-gnu": "0.3.2", + "@nomicfoundation/edr-linux-arm64-musl": "0.3.2", + "@nomicfoundation/edr-linux-x64-gnu": "0.3.2", + "@nomicfoundation/edr-linux-x64-musl": "0.3.2", + "@nomicfoundation/edr-win32-arm64-msvc": "0.3.2", + "@nomicfoundation/edr-win32-ia32-msvc": "0.3.2", + "@nomicfoundation/edr-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-l6wfSBUUbGJiCENT6272CDI8yoMuf0sZH56H5qz3HnAyVzenkOvmzyF6/lar54m986kdAQqWls4cLvDxiOuLxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=14" + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-block/node_modules/ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.2.tgz", + "integrity": "sha512-OboExL7vEw+TRJQl3KkaEKU4K7PTdZPTInZ0fxMAtOpcWp7EKR+dQo68vc/iAOusB3xszHKxt7t+WpisItfdcg==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } + "optional": true, + "os": [ + "darwin" ], - "dependencies": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" + "engines": { + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-blockchain": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.2.tgz", - "integrity": "sha512-8UUsSXJs+MFfIIAKdh3cG16iNmWzWC/91P40sazNvrqhhdR/RtGDlFk2iFTGbBAZPs2+klZVzhRX8m2wvuvz3w==", + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-xtEK+1eg++3pHi6405NDXd80S3CGOFEGQIyVGCwjMGQFOLSzBGGCsrb/0GB4J19zd1o/8ftCd/HjZcbVAWWTLQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.2", - "@nomicfoundation/ethereumjs-common": "4.0.2", - "@nomicfoundation/ethereumjs-ethash": "3.0.2", - "@nomicfoundation/ethereumjs-rlp": "5.0.2", - "@nomicfoundation/ethereumjs-trie": "6.0.2", - "@nomicfoundation/ethereumjs-tx": "5.0.2", - "@nomicfoundation/ethereumjs-util": "9.0.2", - "abstract-level": "^1.0.3", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "level": "^8.0.0", - "lru-cache": "^5.1.1", - "memory-level": "^1.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-common": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.2.tgz", - "integrity": "sha512-I2WGP3HMGsOoycSdOTSqIaES0ughQTueOsddJ36aYVpI3SN8YSusgRFLwzDJwRFVIYDKx/iJz0sQ5kBHVgdDwg==", + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-3cIsskJOXQ1yEVsImmCacY7O03tUTiWrmd54F05PnPFrDLkjbzodQ3b2gUWzfbzUZWl67ZTJd1CvVSzpe7XGzw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-util": "9.0.2", - "crc-32": "^1.2.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-ethash": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.2.tgz", - "integrity": "sha512-8PfoOQCcIcO9Pylq0Buijuq/O73tmMVURK0OqdjhwqcGHYC2PwhbajDh7GZ55ekB0Px197ajK3PQhpKoiI/UPg==", + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-ouPdphHNsyO7wqwa4hwahC5WqBglK/fIvMmhR/SXNZ9qruIpsA8ZZKIURaHMOv/2h2BbNGcyTX9uEk6+5rK/MQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.2", - "@nomicfoundation/ethereumjs-rlp": "5.0.2", - "@nomicfoundation/ethereumjs-util": "9.0.2", - "abstract-level": "^1.0.3", - "bigint-crypto-utils": "^3.0.23", - "ethereum-cryptography": "0.1.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-evm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.2.tgz", - "integrity": "sha512-rBLcUaUfANJxyOx9HIdMX6uXGin6lANCulIm/pjMgRqfiCRMZie3WKYxTSd8ZE/d+qT+zTedBF4+VHTdTSePmQ==", + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-sRhwhiPbkpJMOUwXW1FZw9ks6xWyQhIhM0E8o3TXEXKSPKTE6whQLEk1R37iFITaI36vb6rSwLKTU1cb32gCoA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@ethersproject/providers": "^5.7.1", - "@nomicfoundation/ethereumjs-common": "4.0.2", - "@nomicfoundation/ethereumjs-tx": "5.0.2", - "@nomicfoundation/ethereumjs-util": "9.0.2", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "mcl-wasm": "^0.7.1", - "rustbn.js": "~0.2.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14" + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-rlp": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.2.tgz", - "integrity": "sha512-QwmemBc+MMsHJ1P1QvPl8R8p2aPvvVcKBbvHnQOKBpBztEo0omN0eaob6FeZS/e3y9NSe+mfu3nNFBHszqkjTA==", + "node_modules/@nomicfoundation/edr-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-arm64-msvc/-/edr-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-IEwVealKfumu1HSSnama26yPuQC/uthRPK5IWtFsQUOGwOXaS1r9Bu7cGYH2jBHl3IT/JbxD4xzPq/2pM9uK0A==", + "cpu": [ + "arm64" + ], "dev": true, - "bin": { - "rlp": "bin/rlp" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=14" + "node": ">= 10" } }, - "node_modules/@nomicfoundation/ethereumjs-statemanager": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.2.tgz", - "integrity": "sha512-dlKy5dIXLuDubx8Z74sipciZnJTRSV/uHG48RSijhgm1V7eXYFC567xgKtsKiVZB1ViTP9iFL4B6Je0xD6X2OA==", + "node_modules/@nomicfoundation/edr-win32-ia32-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-ia32-msvc/-/edr-win32-ia32-msvc-0.3.2.tgz", + "integrity": "sha512-jYMnf6SFgguqROswwdsjJ1wvneD/5c16pVu9OD4DxNqhKNP5bHEw6L2N4DcJ89tpXMpJ6AlOpc0QuwzddiZ3tA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-common": "4.0.2", - "@nomicfoundation/ethereumjs-rlp": "5.0.2", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "ethers": "^5.7.1", - "js-sdsl": "^4.1.4" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-statemanager/node_modules/ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-Byn4QuWczRy/DUUQM3WjglgX/jGVUURVFaUsmIhnGg//MPlCLawubBGRqsrMuvaYedlIIJ4I2rgKvZlxdgHrqg==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } + "optional": true, + "os": [ + "win32" ], - "dependencies": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" + "engines": { + "node": ">= 18" } }, - "node_modules/@nomicfoundation/ethereumjs-trie": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.2.tgz", - "integrity": "sha512-yw8vg9hBeLYk4YNg5MrSJ5H55TLOv2FSWUTROtDtTMMmDGROsAu+0tBjiNGTnKRi400M6cEzoFfa89Fc5k8NTQ==", + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", "dev": true, "dependencies": { - "@nomicfoundation/ethereumjs-rlp": "5.0.2", - "@nomicfoundation/ethereumjs-util": "9.0.2", - "@types/readable-stream": "^2.3.13", - "ethereum-cryptography": "0.1.3", - "readable-stream": "^3.6.0" + "@nomicfoundation/ethereumjs-util": "9.0.4" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", + "dev": true, + "bin": { + "rlp": "bin/rlp.cjs" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@nomicfoundation/ethereumjs-tx": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.2.tgz", - "integrity": "sha512-T+l4/MmTp7VhJeNloMkM+lPU3YMUaXdcXgTGCf8+ZFvV9NYZTRLFekRwlG6/JMmVfIfbrW+dRRJ9A6H5Q/Z64g==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", "dev": true, "dependencies": { - "@chainsafe/ssz": "^0.9.2", - "@ethersproject/providers": "^5.7.2", - "@nomicfoundation/ethereumjs-common": "4.0.2", - "@nomicfoundation/ethereumjs-rlp": "5.0.2", - "@nomicfoundation/ethereumjs-util": "9.0.2", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", "ethereum-cryptography": "0.1.3" }, "engines": { - "node": ">=14" + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } } }, "node_modules/@nomicfoundation/ethereumjs-util": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.2.tgz", - "integrity": "sha512-4Wu9D3LykbSBWZo8nJCnzVIYGvGCuyiYLIJa9XXNVt1q1jUzHdB+sJvx95VGCpPkCT+IbLecW6yfzy3E1bQrwQ==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", "dev": true, "dependencies": { - "@chainsafe/ssz": "^0.10.0", - "@nomicfoundation/ethereumjs-rlp": "5.0.2", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", "ethereum-cryptography": "0.1.3" }, "engines": { - "node": ">=14" - } - }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/@chainsafe/persistent-merkle-tree": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz", - "integrity": "sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1" - } - }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/@chainsafe/ssz": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.10.2.tgz", - "integrity": "sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==", - "dev": true, - "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/persistent-merkle-tree": "^0.5.0" - } - }, - "node_modules/@nomicfoundation/ethereumjs-vm": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.2.tgz", - "integrity": "sha512-Bj3KZT64j54Tcwr7Qm/0jkeZXJMfdcAtRBedou+Hx0dPOSIgqaIr0vvLwP65TpHbak2DmAq+KJbW2KNtIoFwvA==", - "dev": true, - "dependencies": { - "@nomicfoundation/ethereumjs-block": "5.0.2", - "@nomicfoundation/ethereumjs-blockchain": "7.0.2", - "@nomicfoundation/ethereumjs-common": "4.0.2", - "@nomicfoundation/ethereumjs-evm": "2.0.2", - "@nomicfoundation/ethereumjs-rlp": "5.0.2", - "@nomicfoundation/ethereumjs-statemanager": "2.0.2", - "@nomicfoundation/ethereumjs-trie": "6.0.2", - "@nomicfoundation/ethereumjs-tx": "5.0.2", - "@nomicfoundation/ethereumjs-util": "9.0.2", - "debug": "^4.3.3", - "ethereum-cryptography": "0.1.3", - "mcl-wasm": "^0.7.1", - "rustbn.js": "~0.2.0" + "node": ">=18" }, - "engines": { - "node": ">=14" + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } } }, "node_modules/@nomicfoundation/hardhat-chai-matchers": { @@ -2930,22 +2833,6 @@ "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", "dev": true }, - "node_modules/@types/readable-stream": { - "version": "2.3.15", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", - "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "safe-buffer": "~5.1.1" - } - }, - "node_modules/@types/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "node_modules/@types/secp256k1": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", @@ -2967,48 +2854,6 @@ "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", "dev": true }, - "node_modules/abstract-level": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", - "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "catering": "^2.1.0", - "is-buffer": "^2.0.5", - "level-supports": "^4.0.0", - "level-transcoder": "^1.0.1", - "module-error": "^1.0.1", - "queue-microtask": "^1.2.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/abstract-level/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -3110,6 +2955,59 @@ "node": ">=0.4.2" } }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -3404,26 +3302,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/bech32": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", @@ -3442,15 +3320,6 @@ "node": ">=4" } }, - "node_modules/bigint-crypto-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.3.0.tgz", - "integrity": "sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3472,6 +3341,142 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3509,18 +3514,6 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true }, - "node_modules/browser-level": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", - "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", - "dev": true, - "dependencies": { - "abstract-level": "^1.0.2", - "catering": "^2.1.1", - "module-error": "^1.0.2", - "run-parallel-limit": "^1.1.0" - } - }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -3619,6 +3612,18 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase-keys": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", @@ -3645,30 +3650,12 @@ "node": ">=6" } }, - "node_modules/case": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", - "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, - "node_modules/catering": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", - "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/cbor": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.1.tgz", @@ -3801,23 +3788,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/classic-level": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.3.0.tgz", - "integrity": "sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "abstract-level": "^1.0.2", - "catering": "^2.1.0", - "module-error": "^1.0.1", - "napi-macros": "^2.2.2", - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -3827,6 +3797,18 @@ "node": ">=6" } }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-table3": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", @@ -4083,18 +4065,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -5701,12 +5671,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -6021,23 +5985,17 @@ } }, "node_modules/hardhat": { - "version": "2.17.4", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.17.4.tgz", - "integrity": "sha512-YTyHjVc9s14CY/O7Dbtzcr/92fcz6AzhrMaj6lYsZpYPIPLzOrFCZHHPxfGQB6FiE6IPNE0uJaAbr7zGF79goA==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.1.tgz", + "integrity": "sha512-cTWYIJc5jQ132XUI8oRI/TO9L6oavPoJRCTRU9sIjkVxvkxz0Axz0K83Z3BEdJTqBQ2W84ZRoTekti84kBwCjg==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/ethereumjs-block": "5.0.2", - "@nomicfoundation/ethereumjs-blockchain": "7.0.2", - "@nomicfoundation/ethereumjs-common": "4.0.2", - "@nomicfoundation/ethereumjs-evm": "2.0.2", - "@nomicfoundation/ethereumjs-rlp": "5.0.2", - "@nomicfoundation/ethereumjs-statemanager": "2.0.2", - "@nomicfoundation/ethereumjs-trie": "6.0.2", - "@nomicfoundation/ethereumjs-tx": "5.0.2", - "@nomicfoundation/ethereumjs-util": "9.0.2", - "@nomicfoundation/ethereumjs-vm": "7.0.2", + "@nomicfoundation/edr": "^0.3.1", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-tx": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", "@nomicfoundation/solidity-analyzer": "^0.1.0", "@sentry/node": "^5.18.1", "@types/bn.js": "^5.1.0", @@ -6045,6 +6003,7 @@ "adm-zip": "^0.4.16", "aggregate-error": "^3.0.0", "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", "chalk": "^2.4.2", "chokidar": "^3.4.0", "ci-info": "^2.0.0", @@ -6604,26 +6563,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -6796,29 +6735,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -7109,16 +7025,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/js-sdsl": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.2.tgz", - "integrity": "sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -7237,69 +7143,6 @@ "node": ">=6" } }, - "node_modules/level": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", - "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", - "dev": true, - "dependencies": { - "browser-level": "^1.0.1", - "classic-level": "^1.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/level" - } - }, - "node_modules/level-supports": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", - "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/level-transcoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", - "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "module-error": "^1.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/level-transcoder/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -7477,15 +7320,6 @@ "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", "dev": true }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -7512,15 +7346,6 @@ "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", "dev": true }, - "node_modules/mcl-wasm": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", - "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==", - "dev": true, - "engines": { - "node": ">=8.9.0" - } - }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -7532,20 +7357,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/memory-level": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", - "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", - "dev": true, - "dependencies": { - "abstract-level": "^1.0.0", - "functional-red-black-tree": "^1.0.1", - "module-error": "^1.0.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -8017,15 +7828,6 @@ "node": ">=10" } }, - "node_modules/module-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", - "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -8044,12 +7846,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-macros": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", - "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==", - "dev": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -9095,35 +8891,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/run-parallel-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", - "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rustbn.js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", - "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==", - "dev": true - }, "node_modules/safe-array-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", @@ -11360,6 +11127,62 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -11606,12 +11429,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -11667,18 +11484,6 @@ "node": ">=10" } }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/yargs-unparser/node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", diff --git a/package.json b/package.json index 80c41b49c7d..4d6c1e13e85 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "ethers": "^6.7.1", "glob": "^10.3.5", "graphlib": "^2.1.8", - "hardhat": "^2.17.4", + "hardhat": "^2.22.1", "hardhat-exposed": "^0.3.14-0", "hardhat-gas-reporter": "^1.0.9", "hardhat-ignore-warnings": "^0.2.0", From f6bc398a40cfbbea1d6650146a778721f706ef50 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 18 Mar 2024 21:14:55 +0100 Subject: [PATCH 04/23] fix cassing --- scripts/generate/templates/StorageSlot.js | 26 +++++----- .../generate/templates/StorageSlot.opts.js | 2 +- scripts/generate/templates/StorageSlot.t.js | 22 ++++---- test/utils/StorageSlot.t.sol | 52 +++++++++---------- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 5b17862e7a3..225da55b6d0 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -92,14 +92,14 @@ function deriveMapping(bytes32 slot, ${type} key) internal pure returns (bytes32 `; const struct = ({ type, name }) => `\ -struct ${name} { +struct ${name}Slot { ${type} value; } /** - * @dev Returns an \`${name}\` with member \`value\` located at \`slot\`. + * @dev Returns an \`${name}Slot\` with member \`value\` located at \`slot\`. */ -function get${name}(bytes32 slot) internal pure returns (${name} storage r) { +function get${name}Slot(bytes32 slot) internal pure returns (${name}Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot @@ -109,9 +109,9 @@ function get${name}(bytes32 slot) internal pure returns (${name} storage r) { const getStorage = ({ type, name }) => `\ /** - * @dev Returns an \`${name}\` representation of the ${type} storage pointer \`store\`. + * @dev Returns an \`${name}Slot\` representation of the ${type} storage pointer \`store\`. */ -function get${name}(${type} storage store) internal pure returns (${name} storage r) { +function get${name}Slot(${type} storage store) internal pure returns (${name}Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot @@ -123,19 +123,19 @@ const udvt = ({ type, name }) => `\ /** * @dev UDVT that represent a slot holding a ${type}. */ -type ${name}Type is bytes32; +type ${name}SlotType is bytes32; /** - * @dev Cast an arbitrary slot to a ${name}Type. + * @dev Cast an arbitrary slot to a ${name}SlotType. */ -function as${name}(bytes32 slot) internal pure returns (${name}Type) { - return ${name}Type.wrap(slot); +function as${name}Slot(bytes32 slot) internal pure returns (${name}SlotType) { + return ${name}SlotType.wrap(slot); } /** * @dev Load the value held at location \`slot\` in (normal) storage. */ -function sload(${name}Type slot) internal view returns (${type} value) { +function sload(${name}SlotType slot) internal view returns (${type} value) { /// @solidity memory-safe-assembly assembly { value := sload(slot) @@ -145,7 +145,7 @@ function sload(${name}Type slot) internal view returns (${type} value) { /** * @dev Store \`value\` at location \`slot\` in (normal) storage. */ -function sstore(${name}Type slot, ${type} value) internal { +function sstore(${name}SlotType slot, ${type} value) internal { /// @solidity memory-safe-assembly assembly { sstore(slot, value) @@ -155,7 +155,7 @@ function sstore(${name}Type slot, ${type} value) internal { /** * @dev Load the value held at location \`slot\` in transient storage. */ -function tload(${name}Type slot) internal view returns (${type} value) { +function tload(${name}SlotType slot) internal view returns (${type} value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) @@ -165,7 +165,7 @@ function tload(${name}Type slot) internal view returns (${type} value) { /** * @dev Store \`value\` at location \`slot\` in transient storage. */ -function tstore(${name}Type slot, ${type} value) internal { +function tstore(${name}SlotType slot, ${type} value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) diff --git a/scripts/generate/templates/StorageSlot.opts.js b/scripts/generate/templates/StorageSlot.opts.js index ef1bd915cff..0dfd0015e80 100644 --- a/scripts/generate/templates/StorageSlot.opts.js +++ b/scripts/generate/templates/StorageSlot.opts.js @@ -8,6 +8,6 @@ const TYPES = [ { type: 'int256', isValueType: true }, { type: 'string', isValueType: false }, { type: 'bytes', isValueType: false }, -].map(type => Object.assign(type, { name: (type.name ?? capitalize(type.type)) + 'Slot' })); +].map(type => Object.assign(type, { name: type.name ?? capitalize(type.type) })); module.exports = { TYPES }; diff --git a/scripts/generate/templates/StorageSlot.t.js b/scripts/generate/templates/StorageSlot.t.js index e4551c61af1..744ccdbe8ad 100644 --- a/scripts/generate/templates/StorageSlot.t.js +++ b/scripts/generate/templates/StorageSlot.t.js @@ -12,7 +12,7 @@ import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; const variable = ({ type, name }) => `\ ${type} private _${type}Variable; -function testValue_${type}1(${type} value) public { +function testValue${name}1(${type} value) public { bytes32 slot; assembly { slot := _${type}Variable.slot @@ -22,17 +22,17 @@ function testValue_${type}1(${type} value) public { _${type}Variable = value; // read using Slots - assertEq(slot.as${name}().sload(), value); + assertEq(slot.as${name}Slot().sload(), value); } -function testValue_${type}2(${type} value) public { +function testValue${name}2(${type} value) public { bytes32 slot; assembly { slot := _${type}Variable.slot } // set using Slots - slot.as${name}().sstore(value); + slot.as${name}Slot().sstore(value); // read in solidity assertEq(_${type}Variable, value); @@ -42,7 +42,7 @@ function testValue_${type}2(${type} value) public { const array = ({ type, name }) => `\ ${type}[] private _${type}Array; -function testArray_${type}1(${type}[] calldata values) public { +function testArray${name}1(${type}[] calldata values) public { bytes32 slot; assembly { slot := _${type}Array.slot @@ -54,11 +54,11 @@ function testArray_${type}1(${type}[] calldata values) public { // read using Slots assertEq(slot.asUint256Slot().sload(), values.length); for (uint256 i = 0; i < values.length; ++i) { - assertEq(slot.deriveArray().offset(i).as${name}().sload(), values[i]); + assertEq(slot.deriveArray().offset(i).as${name}Slot().sload(), values[i]); } } -function testArray_${type}2(${type}[] calldata values) public { +function testArray${name}2(${type}[] calldata values) public { bytes32 slot; assembly { slot := _${type}Array.slot @@ -67,7 +67,7 @@ function testArray_${type}2(${type}[] calldata values) public { // set using Slots slot.asUint256Slot().sstore(values.length); for (uint256 i = 0; i < values.length; ++i) { - slot.deriveArray().offset(i).as${name}().sstore(values[i]); + slot.deriveArray().offset(i).as${name}Slot().sstore(values[i]); } // read in solidity @@ -78,10 +78,10 @@ function testArray_${type}2(${type}[] calldata values) public { } `; -const mapping = ({ type }) => `\ +const mapping = ({ type, name }) => `\ mapping(${type} => uint256) private _${type}Mapping; -function testMapping_${type}1(${type} key, uint256 value) public { +function testMapping${name}1(${type} key, uint256 value) public { bytes32 slot; assembly { slot := _${type}Mapping.slot @@ -93,7 +93,7 @@ function testMapping_${type}1(${type} key, uint256 value) public { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } -function testMapping_${type}2(${type} key, uint256 value) public { +function testMapping${name}2(${type} key, uint256 value) public { bytes32 slot; assembly { slot := _${type}Mapping.slot diff --git a/test/utils/StorageSlot.t.sol b/test/utils/StorageSlot.t.sol index d5409fcbb5c..358eaaca978 100644 --- a/test/utils/StorageSlot.t.sol +++ b/test/utils/StorageSlot.t.sol @@ -12,7 +12,7 @@ contract StorageSlotTest is Test { address private _addressVariable; - function testValue_address1(address value) public { + function testValueAddress1(address value) public { bytes32 slot; assembly { slot := _addressVariable.slot @@ -25,7 +25,7 @@ contract StorageSlotTest is Test { assertEq(slot.asAddressSlot().sload(), value); } - function testValue_address2(address value) public { + function testValueAddress2(address value) public { bytes32 slot; assembly { slot := _addressVariable.slot @@ -40,7 +40,7 @@ contract StorageSlotTest is Test { bytes32 private _bytes32Variable; - function testValue_bytes321(bytes32 value) public { + function testValueBytes321(bytes32 value) public { bytes32 slot; assembly { slot := _bytes32Variable.slot @@ -53,7 +53,7 @@ contract StorageSlotTest is Test { assertEq(slot.asBytes32Slot().sload(), value); } - function testValue_bytes322(bytes32 value) public { + function testValueBytes322(bytes32 value) public { bytes32 slot; assembly { slot := _bytes32Variable.slot @@ -68,7 +68,7 @@ contract StorageSlotTest is Test { uint256 private _uint256Variable; - function testValue_uint2561(uint256 value) public { + function testValueUint2561(uint256 value) public { bytes32 slot; assembly { slot := _uint256Variable.slot @@ -81,7 +81,7 @@ contract StorageSlotTest is Test { assertEq(slot.asUint256Slot().sload(), value); } - function testValue_uint2562(uint256 value) public { + function testValueUint2562(uint256 value) public { bytes32 slot; assembly { slot := _uint256Variable.slot @@ -96,7 +96,7 @@ contract StorageSlotTest is Test { int256 private _int256Variable; - function testValue_int2561(int256 value) public { + function testValueInt2561(int256 value) public { bytes32 slot; assembly { slot := _int256Variable.slot @@ -109,7 +109,7 @@ contract StorageSlotTest is Test { assertEq(slot.asInt256Slot().sload(), value); } - function testValue_int2562(int256 value) public { + function testValueInt2562(int256 value) public { bytes32 slot; assembly { slot := _int256Variable.slot @@ -124,7 +124,7 @@ contract StorageSlotTest is Test { address[] private _addressArray; - function testArray_address1(address[] calldata values) public { + function testArrayAddress1(address[] calldata values) public { bytes32 slot; assembly { slot := _addressArray.slot @@ -140,7 +140,7 @@ contract StorageSlotTest is Test { } } - function testArray_address2(address[] calldata values) public { + function testArrayAddress2(address[] calldata values) public { bytes32 slot; assembly { slot := _addressArray.slot @@ -161,7 +161,7 @@ contract StorageSlotTest is Test { bytes32[] private _bytes32Array; - function testArray_bytes321(bytes32[] calldata values) public { + function testArrayBytes321(bytes32[] calldata values) public { bytes32 slot; assembly { slot := _bytes32Array.slot @@ -177,7 +177,7 @@ contract StorageSlotTest is Test { } } - function testArray_bytes322(bytes32[] calldata values) public { + function testArrayBytes322(bytes32[] calldata values) public { bytes32 slot; assembly { slot := _bytes32Array.slot @@ -198,7 +198,7 @@ contract StorageSlotTest is Test { uint256[] private _uint256Array; - function testArray_uint2561(uint256[] calldata values) public { + function testArrayUint2561(uint256[] calldata values) public { bytes32 slot; assembly { slot := _uint256Array.slot @@ -214,7 +214,7 @@ contract StorageSlotTest is Test { } } - function testArray_uint2562(uint256[] calldata values) public { + function testArrayUint2562(uint256[] calldata values) public { bytes32 slot; assembly { slot := _uint256Array.slot @@ -235,7 +235,7 @@ contract StorageSlotTest is Test { int256[] private _int256Array; - function testArray_int2561(int256[] calldata values) public { + function testArrayInt2561(int256[] calldata values) public { bytes32 slot; assembly { slot := _int256Array.slot @@ -251,7 +251,7 @@ contract StorageSlotTest is Test { } } - function testArray_int2562(int256[] calldata values) public { + function testArrayInt2562(int256[] calldata values) public { bytes32 slot; assembly { slot := _int256Array.slot @@ -272,7 +272,7 @@ contract StorageSlotTest is Test { mapping(address => uint256) private _addressMapping; - function testMapping_address1(address key, uint256 value) public { + function testMappingAddress1(address key, uint256 value) public { bytes32 slot; assembly { slot := _addressMapping.slot @@ -284,7 +284,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMapping_address2(address key, uint256 value) public { + function testMappingAddress2(address key, uint256 value) public { bytes32 slot; assembly { slot := _addressMapping.slot @@ -299,7 +299,7 @@ contract StorageSlotTest is Test { mapping(bool => uint256) private _boolMapping; - function testMapping_bool1(bool key, uint256 value) public { + function testMappingBoolean1(bool key, uint256 value) public { bytes32 slot; assembly { slot := _boolMapping.slot @@ -311,7 +311,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMapping_bool2(bool key, uint256 value) public { + function testMappingBoolean2(bool key, uint256 value) public { bytes32 slot; assembly { slot := _boolMapping.slot @@ -326,7 +326,7 @@ contract StorageSlotTest is Test { mapping(bytes32 => uint256) private _bytes32Mapping; - function testMapping_bytes321(bytes32 key, uint256 value) public { + function testMappingBytes321(bytes32 key, uint256 value) public { bytes32 slot; assembly { slot := _bytes32Mapping.slot @@ -338,7 +338,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMapping_bytes322(bytes32 key, uint256 value) public { + function testMappingBytes322(bytes32 key, uint256 value) public { bytes32 slot; assembly { slot := _bytes32Mapping.slot @@ -353,7 +353,7 @@ contract StorageSlotTest is Test { mapping(uint256 => uint256) private _uint256Mapping; - function testMapping_uint2561(uint256 key, uint256 value) public { + function testMappingUint2561(uint256 key, uint256 value) public { bytes32 slot; assembly { slot := _uint256Mapping.slot @@ -365,7 +365,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMapping_uint2562(uint256 key, uint256 value) public { + function testMappingUint2562(uint256 key, uint256 value) public { bytes32 slot; assembly { slot := _uint256Mapping.slot @@ -380,7 +380,7 @@ contract StorageSlotTest is Test { mapping(int256 => uint256) private _int256Mapping; - function testMapping_int2561(int256 key, uint256 value) public { + function testMappingInt2561(int256 key, uint256 value) public { bytes32 slot; assembly { slot := _int256Mapping.slot @@ -392,7 +392,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMapping_int2562(int256 key, uint256 value) public { + function testMappingInt2562(int256 key, uint256 value) public { bytes32 slot; assembly { slot := _int256Mapping.slot From 45a70488f80269ad3693d7be5f16abc78631854b Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 18 Mar 2024 21:17:13 +0100 Subject: [PATCH 05/23] update hardhat-ignore-warnings --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a8cfc7d81d..ecd59850f74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "hardhat": "^2.22.1", "hardhat-exposed": "^0.3.14-0", "hardhat-gas-reporter": "^1.0.9", - "hardhat-ignore-warnings": "^0.2.0", + "hardhat-ignore-warnings": "^0.2.11", "lodash.startcase": "^4.4.0", "micromatch": "^4.0.2", "p-limit": "^3.1.0", @@ -6078,9 +6078,9 @@ } }, "node_modules/hardhat-ignore-warnings": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/hardhat-ignore-warnings/-/hardhat-ignore-warnings-0.2.9.tgz", - "integrity": "sha512-q1oj6/ixiAx+lgIyGLBajVCSC7qUtAoK7LS9Nr8UVHYo8Iuh5naBiVGo4RDJ6wxbDGYBkeSukUGZrMqzC2DWwA==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/hardhat-ignore-warnings/-/hardhat-ignore-warnings-0.2.11.tgz", + "integrity": "sha512-+nHnRbP6COFZaXE7HAY7TZNE3au5vHe5dkcnyq0XaP07ikT2fJ3NhFY0vn7Deh4Qbz0Z/9Xpnj2ki6Ktgk61pg==", "dev": true, "dependencies": { "minimatch": "^5.1.0", diff --git a/package.json b/package.json index 4d6c1e13e85..fb34df0b6f3 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "hardhat": "^2.22.1", "hardhat-exposed": "^0.3.14-0", "hardhat-gas-reporter": "^1.0.9", - "hardhat-ignore-warnings": "^0.2.0", + "hardhat-ignore-warnings": "^0.2.11", "lodash.startcase": "^4.4.0", "micromatch": "^4.0.2", "p-limit": "^3.1.0", From 1ac21fd8b085ab1aa234a0b6e6498f8404f6daf8 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 18 Mar 2024 21:21:30 +0100 Subject: [PATCH 06/23] fix hardhat-expose exclusion --- hardhat.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat.config.js b/hardhat.config.js index 4bcdb8e5ead..62165db4dfb 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -110,7 +110,7 @@ module.exports = { exposed: { imports: true, initializers: true, - exclude: ['vendor/**/*', '**/*WithInit.sol', 'utils/StorageSlot.sol'], + exclude: ['vendor/**/*', '**/*WithInit.sol', '**/utils/StorageSlot.sol'], }, gasReporter: { enabled: argv.gas, From a13c2bb78ee8d61d4b800a66de0e54f42764ef0d Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 18 Mar 2024 22:25:44 +0100 Subject: [PATCH 07/23] fix test --- test/governance/extensions/GovernorTimelockAccess.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/governance/extensions/GovernorTimelockAccess.test.js b/test/governance/extensions/GovernorTimelockAccess.test.js index 72394e92822..c3d3b32684e 100644 --- a/test/governance/extensions/GovernorTimelockAccess.test.js +++ b/test/governance/extensions/GovernorTimelockAccess.test.js @@ -375,7 +375,7 @@ describe('GovernorTimelockAccess', function () { if (delay > 0) { await this.helper.waitForEta(); } - expect(await this.helper.execute()) + await expect(this.helper.execute()) .to.emit(this.mock, 'ProposalExecuted') .withArgs(this.proposal.id) .to.emit(this.receiver, 'CalledUnrestricted'); From 3f96b4ce9a2511e5516d0412f7473b5ea6b89225 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 18 Mar 2024 22:39:01 +0100 Subject: [PATCH 08/23] re-enable variant testing for mapping key --- .../generate/templates/StorageSlot.opts.js | 6 +- scripts/generate/templates/StorageSlot.t.js | 16 ++- test/utils/StorageSlot.t.sol | 134 ++++++++++++++---- 3 files changed, 120 insertions(+), 36 deletions(-) diff --git a/scripts/generate/templates/StorageSlot.opts.js b/scripts/generate/templates/StorageSlot.opts.js index 0dfd0015e80..aed1f988845 100644 --- a/scripts/generate/templates/StorageSlot.opts.js +++ b/scripts/generate/templates/StorageSlot.opts.js @@ -3,9 +3,9 @@ const { capitalize } = require('../../helpers'); const TYPES = [ { type: 'address', isValueType: true }, { type: 'bool', isValueType: true, name: 'Boolean' }, - { type: 'bytes32', isValueType: true }, - { type: 'uint256', isValueType: true }, - { type: 'int256', isValueType: true }, + { type: 'bytes32', isValueType: true, variants: ['bytes4'] }, + { type: 'uint256', isValueType: true, variants: ['uint32'] }, + { type: 'int256', isValueType: true, variants: ['int32'] }, { type: 'string', isValueType: false }, { type: 'bytes', isValueType: false }, ].map(type => Object.assign(type, { name: type.name ?? capitalize(type.type) })); diff --git a/scripts/generate/templates/StorageSlot.t.js b/scripts/generate/templates/StorageSlot.t.js index 744ccdbe8ad..9d9910e6d54 100644 --- a/scripts/generate/templates/StorageSlot.t.js +++ b/scripts/generate/templates/StorageSlot.t.js @@ -1,4 +1,5 @@ const format = require('../format-lines'); +const { capitalize } = require('../../helpers'); const { TYPES } = require('./StorageSlot.opts'); const header = `\ @@ -12,7 +13,7 @@ import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; const variable = ({ type, name }) => `\ ${type} private _${type}Variable; -function testValue${name}1(${type} value) public { +function testValue${name}_1(${type} value) public { bytes32 slot; assembly { slot := _${type}Variable.slot @@ -25,7 +26,7 @@ function testValue${name}1(${type} value) public { assertEq(slot.as${name}Slot().sload(), value); } -function testValue${name}2(${type} value) public { +function testValue${name}_2(${type} value) public { bytes32 slot; assembly { slot := _${type}Variable.slot @@ -42,7 +43,7 @@ function testValue${name}2(${type} value) public { const array = ({ type, name }) => `\ ${type}[] private _${type}Array; -function testArray${name}1(${type}[] calldata values) public { +function testArray${name}_1(${type}[] calldata values) public { bytes32 slot; assembly { slot := _${type}Array.slot @@ -58,7 +59,7 @@ function testArray${name}1(${type}[] calldata values) public { } } -function testArray${name}2(${type}[] calldata values) public { +function testArray${name}_2(${type}[] calldata values) public { bytes32 slot; assembly { slot := _${type}Array.slot @@ -81,7 +82,7 @@ function testArray${name}2(${type}[] calldata values) public { const mapping = ({ type, name }) => `\ mapping(${type} => uint256) private _${type}Mapping; -function testMapping${name}1(${type} key, uint256 value) public { +function testMapping${name}_1(${type} key, uint256 value) public { bytes32 slot; assembly { slot := _${type}Mapping.slot @@ -93,7 +94,7 @@ function testMapping${name}1(${type} key, uint256 value) public { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } -function testMapping${name}2(${type} key, uint256 value) public { +function testMapping${name}_2(${type} key, uint256 value) public { bytes32 slot; assembly { slot := _${type}Mapping.slot @@ -110,6 +111,7 @@ function testMapping${name}2(${type} key, uint256 value) public { // GENERATE module.exports = format( header.trimEnd(), + '// solhint-disable func-name-mixedcase', 'contract StorageSlotTest is Test {', 'using StorageSlot for *;', '', @@ -120,7 +122,7 @@ module.exports = format( TYPES.filter(type => type.isValueType).flatMap(type => [].concat( mapping(type), - (type.variant ?? []).map(variant => mapping({ type: variant })), + (type.variants ?? []).map(variant => mapping({ type: variant, name: capitalize(variant) })), ), ), '}', diff --git a/test/utils/StorageSlot.t.sol b/test/utils/StorageSlot.t.sol index 358eaaca978..43d6f9dbbcd 100644 --- a/test/utils/StorageSlot.t.sol +++ b/test/utils/StorageSlot.t.sol @@ -7,12 +7,13 @@ import {Test} from "forge-std/Test.sol"; import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; +// solhint-disable func-name-mixedcase contract StorageSlotTest is Test { using StorageSlot for *; address private _addressVariable; - function testValueAddress1(address value) public { + function testValueAddress_1(address value) public { bytes32 slot; assembly { slot := _addressVariable.slot @@ -25,7 +26,7 @@ contract StorageSlotTest is Test { assertEq(slot.asAddressSlot().sload(), value); } - function testValueAddress2(address value) public { + function testValueAddress_2(address value) public { bytes32 slot; assembly { slot := _addressVariable.slot @@ -40,7 +41,7 @@ contract StorageSlotTest is Test { bytes32 private _bytes32Variable; - function testValueBytes321(bytes32 value) public { + function testValueBytes32_1(bytes32 value) public { bytes32 slot; assembly { slot := _bytes32Variable.slot @@ -53,7 +54,7 @@ contract StorageSlotTest is Test { assertEq(slot.asBytes32Slot().sload(), value); } - function testValueBytes322(bytes32 value) public { + function testValueBytes32_2(bytes32 value) public { bytes32 slot; assembly { slot := _bytes32Variable.slot @@ -68,7 +69,7 @@ contract StorageSlotTest is Test { uint256 private _uint256Variable; - function testValueUint2561(uint256 value) public { + function testValueUint256_1(uint256 value) public { bytes32 slot; assembly { slot := _uint256Variable.slot @@ -81,7 +82,7 @@ contract StorageSlotTest is Test { assertEq(slot.asUint256Slot().sload(), value); } - function testValueUint2562(uint256 value) public { + function testValueUint256_2(uint256 value) public { bytes32 slot; assembly { slot := _uint256Variable.slot @@ -96,7 +97,7 @@ contract StorageSlotTest is Test { int256 private _int256Variable; - function testValueInt2561(int256 value) public { + function testValueInt256_1(int256 value) public { bytes32 slot; assembly { slot := _int256Variable.slot @@ -109,7 +110,7 @@ contract StorageSlotTest is Test { assertEq(slot.asInt256Slot().sload(), value); } - function testValueInt2562(int256 value) public { + function testValueInt256_2(int256 value) public { bytes32 slot; assembly { slot := _int256Variable.slot @@ -124,7 +125,7 @@ contract StorageSlotTest is Test { address[] private _addressArray; - function testArrayAddress1(address[] calldata values) public { + function testArrayAddress_1(address[] calldata values) public { bytes32 slot; assembly { slot := _addressArray.slot @@ -140,7 +141,7 @@ contract StorageSlotTest is Test { } } - function testArrayAddress2(address[] calldata values) public { + function testArrayAddress_2(address[] calldata values) public { bytes32 slot; assembly { slot := _addressArray.slot @@ -161,7 +162,7 @@ contract StorageSlotTest is Test { bytes32[] private _bytes32Array; - function testArrayBytes321(bytes32[] calldata values) public { + function testArrayBytes32_1(bytes32[] calldata values) public { bytes32 slot; assembly { slot := _bytes32Array.slot @@ -177,7 +178,7 @@ contract StorageSlotTest is Test { } } - function testArrayBytes322(bytes32[] calldata values) public { + function testArrayBytes32_2(bytes32[] calldata values) public { bytes32 slot; assembly { slot := _bytes32Array.slot @@ -198,7 +199,7 @@ contract StorageSlotTest is Test { uint256[] private _uint256Array; - function testArrayUint2561(uint256[] calldata values) public { + function testArrayUint256_1(uint256[] calldata values) public { bytes32 slot; assembly { slot := _uint256Array.slot @@ -214,7 +215,7 @@ contract StorageSlotTest is Test { } } - function testArrayUint2562(uint256[] calldata values) public { + function testArrayUint256_2(uint256[] calldata values) public { bytes32 slot; assembly { slot := _uint256Array.slot @@ -235,7 +236,7 @@ contract StorageSlotTest is Test { int256[] private _int256Array; - function testArrayInt2561(int256[] calldata values) public { + function testArrayInt256_1(int256[] calldata values) public { bytes32 slot; assembly { slot := _int256Array.slot @@ -251,7 +252,7 @@ contract StorageSlotTest is Test { } } - function testArrayInt2562(int256[] calldata values) public { + function testArrayInt256_2(int256[] calldata values) public { bytes32 slot; assembly { slot := _int256Array.slot @@ -272,7 +273,7 @@ contract StorageSlotTest is Test { mapping(address => uint256) private _addressMapping; - function testMappingAddress1(address key, uint256 value) public { + function testMappingAddress_1(address key, uint256 value) public { bytes32 slot; assembly { slot := _addressMapping.slot @@ -284,7 +285,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMappingAddress2(address key, uint256 value) public { + function testMappingAddress_2(address key, uint256 value) public { bytes32 slot; assembly { slot := _addressMapping.slot @@ -299,7 +300,7 @@ contract StorageSlotTest is Test { mapping(bool => uint256) private _boolMapping; - function testMappingBoolean1(bool key, uint256 value) public { + function testMappingBoolean_1(bool key, uint256 value) public { bytes32 slot; assembly { slot := _boolMapping.slot @@ -311,7 +312,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMappingBoolean2(bool key, uint256 value) public { + function testMappingBoolean_2(bool key, uint256 value) public { bytes32 slot; assembly { slot := _boolMapping.slot @@ -326,7 +327,7 @@ contract StorageSlotTest is Test { mapping(bytes32 => uint256) private _bytes32Mapping; - function testMappingBytes321(bytes32 key, uint256 value) public { + function testMappingBytes32_1(bytes32 key, uint256 value) public { bytes32 slot; assembly { slot := _bytes32Mapping.slot @@ -338,7 +339,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMappingBytes322(bytes32 key, uint256 value) public { + function testMappingBytes32_2(bytes32 key, uint256 value) public { bytes32 slot; assembly { slot := _bytes32Mapping.slot @@ -351,9 +352,36 @@ contract StorageSlotTest is Test { assertEq(_bytes32Mapping[key], value); } + mapping(bytes4 => uint256) private _bytes4Mapping; + + function testMappingBytes4_1(bytes4 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _bytes4Mapping.slot + } + + // set in solidity + _bytes4Mapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMappingBytes4_2(bytes4 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _bytes4Mapping.slot + } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_bytes4Mapping[key], value); + } + mapping(uint256 => uint256) private _uint256Mapping; - function testMappingUint2561(uint256 key, uint256 value) public { + function testMappingUint256_1(uint256 key, uint256 value) public { bytes32 slot; assembly { slot := _uint256Mapping.slot @@ -365,7 +393,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMappingUint2562(uint256 key, uint256 value) public { + function testMappingUint256_2(uint256 key, uint256 value) public { bytes32 slot; assembly { slot := _uint256Mapping.slot @@ -378,9 +406,36 @@ contract StorageSlotTest is Test { assertEq(_uint256Mapping[key], value); } + mapping(uint32 => uint256) private _uint32Mapping; + + function testMappingUint32_1(uint32 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _uint32Mapping.slot + } + + // set in solidity + _uint32Mapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMappingUint32_2(uint32 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _uint32Mapping.slot + } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_uint32Mapping[key], value); + } + mapping(int256 => uint256) private _int256Mapping; - function testMappingInt2561(int256 key, uint256 value) public { + function testMappingInt256_1(int256 key, uint256 value) public { bytes32 slot; assembly { slot := _int256Mapping.slot @@ -392,7 +447,7 @@ contract StorageSlotTest is Test { assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); } - function testMappingInt2562(int256 key, uint256 value) public { + function testMappingInt256_2(int256 key, uint256 value) public { bytes32 slot; assembly { slot := _int256Mapping.slot @@ -404,4 +459,31 @@ contract StorageSlotTest is Test { // read in solidity assertEq(_int256Mapping[key], value); } + + mapping(int32 => uint256) private _int32Mapping; + + function testMappingInt32_1(int32 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _int32Mapping.slot + } + + // set in solidity + _int32Mapping[key] = value; + // read using Slots + assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); + } + + function testMappingInt32_2(int32 key, uint256 value) public { + bytes32 slot; + assembly { + slot := _int32Mapping.slot + } + + // set using Slots + slot.deriveMapping(key).asUint256Slot().sstore(value); + + // read in solidity + assertEq(_int32Mapping[key], value); + } } From c592c26ca86e9dc2918203d5ba0300ac002eb280 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 Mar 2024 10:15:10 +0100 Subject: [PATCH 09/23] add support for string and bytes keys in mapping derivation --- contracts/utils/StorageSlot.sol | 36 ++ scripts/generate/templates/StorageSlot.js | 22 +- scripts/generate/templates/StorageSlot.t.js | 90 ++--- test/utils/StorageSlot.t.sol | 358 +++++--------------- 4 files changed, 184 insertions(+), 322 deletions(-) diff --git a/contracts/utils/StorageSlot.sol b/contracts/utils/StorageSlot.sol index 46ff2d5f832..d1265e6ac4c 100644 --- a/contracts/utils/StorageSlot.sol +++ b/contracts/utils/StorageSlot.sol @@ -143,6 +143,42 @@ library StorageSlot { } } + /** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ + function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + let length := mload(key) + let begin := add(key, 0x20) + let end := add(begin, length) + let cache := mload(end) + mstore(end, slot) + result := keccak256(begin, add(length, 0x20)) + mstore(end, cache) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ + function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + let length := mload(key) + let begin := add(key, 0x20) + let end := add(begin, length) + let cache := mload(end) + mstore(end, slot) + result := keccak256(begin, add(length, 0x20)) + mstore(end, cache) + } + } + /// Storage slots as structs struct AddressSlot { address value; diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 225da55b6d0..0264be8f4b9 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -91,6 +91,26 @@ function deriveMapping(bytes32 slot, ${type} key) internal pure returns (bytes32 } `; +const derive2 = ({ type }) => `\ +/** + * @dev Derive the location of a mapping element from the key. + * + * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + */ +function deriveMapping(bytes32 slot, ${type} memory key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + let length := mload(key) + let begin := add(key, 0x20) + let end := add(begin, length) + let cache := mload(end) + mstore(end, slot) + result := keccak256(begin, add(length, 0x20)) + mstore(end, cache) + } +} +`; + const struct = ({ type, name }) => `\ struct ${name}Slot { ${type} value; @@ -179,7 +199,7 @@ module.exports = format( 'library StorageSlot {', '/// Derivation tooling', tooling, - TYPES.filter(type => type.isValueType).flatMap(type => derive(type)), // TODO support non-value type + TYPES.flatMap(type => (type.isValueType ? derive(type) : derive2(type))), '/// Storage slots as structs', TYPES.flatMap(type => [struct(type), type.isValueType ? '' : getStorage(type)]), '/// Storage slots as udvt', diff --git a/scripts/generate/templates/StorageSlot.t.js b/scripts/generate/templates/StorageSlot.t.js index 9d9910e6d54..70982a3b548 100644 --- a/scripts/generate/templates/StorageSlot.t.js +++ b/scripts/generate/templates/StorageSlot.t.js @@ -3,7 +3,7 @@ const { capitalize } = require('../../helpers'); const { TYPES } = require('./StorageSlot.opts'); const header = `\ -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import {Test} from "forge-std/Test.sol"; @@ -40,71 +40,46 @@ function testValue${name}_2(${type} value) public { } `; -const array = ({ type, name }) => `\ -${type}[] private _${type}Array; +const array = `\ +bytes[] private _array; -function testArray${name}_1(${type}[] calldata values) public { - bytes32 slot; - assembly { - slot := _${type}Array.slot - } - - // set in solidity - _${type}Array = values; - - // read using Slots - assertEq(slot.asUint256Slot().sload(), values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(slot.deriveArray().offset(i).as${name}Slot().sload(), values[i]); - } -} +function testArray(uint256 length, uint256 offset) public { + length = bound(length, 1, type(uint256).max); + offset = bound(offset, 0, length - 1); -function testArray${name}_2(${type}[] calldata values) public { - bytes32 slot; + bytes32 baseSlot; assembly { - slot := _${type}Array.slot + baseSlot := _array.slot } + baseSlot.asUint256Slot().sstore(length); - // set using Slots - slot.asUint256Slot().sstore(values.length); - for (uint256 i = 0; i < values.length; ++i) { - slot.deriveArray().offset(i).as${name}Slot().sstore(values[i]); + bytes storage derived = _array[offset]; + bytes32 derivedSlot; + assembly { + derivedSlot := derived.slot } - // read in solidity - assertEq(_${type}Array.length, values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(_${type}Array[i], values[i]); - } + assertEq(baseSlot.asUint256Slot().sload(), _array.length); + assertEq(baseSlot.deriveArray().offset(offset), derivedSlot); } `; -const mapping = ({ type, name }) => `\ -mapping(${type} => uint256) private _${type}Mapping; +const mapping = ({ type, name, isValueType }) => `\ +mapping(${type} => bytes) private _${type}Mapping; -function testMapping${name}_1(${type} key, uint256 value) public { - bytes32 slot; +function testMapping${name}(${type} ${isValueType ? '' : 'memory'} key) public { + bytes32 baseSlot; assembly { - slot := _${type}Mapping.slot + baseSlot := _${type}Mapping.slot } - // set in solidity - _${type}Mapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); -} - -function testMapping${name}_2(${type} key, uint256 value) public { - bytes32 slot; + bytes storage derived = _${type}Mapping[key]; + bytes32 derivedSlot; assembly { - slot := _${type}Mapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_${type}Mapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } `; @@ -115,15 +90,18 @@ module.exports = format( 'contract StorageSlotTest is Test {', 'using StorageSlot for *;', '', - // bool is not using a full word, solidity allocation in storage is not right aligned + // bool is not using a full word, solidity allocation packs such values TYPES.filter(type => type.isValueType && type.type !== 'bool').map(type => variable(type)), - // bool is not using a full word, solidity allocation in storage is not right aligned - TYPES.filter(type => type.isValueType && type.type !== 'bool').map(type => array(type)), - TYPES.filter(type => type.isValueType).flatMap(type => + array, + TYPES.flatMap(type => [].concat( - mapping(type), - (type.variants ?? []).map(variant => mapping({ type: variant, name: capitalize(variant) })), + type, + (type.variants ?? []).map(variant => ({ + type: variant, + name: capitalize(variant), + isValueType: type.isValueType, + })), ), - ), + ).map(type => mapping(type)), '}', ); diff --git a/test/utils/StorageSlot.t.sol b/test/utils/StorageSlot.t.sol index 43d6f9dbbcd..ba18942cb1c 100644 --- a/test/utils/StorageSlot.t.sol +++ b/test/utils/StorageSlot.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // This file was procedurally generated from scripts/generate/templates/StorageSlot.t.js. -pragma solidity ^0.8.20; +pragma solidity ^0.8.24; import {Test} from "forge-std/Test.sol"; @@ -123,367 +123,195 @@ contract StorageSlotTest is Test { assertEq(_int256Variable, value); } - address[] private _addressArray; + bytes[] private _array; - function testArrayAddress_1(address[] calldata values) public { - bytes32 slot; - assembly { - slot := _addressArray.slot - } - - // set in solidity - _addressArray = values; - - // read using Slots - assertEq(slot.asUint256Slot().sload(), values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(slot.deriveArray().offset(i).asAddressSlot().sload(), values[i]); - } - } + function testArray(uint256 length, uint256 offset) public { + length = bound(length, 1, type(uint256).max); + offset = bound(offset, 0, length - 1); - function testArrayAddress_2(address[] calldata values) public { - bytes32 slot; + bytes32 baseSlot; assembly { - slot := _addressArray.slot - } - - // set using Slots - slot.asUint256Slot().sstore(values.length); - for (uint256 i = 0; i < values.length; ++i) { - slot.deriveArray().offset(i).asAddressSlot().sstore(values[i]); + baseSlot := _array.slot } + baseSlot.asUint256Slot().sstore(length); - // read in solidity - assertEq(_addressArray.length, values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(_addressArray[i], values[i]); - } - } - - bytes32[] private _bytes32Array; - - function testArrayBytes32_1(bytes32[] calldata values) public { - bytes32 slot; - assembly { - slot := _bytes32Array.slot - } - - // set in solidity - _bytes32Array = values; - - // read using Slots - assertEq(slot.asUint256Slot().sload(), values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(slot.deriveArray().offset(i).asBytes32Slot().sload(), values[i]); - } - } - - function testArrayBytes32_2(bytes32[] calldata values) public { - bytes32 slot; + bytes storage derived = _array[offset]; + bytes32 derivedSlot; assembly { - slot := _bytes32Array.slot + derivedSlot := derived.slot } - // set using Slots - slot.asUint256Slot().sstore(values.length); - for (uint256 i = 0; i < values.length; ++i) { - slot.deriveArray().offset(i).asBytes32Slot().sstore(values[i]); - } - - // read in solidity - assertEq(_bytes32Array.length, values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(_bytes32Array[i], values[i]); - } + assertEq(baseSlot.asUint256Slot().sload(), _array.length); + assertEq(baseSlot.deriveArray().offset(offset), derivedSlot); } - uint256[] private _uint256Array; + mapping(address => bytes) private _addressMapping; - function testArrayUint256_1(uint256[] calldata values) public { - bytes32 slot; + function testMappingAddress(address key) public { + bytes32 baseSlot; assembly { - slot := _uint256Array.slot + baseSlot := _addressMapping.slot } - // set in solidity - _uint256Array = values; - - // read using Slots - assertEq(slot.asUint256Slot().sload(), values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(slot.deriveArray().offset(i).asUint256Slot().sload(), values[i]); - } - } - - function testArrayUint256_2(uint256[] calldata values) public { - bytes32 slot; + bytes storage derived = _addressMapping[key]; + bytes32 derivedSlot; assembly { - slot := _uint256Array.slot + derivedSlot := derived.slot } - // set using Slots - slot.asUint256Slot().sstore(values.length); - for (uint256 i = 0; i < values.length; ++i) { - slot.deriveArray().offset(i).asUint256Slot().sstore(values[i]); - } - - // read in solidity - assertEq(_uint256Array.length, values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(_uint256Array[i], values[i]); - } + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - int256[] private _int256Array; + mapping(bool => bytes) private _boolMapping; - function testArrayInt256_1(int256[] calldata values) public { - bytes32 slot; + function testMappingBoolean(bool key) public { + bytes32 baseSlot; assembly { - slot := _int256Array.slot + baseSlot := _boolMapping.slot } - // set in solidity - _int256Array = values; - - // read using Slots - assertEq(slot.asUint256Slot().sload(), values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(slot.deriveArray().offset(i).asInt256Slot().sload(), values[i]); - } - } - - function testArrayInt256_2(int256[] calldata values) public { - bytes32 slot; + bytes storage derived = _boolMapping[key]; + bytes32 derivedSlot; assembly { - slot := _int256Array.slot - } - - // set using Slots - slot.asUint256Slot().sstore(values.length); - for (uint256 i = 0; i < values.length; ++i) { - slot.deriveArray().offset(i).asInt256Slot().sstore(values[i]); + derivedSlot := derived.slot } - // read in solidity - assertEq(_int256Array.length, values.length); - for (uint256 i = 0; i < values.length; ++i) { - assertEq(_int256Array[i], values[i]); - } + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - mapping(address => uint256) private _addressMapping; + mapping(bytes32 => bytes) private _bytes32Mapping; - function testMappingAddress_1(address key, uint256 value) public { - bytes32 slot; + function testMappingBytes32(bytes32 key) public { + bytes32 baseSlot; assembly { - slot := _addressMapping.slot + baseSlot := _bytes32Mapping.slot } - // set in solidity - _addressMapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); - } - - function testMappingAddress_2(address key, uint256 value) public { - bytes32 slot; + bytes storage derived = _bytes32Mapping[key]; + bytes32 derivedSlot; assembly { - slot := _addressMapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_addressMapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - mapping(bool => uint256) private _boolMapping; + mapping(bytes4 => bytes) private _bytes4Mapping; - function testMappingBoolean_1(bool key, uint256 value) public { - bytes32 slot; + function testMappingBytes4(bytes4 key) public { + bytes32 baseSlot; assembly { - slot := _boolMapping.slot + baseSlot := _bytes4Mapping.slot } - // set in solidity - _boolMapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); - } - - function testMappingBoolean_2(bool key, uint256 value) public { - bytes32 slot; + bytes storage derived = _bytes4Mapping[key]; + bytes32 derivedSlot; assembly { - slot := _boolMapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_boolMapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - mapping(bytes32 => uint256) private _bytes32Mapping; + mapping(uint256 => bytes) private _uint256Mapping; - function testMappingBytes32_1(bytes32 key, uint256 value) public { - bytes32 slot; + function testMappingUint256(uint256 key) public { + bytes32 baseSlot; assembly { - slot := _bytes32Mapping.slot + baseSlot := _uint256Mapping.slot } - // set in solidity - _bytes32Mapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); - } - - function testMappingBytes32_2(bytes32 key, uint256 value) public { - bytes32 slot; + bytes storage derived = _uint256Mapping[key]; + bytes32 derivedSlot; assembly { - slot := _bytes32Mapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_bytes32Mapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - mapping(bytes4 => uint256) private _bytes4Mapping; + mapping(uint32 => bytes) private _uint32Mapping; - function testMappingBytes4_1(bytes4 key, uint256 value) public { - bytes32 slot; + function testMappingUint32(uint32 key) public { + bytes32 baseSlot; assembly { - slot := _bytes4Mapping.slot + baseSlot := _uint32Mapping.slot } - // set in solidity - _bytes4Mapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); - } - - function testMappingBytes4_2(bytes4 key, uint256 value) public { - bytes32 slot; + bytes storage derived = _uint32Mapping[key]; + bytes32 derivedSlot; assembly { - slot := _bytes4Mapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_bytes4Mapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - mapping(uint256 => uint256) private _uint256Mapping; + mapping(int256 => bytes) private _int256Mapping; - function testMappingUint256_1(uint256 key, uint256 value) public { - bytes32 slot; + function testMappingInt256(int256 key) public { + bytes32 baseSlot; assembly { - slot := _uint256Mapping.slot + baseSlot := _int256Mapping.slot } - // set in solidity - _uint256Mapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); - } - - function testMappingUint256_2(uint256 key, uint256 value) public { - bytes32 slot; + bytes storage derived = _int256Mapping[key]; + bytes32 derivedSlot; assembly { - slot := _uint256Mapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_uint256Mapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - mapping(uint32 => uint256) private _uint32Mapping; + mapping(int32 => bytes) private _int32Mapping; - function testMappingUint32_1(uint32 key, uint256 value) public { - bytes32 slot; + function testMappingInt32(int32 key) public { + bytes32 baseSlot; assembly { - slot := _uint32Mapping.slot + baseSlot := _int32Mapping.slot } - // set in solidity - _uint32Mapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); - } - - function testMappingUint32_2(uint32 key, uint256 value) public { - bytes32 slot; + bytes storage derived = _int32Mapping[key]; + bytes32 derivedSlot; assembly { - slot := _uint32Mapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_uint32Mapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - mapping(int256 => uint256) private _int256Mapping; + mapping(string => bytes) private _stringMapping; - function testMappingInt256_1(int256 key, uint256 value) public { - bytes32 slot; + function testMappingString(string memory key) public { + bytes32 baseSlot; assembly { - slot := _int256Mapping.slot + baseSlot := _stringMapping.slot } - // set in solidity - _int256Mapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); - } - - function testMappingInt256_2(int256 key, uint256 value) public { - bytes32 slot; + bytes storage derived = _stringMapping[key]; + bytes32 derivedSlot; assembly { - slot := _int256Mapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_int256Mapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } - mapping(int32 => uint256) private _int32Mapping; + mapping(bytes => bytes) private _bytesMapping; - function testMappingInt32_1(int32 key, uint256 value) public { - bytes32 slot; + function testMappingBytes(bytes memory key) public { + bytes32 baseSlot; assembly { - slot := _int32Mapping.slot + baseSlot := _bytesMapping.slot } - // set in solidity - _int32Mapping[key] = value; - // read using Slots - assertEq(slot.deriveMapping(key).asUint256Slot().sload(), value); - } - - function testMappingInt32_2(int32 key, uint256 value) public { - bytes32 slot; + bytes storage derived = _bytesMapping[key]; + bytes32 derivedSlot; assembly { - slot := _int32Mapping.slot + derivedSlot := derived.slot } - // set using Slots - slot.deriveMapping(key).asUint256Slot().sstore(value); - - // read in solidity - assertEq(_int32Mapping[key], value); + assertEq(baseSlot.deriveMapping(key), derivedSlot); } } From 7c5b4c636688f7a2d68d76e55eab197615369687 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 19 Mar 2024 23:30:27 +0100 Subject: [PATCH 10/23] apply recommandations for PR --- contracts/utils/StorageSlot.sol | 28 +++++++++++------------ scripts/generate/templates/StorageSlot.js | 18 +++++++-------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/contracts/utils/StorageSlot.sol b/contracts/utils/StorageSlot.sol index d1265e6ac4c..f0d73f3ef0a 100644 --- a/contracts/utils/StorageSlot.sol +++ b/contracts/utils/StorageSlot.sol @@ -31,22 +31,22 @@ pragma solidity ^0.8.24; library StorageSlot { /// Derivation tooling /** - * @dev Derive an ERC-1967 slot from a string (path). + * @dev Derive an ERC-1967 slot from a string (namespace). */ - function erc1967slot(string memory path) internal pure returns (bytes32 slot) { + function erc1967slot(string memory namespace) internal pure returns (bytes32 slot) { /// @solidity memory-safe-assembly assembly { - slot := sub(keccak256(add(path, 0x20), mload(path)), 1) + slot := sub(keccak256(add(namespace, 0x20), mload(namespace)), 1) } } /** - * @dev Derive an ERC-7201 slot from a string (path). + * @dev Derive an ERC-7201 slot from a string (namespace). */ - function erc7201slot(string memory path) internal pure returns (bytes32 slot) { + function erc7201slot(string memory namespace) internal pure returns (bytes32 slot) { /// @solidity memory-safe-assembly assembly { - mstore(0x00, sub(keccak256(add(path, 0x20), mload(path)), 1)) + mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1)) slot := and(keccak256(0x00, 0x20), not(0xff)) } } @@ -63,7 +63,7 @@ library StorageSlot { /** * @dev Derive the location of the first element in an array from the slot where the length is stored. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveArray(bytes32 slot) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -76,7 +76,7 @@ library StorageSlot { /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -90,7 +90,7 @@ library StorageSlot { /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -104,7 +104,7 @@ library StorageSlot { /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -118,7 +118,7 @@ library StorageSlot { /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -132,7 +132,7 @@ library StorageSlot { /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -146,7 +146,7 @@ library StorageSlot { /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -164,7 +164,7 @@ library StorageSlot { /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 0264be8f4b9..3aa7f024fcc 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -32,22 +32,22 @@ pragma solidity ^0.8.24; const tooling = `\ /** - * @dev Derive an ERC-1967 slot from a string (path). + * @dev Derive an ERC-1967 slot from a string (namespace). */ -function erc1967slot(string memory path) internal pure returns (bytes32 slot) { +function erc1967slot(string memory namespace) internal pure returns (bytes32 slot) { /// @solidity memory-safe-assembly assembly { - slot := sub(keccak256(add(path, 0x20), mload(path)), 1) + slot := sub(keccak256(add(namespace, 0x20), mload(namespace)), 1) } } /** - * @dev Derive an ERC-7201 slot from a string (path). + * @dev Derive an ERC-7201 slot from a string (namespace). */ -function erc7201slot(string memory path) internal pure returns (bytes32 slot) { +function erc7201slot(string memory namespace) internal pure returns (bytes32 slot) { /// @solidity memory-safe-assembly assembly { - mstore(0x00, sub(keccak256(add(path, 0x20), mload(path)), 1)) + mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1)) slot := and(keccak256(0x00, 0x20), not(0xff)) } } @@ -64,7 +64,7 @@ function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result /** * @dev Derive the location of the first element in an array from the slot where the length is stored. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveArray(bytes32 slot) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -79,7 +79,7 @@ const derive = ({ type }) => `\ /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, ${type} key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly @@ -95,7 +95,7 @@ const derive2 = ({ type }) => `\ /** * @dev Derive the location of a mapping element from the key. * - * See: https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. */ function deriveMapping(bytes32 slot, ${type} memory key) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly From 88b3c191b7d55571d75eb44810ecd2d234caca2a Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 15:31:25 +0100 Subject: [PATCH 11/23] divide into 3 libraries --- contracts/mocks/StorageSlotMock.sol | 8 - contracts/utils/Arrays.sol | 2 + contracts/utils/SlotDerivation.sol | 146 ++++++ contracts/utils/StorageSlot.sol | 414 +----------------- contracts/utils/TransientSlot.sol | 189 ++++++++ hardhat.config.js | 2 +- scripts/generate/run.js | 4 +- .../{StorageSlot.opts.js => Slot.opts.js} | 0 scripts/generate/templates/SlotDerivation.js | 101 +++++ .../{StorageSlot.t.js => SlotDerivation.t.js} | 49 +-- scripts/generate/templates/StorageSlot.js | 145 +----- scripts/generate/templates/TransientSlot.js | 100 +++++ ...StorageSlot.t.sol => SlotDerivation.t.sol} | 147 +------ test/utils/SlotDerivation.test.js | 28 ++ test/utils/StorageSlot.test.js | 13 - 15 files changed, 598 insertions(+), 750 deletions(-) create mode 100644 contracts/utils/SlotDerivation.sol create mode 100644 contracts/utils/TransientSlot.sol rename scripts/generate/templates/{StorageSlot.opts.js => Slot.opts.js} (100%) create mode 100644 scripts/generate/templates/SlotDerivation.js rename scripts/generate/templates/{StorageSlot.t.js => SlotDerivation.t.js} (51%) create mode 100644 scripts/generate/templates/TransientSlot.js rename test/utils/{StorageSlot.t.sol => SlotDerivation.t.sol} (55%) create mode 100644 test/utils/SlotDerivation.test.js diff --git a/contracts/mocks/StorageSlotMock.sol b/contracts/mocks/StorageSlotMock.sol index 2417a171f60..c02378b4688 100644 --- a/contracts/mocks/StorageSlotMock.sol +++ b/contracts/mocks/StorageSlotMock.sol @@ -7,14 +7,6 @@ import {StorageSlot} from "../utils/StorageSlot.sol"; contract StorageSlotMock { using StorageSlot for *; - function erc1967slot(string memory path) public pure returns (bytes32) { - return path.erc1967slot(); - } - - function erc7201slot(string memory path) public pure returns (bytes32) { - return path.erc7201slot(); - } - function setBooleanSlot(bytes32 slot, bool value) public { slot.getBooleanSlot().value = value; } diff --git a/contracts/utils/Arrays.sol b/contracts/utils/Arrays.sol index 92d8dcbf626..74233235d39 100644 --- a/contracts/utils/Arrays.sol +++ b/contracts/utils/Arrays.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; +import {SlotDerivation} from "./SlotDerivation.sol"; import {StorageSlot} from "./StorageSlot.sol"; import {Math} from "./math/Math.sol"; @@ -10,6 +11,7 @@ import {Math} from "./math/Math.sol"; * @dev Collection of functions related to array types. */ library Arrays { + using SlotDerivation for bytes32; using StorageSlot for bytes32; /** diff --git a/contracts/utils/SlotDerivation.sol b/contracts/utils/SlotDerivation.sol new file mode 100644 index 00000000000..2f4e8b3f476 --- /dev/null +++ b/contracts/utils/SlotDerivation.sol @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: MIT +// This file was procedurally generated from scripts/generate/templates/SlotDerivation.js. + +pragma solidity ^0.8.20; + +/** + * @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots + * corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by + * the solidity language / compiler. + * + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. + */ +library SlotDerivation { + /** + * @dev Derive an ERC-1967 slot from a string (namespace). + */ + function erc1967slot(string memory namespace) internal pure returns (bytes32 slot) { + /// @solidity memory-safe-assembly + assembly { + slot := sub(keccak256(add(namespace, 0x20), mload(namespace)), 1) + } + } + + /** + * @dev Derive an ERC-7201 slot from a string (namespace). + */ + function erc7201slot(string memory namespace) internal pure returns (bytes32 slot) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1)) + slot := and(keccak256(0x00, 0x20), not(0xff)) + } + } + + /** + * @dev Add an offset to a slot to get the n-th element of a structure or an array. + */ + function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) { + unchecked { + return bytes32(uint256(slot) + pos); + } + } + + /** + * @dev Derive the location of the first element in an array from the slot where the length is stored. + */ + function deriveArray(bytes32 slot) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, slot) + result := keccak256(0x00, 0x20) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + */ + function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + */ + function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + */ + function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + */ + function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + */ + function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + */ + function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + let length := mload(key) + let begin := add(key, 0x20) + let end := add(begin, length) + let cache := mload(end) + mstore(end, slot) + result := keccak256(begin, add(length, 0x20)) + mstore(end, cache) + } + } + + /** + * @dev Derive the location of a mapping element from the key. + */ + function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + let length := mload(key) + let begin := add(key, 0x20) + let end := add(begin, length) + let cache := mload(end) + mstore(end, slot) + result := keccak256(begin, add(length, 0x20)) + mstore(end, cache) + } + } +} diff --git a/contracts/utils/StorageSlot.sol b/contracts/utils/StorageSlot.sol index f0d73f3ef0a..db004540105 100644 --- a/contracts/utils/StorageSlot.sol +++ b/contracts/utils/StorageSlot.sol @@ -2,7 +2,7 @@ // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. @@ -29,157 +29,6 @@ pragma solidity ^0.8.24; * ``` */ library StorageSlot { - /// Derivation tooling - /** - * @dev Derive an ERC-1967 slot from a string (namespace). - */ - function erc1967slot(string memory namespace) internal pure returns (bytes32 slot) { - /// @solidity memory-safe-assembly - assembly { - slot := sub(keccak256(add(namespace, 0x20), mload(namespace)), 1) - } - } - - /** - * @dev Derive an ERC-7201 slot from a string (namespace). - */ - function erc7201slot(string memory namespace) internal pure returns (bytes32 slot) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1)) - slot := and(keccak256(0x00, 0x20), not(0xff)) - } - } - - /** - * @dev Add an offset to a slot to get the n-th element of a structure or an array. - */ - function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) { - unchecked { - return bytes32(uint256(slot) + pos); - } - } - - /** - * @dev Derive the location of the first element in an array from the slot where the length is stored. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ - function deriveArray(bytes32 slot) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, slot) - result := keccak256(0x00, 0x20) - } - } - - /** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ - function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, key) - mstore(0x20, slot) - result := keccak256(0x00, 0x40) - } - } - - /** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ - function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, key) - mstore(0x20, slot) - result := keccak256(0x00, 0x40) - } - } - - /** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ - function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, key) - mstore(0x20, slot) - result := keccak256(0x00, 0x40) - } - } - - /** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ - function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, key) - mstore(0x20, slot) - result := keccak256(0x00, 0x40) - } - } - - /** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ - function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, key) - mstore(0x20, slot) - result := keccak256(0x00, 0x40) - } - } - - /** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ - function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - let length := mload(key) - let begin := add(key, 0x20) - let end := add(begin, length) - let cache := mload(end) - mstore(end, slot) - result := keccak256(begin, add(length, 0x20)) - mstore(end, cache) - } - } - - /** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ - function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - let length := mload(key) - let begin := add(key, 0x20) - let end := add(begin, length) - let cache := mload(end) - mstore(end, slot) - result := keccak256(begin, add(length, 0x20)) - mstore(end, cache) - } - } - - /// Storage slots as structs struct AddressSlot { address value; } @@ -297,265 +146,4 @@ library StorageSlot { r.slot := store.slot } } - - /// Storage slots as udvt - /** - * @dev UDVT that represent a slot holding a address. - */ - type AddressSlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a AddressSlotType. - */ - function asAddressSlot(bytes32 slot) internal pure returns (AddressSlotType) { - return AddressSlotType.wrap(slot); - } - - /** - * @dev Load the value held at location `slot` in (normal) storage. - */ - function sload(AddressSlotType slot) internal view returns (address value) { - /// @solidity memory-safe-assembly - assembly { - value := sload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in (normal) storage. - */ - function sstore(AddressSlotType slot, address value) internal { - /// @solidity memory-safe-assembly - assembly { - sstore(slot, value) - } - } - - /** - * @dev Load the value held at location `slot` in transient storage. - */ - function tload(AddressSlotType slot) internal view returns (address value) { - /// @solidity memory-safe-assembly - assembly { - value := tload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in transient storage. - */ - function tstore(AddressSlotType slot, address value) internal { - /// @solidity memory-safe-assembly - assembly { - tstore(slot, value) - } - } - - /** - * @dev UDVT that represent a slot holding a bool. - */ - type BooleanSlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a BooleanSlotType. - */ - function asBooleanSlot(bytes32 slot) internal pure returns (BooleanSlotType) { - return BooleanSlotType.wrap(slot); - } - - /** - * @dev Load the value held at location `slot` in (normal) storage. - */ - function sload(BooleanSlotType slot) internal view returns (bool value) { - /// @solidity memory-safe-assembly - assembly { - value := sload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in (normal) storage. - */ - function sstore(BooleanSlotType slot, bool value) internal { - /// @solidity memory-safe-assembly - assembly { - sstore(slot, value) - } - } - - /** - * @dev Load the value held at location `slot` in transient storage. - */ - function tload(BooleanSlotType slot) internal view returns (bool value) { - /// @solidity memory-safe-assembly - assembly { - value := tload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in transient storage. - */ - function tstore(BooleanSlotType slot, bool value) internal { - /// @solidity memory-safe-assembly - assembly { - tstore(slot, value) - } - } - - /** - * @dev UDVT that represent a slot holding a bytes32. - */ - type Bytes32SlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a Bytes32SlotType. - */ - function asBytes32Slot(bytes32 slot) internal pure returns (Bytes32SlotType) { - return Bytes32SlotType.wrap(slot); - } - - /** - * @dev Load the value held at location `slot` in (normal) storage. - */ - function sload(Bytes32SlotType slot) internal view returns (bytes32 value) { - /// @solidity memory-safe-assembly - assembly { - value := sload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in (normal) storage. - */ - function sstore(Bytes32SlotType slot, bytes32 value) internal { - /// @solidity memory-safe-assembly - assembly { - sstore(slot, value) - } - } - - /** - * @dev Load the value held at location `slot` in transient storage. - */ - function tload(Bytes32SlotType slot) internal view returns (bytes32 value) { - /// @solidity memory-safe-assembly - assembly { - value := tload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in transient storage. - */ - function tstore(Bytes32SlotType slot, bytes32 value) internal { - /// @solidity memory-safe-assembly - assembly { - tstore(slot, value) - } - } - - /** - * @dev UDVT that represent a slot holding a uint256. - */ - type Uint256SlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a Uint256SlotType. - */ - function asUint256Slot(bytes32 slot) internal pure returns (Uint256SlotType) { - return Uint256SlotType.wrap(slot); - } - - /** - * @dev Load the value held at location `slot` in (normal) storage. - */ - function sload(Uint256SlotType slot) internal view returns (uint256 value) { - /// @solidity memory-safe-assembly - assembly { - value := sload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in (normal) storage. - */ - function sstore(Uint256SlotType slot, uint256 value) internal { - /// @solidity memory-safe-assembly - assembly { - sstore(slot, value) - } - } - - /** - * @dev Load the value held at location `slot` in transient storage. - */ - function tload(Uint256SlotType slot) internal view returns (uint256 value) { - /// @solidity memory-safe-assembly - assembly { - value := tload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in transient storage. - */ - function tstore(Uint256SlotType slot, uint256 value) internal { - /// @solidity memory-safe-assembly - assembly { - tstore(slot, value) - } - } - - /** - * @dev UDVT that represent a slot holding a int256. - */ - type Int256SlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a Int256SlotType. - */ - function asInt256Slot(bytes32 slot) internal pure returns (Int256SlotType) { - return Int256SlotType.wrap(slot); - } - - /** - * @dev Load the value held at location `slot` in (normal) storage. - */ - function sload(Int256SlotType slot) internal view returns (int256 value) { - /// @solidity memory-safe-assembly - assembly { - value := sload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in (normal) storage. - */ - function sstore(Int256SlotType slot, int256 value) internal { - /// @solidity memory-safe-assembly - assembly { - sstore(slot, value) - } - } - - /** - * @dev Load the value held at location `slot` in transient storage. - */ - function tload(Int256SlotType slot) internal view returns (int256 value) { - /// @solidity memory-safe-assembly - assembly { - value := tload(slot) - } - } - - /** - * @dev Store `value` at location `slot` in transient storage. - */ - function tstore(Int256SlotType slot, int256 value) internal { - /// @solidity memory-safe-assembly - assembly { - tstore(slot, value) - } - } } diff --git a/contracts/utils/TransientSlot.sol b/contracts/utils/TransientSlot.sol new file mode 100644 index 00000000000..4d7240d542b --- /dev/null +++ b/contracts/utils/TransientSlot.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT +// This file was procedurally generated from scripts/generate/templates/TransientSlot.js. + +pragma solidity ^0.8.24; + +/** + * @dev Library for reading and writing primitive types to specific storage slots. This is a variant of {StorageSlot} + * that supports transient storage. + * + * The functions in this library return types that give access to reading or writting primitives. + * + * Example usage: + * ```solidity + * contract ReentrancyGuard { + * using TransientSlot for *; + * + * bytes32 internal constant _REENTRANCY_SLOT = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + * + * modifier nonReentrant() { + * require(!_REENTRANCY_SLOT.asBooleanSlot().tload()); + * + * _REENTRANCY_SLOT.asBooleanSlot().tstore(true); + * _; + * _REENTRANCY_SLOT.asBooleanSlot().tstore(false); + * } + * } + * ``` + */ +library TransientSlot { + /** + * @dev UDVT that represent a slot holding a address. + */ + type AddressSlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a AddressSlotType. + */ + function asAddressSlot(bytes32 slot) internal pure returns (AddressSlotType) { + return AddressSlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(AddressSlotType slot) internal view returns (address value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(AddressSlotType slot, address value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } + + /** + * @dev UDVT that represent a slot holding a bool. + */ + type BooleanSlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a BooleanSlotType. + */ + function asBooleanSlot(bytes32 slot) internal pure returns (BooleanSlotType) { + return BooleanSlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(BooleanSlotType slot) internal view returns (bool value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(BooleanSlotType slot, bool value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } + + /** + * @dev UDVT that represent a slot holding a bytes32. + */ + type Bytes32SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Bytes32SlotType. + */ + function asBytes32Slot(bytes32 slot) internal pure returns (Bytes32SlotType) { + return Bytes32SlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(Bytes32SlotType slot) internal view returns (bytes32 value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(Bytes32SlotType slot, bytes32 value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } + + /** + * @dev UDVT that represent a slot holding a uint256. + */ + type Uint256SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Uint256SlotType. + */ + function asUint256Slot(bytes32 slot) internal pure returns (Uint256SlotType) { + return Uint256SlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(Uint256SlotType slot) internal view returns (uint256 value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(Uint256SlotType slot, uint256 value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } + + /** + * @dev UDVT that represent a slot holding a int256. + */ + type Int256SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Int256SlotType. + */ + function asInt256Slot(bytes32 slot) internal pure returns (Int256SlotType) { + return Int256SlotType.wrap(slot); + } + + /** + * @dev Load the value held at location `slot` in transient storage. + */ + function tload(Int256SlotType slot) internal view returns (int256 value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } + } + + /** + * @dev Store `value` at location `slot` in transient storage. + */ + function tstore(Int256SlotType slot, int256 value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } + } +} diff --git a/hardhat.config.js b/hardhat.config.js index 62165db4dfb..9bfc5e3d496 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -110,7 +110,7 @@ module.exports = { exposed: { imports: true, initializers: true, - exclude: ['vendor/**/*', '**/*WithInit.sol', '**/utils/StorageSlot.sol'], + exclude: ['vendor/**/*', '**/*WithInit.sol', 'utils/TransientSlot.sol'], }, gasReporter: { enabled: argv.gas, diff --git a/scripts/generate/run.js b/scripts/generate/run.js index 2f4cbdc119d..7e49b92edd1 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -36,7 +36,9 @@ for (const [file, template] of Object.entries({ 'utils/structs/EnumerableSet.sol': './templates/EnumerableSet.js', 'utils/structs/EnumerableMap.sol': './templates/EnumerableMap.js', 'utils/structs/Checkpoints.sol': './templates/Checkpoints.js', + 'utils/SlotDerivation.sol': './templates/SlotDerivation.js', 'utils/StorageSlot.sol': './templates/StorageSlot.js', + 'utils/TransientSlot.sol': './templates/TransientSlot.js', })) { generateFromTemplate(file, template, './contracts/'); } @@ -44,7 +46,7 @@ for (const [file, template] of Object.entries({ // Tests for (const [file, template] of Object.entries({ 'utils/structs/Checkpoints.t.sol': './templates/Checkpoints.t.js', - 'utils/StorageSlot.t.sol': './templates/StorageSlot.t.js', + 'utils/SlotDerivation.t.sol': './templates/SlotDerivation.t.js', })) { generateFromTemplate(file, template, './test/'); } diff --git a/scripts/generate/templates/StorageSlot.opts.js b/scripts/generate/templates/Slot.opts.js similarity index 100% rename from scripts/generate/templates/StorageSlot.opts.js rename to scripts/generate/templates/Slot.opts.js diff --git a/scripts/generate/templates/SlotDerivation.js b/scripts/generate/templates/SlotDerivation.js new file mode 100644 index 00000000000..a7ecbfe1235 --- /dev/null +++ b/scripts/generate/templates/SlotDerivation.js @@ -0,0 +1,101 @@ +const format = require('../format-lines'); +const { TYPES } = require('./Slot.opts'); + +const header = `\ +pragma solidity ^0.8.20; + +/** + * @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots + * corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by + * the solidity language / compiler. + * + * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. + */ +`; + +const namespace = `\ +/** + * @dev Derive an ERC-1967 slot from a string (namespace). + */ +function erc1967slot(string memory namespace) internal pure returns (bytes32 slot) { + /// @solidity memory-safe-assembly + assembly { + slot := sub(keccak256(add(namespace, 0x20), mload(namespace)), 1) + } +} + +/** + * @dev Derive an ERC-7201 slot from a string (namespace). + */ +function erc7201slot(string memory namespace) internal pure returns (bytes32 slot) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1)) + slot := and(keccak256(0x00, 0x20), not(0xff)) + } +} +`; + +const array = `\ +/** + * @dev Add an offset to a slot to get the n-th element of a structure or an array. + */ +function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) { + unchecked { + return bytes32(uint256(slot) + pos); + } +} + +/** + * @dev Derive the location of the first element in an array from the slot where the length is stored. + */ +function deriveArray(bytes32 slot) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, slot) + result := keccak256(0x00, 0x20) + } +} +`; + +const mapping = ({ type }) => `\ +/** + * @dev Derive the location of a mapping element from the key. + */ +function deriveMapping(bytes32 slot, ${type} key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, key) + mstore(0x20, slot) + result := keccak256(0x00, 0x40) + } +} +`; + +const mapping2 = ({ type }) => `\ +/** + * @dev Derive the location of a mapping element from the key. + */ +function deriveMapping(bytes32 slot, ${type} memory key) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + let length := mload(key) + let begin := add(key, 0x20) + let end := add(begin, length) + let cache := mload(end) + mstore(end, slot) + result := keccak256(begin, add(length, 0x20)) + mstore(end, cache) + } +} +`; + +// GENERATE +module.exports = format( + header.trimEnd(), + 'library SlotDerivation {', + namespace, + array, + TYPES.flatMap(type => (type.isValueType ? mapping(type) : mapping2(type))), + '}', +); diff --git a/scripts/generate/templates/StorageSlot.t.js b/scripts/generate/templates/SlotDerivation.t.js similarity index 51% rename from scripts/generate/templates/StorageSlot.t.js rename to scripts/generate/templates/SlotDerivation.t.js index 70982a3b548..9a570053fa6 100644 --- a/scripts/generate/templates/StorageSlot.t.js +++ b/scripts/generate/templates/SlotDerivation.t.js @@ -1,57 +1,27 @@ const format = require('../format-lines'); const { capitalize } = require('../../helpers'); -const { TYPES } = require('./StorageSlot.opts'); +const { TYPES } = require('./Slot.opts'); const header = `\ -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; -`; - -const variable = ({ type, name }) => `\ -${type} private _${type}Variable; - -function testValue${name}_1(${type} value) public { - bytes32 slot; - assembly { - slot := _${type}Variable.slot - } - - // set in solidity - _${type}Variable = value; - - // read using Slots - assertEq(slot.as${name}Slot().sload(), value); -} - -function testValue${name}_2(${type} value) public { - bytes32 slot; - assembly { - slot := _${type}Variable.slot - } - - // set using Slots - slot.as${name}Slot().sstore(value); - - // read in solidity - assertEq(_${type}Variable, value); -} +import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; `; const array = `\ bytes[] private _array; -function testArray(uint256 length, uint256 offset) public { +function testDeriveArray(uint256 length, uint256 offset) public { length = bound(length, 1, type(uint256).max); offset = bound(offset, 0, length - 1); bytes32 baseSlot; assembly { baseSlot := _array.slot + sstore(baseSlot, length) // store length so solidity access does not revert } - baseSlot.asUint256Slot().sstore(length); bytes storage derived = _array[offset]; bytes32 derivedSlot; @@ -59,7 +29,6 @@ function testArray(uint256 length, uint256 offset) public { derivedSlot := derived.slot } - assertEq(baseSlot.asUint256Slot().sload(), _array.length); assertEq(baseSlot.deriveArray().offset(offset), derivedSlot); } `; @@ -67,7 +36,7 @@ function testArray(uint256 length, uint256 offset) public { const mapping = ({ type, name, isValueType }) => `\ mapping(${type} => bytes) private _${type}Mapping; -function testMapping${name}(${type} ${isValueType ? '' : 'memory'} key) public { +function testDeriveMapping${name}(${type} ${isValueType ? '' : 'memory'} key) public { bytes32 baseSlot; assembly { baseSlot := _${type}Mapping.slot @@ -87,11 +56,9 @@ function testMapping${name}(${type} ${isValueType ? '' : 'memory'} key) public { module.exports = format( header.trimEnd(), '// solhint-disable func-name-mixedcase', - 'contract StorageSlotTest is Test {', - 'using StorageSlot for *;', + 'contract SlotDerivationTest is Test {', + 'using SlotDerivation for bytes32;', '', - // bool is not using a full word, solidity allocation packs such values - TYPES.filter(type => type.isValueType && type.type !== 'bool').map(type => variable(type)), array, TYPES.flatMap(type => [].concat( diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 3aa7f024fcc..65aaa67dccf 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -1,8 +1,8 @@ const format = require('../format-lines'); -const { TYPES } = require('./StorageSlot.opts'); +const { TYPES } = require('./Slot.opts'); const header = `\ -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. @@ -30,87 +30,6 @@ pragma solidity ^0.8.24; */ `; -const tooling = `\ -/** - * @dev Derive an ERC-1967 slot from a string (namespace). - */ -function erc1967slot(string memory namespace) internal pure returns (bytes32 slot) { - /// @solidity memory-safe-assembly - assembly { - slot := sub(keccak256(add(namespace, 0x20), mload(namespace)), 1) - } -} - -/** - * @dev Derive an ERC-7201 slot from a string (namespace). - */ -function erc7201slot(string memory namespace) internal pure returns (bytes32 slot) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1)) - slot := and(keccak256(0x00, 0x20), not(0xff)) - } -} - -/** - * @dev Add an offset to a slot to get the n-th element of a structure or an array. - */ -function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) { - unchecked { - return bytes32(uint256(slot) + pos); - } -} - -/** - * @dev Derive the location of the first element in an array from the slot where the length is stored. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ -function deriveArray(bytes32 slot) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, slot) - result := keccak256(0x00, 0x20) - } -} -`; - -const derive = ({ type }) => `\ -/** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ -function deriveMapping(bytes32 slot, ${type} key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, key) - mstore(0x20, slot) - result := keccak256(0x00, 0x40) - } -} -`; - -const derive2 = ({ type }) => `\ -/** - * @dev Derive the location of a mapping element from the key. - * - * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.]. - */ -function deriveMapping(bytes32 slot, ${type} memory key) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - let length := mload(key) - let begin := add(key, 0x20) - let end := add(begin, length) - let cache := mload(end) - mstore(end, slot) - result := keccak256(begin, add(length, 0x20)) - mstore(end, cache) - } -} -`; - const struct = ({ type, name }) => `\ struct ${name}Slot { ${type} value; @@ -139,70 +58,10 @@ function get${name}Slot(${type} storage store) internal pure returns (${name}Slo } `; -const udvt = ({ type, name }) => `\ -/** - * @dev UDVT that represent a slot holding a ${type}. - */ -type ${name}SlotType is bytes32; - -/** - * @dev Cast an arbitrary slot to a ${name}SlotType. - */ -function as${name}Slot(bytes32 slot) internal pure returns (${name}SlotType) { - return ${name}SlotType.wrap(slot); -} - -/** - * @dev Load the value held at location \`slot\` in (normal) storage. - */ -function sload(${name}SlotType slot) internal view returns (${type} value) { - /// @solidity memory-safe-assembly - assembly { - value := sload(slot) - } -} - -/** - * @dev Store \`value\` at location \`slot\` in (normal) storage. - */ -function sstore(${name}SlotType slot, ${type} value) internal { - /// @solidity memory-safe-assembly - assembly { - sstore(slot, value) - } -} - -/** - * @dev Load the value held at location \`slot\` in transient storage. - */ -function tload(${name}SlotType slot) internal view returns (${type} value) { - /// @solidity memory-safe-assembly - assembly { - value := tload(slot) - } -} - -/** - * @dev Store \`value\` at location \`slot\` in transient storage. - */ -function tstore(${name}SlotType slot, ${type} value) internal { - /// @solidity memory-safe-assembly - assembly { - tstore(slot, value) - } -} -`; - // GENERATE module.exports = format( header.trimEnd(), 'library StorageSlot {', - '/// Derivation tooling', - tooling, - TYPES.flatMap(type => (type.isValueType ? derive(type) : derive2(type))), - '/// Storage slots as structs', TYPES.flatMap(type => [struct(type), type.isValueType ? '' : getStorage(type)]), - '/// Storage slots as udvt', - TYPES.filter(type => type.isValueType).map(type => udvt(type)), '}', ); diff --git a/scripts/generate/templates/TransientSlot.js b/scripts/generate/templates/TransientSlot.js new file mode 100644 index 00000000000..4db1bae0f27 --- /dev/null +++ b/scripts/generate/templates/TransientSlot.js @@ -0,0 +1,100 @@ +const format = require('../format-lines'); +const { TYPES } = require('./Slot.opts'); + +const header = `\ +pragma solidity ^0.8.24; + +/** + * @dev Library for reading and writing primitive types to specific storage slots. This is a variant of {StorageSlot} + * that supports transient storage. + * + * The functions in this library return types that give access to reading or writting primitives. + * + * Example usage: + * \`\`\`solidity + * contract ReentrancyGuard { + * using TransientSlot for *; + * + * bytes32 internal constant _REENTRANCY_SLOT = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + * + * modifier nonReentrant() { + * require(!_REENTRANCY_SLOT.asBooleanSlot().tload()); + * + * _REENTRANCY_SLOT.asBooleanSlot().tstore(true); + * _; + * _REENTRANCY_SLOT.asBooleanSlot().tstore(false); + * } + * } + * \`\`\` + */ +`; + +const udvt = ({ type, name }) => `\ +/** + * @dev UDVT that represent a slot holding a ${type}. + */ +type ${name}SlotType is bytes32; + +/** + * @dev Cast an arbitrary slot to a ${name}SlotType. + */ +function as${name}Slot(bytes32 slot) internal pure returns (${name}SlotType) { + return ${name}SlotType.wrap(slot); +} +`; + +const storage = ({ type, name }) => `\ +/** + * @dev Load the value held at location \`slot\` in (normal) storage. + */ +function sload(${name}SlotType slot) internal view returns (${type} value) { + /// @solidity memory-safe-assembly + assembly { + value := sload(slot) + } +} + +/** + * @dev Store \`value\` at location \`slot\` in (normal) storage. + */ +function sstore(${name}SlotType slot, ${type} value) internal { + /// @solidity memory-safe-assembly + assembly { + sstore(slot, value) + } +} +`; + +const transient = ({ type, name }) => `\ +/** + * @dev Load the value held at location \`slot\` in transient storage. + */ +function tload(${name}SlotType slot) internal view returns (${type} value) { + /// @solidity memory-safe-assembly + assembly { + value := tload(slot) + } +} + +/** + * @dev Store \`value\` at location \`slot\` in transient storage. + */ +function tstore(${name}SlotType slot, ${type} value) internal { + /// @solidity memory-safe-assembly + assembly { + tstore(slot, value) + } +} +`; + +// GENERATE +module.exports = format( + header.trimEnd(), + 'library TransientSlot {', + TYPES.filter(type => type.isValueType).flatMap(type => [ + udvt(type), + // storage(type), // disabled for now + transient(type), + ]), + '}', +); diff --git a/test/utils/StorageSlot.t.sol b/test/utils/SlotDerivation.t.sol similarity index 55% rename from test/utils/StorageSlot.t.sol rename to test/utils/SlotDerivation.t.sol index ba18942cb1c..2551ef22057 100644 --- a/test/utils/StorageSlot.t.sol +++ b/test/utils/SlotDerivation.t.sol @@ -1,139 +1,27 @@ // SPDX-License-Identifier: MIT -// This file was procedurally generated from scripts/generate/templates/StorageSlot.t.js. +// This file was procedurally generated from scripts/generate/templates/SlotDerivation.t.js. -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; +import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; // solhint-disable func-name-mixedcase -contract StorageSlotTest is Test { - using StorageSlot for *; - - address private _addressVariable; - - function testValueAddress_1(address value) public { - bytes32 slot; - assembly { - slot := _addressVariable.slot - } - - // set in solidity - _addressVariable = value; - - // read using Slots - assertEq(slot.asAddressSlot().sload(), value); - } - - function testValueAddress_2(address value) public { - bytes32 slot; - assembly { - slot := _addressVariable.slot - } - - // set using Slots - slot.asAddressSlot().sstore(value); - - // read in solidity - assertEq(_addressVariable, value); - } - - bytes32 private _bytes32Variable; - - function testValueBytes32_1(bytes32 value) public { - bytes32 slot; - assembly { - slot := _bytes32Variable.slot - } - - // set in solidity - _bytes32Variable = value; - - // read using Slots - assertEq(slot.asBytes32Slot().sload(), value); - } - - function testValueBytes32_2(bytes32 value) public { - bytes32 slot; - assembly { - slot := _bytes32Variable.slot - } - - // set using Slots - slot.asBytes32Slot().sstore(value); - - // read in solidity - assertEq(_bytes32Variable, value); - } - - uint256 private _uint256Variable; - - function testValueUint256_1(uint256 value) public { - bytes32 slot; - assembly { - slot := _uint256Variable.slot - } - - // set in solidity - _uint256Variable = value; - - // read using Slots - assertEq(slot.asUint256Slot().sload(), value); - } - - function testValueUint256_2(uint256 value) public { - bytes32 slot; - assembly { - slot := _uint256Variable.slot - } - - // set using Slots - slot.asUint256Slot().sstore(value); - - // read in solidity - assertEq(_uint256Variable, value); - } - - int256 private _int256Variable; - - function testValueInt256_1(int256 value) public { - bytes32 slot; - assembly { - slot := _int256Variable.slot - } - - // set in solidity - _int256Variable = value; - - // read using Slots - assertEq(slot.asInt256Slot().sload(), value); - } - - function testValueInt256_2(int256 value) public { - bytes32 slot; - assembly { - slot := _int256Variable.slot - } - - // set using Slots - slot.asInt256Slot().sstore(value); - - // read in solidity - assertEq(_int256Variable, value); - } +contract SlotDerivationTest is Test { + using SlotDerivation for bytes32; bytes[] private _array; - function testArray(uint256 length, uint256 offset) public { + function testDeriveArray(uint256 length, uint256 offset) public { length = bound(length, 1, type(uint256).max); offset = bound(offset, 0, length - 1); bytes32 baseSlot; assembly { baseSlot := _array.slot + sstore(baseSlot, length) // store length so solidity access does not revert } - baseSlot.asUint256Slot().sstore(length); bytes storage derived = _array[offset]; bytes32 derivedSlot; @@ -141,13 +29,12 @@ contract StorageSlotTest is Test { derivedSlot := derived.slot } - assertEq(baseSlot.asUint256Slot().sload(), _array.length); assertEq(baseSlot.deriveArray().offset(offset), derivedSlot); } mapping(address => bytes) private _addressMapping; - function testMappingAddress(address key) public { + function testDeriveMappingAddress(address key) public { bytes32 baseSlot; assembly { baseSlot := _addressMapping.slot @@ -164,7 +51,7 @@ contract StorageSlotTest is Test { mapping(bool => bytes) private _boolMapping; - function testMappingBoolean(bool key) public { + function testDeriveMappingBoolean(bool key) public { bytes32 baseSlot; assembly { baseSlot := _boolMapping.slot @@ -181,7 +68,7 @@ contract StorageSlotTest is Test { mapping(bytes32 => bytes) private _bytes32Mapping; - function testMappingBytes32(bytes32 key) public { + function testDeriveMappingBytes32(bytes32 key) public { bytes32 baseSlot; assembly { baseSlot := _bytes32Mapping.slot @@ -198,7 +85,7 @@ contract StorageSlotTest is Test { mapping(bytes4 => bytes) private _bytes4Mapping; - function testMappingBytes4(bytes4 key) public { + function testDeriveMappingBytes4(bytes4 key) public { bytes32 baseSlot; assembly { baseSlot := _bytes4Mapping.slot @@ -215,7 +102,7 @@ contract StorageSlotTest is Test { mapping(uint256 => bytes) private _uint256Mapping; - function testMappingUint256(uint256 key) public { + function testDeriveMappingUint256(uint256 key) public { bytes32 baseSlot; assembly { baseSlot := _uint256Mapping.slot @@ -232,7 +119,7 @@ contract StorageSlotTest is Test { mapping(uint32 => bytes) private _uint32Mapping; - function testMappingUint32(uint32 key) public { + function testDeriveMappingUint32(uint32 key) public { bytes32 baseSlot; assembly { baseSlot := _uint32Mapping.slot @@ -249,7 +136,7 @@ contract StorageSlotTest is Test { mapping(int256 => bytes) private _int256Mapping; - function testMappingInt256(int256 key) public { + function testDeriveMappingInt256(int256 key) public { bytes32 baseSlot; assembly { baseSlot := _int256Mapping.slot @@ -266,7 +153,7 @@ contract StorageSlotTest is Test { mapping(int32 => bytes) private _int32Mapping; - function testMappingInt32(int32 key) public { + function testDeriveMappingInt32(int32 key) public { bytes32 baseSlot; assembly { baseSlot := _int32Mapping.slot @@ -283,7 +170,7 @@ contract StorageSlotTest is Test { mapping(string => bytes) private _stringMapping; - function testMappingString(string memory key) public { + function testDeriveMappingString(string memory key) public { bytes32 baseSlot; assembly { baseSlot := _stringMapping.slot @@ -300,7 +187,7 @@ contract StorageSlotTest is Test { mapping(bytes => bytes) private _bytesMapping; - function testMappingBytes(bytes memory key) public { + function testDeriveMappingBytes(bytes memory key) public { bytes32 baseSlot; assembly { baseSlot := _bytesMapping.slot diff --git a/test/utils/SlotDerivation.test.js b/test/utils/SlotDerivation.test.js new file mode 100644 index 00000000000..c91feba6494 --- /dev/null +++ b/test/utils/SlotDerivation.test.js @@ -0,0 +1,28 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +const { erc1967slot, erc7201slot } = require('../helpers/storage'); + +async function fixture() { + const [account] = await ethers.getSigners(); + const mock = await ethers.deployContract('$SlotDerivation'); + return { mock, account }; +} + +describe('SlotDerivation', function () { + beforeEach(async function () { + Object.assign(this, await loadFixture(fixture)); + }); + + describe('namespaces', function () { + const namespace = 'example.main'; + + it('erc-1967', async function () { + expect(await this.mock.$erc1967slot(namespace)).to.equal(erc1967slot(namespace)); + }); + + it('erc-7201', async function () { + expect(await this.mock.$erc7201slot(namespace)).to.equal(erc7201slot(namespace)); + }); + }); +}); diff --git a/test/utils/StorageSlot.test.js b/test/utils/StorageSlot.test.js index fdd3eb01500..bb218ee446f 100644 --- a/test/utils/StorageSlot.test.js +++ b/test/utils/StorageSlot.test.js @@ -1,7 +1,6 @@ const { ethers } = require('hardhat'); const { expect } = require('chai'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { erc1967slot, erc7201slot } = require('../helpers/storage'); const { generators } = require('../helpers/random'); const slot = ethers.id('some.storage.slot'); @@ -18,18 +17,6 @@ describe('StorageSlot', function () { Object.assign(this, await loadFixture(fixture)); }); - describe('slot derivation', function () { - const path = 'example.main'; - - it('erc-1967', async function () { - expect(await this.mock.erc1967slot(path)).to.equal(erc1967slot(path)); - }); - - it('erc-7201', async function () { - expect(await this.mock.erc7201slot(path)).to.equal(erc7201slot(path)); - }); - }); - for (const { type, value, zero } of [ { type: 'Boolean', value: true, zero: false }, { type: 'Address', value: generators.address(), zero: generators.address.zero }, From 97b30489b20cbac7414227c301fd44db9411217f Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 15:32:42 +0100 Subject: [PATCH 12/23] Update .changeset/kind-planets-cough.md --- .changeset/kind-planets-cough.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/kind-planets-cough.md b/.changeset/kind-planets-cough.md index 65e79706017..ee850e86aa2 100644 --- a/.changeset/kind-planets-cough.md +++ b/.changeset/kind-planets-cough.md @@ -2,4 +2,4 @@ 'openzeppelin-solidity': minor --- -`StorageSlot`: Add storage slot derivation tooling and typed-slot representation with transient storage support. +`TransientSlot`: Add library for typed-slot representation with transient storage support. From 38f314394956344187a50ba7d2b2c4b9c79a19f5 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 15:33:33 +0100 Subject: [PATCH 13/23] add changeset --- .changeset/gorgeous-badgers-vanish.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/gorgeous-badgers-vanish.md diff --git a/.changeset/gorgeous-badgers-vanish.md b/.changeset/gorgeous-badgers-vanish.md new file mode 100644 index 00000000000..99f90ab6920 --- /dev/null +++ b/.changeset/gorgeous-badgers-vanish.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': minor +--- + +`SlotDerivation`: Add library for storage slot derivation. From c1a83f11521276a7651b4de64cb955127ec46411 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 15:35:46 +0100 Subject: [PATCH 14/23] min diff --- contracts/utils/StorageSlot.sol | 48 +++++++++++------------ scripts/generate/templates/StorageSlot.js | 5 ++- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/contracts/utils/StorageSlot.sol b/contracts/utils/StorageSlot.sol index db004540105..5dad9479770 100644 --- a/contracts/utils/StorageSlot.sol +++ b/contracts/utils/StorageSlot.sol @@ -33,6 +33,30 @@ library StorageSlot { address value; } + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + struct Int256Slot { + int256 value; + } + + struct StringSlot { + string value; + } + + struct BytesSlot { + bytes value; + } + /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ @@ -43,10 +67,6 @@ library StorageSlot { } } - struct BooleanSlot { - bool value; - } - /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ @@ -57,10 +77,6 @@ library StorageSlot { } } - struct Bytes32Slot { - bytes32 value; - } - /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ @@ -71,10 +87,6 @@ library StorageSlot { } } - struct Uint256Slot { - uint256 value; - } - /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ @@ -85,10 +97,6 @@ library StorageSlot { } } - struct Int256Slot { - int256 value; - } - /** * @dev Returns an `Int256Slot` with member `value` located at `slot`. */ @@ -99,10 +107,6 @@ library StorageSlot { } } - struct StringSlot { - string value; - } - /** * @dev Returns an `StringSlot` with member `value` located at `slot`. */ @@ -123,10 +127,6 @@ library StorageSlot { } } - struct BytesSlot { - bytes value; - } - /** * @dev Returns an `BytesSlot` with member `value` located at `slot`. */ diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 65aaa67dccf..19eda3e4b02 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -34,7 +34,9 @@ const struct = ({ type, name }) => `\ struct ${name}Slot { ${type} value; } +`; +const get = ({ name }) => `\ /** * @dev Returns an \`${name}Slot\` with member \`value\` located at \`slot\`. */ @@ -62,6 +64,7 @@ function get${name}Slot(${type} storage store) internal pure returns (${name}Slo module.exports = format( header.trimEnd(), 'library StorageSlot {', - TYPES.flatMap(type => [struct(type), type.isValueType ? '' : getStorage(type)]), + TYPES.flatMap(type => struct(type)), + TYPES.flatMap(type => [get(type), type.isValueType ? '' : getStorage(type)]), '}', ); From b6ff468892967abf2c8f9f3d8aaa1b336e932232 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 15:37:44 +0100 Subject: [PATCH 15/23] Update scripts/generate/templates/StorageSlot.js --- scripts/generate/templates/StorageSlot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate/templates/StorageSlot.js b/scripts/generate/templates/StorageSlot.js index 19eda3e4b02..bcd14ad5fc3 100644 --- a/scripts/generate/templates/StorageSlot.js +++ b/scripts/generate/templates/StorageSlot.js @@ -64,7 +64,7 @@ function get${name}Slot(${type} storage store) internal pure returns (${name}Slo module.exports = format( header.trimEnd(), 'library StorageSlot {', - TYPES.flatMap(type => struct(type)), + TYPES.map(type => struct(type)), TYPES.flatMap(type => [get(type), type.isValueType ? '' : getStorage(type)]), '}', ); From f19086c21e06d898827b1e22772a65d9f0825df3 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 15:39:14 +0100 Subject: [PATCH 16/23] fix lint --- scripts/generate/templates/SlotDerivation.js | 2 +- scripts/generate/templates/TransientSlot.js | 42 ++++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/scripts/generate/templates/SlotDerivation.js b/scripts/generate/templates/SlotDerivation.js index a7ecbfe1235..dc62ad7665d 100644 --- a/scripts/generate/templates/SlotDerivation.js +++ b/scripts/generate/templates/SlotDerivation.js @@ -96,6 +96,6 @@ module.exports = format( 'library SlotDerivation {', namespace, array, - TYPES.flatMap(type => (type.isValueType ? mapping(type) : mapping2(type))), + TYPES.map(type => (type.isValueType ? mapping(type) : mapping2(type))), '}', ); diff --git a/scripts/generate/templates/TransientSlot.js b/scripts/generate/templates/TransientSlot.js index 4db1bae0f27..cabdce9b3b0 100644 --- a/scripts/generate/templates/TransientSlot.js +++ b/scripts/generate/templates/TransientSlot.js @@ -43,27 +43,27 @@ function as${name}Slot(bytes32 slot) internal pure returns (${name}SlotType) { } `; -const storage = ({ type, name }) => `\ -/** - * @dev Load the value held at location \`slot\` in (normal) storage. - */ -function sload(${name}SlotType slot) internal view returns (${type} value) { - /// @solidity memory-safe-assembly - assembly { - value := sload(slot) - } -} - -/** - * @dev Store \`value\` at location \`slot\` in (normal) storage. - */ -function sstore(${name}SlotType slot, ${type} value) internal { - /// @solidity memory-safe-assembly - assembly { - sstore(slot, value) - } -} -`; +// const storage = ({ type, name }) => `\ +// /** +// * @dev Load the value held at location \`slot\` in (normal) storage. +// */ +// function sload(${name}SlotType slot) internal view returns (${type} value) { +// /// @solidity memory-safe-assembly +// assembly { +// value := sload(slot) +// } +// } +// +// /** +// * @dev Store \`value\` at location \`slot\` in (normal) storage. +// */ +// function sstore(${name}SlotType slot, ${type} value) internal { +// /// @solidity memory-safe-assembly +// assembly { +// sstore(slot, value) +// } +// } +// `; const transient = ({ type, name }) => `\ /** From 3c11e1dd1a9546bc38732c2057d2a7e613e63bb2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 17:10:47 +0100 Subject: [PATCH 17/23] move UDVT to types/TypedSlot.sol --- contracts/utils/TransientSlot.sol | 83 ++++----------------- contracts/utils/types/TypedSlot.sol | 69 +++++++++++++++++ scripts/generate/run.js | 1 + scripts/generate/templates/TransientSlot.js | 49 ++---------- scripts/generate/templates/TypedSlot.js | 32 ++++++++ 5 files changed, 121 insertions(+), 113 deletions(-) create mode 100644 contracts/utils/types/TypedSlot.sol create mode 100644 scripts/generate/templates/TypedSlot.js diff --git a/contracts/utils/TransientSlot.sol b/contracts/utils/TransientSlot.sol index 4d7240d542b..ff5c3746a78 100644 --- a/contracts/utils/TransientSlot.sol +++ b/contracts/utils/TransientSlot.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.24; +import {TypedSlot} from "./types/TypedSlot.sol"; + /** * @dev Library for reading and writing primitive types to specific storage slots. This is a variant of {StorageSlot} * that supports transient storage. @@ -12,6 +14,7 @@ pragma solidity ^0.8.24; * Example usage: * ```solidity * contract ReentrancyGuard { + * using TypedSlot for bytes32; * using TransientSlot for *; * * bytes32 internal constant _REENTRANCY_SLOT = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; @@ -27,22 +30,10 @@ pragma solidity ^0.8.24; * ``` */ library TransientSlot { - /** - * @dev UDVT that represent a slot holding a address. - */ - type AddressSlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a AddressSlotType. - */ - function asAddressSlot(bytes32 slot) internal pure returns (AddressSlotType) { - return AddressSlotType.wrap(slot); - } - /** * @dev Load the value held at location `slot` in transient storage. */ - function tload(AddressSlotType slot) internal view returns (address value) { + function tload(TypedSlot.AddressSlotType slot) internal view returns (address value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) @@ -52,29 +43,17 @@ library TransientSlot { /** * @dev Store `value` at location `slot` in transient storage. */ - function tstore(AddressSlotType slot, address value) internal { + function tstore(TypedSlot.AddressSlotType slot, address value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } - /** - * @dev UDVT that represent a slot holding a bool. - */ - type BooleanSlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a BooleanSlotType. - */ - function asBooleanSlot(bytes32 slot) internal pure returns (BooleanSlotType) { - return BooleanSlotType.wrap(slot); - } - /** * @dev Load the value held at location `slot` in transient storage. */ - function tload(BooleanSlotType slot) internal view returns (bool value) { + function tload(TypedSlot.BooleanSlotType slot) internal view returns (bool value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) @@ -84,29 +63,17 @@ library TransientSlot { /** * @dev Store `value` at location `slot` in transient storage. */ - function tstore(BooleanSlotType slot, bool value) internal { + function tstore(TypedSlot.BooleanSlotType slot, bool value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } - /** - * @dev UDVT that represent a slot holding a bytes32. - */ - type Bytes32SlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a Bytes32SlotType. - */ - function asBytes32Slot(bytes32 slot) internal pure returns (Bytes32SlotType) { - return Bytes32SlotType.wrap(slot); - } - /** * @dev Load the value held at location `slot` in transient storage. */ - function tload(Bytes32SlotType slot) internal view returns (bytes32 value) { + function tload(TypedSlot.Bytes32SlotType slot) internal view returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) @@ -116,29 +83,17 @@ library TransientSlot { /** * @dev Store `value` at location `slot` in transient storage. */ - function tstore(Bytes32SlotType slot, bytes32 value) internal { + function tstore(TypedSlot.Bytes32SlotType slot, bytes32 value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } - /** - * @dev UDVT that represent a slot holding a uint256. - */ - type Uint256SlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a Uint256SlotType. - */ - function asUint256Slot(bytes32 slot) internal pure returns (Uint256SlotType) { - return Uint256SlotType.wrap(slot); - } - /** * @dev Load the value held at location `slot` in transient storage. */ - function tload(Uint256SlotType slot) internal view returns (uint256 value) { + function tload(TypedSlot.Uint256SlotType slot) internal view returns (uint256 value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) @@ -148,29 +103,17 @@ library TransientSlot { /** * @dev Store `value` at location `slot` in transient storage. */ - function tstore(Uint256SlotType slot, uint256 value) internal { + function tstore(TypedSlot.Uint256SlotType slot, uint256 value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) } } - /** - * @dev UDVT that represent a slot holding a int256. - */ - type Int256SlotType is bytes32; - - /** - * @dev Cast an arbitrary slot to a Int256SlotType. - */ - function asInt256Slot(bytes32 slot) internal pure returns (Int256SlotType) { - return Int256SlotType.wrap(slot); - } - /** * @dev Load the value held at location `slot` in transient storage. */ - function tload(Int256SlotType slot) internal view returns (int256 value) { + function tload(TypedSlot.Int256SlotType slot) internal view returns (int256 value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) @@ -180,7 +123,7 @@ library TransientSlot { /** * @dev Store `value` at location `slot` in transient storage. */ - function tstore(Int256SlotType slot, int256 value) internal { + function tstore(TypedSlot.Int256SlotType slot, int256 value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) diff --git a/contracts/utils/types/TypedSlot.sol b/contracts/utils/types/TypedSlot.sol new file mode 100644 index 00000000000..a74f56a1975 --- /dev/null +++ b/contracts/utils/types/TypedSlot.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +// This file was procedurally generated from scripts/generate/templates/TypedSlot.js. + +pragma solidity ^0.8.20; + +/** + * @dev Library for representing bytes32 slot as typed objects. + */ +library TypedSlot { + /** + * @dev UDVT that represent a slot holding a address. + */ + type AddressSlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a AddressSlotType. + */ + function asAddressSlot(bytes32 slot) internal pure returns (AddressSlotType) { + return AddressSlotType.wrap(slot); + } + + /** + * @dev UDVT that represent a slot holding a bool. + */ + type BooleanSlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a BooleanSlotType. + */ + function asBooleanSlot(bytes32 slot) internal pure returns (BooleanSlotType) { + return BooleanSlotType.wrap(slot); + } + + /** + * @dev UDVT that represent a slot holding a bytes32. + */ + type Bytes32SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Bytes32SlotType. + */ + function asBytes32Slot(bytes32 slot) internal pure returns (Bytes32SlotType) { + return Bytes32SlotType.wrap(slot); + } + + /** + * @dev UDVT that represent a slot holding a uint256. + */ + type Uint256SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Uint256SlotType. + */ + function asUint256Slot(bytes32 slot) internal pure returns (Uint256SlotType) { + return Uint256SlotType.wrap(slot); + } + + /** + * @dev UDVT that represent a slot holding a int256. + */ + type Int256SlotType is bytes32; + + /** + * @dev Cast an arbitrary slot to a Int256SlotType. + */ + function asInt256Slot(bytes32 slot) internal pure returns (Int256SlotType) { + return Int256SlotType.wrap(slot); + } +} diff --git a/scripts/generate/run.js b/scripts/generate/run.js index 7e49b92edd1..9bdc3bdabd1 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -36,6 +36,7 @@ for (const [file, template] of Object.entries({ 'utils/structs/EnumerableSet.sol': './templates/EnumerableSet.js', 'utils/structs/EnumerableMap.sol': './templates/EnumerableMap.js', 'utils/structs/Checkpoints.sol': './templates/Checkpoints.js', + 'utils/types/TypedSlot.sol': './templates/TypedSlot.js', 'utils/SlotDerivation.sol': './templates/SlotDerivation.js', 'utils/StorageSlot.sol': './templates/StorageSlot.js', 'utils/TransientSlot.sol': './templates/TransientSlot.js', diff --git a/scripts/generate/templates/TransientSlot.js b/scripts/generate/templates/TransientSlot.js index cabdce9b3b0..ff57b4bc838 100644 --- a/scripts/generate/templates/TransientSlot.js +++ b/scripts/generate/templates/TransientSlot.js @@ -4,6 +4,8 @@ const { TYPES } = require('./Slot.opts'); const header = `\ pragma solidity ^0.8.24; +import {TypedSlot} from "./types/TypedSlot.sol"; + /** * @dev Library for reading and writing primitive types to specific storage slots. This is a variant of {StorageSlot} * that supports transient storage. @@ -13,6 +15,7 @@ pragma solidity ^0.8.24; * Example usage: * \`\`\`solidity * contract ReentrancyGuard { + * using TypedSlot for bytes32; * using TransientSlot for *; * * bytes32 internal constant _REENTRANCY_SLOT = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; @@ -29,47 +32,11 @@ pragma solidity ^0.8.24; */ `; -const udvt = ({ type, name }) => `\ -/** - * @dev UDVT that represent a slot holding a ${type}. - */ -type ${name}SlotType is bytes32; - -/** - * @dev Cast an arbitrary slot to a ${name}SlotType. - */ -function as${name}Slot(bytes32 slot) internal pure returns (${name}SlotType) { - return ${name}SlotType.wrap(slot); -} -`; - -// const storage = ({ type, name }) => `\ -// /** -// * @dev Load the value held at location \`slot\` in (normal) storage. -// */ -// function sload(${name}SlotType slot) internal view returns (${type} value) { -// /// @solidity memory-safe-assembly -// assembly { -// value := sload(slot) -// } -// } -// -// /** -// * @dev Store \`value\` at location \`slot\` in (normal) storage. -// */ -// function sstore(${name}SlotType slot, ${type} value) internal { -// /// @solidity memory-safe-assembly -// assembly { -// sstore(slot, value) -// } -// } -// `; - const transient = ({ type, name }) => `\ /** * @dev Load the value held at location \`slot\` in transient storage. */ -function tload(${name}SlotType slot) internal view returns (${type} value) { +function tload(TypedSlot.${name}SlotType slot) internal view returns (${type} value) { /// @solidity memory-safe-assembly assembly { value := tload(slot) @@ -79,7 +46,7 @@ function tload(${name}SlotType slot) internal view returns (${type} value) { /** * @dev Store \`value\` at location \`slot\` in transient storage. */ -function tstore(${name}SlotType slot, ${type} value) internal { +function tstore(TypedSlot.${name}SlotType slot, ${type} value) internal { /// @solidity memory-safe-assembly assembly { tstore(slot, value) @@ -91,10 +58,6 @@ function tstore(${name}SlotType slot, ${type} value) internal { module.exports = format( header.trimEnd(), 'library TransientSlot {', - TYPES.filter(type => type.isValueType).flatMap(type => [ - udvt(type), - // storage(type), // disabled for now - transient(type), - ]), + TYPES.filter(type => type.isValueType).map(type => transient(type)), '}', ); diff --git a/scripts/generate/templates/TypedSlot.js b/scripts/generate/templates/TypedSlot.js new file mode 100644 index 00000000000..6a87bc40cb0 --- /dev/null +++ b/scripts/generate/templates/TypedSlot.js @@ -0,0 +1,32 @@ +const format = require('../format-lines'); +const { TYPES } = require('./Slot.opts'); + +const header = `\ +pragma solidity ^0.8.20; + +/** + * @dev Library for representing bytes32 slot as typed objects. + */ +`; + +const udvt = ({ type, name }) => `\ +/** + * @dev UDVT that represent a slot holding a ${type}. + */ +type ${name}SlotType is bytes32; + +/** + * @dev Cast an arbitrary slot to a ${name}SlotType. + */ +function as${name}Slot(bytes32 slot) internal pure returns (${name}SlotType) { + return ${name}SlotType.wrap(slot); +} +`; + +// GENERATE +module.exports = format( + header.trimEnd(), + 'library TypedSlot {', + TYPES.filter(type => type.isValueType).map(type => udvt(type)), + '}', +); From 24a584b98b0793fd7f158b4ebde6d95d42e041d1 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 17:12:05 +0100 Subject: [PATCH 18/23] changeset --- .changeset/popular-insects-sleep.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/popular-insects-sleep.md diff --git a/.changeset/popular-insects-sleep.md b/.changeset/popular-insects-sleep.md new file mode 100644 index 00000000000..26721de446f --- /dev/null +++ b/.changeset/popular-insects-sleep.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': minor +--- + +`TypedSlot`: Add a library to represent bytes32 slots as typed-object based on the value they contain. From 4676af3e8e0178a2d9ec850052961bf54185d794 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 17:12:57 +0100 Subject: [PATCH 19/23] Update .changeset/kind-planets-cough.md --- .changeset/kind-planets-cough.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/kind-planets-cough.md b/.changeset/kind-planets-cough.md index ee850e86aa2..84991ff9cab 100644 --- a/.changeset/kind-planets-cough.md +++ b/.changeset/kind-planets-cough.md @@ -2,4 +2,4 @@ 'openzeppelin-solidity': minor --- -`TransientSlot`: Add library for typed-slot representation with transient storage support. +`TransientSlot`: Add library for operating on the transient storage space using the typed-slot representation. From cd62c3dd2b01d50e0ae59ba1e5561fdc10b9354f Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 17:19:31 +0100 Subject: [PATCH 20/23] doc --- contracts/utils/README.adoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/contracts/utils/README.adoc b/contracts/utils/README.adoc index b7c1baf8db6..7bb764a145f 100644 --- a/contracts/utils/README.adoc +++ b/contracts/utils/README.adoc @@ -28,9 +28,13 @@ Miscellaneous contracts and libraries containing utility functions you can use t * {Base64}: On-chain base64 and base64URL encoding according to https://datatracker.ietf.org/doc/html/rfc4648[RFC-4648]. * {Strings}: Common operations for strings formatting. * {ShortString}: Library to encode (and decode) short strings into (or from) a single bytes32 slot for optimizing costs. Short strings are limited to 31 characters. + * {SlotDerivation}: Methods for deriving storage slot from ERC-1967 and ERC-7201 namespaces as well as from constructions such as mapping and arrays. + * {TypedSlot}: Types to represent storage slots as typed-objects. * {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types. + * {TransientSlot}: Methods for accessing the transient storage space using the typed-slot representation for {TypedSlot} * {Multicall}: Abstract contract with an utility to allow batching together multiple calls in a single transaction. Useful for allowing EOAs to perform multiple operations at once. * {Context}: An utility for abstracting the sender and calldata in the current execution context. + * {Time}: Types (and helpers) to manipulate clocks and time objects such as delays. * {Panic}: A library to revert with https://docs.soliditylang.org/en/v0.8.20/control-structures.html#panic-via-assert-and-error-via-require[Solidity panic codes]. [NOTE] @@ -108,10 +112,20 @@ Ethereum contracts have no native concept of an interface, so applications must {{ShortStrings}} +{{SlotDerivation}} + {{StorageSlot}} +{{TransientSlot}} + {{Multicall}} {{Context}} {{Panic}} + +== Types + +{{Time}} + +{{TypedSlot}} From 2cd2c38f7156b28c756ca1450ba3e9f51827ad29 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 17:22:17 +0100 Subject: [PATCH 21/23] codespell --- contracts/utils/TransientSlot.sol | 2 +- scripts/generate/templates/TransientSlot.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/utils/TransientSlot.sol b/contracts/utils/TransientSlot.sol index ff5c3746a78..9c154a1d740 100644 --- a/contracts/utils/TransientSlot.sol +++ b/contracts/utils/TransientSlot.sol @@ -9,7 +9,7 @@ import {TypedSlot} from "./types/TypedSlot.sol"; * @dev Library for reading and writing primitive types to specific storage slots. This is a variant of {StorageSlot} * that supports transient storage. * - * The functions in this library return types that give access to reading or writting primitives. + * The functions in this library return types that give access to reading or writing primitives. * * Example usage: * ```solidity diff --git a/scripts/generate/templates/TransientSlot.js b/scripts/generate/templates/TransientSlot.js index ff57b4bc838..6f34447eff8 100644 --- a/scripts/generate/templates/TransientSlot.js +++ b/scripts/generate/templates/TransientSlot.js @@ -10,7 +10,7 @@ import {TypedSlot} from "./types/TypedSlot.sol"; * @dev Library for reading and writing primitive types to specific storage slots. This is a variant of {StorageSlot} * that supports transient storage. * - * The functions in this library return types that give access to reading or writting primitives. + * The functions in this library return types that give access to reading or writing primitives. * * Example usage: * \`\`\`solidity From 644597333ab33d3ae8ed7620c0ee390b1ceb2022 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 21:31:23 +0100 Subject: [PATCH 22/23] update --- .changeset/gorgeous-badgers-vanish.md | 2 +- contracts/utils/TransientSlot.sol | 12 ++++++------ scripts/generate/templates/TransientSlot.js | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.changeset/gorgeous-badgers-vanish.md b/.changeset/gorgeous-badgers-vanish.md index 99f90ab6920..ce75ed6ebae 100644 --- a/.changeset/gorgeous-badgers-vanish.md +++ b/.changeset/gorgeous-badgers-vanish.md @@ -2,4 +2,4 @@ 'openzeppelin-solidity': minor --- -`SlotDerivation`: Add library for storage slot derivation. +`SlotDerivation`: Add a library of methods for derivating common storage slots. diff --git a/contracts/utils/TransientSlot.sol b/contracts/utils/TransientSlot.sol index 9c154a1d740..d963f4c3e3b 100644 --- a/contracts/utils/TransientSlot.sol +++ b/contracts/utils/TransientSlot.sol @@ -13,18 +13,18 @@ import {TypedSlot} from "./types/TypedSlot.sol"; * * Example usage: * ```solidity - * contract ReentrancyGuard { + * contract Lock { * using TypedSlot for bytes32; * using TransientSlot for *; * - * bytes32 internal constant _REENTRANCY_SLOT = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + * bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542; * - * modifier nonReentrant() { - * require(!_REENTRANCY_SLOT.asBooleanSlot().tload()); + * modifier locked() { + * require(!_LOCK_SLOT.asBooleanSlot().tload()); * - * _REENTRANCY_SLOT.asBooleanSlot().tstore(true); + * _LOCK_SLOT.asBooleanSlot().tstore(true); * _; - * _REENTRANCY_SLOT.asBooleanSlot().tstore(false); + * _LOCK_SLOT.asBooleanSlot().tstore(false); * } * } * ``` diff --git a/scripts/generate/templates/TransientSlot.js b/scripts/generate/templates/TransientSlot.js index 6f34447eff8..91ff6fc0aa4 100644 --- a/scripts/generate/templates/TransientSlot.js +++ b/scripts/generate/templates/TransientSlot.js @@ -14,18 +14,18 @@ import {TypedSlot} from "./types/TypedSlot.sol"; * * Example usage: * \`\`\`solidity - * contract ReentrancyGuard { + * contract Lock { * using TypedSlot for bytes32; * using TransientSlot for *; * - * bytes32 internal constant _REENTRANCY_SLOT = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + * bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542; * - * modifier nonReentrant() { - * require(!_REENTRANCY_SLOT.asBooleanSlot().tload()); + * modifier locked() { + * require(!_LOCK_SLOT.asBooleanSlot().tload()); * - * _REENTRANCY_SLOT.asBooleanSlot().tstore(true); + * _LOCK_SLOT.asBooleanSlot().tstore(true); * _; - * _REENTRANCY_SLOT.asBooleanSlot().tstore(false); + * _LOCK_SLOT.asBooleanSlot().tstore(false); * } * } * \`\`\` From 40c49431066721a9bb613d736060280eab348719 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 20 Mar 2024 21:40:01 +0100 Subject: [PATCH 23/23] rename --- .changeset/kind-planets-cough.md | 2 +- contracts/utils/README.adoc | 4 ++-- contracts/utils/{TransientSlot.sol => TransientStorage.sol} | 4 ++-- scripts/generate/run.js | 2 +- .../templates/{TransientSlot.js => TransientStorage.js} | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) rename contracts/utils/{TransientSlot.sol => TransientStorage.sol} (98%) rename scripts/generate/templates/{TransientSlot.js => TransientStorage.js} (98%) diff --git a/.changeset/kind-planets-cough.md b/.changeset/kind-planets-cough.md index 84991ff9cab..73f3c23402f 100644 --- a/.changeset/kind-planets-cough.md +++ b/.changeset/kind-planets-cough.md @@ -2,4 +2,4 @@ 'openzeppelin-solidity': minor --- -`TransientSlot`: Add library for operating on the transient storage space using the typed-slot representation. +`TransientStorage`: Add library for operating on the transient storage space using the typed-slot representation. diff --git a/contracts/utils/README.adoc b/contracts/utils/README.adoc index 7bb764a145f..ef13f703939 100644 --- a/contracts/utils/README.adoc +++ b/contracts/utils/README.adoc @@ -31,7 +31,7 @@ Miscellaneous contracts and libraries containing utility functions you can use t * {SlotDerivation}: Methods for deriving storage slot from ERC-1967 and ERC-7201 namespaces as well as from constructions such as mapping and arrays. * {TypedSlot}: Types to represent storage slots as typed-objects. * {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types. - * {TransientSlot}: Methods for accessing the transient storage space using the typed-slot representation for {TypedSlot} + * {TransientStorage}: Methods for accessing the transient storage space using the typed-slot representation for {TypedSlot} * {Multicall}: Abstract contract with an utility to allow batching together multiple calls in a single transaction. Useful for allowing EOAs to perform multiple operations at once. * {Context}: An utility for abstracting the sender and calldata in the current execution context. * {Time}: Types (and helpers) to manipulate clocks and time objects such as delays. @@ -116,7 +116,7 @@ Ethereum contracts have no native concept of an interface, so applications must {{StorageSlot}} -{{TransientSlot}} +{{TransientStorage}} {{Multicall}} diff --git a/contracts/utils/TransientSlot.sol b/contracts/utils/TransientStorage.sol similarity index 98% rename from contracts/utils/TransientSlot.sol rename to contracts/utils/TransientStorage.sol index d963f4c3e3b..9f0677a47c4 100644 --- a/contracts/utils/TransientSlot.sol +++ b/contracts/utils/TransientStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// This file was procedurally generated from scripts/generate/templates/TransientSlot.js. +// This file was procedurally generated from scripts/generate/templates/TransientStorage.js. pragma solidity ^0.8.24; @@ -29,7 +29,7 @@ import {TypedSlot} from "./types/TypedSlot.sol"; * } * ``` */ -library TransientSlot { +library TransientStorage { /** * @dev Load the value held at location `slot` in transient storage. */ diff --git a/scripts/generate/run.js b/scripts/generate/run.js index 9bdc3bdabd1..263ac7c8587 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -39,7 +39,7 @@ for (const [file, template] of Object.entries({ 'utils/types/TypedSlot.sol': './templates/TypedSlot.js', 'utils/SlotDerivation.sol': './templates/SlotDerivation.js', 'utils/StorageSlot.sol': './templates/StorageSlot.js', - 'utils/TransientSlot.sol': './templates/TransientSlot.js', + 'utils/TransientStorage.sol': './templates/TransientStorage.js', })) { generateFromTemplate(file, template, './contracts/'); } diff --git a/scripts/generate/templates/TransientSlot.js b/scripts/generate/templates/TransientStorage.js similarity index 98% rename from scripts/generate/templates/TransientSlot.js rename to scripts/generate/templates/TransientStorage.js index 91ff6fc0aa4..ca5acd06b56 100644 --- a/scripts/generate/templates/TransientSlot.js +++ b/scripts/generate/templates/TransientStorage.js @@ -57,7 +57,7 @@ function tstore(TypedSlot.${name}SlotType slot, ${type} value) internal { // GENERATE module.exports = format( header.trimEnd(), - 'library TransientSlot {', + 'library TransientStorage {', TYPES.filter(type => type.isValueType).map(type => transient(type)), '}', );