-
Notifications
You must be signed in to change notification settings - Fork 976
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
use custom storage for lockData #353
Changes from 2 commits
75769dd
ed5b1db
b26114a
d7146a0
bfb45fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
96005 | ||
95788 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
153358 | ||
152900 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
58562 | ||
58632 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
38009 | ||
37964 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
307152 | ||
306957 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
275102 | ||
274907 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
293760 | ||
293565 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
49720 | ||
49766 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
124982 | ||
124524 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
109692 | ||
109234 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
91502 | ||
91548 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
49691 | ||
49737 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
49720 | ||
49766 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,49 +2,91 @@ | |
pragma solidity ^0.8.20; | ||
|
||
import {IPoolManager} from "../interfaces/IPoolManager.sol"; | ||
import {LockData} from "../types/LockData.sol"; | ||
|
||
/// @dev This library manages a custom storage implementation for a queue | ||
/// that tracks current lockers. The "sentinel" storage slot for this data structure, | ||
/// always passed in as IPoolManager.LockData storage self, stores not just the current | ||
/// that tracks current lockers. The LOCK_DATA storage slot for this data structure, | ||
/// passed in as a LockData custom type that is a uint256, stores not just the current | ||
/// length of the queue but also the global count of non-zero deltas across all lockers. | ||
/// The values of the data structure start at OFFSET, and each value is a locker address. | ||
/// The values of the data structure start at LOCKERS, and each value is a locker address. | ||
library LockDataLibrary { | ||
uint256 private constant OFFSET = uint256(keccak256("LockData")); | ||
using LockDataLibrary for LockData; | ||
|
||
/// @dev Pushes a locker onto the end of the queue, and updates the sentinel storage slot. | ||
function push(IPoolManager.LockData storage self, address locker) internal { | ||
// read current value from the sentinel storage slot | ||
uint128 length = self.length; | ||
uint256 private constant LOCK_DATA = uint256(keccak256("LockData")); | ||
uint256 private constant LOCKERS = uint256(keccak256("Lockers")); | ||
|
||
/// @dev Pushes a locker onto the end of the queue at the LOCKERS + length storage slot, and updates the length at the LOCK_DATA storage slot. | ||
function push(LockData lockData, address locker) internal { | ||
// read current length from the LOCK_DATA storage slot | ||
uint128 _length = lockData.length(); | ||
unchecked { | ||
uint256 indexToWrite = OFFSET + length; // not in assembly because OFFSET is in the library scope | ||
uint256 lockerSlot = LOCKERS + _length; // not in assembly because LOCKERS is in the library scope | ||
|
||
/// @solidity memory-safe-assembly | ||
assembly { | ||
// in the next storage slot, write the locker | ||
sstore(indexToWrite, locker) | ||
sstore(lockerSlot, locker) | ||
} | ||
// update the sentinel storage slot | ||
self.length = length + 1; | ||
lockData.update(_length + 1, lockData.nonzeroDeltaCount()); | ||
} | ||
} | ||
|
||
/// @dev Pops a locker off the end of the queue. Note that no storage gets cleared. | ||
function pop(IPoolManager.LockData storage self) internal { | ||
function pop(LockData lockData) internal { | ||
unchecked { | ||
self.length--; | ||
lockData.update(lockData.length() - 1, lockData.nonzeroDeltaCount()); | ||
} | ||
} | ||
|
||
function length(LockData lockData) internal pure returns (uint128 _length) { | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
_length := shr(128, lockData) | ||
} | ||
} | ||
|
||
function nonzeroDeltaCount(LockData lockData) internal pure returns (uint128 _nonzeroDeltaCount) { | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
_nonzeroDeltaCount := lockData | ||
} | ||
} | ||
|
||
function update(LockData lockData, uint128 _length, uint128 _nonzeroDeltaCount) internal { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for readability/less footguns (there's certain parameter combinations here that are unacceptable), wonder if we can break this into 3 functions: probably at the expense of some bytecode savings, but I think it would make a lot of the code nicer to look at, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok i added these functions but they all use similar code paths.. which I separated into |
||
uint256 lockDataSlot = LOCK_DATA; | ||
assembly { | ||
lockData := | ||
or( | ||
shl(128, _length), | ||
and(0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff, _nonzeroDeltaCount) | ||
) | ||
|
||
sstore(lockDataSlot, lockData) | ||
} | ||
} | ||
|
||
function getLock(uint256 i) internal view returns (address locker) { | ||
unchecked { | ||
uint256 position = OFFSET + i; // not in assembly because OFFSET is in the library scope | ||
uint256 position = LOCKERS + i; // not in assembly because LOCKERS is in the library scope | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
locker := sload(position) | ||
} | ||
} | ||
} | ||
|
||
function getActiveLock(IPoolManager.LockData storage self) internal view returns (address locker) { | ||
return getLock(self.length - 1); | ||
function getActiveLock(LockData lockData) internal view returns (address) { | ||
uint128 _length; | ||
unchecked { | ||
_length = lockData.length() - 1; | ||
} | ||
return getLock(_length); | ||
} | ||
|
||
function getLockData() internal view returns (LockData lockData) { | ||
uint256 lockDataSlot = LOCK_DATA; | ||
assembly { | ||
lockData := sload(lockDataSlot) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
pragma solidity ^0.8.20; | ||
|
||
import {LockDataLibrary} from "../libraries/LockDataLibrary.sol"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. keeping this in a separate file is breaking convention with types Currency + BalanceDelta. Wonder if it's worth migrating the library here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moved it to types/LockData.sol to match |
||
|
||
/// @notice The LockData holds global information for the lock data structure: the length and nonzeroDeltaCount. | ||
/// @dev The left most 128 bits is the length, or total number of active lockers. | ||
/// @dev The right most 128 bits is the nonzeroDeltaCount, or total number of nonzero deltas over all active + completed lockers. | ||
/// @dev Located in transient storage. | ||
type LockData is uint256; | ||
|
||
using LockDataLibrary for LockData global; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something definitely feels a bit weird to me about using pop/push (write functionality) on a type memory uint256 that actually does storage writes. I wish there was a nice way to return a storage reference/pointer here instead of recaching below. Feels a bit off but can't think of any great solutions...this + update() feel a big "loosey goosey" if you will lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it is a bit wonky..let me know if you can think of another way, but I had to move away from a library for storage pointers because we don't yet have the
transient
keyword.We can rewrite the lib to use the transient keyword when there is solidity support.