Split StorageSlot into TransientSlot (#5239)

Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Signed-off-by: Hadrien Croubois <hadrien.croubois@gmail.com>
This commit is contained in:
Ernesto García
2024-10-08 13:39:53 -06:00
committed by Hadrien Croubois
parent f8432e82d6
commit 1bcd1c65db
15 changed files with 434 additions and 333 deletions

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/StorageSlotMock.js.
pragma solidity ^0.8.24;
pragma solidity ^0.8.20;
import {Multicall} from "../utils/Multicall.sol";
import {StorageSlot} from "../utils/StorageSlot.sol";
@ -84,54 +84,4 @@ contract StorageSlotMock is Multicall {
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);
}
}

View File

@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/TransientSlotMock.js.
pragma solidity ^0.8.24;
import {Multicall} from "../utils/Multicall.sol";
import {TransientSlot} from "../utils/TransientSlot.sol";
contract TransientSlotMock is Multicall {
using TransientSlot for *;
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);
}
}

View File

@ -7,7 +7,7 @@ import {IERC20, ERC20} from "../ERC20.sol";
import {IERC7674} from "../../../interfaces/draft-IERC7674.sol";
import {Math} from "../../../utils/math/Math.sol";
import {SlotDerivation} from "../../../utils/SlotDerivation.sol";
import {StorageSlot} from "../../../utils/StorageSlot.sol";
import {TransientSlot} from "../../../utils/TransientSlot.sol";
/**
* @dev Extension of {ERC20} that adds support for temporary allowances following ERC-7674.
@ -18,8 +18,8 @@ import {StorageSlot} from "../../../utils/StorageSlot.sol";
*/
abstract contract ERC20TemporaryApproval is ERC20, IERC7674 {
using SlotDerivation for bytes32;
using StorageSlot for bytes32;
using StorageSlot for StorageSlot.Uint256SlotType;
using TransientSlot for bytes32;
using TransientSlot for TransientSlot.Uint256Slot;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20_TEMPORARY_APPROVAL_STORAGE")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC20_TEMPORARY_APPROVAL_STORAGE =
@ -113,10 +113,7 @@ abstract contract ERC20TemporaryApproval is ERC20, IERC7674 {
}
}
function _temporaryAllowanceSlot(
address owner,
address spender
) private pure returns (StorageSlot.Uint256SlotType) {
function _temporaryAllowanceSlot(address owner, address spender) private pure returns (TransientSlot.Uint256Slot) {
return ERC20_TEMPORARY_APPROVAL_STORAGE.deriveMapping(owner).deriveMapping(spender).asUint256();
}
}

View File

