-
Notifications
You must be signed in to change notification settings - Fork 61
Feature/safe transient #239
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
Open
ifelsedeveloper
wants to merge
13
commits into
master
Choose a base branch
from
feature/safe-transient
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
74d99c4
feat: add OFFSET to prevent storage collision in TransientLib
ifelsedeveloper 176179f
feat: implement TransientUnsafe library with arithmetic operations an…
ifelsedeveloper bf966c9
chore: bump version to 6.9.6
ifelsedeveloper 1e09d9f
fix: correct import statement and update gasUsed function type in Tra…
ifelsedeveloper dd7ace7
test: enhance Transient tests with comments on transient storage beha…
ifelsedeveloper fe625ab
refactor: rename gasUsed function to executionGas and update gas calc…
ifelsedeveloper fffc457
feat: add TransientLock and TransientLockUnsafe libraries for managin…
ifelsedeveloper dd89e1c
feat: add TransientLockUnsafeMock contract and enhance TransientLock …
ifelsedeveloper b419f60
refactor: rename deployMock function to deployTransientMock and updat…
ifelsedeveloper 3b1800e
refactor: update deployTransientMock function to include type annotat…
ifelsedeveloper 717da9c
Merge branch 'master' into feature/safe-transient
ifelsedeveloper 06e228b
Merge branch 'master' into feature/safe-transient
ifelsedeveloper 6370eb8
Enhance documentation in TransientUnsafe.sol to clarify potential slo…
ifelsedeveloper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.30; | ||
|
|
||
| import { TransientUnsafe, tuint256 } from "./TransientUnsafe.sol"; | ||
| import { TransientLock } from "./TransientLock.sol"; | ||
|
|
||
| /** | ||
| * @title TransientLockUnsafeLib | ||
| * @dev Library for managing transient storage locks without slot offset. | ||
| * Provides gas-efficient reentrancy protection using raw storage slots. | ||
| * Safe for use with mappings where slots are already keccak256-hashed. | ||
| * WARNING: Do not use for simple struct fields — use TransientLockLib instead. | ||
| */ | ||
| library TransientLockUnsafeLib { | ||
| using TransientUnsafe for tuint256; | ||
|
|
||
| /** | ||
| * @dev Error thrown when attempting to lock an already locked state. | ||
| */ | ||
| error UnexpectedLock(); | ||
|
|
||
| /** | ||
| * @dev Error thrown when attempting to unlock a non-locked state. | ||
| */ | ||
| error UnexpectedUnlock(); | ||
|
|
||
| uint256 constant private _UNLOCKED = 0; | ||
| uint256 constant private _LOCKED = 1; | ||
|
|
||
| /** | ||
| * @dev Acquires the lock. Reverts with UnexpectedLock if already locked. | ||
| * @param self The transient lock to acquire. | ||
| */ | ||
| function lock(TransientLock storage self) internal { | ||
| require(self._raw.inc() == _LOCKED, UnexpectedLock()); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Releases the lock. Reverts with UnexpectedUnlock if not currently locked. | ||
| * @param self The transient lock to release. | ||
| */ | ||
| function unlock(TransientLock storage self) internal { | ||
| self._raw.dec(UnexpectedUnlock.selector); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Checks if the lock is currently held. | ||
| * @param self The transient lock to check. | ||
| * @return True if the lock is held, false otherwise. | ||
| */ | ||
| function isLocked(TransientLock storage self) internal view returns (bool) { | ||
| return self._raw.tload() == _LOCKED; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,195 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.30; | ||
|
|
||
| import { tuint256, taddress, tbytes32 } from "./Transient.sol"; | ||
|
|
||
| /** | ||
| * @title TransientUnsafe | ||
| * @dev Library for transient storage without slot offset. | ||
| * Uses raw storage slots directly, saving 6 gas per access on dynamic slots (mappings). | ||
| * Safe for use with mappings where slots are already keccak256-hashed and cannot collide | ||
| * storage slots can collide with native `transient` keyword variables because | ||
| * transient storage has it is own space and storage slots for transient storage also starts from zero, | ||
| * so when we use transient lib which operates storage slots indexes from storage | ||
| * and collides if use transient lib for a varible with static storage index | ||
| * with Solidity's `transient` keyword (which does not support mappings). | ||
| * WARNING: Do not use for simple struct fields — use TransientLib instead to avoid | ||
| * potential slot collisions with native `transient` keyword variables. | ||
| */ | ||
| library TransientUnsafe { | ||
| /** | ||
| * @dev Error thrown when increment would cause overflow. | ||
| */ | ||
| error MathOverflow(); | ||
|
|
||
| /** | ||
| * @dev Error thrown when decrement would cause underflow. | ||
| */ | ||
| error MathUnderflow(); | ||
|
|
||
| // ===================== Functions for tuint256 ===================== | ||
|
|
||
| /** | ||
| * @dev Loads a uint256 value from transient storage. | ||
| * @param self The transient uint256 storage slot. | ||
| * @return ret The value stored in transient storage. | ||
| */ | ||
| function tload(tuint256 storage self) internal view returns (uint256 ret) { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| ret := tload(self.slot) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Stores a uint256 value to transient storage. | ||
| * @param self The transient uint256 storage slot. | ||
| * @param value The value to store. | ||
| */ | ||
| function tstore(tuint256 storage self, uint256 value) internal { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| tstore(self.slot, value) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Increments the transient uint256 value by 1 with overflow check. | ||
| * Reverts with MathOverflow if the value would overflow. | ||
| * @param self The transient uint256 storage slot. | ||
| * @return incremented The new value after incrementing. | ||
| */ | ||
| function inc(tuint256 storage self) internal returns (uint256 incremented) { | ||
| return inc(self, TransientUnsafe.MathOverflow.selector); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Increments the transient uint256 value by 1 with custom overflow error. | ||
| * @param self The transient uint256 storage slot. | ||
| * @param exception The error selector to revert with on overflow. | ||
| * @return incremented The new value after incrementing. | ||
| */ | ||
| function inc(tuint256 storage self, bytes4 exception) internal returns (uint256 incremented) { | ||
| incremented = unsafeInc(self); | ||
| if (incremented == 0) { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| mstore(0, exception) | ||
| revert(0, 4) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Increments the transient uint256 value by 1 without overflow check. | ||
| * Warning: May overflow silently. | ||
| * @param self The transient uint256 storage slot. | ||
| * @return incremented The new value after incrementing. | ||
| */ | ||
| function unsafeInc(tuint256 storage self) internal returns (uint256 incremented) { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| incremented := add(tload(self.slot), 1) | ||
| tstore(self.slot, incremented) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Decrements the transient uint256 value by 1 with underflow check. | ||
| * Reverts with MathUnderflow if the value would underflow. | ||
| * @param self The transient uint256 storage slot. | ||
| * @return decremented The new value after decrementing. | ||
| */ | ||
| function dec(tuint256 storage self) internal returns (uint256 decremented) { | ||
| return dec(self, TransientUnsafe.MathUnderflow.selector); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Decrements the transient uint256 value by 1 with custom underflow error. | ||
| * @param self The transient uint256 storage slot. | ||
| * @param exception The error selector to revert with on underflow. | ||
| * @return decremented The new value after decrementing. | ||
| */ | ||
| function dec(tuint256 storage self, bytes4 exception) internal returns (uint256 decremented) { | ||
| decremented = unsafeDec(self); | ||
| if (decremented == type(uint256).max) { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| mstore(0, exception) | ||
| revert(0, 4) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Decrements the transient uint256 value by 1 without underflow check. | ||
| * Warning: May underflow silently. | ||
| * @param self The transient uint256 storage slot. | ||
| * @return decremented The new value after decrementing. | ||
| */ | ||
| function unsafeDec(tuint256 storage self) internal returns (uint256 decremented) { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| decremented := sub(tload(self.slot), 1) | ||
| tstore(self.slot, decremented) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Initializes with a value if zero, then adds to the transient uint256. | ||
| * @param self The transient uint256 storage slot. | ||
| * @param initialValue The value to use if current value is zero. | ||
| * @param toAdd The value to add. | ||
| * @return result The final value after initialization and addition. | ||
| */ | ||
| function initAndAdd(tuint256 storage self, uint256 initialValue, uint256 toAdd) internal returns (uint256 result) { | ||
| result = tload(self); | ||
| if (result == 0) { | ||
| result = initialValue; | ||
| } | ||
| result += toAdd; | ||
| tstore(self, result); | ||
| } | ||
|
|
||
| // ===================== Functions for taddress ===================== | ||
|
|
||
| /** | ||
| * @dev Loads an address value from transient storage. | ||
| * @param self The transient address storage slot. | ||
| * @return ret The address stored in transient storage. | ||
| */ | ||
| function tload(taddress storage self) internal view returns (address ret) { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| ret := tload(self.slot) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Stores an address value to transient storage. | ||
| * @param self The transient address storage slot. | ||
| * @param value The address to store. | ||
| */ | ||
| function tstore(taddress storage self, address value) internal { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| tstore(self.slot, value) | ||
| } | ||
| } | ||
|
|
||
| // ===================== Functions for tbytes32 ===================== | ||
|
|
||
| /** | ||
| * @dev Loads a bytes32 value from transient storage. | ||
| * @param self The transient bytes32 storage slot. | ||
| * @return ret The bytes32 value stored in transient storage. | ||
| */ | ||
| function tload(tbytes32 storage self) internal view returns (bytes32 ret) { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| ret := tload(self.slot) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Stores a bytes32 value to transient storage. | ||
| * @param self The transient bytes32 storage slot. | ||
| * @param value The bytes32 value to store. | ||
| */ | ||
| function tstore(tbytes32 storage self, bytes32 value) internal { | ||
| assembly ("memory-safe") { // solhint-disable-line no-inline-assembly | ||
| tstore(self.slot, value) | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.30; | ||
|
|
||
| import "../../libraries/TransientLock.sol"; | ||
|
|
||
| contract TransientLockNestedMock { | ||
| using TransientLockLib for TransientLock; | ||
|
|
||
| mapping(address maker => mapping(bytes32 strategyHash => TransientLock)) internal _reentrancyLocks; | ||
|
|
||
| function lock(address maker, bytes32 strategyHash) external { | ||
| _reentrancyLocks[maker][strategyHash].lock(); | ||
| } | ||
|
|
||
| function unlock(address maker, bytes32 strategyHash) external { | ||
| _reentrancyLocks[maker][strategyHash].unlock(); | ||
| } | ||
|
|
||
| function isLocked(address maker, bytes32 strategyHash) external view returns (bool) { | ||
| return _reentrancyLocks[maker][strategyHash].isLocked(); | ||
| } | ||
|
|
||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.