From 14e6fbeb764ba12a6c14a0736cf534b62958e20e Mon Sep 17 00:00:00 2001 From: toninorair Date: Thu, 7 Dec 2023 20:42:59 -0500 Subject: [PATCH 01/11] gas optimizations --- src/Protocol.sol | 161 +++++++++++++++++++-------------- src/interfaces/IProtocol.sol | 2 +- src/libs/UIntMath.sol | 26 ++++++ test/Protocol.t.sol | 8 +- test/utils/ProtocolHarness.sol | 26 ++++-- 5 files changed, 139 insertions(+), 84 deletions(-) create mode 100644 src/libs/UIntMath.sol diff --git a/src/Protocol.sol b/src/Protocol.sol index 764f1fc2..28b1d8c0 100644 --- a/src/Protocol.sol +++ b/src/Protocol.sol @@ -6,6 +6,7 @@ import { SignatureChecker } from "../lib/common/src/SignatureChecker.sol"; import { ERC712 } from "../lib/common/src/ERC712.sol"; import { SPOGRegistrarReader } from "./libs/SPOGRegistrarReader.sol"; +import { UIntMath } from "./libs/UIntMath.sol"; import { IContinuousIndexing } from "./interfaces/IContinuousIndexing.sol"; import { IMToken } from "./interfaces/IMToken.sol"; @@ -21,12 +22,25 @@ import { ContinuousIndexing } from "./ContinuousIndexing.sol"; Minting Gateway of M Token for all approved by SPOG and activated minters. */ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { - // TODO: bit-packing struct MintProposal { - uint256 id; // TODO: uint96 or uint48 if 2 additional fields + uint48 id; + uint48 createdAt; address destination; - uint256 amount; - uint256 createdAt; + uint256 amount; // NOTE: it can be uint128, but nothing else is left to pack + } + + struct MinterBasic { + uint128 collateral; + uint128 totalPendingRetrievals; + uint48 lastUpdateInterval; + uint48 updateTimestamp; + uint48 penalizedUntilTimestamp; + uint48 unfrozenTimestamp; + } + + struct OwedM { + uint128 principalOfActive; + uint128 inactive; } /******************************************************************************************************************\ @@ -49,33 +63,26 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { address public immutable mToken; /// @notice Nonce used to generate unique mint proposal IDs. - uint256 internal _mintNonce; + uint48 internal _mintNonce; /// @notice Nonce used to generate unique retrieval proposal IDs. - uint256 internal _retrievalNonce; + uint48 internal _retrievalNonce; /// @notice The total principal amount of active M - uint256 internal _totalPrincipalOfActiveOwedM; + uint128 internal _totalPrincipalOfActiveOwedM; /// @notice The total amount of inactive M, sum of all inactive minter's owed M - uint256 internal _totalInactiveOwedM; + uint128 internal _totalInactiveOwedM; mapping(address minter => bool isActiveMinter) internal _isActiveMinter; - mapping(address minter => MintProposal proposal) internal _mintProposals; - - mapping(address minter => uint256 amount) internal _inactiveOwedM; - mapping(address minter => uint256 principal) internal _principalOfActiveOwedM; + mapping(address minter => MinterBasic basic) internal _minterBasics; - mapping(address minter => uint256 collateral) internal _collaterals; - mapping(address minter => uint256 updateInterval) internal _lastUpdateIntervals; - mapping(address minter => uint256 timestamp) internal _lastCollateralUpdates; - mapping(address minter => uint256 timestamp) internal _penalizedUntilTimestamps; + mapping(address minter => MintProposal proposal) internal _mintProposals; - mapping(address minter => uint256 collateral) internal _totalPendingCollateralRetrievals; - mapping(address minter => mapping(uint256 retrievalId => uint256 amount)) internal _pendingCollateralRetrievals; + mapping(address minter => OwedM owedM) internal _owedM; - mapping(address minter => uint256 timestamp) internal _unfrozenTimestamps; + mapping(address minter => mapping(uint48 retrievalId => uint128 amount)) internal _pendingCollateralRetrievals; /******************************************************************************************************************\ | Modifiers and Constructor | @@ -162,8 +169,9 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { retrievalId_ = ++_retrievalNonce; } - _totalPendingCollateralRetrievals[msg.sender] += collateral_; - _pendingCollateralRetrievals[msg.sender][retrievalId_] = collateral_; + uint128 safeCollateral_ = UIntMath.safe128(collateral_); + _minterBasics[msg.sender].totalPendingRetrievals += safeCollateral_; + _pendingCollateralRetrievals[msg.sender][UIntMath.safe48(retrievalId_)] = safeCollateral_; _revertIfUndercollateralized(msg.sender, 0); @@ -181,7 +189,12 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { mintId_ = ++_mintNonce; } - _mintProposals[msg.sender] = MintProposal(mintId_, destination_, amount_, block.timestamp); + _mintProposals[msg.sender] = MintProposal( + UIntMath.safe48(mintId_), + UIntMath.safe48(block.timestamp), + destination_, + amount_ + ); emit MintProposed(mintId_, msg.sender, amount_, destination_); } @@ -190,7 +203,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { function mintM(uint256 mintId_) external onlyActiveMinter onlyUnfrozenMinter { MintProposal storage mintProposal_ = _mintProposals[msg.sender]; - (uint256 id_, uint256 amount_, uint256 createdAt_, address destination_) = ( + (uint48 id_, uint256 amount_, uint256 createdAt_, address destination_) = ( mintProposal_.id, mintProposal_.amount, mintProposal_.createdAt, @@ -213,8 +226,8 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { emit MintExecuted(mintId_); // Adjust principal of active owed M for minter. - uint256 principalAmount_ = _getPrincipalValue(amount_); - _principalOfActiveOwedM[msg.sender] += principalAmount_; + uint128 principalAmount_ = UIntMath.safe128(_getPrincipalValue(amount_)); + _owedM[msg.sender].principalOfActive += principalAmount_; _totalPrincipalOfActiveOwedM += principalAmount_; IMToken(mToken).mint(destination_, amount_); @@ -258,7 +271,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { frozenUntil_ = block.timestamp + minterFreezeTime(); - emit MinterFrozen(minter_, _unfrozenTimestamps[minter_] = frozenUntil_); + emit MinterFrozen(minter_, _minterBasics[minter_].unfrozenTimestamp = UIntMath.safe48(frozenUntil_)); } /// @inheritdoc IProtocol @@ -278,29 +291,29 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { _revertIfInactiveMinter(minter_); // NOTE: Instead of imposing, calculate penalty and add it to `_inactiveOwedM` to save gas. - inactiveOwedM_ = activeOwedMOf(minter_) + getPenaltyForMissedCollateralUpdates(minter_); + uint128 safeInactiveOwedM_ = UIntMath.safe128( + activeOwedMOf(minter_) + getPenaltyForMissedCollateralUpdates(minter_) + ); - emit MinterDeactivated(minter_, inactiveOwedM_, msg.sender); + emit MinterDeactivated(minter_, safeInactiveOwedM_, msg.sender); - _inactiveOwedM[minter_] += inactiveOwedM_; - _totalInactiveOwedM += inactiveOwedM_; + _owedM[minter_].inactive += safeInactiveOwedM_; + _totalInactiveOwedM += safeInactiveOwedM_; // Adjust total principal of owed M. - _totalPrincipalOfActiveOwedM -= _principalOfActiveOwedM[minter_]; + _totalPrincipalOfActiveOwedM -= _owedM[minter_].principalOfActive; // Reset reasonable aspects of minter's state. delete _isActiveMinter[minter_]; - delete _collaterals[minter_]; - delete _lastUpdateIntervals[minter_]; - delete _lastCollateralUpdates[minter_]; + delete _minterBasics[minter_]; delete _mintProposals[minter_]; - delete _penalizedUntilTimestamps[minter_]; - delete _principalOfActiveOwedM[minter_]; - delete _unfrozenTimestamps[minter_]; + delete _owedM[minter_].principalOfActive; // NOTE: Above functionality already has access to `currentIndex()`, and since the completion of the // deactivation can result in a new rate, we should update the index here to lock in that rate. updateIndex(); + + return safeInactiveOwedM_; } /// @inheritdoc IContinuousIndexing @@ -360,7 +373,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { // TODO: This should also include the present value of unavoidable penalities. But then it would be very, if not // impossible, to determine the `totalActiveOwedM` to the same standards. Perhaps we need a `penaltiesOf` // external function to provide the present value of unavoidable penalities - return _getPresentValue(_principalOfActiveOwedM[minter_]); + return _getPresentValue(_owedM[minter_].principalOfActive); } /// @inheritdoc IProtocol @@ -370,7 +383,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { /// @inheritdoc IProtocol function inactiveOwedMOf(address minter_) external view returns (uint256 inactiveOwedM_) { - return _inactiveOwedM[minter_]; + return _owedM[minter_].inactive; } /// @inheritdoc IProtocol @@ -378,28 +391,28 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { // If collateral was not updated before deadline, assume that minter's collateral is zero. return block.timestamp < collateralUpdateDeadlineOf(minter_) - ? _collaterals[minter_] - _totalPendingCollateralRetrievals[minter_] + ? _minterBasics[minter_].collateral - _minterBasics[minter_].totalPendingRetrievals : 0; } /// @inheritdoc IProtocol function collateralUpdateOf(address minter_) external view returns (uint256 lastUpdate_) { - return _lastCollateralUpdates[minter_]; + return _minterBasics[minter_].updateTimestamp; } /// @inheritdoc IProtocol function collateralUpdateDeadlineOf(address minter_) public view returns (uint256 updateDeadline_) { - return _lastCollateralUpdates[minter_] + _lastUpdateIntervals[minter_]; + return _minterBasics[minter_].updateTimestamp + _minterBasics[minter_].lastUpdateInterval; } /// @inheritdoc IProtocol function lastCollateralUpdateIntervalOf(address minter_) external view returns (uint256 lastUpdateInterval_) { - return _lastUpdateIntervals[minter_]; + return _minterBasics[minter_].lastUpdateInterval; } /// @inheritdoc IProtocol function penalizedUntilOf(address minter_) external view returns (uint256 penalizedUntil_) { - return _penalizedUntilTimestamps[minter_]; + return _minterBasics[minter_].penalizedUntilTimestamp; } /// @inheritdoc IProtocol @@ -412,11 +425,11 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { /// @inheritdoc IProtocol function mintProposalOf( address minter_ - ) external view returns (uint256 mintId_, address destination_, uint256 amount_, uint256 createdAt_) { + ) external view returns (uint256 mintId_, uint256 createdAt_, address destination_, uint256 amount_) { mintId_ = _mintProposals[minter_].id; + createdAt_ = _mintProposals[minter_].createdAt; destination_ = _mintProposals[minter_].destination; amount_ = _mintProposals[minter_].amount; - createdAt_ = _mintProposals[minter_].createdAt; } /// @inheritdoc IProtocol @@ -424,17 +437,17 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { address minter_, uint256 retrievalId_ ) external view returns (uint256 collateral) { - return _pendingCollateralRetrievals[minter_][retrievalId_]; + return _pendingCollateralRetrievals[minter_][UIntMath.safe48(retrievalId_)]; } /// @inheritdoc IProtocol function totalPendingCollateralRetrievalsOf(address minter_) external view returns (uint256 collateral_) { - return _totalPendingCollateralRetrievals[minter_]; + return _minterBasics[minter_].totalPendingRetrievals; } /// @inheritdoc IProtocol function unfrozenTimeOf(address minter_) external view returns (uint256 timestamp_) { - return _unfrozenTimestamps[minter_]; + return _minterBasics[minter_].unfrozenTimestamp; } /******************************************************************************************************************\ @@ -507,10 +520,10 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { */ function _imposePenalty(address minter_, uint256 penaltyBase_) internal { uint256 penalty_ = (penaltyBase_ * penaltyRate()) / ONE; - uint256 penaltyPrincipal_ = _getPrincipalValue(penalty_); + uint128 penaltyPrincipal_ = UIntMath.safe128(_getPrincipalValue(penalty_)); // Calculate and add penalty principal to total minter's principal of active owed M - _principalOfActiveOwedM[minter_] += penaltyPrincipal_; + _owedM[minter_].principalOfActive += penaltyPrincipal_; _totalPrincipalOfActiveOwedM += penaltyPrincipal_; emit PenaltyImposed(minter_, penalty_); @@ -527,10 +540,10 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { if (penaltyBase_ == 0) return; // Save penalization interval to not double charge for the same missed periods again - _penalizedUntilTimestamps[minter_] = penalizedUntil_; + _minterBasics[minter_].penalizedUntilTimestamp = UIntMath.safe48(penalizedUntil_); // We charged for the first missed interval based on previous collateral interval length only once // NOTE: extra caution for the case when SPOG changed collateral interval length - _lastUpdateIntervals[minter_] = updateCollateralInterval(); + _minterBasics[minter_].lastUpdateInterval = UIntMath.safe48(updateCollateralInterval()); _imposePenalty(minter_, penaltyBase_); } @@ -557,9 +570,9 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { */ function _repayForActiveMinter(address minter_, uint256 maxAmount_) internal returns (uint256 amount_) { amount_ = _min(activeOwedMOf(minter_), maxAmount_); - uint256 principalAmount_ = _getPrincipalValue(amount_); + uint128 principalAmount_ = UIntMath.safe128(_getPrincipalValue(amount_)); - _principalOfActiveOwedM[minter_] -= principalAmount_; + _owedM[minter_].principalOfActive -= principalAmount_; _totalPrincipalOfActiveOwedM -= principalAmount_; } @@ -570,10 +583,12 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { * @return amount_ The amount of inactive owed M that was actually repaid */ function _repayForInactiveMinter(address minter_, uint256 maxAmount_) internal returns (uint256 amount_) { - amount_ = _min(_inactiveOwedM[minter_], maxAmount_); + uint128 safeAmount_ = UIntMath.safe128(_min(_owedM[minter_].inactive, maxAmount_)); - _inactiveOwedM[minter_] -= amount_; - _totalInactiveOwedM -= amount_; + _owedM[minter_].inactive -= safeAmount_; + _totalInactiveOwedM -= safeAmount_; + + return safeAmount_; } /** @@ -583,9 +598,9 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { */ function _resolvePendingRetrievals(address minter_, uint256[] calldata retrievalIds_) internal { for (uint256 index_; index_ < retrievalIds_.length; ++index_) { - uint256 retrievalId_ = retrievalIds_[index_]; + uint48 retrievalId_ = UIntMath.safe48(retrievalIds_[index_]); - _totalPendingCollateralRetrievals[minter_] -= _pendingCollateralRetrievals[minter_][retrievalId_]; + _minterBasics[minter_].totalPendingRetrievals -= _pendingCollateralRetrievals[minter_][retrievalId_]; delete _pendingCollateralRetrievals[minter_][retrievalId_]; } @@ -598,15 +613,16 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { * @param newTimestamp_ The timestamp of the collateral update, minimum of all given validator timestamps */ function _updateCollateral(address minter_, uint256 amount_, uint256 newTimestamp_) internal { - uint256 lastCollateralUpdate_ = _lastCollateralUpdates[minter_]; + uint256 lastUpdateTimestamp_ = _minterBasics[minter_].updateTimestamp; // Protocol already has more recent collateral update - if (newTimestamp_ < lastCollateralUpdate_) revert StaleCollateralUpdate(newTimestamp_, lastCollateralUpdate_); + if (newTimestamp_ < lastUpdateTimestamp_) revert StaleCollateralUpdate(newTimestamp_, lastUpdateTimestamp_); + + _minterBasics[minter_].collateral = UIntMath.safe128(amount_); + _minterBasics[minter_].updateTimestamp = UIntMath.safe48(newTimestamp_); - _collaterals[minter_] = amount_; - _lastCollateralUpdates[minter_] = newTimestamp_; - // NOTE: Save for the future potential valid penalization if update collateral interval is changed by SPOG - _lastUpdateIntervals[minter_] = updateCollateralInterval(); + // NOTE: Save for the future potential valid penalization if update collateral interval is changed by SPOG. + _minterBasics[minter_].lastUpdateInterval = UIntMath.safe48(updateCollateralInterval()); } /******************************************************************************************************************\ @@ -622,9 +638,14 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { function _getPenaltyBaseAndTimeForMissedCollateralUpdates( address minter_ ) internal view returns (uint256 penaltyBase_, uint256 penalizedUntil_) { - uint256 updateInterval_ = _lastUpdateIntervals[minter_]; - uint256 lastUpdate_ = _lastCollateralUpdates[minter_]; - uint256 penalizeFrom_ = _max(lastUpdate_, _penalizedUntilTimestamps[minter_]); + MinterBasic storage MinterBasic_ = _minterBasics[minter_]; + (uint256 updateInterval_, uint256 lastUpdate_, uint256 lastPenalizedUntil_) = ( + MinterBasic_.lastUpdateInterval, + MinterBasic_.updateTimestamp, + MinterBasic_.penalizedUntilTimestamp + ); + + uint256 penalizeFrom_ = _max(lastUpdate_, lastPenalizedUntil_); uint256 penalizationDeadline_ = penalizeFrom_ + updateInterval_; // Return if it is first update collateral ever or deadline for new penalization was not reached yet @@ -712,7 +733,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { * @param minter_ The address of the minter */ function _revertIfMinterFrozen(address minter_) internal view { - if (block.timestamp < _unfrozenTimestamps[minter_]) revert FrozenMinter(); + if (block.timestamp < _minterBasics[minter_].unfrozenTimestamp) revert FrozenMinter(); } /** diff --git a/src/interfaces/IProtocol.sol b/src/interfaces/IProtocol.sol index a3389bca..87c59688 100644 --- a/src/interfaces/IProtocol.sol +++ b/src/interfaces/IProtocol.sol @@ -299,7 +299,7 @@ interface IProtocol is IContinuousIndexing { /// @notice The mint proposal of minters, only 1 active proposal per minter function mintProposalOf( address minter - ) external view returns (uint256 mintId, address destination, uint256 amount, uint256 createdAt); + ) external view returns (uint256 mintId, uint256 createdAt, address destination, uint256 amount); /// @notice The minter's proposeRetrieval proposal amount function pendingCollateralRetrievalOf( diff --git a/src/libs/UIntMath.sol b/src/libs/UIntMath.sol new file mode 100644 index 00000000..54b605e0 --- /dev/null +++ b/src/libs/UIntMath.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.23; + +library UIntMath { + error InvalidUInt48(); + + error InvalidUInt64(); + + error InvalidUInt128(); + + function safe48(uint256 n) internal pure returns (uint48) { + if (n > type(uint48).max) revert InvalidUInt48(); + return uint48(n); + } + + function safe64(uint256 n) internal pure returns (uint64) { + if (n > type(uint64).max) revert InvalidUInt64(); + return uint64(n); + } + + function safe128(uint256 n) internal pure returns (uint128) { + if (n > type(uint128).max) revert InvalidUInt128(); + return uint128(n); + } +} diff --git a/test/Protocol.t.sol b/test/Protocol.t.sol index ee1d14e7..9929dc7b 100644 --- a/test/Protocol.t.sol +++ b/test/Protocol.t.sol @@ -292,7 +292,7 @@ contract ProtocolTests is Test { assertEq(mintId, expectedMintId); - (uint256 mintId_, address destination_, uint256 amount_, uint256 timestamp_) = _protocol.mintProposalOf( + (uint256 mintId_, uint256 timestamp_, address destination_, uint256 amount_) = _protocol.mintProposalOf( _minter1 ); @@ -351,7 +351,7 @@ contract ProtocolTests is Test { _protocol.mintM(mintId); // check that mint request has been deleted - (uint256 mintId_, address destination_, uint256 amount_, uint256 timestamp_) = _protocol.mintProposalOf( + (uint256 mintId_, uint256 timestamp_, address destination_, uint256 amount_) = _protocol.mintProposalOf( _minter1 ); @@ -519,7 +519,7 @@ contract ProtocolTests is Test { vm.prank(_validator1); _protocol.cancelMint(_minter1, mintId); - (uint256 mintId_, address destination_, uint256 amount_, uint256 timestamp) = _protocol.mintProposalOf( + (uint256 mintId_, uint256 timestamp, address destination_, uint256 amount_) = _protocol.mintProposalOf( _minter1 ); @@ -536,7 +536,7 @@ contract ProtocolTests is Test { vm.prank(_alice); _protocol.cancelMint(_minter1, mintId); - (uint256 mintId_, address destination_, uint256 amount_, uint256 timestamp_) = _protocol.mintProposalOf( + (uint256 mintId_, uint256 timestamp_, address destination_, uint256 amount_) = _protocol.mintProposalOf( _minter1 ); diff --git a/test/utils/ProtocolHarness.sol b/test/utils/ProtocolHarness.sol index dcce2e91..aa81be7e 100644 --- a/test/utils/ProtocolHarness.sol +++ b/test/utils/ProtocolHarness.sol @@ -2,12 +2,14 @@ pragma solidity 0.8.23; +import { UIntMath } from "../../src/libs/UIntMath.sol"; + import { Protocol } from "../../src/Protocol.sol"; contract ProtocolHarness is Protocol { constructor(address spogRegistrar_, address mToken_) Protocol(spogRegistrar_, mToken_) {} - function mintNonce() external view returns (uint256) { + function mintNonce() external view returns (uint48) { return _mintNonce; } @@ -26,28 +28,34 @@ contract ProtocolHarness is Protocol { address destination_ ) external returns (uint256 mintId_) { mintId_ = ++_mintNonce; - _mintProposals[minter_] = MintProposal(mintId_, destination_, amount_, createdAt_); + + _mintProposals[minter_] = MintProposal( + UIntMath.safe48(mintId_), + UIntMath.safe48(createdAt_), + destination_, + UIntMath.safe128(amount_) + ); } function setCollateralOf(address minter_, uint256 collateral_) external { - _collaterals[minter_] = collateral_; + _minterBasics[minter_].collateral = UIntMath.safe128(collateral_); } function setCollateralUpdateOf(address minter_, uint256 lastUpdated_) external { - _lastCollateralUpdates[minter_] = lastUpdated_; + _minterBasics[minter_].updateTimestamp = UIntMath.safe48(lastUpdated_); } function setLastCollateralUpdateIntervalOf(address minter_, uint256 updateInterval_) external { - _lastUpdateIntervals[minter_] = updateInterval_; + _minterBasics[minter_].lastUpdateInterval = UIntMath.safe48(updateInterval_); } function setPenalizedUntilOf(address minter_, uint256 penalizedUntil_) external { - _penalizedUntilTimestamps[minter_] = penalizedUntil_; + _minterBasics[minter_].penalizedUntilTimestamp = UIntMath.safe48(penalizedUntil_); } function setPrincipalOfActiveOwedMOf(address minter_, uint256 amount_) external { - _principalOfActiveOwedM[minter_] = amount_; - _totalPrincipalOfActiveOwedM += amount_; // TODO: fix this side effect. + _owedM[minter_].principalOfActive = UIntMath.safe128(amount_); + _totalPrincipalOfActiveOwedM += UIntMath.safe128(amount_); // TODO: fix this side effect. ? } function setLatestIndex(uint256 index_) external { @@ -59,6 +67,6 @@ contract ProtocolHarness is Protocol { } function principalOfActiveOwedMOf(address minter_) external view returns (uint256 principalOfActiveOwedM_) { - return _principalOfActiveOwedM[minter_]; + return _owedM[minter_].principalOfActive; } } From 8dacc1af0ae92c182790cde8f9c6db7b374a72eb Mon Sep 17 00:00:00 2001 From: toninorair Date: Fri, 8 Dec 2023 22:33:07 -0500 Subject: [PATCH 02/11] No need for uint48 --- src/Protocol.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol.sol b/src/Protocol.sol index 28b1d8c0..39abee07 100644 --- a/src/Protocol.sol +++ b/src/Protocol.sol @@ -203,7 +203,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { function mintM(uint256 mintId_) external onlyActiveMinter onlyUnfrozenMinter { MintProposal storage mintProposal_ = _mintProposals[msg.sender]; - (uint48 id_, uint256 amount_, uint256 createdAt_, address destination_) = ( + (uint256 id_, uint256 amount_, uint256 createdAt_, address destination_) = ( mintProposal_.id, mintProposal_.amount, mintProposal_.createdAt, From cbefe57f2ad03f12b2bdec38d99b296ec7cdf51d Mon Sep 17 00:00:00 2001 From: toninorair Date: Fri, 8 Dec 2023 23:04:44 -0500 Subject: [PATCH 03/11] Add natspecs --- src/Protocol.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Protocol.sol b/src/Protocol.sol index 39abee07..5bc0783b 100644 --- a/src/Protocol.sol +++ b/src/Protocol.sol @@ -71,17 +71,22 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { /// @notice The total principal amount of active M uint128 internal _totalPrincipalOfActiveOwedM; - /// @notice The total amount of inactive M, sum of all inactive minter's owed M + /// @notice The total amount of inactive M, sum of all inactive minter's owed M. uint128 internal _totalInactiveOwedM; + /// @notice Indicates if minter was approved by SPOG and activated in Protocol. mapping(address minter => bool isActiveMinter) internal _isActiveMinter; + /// @notice The basic information of minter (collateral, last update interval, last update timestamp, etc.) mapping(address minter => MinterBasic basic) internal _minterBasics; + /// @notice The mint proposals of minter (mint ID, creation timestamp, destination, amount). mapping(address minter => MintProposal proposal) internal _mintProposals; + /// @notice The owed M of active and inactive minters (principal of active, inactive). mapping(address minter => OwedM owedM) internal _owedM; + /// @notice The pending collateral retrievals of minter (retrieval ID, amount). mapping(address minter => mapping(uint48 retrievalId => uint128 amount)) internal _pendingCollateralRetrievals; /******************************************************************************************************************\ From 885f53de295dee3e960b1d87cd7c628eabcb4a5a Mon Sep 17 00:00:00 2001 From: toninorair Date: Fri, 8 Dec 2023 23:37:14 -0500 Subject: [PATCH 04/11] Pack index --- src/ContinuousIndexing.sol | 19 ++++++++++--------- src/Protocol.sol | 4 ++++ src/libs/UIntMath.sol | 14 ++++++++++++++ test/utils/MTokenHarness.sol | 8 +++++--- test/utils/ProtocolHarness.sol | 4 ++-- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/ContinuousIndexing.sol b/src/ContinuousIndexing.sol index 1703fa77..3914ea00 100644 --- a/src/ContinuousIndexing.sol +++ b/src/ContinuousIndexing.sol @@ -2,19 +2,20 @@ pragma solidity 0.8.23; +import { UIntMath } from "./libs/UIntMath.sol"; + import { IContinuousIndexing } from "./interfaces/IContinuousIndexing.sol"; import { ContinuousIndexingMath } from "./libs/ContinuousIndexingMath.sol"; abstract contract ContinuousIndexing is IContinuousIndexing { - // TODO: Consider packing these into a single slot. - uint256 internal _latestIndex; - uint256 internal _latestRate; - uint256 internal _latestUpdateTimestamp; + uint184 internal _latestIndex; + uint24 internal _latestRate; + uint48 internal _latestUpdateTimestamp; constructor() { - _latestIndex = 1 * ContinuousIndexingMath.EXP_BASE_SCALE; - _latestUpdateTimestamp = block.timestamp; + _latestIndex = UIntMath.safe184(1 * ContinuousIndexingMath.EXP_BASE_SCALE); + _latestUpdateTimestamp = UIntMath.safe48(block.timestamp); } /******************************************************************************************************************\ @@ -30,9 +31,9 @@ abstract contract ContinuousIndexing is IContinuousIndexing { if (_latestUpdateTimestamp == block.timestamp && _latestRate == rate_) return currentIndex_; - _latestIndex = currentIndex_; - _latestRate = rate_; - _latestUpdateTimestamp = block.timestamp; + _latestIndex = UIntMath.safe184(currentIndex_); + _latestRate = UIntMath.safe24(rate_); + _latestUpdateTimestamp = UIntMath.safe48(block.timestamp); emit IndexUpdated(currentIndex_, _latestRate); } diff --git a/src/Protocol.sol b/src/Protocol.sol index 5bc0783b..f3a8b3ad 100644 --- a/src/Protocol.sol +++ b/src/Protocol.sol @@ -23,15 +23,19 @@ import { ContinuousIndexing } from "./ContinuousIndexing.sol"; */ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { struct MintProposal { + // 1st slot uint48 id; uint48 createdAt; address destination; + // 2nd slot uint256 amount; // NOTE: it can be uint128, but nothing else is left to pack } struct MinterBasic { + // 1st slot uint128 collateral; uint128 totalPendingRetrievals; + // 2nd slot uint48 lastUpdateInterval; uint48 updateTimestamp; uint48 penalizedUntilTimestamp; diff --git a/src/libs/UIntMath.sol b/src/libs/UIntMath.sol index 54b605e0..3da66f68 100644 --- a/src/libs/UIntMath.sol +++ b/src/libs/UIntMath.sol @@ -3,12 +3,21 @@ pragma solidity 0.8.23; library UIntMath { + error InvalidUInt24(); + error InvalidUInt48(); error InvalidUInt64(); error InvalidUInt128(); + error InvalidUInt184(); + + function safe24(uint256 n) internal pure returns (uint24) { + if (n > type(uint24).max) revert InvalidUInt24(); + return uint24(n); + } + function safe48(uint256 n) internal pure returns (uint48) { if (n > type(uint48).max) revert InvalidUInt48(); return uint48(n); @@ -23,4 +32,9 @@ library UIntMath { if (n > type(uint128).max) revert InvalidUInt128(); return uint128(n); } + + function safe184(uint256 n) internal pure returns (uint184) { + if (n > type(uint184).max) revert InvalidUInt184(); + return uint184(n); + } } diff --git a/test/utils/MTokenHarness.sol b/test/utils/MTokenHarness.sol index a43d9d6e..bc460d6d 100644 --- a/test/utils/MTokenHarness.sol +++ b/test/utils/MTokenHarness.sol @@ -2,21 +2,23 @@ pragma solidity 0.8.23; +import { UIntMath } from "../../src/libs/UIntMath.sol"; + import { MToken } from "../../src/MToken.sol"; contract MTokenHarness is MToken { constructor(address spogRegistrar_, address protocol_) MToken(spogRegistrar_, protocol_) {} function setLatestIndex(uint256 index_) external { - _latestIndex = index_; + _latestIndex = UIntMath.safe184(index_); } function setLatestRate(uint256 rate_) external { - _latestRate = rate_; + _latestRate = UIntMath.safe24(rate_); } function setLatestUpdated(uint256 timestamp_) external { - _latestUpdateTimestamp = timestamp_; + _latestUpdateTimestamp = UIntMath.safe48(timestamp_); } function setIsEarning(address account_, bool isEarning_) external { diff --git a/test/utils/ProtocolHarness.sol b/test/utils/ProtocolHarness.sol index aa81be7e..de7aa756 100644 --- a/test/utils/ProtocolHarness.sol +++ b/test/utils/ProtocolHarness.sol @@ -59,11 +59,11 @@ contract ProtocolHarness is Protocol { } function setLatestIndex(uint256 index_) external { - _latestIndex = index_; + _latestIndex = UIntMath.safe184(index_); } function setLatestRate(uint256 rate_) external { - _latestRate = rate_; + _latestRate = UIntMath.safe24(rate_); } function principalOfActiveOwedMOf(address minter_) external view returns (uint256 principalOfActiveOwedM_) { From fc02ed790a8a9b4e0c6054f71248e47eb4af993b Mon Sep 17 00:00:00 2001 From: toninorair Date: Fri, 8 Dec 2023 23:56:39 -0500 Subject: [PATCH 05/11] active packed --- src/Protocol.sol | 17 +++++++---------- test/utils/ProtocolHarness.sol | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Protocol.sol b/src/Protocol.sol index f3a8b3ad..0c7bb682 100644 --- a/src/Protocol.sol +++ b/src/Protocol.sol @@ -40,6 +40,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { uint48 updateTimestamp; uint48 penalizedUntilTimestamp; uint48 unfrozenTimestamp; + bool isActive; } struct OwedM { @@ -78,10 +79,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { /// @notice The total amount of inactive M, sum of all inactive minter's owed M. uint128 internal _totalInactiveOwedM; - /// @notice Indicates if minter was approved by SPOG and activated in Protocol. - mapping(address minter => bool isActiveMinter) internal _isActiveMinter; - - /// @notice The basic information of minter (collateral, last update interval, last update timestamp, etc.) + /// @notice The basic information of minter (collateral, last update interval, collateral update timestamp, etc.) mapping(address minter => MinterBasic basic) internal _minterBasics; /// @notice The mint proposals of minter (mint ID, creation timestamp, destination, amount). @@ -252,7 +250,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { // Undercollateralization within one update interval is forgiven. _imposePenaltyIfMissedCollateralUpdates(minter_); - uint256 amount_ = _isActiveMinter[minter_] + uint256 amount_ = _minterBasics[minter_].isActive ? _repayForActiveMinter(minter_, maxAmount_) : _repayForInactiveMinter(minter_, maxAmount_); @@ -286,9 +284,9 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { /// @inheritdoc IProtocol function activateMinter(address minter_) external { if (!isMinterApprovedBySPOG(minter_)) revert NotApprovedMinter(); - if (_isActiveMinter[minter_]) revert AlreadyActiveMinter(); + if (_minterBasics[minter_].isActive) revert AlreadyActiveMinter(); - _isActiveMinter[minter_] = true; + _minterBasics[minter_].isActive = true; emit MinterActivated(minter_, msg.sender); } @@ -313,7 +311,6 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { _totalPrincipalOfActiveOwedM -= _owedM[minter_].principalOfActive; // Reset reasonable aspects of minter's state. - delete _isActiveMinter[minter_]; delete _minterBasics[minter_]; delete _mintProposals[minter_]; delete _owedM[minter_].principalOfActive; @@ -464,7 +461,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { \******************************************************************************************************************/ /// @inheritdoc IProtocol function isActiveMinter(address minter_) external view returns (bool isActive_) { - return _isActiveMinter[minter_]; + return _minterBasics[minter_].isActive; } /// @inheritdoc IProtocol @@ -750,7 +747,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { * @param minter_ The address of the minter */ function _revertIfInactiveMinter(address minter_) internal view { - if (!_isActiveMinter[minter_]) revert InactiveMinter(); + if (!_minterBasics[minter_].isActive) revert InactiveMinter(); } /** diff --git a/test/utils/ProtocolHarness.sol b/test/utils/ProtocolHarness.sol index de7aa756..05eb8477 100644 --- a/test/utils/ProtocolHarness.sol +++ b/test/utils/ProtocolHarness.sol @@ -18,7 +18,7 @@ contract ProtocolHarness is Protocol { } function setActiveMinter(address minter_, bool isActive_) external { - _isActiveMinter[minter_] = isActive_; + _minterBasics[minter_].isActive = isActive_; } function setMintProposalOf( From df62bad941bf54a1ceb27de47c5b55325ee7b09b Mon Sep 17 00:00:00 2001 From: toninorair Date: Mon, 11 Dec 2023 09:28:30 -0500 Subject: [PATCH 06/11] Readability fix --- test/utils/ProtocolHarness.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/ProtocolHarness.sol b/test/utils/ProtocolHarness.sol index 05eb8477..8675ecd6 100644 --- a/test/utils/ProtocolHarness.sol +++ b/test/utils/ProtocolHarness.sol @@ -9,7 +9,7 @@ import { Protocol } from "../../src/Protocol.sol"; contract ProtocolHarness is Protocol { constructor(address spogRegistrar_, address mToken_) Protocol(spogRegistrar_, mToken_) {} - function mintNonce() external view returns (uint48) { + function mintNonce() external view returns (uint256) { return _mintNonce; } From 1eff9612f659a03cfc5079f4d595424201d5d4c4 Mon Sep 17 00:00:00 2001 From: toninorair Date: Mon, 11 Dec 2023 13:48:40 -0500 Subject: [PATCH 07/11] Update src/ContinuousIndexing.sol Co-authored-by: Pierrick Turelier --- src/ContinuousIndexing.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ContinuousIndexing.sol b/src/ContinuousIndexing.sol index 3914ea00..f87119e5 100644 --- a/src/ContinuousIndexing.sol +++ b/src/ContinuousIndexing.sol @@ -14,7 +14,7 @@ abstract contract ContinuousIndexing is IContinuousIndexing { uint48 internal _latestUpdateTimestamp; constructor() { - _latestIndex = UIntMath.safe184(1 * ContinuousIndexingMath.EXP_BASE_SCALE); + _latestIndex = uint184(1 * ContinuousIndexingMath.EXP_BASE_SCALE); _latestUpdateTimestamp = UIntMath.safe48(block.timestamp); } From c2fc430c814d10c9cc90dbe8fffdee3d09cd53f2 Mon Sep 17 00:00:00 2001 From: toninorair Date: Mon, 11 Dec 2023 13:49:01 -0500 Subject: [PATCH 08/11] Update src/ContinuousIndexing.sol Co-authored-by: Pierrick Turelier --- src/ContinuousIndexing.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ContinuousIndexing.sol b/src/ContinuousIndexing.sol index f87119e5..e88b4b41 100644 --- a/src/ContinuousIndexing.sol +++ b/src/ContinuousIndexing.sol @@ -15,7 +15,7 @@ abstract contract ContinuousIndexing is IContinuousIndexing { constructor() { _latestIndex = uint184(1 * ContinuousIndexingMath.EXP_BASE_SCALE); - _latestUpdateTimestamp = UIntMath.safe48(block.timestamp); + _latestUpdateTimestamp = uint48(block.timestamp); } /******************************************************************************************************************\ From f9cb39b9eb5b8c423daa887410ff95bce28e4760 Mon Sep 17 00:00:00 2001 From: toninorair Date: Mon, 11 Dec 2023 23:08:31 -0500 Subject: [PATCH 09/11] After review --- src/Protocol.sol | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/Protocol.sol b/src/Protocol.sol index 0c7bb682..cf23f12f 100644 --- a/src/Protocol.sol +++ b/src/Protocol.sol @@ -28,7 +28,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { uint48 createdAt; address destination; // 2nd slot - uint256 amount; // NOTE: it can be uint128, but nothing else is left to pack + uint128 amount; } struct MinterBasic { @@ -178,7 +178,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { uint128 safeCollateral_ = UIntMath.safe128(collateral_); _minterBasics[msg.sender].totalPendingRetrievals += safeCollateral_; - _pendingCollateralRetrievals[msg.sender][UIntMath.safe48(retrievalId_)] = safeCollateral_; + _pendingCollateralRetrievals[msg.sender][uint48(retrievalId_)] = safeCollateral_; _revertIfUndercollateralized(msg.sender, 0); @@ -197,10 +197,10 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { } _mintProposals[msg.sender] = MintProposal( - UIntMath.safe48(mintId_), - UIntMath.safe48(block.timestamp), + uint48(mintId_), + uint48(block.timestamp), destination_, - amount_ + UIntMath.safe128(amount_) ); emit MintProposed(mintId_, msg.sender, amount_, destination_); @@ -210,7 +210,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { function mintM(uint256 mintId_) external onlyActiveMinter onlyUnfrozenMinter { MintProposal storage mintProposal_ = _mintProposals[msg.sender]; - (uint256 id_, uint256 amount_, uint256 createdAt_, address destination_) = ( + (uint256 id_, uint128 amount_, uint256 createdAt_, address destination_) = ( mintProposal_.id, mintProposal_.amount, mintProposal_.createdAt, @@ -233,7 +233,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { emit MintExecuted(mintId_); // Adjust principal of active owed M for minter. - uint128 principalAmount_ = UIntMath.safe128(_getPrincipalValue(amount_)); + uint128 principalAmount_ = _getPrincipalValue(amount_); _owedM[msg.sender].principalOfActive += principalAmount_; _totalPrincipalOfActiveOwedM += principalAmount_; @@ -278,7 +278,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { frozenUntil_ = block.timestamp + minterFreezeTime(); - emit MinterFrozen(minter_, _minterBasics[minter_].unfrozenTimestamp = UIntMath.safe48(frozenUntil_)); + emit MinterFrozen(minter_, _minterBasics[minter_].unfrozenTimestamp = uint48(frozenUntil_)); } /// @inheritdoc IProtocol @@ -443,7 +443,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { address minter_, uint256 retrievalId_ ) external view returns (uint256 collateral) { - return _pendingCollateralRetrievals[minter_][UIntMath.safe48(retrievalId_)]; + return _pendingCollateralRetrievals[minter_][uint48(retrievalId_)]; } /// @inheritdoc IProtocol @@ -526,7 +526,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { */ function _imposePenalty(address minter_, uint256 penaltyBase_) internal { uint256 penalty_ = (penaltyBase_ * penaltyRate()) / ONE; - uint128 penaltyPrincipal_ = UIntMath.safe128(_getPrincipalValue(penalty_)); + uint128 penaltyPrincipal_ = _getPrincipalValue(penalty_); // Calculate and add penalty principal to total minter's principal of active owed M _owedM[minter_].principalOfActive += penaltyPrincipal_; @@ -546,7 +546,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { if (penaltyBase_ == 0) return; // Save penalization interval to not double charge for the same missed periods again - _minterBasics[minter_].penalizedUntilTimestamp = UIntMath.safe48(penalizedUntil_); + _minterBasics[minter_].penalizedUntilTimestamp = uint48(penalizedUntil_); // We charged for the first missed interval based on previous collateral interval length only once // NOTE: extra caution for the case when SPOG changed collateral interval length _minterBasics[minter_].lastUpdateInterval = UIntMath.safe48(updateCollateralInterval()); @@ -588,13 +588,11 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { * @param maxAmount_ The maximum amount of inactive owed M to repay * @return amount_ The amount of inactive owed M that was actually repaid */ - function _repayForInactiveMinter(address minter_, uint256 maxAmount_) internal returns (uint256 amount_) { - uint128 safeAmount_ = UIntMath.safe128(_min(_owedM[minter_].inactive, maxAmount_)); - - _owedM[minter_].inactive -= safeAmount_; - _totalInactiveOwedM -= safeAmount_; + function _repayForInactiveMinter(address minter_, uint256 maxAmount_) internal returns (uint128 amount_) { + amount_ = UIntMath.safe128(_min(_owedM[minter_].inactive, maxAmount_)); - return safeAmount_; + _owedM[minter_].inactive -= amount_; + _totalInactiveOwedM -= amount_; } /** @@ -604,7 +602,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { */ function _resolvePendingRetrievals(address minter_, uint256[] calldata retrievalIds_) internal { for (uint256 index_; index_ < retrievalIds_.length; ++index_) { - uint48 retrievalId_ = UIntMath.safe48(retrievalIds_[index_]); + uint48 retrievalId_ = uint48(retrievalIds_[index_]); _minterBasics[minter_].totalPendingRetrievals -= _pendingCollateralRetrievals[minter_][retrievalId_]; @@ -625,7 +623,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { if (newTimestamp_ < lastUpdateTimestamp_) revert StaleCollateralUpdate(newTimestamp_, lastUpdateTimestamp_); _minterBasics[minter_].collateral = UIntMath.safe128(amount_); - _minterBasics[minter_].updateTimestamp = UIntMath.safe48(newTimestamp_); + _minterBasics[minter_].updateTimestamp = uint48(newTimestamp_); // NOTE: Save for the future potential valid penalization if update collateral interval is changed by SPOG. _minterBasics[minter_].lastUpdateInterval = UIntMath.safe48(updateCollateralInterval()); @@ -644,11 +642,11 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { function _getPenaltyBaseAndTimeForMissedCollateralUpdates( address minter_ ) internal view returns (uint256 penaltyBase_, uint256 penalizedUntil_) { - MinterBasic storage MinterBasic_ = _minterBasics[minter_]; + MinterBasic storage minterBasic_ = _minterBasics[minter_]; (uint256 updateInterval_, uint256 lastUpdate_, uint256 lastPenalizedUntil_) = ( - MinterBasic_.lastUpdateInterval, - MinterBasic_.updateTimestamp, - MinterBasic_.penalizedUntilTimestamp + minterBasic_.lastUpdateInterval, + minterBasic_.updateTimestamp, + minterBasic_.penalizedUntilTimestamp ); uint256 penalizeFrom_ = _max(lastUpdate_, lastPenalizedUntil_); @@ -677,8 +675,8 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { * @dev present = principal * index * @param presentValue_ The present value of M */ - function _getPrincipalValue(uint256 presentValue_) internal view returns (uint256 principalValue_) { - return _getPrincipalAmount(presentValue_, currentIndex()); + function _getPrincipalValue(uint256 presentValue_) internal view returns (uint128 principalValue_) { + return UIntMath.safe128(_getPrincipalAmount(presentValue_, currentIndex())); } /** From fb3b00b4d6993d7907d63501d255d4b417f3c001 Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 12 Dec 2023 09:06:00 -0500 Subject: [PATCH 10/11] Save progress --- src/Protocol.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Protocol.sol b/src/Protocol.sol index cf23f12f..8937142c 100644 --- a/src/Protocol.sol +++ b/src/Protocol.sol @@ -576,7 +576,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { */ function _repayForActiveMinter(address minter_, uint256 maxAmount_) internal returns (uint256 amount_) { amount_ = _min(activeOwedMOf(minter_), maxAmount_); - uint128 principalAmount_ = UIntMath.safe128(_getPrincipalValue(amount_)); + uint128 principalAmount_ = _getPrincipalValue(amount_); _owedM[minter_].principalOfActive -= principalAmount_; _totalPrincipalOfActiveOwedM -= principalAmount_; @@ -650,7 +650,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { ); uint256 penalizeFrom_ = _max(lastUpdate_, lastPenalizedUntil_); - uint256 penalizationDeadline_ = penalizeFrom_ + updateInterval_; + uint256 penalizationDeadline_ = UIntMath.safe48(penalizeFrom_ + updateInterval_); // Return if it is first update collateral ever or deadline for new penalization was not reached yet if (updateInterval_ == 0 || penalizationDeadline_ > block.timestamp) return (0, penalizeFrom_); From c270177a5580b9885dff16a8e83bd555bbb3d825 Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 12 Dec 2023 14:29:52 -0500 Subject: [PATCH 11/11] Make timestamps to be uint40 --- src/ContinuousIndexing.sol | 10 +++++----- src/Protocol.sol | 24 ++++++++++++------------ src/libs/UIntMath.sol | 23 ++++++++--------------- test/utils/MTokenHarness.sol | 4 ++-- test/utils/ProtocolHarness.sol | 12 ++++++------ 5 files changed, 33 insertions(+), 40 deletions(-) diff --git a/src/ContinuousIndexing.sol b/src/ContinuousIndexing.sol index e88b4b41..84e2cdfe 100644 --- a/src/ContinuousIndexing.sol +++ b/src/ContinuousIndexing.sol @@ -9,13 +9,13 @@ import { IContinuousIndexing } from "./interfaces/IContinuousIndexing.sol"; import { ContinuousIndexingMath } from "./libs/ContinuousIndexingMath.sol"; abstract contract ContinuousIndexing is IContinuousIndexing { - uint184 internal _latestIndex; + uint192 internal _latestIndex; uint24 internal _latestRate; - uint48 internal _latestUpdateTimestamp; + uint40 internal _latestUpdateTimestamp; constructor() { _latestIndex = uint184(1 * ContinuousIndexingMath.EXP_BASE_SCALE); - _latestUpdateTimestamp = uint48(block.timestamp); + _latestUpdateTimestamp = uint40(block.timestamp); } /******************************************************************************************************************\ @@ -31,9 +31,9 @@ abstract contract ContinuousIndexing is IContinuousIndexing { if (_latestUpdateTimestamp == block.timestamp && _latestRate == rate_) return currentIndex_; - _latestIndex = UIntMath.safe184(currentIndex_); + _latestIndex = UIntMath.safe192(currentIndex_); _latestRate = UIntMath.safe24(rate_); - _latestUpdateTimestamp = UIntMath.safe48(block.timestamp); + _latestUpdateTimestamp = uint40(block.timestamp); emit IndexUpdated(currentIndex_, _latestRate); } diff --git a/src/Protocol.sol b/src/Protocol.sol index 8937142c..5cbe7395 100644 --- a/src/Protocol.sol +++ b/src/Protocol.sol @@ -25,7 +25,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { struct MintProposal { // 1st slot uint48 id; - uint48 createdAt; + uint40 createdAt; address destination; // 2nd slot uint128 amount; @@ -36,10 +36,10 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { uint128 collateral; uint128 totalPendingRetrievals; // 2nd slot - uint48 lastUpdateInterval; - uint48 updateTimestamp; - uint48 penalizedUntilTimestamp; - uint48 unfrozenTimestamp; + uint32 lastUpdateInterval; + uint40 updateTimestamp; + uint40 penalizedUntilTimestamp; + uint40 unfrozenTimestamp; bool isActive; } @@ -198,7 +198,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { _mintProposals[msg.sender] = MintProposal( uint48(mintId_), - uint48(block.timestamp), + uint40(block.timestamp), destination_, UIntMath.safe128(amount_) ); @@ -278,7 +278,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { frozenUntil_ = block.timestamp + minterFreezeTime(); - emit MinterFrozen(minter_, _minterBasics[minter_].unfrozenTimestamp = uint48(frozenUntil_)); + emit MinterFrozen(minter_, _minterBasics[minter_].unfrozenTimestamp = uint40(frozenUntil_)); } /// @inheritdoc IProtocol @@ -546,10 +546,10 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { if (penaltyBase_ == 0) return; // Save penalization interval to not double charge for the same missed periods again - _minterBasics[minter_].penalizedUntilTimestamp = uint48(penalizedUntil_); + _minterBasics[minter_].penalizedUntilTimestamp = uint40(penalizedUntil_); // We charged for the first missed interval based on previous collateral interval length only once // NOTE: extra caution for the case when SPOG changed collateral interval length - _minterBasics[minter_].lastUpdateInterval = UIntMath.safe48(updateCollateralInterval()); + _minterBasics[minter_].lastUpdateInterval = uint32(updateCollateralInterval()); _imposePenalty(minter_, penaltyBase_); } @@ -623,10 +623,10 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { if (newTimestamp_ < lastUpdateTimestamp_) revert StaleCollateralUpdate(newTimestamp_, lastUpdateTimestamp_); _minterBasics[minter_].collateral = UIntMath.safe128(amount_); - _minterBasics[minter_].updateTimestamp = uint48(newTimestamp_); + _minterBasics[minter_].updateTimestamp = uint40(newTimestamp_); // NOTE: Save for the future potential valid penalization if update collateral interval is changed by SPOG. - _minterBasics[minter_].lastUpdateInterval = UIntMath.safe48(updateCollateralInterval()); + _minterBasics[minter_].lastUpdateInterval = uint32(updateCollateralInterval()); } /******************************************************************************************************************\ @@ -650,7 +650,7 @@ contract Protocol is IProtocol, ContinuousIndexing, ERC712 { ); uint256 penalizeFrom_ = _max(lastUpdate_, lastPenalizedUntil_); - uint256 penalizationDeadline_ = UIntMath.safe48(penalizeFrom_ + updateInterval_); + uint256 penalizationDeadline_ = UIntMath.safe40(penalizeFrom_ + updateInterval_); // Return if it is first update collateral ever or deadline for new penalization was not reached yet if (updateInterval_ == 0 || penalizationDeadline_ > block.timestamp) return (0, penalizeFrom_); diff --git a/src/libs/UIntMath.sol b/src/libs/UIntMath.sol index 3da66f68..bcef608c 100644 --- a/src/libs/UIntMath.sol +++ b/src/libs/UIntMath.sol @@ -5,27 +5,20 @@ pragma solidity 0.8.23; library UIntMath { error InvalidUInt24(); - error InvalidUInt48(); - - error InvalidUInt64(); + error InvalidUInt40(); error InvalidUInt128(); - error InvalidUInt184(); + error InvalidUInt192(); function safe24(uint256 n) internal pure returns (uint24) { if (n > type(uint24).max) revert InvalidUInt24(); return uint24(n); } - function safe48(uint256 n) internal pure returns (uint48) { - if (n > type(uint48).max) revert InvalidUInt48(); - return uint48(n); - } - - function safe64(uint256 n) internal pure returns (uint64) { - if (n > type(uint64).max) revert InvalidUInt64(); - return uint64(n); + function safe40(uint256 n) internal pure returns (uint40) { + if (n > type(uint40).max) revert InvalidUInt40(); + return uint40(n); } function safe128(uint256 n) internal pure returns (uint128) { @@ -33,8 +26,8 @@ library UIntMath { return uint128(n); } - function safe184(uint256 n) internal pure returns (uint184) { - if (n > type(uint184).max) revert InvalidUInt184(); - return uint184(n); + function safe192(uint256 n) internal pure returns (uint192) { + if (n > type(uint192).max) revert InvalidUInt192(); + return uint192(n); } } diff --git a/test/utils/MTokenHarness.sol b/test/utils/MTokenHarness.sol index bc460d6d..9aea66ca 100644 --- a/test/utils/MTokenHarness.sol +++ b/test/utils/MTokenHarness.sol @@ -10,7 +10,7 @@ contract MTokenHarness is MToken { constructor(address spogRegistrar_, address protocol_) MToken(spogRegistrar_, protocol_) {} function setLatestIndex(uint256 index_) external { - _latestIndex = UIntMath.safe184(index_); + _latestIndex = UIntMath.safe192(index_); } function setLatestRate(uint256 rate_) external { @@ -18,7 +18,7 @@ contract MTokenHarness is MToken { } function setLatestUpdated(uint256 timestamp_) external { - _latestUpdateTimestamp = UIntMath.safe48(timestamp_); + _latestUpdateTimestamp = uint40(timestamp_); } function setIsEarning(address account_, bool isEarning_) external { diff --git a/test/utils/ProtocolHarness.sol b/test/utils/ProtocolHarness.sol index 8675ecd6..c620cc76 100644 --- a/test/utils/ProtocolHarness.sol +++ b/test/utils/ProtocolHarness.sol @@ -30,8 +30,8 @@ contract ProtocolHarness is Protocol { mintId_ = ++_mintNonce; _mintProposals[minter_] = MintProposal( - UIntMath.safe48(mintId_), - UIntMath.safe48(createdAt_), + uint48(mintId_), + uint40(createdAt_), destination_, UIntMath.safe128(amount_) ); @@ -42,15 +42,15 @@ contract ProtocolHarness is Protocol { } function setCollateralUpdateOf(address minter_, uint256 lastUpdated_) external { - _minterBasics[minter_].updateTimestamp = UIntMath.safe48(lastUpdated_); + _minterBasics[minter_].updateTimestamp = uint40(lastUpdated_); } function setLastCollateralUpdateIntervalOf(address minter_, uint256 updateInterval_) external { - _minterBasics[minter_].lastUpdateInterval = UIntMath.safe48(updateInterval_); + _minterBasics[minter_].lastUpdateInterval = uint32(updateInterval_); } function setPenalizedUntilOf(address minter_, uint256 penalizedUntil_) external { - _minterBasics[minter_].penalizedUntilTimestamp = UIntMath.safe48(penalizedUntil_); + _minterBasics[minter_].penalizedUntilTimestamp = uint40(penalizedUntil_); } function setPrincipalOfActiveOwedMOf(address minter_, uint256 amount_) external { @@ -59,7 +59,7 @@ contract ProtocolHarness is Protocol { } function setLatestIndex(uint256 index_) external { - _latestIndex = UIntMath.safe184(index_); + _latestIndex = UIntMath.safe192(index_); } function setLatestRate(uint256 rate_) external {