@ -34,7 +34,8 @@ 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. Also include primitives for reading from and writing to transient storage (only value types are currently supported).
* {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types.
* {TransientSlot}: Primitives for reading from and writing to transient storage (only value types are currently supported).
* {Multicall}: Abstract contract with a utility to allow batching together multiple calls in a single transaction. Useful for allowing EOAs to perform multiple operations at once.
* {Context}: A utility for abstracting the sender and calldata in the current execution context.
* {Packing}: A library for packing and unpacking multiple values into bytes32
@ -130,6 +131,8 @@ Ethereum contracts have no native concept of an interface, so applications must
{{StorageSlot}}
{{TransientSlot}}
{{Multicall}}
{{Context}}

View File

@ -3,7 +3,7 @@
pragma solidity ^0.8.24;
import {StorageSlot} from "./StorageSlot.sol";
import {TransientSlot} from "./TransientSlot.sol";
/**
* @dev Variant of {ReentrancyGuard} that uses transient storage.
@ -13,7 +13,7 @@ import {StorageSlot} from "./StorageSlot.sol";
* _Available since v5.1._
*/
abstract contract ReentrancyGuardTransient {
using StorageSlot for *;
using TransientSlot for *;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =

View File

@ -2,7 +2,7 @@
// OpenZeppelin Contracts (last updated v5.1.0-rc.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,24 +29,6 @@ pragma solidity ^0.8.24;
* }
* ```
*
* 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 {
@ -158,154 +140,4 @@ 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) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(AddressSlotType slot, address value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(BooleanSlotType slot) internal view returns (bool value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(BooleanSlotType slot, bool value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Bytes32SlotType slot) internal view returns (bytes32 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Bytes32SlotType slot, bytes32 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Uint256SlotType slot) internal view returns (uint256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Uint256SlotType slot, uint256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Int256SlotType slot) internal view returns (int256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Int256SlotType slot, int256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
}

View File

@ -0,0 +1,182 @@
// 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 value-types to specific transient storage slots.
*
* Transient slots are often used to store temporary values that are removed after the current transaction.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* * Example reading and writing values using transient storage:
* ```solidity
* contract Lock {
* using TransientSlot for *;
*
* // 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 TransientSlot {
/**
* @dev UDVT that represent a slot holding a address.
*/
type AddressSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a AddressSlot.
*/
function asAddress(bytes32 slot) internal pure returns (AddressSlot) {
return AddressSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bool.
*/
type BooleanSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a BooleanSlot.
*/
function asBoolean(bytes32 slot) internal pure returns (BooleanSlot) {
return BooleanSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bytes32.
*/
type Bytes32Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Bytes32Slot.
*/
function asBytes32(bytes32 slot) internal pure returns (Bytes32Slot) {
return Bytes32Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a uint256.
*/
type Uint256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Uint256Slot.
*/
function asUint256(bytes32 slot) internal pure returns (Uint256Slot) {
return Uint256Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a int256.
*/
type Int256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Int256Slot.
*/
function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
return Int256Slot.wrap(slot);
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(AddressSlot slot) internal view returns (address value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(AddressSlot slot, address value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(BooleanSlot slot) internal view returns (bool value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(BooleanSlot slot, bool value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Bytes32Slot slot, bytes32 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Uint256Slot slot) internal view returns (uint256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Uint256Slot slot, uint256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Int256Slot slot) internal view returns (int256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Int256Slot slot, int256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
}

View File

@ -319,7 +319,7 @@ function _setImplementation(address newImplementation) internal {
}
----
The xref:api:utils.adoc#StorageSlot[`StorageSlot`] library also supports transient storage through user defined value types (https://docs.soliditylang.org/en/latest/types.html#user-defined-value-types[UDVTs]), which enables the same value types as in Solidity.
The xref:api:utils.adoc#TransientSlot[`TransientSlot`] library supports transient storage through user defined value types (https://docs.soliditylang.org/en/latest/types.html#user-defined-value-types[UDVTs]), which enables the same value types as in Solidity.
[source,solidity]
----

View File

@ -39,9 +39,11 @@ for (const [file, template] of Object.entries({
'utils/structs/EnumerableMap.sol': './templates/EnumerableMap.js',
'utils/SlotDerivation.sol': './templates/SlotDerivation.js',
'utils/StorageSlot.sol': './templates/StorageSlot.js',
'utils/TransientSlot.sol': './templates/TransientSlot.js',
'utils/Arrays.sol': './templates/Arrays.js',
'utils/Packing.sol': './templates/Packing.js',
'mocks/StorageSlotMock.sol': './templates/StorageSlotMock.js',
'mocks/TransientSlotMock.sol': './templates/TransientSlotMock.js',
})) {
generateFromTemplate(file, template, './contracts/');
}

View File

@ -2,7 +2,7 @@ const format = require('../format-lines');
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.
@ -29,24 +29,6 @@ pragma solidity ^0.8.24;
* }
* \`\`\`
*
* 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}.
*/
`;
@ -81,40 +63,6 @@ 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}(bytes32 slot) internal pure returns (${name}SlotType) {
return ${name}SlotType.wrap(slot);
}
`;
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) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store \`value\` at location \`slot\` in transient storage.
*/
function tstore(${name}SlotType slot, ${type} value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
`;
// GENERATE
module.exports = format(
header.trimEnd(),
@ -123,8 +71,6 @@ module.exports = format(
[].concat(
TYPES.map(type => struct(type)),
TYPES.flatMap(type => [get(type), !type.isValueType && getStorage(type)].filter(Boolean)),
TYPES.filter(type => type.isValueType).map(type => udvt(type)),
TYPES.filter(type => type.isValueType).map(type => transient(type)),
),
).trimEnd(),
'}',

View File

@ -2,7 +2,7 @@ const format = require('../format-lines');
const { TYPES } = require('./Slot.opts');
const header = `\
pragma solidity ^0.8.24;
pragma solidity ^0.8.20;
import {Multicall} from "../utils/Multicall.sol";
import {StorageSlot} from "../utils/StorageSlot.sol";
@ -40,18 +40,6 @@ function get${name}Storage(uint256 key) public view returns (${type} memory) {
}
`;
const transient = ({ type, name }) => `\
event ${name}Value(bytes32 slot, ${type} value);
function tload${name}(bytes32 slot) public {
emit ${name}Value(slot, slot.as${name}().tload());
}
function tstore(bytes32 slot, ${type} value) public {
slot.as${name}().tstore(value);
}
`;
// GENERATE
module.exports = format(
header,
@ -63,7 +51,6 @@ module.exports = format(
TYPES.filter(type => type.isValueType).map(type => storageSetValueType(type)),
TYPES.filter(type => type.isValueType).map(type => storageGetValueType(type)),
TYPES.filter(type => !type.isValueType).map(type => storageSetNonValueType(type)),
TYPES.filter(type => type.isValueType).map(type => transient(type)),
),
).trimEnd(),
'}',

View File

@ -0,0 +1,80 @@
const format = require('../format-lines');
const { TYPES } = require('./Slot.opts');
const header = `\
pragma solidity ^0.8.24;
/**
* @dev Library for reading and writing value-types to specific transient storage slots.
*
* Transient slots are often used to store temporary values that are removed after the current transaction.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* * Example reading and writing values using transient storage:
* \`\`\`solidity
* contract Lock {
* using TransientSlot for *;
*
* // 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}.
*/
`;
const udvt = ({ type, name }) => `\
/**
* @dev UDVT that represent a slot holding a ${type}.
*/
type ${name}Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a ${name}Slot.
*/
function as${name}(bytes32 slot) internal pure returns (${name}Slot) {
return ${name}Slot.wrap(slot);
}
`;
const transient = ({ type, name }) => `\
/**
* @dev Load the value held at location \`slot\` in transient storage.
*/
function tload(${name}Slot slot) internal view returns (${type} value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store \`value\` at location \`slot\` in transient storage.
*/
function tstore(${name}Slot slot, ${type} value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
`;
// GENERATE
module.exports = format(
header.trimEnd(),
'library TransientSlot {',
format(
[].concat(
TYPES.filter(type => type.isValueType).map(type => udvt(type)),
TYPES.filter(type => type.isValueType).map(type => transient(type)),
),
).trimEnd(),
'}',
);

View File

@ -0,0 +1,35 @@
const format = require('../format-lines');
const { TYPES } = require('./Slot.opts');
const header = `\
pragma solidity ^0.8.24;
import {Multicall} from "../utils/Multicall.sol";
import {TransientSlot} from "../utils/TransientSlot.sol";
`;
const transient = ({ type, name }) => `\
event ${name}Value(bytes32 slot, ${type} value);
function tload${name}(bytes32 slot) public {
emit ${name}Value(slot, slot.as${name}().tload());
}
function tstore(bytes32 slot, ${type} value) public {
slot.as${name}().tstore(value);
}
`;
// GENERATE
module.exports = format(
header,
'contract TransientSlotMock is Multicall {',
format(
[].concat(
'using TransientSlot for *;',
'',
TYPES.filter(type => type.isValueType).map(type => transient(type)),
),
).trimEnd(),
'}',
);

View File

@ -70,37 +70,4 @@ describe('StorageSlot', function () {
});
});
}
for (const { name, type, value, zero } of TYPES.filter(type => type.isValueType)) {
describe(`${type} transient slot`, function () {
const load = `tload${name}(bytes32)`;
const store = `tstore(bytes32,${type})`;
const event = `${name}Value`;
it('load', async function () {
await expect(this.mock[load](slot)).to.emit(this.mock, event).withArgs(slot, zero);
});
it('store and load (2 txs)', async function () {
await this.mock[store](slot, value);
await expect(this.mock[load](slot)).to.emit(this.mock, event).withArgs(slot, zero);
});
it('store and load (batched)', async function () {
await expect(
this.mock.multicall([
this.mock.interface.encodeFunctionData(store, [slot, value]),
this.mock.interface.encodeFunctionData(load, [slot]),
this.mock.interface.encodeFunctionData(load, [otherSlot]),
]),
)
.to.emit(this.mock, event)
.withArgs(slot, value)
.to.emit(this.mock, event)
.withArgs(otherSlot, zero);
await expect(this.mock[load](slot)).to.emit(this.mock, event).withArgs(slot, zero);
});
});
}
});

View File

@ -0,0 +1,59 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { generators } = require('../helpers/random');
const slot = ethers.id('some.storage.slot');
const otherSlot = ethers.id('some.other.storage.slot');
// Non-value types are not supported by the `TransientSlot` library.
const TYPES = [
{ name: 'Boolean', type: 'bool', value: true, zero: false },
{ name: 'Address', type: 'address', value: generators.address(), zero: generators.address.zero },
{ name: 'Bytes32', type: 'bytes32', value: generators.bytes32(), zero: generators.bytes32.zero },
{ name: 'Uint256', type: 'uint256', value: generators.uint256(), zero: generators.uint256.zero },
{ name: 'Int256', type: 'int256', value: generators.int256(), zero: generators.int256.zero },
];
async function fixture() {
return { mock: await ethers.deployContract('TransientSlotMock') };
}
describe('TransientSlot', function () {
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
for (const { name, type, value, zero } of TYPES) {
describe(`${type} transient slot`, function () {
const load = `tload${name}(bytes32)`;
const store = `tstore(bytes32,${type})`;
const event = `${name}Value`;
it('load', async function () {
await expect(this.mock[load](slot)).to.emit(this.mock, event).withArgs(slot, zero);
});
it('store and load (2 txs)', async function () {
await this.mock[store](slot, value);
await expect(this.mock[load](slot)).to.emit(this.mock, event).withArgs(slot, zero);
});
it('store and load (batched)', async function () {
await expect(
this.mock.multicall([
this.mock.interface.encodeFunctionData(store, [slot, value]),
this.mock.interface.encodeFunctionData(load, [slot]),
this.mock.interface.encodeFunctionData(load, [otherSlot]),
]),
)
.to.emit(this.mock, event)
.withArgs(slot, value)
.to.emit(this.mock, event)
.withArgs(otherSlot, zero);
await expect(this.mock[load](slot)).to.emit(this.mock, event).withArgs(slot, zero);
});
});
}
});