Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transient storage slot support in StorageSlot.sol #4980

Merged
merged 17 commits into from
Apr 3, 2024
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
Loading