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

Custom types code standardization & tests #132

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions contracts/libraries/AssetsAccounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,10 @@ library AssetsAccounting {
_checkNonZeroShares(stETHSharesToWithdraw);

assets.stETHLockedShares = SharesValues.ZERO;
ethWithdrawn =
SharesValues.calcETHValue(self.stETHTotals.claimedETH, stETHSharesToWithdraw, self.stETHTotals.lockedShares);
ethWithdrawn = ETHValues.from(
self.stETHTotals.claimedETH.toUint256() * stETHSharesToWithdraw.toUint256()
/ self.stETHTotals.lockedShares.toUint256()
);

emit ETHWithdrawn(holder, stETHSharesToWithdraw, ethWithdrawn);
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/libraries/DualGovernanceConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ library DualGovernanceConfig {

return vetoSignallingMinDuration
+ Durations.from(
PercentD16.unwrap(rageQuitSupport - firstSealRageQuitSupport)
(rageQuitSupport - firstSealRageQuitSupport).toUint256()
* (vetoSignallingMaxDuration - vetoSignallingMinDuration).toSeconds()
/ PercentD16.unwrap(secondSealRageQuitSupport - firstSealRageQuitSupport)
/ (secondSealRageQuitSupport - firstSealRageQuitSupport).toUint256()
);
}

Expand Down
132 changes: 87 additions & 45 deletions contracts/types/Duration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,37 @@ pragma solidity 0.8.26;

import {Timestamp, Timestamps} from "./Timestamp.sol";

// ---
// Type Definition
// ---

type Duration is uint32;

// ---
// Assign Global Operations
// ---

using {lt as <, lte as <=, eq as ==, neq as !=, gte as >=, gt as >} for Duration global;
using {addTo, plusSeconds, minusSeconds, multipliedBy, dividedBy, toSeconds} for Duration global;
using {plus as +, minus as -} for Duration global;

// ---
// Errors
// ---

error DivisionByZero();
error DurationOverflow();
error DurationUnderflow();

// the max possible duration is ~ 106 years
uint256 constant MAX_VALUE = type(uint32).max;
// ---
// Constants
// ---

using {lt as <, lte as <=, gt as >, gte as >=, eq as ==, notEq as !=} for Duration global;
using {plus as +, minus as -} for Duration global;
using {addTo, plusSeconds, minusSeconds, multipliedBy, dividedBy, toSeconds} for Duration global;
/// @dev The maximum possible duration is approximately 136 years (assuming 365 days per year).
uint32 constant MAX_DURATION_VALUE = type(uint32).max;

// ---
// Comparison Ops
// Comparison Operations
// ---

function lt(Duration d1, Duration d2) pure returns (bool) {
Expand All @@ -27,88 +44,113 @@ function lte(Duration d1, Duration d2) pure returns (bool) {
return Duration.unwrap(d1) <= Duration.unwrap(d2);
}

function gt(Duration d1, Duration d2) pure returns (bool) {
return Duration.unwrap(d1) > Duration.unwrap(d2);
function eq(Duration d1, Duration d2) pure returns (bool) {
return Duration.unwrap(d1) == Duration.unwrap(d2);
}

function neq(Duration d1, Duration d2) pure returns (bool) {
return Duration.unwrap(d1) != Duration.unwrap(d2);
}

function gte(Duration d1, Duration d2) pure returns (bool) {
return Duration.unwrap(d1) >= Duration.unwrap(d2);
}

function eq(Duration d1, Duration d2) pure returns (bool) {
return Duration.unwrap(d1) == Duration.unwrap(d2);
function gt(Duration d1, Duration d2) pure returns (bool) {
return Duration.unwrap(d1) > Duration.unwrap(d2);
}

function notEq(Duration d1, Duration d2) pure returns (bool) {
return !(d1 == d2);
// ---
// Conversion Operations
// ---

function toSeconds(Duration d) pure returns (uint256) {
return Duration.unwrap(d);
}

// ---
// Arithmetic Operations
// ---

function plus(Duration d1, Duration d2) pure returns (Duration) {
return toDuration(Duration.unwrap(d1) + Duration.unwrap(d2));
unchecked {
/// @dev Both `d1.toSeconds()` and `d2.toSeconds()` are <= type(uint32).max. Therefore, their
/// sum is <= type(uint256).max.
return Durations.from(d1.toSeconds() + d2.toSeconds());
}
}

function minus(Duration d1, Duration d2) pure returns (Duration) {
if (d1 < d2) {
uint256 d1Seconds = d1.toSeconds();
uint256 d2Seconds = d2.toSeconds();

if (d1Seconds < d2Seconds) {
revert DurationUnderflow();
}
return Duration.wrap(Duration.unwrap(d1) - Duration.unwrap(d2));

unchecked {
/// @dev Subtraction is safe because `d1Seconds` >= `d2Seconds`.
/// Both `d1Seconds` and `d2Seconds` <= `type(uint32).max`, so the difference fits within `uint32`.
return Duration.wrap(uint32(d1Seconds - d2Seconds));
}
}

function plusSeconds(Duration d, uint256 seconds_) pure returns (Duration) {
return toDuration(Duration.unwrap(d) + seconds_);
// ---
// Custom Operations
// ---

function plusSeconds(Duration d, uint256 secondsToAdd) pure returns (Duration) {
return Durations.from(d.toSeconds() + secondsToAdd);
}

function minusSeconds(Duration d, uint256 seconds_) pure returns (Duration) {
uint256 durationValue = Duration.unwrap(d);
if (durationValue < seconds_) {
function minusSeconds(Duration d, uint256 secondsToSubtract) pure returns (Duration) {
uint256 durationSeconds = d.toSeconds();

if (durationSeconds < secondsToSubtract) {
revert DurationUnderflow();
}
return Duration.wrap(uint32(durationValue - seconds_));

unchecked {
/// @dev Subtraction is safe because `durationSeconds` >= `secondsToSubtract`.
/// Both `durationSeconds` and `secondsToSubtract` <= `type(uint32).max`,
/// so the difference fits within `uint32`.
return Duration.wrap(uint32(durationSeconds - secondsToSubtract));
}
}

function dividedBy(Duration d, uint256 divisor) pure returns (Duration) {
return Duration.wrap(uint32(Duration.unwrap(d) / divisor));
if (divisor == 0) {
revert DivisionByZero();
}
return Duration.wrap(uint32(d.toSeconds() / divisor));
}

function multipliedBy(Duration d, uint256 multiplicand) pure returns (Duration) {
return toDuration(Duration.unwrap(d) * multiplicand);
return Durations.from(multiplicand * d.toSeconds());
}

function addTo(Duration d, Timestamp t) pure returns (Timestamp) {
return Timestamps.from(t.toSeconds() + d.toSeconds());
unchecked {
/// @dev Both `t.toSeconds()` <= `type(uint40).max` and `d.toSeconds()` <= `type(uint32).max`, so their
/// sum fits within `uint256`.
return Timestamps.from(t.toSeconds() + d.toSeconds());
}
}

// ---
// Conversion Ops
// Namespaced Helper Methods
// ---

function toDuration(uint256 value) pure returns (Duration) {
if (value > MAX_VALUE) {
revert DurationOverflow();
}
return Duration.wrap(uint32(value));
}

function toSeconds(Duration d) pure returns (uint256) {
return Duration.unwrap(d);
}

library Durations {
Duration internal constant ZERO = Duration.wrap(0);

Duration internal constant MIN = ZERO;
Duration internal constant MAX = Duration.wrap(uint32(MAX_VALUE));

function from(uint256 seconds_) internal pure returns (Duration res) {
res = toDuration(seconds_);
}

function between(Timestamp t1, Timestamp t2) internal pure returns (Duration res) {
res = toDuration(t1.toSeconds() - t2.toSeconds());
function from(uint256 durationInSeconds) internal pure returns (Duration res) {
if (durationInSeconds > MAX_DURATION_VALUE) {
revert DurationOverflow();
}
/// @dev Casting `durationInSeconds` to `uint32` is safe as the check ensures it is less than or equal
/// to `MAX_DURATION_VALUE`, which fits within the `uint32`.
res = Duration.wrap(uint32(durationInSeconds));
}

function min(Duration d1, Duration d2) internal pure returns (Duration res) {
Expand Down
94 changes: 73 additions & 21 deletions contracts/types/ETHValue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,41 @@ pragma solidity 0.8.26;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";

// ---
// Type Definition
// ---

type ETHValue is uint128;

// ---
// Assign Global Operations
// ---

using {lt as <, eq as ==, neq as !=, gt as >} for ETHValue global;
using {toUint256, sendTo} for ETHValue global;
using {plus as +, minus as -} for ETHValue global;

// ---
// Errors
// ---

error ETHValueOverflow();
error ETHValueUnderflow();

using {plus as +, minus as -, lt as <, gt as >, eq as ==, neq as !=} for ETHValue global;
using {toUint256} for ETHValue global;
using {sendTo} for ETHValue global;
// ---
// Constants
// ---

function sendTo(ETHValue value, address payable recipient) {
Address.sendValue(recipient, value.toUint256());
}
uint128 constant MAX_ETH_VALUE = type(uint128).max;

function plus(ETHValue v1, ETHValue v2) pure returns (ETHValue) {
return ETHValues.from(ETHValue.unwrap(v1) + ETHValue.unwrap(v2));
}

function minus(ETHValue v1, ETHValue v2) pure returns (ETHValue) {
if (v1 < v2) {
revert ETHValueUnderflow();
}
return ETHValues.from(ETHValue.unwrap(v1) - ETHValue.unwrap(v2));
}
// ---
// Comparison Operations
// ---

function lt(ETHValue v1, ETHValue v2) pure returns (bool) {
return ETHValue.unwrap(v1) < ETHValue.unwrap(v2);
}

function gt(ETHValue v1, ETHValue v2) pure returns (bool) {
return ETHValue.unwrap(v1) > ETHValue.unwrap(v2);
}

function eq(ETHValue v1, ETHValue v2) pure returns (bool) {
return ETHValue.unwrap(v1) == ETHValue.unwrap(v2);
}
Expand All @@ -43,17 +46,66 @@ function neq(ETHValue v1, ETHValue v2) pure returns (bool) {
return ETHValue.unwrap(v1) != ETHValue.unwrap(v2);
}

function gt(ETHValue v1, ETHValue v2) pure returns (bool) {
return ETHValue.unwrap(v1) > ETHValue.unwrap(v2);
}

// ---
// Conversion Operations
// ---

function toUint256(ETHValue value) pure returns (uint256) {
return ETHValue.unwrap(value);
}

// ---
// Arithmetic Operations
// ---

function plus(ETHValue v1, ETHValue v2) pure returns (ETHValue) {
unchecked {
/// @dev Both `v1.toUint256()` and `v2.toUint256()` are <= type(uint128).max. Therefore, their
/// sum is <= type(uint256).max.
return ETHValues.from(v1.toUint256() + v2.toUint256());
}
}

function minus(ETHValue v1, ETHValue v2) pure returns (ETHValue) {
uint256 v1Value = v1.toUint256();
uint256 v2Value = v2.toUint256();

if (v1Value < v2Value) {
revert ETHValueUnderflow();
}

unchecked {
/// @dev Subtraction is safe because `v1Value` >= `v2Value`.
/// Both `v1Value` and `v2Value` <= `type(uint128).max`, so the difference fits within `uint128`.
return ETHValue.wrap(uint128(v1Value - v2Value));
}
}

// ---
// Custom Operations
// ---

function sendTo(ETHValue value, address payable recipient) {
Address.sendValue(recipient, value.toUint256());
}

// ---
// Namespaced Helper Methods
// ---

library ETHValues {
ETHValue internal constant ZERO = ETHValue.wrap(0);

function from(uint256 value) internal pure returns (ETHValue) {
if (value > type(uint128).max) {
if (value > MAX_ETH_VALUE) {
revert ETHValueOverflow();
}
/// @dev Casting `value` to `uint128` is safe as the check ensures it is less than or equal
/// to `MAX_ETH_VALUE`, which fits within the `uint128`.
return ETHValue.wrap(uint128(value));
}

Expand Down
Loading