Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/kind-planets-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`StorageSlot`: Add primitives for operating on the transient storage space using a typed-slot representation.
72 changes: 62 additions & 10 deletions contracts/mocks/StorageSlotMock.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/StorageSlotMock.js.

pragma solidity ^0.8.20;
pragma solidity ^0.8.24;

import {Multicall} from "../utils/Multicall.sol";
import {StorageSlot} from "../utils/StorageSlot.sol";

contract StorageSlotMock {
contract StorageSlotMock is Multicall {
using StorageSlot for *;

function setBooleanSlot(bytes32 slot, bool value) public {
slot.getBooleanSlot().value = value;
}

function setAddressSlot(bytes32 slot, address value) public {
slot.getAddressSlot().value = value;
}

function setBooleanSlot(bytes32 slot, bool value) public {
slot.getBooleanSlot().value = value;
}

function setBytes32Slot(bytes32 slot, bytes32 value) public {
slot.getBytes32Slot().value = value;
}
Expand All @@ -27,14 +29,14 @@ contract StorageSlotMock {
slot.getInt256Slot().value = value;
}

function getBooleanSlot(bytes32 slot) public view returns (bool) {
return slot.getBooleanSlot().value;
}

function getAddressSlot(bytes32 slot) public view returns (address) {
return slot.getAddressSlot().value;
}

function getBooleanSlot(bytes32 slot) public view returns (bool) {
return slot.getBooleanSlot().value;
}

function getBytes32Slot(bytes32 slot) public view returns (bytes32) {
return slot.getBytes32Slot().value;
}
Expand Down Expand Up @@ -82,4 +84,54 @@ contract StorageSlotMock {
function getBytesStorage(uint256 key) public view returns (bytes memory) {
return bytesMap[key].getBytesSlot().value;
}

event AddressValue(bytes32 slot, address value);

function tloadAddress(bytes32 slot) public {
emit AddressValue(slot, slot.asAddress().tload());
}

function tstore(bytes32 slot, address value) public {
slot.asAddress().tstore(value);
}

event BooleanValue(bytes32 slot, bool value);

function tloadBoolean(bytes32 slot) public {
emit BooleanValue(slot, slot.asBoolean().tload());
}

function tstore(bytes32 slot, bool value) public {
slot.asBoolean().tstore(value);
}

event Bytes32Value(bytes32 slot, bytes32 value);

function tloadBytes32(bytes32 slot) public {
emit Bytes32Value(slot, slot.asBytes32().tload());
}

function tstore(bytes32 slot, bytes32 value) public {
slot.asBytes32().tstore(value);
}

event Uint256Value(bytes32 slot, uint256 value);

function tloadUint256(bytes32 slot) public {
emit Uint256Value(slot, slot.asUint256().tload());
}

function tstore(bytes32 slot, uint256 value) public {
slot.asUint256().tstore(value);
}

event Int256Value(bytes32 slot, int256 value);

function tloadInt256(bytes32 slot) public {
emit Int256Value(slot, slot.asInt256().tload());
}

function tstore(bytes32 slot, int256 value) public {
slot.asInt256().tstore(value);
}
}
2 changes: 1 addition & 1 deletion contracts/utils/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Miscellaneous contracts and libraries containing utility functions you can use t
* {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-7201 namespaces as well as from constructions such as mapping and arrays.
* {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types.
* {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types. Also include primitives for reading from and writing to transient storage (only value types are currently supported).
* {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.
* {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].
Expand Down
180 changes: 179 additions & 1 deletion contracts/utils/StorageSlot.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -29,6 +29,24 @@ pragma solidity ^0.8.20;
* }
* ```
*
* Since version 5.1, this library also support writing and reading value types to and from transient storage.
*
* * Example using transient storage:
* ```solidity
* contract Lock {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
*
* modifier locked() {
* require(!_LOCK_SLOT.asBoolean().tload());
*
* _LOCK_SLOT.asBoolean().tstore(true);
* _;
* _LOCK_SLOT.asBoolean().tstore(false);
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
Expand Down Expand Up @@ -149,4 +167,164 @@ library StorageSlot {
r.slot := store.slot
}
}

/**
* @dev UDVT that represent a slot holding a address.
*/
type AddressSlotType is bytes32;

/**
* @dev Cast an arbitrary slot to a AddressSlotType.
*/
function asAddress(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 asBoolean(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 asBytes32(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 asUint256(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 asInt256(bytes32 slot) internal pure returns (Int256SlotType) {
return Int256SlotType.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 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 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 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 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)
}
}
}
2 changes: 2 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
[profile.default]
solc_version = '0.8.24'
evm_version = 'cancun'
src = 'contracts'
out = 'out'
libs = ['node_modules', 'lib']
Expand Down
12 changes: 10 additions & 2 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand Down Expand Up @@ -78,6 +83,7 @@ module.exports = {
enabled: withOptimizations,
runs: 200,
},
evmVersion: argv.evm,
viaIR: withOptimizations && argv.ir,
outputSelection: { '*': { '*': ['storageLayout'] } },
},
Expand All @@ -90,19 +96,21 @@ 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,
},
},
exposed: {
imports: true,
initializers: true,
exclude: ['vendor/**/*', '**/*WithInit.sol'],
exclude: ['vendor/**/*', '**/*WithInit.sol', 'utils/TransientSlot.sol'],
},
gasReporter: {
enabled: argv.gas,
Expand Down
Loading