From 4f63ee40c0aecdbc7963bfbcdd6868f57266b2ea Mon Sep 17 00:00:00 2001
From: Cal Mac Fadden <108666242+calmacfadden@users.noreply.github.com>
Date: Fri, 23 May 2025 16:14:16 +0400
Subject: [PATCH 01/21] Update README.md new logo (#28)
* Update README.md new logo
* fix: fixed ci versions
---
.github/workflows/ci.yml | 6 +++---
README.md | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f99b901..7985434 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,7 +36,7 @@ jobs:
FORGE_GAS_REPORT: true
FOUNDRY_PROFILE: ${{ github.event_name == 'push' && 'ci' || '' }}
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
with:
name: gas-report
path: gas.txt
@@ -94,7 +94,7 @@ jobs:
FOUNDRY_PROFILE: ${{ github.event_name == 'push' && 'ci' || '' }}
- name: Report code coverage
- uses: zgosalvez/github-actions-report-lcov@v1
+ uses: zgosalvez/github-actions-report-lcov@v4
with:
coverage-files: lcov.info.pruned
minimum-coverage: 90
@@ -108,7 +108,7 @@ jobs:
runs-on: ubuntu-latest
needs: [test]
steps:
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
with:
name: gas-report
path: gas.txt
diff --git a/README.md b/README.md
index 54446f8..6814524 100644
--- a/README.md
+++ b/README.md
@@ -41,5 +41,5 @@ For all information related to the ongoing bug bounty for these contracts run by
---
-
+
From ecccf0a1dd588dbdcbf26cbb8cd9389aab0e0778 Mon Sep 17 00:00:00 2001
From: Cal Mac Fadden <108666242+calmacfadden@users.noreply.github.com>
Date: Sun, 29 Jun 2025 15:41:37 +0400
Subject: [PATCH 02/21] feat: Multiple Requests per Owner (SC-20630) (#30)
* feat: update shares, refactor, basic test
Signed-off-by: calmacfadden
* fix: avoid trailing zero array entries
Signed-off-by: calmacfadden
* chore: formatting and shortcut return if zero array
Signed-off-by: calmacfadden
* feat: added some unit tests, batch updateShares and fixes.
Signed-off-by: calmacfadden
* feat: updated tests
Signed-off-by: calmacfadden
* chore: removed commented code
Signed-off-by: calmacfadden
* chore: removed console logs
Signed-off-by: calmacfadden
* feat: pr review changes
Signed-off-by: calmacfadden
* fix: pr feedback
Signed-off-by: calmacfadden
* chore: formatting
Signed-off-by: calmacfadden
* feat: updated tests, added requestsCountByUser mapping
Signed-off-by: calmacfadden
* feat: max iteration check
Signed-off-by: calmacfadden
* chore: formatting
Signed-off-by: calmacfadden
* fix: removed gas limit test as it broke the gas report
Signed-off-by: calmacfadden
* Feat: Depreciate request ids mapping (Sc-20631) (#31)
* feat: updated tests to depreciate requestIds mapping (WIP)
Signed-off-by: calmacfadden
* feat: fixed tests
Signed-off-by: calmacfadden
* fix: fixed returning wrong array elements
Signed-off-by: calmacfadden
---------
Signed-off-by: calmacfadden
* fix: pr review fixes
Signed-off-by: calmacfadden
* fix: pr review fixes
Signed-off-by: calmacfadden
* feat: updated tests
Signed-off-by: calmacfadden
* chore: naming conventions
Signed-off-by: calmacfadden
* chore: Add TODOs and formatting
* chore: Add TODOs and formatting
* feat: refactored the view functions
Signed-off-by: calmacfadden
* feat: renamed storage
Signed-off-by: calmacfadden
* feat: added tests
Signed-off-by: calmacfadden
* chore: cleanup
* chore: cleanup
---------
Signed-off-by: calmacfadden
Co-authored-by: 0xfarhaan <59924029+0xfarhaan@users.noreply.github.com>
---
.gitignore | 3 +
contracts/MapleWithdrawalManager.sol | 196 ++++--
.../interfaces/IMapleWithdrawalManager.sol | 84 ++-
.../IMapleWithdrawalManagerStorage.sol | 17 +-
.../proxy/MapleWithdrawalManagerStorage.sol | 3 +-
tests/fuzz/AddSharesFuzz.t.sol | 22 +-
tests/fuzz/RemoveSharesFuzz.t.sol | 21 +-
tests/integration/EndToEndTests.t.sol | 32 +-
tests/unit/AddShares.t.sol | 68 +-
tests/unit/CreateInstance.t.sol | 4 +-
tests/unit/ProcessExit.t.sol | 23 +-
tests/unit/ProcessRedemptions.t.sol | 168 ++++-
tests/unit/RemoveRequest.t.sol | 55 +-
tests/unit/RemoveShares.t.sol | 57 +-
tests/unit/SetManualWithdrawal.t.sol | 8 -
tests/unit/UpdateShares.t.sol | 600 ++++++++++++++++++
tests/unit/ViewFunctions.t.sol | 150 +++++
tests/utils/Harnesses.sol | 10 +-
tests/utils/TestBase.sol | 6 +
19 files changed, 1301 insertions(+), 226 deletions(-)
create mode 100644 tests/unit/UpdateShares.t.sol
diff --git a/.gitignore b/.gitignore
index f712bc8..deeea5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ docs/
# Dotenv file
.env
+
+# Vscode
+.vscode*
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index e693114..5ffb820 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -43,6 +43,7 @@ import { MapleWithdrawalManagerStorage } from "./proxy/MapleWithdrawalManagerSto
*/
+// TODO: Ordering of functions
contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManagerStorage , MapleProxiedInternals {
/**************************************************************************************************************************************/
@@ -131,22 +132,10 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
/*** State-Changing Functions ***/
/**************************************************************************************************************************************/
- function addShares(uint256 shares_, address owner_) external override onlyPoolManager {
- require(shares_ > 0, "WM:AS:ZERO_SHARES");
- require(requestIds[owner_] == 0, "WM:AS:IN_QUEUE");
+ function addShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint128 lastRequestId_) {
+ require(shares_ > 0, "WM:AS:ZERO_SHARES");
- uint128 lastRequestId_ = ++queue.lastRequestId;
-
- queue.requests[lastRequestId_] = WithdrawalRequest(owner_, shares_);
-
- requestIds[owner_] = lastRequestId_;
-
- // Increase the number of shares locked.
- totalShares += shares_;
-
- require(ERC20Helper.transferFrom(pool, msg.sender, address(this), shares_), "WM:AS:FAILED_TRANSFER");
-
- emit RequestCreated(lastRequestId_, owner_, shares_);
+ lastRequestId_ = _addRequest(owner_, shares_);
}
function processExit(
@@ -193,61 +182,129 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function removeShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint256 sharesReturned_) {
- uint128 requestId_ = requestIds[owner_];
-
require(shares_ > 0, "WM:RS:ZERO_SHARES");
- require(requestId_ > 0, "WM:RS:NOT_IN_QUEUE");
- uint256 currentShares_ = queue.requests[requestId_].shares;
+ uint128 lastRequestId_ = lastRequestIds[owner_];
- require(shares_ <= currentShares_, "WM:RS:INSUFFICIENT_SHARES");
+ require(lastRequestId_ > 0, "WM:RS:NO_REQUESTS");
- uint256 sharesRemaining_ = currentShares_ - shares_;
+ WithdrawalRequest memory request_ = queue.requests[lastRequestId_];
- totalShares -= shares_;
+ require(request_.shares >= shares_, "WM:RS:INSUFFICIENT_SHARES");
- // If there are no shares remaining, cancel the withdrawal request.
- if (sharesRemaining_ == 0) {
- _removeRequest(owner_, requestId_);
- } else {
- queue.requests[requestId_].shares = sharesRemaining_;
+ sharesReturned_ = _removeShares(lastRequestId_, shares_, request_.owner, request_.shares);
+ }
- emit RequestDecreased(requestId_, shares_);
- }
+ // TODO: To be more gas efficient accumulate shares to be removed and then remove them all at once from totalShares.
+ function removeRequest(
+ address owner_,
+ uint128[] calldata requestIds_
+ ) external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins
+ {
+ require(owner_ != address(0), "WM:RR:ZERO_OWNER");
+ require(requestIds_.length > 0, "WM:RR:ZERO_REQUESTS");
- require(ERC20Helper.transfer(pool, owner_, shares_), "WM:RS:TRANSFER_FAIL");
+ WithdrawalRequest memory withdrawalRequest_;
- sharesReturned_ = shares_;
- }
+ for (uint256 i = 0; i < requestIds_.length; ++i) {
+ withdrawalRequest_ = queue.requests[requestIds_[i]];
- function removeRequest(address owner_) external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins {
- uint128 requestId_ = requestIds[owner_];
+ require(withdrawalRequest_.shares > 0, "WM:RR:NOT_IN_QUEUE");
+ require(withdrawalRequest_.owner == owner_, "WM:RR:NOT_OWNER");
- require(requestId_ > 0, "WM:RR:NOT_IN_QUEUE");
+ _removeRequest(owner_, requestIds_[i]);
- uint256 shares_ = queue.requests[requestId_].shares;
+ totalShares -= withdrawalRequest_.shares;
- totalShares -= shares_;
+ require(ERC20Helper.transfer(pool, owner_, withdrawalRequest_.shares), "WM:RR:TRANSFER_FAIL");
+ }
+ }
- _removeRequest(owner_, requestId_);
+ function setManualWithdrawal(address owner_, bool isManual_) external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins {
+ isManualWithdrawal[owner_] = isManual_;
- require(ERC20Helper.transfer(pool, owner_, shares_), "WM:RR:TRANSFER_FAIL");
+ emit ManualWithdrawalSet(owner_, isManual_);
}
- function setManualWithdrawal(address owner_, bool isManual_) external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins {
- uint128 requestId_ = requestIds[owner_];
+ // TODO: Do both updateShares need nonReentrant guards?
+ function updateShares(
+ uint128 requestId_,
+ uint256 newSharesTotal_
+ ) public override whenProtocolNotPaused returns (uint128 updatedRequestId_)
+ {
+ WithdrawalRequest memory request_ = queue.requests[requestId_];
- require(requestId_ == 0, "WM:SMW:IN_QUEUE");
+ require(request_.owner != address(0), "WM:US:INVALID_REQUEST");
+ require(request_.owner == msg.sender, "WM:US:NOT_OWNER");
+ require(request_.shares != newSharesTotal_, "WM:US:NO_CHANGE");
- isManualWithdrawal[owner_] = isManual_;
+ uint256 sharesToRemove_ = newSharesTotal_ < request_.shares ? request_.shares - newSharesTotal_ : request_.shares;
- emit ManualWithdrawalSet(owner_, isManual_);
+ _removeShares(requestId_, sharesToRemove_, request_.owner, request_.shares); // Removes shares and will cancel the request if there are no shares remaining.
+
+ if (newSharesTotal_ > request_.shares)
+ updatedRequestId_ = _addRequest(request_.owner, newSharesTotal_);
+ else
+ updatedRequestId_ = newSharesTotal_ == 0 ? 0 : requestId_;
+ }
+
+ function updateSharesBatch(
+ uint128[] memory requestIds_,
+ uint256[] calldata newSharesTotals_
+ ) external override whenProtocolNotPaused returns (uint128[] memory updatedRequestIds_)
+ {
+ require(requestIds_.length == newSharesTotals_.length, "WM:USB:ARRAY_LENGTH_MISMATCH");
+
+ for (uint256 i = 0; i < requestIds_.length; ++i) {
+ requestIds_[i] = updateShares(requestIds_[i], newSharesTotals_[i]);
+ }
+
+ updatedRequestIds_ = requestIds_;
}
/**************************************************************************************************************************************/
/*** Internal Functions ***/
/**************************************************************************************************************************************/
+ function _addRequest(address owner_, uint256 shares_) internal returns (uint128 lastRequestId_) {
+ lastRequestId_ = ++queue.lastRequestId;
+
+ queue.requests[lastRequestId_] = WithdrawalRequest(owner_, shares_);
+ lastRequestIds[owner_] = lastRequestId_;
+ requestCount[owner_]++;
+
+ // Increase the number of shares locked.
+ totalShares += shares_;
+
+ require(ERC20Helper.transferFrom(pool, msg.sender, address(this), shares_), "WM:AS:FAILED_TRANSFER");
+
+ emit RequestCreated(lastRequestId_, owner_, shares_);
+ }
+
+ function _removeShares(uint128 requestId_,
+ uint256 sharesToRemove_,
+ address owner_,
+ uint256 currentShares_
+ ) internal returns (uint256 sharesReturned_)
+ {
+ uint256 sharesRemaining_ = currentShares_ - sharesToRemove_;
+
+ totalShares -= sharesToRemove_;
+
+ // If there are no shares remaining, cancel the withdrawal request.
+ if (sharesRemaining_ == 0) {
+ _removeRequest(owner_, requestId_);
+ } else {
+ queue.requests[requestId_].shares = sharesRemaining_;
+
+ emit RequestDecreased(requestId_, sharesToRemove_);
+ }
+
+ require(ERC20Helper.transfer(pool, owner_, sharesToRemove_), "WM:RS:TRANSFER_FAIL");
+
+ sharesReturned_ = sharesToRemove_;
+ }
+
function _calculateRedemption(uint256 sharesToRedeem_) internal view returns (uint256 redeemableShares_, uint256 resultingAssets_) {
IPoolManagerLike poolManager_ = IPoolManagerLike(poolManager);
@@ -314,8 +371,10 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
( processedShares_, resultingAssets_ ) = _calculateRedemption(sharesToProcess_);
+ uint256 sharesRemaining_ = request_.shares - processedShares_;
+
// If there are no remaining shares, request has been fully processed.
- isProcessed_ = (request_.shares - processedShares_) == 0;
+ isProcessed_ = sharesRemaining_ == 0;
emit RequestProcessed(requestId_, request_.owner, processedShares_, resultingAssets_);
@@ -324,7 +383,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_removeRequest(request_.owner, requestId_);
} else {
// Update the withdrawal request.
- queue.requests[requestId_].shares = request_.shares - processedShares_;
+ queue.requests[requestId_].shares = sharesRemaining_;
emit RequestDecreased(requestId_, processedShares_);
}
@@ -343,8 +402,9 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function _removeRequest(address owner_, uint128 requestId_) internal {
- delete requestIds[owner_];
+ delete lastRequestIds[owner_];
delete queue.requests[requestId_];
+ requestCount[owner_]--;
emit RequestRemoved(requestId_);
}
@@ -424,6 +484,48 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
shares_ = queue.requests[requestId_].shares;
}
+ function requests(address owner_)
+ external view override returns (
+ uint128[] memory requestIds_,
+ uint256[] memory shares_
+ )
+ {
+ ( requestIds_, shares_ ) = requests(owner_, queue.nextRequestId, queue.lastRequestId);
+ }
+
+ function requests(address owner_, uint128 firstRequestId_, uint128 lastRequestId_)
+ public view override returns (
+ uint128[] memory requestIds_,
+ uint256[] memory shares_
+ )
+ {
+ WithdrawalRequest memory request_;
+ uint256 count_ = requestCount[owner_];
+ uint256 index_;
+ uint256 requestsAdded_;
+
+ requestIds_ = new uint128[](count_);
+ shares_ = new uint256[](count_);
+
+ while (firstRequestId_ <= lastRequestId_) {
+
+ if(requestsAdded_ == count_) {
+ break;
+ }
+
+ request_ = queue.requests[firstRequestId_];
+
+ if (request_.owner == owner_) {
+ requestIds_[index_] = firstRequestId_;
+ shares_[index_] = request_.shares;
+ ++index_;
+ ++requestsAdded_;
+ }
+
+ ++firstRequestId_;
+ }
+ }
+
function securityAdmin() public view override returns (address securityAdmin_) {
securityAdmin_ = IGlobalsLike(globals()).securityAdmin();
}
diff --git a/contracts/interfaces/IMapleWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol
index 261ebab..6bca5c9 100644
--- a/contracts/interfaces/IMapleWithdrawalManager.sol
+++ b/contracts/interfaces/IMapleWithdrawalManager.sol
@@ -5,6 +5,8 @@ import { IMapleProxied } from "../../modules/maple-proxy-factory/contracts/inter
import { IMapleWithdrawalManagerStorage } from "./IMapleWithdrawalManagerStorage.sol";
+// TODO: naming of two functions with requests()
+// TODO: Add disclaimer about running out of gas on requests() without pagination
interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxied {
/**************************************************************************************************************************************/
@@ -71,7 +73,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @param shares Amount of shares to add.
* @param owner Address of the owner of shares.
*/
- function addShares(uint256 shares, address owner) external;
+ function addShares(uint256 shares, address owner) external returns (uint128 lastRequestId);
/**
* @dev Processes a withdrawal request.
@@ -100,11 +102,12 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
function removeShares(uint256 shares, address owner) external returns (uint256 sharesReturned);
/**
- * @dev Removes a withdrawal request from the queue.
+ * @dev Removes withdrawal requests from the queue.
* Can only be called by the pool delegate.
- * @param owner Address of the owner of shares.
+ * @param owner Address of the owner of shares.
+ * @param requestIds Array of identifiers of the withdrawal requests to remove.
*/
- function removeRequest(address owner) external;
+ function removeRequest(address owner, uint128[] calldata requestIds) external;
/**
* @dev Defines if an account will withdraw shares manually or automatically.
@@ -113,6 +116,30 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
*/
function setManualWithdrawal(address account, bool isManual) external;
+
+ /**
+ * @dev Updates the total amount of shares pending redemption.
+ * 1. If newSharesTotal is zero then the request will be removed.
+ * 2. If newSharesTotal is less than the current shares then the request will be decreased.
+ * 3. If newSharesTotal is greater than the current shares then the current request will be cancelled
+ * and a new request will be created at the end of the queue.
+ * @param requestId Identifier of the withdrawal request that is being updated.
+ * @param newSharesTotal New total amount of shares pending redemption.
+ * @return currentRequestId Identifier of the withdrawal request that was updated or created.
+ */
+ function updateShares(uint128 requestId, uint256 newSharesTotal) external returns (uint128 currentRequestId);
+
+ /**
+ * @dev Updates the total amount of shares pending redemption in batch.
+ * @param requestIds Array of identifiers of the withdrawal requests that are being updated.
+ * @param newSharesTotals Array of new total amounts of shares pending redemption.
+ * @return currentRequestIds Array of identifiers of the withdrawal requests that were updated or created.
+ */
+ function updateSharesBatch(
+ uint128[] calldata requestIds,
+ uint256[] calldata newSharesTotals
+ ) external returns (uint128[] memory currentRequestIds);
+
/**************************************************************************************************************************************/
/*** View Functions ***/
/**************************************************************************************************************************************/
@@ -138,24 +165,24 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
/**
* @dev Returns if a user is able to withdraw. Required for compatibility with pool managers.
* NOTE: Always returns true to fulfil interface requirements.
- * @param owner_ The account to check if it's in withdraw window.
- * @return isInExitWindow_ True if the account is in the withdraw window.
+ * @param owner The account to check if it's in withdraw window.
+ * @return isInExitWindow True if the account is in the withdraw window.
*/
- function isInExitWindow(address owner_) external view returns (bool isInExitWindow_);
+ function isInExitWindow(address owner) external view returns (bool isInExitWindow);
/**
* @dev Gets the total amount of funds that need to be locked to fulfill exits.
* NOTE: Always zero for this implementation.
- * @return lockedLiquidity_ The amount of locked liquidity.
+ * @return lockedLiquidity The amount of locked liquidity.
*/
- function lockedLiquidity() external view returns (uint256 lockedLiquidity_);
+ function lockedLiquidity() external view returns (uint256 lockedLiquidity);
/**
* @dev Gets the amount of locked shares for an account.
- * @param owner_ The address to check the exit for.
- * @return lockedShares_ The amount of manual shares available.
+ * @param owner The address to check the exit for.
+ * @return lockedShares The amount of manual shares available.
*/
- function lockedShares(address owner_) external view returns (uint256 lockedShares_);
+ function lockedShares(address owner) external view returns (uint256 lockedShares);
/**
* @dev Returns the address of the pool delegate.
@@ -176,12 +203,12 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
/**
* @dev Gets the amount of shares that can be withdrawn.
* NOTE: Values just passed through as withdraw is not implemented.
- * @param owner_ The address to check the withdrawal for.
- * @param assets_ The amount of requested shares to withdraw.
- * @return redeemableAssets_ The amount of assets that can be withdrawn.
- * @return resultingShares_ The amount of shares that will be burned.
+ * @param owner The address to check the withdrawal for.
+ * @param assets The amount of requested shares to withdraw.
+ * @return redeemableAssets The amount of assets that can be withdrawn.
+ * @return resultingShares The amount of shares that will be burned.
*/
- function previewWithdraw(address owner_, uint256 assets_) external view returns (uint256 redeemableAssets_, uint256 resultingShares_);
+ function previewWithdraw(address owner, uint256 assets) external view returns (uint256 redeemableAssets, uint256 resultingShares);
/**
* @dev Returns the owner and amount of shares associated with a withdrawal request.
@@ -191,6 +218,29 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
*/
function requests(uint128 requestId) external view returns (address owner, uint256 shares);
+ /**
+ * @dev Returns the pending requests by owner.
+ * @param owner Address of the account to check for pending requests.
+ * @return requestIds Array of request identifiers.
+ * @return shares Array of shares associated with each request.
+ */
+ function requests(address owner) external view returns (uint128[] memory requestIds, uint256[] memory shares);
+
+ /**
+ * @dev Returns the pending requests by owner and request ID range.
+ * Only returns requests that belong to the specified owner.
+ * @param owner Address of the account to check for pending requests.
+ * @param firstRequestId First request ID to include in the result.
+ * @param lastRequestId Last request ID to include in the result.
+ * @return requestIds Array of request identifiers.
+ * @return shares Array of shares associated with each request.
+ */
+ function requests(address owner, uint128 firstRequestId, uint128 lastRequestId)
+ external view returns (
+ uint128[] memory requestIds,
+ uint256[] memory shares
+ );
+
/**
* @dev Returns the address of the security admin.
* @param securityAdmin Address of the security admin.
diff --git a/contracts/interfaces/IMapleWithdrawalManagerStorage.sol b/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
index 9b40966..eb94f51 100644
--- a/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
+++ b/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
@@ -3,6 +3,13 @@ pragma solidity ^0.8.7;
interface IMapleWithdrawalManagerStorage {
+ /**
+ * @dev Returns the identifier of the last withdrawal request made by a specific user.
+ * @param account Address of the user.
+ * @return requestId Identifier of the last withdrawal request made by the user.
+ */
+ function lastRequestIds(address account) external view returns (uint128 requestId);
+
/**
* @dev Returns the address of the pool contract.
* @return pool Address of the pool contract.
@@ -36,12 +43,11 @@ interface IMapleWithdrawalManagerStorage {
function manualSharesAvailable(address owner) external view returns (uint256 sharesAvailable);
/**
- * @dev Returns the request identifier of an account.
- * Returns zero if the account does not have a withdrawal request.
- * @param account Address of the account.
- * @return requestId Identifier of the withdrawal request.
+ * @dev Returns the number of pending withdrawal requests for a specific user.
+ * @param account Account to retrieve the request count for.
+ * @return requestCount Amount of requests pending redemption for the user.
*/
- function requestIds(address account) external view returns (uint128 requestId);
+ function requestCount(address account) external view returns (uint256 requestCount);
/**
* @dev Returns the first and last withdrawal requests pending redemption.
@@ -49,5 +55,4 @@ interface IMapleWithdrawalManagerStorage {
* @return lastRequestId Identifier of the last created withdrawal request.
*/
function queue() external view returns (uint128 nextRequestId, uint128 lastRequestId);
-
}
diff --git a/contracts/proxy/MapleWithdrawalManagerStorage.sol b/contracts/proxy/MapleWithdrawalManagerStorage.sol
index 71dc763..dea3368 100644
--- a/contracts/proxy/MapleWithdrawalManagerStorage.sol
+++ b/contracts/proxy/MapleWithdrawalManagerStorage.sol
@@ -35,8 +35,9 @@ contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
mapping(address => bool) public override isManualWithdrawal; // Defines which users use automated withdrawals (false by default).
- mapping(address => uint128) public override requestIds; // Maps users to their withdrawal requests identifiers.
+ mapping(address => uint128) public override lastRequestIds; // Maps users to their last withdrawal request.
mapping(address => uint256) public override manualSharesAvailable; // Shares available to withdraw for a given manual owner.
+ mapping(address => uint256) public override requestCount; // Maps users to the number of pending requests.
}
diff --git a/tests/fuzz/AddSharesFuzz.t.sol b/tests/fuzz/AddSharesFuzz.t.sol
index 93e8176..2fb5d33 100644
--- a/tests/fuzz/AddSharesFuzz.t.sol
+++ b/tests/fuzz/AddSharesFuzz.t.sol
@@ -10,22 +10,14 @@ contract AddSharesFuzzTests is TestBase {
}
function testFuzz_addShares(uint256[50] memory amount_, address[50] calldata account_) external {
- address owner_;
-
uint128 lastRequestId;
- uint256 shares_;
uint256 totalShares_;
+ uint128 requestId_;
+ uint256 shares_;
for (uint256 i; i < account_.length; ++i) {
amount_[i] = bound(amount_[i], 1, 1e29);
-
- if (withdrawalManager.requestIds(account_[i]) > 0) {
- vm.prank(pm);
- vm.expectRevert("WM:AS:IN_QUEUE");
- withdrawalManager.addShares(amount_[i], account_[i]);
- break;
- }
-
+
pool.mint(pm, amount_[i]);
vm.startPrank(pm);
@@ -40,11 +32,11 @@ contract AddSharesFuzzTests is TestBase {
assertEq(lastRequestId, i + 1);
- ( owner_, shares_ ) = withdrawalManager.requests(lastRequestId);
+ (requestId_, shares_) = getLastRequestByOwner(account_[i]);
- assertEq(shares_, amount_[i]);
- assertEq(withdrawalManager.totalShares(), totalShares_);
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
+ assertEq(shares_, amount_[i]);
+ assertEq(withdrawalManager.totalShares(), totalShares_);
+ assertEq(requestId_, lastRequestId);
}
}
diff --git a/tests/fuzz/RemoveSharesFuzz.t.sol b/tests/fuzz/RemoveSharesFuzz.t.sol
index e8311b5..86519a7 100644
--- a/tests/fuzz/RemoveSharesFuzz.t.sol
+++ b/tests/fuzz/RemoveSharesFuzz.t.sol
@@ -15,12 +15,14 @@ contract RemoveSharesFuzzTests is TestBase {
uint128 lastRequestId;
uint256 shares_;
uint256 totalShares_;
-
+ uint128 requestId_;
+
for (uint256 i; i < account_.length; ++i) {
amount0_[i] = bound(amount0_[i], 1, 1e29);
amount1_[i] = bound(amount1_[i], 1, 1e29);
- if (withdrawalManager.requestIds(account_[i]) > 0) break;
+ ( requestId_, ) = getLastRequestByOwner(account_[i]);
+ if (requestId_ > 0) break;
pool.mint(pm, amount0_[i]);
@@ -36,10 +38,12 @@ contract RemoveSharesFuzzTests is TestBase {
( owner_, shares_ ) = withdrawalManager.requests(lastRequestId);
- assertEq(shares_, amount0_[i]);
- assertEq(withdrawalManager.totalShares(), totalShares_);
- assertEq(lastRequestId, i + 1);
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
+ ( requestId_, ) = getLastRequestByOwner(account_[i]);
+
+ assertEq(shares_, amount0_[i]);
+ assertEq(withdrawalManager.totalShares(), totalShares_);
+ assertEq(lastRequestId, i + 1);
+ assertEq(requestId_, lastRequestId);
if (amount1_[i] > amount0_[i]) {
vm.prank(pm);
@@ -61,10 +65,11 @@ contract RemoveSharesFuzzTests is TestBase {
assertEq(withdrawalManager.totalShares(), totalShares_);
assertEq(lastRequestId, i + 1);
+ ( requestId_, ) = getLastRequestByOwner(owner_);
if (amount0_[i] == amount1_[i]) {
- assertEq(withdrawalManager.requestIds(owner_), 0);
+ assertEq(requestId_, 0);
} else {
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
+ assertEq(requestId_, lastRequestId);
}
}
}
diff --git a/tests/integration/EndToEndTests.t.sol b/tests/integration/EndToEndTests.t.sol
index e128f02..2f86064 100644
--- a/tests/integration/EndToEndTests.t.sol
+++ b/tests/integration/EndToEndTests.t.sol
@@ -26,6 +26,8 @@ contract EndToEndTests is TestBase {
uint256 totalInitialShares = shares1 + shares2 + shares3;
+ uint128 requestId_;
+
// Simulate shares being sent to PM
pool.mint(pm, totalInitialShares);
@@ -45,21 +47,27 @@ contract EndToEndTests is TestBase {
assertQueue({ nextRequestId: 1, lastRequestId: 1 });
assertRequest({ requestId: 1, shares: shares1, owner: lp1 });
- assertEq(withdrawalManager.requestIds(lp1), 1);
+ ( requestId_, ) = getLastRequestByOwner(lp1);
+
+ assertEq(requestId_, 1);
assertEq(pool.balanceOf(address(withdrawalManager)), shares1);
withdrawalManager.addShares(shares2, lp2);
assertQueue({ nextRequestId: 1, lastRequestId: 2 });
assertRequest({ requestId: 2, shares: shares2, owner: lp2 });
- assertEq(withdrawalManager.requestIds(lp2), 2);
+ ( requestId_, ) = getLastRequestByOwner(lp2);
+
+ assertEq(requestId_, 2);
assertEq(pool.balanceOf(address(withdrawalManager)), shares1 + shares2);
withdrawalManager.addShares(shares3, lp3);
assertQueue({ nextRequestId: 1, lastRequestId: 3 });
assertRequest({ requestId: 3, shares: shares3, owner: lp3 });
- assertEq(withdrawalManager.requestIds(lp3), 3);
+ ( requestId_, ) = getLastRequestByOwner(lp3);
+
+ assertEq(requestId_, 3);
assertEq(pool.balanceOf(address(withdrawalManager)), shares1 + shares2 + shares3);
vm.stopPrank();
@@ -77,7 +85,9 @@ contract EndToEndTests is TestBase {
assertQueue({ nextRequestId: 1, lastRequestId: 3 });
assertRequest({ requestId: 1, shares: shares1 / 2, owner: lp1 });
- assertEq(withdrawalManager.requestIds(lp1), 1);
+ ( requestId_, ) = getLastRequestByOwner(lp1);
+
+ assertEq(requestId_, 1);
// Pool Delegate process rest of request 1 + half of request 2
vm.prank(poolDelegate);
@@ -86,15 +96,19 @@ contract EndToEndTests is TestBase {
// Shares of lp2 remain locked in wm
assertEq(withdrawalManager.totalShares(), totalInitialShares - shares1);
+ ( requestId_, ) = getLastRequestByOwner(lp1);
+
// Lp1 is removed from queue, although the `request` data structure remains populated.
assertQueue({ nextRequestId: 2, lastRequestId: 3 });
assertRequest({ requestId: 1, shares: 0, owner: address(0) });
- assertEq(withdrawalManager.requestIds(lp1), 0);
+ assertEq(requestId_, 0);
// Lp2 is still on the queue, and had it's manual shares incremented.
assertRequest({ requestId: 2, shares: shares2 / 2, owner: lp2 });
- assertEq(withdrawalManager.requestIds(lp2), 2);
+ ( requestId_, ) = getLastRequestByOwner(lp2);
+
+ assertEq(requestId_, 2);
assertEq(withdrawalManager.manualSharesAvailable(lp2), shares2 / 2);
vm.prank(pm);
@@ -121,12 +135,16 @@ contract EndToEndTests is TestBase {
function testFuzz_fullFLow_fixedExchangeRate(address[10] memory lps, bool[10] memory isManual, uint256[10] memory shares) external {
uint256 totalShares;
uint128 inQueue;
+ uint128 requestId_;
// Iterate through all users and add shares to pool manager
for (uint256 i = 0; i < 10; i++) {
vm.assume(lps[i] != address(0));
+
+ ( requestId_, ) = getLastRequestByOwner(lps[i]);
+
// If it's a unique user, add to the queue
- if (withdrawalManager.requestIds(lps[i]) == 0) {
+ if (requestId_ == 0) {
uint256 sharesRequested = bound(shares[i], 1, 1e18);
// Save each LP value to verify later
diff --git a/tests/unit/AddShares.t.sol b/tests/unit/AddShares.t.sol
index e4a1fa5..ffb70ba 100644
--- a/tests/unit/AddShares.t.sol
+++ b/tests/unit/AddShares.t.sol
@@ -28,13 +28,30 @@ contract AddSharesTests is TestBase {
withdrawalManager.addShares(0, lp);
}
- function test_addShares_alreadyInQueue() external {
+ function test_addShares_multiple_requests() external{
vm.prank(pm);
withdrawalManager.addShares(1, lp);
vm.prank(pm);
- vm.expectRevert("WM:AS:IN_QUEUE");
withdrawalManager.addShares(1, lp);
+
+ ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
+
+ assertEq(lastRequestId_, 2);
+
+ ( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
+
+ assertEq(owner_, lp);
+ assertEq(shares_, 1);
+
+ (uint128[] memory requestIds, uint256[] memory requestShares) = withdrawalManager.requests(lp);
+
+ assertEq(requestIds.length, 2);
+ assertEq(requestShares.length, 2);
+ assertEq(requestIds[0], 1);
+ assertEq(requestIds[1], 2);
+ assertEq(requestShares[0], 1);
+ assertEq(requestShares[1], 1);
}
function test_addShares_failedTransfer() external {
@@ -44,9 +61,9 @@ contract AddSharesTests is TestBase {
}
function test_addShares_newRequestAddedToQueue() external {
- ( , uint128 lastRequestId ) = withdrawalManager.queue();
+ ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
- assertEq(lastRequestId, 0);
+ assertEq(lastRequestId_, 0);
vm.expectEmit();
emit RequestCreated(1, lp, 1);
@@ -54,17 +71,17 @@ contract AddSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(1, lp);
- ( , lastRequestId ) = withdrawalManager.queue();
+ ( , lastRequestId_ ) = withdrawalManager.queue();
- assertEq(lastRequestId, 1);
+ assertEq(lastRequestId_, 1);
}
function test_addShares_newRequestAddedToQueue_manual() external {
withdrawalManager.__setManualWithdrawal(lp, true);
- ( , uint128 lastRequestId ) = withdrawalManager.queue();
+ ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
- assertEq(lastRequestId, 0);
+ assertEq(lastRequestId_, 0);
vm.expectEmit();
emit RequestCreated(1, lp, 1);
@@ -72,27 +89,30 @@ contract AddSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(1, lp);
- ( , lastRequestId ) = withdrawalManager.queue();
+ ( , lastRequestId_ ) = withdrawalManager.queue();
- assertEq(lastRequestId, 1);
+ assertEq(lastRequestId_, 1);
}
function test_addShares_success() external {
+ uint128 requestId_;
+
vm.expectEmit();
emit RequestCreated(1, lp, 1);
vm.prank(pm);
withdrawalManager.addShares(1, lp);
- ( , uint128 lastRequestId ) = withdrawalManager.queue();
+ ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
- assertEq(lastRequestId, 1);
+ assertEq(lastRequestId_, 1);
- ( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId);
-
- assertEq(shares_, 1);
- assertEq(withdrawalManager.totalShares(), 1);
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
+ ( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
+ ( requestId_,) = getLastRequestByOwner(owner_);
+
+ assertEq(shares_, 1);
+ assertEq(withdrawalManager.totalShares(), 1);
+ assertEq(requestId_, lastRequestId_);
address lp2 = makeAddr("lp2");
@@ -102,15 +122,17 @@ contract AddSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(1, lp2);
- ( , lastRequestId ) = withdrawalManager.queue();
+ ( , lastRequestId_ ) = withdrawalManager.queue();
+
+ assertEq(lastRequestId_, 2);
- assertEq(lastRequestId, 2);
+ ( owner_, shares_ ) = withdrawalManager.requests(lastRequestId_);
- ( owner_, shares_ ) = withdrawalManager.requests(lastRequestId);
+ ( requestId_,) = getLastRequestByOwner(owner_);
- assertEq(shares_, 1);
- assertEq(withdrawalManager.totalShares(), 2);
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
+ assertEq(shares_, 1);
+ assertEq(withdrawalManager.totalShares(), 2);
+ assertEq(requestId_, lastRequestId_);
}
}
diff --git a/tests/unit/CreateInstance.t.sol b/tests/unit/CreateInstance.t.sol
index cee2f2a..07a6099 100644
--- a/tests/unit/CreateInstance.t.sol
+++ b/tests/unit/CreateInstance.t.sol
@@ -74,9 +74,9 @@ contract CreateInstanceTests is TestBase {
assertEq(withdrawalManager_.pool(), address(pool));
assertEq(withdrawalManager_.poolManager(), pm);
- ( uint128 nextRequestId, ) = withdrawalManager_.queue();
+ ( uint128 nextRequestId_, ) = withdrawalManager_.queue();
- assertEq(nextRequestId, 1);
+ assertEq(nextRequestId_, 1);
}
}
diff --git a/tests/unit/ProcessExit.t.sol b/tests/unit/ProcessExit.t.sol
index b075bdb..c1f6161 100644
--- a/tests/unit/ProcessExit.t.sol
+++ b/tests/unit/ProcessExit.t.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.7;
-import { TestBase } from "../utils/TestBase.sol";
+import { TestBase, console } from "../utils/TestBase.sol";
// TODO: Add ManualSharesDecreased event to tests
contract ProcessExitTests is TestBase {
@@ -45,6 +45,7 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesToRedeem);
withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
vm.prank(pm);
vm.expectRevert("WM:PE:TOO_MANY_SHARES");
@@ -56,6 +57,8 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setRequest(1, lp, sharesToRedeem);
withdrawalManager.__setTotalShares(sharesToRedeem);
withdrawalManager.__setManualSharesAvailable(lp, sharesToRedeem);
+ withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
asset.burn(address(pool), assetsDeposited);
@@ -69,6 +72,8 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setRequest(1, lp, sharesToRedeem);
withdrawalManager.__setTotalShares(sharesToRedeem);
withdrawalManager.__setManualSharesAvailable(lp, sharesToRedeem);
+ withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
pool.burn(wm, 1);
@@ -81,7 +86,7 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesToRedeem);
withdrawalManager.__setTotalShares(sharesToRedeem);
- withdrawalManager.__setOwnerRequest(lp, 0);
+ withdrawalManager.__setLastRequest(lp, 0);
withdrawalManager.__setManualSharesAvailable(lp, sharesToRedeem);
assertEq(pool.balanceOf(lp), 0);
@@ -93,8 +98,8 @@ contract ProcessExitTests is TestBase {
assertEq(pool.balanceOf(lp), sharesToRedeem);
assertEq(pool.balanceOf(wm), 0);
- assertEq(withdrawalManager.totalShares(), 0);
- assertEq(withdrawalManager.requestIds(lp), 0);
+ assertEq(withdrawalManager.totalShares(), 0);
+ assertEq(withdrawalManager.lastRequestIds(lp), 0);
assertEq(withdrawalManager.manualSharesAvailable(lp), 0);
assertRequest({ requestId: 1, owner: lp, shares: sharesToRedeem });
@@ -105,7 +110,7 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setRequest(1, lp, sharesToRedeem / 2);
withdrawalManager.__setTotalShares(sharesToRedeem);
withdrawalManager.__setManualSharesAvailable(lp, sharesToRedeem / 2);
-
+
// Only half of the liquidity is available.
asset.burn(address(pool), assetsDeposited / 2);
@@ -120,17 +125,17 @@ contract ProcessExitTests is TestBase {
assertEq(withdrawalManager.totalShares(), sharesToRedeem / 2);
- assertEq(withdrawalManager.requestIds(lp), 1);
+ assertEq(withdrawalManager.lastRequestIds(lp), 1);
assertRequest({ requestId: 1, owner: lp, shares: sharesToRedeem / 2 });
}
function test_processExit_automatic() external {
vm.prank(pm);
- ( uint256 redeemableShares, uint256 resultingAssets ) = withdrawalManager.processExit(sharesToRedeem, wm);
+ ( uint256 redeemableShares_, uint256 resultingAssets_ ) = withdrawalManager.processExit(sharesToRedeem, wm);
- assertEq(redeemableShares, sharesToRedeem);
- assertEq(resultingAssets, assetsDeposited);
+ assertEq(redeemableShares_, sharesToRedeem);
+ assertEq(resultingAssets_, assetsDeposited);
}
}
diff --git a/tests/unit/ProcessRedemptions.t.sol b/tests/unit/ProcessRedemptions.t.sol
index 13400e6..eb3a202 100644
--- a/tests/unit/ProcessRedemptions.t.sol
+++ b/tests/unit/ProcessRedemptions.t.sol
@@ -86,23 +86,88 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+
assertEq(withdrawalManager.totalShares(), sharesLocked);
- assertEq(withdrawalManager.requestIds(lp), 0);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
+ assertEq(requestIdsLp_.length, 0);
+ assertEq(sharesLp_.length, 0);
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
}
+ function test_processRedemptions_manual_multiple_requests() external {
+ withdrawalManager.__setManualWithdrawal(lp, true);
+ withdrawalManager.__setRequest(1, lp, sharesLocked / 2);
+ withdrawalManager.__setRequest(2, lp, sharesLocked / 2);
+ withdrawalManager.__setQueue(1, 2);
+ withdrawalManager.__setUserRequestCount(lp, 2);
+
+ vm.prank(poolDelegate);
+ withdrawalManager.processRedemptions(sharesLocked);
+
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+
+ assertEq(withdrawalManager.totalShares(), sharesLocked);
+ assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
+ assertEq(requestIdsLp_.length, 0);
+ assertEq(sharesLp_.length, 0);
+
+ assertRequest({ requestId: 1, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 2, owner: address(0), shares: 0 });
+
+ assertQueue({ nextRequestId: 3, lastRequestId: 2 });
+ }
+
+ function test_processRedemptions_manual_multipleLps_multiple_requests() external {
+
+ address lp2 = makeAddr("lp2");
+ address lp3 = makeAddr("lp3");
+
+ withdrawalManager.__setManualWithdrawal(lp, true);
+ withdrawalManager.__setManualWithdrawal(lp2, true);
+ withdrawalManager.__setManualWithdrawal(lp3, true);
+ withdrawalManager.__setRequest(1, lp, sharesLocked / 4);
+ withdrawalManager.__setRequest(2, lp2, sharesLocked / 4);
+ withdrawalManager.__setRequest(3, lp3, sharesLocked / 4);
+ withdrawalManager.__setRequest(4, lp, sharesLocked / 4);
+ withdrawalManager.__setQueue(1, 4);
+ withdrawalManager.__setUserRequestCount(lp, 2);
+ withdrawalManager.__setUserRequestCount(lp2, 1);
+ withdrawalManager.__setUserRequestCount(lp3, 1);
+
+ vm.prank(poolDelegate);
+ withdrawalManager.processRedemptions(sharesLocked);
+
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+
+ assertEq(withdrawalManager.totalShares(), sharesLocked);
+ assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked / 2);
+ assertEq(withdrawalManager.manualSharesAvailable(lp2), sharesLocked / 4);
+ assertEq(withdrawalManager.manualSharesAvailable(lp3), sharesLocked / 4);
+ assertEq(requestIdsLp_.length, 0);
+ assertEq(sharesLp_.length, 0);
+
+ assertRequest({ requestId: 1, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 2, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 3, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 4, owner: address(0), shares: 0 });
+
+ assertQueue({ nextRequestId: 5, lastRequestId: 4 });
+ }
+
function test_processRedemptions_manual_partial() external {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
// Only half of the liquidity is available.
asset.burn(address(pool), assetsDeposited / 2);
@@ -110,9 +175,16 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked / 2);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ ( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
+
assertEq(withdrawalManager.totalShares(), sharesLocked);
- assertEq(withdrawalManager.requestIds(lp), 1);
+ assertEq(lastRequestId_, 1);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked / 2);
+ assertEq(requestIdsLp_.length, 1);
+ assertEq(sharesLp_.length, 1);
+ assertEq(requestIdsLp_[0], 1);
+ assertEq(sharesLp_[0], sharesLocked / 2);
assertRequest({ requestId: 1, owner: lp, shares: sharesLocked / 2 });
@@ -123,6 +195,7 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
// Add extra liquidity.
asset.mint(address(pool), assetsDeposited);
@@ -130,9 +203,12 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(2 * sharesLocked);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+
assertEq(withdrawalManager.totalShares(), sharesLocked);
- assertEq(withdrawalManager.requestIds(lp), 0);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
+ assertEq(requestIdsLp_.length, 0);
+ assertEq(sharesLp_.length, 0);
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
@@ -142,6 +218,7 @@ contract ProcessRedemptionsTests is TestBase {
function test_processRedemptions_automatic_complete() external {
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
vm.expectEmit();
emit RequestProcessed(1, lp, sharesLocked, assetsDeposited);
@@ -152,9 +229,11 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- assertEq(withdrawalManager.totalShares(), 0);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
- assertEq(withdrawalManager.requestIds(lp), 0);
+ assertEq(withdrawalManager.totalShares(), 0);
+ assertEq(requestIdsLp_.length, 0);
+ assertEq(sharesLp_.length, 0);
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
@@ -164,6 +243,7 @@ contract ProcessRedemptionsTests is TestBase {
function test_processRedemptions_automatic_partial() external {
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
// Only half of the liquidity is available.
asset.burn(address(pool), assetsDeposited / 2);
@@ -177,9 +257,15 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked / 2);
- assertEq(withdrawalManager.totalShares(), sharesLocked / 2);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ ( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
- assertEq(withdrawalManager.requestIds(lp), 1);
+ assertEq(withdrawalManager.totalShares(), sharesLocked / 2);
+ assertEq(lastRequestId_, 1);
+ assertEq(requestIdsLp_.length, 1);
+ assertEq(sharesLp_.length, 1);
+ assertEq(requestIdsLp_[0], 1);
+ assertEq(sharesLp_[0], sharesLocked / 2);
assertRequest({ requestId: 1, owner: lp, shares: sharesLocked / 2 });
@@ -189,6 +275,7 @@ contract ProcessRedemptionsTests is TestBase {
function test_processRedemptions_automatic_overkill() external {
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1);
// Add extra liquidity.
asset.mint(address(pool), assetsDeposited);
@@ -202,9 +289,11 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(2 * sharesLocked);
- assertEq(withdrawalManager.totalShares(), 0);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
- assertEq(withdrawalManager.requestIds(lp), 0);
+ assertEq(withdrawalManager.totalShares(), 0);
+ assertEq(requestIdsLp_.length, 0);
+ assertEq(sharesLp_.length, 0);
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
@@ -218,6 +307,8 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setRequest(1, lp1, 100e18);
withdrawalManager.__setRequest(2, lp2, 150e18);
withdrawalManager.__setQueue(1, 2);
+ withdrawalManager.__setUserRequestCount(lp1, 1);
+ withdrawalManager.__setUserRequestCount(lp2, 1);
vm.expectEmit();
emit RequestProcessed(1, lp1, 100e18, 40e18);
@@ -234,10 +325,14 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- assertEq(withdrawalManager.totalShares(), 0);
+ (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2);
- assertEq(withdrawalManager.requestIds(lp1), 0);
- assertEq(withdrawalManager.requestIds(lp2), 0);
+ assertEq(withdrawalManager.totalShares(), 0);
+ assertEq(requestIdsLp1_.length, 0);
+ assertEq(sharesLp1_.length, 0);
+ assertEq(requestIdsLp2_.length, 0);
+ assertEq(sharesLp2_.length, 0);
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
assertRequest({ requestId: 2, owner: address(0), shares: 0 });
@@ -254,14 +349,14 @@ contract ComplexRedemptionTests is TestBase {
event RequestRemoved(uint128 indexed requestId);
function test_processRedemptions_complex() external {
- uint256 totalAssets = 100e18;
- uint256 totalShares = 250e18;
- uint256 sharesToProcess = 200e18;
+ uint256 totalAssets_ = 100e18;
+ uint256 totalShares_ = 250e18;
+ uint256 sharesToProcess_ = 200e18;
- asset.mint(address(pool), totalShares);
- pool.mint(wm, totalShares);
+ asset.mint(address(pool), totalShares_);
+ pool.mint(wm, totalShares_);
- poolManager.__setTotalAssets(totalAssets);
+ poolManager.__setTotalAssets(totalAssets_);
withdrawalManager.__setRequest(1, address(0), 0); // Already processed
withdrawalManager.__setRequest(2, address(2), 100e18); // Fully processed
@@ -271,9 +366,14 @@ contract ComplexRedemptionTests is TestBase {
withdrawalManager.__setRequest(6, address(6), 25e18); // Out of shares
withdrawalManager.__setManualWithdrawal(address(3), true);
- withdrawalManager.__setTotalShares(totalShares);
+ withdrawalManager.__setTotalShares(totalShares_);
withdrawalManager.__setQueue(2, 6);
+ withdrawalManager.__setUserRequestCount(address(2), 1);
+ withdrawalManager.__setUserRequestCount(address(3), 1);
+ withdrawalManager.__setUserRequestCount(address(5), 1);
+ withdrawalManager.__setUserRequestCount(address(6), 1);
+
vm.expectEmit();
emit RequestProcessed(2, address(2), 100e18, 40e18);
@@ -287,13 +387,27 @@ contract ComplexRedemptionTests is TestBase {
emit RequestDecreased(5, 50e18);
vm.prank(poolDelegate);
- withdrawalManager.processRedemptions(sharesToProcess);
-
- assertEq(withdrawalManager.requestIds(address(2)), 0);
- assertEq(withdrawalManager.requestIds(address(3)), 0);
- assertEq(withdrawalManager.requestIds(address(4)), 0);
- assertEq(withdrawalManager.requestIds(address(5)), 5);
- assertEq(withdrawalManager.requestIds(address(6)), 6);
+ withdrawalManager.processRedemptions(sharesToProcess_);
+
+ (uint128[] memory requestIds2_, uint256[] memory shares2_) = withdrawalManager.requests(address(2));
+ (uint128[] memory requestIds3_, uint256[] memory shares3_) = withdrawalManager.requests(address(3));
+ (uint128[] memory requestIds5_, uint256[] memory shares5_) = withdrawalManager.requests(address(5));
+ (uint128[] memory requestIds6_, uint256[] memory shares6_) = withdrawalManager.requests(address(6));
+
+ assertEq(requestIds2_.length, 0);
+ assertEq(shares2_.length, 0);
+ assertEq(requestIds3_.length, 0);
+ assertEq(shares3_.length, 0);
+
+ assertEq(requestIds5_.length, 1);
+ assertEq(shares5_.length, 1);
+ assertEq(requestIds5_[0], 5);
+ assertEq(shares5_[0], 25e18);
+
+ assertEq(requestIds6_.length, 1);
+ assertEq(shares6_.length, 1);
+ assertEq(requestIds6_[0], 6);
+ assertEq(shares6_[0], 25e18);
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
assertRequest({ requestId: 2, owner: address(0), shares: 0 });
@@ -305,7 +419,7 @@ contract ComplexRedemptionTests is TestBase {
assertEq(withdrawalManager.manualSharesAvailable(address(3)), 50e18);
// Shares from the manual request are not redeemed.
- assertEq(withdrawalManager.totalShares(), totalShares - sharesToProcess + 50e18);
+ assertEq(withdrawalManager.totalShares(), totalShares_ - sharesToProcess_ + 50e18);
// Request `5` is partially processed and becomes the next request.
assertQueue({ nextRequestId: 5, lastRequestId: 6 });
diff --git a/tests/unit/RemoveRequest.t.sol b/tests/unit/RemoveRequest.t.sol
index 2641cab..0b2fda5 100644
--- a/tests/unit/RemoveRequest.t.sol
+++ b/tests/unit/RemoveRequest.t.sol
@@ -20,21 +20,27 @@ contract RemoveRequestTests is TestBase {
globals.__setFunctionPaused(true);
vm.expectRevert("WM:PAUSED");
- withdrawalManager.removeRequest(lp);
+ withdrawalManager.removeRequest(lp, new uint128[](0));
}
function test_removeRequest_notProtocolAdmin() external {
vm.expectRevert("WM:NOT_PD_OR_GOV_OR_OA");
- withdrawalManager.removeRequest(lp);
+ withdrawalManager.removeRequest(lp, new uint128[](0));
}
function test_removeRequest_notInQueue() external {
+ uint128[] memory requestIds_ = new uint128[](1);
+ requestIds_[0] = 1;
+
vm.prank(poolDelegate);
vm.expectRevert("WM:RR:NOT_IN_QUEUE");
- withdrawalManager.removeRequest(lp);
+ withdrawalManager.removeRequest(lp, requestIds_);
}
function test_removeRequest_failedTransfer() external {
+ uint128[] memory requestIds_ = new uint128[](1);
+ requestIds_[0] = 1;
+
vm.prank(pm);
withdrawalManager.addShares(1, lp);
@@ -42,38 +48,49 @@ contract RemoveRequestTests is TestBase {
vm.prank(poolDelegate);
vm.expectRevert("WM:RR:TRANSFER_FAIL");
- withdrawalManager.removeRequest(lp);
+ withdrawalManager.removeRequest(lp, requestIds_);
}
+ // TODO: Add test with multiple requests to remove.
function test_removeRequest_success() external {
+ uint128 lastRequestIdLp_;
+
vm.prank(pm);
withdrawalManager.addShares(2, lp);
- ( , uint128 lastRequestId ) = withdrawalManager.queue();
+ ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
- ( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId);
+ ( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
- assertEq(shares_, 2);
- assertEq(withdrawalManager.totalShares(), 2);
- assertEq(lastRequestId, 1);
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
+ ( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
+
+ assertEq(shares_, 2);
+ assertEq(withdrawalManager.totalShares(), 2);
+ assertEq(lastRequestId_, 1);
+ assertEq(lastRequestIdLp_, lastRequestId_);
+
+ uint128[] memory requestIds = new uint128[](1);
+ requestIds[0] = lastRequestId_;
vm.expectEmit();
emit RequestRemoved(1);
vm.prank(poolDelegate);
- withdrawalManager.removeRequest(lp);
+ withdrawalManager.removeRequest(lp, requestIds);
+
+ ( , lastRequestId_ ) = withdrawalManager.queue();
- ( , lastRequestId ) = withdrawalManager.queue();
+ ( owner_, shares_ ) = withdrawalManager.requests(lastRequestId_);
- ( owner_, shares_ ) = withdrawalManager.requests(lastRequestId);
+ ( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
+ ( uint128 lastRequestByOwner_, ) = getLastRequestByOwner(owner_);
- assertEq(lastRequestId, 1);
- assertEq(shares_, 0);
- assertEq(owner_, address(0));
- assertEq(withdrawalManager.requestIds(owner_), 0);
- assertEq(withdrawalManager.requestIds(lp), 0);
- assertEq(withdrawalManager.totalShares(), 0);
+ assertEq(lastRequestId_, 1);
+ assertEq(shares_, 0);
+ assertEq(owner_, address(0));
+ assertEq(lastRequestIdLp_, 0);
+ assertEq(lastRequestByOwner_, 0);
+ assertEq(withdrawalManager.totalShares(), 0);
}
}
diff --git a/tests/unit/RemoveShares.t.sol b/tests/unit/RemoveShares.t.sol
index 43ba227..013cc8b 100644
--- a/tests/unit/RemoveShares.t.sol
+++ b/tests/unit/RemoveShares.t.sol
@@ -30,10 +30,12 @@ contract RemoveSharesTests is TestBase {
}
function test_removeShares_notInQueue() external {
- assertEq(withdrawalManager.requestIds(pm), 0);
+ ( uint128 lastRequestId_,) = getLastRequestByOwner(pm);
+
+ assertEq(lastRequestId_, 0);
vm.prank(pm);
- vm.expectRevert("WM:RS:NOT_IN_QUEUE");
+ vm.expectRevert("WM:RS:NO_REQUESTS");
withdrawalManager.removeShares(1, lp);
}
@@ -58,62 +60,49 @@ contract RemoveSharesTests is TestBase {
}
function test_removeShares_success_decreaseRequest() external {
+ uint128 lastRequestId_;
+
vm.prank(pm);
withdrawalManager.addShares(2, lp);
- ( , uint128 lastRequestId ) = withdrawalManager.queue();
-
- ( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId);
-
- assertEq(shares_, 2);
- assertEq(withdrawalManager.totalShares(), 2);
- assertEq(lastRequestId, 1);
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
-
vm.expectEmit();
emit RequestDecreased(1, 1);
vm.prank(pm);
withdrawalManager.removeShares(1, lp);
- ( , lastRequestId ) = withdrawalManager.queue();
+ ( , lastRequestId_ ) = withdrawalManager.queue();
+
+ ( address owner_ , uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
- ( , shares_ ) = withdrawalManager.requests(lastRequestId);
+ ( lastRequestId_, ) = getLastRequestByOwner(owner_);
- assertEq(shares_, 1);
- assertEq(withdrawalManager.totalShares(), 1);
- assertEq(lastRequestId, 1);
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
+ assertEq(shares_, 1);
+ assertEq(withdrawalManager.totalShares(), 1);
+ assertEq(lastRequestId_, 1);
}
function test_removeShares_success_cancelRequest() external {
+ uint128 lastRequestId_;
+
vm.prank(pm);
withdrawalManager.addShares(2, lp);
-
- ( , uint128 lastRequestId ) = withdrawalManager.queue();
-
- ( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId);
-
- assertEq(shares_, 2);
- assertEq(withdrawalManager.totalShares(), 2);
- assertEq(lastRequestId, 1);
- assertEq(withdrawalManager.requestIds(owner_), lastRequestId);
-
vm.expectEmit();
emit RequestRemoved(1);
vm.prank(pm);
withdrawalManager.removeShares(2, lp);
- ( , lastRequestId ) = withdrawalManager.queue();
+ ( , lastRequestId_ ) = withdrawalManager.queue();
+
+ ( address owner_ , uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
- ( owner_, shares_ ) = withdrawalManager.requests(lastRequestId);
+ ( lastRequestId_, ) = getLastRequestByOwner(owner_);
- assertEq(lastRequestId, 1);
- assertEq(owner_, address(0));
- assertEq(shares_, 0);
- assertEq(withdrawalManager.requestIds(owner_), 0);
- assertEq(withdrawalManager.totalShares(), 0);
+ assertEq(lastRequestId_, 0);
+ assertEq(owner_, address(0));
+ assertEq(shares_, 0);
+ assertEq(withdrawalManager.totalShares(), 0);
}
}
diff --git a/tests/unit/SetManualWithdrawal.t.sol b/tests/unit/SetManualWithdrawal.t.sol
index f36aff4..465aac7 100644
--- a/tests/unit/SetManualWithdrawal.t.sol
+++ b/tests/unit/SetManualWithdrawal.t.sol
@@ -19,14 +19,6 @@ contract SetManualWithdrawalTests is TestBase {
withdrawalManager.setManualWithdrawal(lp, true);
}
- function test_setManualWithdrawal_existingRequest() external {
- withdrawalManager.__setRequest(1, lp, 100e18);
-
- vm.prank(poolDelegate);
- vm.expectRevert("WM:SMW:IN_QUEUE");
- withdrawalManager.setManualWithdrawal(lp, true);
- }
-
function test_setManualWithdrawal_success() external {
assertEq(withdrawalManager.isManualWithdrawal(lp), false);
diff --git a/tests/unit/UpdateShares.t.sol b/tests/unit/UpdateShares.t.sol
new file mode 100644
index 0000000..eb9d358
--- /dev/null
+++ b/tests/unit/UpdateShares.t.sol
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+import { TestBase } from "../utils/TestBase.sol";
+
+contract UpdateSharesFailureTests is TestBase {
+
+ function setUp() public override {
+ super.setUp();
+ uint256 mintAmount = 200;
+
+ // Simulate LP transfer into PM.
+ pool.mint(pm, mintAmount);
+
+ vm.prank(pm);
+ pool.approve(address(withdrawalManager), mintAmount);
+ }
+
+ function test_updateShares_invalidRequest() external {
+ vm.expectRevert("WM:US:INVALID_REQUEST");
+ withdrawalManager.updateShares(1, 1);
+ }
+
+ function test_updateShares_notOwner() external {
+ vm.prank(pm);
+ withdrawalManager.addShares(1, lp);
+
+ vm.prank(address(0x12));
+ vm.expectRevert("WM:US:NOT_OWNER");
+ withdrawalManager.updateShares(1, 1);
+ }
+
+ function test_updateShares_noChange() external {
+ vm.prank(pm);
+ withdrawalManager.addShares(1, lp);
+
+ vm.prank(lp);
+ vm.expectRevert("WM:US:NO_CHANGE");
+ withdrawalManager.updateShares(1, 1);
+ }
+
+ function test_updateShares_failed_increase_insufficient_shares() external{
+ vm.prank(pm);
+ withdrawalManager.addShares(1, lp);
+
+ vm.expectRevert("WM:AS:FAILED_TRANSFER");
+ vm.prank(lp);
+ withdrawalManager.updateShares(1, 100);
+ }
+
+ function test_updateShares_failed_transfer_insufficient_tokens() external{
+ vm.prank(pm);
+ withdrawalManager.addShares(1, lp);
+
+ vm.expectRevert("WM:AS:FAILED_TRANSFER");
+ vm.prank(lp);
+ withdrawalManager.updateShares(1, 100);
+ }
+
+ function test_updateShares_failed_update_request_already_removed() external{
+ vm.prank(pm);
+ withdrawalManager.addShares(1, lp);
+
+ uint128[] memory requestIds = new uint128[](1);
+ requestIds[0] = 1;
+
+ vm.prank(governor);
+ withdrawalManager.removeRequest(lp, requestIds);
+
+ vm.expectRevert("WM:US:INVALID_REQUEST");
+ vm.prank(lp);
+ withdrawalManager.updateShares(1, 100);
+ }
+}
+
+contract UpdateSharesSuccessTests is TestBase {
+
+ event RequestCreated(uint128 indexed requestId, address indexed owner, uint256 shares);
+
+ event RequestDecreased(uint128 indexed requestId, uint256 shares);
+
+ event RequestRemoved(uint128 indexed requestId);
+
+ function setUp() public override {
+ super.setUp();
+ uint256 mintAmount = 200;
+
+ pool.mint(pm, mintAmount); // Simulate LP transfer into PM.
+ pool.mint(lp, mintAmount); // Give LP some shares to use to increase shares.
+
+ vm.prank(pm);
+ pool.approve(address(withdrawalManager), mintAmount);
+
+ vm.prank(lp);
+ pool.approve(address(withdrawalManager), mintAmount);
+ }
+
+ function test_updateShares_increase() external {
+ vm.prank(pm);
+ uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
+
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIdBefore_, 1, "Request ID should be 1");
+ assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
+ assertEq(sharesBefore_.length, 1, "Shares length should be 1");
+ assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
+ assertEq(sharesBefore_[0], 1, "Shares should be 1");
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestRemoved(requestIdBefore_);
+ emit RequestCreated(requestIdBefore_ + 1, lp, 2);
+ // Increase shares from 1 to 2 creates a new request and removes the old one.
+ uint128 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 2);
+
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+
+ assertEq(newRequestId_, 2, "Request ID should be 2");
+ assertEq(requestIds_.length, 1, "Request IDs length should be 1");
+ assertEq(shares_.length, 1, "Shares length should be 1");
+ assertEq(requestIds_[0], 2, "Request ID should be 2");
+ assertEq(shares_[0], 2, "Shares should be 2");
+ }
+
+ function test_updateShares_remove_request() external{
+ vm.prank(pm);
+ uint128 requestId_ = withdrawalManager.addShares(1, lp);
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestRemoved(requestId_);
+
+ uint128 newRequestId_ = withdrawalManager.updateShares(requestId_, 0);
+
+ assertEq(newRequestId_, 0, "Request ID should be 0");
+ }
+
+ function test_updateShares_decrease() external {
+ vm.prank(pm);
+ uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
+
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIdBefore_, 1, "Request ID Before should be 1 (addShares)");
+ assertEq(requestIdsBefore_.length, 1, "Request IDs length before should be 1");
+ assertEq(sharesBefore_.length, 1, "Shares length before should be 1");
+ assertEq(requestIdsBefore_[0], 1, "Request ID before should be 1");
+ assertEq(sharesBefore_[0], 2, "Shares before should be 2");
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestDecreased(requestIdBefore_, 1);
+ // Decrease shares from 2 to 1 keeps the same request ID.
+ uint128 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 1);
+
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+
+ assertEq(newRequestId_, 1, "Request ID should be 1 (addShares)");
+ assertEq(requestIds_.length, 1, "Request IDs length should be 1");
+ assertEq(shares_.length, 1, "Shares length should be 1");
+ assertEq(requestIds_[0], 1, "Request ID should be 1");
+ assertEq(shares_[0], 1, "Shares should be 1");
+ }
+
+ function test_update_shares_multiple_lps_requestsByOwner() external {
+ address lp2_ = makeAddr("lp2");
+ address lp3_ = makeAddr("lp3");
+
+ uint256 requestAmount1_ = 10;
+ uint256 requestAmount2_ = 20;
+
+ pool.mint(pm, 3000);
+ pool.mint(lp, 1000);
+ pool.mint(lp2_, 1000);
+ pool.mint(lp3_, 1000);
+
+ vm.prank(lp3_);
+ pool.approve(address(withdrawalManager), 1000);
+
+ vm.startPrank(pm);
+
+ vm.expectEmit();
+ emit RequestCreated(1, lp, requestAmount1_);
+ withdrawalManager.addShares(requestAmount1_, lp);
+ vm.expectEmit();
+ emit RequestCreated(2, lp, requestAmount2_);
+ withdrawalManager.addShares(requestAmount2_, lp);
+
+ vm.expectEmit();
+ emit RequestCreated(3, lp2_, requestAmount1_);
+ withdrawalManager.addShares(requestAmount1_, lp2_);
+ vm.expectEmit();
+ emit RequestCreated(4, lp2_, requestAmount2_);
+ withdrawalManager.addShares(requestAmount2_, lp2_);
+
+ vm.expectEmit();
+ emit RequestCreated(5, lp3_, requestAmount1_);
+ withdrawalManager.addShares(requestAmount1_, lp3_);
+ vm.expectEmit();
+ emit RequestCreated(6, lp3_, requestAmount2_);
+ withdrawalManager.addShares(requestAmount2_, lp3_);
+
+ vm.stopPrank();
+
+ {
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2_);
+ (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requests(lp3_);
+
+ assertEq(requestIdsLp_.length, 2, "LP request IDs length should be 2");
+ assertEq(requestIdsLp2_.length, 2, "LP2 request IDs length should be 2");
+ assertEq(requestIdsLp3_.length, 2, "LP3 request IDs length should be 2");
+
+ assertEq(sharesLp_.length, 2, "LP shares length should be 2");
+ assertEq(sharesLp2_.length, 2, "LP2 shares length should be 2");
+ assertEq(sharesLp3_.length, 2, "LP3 shares length should be 2");
+
+ assertEq(requestIdsLp_[0], 1, "LP request ID index 0 should be 1");
+ assertEq(requestIdsLp_[1], 2, "LP request ID index 1 should be 2");
+ assertEq(requestIdsLp2_[0], 3, "LP2 request ID index 0 should be 3");
+ assertEq(requestIdsLp2_[1], 4, "LP2 request ID index 1 should be 4");
+ assertEq(requestIdsLp3_[0], 5, "LP3 request ID index 0 should be 5");
+ assertEq(requestIdsLp3_[1], 6, "LP3 request ID index 1 should be 6");
+
+ assertEq(sharesLp_[0], requestAmount1_, "LP shares index 0 is incorrect");
+ assertEq(sharesLp_[1], requestAmount2_, "LP shares index 1 is incorrect");
+ assertEq(sharesLp2_[0], requestAmount1_, "LP2 shares index 0 is incorrect");
+ assertEq(sharesLp2_[1], requestAmount2_, "LP2 shares index 1 is incorrect");
+ assertEq(sharesLp3_[0], requestAmount1_, "LP3 shares index 0 is incorrect");
+ assertEq(sharesLp3_[1], requestAmount2_, "LP3 shares index 1 is incorrect");
+ }
+ {
+ // Decrease a request
+ vm.prank(lp);
+ uint128 requestIdLp_ = 1;
+ vm.expectEmit();
+ emit RequestDecreased(requestIdLp_, 1);
+ uint128 updatedRequestIdLp_ = withdrawalManager.updateShares(requestIdLp_, requestAmount1_ - 1);
+ assertEq(updatedRequestIdLp_, requestIdLp_, "Updated request ID should be 1");
+
+ // Remove a request
+ vm.prank(lp2_);
+ uint128 requestIdLp2_ = 3;
+ vm.expectEmit();
+ emit RequestRemoved(requestIdLp2_);
+ uint128 updatedRequestIdLp2_ = withdrawalManager.updateShares(requestIdLp2_, 0);
+ assertEq(updatedRequestIdLp2_, 0, "Updated request ID should be 0");
+
+ // Increase a request
+ vm.prank(lp3_);
+ uint128 requestIdLp3_ = 5;
+ vm.expectEmit();
+ emit RequestRemoved(requestIdLp3_);
+ emit RequestCreated(7, lp3_, requestAmount2_ + 10);
+ uint128 updatedRequestIdLp3 = withdrawalManager.updateShares(requestIdLp3_, requestAmount2_ + 10);
+ assertEq(updatedRequestIdLp3, 7, "Updated request ID should be 7");
+
+ vm.stopPrank();
+ }
+
+ {
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2_);
+ (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requests(lp3_);
+
+ assertEq(requestIdsLp_.length, 2, "LP request IDs length should be 2");
+ assertEq(requestIdsLp2_.length, 1, "LP2 request IDs length should be 1");
+ assertEq(requestIdsLp3_.length, 2, "LP3 request IDs length should be 2");
+
+ assertEq(sharesLp_.length, 2, "LP shares length should be 2");
+ assertEq(sharesLp2_.length, 1, "LP2 shares length should be 2");
+ assertEq(sharesLp3_.length, 2, "LP3 shares length should be 2");
+
+ assertEq(requestIdsLp_[0], 1, "LP request ID index 0 should be 1");
+ assertEq(requestIdsLp_[1], 2, "LP request ID index 1 should be 2");
+ assertEq(requestIdsLp2_[0], 4, "LP2 request ID index 1 should be 4");
+ assertEq(requestIdsLp3_[0], 6, "LP3 request ID index 0 should be 5");
+ assertEq(requestIdsLp3_[1], 7, "LP3 request ID index 1 should be 7");
+
+ assertEq(sharesLp_[0], requestAmount1_ - 1, "LP shares index 0 is incorrect");
+ assertEq(sharesLp_[1], requestAmount2_, "LP shares index 1 is incorrect");
+ assertEq(sharesLp2_[0], requestAmount2_, "LP2 shares index 0 is incorrect");
+ assertEq(sharesLp3_[0], requestAmount2_, "LP3 shares index 0 is incorrect");
+ assertEq(sharesLp3_[1], requestAmount2_ + 10, "LP3 shares index 1 is incorrect");
+ }
+ }
+
+}
+
+contract UpdateSharesBatchFailureTests is TestBase {
+
+ function setUp() public override {
+ super.setUp();
+ uint256 mintAmount = 200;
+
+ pool.mint(pm, mintAmount); // Simulate LP transfer into PM.
+ pool.mint(lp, mintAmount); // Give LP some shares to use to increase shares.
+
+ vm.prank(pm);
+ pool.approve(address(withdrawalManager), mintAmount);
+
+ vm.prank(lp);
+ pool.approve(address(withdrawalManager), mintAmount);
+ }
+
+ function test_updateSharesBatch_invalid_array_lengths() external {
+ uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory sharesToUpdate_ = new uint256[](2);
+
+ vm.expectRevert("WM:USB:ARRAY_LENGTH_MISMATCH");
+ withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ }
+
+ function test_updateSharesBatch_invalid_request() external {
+ uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory sharesToUpdate_ = new uint256[](1);
+
+ vm.expectRevert("WM:US:INVALID_REQUEST");
+ withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ }
+
+ function test_updateSharesBatch_invalid_owner() external {
+ uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory sharesToUpdate_ = new uint256[](1);
+
+ vm.prank(pm);
+ uint128 requestId_ = withdrawalManager.addShares(1, lp);
+
+ requestIdsToUpdate_[0] = requestId_;
+ sharesToUpdate_[0] = 2;
+
+ vm.prank(address(0x12));
+ vm.expectRevert("WM:US:NOT_OWNER");
+ withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ }
+
+ function test_updateSharesBatch_no_change_in_shares() external {
+ uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory sharesToUpdate_ = new uint256[](1);
+
+ vm.prank(pm);
+ uint128 requestId_ = withdrawalManager.addShares(1, lp);
+
+ requestIdsToUpdate_[0] = requestId_;
+ sharesToUpdate_[0] = 1;
+
+ vm.prank(lp);
+ vm.expectRevert("WM:US:NO_CHANGE");
+ withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ }
+}
+
+contract UpdateSharesBatchSuccessTests is TestBase {
+
+ event RequestCreated(uint128 indexed requestId, address indexed owner, uint256 shares);
+
+ event RequestDecreased(uint128 indexed requestId, uint256 shares);
+
+ event RequestRemoved(uint128 indexed requestId);
+
+ function setUp() public override {
+ super.setUp();
+ uint256 mintAmount = 200;
+
+ pool.mint(pm, mintAmount); // Simulate LP transfer into PM.
+ pool.mint(lp, mintAmount); // Give LP some shares to use to increase shares.
+
+ vm.prank(pm);
+ pool.approve(address(withdrawalManager), mintAmount);
+
+ vm.prank(lp);
+ pool.approve(address(withdrawalManager), mintAmount);
+ }
+
+
+ function test_updateSharesBatch_increase_single_request() external {
+ vm.prank(pm);
+ uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
+
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIdBefore_, 1, "Request ID should be 1");
+ assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
+ assertEq(sharesBefore_.length, 1, "Shares length should be 1");
+ assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
+ assertEq(sharesBefore_[0], 1, "Shares should be 1");
+
+ uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ requestIdsToUpdate_[0] = requestIdBefore_;
+
+ uint256[] memory sharesToUpdate_ = new uint256[](1);
+ sharesToUpdate_[0] = 2;
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestRemoved(requestIdBefore_);
+ emit RequestCreated(requestIdBefore_ + 1, lp, 2);
+
+ // Increase shares creates a new request and removes the old one.
+ uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+
+ assertEq(newRequestIds_.length, 1, "New request IDs length should be 1");
+ assertEq(newRequestIds_[0], 2, "New request ID should be 2");
+
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIds_.length, 1, "Request IDs length should be 1");
+ assertEq(shares_.length, 1, "Shares length should be 1");
+ assertEq(requestIds_[0], 2, "Request ID should be 2");
+ assertEq(shares_[0], 2, "Shares should be 2");
+ }
+
+ function test_updateSharesBatch_increase_multiple_requests() external {
+ vm.startPrank(pm);
+ uint128 requestId1Before_ = withdrawalManager.addShares(1, lp);
+ uint128 requestId2Before_ = withdrawalManager.addShares(1, lp);
+ vm.stopPrank();
+
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+
+ assertEq(requestId1Before_, 1, "Request ID should be 1");
+ assertEq(requestId2Before_, 2, "Request ID should be 1");
+ assertEq(requestIdsBefore_.length, 2, "Request IDs length should be 2");
+ assertEq(sharesBefore_.length, 2, "Shares length should be 2");
+ assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
+ assertEq(sharesBefore_[0], 1, "Shares should be 1");
+ assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
+ assertEq(sharesBefore_[1], 1, "Shares should be 1");
+
+ uint128[] memory requestIdsToUpdate_ = new uint128[](2);
+ requestIdsToUpdate_[0] = requestId1Before_;
+ requestIdsToUpdate_[1] = requestId2Before_;
+
+ uint256[] memory sharesToUpdate_ = new uint256[](2);
+ sharesToUpdate_[0] = 2;
+ sharesToUpdate_[1] = 3;
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestRemoved(requestId1Before_);
+ emit RequestCreated(3, lp, 2);
+
+ vm.expectEmit();
+ emit RequestRemoved(requestId2Before_);
+ emit RequestCreated(4, lp, 3);
+
+ // Increase shares creates a new request and removes the old one.
+ uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+
+ assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
+ assertEq(newRequestIds_[0], 3, "New request ID should be 3");
+ assertEq(newRequestIds_[1], 4, "New request ID should be 4");
+
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIds_.length, 2, "Request IDs length should be 1");
+ assertEq(shares_.length, 2, "Shares length should be 1");
+ assertEq(requestIds_[0], 3, "Request ID should be 2");
+ assertEq(shares_[0], 2, "Shares should be 2");
+ assertEq(requestIds_[1], 4, "Request ID should be 3");
+ assertEq(shares_[1], 3, "Shares should be 3");
+ }
+
+ function test_updateSharesBatch_decrease_single_request() external {
+ vm.prank(pm);
+ uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
+
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIdBefore_, 1, "Request ID should be 1");
+ assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
+ assertEq(sharesBefore_.length, 1, "Shares length should be 1");
+ assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
+ assertEq(sharesBefore_[0], 2, "Shares should be 2");
+
+ uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ requestIdsToUpdate_[0] = requestIdBefore_;
+
+ uint256[] memory sharesToUpdate_ = new uint256[](1);
+ sharesToUpdate_[0] = 1;
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestDecreased(requestIdBefore_, 1);
+
+ // Decrease in shares updates the existing request.
+ uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+
+ assertEq(newRequestIds_.length, 1, "New request IDs length should be 1");
+ assertEq(newRequestIds_[0], 1, "New request ID should be 1");
+
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIds_.length, 1, "Request IDs length should be 1");
+ assertEq(shares_.length, 1, "Shares length should be 1");
+ assertEq(requestIds_[0], 1, "Request ID should be 1");
+ assertEq(shares_[0], 1, "Shares should be 1");
+ }
+
+ function test_updateSharesBatch_decrease_multiple_requests() external {
+ vm.startPrank(pm);
+ uint128 requestId1Before_ = withdrawalManager.addShares(2, lp);
+ uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
+ vm.stopPrank();
+
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+
+ assertEq(requestId1Before_, 1, "Request ID should be 1");
+ assertEq(requestId2Before_, 2, "Request ID should be 1");
+ assertEq(requestIdsBefore_.length, 2, "Request IDs length should be 2");
+ assertEq(sharesBefore_.length, 2, "Shares length should be 2");
+ assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
+ assertEq(sharesBefore_[0], 2, "Shares should be 2");
+ assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
+ assertEq(sharesBefore_[1], 2, "Shares should be 2");
+
+ uint128[] memory requestIdsToUpdate_ = new uint128[](2);
+ requestIdsToUpdate_[0] = requestId1Before_;
+ requestIdsToUpdate_[1] = requestId2Before_;
+
+ uint256[] memory sharesToUpdate_ = new uint256[](2);
+ sharesToUpdate_[0] = 1;
+ sharesToUpdate_[1] = 1;
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestDecreased(requestId1Before_, 1);
+
+ vm.expectEmit();
+ emit RequestDecreased(requestId2Before_, 1);
+
+ // Decrease in shares updates the existing requests.
+ uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+
+ assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
+ assertEq(newRequestIds_[0], 1, "New request ID should be 1");
+ assertEq(newRequestIds_[1], 2, "New request ID should be 2");
+
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIds_.length, 2, "Request IDs length should be 1");
+ assertEq(shares_.length, 2, "Shares length should be 1");
+ assertEq(requestIds_[0], 1, "Request ID should be 1");
+ assertEq(shares_[0], 1, "Shares should be 1");
+ assertEq(requestIds_[1], 2, "Request ID should be 2");
+ assertEq(shares_[1], 1, "Shares should be 1");
+ }
+
+ function test_updateSharesBatch_increase_and_decrease_multiple_requests() external {
+ vm.startPrank(pm);
+ uint128 requestId1Before_ = withdrawalManager.addShares(1, lp);
+ uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
+ vm.stopPrank();
+
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+
+ assertEq(requestId1Before_, 1, "Request ID should be 1");
+ assertEq(requestId2Before_, 2, "Request ID should be 1");
+ assertEq(requestIdsBefore_.length, 2, "Request IDs length should be 2");
+ assertEq(sharesBefore_.length, 2, "Shares length should be 2");
+ assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
+ assertEq(sharesBefore_[0], 1, "Shares should be 1");
+ assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
+ assertEq(sharesBefore_[1], 2, "Shares should be 2");
+
+ uint128[] memory requestIdsToUpdate_ = new uint128[](2);
+ requestIdsToUpdate_[0] = requestId1Before_;
+ requestIdsToUpdate_[1] = requestId2Before_;
+
+ uint256[] memory sharesToUpdate_ = new uint256[](2);
+ sharesToUpdate_[0] = 2;
+ sharesToUpdate_[1] = 1;
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestRemoved(requestId1Before_);
+ emit RequestCreated(3, lp, 2);
+
+ vm.expectEmit();
+ emit RequestDecreased(requestId2Before_, 1);
+
+ // Decrease in shares updates the existing requests.
+ uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+
+ assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
+ assertEq(newRequestIds_[0], 3, "New request ID should be 3");
+ assertEq(newRequestIds_[1], 2, "New request ID should be 2");
+
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+
+ assertEq(requestIds_.length, 2, "Request IDs length should be 1");
+ assertEq(shares_.length, 2, "Shares length should be 1");
+ assertEq(requestIds_[0], 2, "Request ID should be 2");
+ assertEq(shares_[0], 1, "Shares should be 1");
+ assertEq(requestIds_[1], 3, "Request ID should be 3");
+ assertEq(shares_[1], 2, "Shares should be 2");
+ }
+
+}
diff --git a/tests/unit/ViewFunctions.t.sol b/tests/unit/ViewFunctions.t.sol
index c5624bb..4ba72f1 100644
--- a/tests/unit/ViewFunctions.t.sol
+++ b/tests/unit/ViewFunctions.t.sol
@@ -24,4 +24,154 @@ contract ViewFunctionsTests is TestBase {
assertEq(redeemableShares, 0);
}
+ function test_requests_by_owner() external {
+ address lp1 = makeAddr("lp1");
+ address lp2 = makeAddr("lp2");
+ address lp3 = makeAddr("lp3");
+ address lp4 = makeAddr("lp4");
+
+ uint256 assetsDeposited_ = 100e18;
+
+ withdrawalManager.__setRequest(1, lp1, assetsDeposited_);
+ withdrawalManager.__setRequest(2, lp2, assetsDeposited_);
+ withdrawalManager.__setRequest(3, lp3, assetsDeposited_);
+ withdrawalManager.__setRequest(4, lp4, assetsDeposited_);
+ withdrawalManager.__setRequest(5, lp1, assetsDeposited_);
+
+ withdrawalManager.__setUserRequestCount(lp1, 2);
+ withdrawalManager.__setUserRequestCount(lp2, 1);
+ withdrawalManager.__setUserRequestCount(lp3, 1);
+ withdrawalManager.__setUserRequestCount(lp4, 1);
+
+ withdrawalManager.__setQueue(1, 5);
+
+ (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requests(lp1);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2);
+ (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requests(lp3);
+ (uint128[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requests(lp4);
+
+
+ assertEq(requestIdsLp1_.length, 2);
+ assertEq(requestIdsLp1_[0], 1);
+ assertEq(requestIdsLp1_[1], 5);
+
+ assertEq(sharesLp1_.length, 2);
+ assertEq(sharesLp1_[0], assetsDeposited_);
+ assertEq(sharesLp1_[1], assetsDeposited_);
+
+ assertEq(requestIdsLp2_.length, 1);
+ assertEq(requestIdsLp2_[0], 2);
+
+ assertEq(sharesLp2_.length, 1);
+ assertEq(sharesLp2_[0], assetsDeposited_);
+
+ assertEq(requestIdsLp3_.length, 1);
+ assertEq(requestIdsLp3_[0], 3);
+
+ assertEq(sharesLp3_.length, 1);
+ assertEq(sharesLp3_[0], assetsDeposited_);
+
+ assertEq(requestIdsLp4_.length, 1);
+ assertEq(requestIdsLp4_[0], 4);
+
+ assertEq(sharesLp4_.length, 1);
+ assertEq(sharesLp4_[0], assetsDeposited_);
+ }
+
+ function test_requests_by_owner_and_range() external {
+ address lp1 = makeAddr("lp1");
+ address lp2 = makeAddr("lp2");
+ address lp3 = makeAddr("lp3");
+ address lp4 = makeAddr("lp4");
+
+ uint256 assetsDeposited_ = 100e18;
+
+ withdrawalManager.__setRequest(1, lp1, assetsDeposited_);
+ withdrawalManager.__setRequest(2, lp2, assetsDeposited_);
+ withdrawalManager.__setRequest(3, lp3, assetsDeposited_);
+ withdrawalManager.__setRequest(4, lp4, assetsDeposited_);
+ withdrawalManager.__setRequest(5, lp1, assetsDeposited_);
+
+ withdrawalManager.__setUserRequestCount(lp1, 2);
+ withdrawalManager.__setUserRequestCount(lp2, 1);
+ withdrawalManager.__setUserRequestCount(lp3, 1);
+ withdrawalManager.__setUserRequestCount(lp4, 1);
+
+ withdrawalManager.__setQueue(1, 5);
+
+ (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requests(lp1, 1, 5);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2, 1, 5);
+ (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requests(lp3, 1, 5);
+ (uint128[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requests(lp4, 1, 5);
+
+
+ assertEq(requestIdsLp1_.length, 2);
+ assertEq(requestIdsLp1_[0], 1);
+ assertEq(requestIdsLp1_[1], 5);
+
+ assertEq(sharesLp1_.length, 2);
+ assertEq(sharesLp1_[0], assetsDeposited_);
+ assertEq(sharesLp1_[1], assetsDeposited_);
+
+ assertEq(requestIdsLp2_.length, 1);
+ assertEq(requestIdsLp2_[0], 2);
+
+ assertEq(sharesLp2_.length, 1);
+ assertEq(sharesLp2_[0], assetsDeposited_);
+
+ assertEq(requestIdsLp3_.length, 1);
+ assertEq(requestIdsLp3_[0], 3);
+
+ assertEq(sharesLp3_.length, 1);
+ assertEq(sharesLp3_[0], assetsDeposited_);
+
+ assertEq(requestIdsLp4_.length, 1);
+ assertEq(requestIdsLp4_[0], 4);
+
+ assertEq(sharesLp4_.length, 1);
+ assertEq(sharesLp4_[0], assetsDeposited_);
+ }
+
+ function test_requests_by_requestId() external {
+ address lp2 = makeAddr("lp2");
+ address lp3 = makeAddr("lp3");
+ address lp4 = makeAddr("lp4");
+
+ uint256 assetsDeposited_ = 100e18;
+
+ withdrawalManager.__setRequest(1, lp, assetsDeposited_);
+ withdrawalManager.__setRequest(2, lp2, assetsDeposited_);
+ withdrawalManager.__setRequest(3, lp3, assetsDeposited_);
+ withdrawalManager.__setRequest(4, lp4, assetsDeposited_);
+ withdrawalManager.__setRequest(5, lp, assetsDeposited_);
+
+ withdrawalManager.__setUserRequestCount(lp, 2);
+ withdrawalManager.__setUserRequestCount(lp2, 1);
+ withdrawalManager.__setUserRequestCount(lp3, 1);
+ withdrawalManager.__setUserRequestCount(lp4, 1);
+
+ withdrawalManager.__setQueue(1, 5);
+
+ (address ownerLp1_, uint256 sharesLp1_) = withdrawalManager.requests(1);
+ (address ownerLp2_, uint256 sharesLp2_) = withdrawalManager.requests(2);
+ (address ownerLp3_, uint256 sharesLp3_) = withdrawalManager.requests(3);
+ (address ownerLp4_, uint256 sharesLp4_) = withdrawalManager.requests(4);
+ (address ownerLp1_2_, uint256 sharesLp1_2_) = withdrawalManager.requests(5);
+
+ assertEq(ownerLp1_, lp);
+ assertEq(sharesLp1_, assetsDeposited_);
+
+ assertEq(ownerLp2_, lp2);
+ assertEq(sharesLp2_, assetsDeposited_);
+
+ assertEq(ownerLp3_, lp3);
+ assertEq(sharesLp3_, assetsDeposited_);
+
+ assertEq(ownerLp4_, lp4);
+ assertEq(sharesLp4_, assetsDeposited_);
+
+ assertEq(ownerLp1_2_, lp);
+ assertEq(sharesLp1_2_, assetsDeposited_);
+ }
+
}
diff --git a/tests/utils/Harnesses.sol b/tests/utils/Harnesses.sol
index ccb252b..6f4cdbf 100644
--- a/tests/utils/Harnesses.sol
+++ b/tests/utils/Harnesses.sol
@@ -17,8 +17,8 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
isManualWithdrawal[owner_] = isManual_;
}
- function __setOwnerRequest(address owner_, uint128 requestId_) external {
- requestIds[owner_] = requestId_;
+ function __setLastRequest(address owner_, uint128 requestId_) external {
+ lastRequestIds[owner_] = requestId_;
}
function __setQueue(uint128 nextRequestId_, uint128 lastRequestId_) external {
@@ -27,12 +27,16 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
function __setRequest(uint128 requestId_, address owner_, uint256 shares_) external {
- requestIds[owner_] = requestId_;
queue.requests[requestId_] = WithdrawalRequest(owner_, shares_);
+ lastRequestIds[owner_] = requestId_;
}
function __setTotalShares(uint256 totalShares_) external {
totalShares = totalShares_;
}
+ function __setUserRequestCount(address owner_, uint256 requestCount_) external {
+ requestCount[owner_] = requestCount_;
+ }
+
}
diff --git a/tests/utils/TestBase.sol b/tests/utils/TestBase.sol
index 88d813c..6509f6e 100644
--- a/tests/utils/TestBase.sol
+++ b/tests/utils/TestBase.sol
@@ -85,4 +85,10 @@ contract TestBase is Test {
assertEq(lastRequestId_, lastRequestId);
}
+ function getLastRequestByOwner(address owner) internal view returns (uint128 lastRequestId, uint256 shares) {
+ ( uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(owner);
+
+ return requestIds_.length == 0 ? (0,0) : (requestIds_[requestIds_.length - 1], shares_[shares_.length - 1]);
+ }
+
}
From 2a64e1e2ae5277cb831fea1d39bb08e70db8267e Mon Sep 17 00:00:00 2001
From: Cal Mac Fadden <108666242+calmacfadden@users.noreply.github.com>
Date: Wed, 16 Jul 2025 12:41:28 +0400
Subject: [PATCH 03/21] feat: TODO Resolutions (SC-20711) (#32)
* feat: fixed todo's
Signed-off-by: calmacfadden
* feat: added multiple remove tests
Signed-off-by: calmacfadden
* feat:reordered some functions
Signed-off-by: calmacfadden
* feat: fix formatting
---------
Signed-off-by: calmacfadden
Co-authored-by: 0xfarhaan <59924029+0xfarhaan@users.noreply.github.com>
---
contracts/MapleWithdrawalManager.sol | 60 ++++++++++++-------
.../interfaces/IMapleWithdrawalManager.sol | 7 +--
tests/unit/AddShares.t.sol | 2 +-
tests/unit/ProcessRedemptions.t.sol | 28 ++++-----
tests/unit/RemoveRequest.t.sol | 52 +++++++++++++++-
tests/unit/UpdateShares.t.sol | 40 ++++++-------
tests/unit/ViewFunctions.t.sol | 16 ++---
tests/utils/TestBase.sol | 2 +-
8 files changed, 133 insertions(+), 74 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 5ffb820..a18424a 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -43,7 +43,6 @@ import { MapleWithdrawalManagerStorage } from "./proxy/MapleWithdrawalManagerSto
*/
-// TODO: Ordering of functions
contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManagerStorage , MapleProxiedInternals {
/**************************************************************************************************************************************/
@@ -129,7 +128,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
/**************************************************************************************************************************************/
- /*** State-Changing Functions ***/
+ /*** State-Changing Functions - OnlyPoolManager ***/
/**************************************************************************************************************************************/
function addShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint128 lastRequestId_) {
@@ -152,6 +151,24 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
: _processManualExit(shares_, owner_);
}
+ function removeShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint256 sharesReturned_) {
+ require(shares_ > 0, "WM:RS:ZERO_SHARES");
+
+ uint128 lastRequestId_ = lastRequestIds[owner_];
+
+ require(lastRequestId_ > 0, "WM:RS:NO_REQUESTS");
+
+ WithdrawalRequest memory request_ = queue.requests[lastRequestId_];
+
+ require(request_.shares >= shares_, "WM:RS:INSUFFICIENT_SHARES");
+
+ sharesReturned_ = _removeShares(lastRequestId_, shares_, request_.owner, request_.shares);
+ }
+
+ /**************************************************************************************************************************************/
+ /*** State-Changing Functions - onlyRedeemer ***/
+ /**************************************************************************************************************************************/
+
function processRedemptions(uint256 maxSharesToProcess_) external override whenProtocolNotPaused nonReentrant onlyRedeemer {
require(maxSharesToProcess_ > 0, "WM:PR:ZERO_SHARES");
@@ -181,21 +198,10 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
queue.nextRequestId = nextRequestId_;
}
- function removeShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint256 sharesReturned_) {
- require(shares_ > 0, "WM:RS:ZERO_SHARES");
-
- uint128 lastRequestId_ = lastRequestIds[owner_];
-
- require(lastRequestId_ > 0, "WM:RS:NO_REQUESTS");
-
- WithdrawalRequest memory request_ = queue.requests[lastRequestId_];
-
- require(request_.shares >= shares_, "WM:RS:INSUFFICIENT_SHARES");
-
- sharesReturned_ = _removeShares(lastRequestId_, shares_, request_.owner, request_.shares);
- }
+ /**************************************************************************************************************************************/
+ /*** State-Changing Functions - Admin functions ***/
+ /**************************************************************************************************************************************/
- // TODO: To be more gas efficient accumulate shares to be removed and then remove them all at once from totalShares.
function removeRequest(
address owner_,
uint128[] calldata requestIds_
@@ -204,6 +210,8 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
require(owner_ != address(0), "WM:RR:ZERO_OWNER");
require(requestIds_.length > 0, "WM:RR:ZERO_REQUESTS");
+ uint256 sharesToRemove_;
+
WithdrawalRequest memory withdrawalRequest_;
for (uint256 i = 0; i < requestIds_.length; ++i) {
@@ -214,10 +222,12 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_removeRequest(owner_, requestIds_[i]);
- totalShares -= withdrawalRequest_.shares;
+ sharesToRemove_ += withdrawalRequest_.shares;
require(ERC20Helper.transfer(pool, owner_, withdrawalRequest_.shares), "WM:RR:TRANSFER_FAIL");
}
+
+ totalShares -= sharesToRemove_;
}
function setManualWithdrawal(address owner_, bool isManual_) external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins {
@@ -226,11 +236,14 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
emit ManualWithdrawalSet(owner_, isManual_);
}
- // TODO: Do both updateShares need nonReentrant guards?
+ /**************************************************************************************************************************************/
+ /*** Unprivileged External Functions ***/
+ /**************************************************************************************************************************************/
+
function updateShares(
uint128 requestId_,
uint256 newSharesTotal_
- ) public override whenProtocolNotPaused returns (uint128 updatedRequestId_)
+ ) public override whenProtocolNotPaused nonReentrant returns (uint128 updatedRequestId_)
{
WithdrawalRequest memory request_ = queue.requests[requestId_];
@@ -240,7 +253,8 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
uint256 sharesToRemove_ = newSharesTotal_ < request_.shares ? request_.shares - newSharesTotal_ : request_.shares;
- _removeShares(requestId_, sharesToRemove_, request_.owner, request_.shares); // Removes shares and will cancel the request if there are no shares remaining.
+ // Removes shares and will cancel the request if there are no shares remaining.
+ _removeShares(requestId_, sharesToRemove_, request_.owner, request_.shares);
if (newSharesTotal_ > request_.shares)
updatedRequestId_ = _addRequest(request_.owner, newSharesTotal_);
@@ -484,16 +498,16 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
shares_ = queue.requests[requestId_].shares;
}
- function requests(address owner_)
+ function requestsByOwner(address owner_)
external view override returns (
uint128[] memory requestIds_,
uint256[] memory shares_
)
{
- ( requestIds_, shares_ ) = requests(owner_, queue.nextRequestId, queue.lastRequestId);
+ ( requestIds_, shares_ ) = requestsByOwnerRange(owner_, queue.nextRequestId, queue.lastRequestId);
}
- function requests(address owner_, uint128 firstRequestId_, uint128 lastRequestId_)
+ function requestsByOwnerRange(address owner_, uint128 firstRequestId_, uint128 lastRequestId_)
public view override returns (
uint128[] memory requestIds_,
uint256[] memory shares_
diff --git a/contracts/interfaces/IMapleWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol
index 6bca5c9..d1ebecf 100644
--- a/contracts/interfaces/IMapleWithdrawalManager.sol
+++ b/contracts/interfaces/IMapleWithdrawalManager.sol
@@ -5,8 +5,6 @@ import { IMapleProxied } from "../../modules/maple-proxy-factory/contracts/inter
import { IMapleWithdrawalManagerStorage } from "./IMapleWithdrawalManagerStorage.sol";
-// TODO: naming of two functions with requests()
-// TODO: Add disclaimer about running out of gas on requests() without pagination
interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxied {
/**************************************************************************************************************************************/
@@ -220,11 +218,12 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
/**
* @dev Returns the pending requests by owner.
+ * NOTE: This function may run out of gas if there are too many requests. Use the overload with pagination.
* @param owner Address of the account to check for pending requests.
* @return requestIds Array of request identifiers.
* @return shares Array of shares associated with each request.
*/
- function requests(address owner) external view returns (uint128[] memory requestIds, uint256[] memory shares);
+ function requestsByOwner(address owner) external view returns (uint128[] memory requestIds, uint256[] memory shares);
/**
* @dev Returns the pending requests by owner and request ID range.
@@ -235,7 +234,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @return requestIds Array of request identifiers.
* @return shares Array of shares associated with each request.
*/
- function requests(address owner, uint128 firstRequestId, uint128 lastRequestId)
+ function requestsByOwnerRange(address owner, uint128 firstRequestId, uint128 lastRequestId)
external view returns (
uint128[] memory requestIds,
uint256[] memory shares
diff --git a/tests/unit/AddShares.t.sol b/tests/unit/AddShares.t.sol
index ffb70ba..f3d7dec 100644
--- a/tests/unit/AddShares.t.sol
+++ b/tests/unit/AddShares.t.sol
@@ -44,7 +44,7 @@ contract AddSharesTests is TestBase {
assertEq(owner_, lp);
assertEq(shares_, 1);
- (uint128[] memory requestIds, uint256[] memory requestShares) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIds, uint256[] memory requestShares) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds.length, 2);
assertEq(requestShares.length, 2);
diff --git a/tests/unit/ProcessRedemptions.t.sol b/tests/unit/ProcessRedemptions.t.sol
index eb3a202..a8a840d 100644
--- a/tests/unit/ProcessRedemptions.t.sol
+++ b/tests/unit/ProcessRedemptions.t.sol
@@ -91,7 +91,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
@@ -113,7 +113,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
@@ -146,7 +146,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked / 2);
@@ -175,7 +175,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked / 2);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
@@ -203,7 +203,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(2 * sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
@@ -229,7 +229,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), 0);
assertEq(requestIdsLp_.length, 0);
@@ -257,7 +257,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked / 2);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked / 2);
@@ -289,7 +289,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(2 * sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), 0);
assertEq(requestIdsLp_.length, 0);
@@ -325,8 +325,8 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requests(lp);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2);
+ (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requestsByOwner(lp);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2);
assertEq(withdrawalManager.totalShares(), 0);
assertEq(requestIdsLp1_.length, 0);
@@ -389,10 +389,10 @@ contract ComplexRedemptionTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesToProcess_);
- (uint128[] memory requestIds2_, uint256[] memory shares2_) = withdrawalManager.requests(address(2));
- (uint128[] memory requestIds3_, uint256[] memory shares3_) = withdrawalManager.requests(address(3));
- (uint128[] memory requestIds5_, uint256[] memory shares5_) = withdrawalManager.requests(address(5));
- (uint128[] memory requestIds6_, uint256[] memory shares6_) = withdrawalManager.requests(address(6));
+ (uint128[] memory requestIds2_, uint256[] memory shares2_) = withdrawalManager.requestsByOwner(address(2));
+ (uint128[] memory requestIds3_, uint256[] memory shares3_) = withdrawalManager.requestsByOwner(address(3));
+ (uint128[] memory requestIds5_, uint256[] memory shares5_) = withdrawalManager.requestsByOwner(address(5));
+ (uint128[] memory requestIds6_, uint256[] memory shares6_) = withdrawalManager.requestsByOwner(address(6));
assertEq(requestIds2_.length, 0);
assertEq(shares2_.length, 0);
diff --git a/tests/unit/RemoveRequest.t.sol b/tests/unit/RemoveRequest.t.sol
index 0b2fda5..89e20c0 100644
--- a/tests/unit/RemoveRequest.t.sol
+++ b/tests/unit/RemoveRequest.t.sol
@@ -10,10 +10,10 @@ contract RemoveRequestTests is TestBase {
function setUp() public override {
super.setUp();
- pool.mint(pm, 2);
+ pool.mint(pm, 4);
vm.prank(pm);
- pool.approve(address(withdrawalManager), 2);
+ pool.approve(address(withdrawalManager), 4);
}
function test_removeRequest_protocolPaused() external {
@@ -51,7 +51,6 @@ contract RemoveRequestTests is TestBase {
withdrawalManager.removeRequest(lp, requestIds_);
}
- // TODO: Add test with multiple requests to remove.
function test_removeRequest_success() external {
uint128 lastRequestIdLp_;
@@ -93,4 +92,51 @@ contract RemoveRequestTests is TestBase {
assertEq(withdrawalManager.totalShares(), 0);
}
+ function test_removeRequest_multiple_requests_success() external {
+ uint128 lastRequestIdLp_;
+
+ vm.startPrank(pm);
+ withdrawalManager.addShares(2, lp);
+ withdrawalManager.addShares(2, lp);
+ vm.stopPrank();
+
+ ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
+
+ ( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
+
+ ( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
+
+ assertEq(shares_, 2);
+ assertEq(withdrawalManager.totalShares(), 4);
+ assertEq(lastRequestId_, 2);
+ assertEq(lastRequestIdLp_, lastRequestId_);
+
+ uint128[] memory requestIds = new uint128[](2);
+ requestIds[0] = 1;
+ requestIds[1] = 2;
+
+ vm.expectEmit();
+ emit RequestRemoved(1);
+
+ vm.expectEmit();
+ emit RequestRemoved(2);
+
+ vm.prank(poolDelegate);
+ withdrawalManager.removeRequest(lp, requestIds);
+
+ ( , lastRequestId_ ) = withdrawalManager.queue();
+
+ ( owner_, shares_ ) = withdrawalManager.requests(lastRequestId_);
+
+ ( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
+ ( uint128 lastRequestByOwner_, ) = getLastRequestByOwner(owner_);
+
+ assertEq(lastRequestId_, 2);
+ assertEq(shares_, 0);
+ assertEq(owner_, address(0));
+ assertEq(lastRequestIdLp_, 0);
+ assertEq(lastRequestByOwner_, 0);
+ assertEq(withdrawalManager.totalShares(), 0);
+ }
+
}
diff --git a/tests/unit/UpdateShares.t.sol b/tests/unit/UpdateShares.t.sol
index eb9d358..623e057 100644
--- a/tests/unit/UpdateShares.t.sol
+++ b/tests/unit/UpdateShares.t.sol
@@ -99,7 +99,7 @@ contract UpdateSharesSuccessTests is TestBase {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
@@ -114,7 +114,7 @@ contract UpdateSharesSuccessTests is TestBase {
// Increase shares from 1 to 2 creates a new request and removes the old one.
uint128 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 2);
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(newRequestId_, 2, "Request ID should be 2");
assertEq(requestIds_.length, 1, "Request IDs length should be 1");
@@ -140,7 +140,7 @@ contract UpdateSharesSuccessTests is TestBase {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID Before should be 1 (addShares)");
assertEq(requestIdsBefore_.length, 1, "Request IDs length before should be 1");
@@ -154,7 +154,7 @@ contract UpdateSharesSuccessTests is TestBase {
// Decrease shares from 2 to 1 keeps the same request ID.
uint128 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 1);
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(newRequestId_, 1, "Request ID should be 1 (addShares)");
assertEq(requestIds_.length, 1, "Request IDs length should be 1");
@@ -204,9 +204,9 @@ contract UpdateSharesSuccessTests is TestBase {
vm.stopPrank();
{
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2_);
- (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requests(lp3_);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
+ (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3_);
assertEq(requestIdsLp_.length, 2, "LP request IDs length should be 2");
assertEq(requestIdsLp2_.length, 2, "LP2 request IDs length should be 2");
@@ -260,9 +260,9 @@ contract UpdateSharesSuccessTests is TestBase {
}
{
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requests(lp);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2_);
- (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requests(lp3_);
+ (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
+ (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3_);
assertEq(requestIdsLp_.length, 2, "LP request IDs length should be 2");
assertEq(requestIdsLp2_.length, 1, "LP2 request IDs length should be 1");
@@ -378,7 +378,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
@@ -403,7 +403,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(newRequestIds_.length, 1, "New request IDs length should be 1");
assertEq(newRequestIds_[0], 2, "New request ID should be 2");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 1, "Request IDs length should be 1");
assertEq(shares_.length, 1, "Shares length should be 1");
@@ -417,7 +417,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(1, lp);
vm.stopPrank();
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
assertEq(requestId2Before_, 2, "Request ID should be 1");
@@ -452,7 +452,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(newRequestIds_[0], 3, "New request ID should be 3");
assertEq(newRequestIds_[1], 4, "New request ID should be 4");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 2, "Request IDs length should be 1");
assertEq(shares_.length, 2, "Shares length should be 1");
@@ -466,7 +466,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
@@ -490,7 +490,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(newRequestIds_.length, 1, "New request IDs length should be 1");
assertEq(newRequestIds_[0], 1, "New request ID should be 1");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 1, "Request IDs length should be 1");
assertEq(shares_.length, 1, "Shares length should be 1");
@@ -504,7 +504,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
vm.stopPrank();
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
assertEq(requestId2Before_, 2, "Request ID should be 1");
@@ -537,7 +537,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(newRequestIds_[0], 1, "New request ID should be 1");
assertEq(newRequestIds_[1], 2, "New request ID should be 2");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 2, "Request IDs length should be 1");
assertEq(shares_.length, 2, "Shares length should be 1");
@@ -553,7 +553,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
vm.stopPrank();
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
assertEq(requestId2Before_, 2, "Request ID should be 1");
@@ -587,7 +587,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(newRequestIds_[0], 3, "New request ID should be 3");
assertEq(newRequestIds_[1], 2, "New request ID should be 2");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(lp);
+ (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 2, "Request IDs length should be 1");
assertEq(shares_.length, 2, "Shares length should be 1");
diff --git a/tests/unit/ViewFunctions.t.sol b/tests/unit/ViewFunctions.t.sol
index 4ba72f1..57c88dc 100644
--- a/tests/unit/ViewFunctions.t.sol
+++ b/tests/unit/ViewFunctions.t.sol
@@ -45,10 +45,10 @@ contract ViewFunctionsTests is TestBase {
withdrawalManager.__setQueue(1, 5);
- (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requests(lp1);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2);
- (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requests(lp3);
- (uint128[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requests(lp4);
+ (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requestsByOwner(lp1);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2);
+ (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3);
+ (uint128[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requestsByOwner(lp4);
assertEq(requestIdsLp1_.length, 2);
@@ -99,10 +99,10 @@ contract ViewFunctionsTests is TestBase {
withdrawalManager.__setQueue(1, 5);
- (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requests(lp1, 1, 5);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requests(lp2, 1, 5);
- (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requests(lp3, 1, 5);
- (uint128[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requests(lp4, 1, 5);
+ (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requestsByOwnerRange(lp1, 1, 5);
+ (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwnerRange(lp2, 1, 5);
+ (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwnerRange(lp3, 1, 5);
+ (uint128[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requestsByOwnerRange(lp4, 1, 5);
assertEq(requestIdsLp1_.length, 2);
diff --git a/tests/utils/TestBase.sol b/tests/utils/TestBase.sol
index 6509f6e..2e9c296 100644
--- a/tests/utils/TestBase.sol
+++ b/tests/utils/TestBase.sol
@@ -86,7 +86,7 @@ contract TestBase is Test {
}
function getLastRequestByOwner(address owner) internal view returns (uint128 lastRequestId, uint256 shares) {
- ( uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requests(owner);
+ ( uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(owner);
return requestIds_.length == 0 ? (0,0) : (requestIds_[requestIds_.length - 1], shares_[shares_.length - 1]);
}
From 8333ed43b59442e867ab021048834c8410ec10f1 Mon Sep 17 00:00:00 2001
From: Cal Mac Fadden <108666242+calmacfadden@users.noreply.github.com>
Date: Thu, 17 Jul 2025 14:31:59 +0400
Subject: [PATCH 04/21] feat: added userRequestSummaries (#33)
* feat: added userRequestSummaries
Signed-off-by: calmacfadden
* fix: missing share changes
Signed-off-by: calmacfadden
* fix: missing natspec
Signed-off-by: calmacfadden
* feat: added tests for userRequestSummaries
Signed-off-by: calmacfadden
* chore: comments
Signed-off-by: calmacfadden
* chore: formatting
---------
Signed-off-by: calmacfadden
Co-authored-by: 0xfarhaan <59924029+0xfarhaan@users.noreply.github.com>
---
contracts/MapleWithdrawalManager.sol | 14 +-
.../IMapleWithdrawalManagerStorage.sol | 7 +-
.../proxy/MapleWithdrawalManagerStorage.sol | 9 +-
tests/unit/AddShares.t.sol | 25 +++
tests/unit/ProcessExit.t.sol | 6 +-
tests/unit/ProcessRedemptions.t.sol | 146 ++++++++++++++++--
tests/unit/RemoveRequest.t.sol | 20 +++
tests/unit/RemoveShares.t.sol | 21 +++
tests/unit/UpdateShares.t.sol | 128 ++++++++++++++-
tests/unit/ViewFunctions.t.sol | 24 +--
tests/utils/Harnesses.sol | 5 +-
11 files changed, 356 insertions(+), 49 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index a18424a..8d2dc54 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -285,7 +285,9 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
queue.requests[lastRequestId_] = WithdrawalRequest(owner_, shares_);
lastRequestIds[owner_] = lastRequestId_;
- requestCount[owner_]++;
+
+ ++userRequestSummaries[owner_].requestCount;
+ userRequestSummaries[owner_].escrowedSharesTotal += shares_;
// Increase the number of shares locked.
totalShares += shares_;
@@ -310,6 +312,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_removeRequest(owner_, requestId_);
} else {
queue.requests[requestId_].shares = sharesRemaining_;
+ userRequestSummaries[owner_].escrowedSharesTotal -= sharesToRemove_;
emit RequestDecreased(requestId_, sharesToRemove_);
}
@@ -398,7 +401,8 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
} else {
// Update the withdrawal request.
queue.requests[requestId_].shares = sharesRemaining_;
-
+ userRequestSummaries[request_.owner].escrowedSharesTotal -= processedShares_;
+
emit RequestDecreased(requestId_, processedShares_);
}
@@ -416,9 +420,11 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function _removeRequest(address owner_, uint128 requestId_) internal {
+ --userRequestSummaries[owner_].requestCount;
+ userRequestSummaries[owner_].escrowedSharesTotal -= queue.requests[requestId_].shares;
+
delete lastRequestIds[owner_];
delete queue.requests[requestId_];
- requestCount[owner_]--;
emit RequestRemoved(requestId_);
}
@@ -514,7 +520,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
)
{
WithdrawalRequest memory request_;
- uint256 count_ = requestCount[owner_];
+ uint256 count_ = userRequestSummaries[owner_].requestCount;
uint256 index_;
uint256 requestsAdded_;
diff --git a/contracts/interfaces/IMapleWithdrawalManagerStorage.sol b/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
index eb94f51..a076584 100644
--- a/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
+++ b/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
@@ -44,10 +44,11 @@ interface IMapleWithdrawalManagerStorage {
/**
* @dev Returns the number of pending withdrawal requests for a specific user.
- * @param account Account to retrieve the request count for.
- * @return requestCount Amount of requests pending redemption for the user.
+ * @param account Account to retrieve the request count for.
+ * @return requestCount Amount of requests pending redemption for the user.
+ * @return escrowedSharesTotal Total amount of shares escrowed for the user.
*/
- function requestCount(address account) external view returns (uint256 requestCount);
+ function userRequestSummaries(address account) external view returns (uint256 requestCount, uint256 escrowedSharesTotal);
/**
* @dev Returns the first and last withdrawal requests pending redemption.
diff --git a/contracts/proxy/MapleWithdrawalManagerStorage.sol b/contracts/proxy/MapleWithdrawalManagerStorage.sol
index dea3368..1f5ef7a 100644
--- a/contracts/proxy/MapleWithdrawalManagerStorage.sol
+++ b/contracts/proxy/MapleWithdrawalManagerStorage.sol
@@ -20,12 +20,17 @@ contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
mapping(uint128 => WithdrawalRequest) requests; // Maps withdrawal requests to their positions in the queue.
}
+ struct UserRequestSummary {
+ uint256 requestCount;
+ uint256 escrowedSharesTotal; // Escrowed shares yet to be processed.
+ }
+
/**************************************************************************************************************************************/
/*** State Variables ***/
/**************************************************************************************************************************************/
uint256 internal _locked; // Used when checking for reentrancy.
-
+
address public override pool;
address public override poolManager;
@@ -39,5 +44,5 @@ contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
mapping(address => uint256) public override manualSharesAvailable; // Shares available to withdraw for a given manual owner.
- mapping(address => uint256) public override requestCount; // Maps users to the number of pending requests.
+ mapping(address => UserRequestSummary) public override userRequestSummaries; // Maps users to their request summaries.
}
diff --git a/tests/unit/AddShares.t.sol b/tests/unit/AddShares.t.sol
index f3d7dec..5554ce2 100644
--- a/tests/unit/AddShares.t.sol
+++ b/tests/unit/AddShares.t.sol
@@ -39,6 +39,11 @@ contract AddSharesTests is TestBase {
assertEq(lastRequestId_, 2);
+ ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 2);
+ assertEq(escrowedSharesTotal_, 2);
+
( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
assertEq(owner_, lp);
@@ -74,6 +79,11 @@ contract AddSharesTests is TestBase {
( , lastRequestId_ ) = withdrawalManager.queue();
assertEq(lastRequestId_, 1);
+
+ ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 1);
+ assertEq(escrowedSharesTotal_, 1);
}
function test_addShares_newRequestAddedToQueue_manual() external {
@@ -92,6 +102,11 @@ contract AddSharesTests is TestBase {
( , lastRequestId_ ) = withdrawalManager.queue();
assertEq(lastRequestId_, 1);
+
+ ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 1);
+ assertEq(escrowedSharesTotal_, 1);
}
function test_addShares_success() external {
@@ -107,6 +122,11 @@ contract AddSharesTests is TestBase {
assertEq(lastRequestId_, 1);
+ ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 1);
+ assertEq(escrowedSharesTotal_, 1);
+
( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
( requestId_,) = getLastRequestByOwner(owner_);
@@ -126,6 +146,11 @@ contract AddSharesTests is TestBase {
assertEq(lastRequestId_, 2);
+ ( requestCount_, escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 1);
+ assertEq(escrowedSharesTotal_, 1);
+
( owner_, shares_ ) = withdrawalManager.requests(lastRequestId_);
( requestId_,) = getLastRequestByOwner(owner_);
diff --git a/tests/unit/ProcessExit.t.sol b/tests/unit/ProcessExit.t.sol
index c1f6161..6d6da9c 100644
--- a/tests/unit/ProcessExit.t.sol
+++ b/tests/unit/ProcessExit.t.sol
@@ -45,7 +45,7 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesToRedeem);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesToRedeem);
vm.prank(pm);
vm.expectRevert("WM:PE:TOO_MANY_SHARES");
@@ -58,7 +58,7 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setTotalShares(sharesToRedeem);
withdrawalManager.__setManualSharesAvailable(lp, sharesToRedeem);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesToRedeem);
asset.burn(address(pool), assetsDeposited);
@@ -73,7 +73,7 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setTotalShares(sharesToRedeem);
withdrawalManager.__setManualSharesAvailable(lp, sharesToRedeem);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesToRedeem);
pool.burn(wm, 1);
diff --git a/tests/unit/ProcessRedemptions.t.sol b/tests/unit/ProcessRedemptions.t.sol
index a8a840d..cf80d7f 100644
--- a/tests/unit/ProcessRedemptions.t.sol
+++ b/tests/unit/ProcessRedemptions.t.sol
@@ -86,7 +86,12 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
+
+ ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 1);
+ assertEq(escrowedSharesTotal_, sharesLocked);
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
@@ -101,6 +106,11 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+
+ ( requestCount_, escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 0);
+ assertEq(escrowedSharesTotal_, 0);
}
function test_processRedemptions_manual_multiple_requests() external {
@@ -108,7 +118,12 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setRequest(1, lp, sharesLocked / 2);
withdrawalManager.__setRequest(2, lp, sharesLocked / 2);
withdrawalManager.__setQueue(1, 2);
- withdrawalManager.__setUserRequestCount(lp, 2);
+ withdrawalManager.__setUserRequestCount(lp, 2, sharesLocked);
+
+ ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 2);
+ assertEq(escrowedSharesTotal_, sharesLocked);
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
@@ -124,6 +139,11 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 2, owner: address(0), shares: 0 });
assertQueue({ nextRequestId: 3, lastRequestId: 2 });
+
+ ( requestCount_, escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCount_, 0);
+ assertEq(escrowedSharesTotal_, 0);
}
function test_processRedemptions_manual_multipleLps_multiple_requests() external {
@@ -139,9 +159,22 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setRequest(3, lp3, sharesLocked / 4);
withdrawalManager.__setRequest(4, lp, sharesLocked / 4);
withdrawalManager.__setQueue(1, 4);
- withdrawalManager.__setUserRequestCount(lp, 2);
- withdrawalManager.__setUserRequestCount(lp2, 1);
- withdrawalManager.__setUserRequestCount(lp3, 1);
+ withdrawalManager.__setUserRequestCount(lp, 2, sharesLocked / 2);
+ withdrawalManager.__setUserRequestCount(lp2, 1, sharesLocked / 4);
+ withdrawalManager.__setUserRequestCount(lp3, 1, sharesLocked / 4);
+
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+ ( uint256 requestCountLp2_, uint256 escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2);
+ ( uint256 requestCountLp3_, uint256 escrowedSharesTotalLp3_ ) = withdrawalManager.userRequestSummaries(lp3);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, sharesLocked / 2);
+
+ assertEq(requestCountLp2_, 1);
+ assertEq(escrowedSharesTotalLp2_, sharesLocked / 4);
+
+ assertEq(requestCountLp3_, 1);
+ assertEq(escrowedSharesTotalLp3_, sharesLocked / 4);
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
@@ -161,13 +194,31 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 4, owner: address(0), shares: 0 });
assertQueue({ nextRequestId: 5, lastRequestId: 4 });
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+ ( requestCountLp2_, escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2);
+ ( requestCountLp3_, escrowedSharesTotalLp3_ ) = withdrawalManager.userRequestSummaries(lp3);
+
+ assertEq(requestCountLp_, 0);
+ assertEq(escrowedSharesTotalLp_, 0);
+
+ assertEq(requestCountLp2_, 0);
+ assertEq(escrowedSharesTotalLp2_, 0);
+
+ assertEq(requestCountLp3_, 0);
+ assertEq(escrowedSharesTotalLp3_, 0);
}
function test_processRedemptions_manual_partial() external {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
+
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, sharesLocked);
// Only half of the liquidity is available.
asset.burn(address(pool), assetsDeposited / 2);
@@ -189,13 +240,23 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 1, owner: lp, shares: sharesLocked / 2 });
assertQueue({ nextRequestId: 1, lastRequestId: 1 });
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, sharesLocked / 2);
}
function test_processRedemptions_manual_overkill() external {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
+
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, sharesLocked);
// Add extra liquidity.
asset.mint(address(pool), assetsDeposited);
@@ -213,12 +274,22 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 0);
+ assertEq(escrowedSharesTotalLp_, 0);
}
function test_processRedemptions_automatic_complete() external {
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
+
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, sharesLocked);
vm.expectEmit();
emit RequestProcessed(1, lp, sharesLocked, assetsDeposited);
@@ -238,12 +309,22 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 0);
+ assertEq(escrowedSharesTotalLp_, 0);
}
function test_processRedemptions_automatic_partial() external {
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
+
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, sharesLocked);
// Only half of the liquidity is available.
asset.burn(address(pool), assetsDeposited / 2);
@@ -270,12 +351,22 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 1, owner: lp, shares: sharesLocked / 2 });
assertQueue({ nextRequestId: 1, lastRequestId: 1 });
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, sharesLocked / 2);
}
function test_processRedemptions_automatic_overkill() external {
withdrawalManager.__setRequest(1, lp, sharesLocked);
withdrawalManager.__setQueue(1, 1);
- withdrawalManager.__setUserRequestCount(lp, 1);
+ withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
+
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, sharesLocked);
// Add extra liquidity.
asset.mint(address(pool), assetsDeposited);
@@ -298,6 +389,11 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 1, owner: address(0), shares: 0 });
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 0);
+ assertEq(escrowedSharesTotalLp_, 0);
}
function test_processRedemptions_multiple() external {
@@ -307,8 +403,17 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setRequest(1, lp1, 100e18);
withdrawalManager.__setRequest(2, lp2, 150e18);
withdrawalManager.__setQueue(1, 2);
- withdrawalManager.__setUserRequestCount(lp1, 1);
- withdrawalManager.__setUserRequestCount(lp2, 1);
+ withdrawalManager.__setUserRequestCount(lp1, 1, 100e18);
+ withdrawalManager.__setUserRequestCount(lp2, 1, 150e18);
+
+ ( uint256 requestCountLp1_, uint256 escrowedSharesTotalLp1_ ) = withdrawalManager.userRequestSummaries(lp1);
+ ( uint256 requestCountLp2_, uint256 escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2);
+
+ assertEq(requestCountLp1_, 1);
+ assertEq(escrowedSharesTotalLp1_, 100e18);
+
+ assertEq(requestCountLp2_, 1);
+ assertEq(escrowedSharesTotalLp2_, 150e18);
vm.expectEmit();
emit RequestProcessed(1, lp1, 100e18, 40e18);
@@ -338,6 +443,15 @@ contract ProcessRedemptionsTests is TestBase {
assertRequest({ requestId: 2, owner: address(0), shares: 0 });
assertQueue({ nextRequestId: 3, lastRequestId: 2 });
+
+ ( requestCountLp1_, escrowedSharesTotalLp1_ ) = withdrawalManager.userRequestSummaries(lp1);
+ ( requestCountLp2_, escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2);
+
+ assertEq(requestCountLp1_, 0);
+ assertEq(escrowedSharesTotalLp1_, 0);
+
+ assertEq(requestCountLp2_, 0);
+ assertEq(escrowedSharesTotalLp2_, 0);
}
}
@@ -369,10 +483,10 @@ contract ComplexRedemptionTests is TestBase {
withdrawalManager.__setTotalShares(totalShares_);
withdrawalManager.__setQueue(2, 6);
- withdrawalManager.__setUserRequestCount(address(2), 1);
- withdrawalManager.__setUserRequestCount(address(3), 1);
- withdrawalManager.__setUserRequestCount(address(5), 1);
- withdrawalManager.__setUserRequestCount(address(6), 1);
+ withdrawalManager.__setUserRequestCount(address(2), 1, 100e18);
+ withdrawalManager.__setUserRequestCount(address(3), 1, 50e18);
+ withdrawalManager.__setUserRequestCount(address(5), 1, 75e18);
+ withdrawalManager.__setUserRequestCount(address(6), 1, 25e18);
vm.expectEmit();
emit RequestProcessed(2, address(2), 100e18, 40e18);
diff --git a/tests/unit/RemoveRequest.t.sol b/tests/unit/RemoveRequest.t.sol
index 89e20c0..3570d36 100644
--- a/tests/unit/RemoveRequest.t.sol
+++ b/tests/unit/RemoveRequest.t.sol
@@ -57,6 +57,11 @@ contract RemoveRequestTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(2, lp);
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 2);
+
( , uint128 lastRequestId_ ) = withdrawalManager.queue();
( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
@@ -90,6 +95,11 @@ contract RemoveRequestTests is TestBase {
assertEq(lastRequestIdLp_, 0);
assertEq(lastRequestByOwner_, 0);
assertEq(withdrawalManager.totalShares(), 0);
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 0);
+ assertEq(escrowedSharesTotalLp_, 0);
}
function test_removeRequest_multiple_requests_success() external {
@@ -111,6 +121,11 @@ contract RemoveRequestTests is TestBase {
assertEq(lastRequestId_, 2);
assertEq(lastRequestIdLp_, lastRequestId_);
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, 4);
+
uint128[] memory requestIds = new uint128[](2);
requestIds[0] = 1;
requestIds[1] = 2;
@@ -137,6 +152,11 @@ contract RemoveRequestTests is TestBase {
assertEq(lastRequestIdLp_, 0);
assertEq(lastRequestByOwner_, 0);
assertEq(withdrawalManager.totalShares(), 0);
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 0);
+ assertEq(escrowedSharesTotalLp_, 0);
}
}
diff --git a/tests/unit/RemoveShares.t.sol b/tests/unit/RemoveShares.t.sol
index 013cc8b..645cb60 100644
--- a/tests/unit/RemoveShares.t.sol
+++ b/tests/unit/RemoveShares.t.sol
@@ -65,12 +65,22 @@ contract RemoveSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(2, lp);
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 2);
+
vm.expectEmit();
emit RequestDecreased(1, 1);
vm.prank(pm);
withdrawalManager.removeShares(1, lp);
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 1);
+
( , lastRequestId_ ) = withdrawalManager.queue();
( address owner_ , uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
@@ -87,6 +97,12 @@ contract RemoveSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(2, lp);
+
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 2);
+
vm.expectEmit();
emit RequestRemoved(1);
@@ -103,6 +119,11 @@ contract RemoveSharesTests is TestBase {
assertEq(owner_, address(0));
assertEq(shares_, 0);
assertEq(withdrawalManager.totalShares(), 0);
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 0);
+ assertEq(escrowedSharesTotalLp_, 0);
}
}
diff --git a/tests/unit/UpdateShares.t.sol b/tests/unit/UpdateShares.t.sol
index 623e057..ac2ebc1 100644
--- a/tests/unit/UpdateShares.t.sol
+++ b/tests/unit/UpdateShares.t.sol
@@ -99,6 +99,11 @@ contract UpdateSharesSuccessTests is TestBase {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 1);
+
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
@@ -121,12 +126,22 @@ contract UpdateSharesSuccessTests is TestBase {
assertEq(shares_.length, 1, "Shares length should be 1");
assertEq(requestIds_[0], 2, "Request ID should be 2");
assertEq(shares_[0], 2, "Shares should be 2");
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 2);
}
function test_updateShares_remove_request() external{
vm.prank(pm);
uint128 requestId_ = withdrawalManager.addShares(1, lp);
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 1);
+
vm.prank(lp);
vm.expectEmit();
emit RequestRemoved(requestId_);
@@ -134,12 +149,22 @@ contract UpdateSharesSuccessTests is TestBase {
uint128 newRequestId_ = withdrawalManager.updateShares(requestId_, 0);
assertEq(newRequestId_, 0, "Request ID should be 0");
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 0);
+ assertEq(escrowedSharesTotalLp_, 0);
}
function test_updateShares_decrease() external {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 2);
+
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID Before should be 1 (addShares)");
@@ -161,6 +186,11 @@ contract UpdateSharesSuccessTests is TestBase {
assertEq(shares_.length, 1, "Shares length should be 1");
assertEq(requestIds_[0], 1, "Request ID should be 1");
assertEq(shares_[0], 1, "Shares should be 1");
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 1);
}
function test_update_shares_multiple_lps_requestsByOwner() external {
@@ -183,6 +213,7 @@ contract UpdateSharesSuccessTests is TestBase {
vm.expectEmit();
emit RequestCreated(1, lp, requestAmount1_);
withdrawalManager.addShares(requestAmount1_, lp);
+
vm.expectEmit();
emit RequestCreated(2, lp, requestAmount2_);
withdrawalManager.addShares(requestAmount2_, lp);
@@ -190,6 +221,7 @@ contract UpdateSharesSuccessTests is TestBase {
vm.expectEmit();
emit RequestCreated(3, lp2_, requestAmount1_);
withdrawalManager.addShares(requestAmount1_, lp2_);
+
vm.expectEmit();
emit RequestCreated(4, lp2_, requestAmount2_);
withdrawalManager.addShares(requestAmount2_, lp2_);
@@ -197,12 +229,29 @@ contract UpdateSharesSuccessTests is TestBase {
vm.expectEmit();
emit RequestCreated(5, lp3_, requestAmount1_);
withdrawalManager.addShares(requestAmount1_, lp3_);
+
vm.expectEmit();
emit RequestCreated(6, lp3_, requestAmount2_);
withdrawalManager.addShares(requestAmount2_, lp3_);
vm.stopPrank();
+
+ {
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+ ( uint256 requestCountLp2_, uint256 escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2_);
+ ( uint256 requestCountLp3_, uint256 escrowedSharesTotalLp3_ ) = withdrawalManager.userRequestSummaries(lp3_);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, requestAmount1_ + requestAmount2_);
+
+ assertEq(requestCountLp2_, 2);
+ assertEq(escrowedSharesTotalLp2_, requestAmount1_ + requestAmount2_);
+
+ assertEq(requestCountLp3_, 2);
+ assertEq(escrowedSharesTotalLp3_, requestAmount1_ + requestAmount2_);
+ }
+
{
(uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
(uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
@@ -284,6 +333,21 @@ contract UpdateSharesSuccessTests is TestBase {
assertEq(sharesLp3_[0], requestAmount2_, "LP3 shares index 0 is incorrect");
assertEq(sharesLp3_[1], requestAmount2_ + 10, "LP3 shares index 1 is incorrect");
}
+
+ {
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+ ( uint256 requestCountLp2_, uint256 escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2_);
+ ( uint256 requestCountLp3_, uint256 escrowedSharesTotalLp3_ ) = withdrawalManager.userRequestSummaries(lp3_);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, requestAmount1_ - 1 + requestAmount2_);
+
+ assertEq(requestCountLp2_, 1);
+ assertEq(escrowedSharesTotalLp2_, requestAmount2_);
+
+ assertEq(requestCountLp3_, 2);
+ assertEq(escrowedSharesTotalLp3_, requestAmount2_ + 10 + requestAmount2_);
+ }
}
}
@@ -378,6 +442,11 @@ contract UpdateSharesBatchSuccessTests is TestBase {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 1);
+
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
@@ -409,6 +478,11 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(shares_.length, 1, "Shares length should be 1");
assertEq(requestIds_[0], 2, "Request ID should be 2");
assertEq(shares_[0], 2, "Shares should be 2");
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 2);
}
function test_updateSharesBatch_increase_multiple_requests() external {
@@ -417,10 +491,15 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(1, lp);
vm.stopPrank();
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, 2);
+
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
- assertEq(requestId2Before_, 2, "Request ID should be 1");
+ assertEq(requestId2Before_, 2, "Request ID should be 2");
assertEq(requestIdsBefore_.length, 2, "Request IDs length should be 2");
assertEq(sharesBefore_.length, 2, "Shares length should be 2");
assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
@@ -454,18 +533,28 @@ contract UpdateSharesBatchSuccessTests is TestBase {
(uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
- assertEq(requestIds_.length, 2, "Request IDs length should be 1");
- assertEq(shares_.length, 2, "Shares length should be 1");
- assertEq(requestIds_[0], 3, "Request ID should be 2");
+ assertEq(requestIds_.length, 2, "Request IDs length should be 2");
+ assertEq(shares_.length, 2, "Shares length should be 2");
+ assertEq(requestIds_[0], 3, "Request ID should be 3");
assertEq(shares_[0], 2, "Shares should be 2");
- assertEq(requestIds_[1], 4, "Request ID should be 3");
+ assertEq(requestIds_[1], 4, "Request ID should be 4");
assertEq(shares_[1], 3, "Shares should be 3");
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, 5);
}
function test_updateSharesBatch_decrease_single_request() external {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 2);
+
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
@@ -496,6 +585,11 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(shares_.length, 1, "Shares length should be 1");
assertEq(requestIds_[0], 1, "Request ID should be 1");
assertEq(shares_[0], 1, "Shares should be 1");
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 1);
+ assertEq(escrowedSharesTotalLp_, 1);
}
function test_updateSharesBatch_decrease_multiple_requests() external {
@@ -504,6 +598,11 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
vm.stopPrank();
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, 4);
+
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
@@ -539,12 +638,17 @@ contract UpdateSharesBatchSuccessTests is TestBase {
(uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
- assertEq(requestIds_.length, 2, "Request IDs length should be 1");
- assertEq(shares_.length, 2, "Shares length should be 1");
+ assertEq(requestIds_.length, 2, "Request IDs length should be 2");
+ assertEq(shares_.length, 2, "Shares length should be 2");
assertEq(requestIds_[0], 1, "Request ID should be 1");
assertEq(shares_[0], 1, "Shares should be 1");
assertEq(requestIds_[1], 2, "Request ID should be 2");
assertEq(shares_[1], 1, "Shares should be 1");
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, 2);
}
function test_updateSharesBatch_increase_and_decrease_multiple_requests() external {
@@ -553,6 +657,11 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
vm.stopPrank();
+ ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, 3);
+
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
@@ -595,6 +704,11 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(shares_[0], 1, "Shares should be 1");
assertEq(requestIds_[1], 3, "Request ID should be 3");
assertEq(shares_[1], 2, "Shares should be 2");
+
+ ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+
+ assertEq(requestCountLp_, 2);
+ assertEq(escrowedSharesTotalLp_, 3);
}
}
diff --git a/tests/unit/ViewFunctions.t.sol b/tests/unit/ViewFunctions.t.sol
index 57c88dc..1bf41d5 100644
--- a/tests/unit/ViewFunctions.t.sol
+++ b/tests/unit/ViewFunctions.t.sol
@@ -38,10 +38,10 @@ contract ViewFunctionsTests is TestBase {
withdrawalManager.__setRequest(4, lp4, assetsDeposited_);
withdrawalManager.__setRequest(5, lp1, assetsDeposited_);
- withdrawalManager.__setUserRequestCount(lp1, 2);
- withdrawalManager.__setUserRequestCount(lp2, 1);
- withdrawalManager.__setUserRequestCount(lp3, 1);
- withdrawalManager.__setUserRequestCount(lp4, 1);
+ withdrawalManager.__setUserRequestCount(lp1, 2, assetsDeposited_ * 2);
+ withdrawalManager.__setUserRequestCount(lp2, 1, assetsDeposited_);
+ withdrawalManager.__setUserRequestCount(lp3, 1, assetsDeposited_);
+ withdrawalManager.__setUserRequestCount(lp4, 1, assetsDeposited_);
withdrawalManager.__setQueue(1, 5);
@@ -92,10 +92,10 @@ contract ViewFunctionsTests is TestBase {
withdrawalManager.__setRequest(4, lp4, assetsDeposited_);
withdrawalManager.__setRequest(5, lp1, assetsDeposited_);
- withdrawalManager.__setUserRequestCount(lp1, 2);
- withdrawalManager.__setUserRequestCount(lp2, 1);
- withdrawalManager.__setUserRequestCount(lp3, 1);
- withdrawalManager.__setUserRequestCount(lp4, 1);
+ withdrawalManager.__setUserRequestCount(lp1, 2, assetsDeposited_ * 2);
+ withdrawalManager.__setUserRequestCount(lp2, 1, assetsDeposited_);
+ withdrawalManager.__setUserRequestCount(lp3, 1, assetsDeposited_);
+ withdrawalManager.__setUserRequestCount(lp4, 1, assetsDeposited_);
withdrawalManager.__setQueue(1, 5);
@@ -145,10 +145,10 @@ contract ViewFunctionsTests is TestBase {
withdrawalManager.__setRequest(4, lp4, assetsDeposited_);
withdrawalManager.__setRequest(5, lp, assetsDeposited_);
- withdrawalManager.__setUserRequestCount(lp, 2);
- withdrawalManager.__setUserRequestCount(lp2, 1);
- withdrawalManager.__setUserRequestCount(lp3, 1);
- withdrawalManager.__setUserRequestCount(lp4, 1);
+ withdrawalManager.__setUserRequestCount(lp, 2, assetsDeposited_ * 2);
+ withdrawalManager.__setUserRequestCount(lp2, 1, assetsDeposited_);
+ withdrawalManager.__setUserRequestCount(lp3, 1, assetsDeposited_);
+ withdrawalManager.__setUserRequestCount(lp4, 1, assetsDeposited_);
withdrawalManager.__setQueue(1, 5);
diff --git a/tests/utils/Harnesses.sol b/tests/utils/Harnesses.sol
index 6f4cdbf..33af81a 100644
--- a/tests/utils/Harnesses.sol
+++ b/tests/utils/Harnesses.sol
@@ -35,8 +35,9 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
totalShares = totalShares_;
}
- function __setUserRequestCount(address owner_, uint256 requestCount_) external {
- requestCount[owner_] = requestCount_;
+ function __setUserRequestCount(address owner_, uint256 requestCount_, uint256 escrowSharesTotal_) external {
+ userRequestSummaries[owner_].requestCount = requestCount_;
+ userRequestSummaries[owner_].escrowedSharesTotal = escrowSharesTotal_;
}
}
From 0b48f84c549c37a0298a10e16041cecbd2804807 Mon Sep 17 00:00:00 2001
From: Danilo Kitanovic <116795362+kitanovicd@users.noreply.github.com>
Date: Tue, 29 Jul 2025 14:13:04 +0200
Subject: [PATCH 05/21] fix: keep track of withdraw requests per user
(SC-21032) (#34)
* fix: keep track of withdraw requests per user
* feat: keep only active redeem requests in userRequests array
* fix: update failing tests and fix indexes
* fix: bring back console import
* chore: import style
* chore: library format
* feat: upgrade removeRequest test for more complex scenario
* fix: remove userRequestSummaries and bring back userRequests function
* chore: rename mapping
* fix: remove isPresent mapping
* fix: renamings
* feat: unit tests for library
* fix: remove empty lines at the end of the files
* Revert "fix: remove empty lines at the end of the files"
This reverts commit 312e3207f9b6a17a8944d9540dc2273156bd9968.
* chore: format
* chore: formatting
* fix: add extra require statements
* feat: add invalid range test
* fix: rename removeByValue to remove
* fix: remove padination request getter
* chore: Fix formatting, return value and test ordering
* chore: Add TODO
---------
Co-authored-by: 0xfarhaan <59924029+0xfarhaan@users.noreply.github.com>
---
contracts/MapleWithdrawalManager.sol | 74 ++++-------
.../interfaces/IMapleWithdrawalManager.sol | 23 ++--
.../IMapleWithdrawalManagerStorage.sol | 17 +--
.../proxy/MapleWithdrawalManagerStorage.sol | 14 +-
contracts/utils/SortedArray.sol | 114 +++++++++++++++++
tests/unit/AddShares.t.sol | 25 +---
tests/unit/ProcessExit.t.sol | 15 ++-
tests/unit/ProcessRedemptions.t.sol | 120 ++++--------------
tests/unit/RemoveRequest.t.sol | 68 ++++++----
tests/unit/RemoveShares.t.sol | 20 +--
tests/unit/SortedArray/Get.t.sol | 29 +++++
tests/unit/SortedArray/Push.t.sol | 46 +++++++
tests/unit/SortedArray/Remove.t.sol | 51 ++++++++
tests/unit/UpdateShares.t.sol | 114 ++++-------------
tests/unit/ViewFunctions.t.sol | 54 --------
tests/utils/Harnesses.sol | 54 +++++++-
tests/utils/TestBase.sol | 27 ++++
17 files changed, 465 insertions(+), 400 deletions(-)
create mode 100644 contracts/utils/SortedArray.sol
create mode 100644 tests/unit/SortedArray/Get.t.sol
create mode 100644 tests/unit/SortedArray/Push.t.sol
create mode 100644 tests/unit/SortedArray/Remove.t.sol
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 8d2dc54..c1f0533 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -16,6 +16,8 @@ import {
import { MapleWithdrawalManagerStorage } from "./proxy/MapleWithdrawalManagerStorage.sol";
+import { SortedArray } from "./utils/SortedArray.sol";
+
/*
███╗ ███╗ █████╗ ██████╗ ██╗ ███████╗
@@ -152,9 +154,9 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function removeShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint256 sharesReturned_) {
- require(shares_ > 0, "WM:RS:ZERO_SHARES");
+ require(shares_ > 0, "WM:RS:ZERO_SHARES");
- uint128 lastRequestId_ = lastRequestIds[owner_];
+ uint128 lastRequestId_ = SortedArray.getLast(userRequests[owner_]);
require(lastRequestId_ > 0, "WM:RS:NO_REQUESTS");
@@ -284,10 +286,9 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
lastRequestId_ = ++queue.lastRequestId;
queue.requests[lastRequestId_] = WithdrawalRequest(owner_, shares_);
- lastRequestIds[owner_] = lastRequestId_;
-
- ++userRequestSummaries[owner_].requestCount;
- userRequestSummaries[owner_].escrowedSharesTotal += shares_;
+ userEscrowedShares[owner_] += shares_;
+
+ SortedArray.push(userRequests[owner_], lastRequestId_);
// Increase the number of shares locked.
totalShares += shares_;
@@ -312,7 +313,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_removeRequest(owner_, requestId_);
} else {
queue.requests[requestId_].shares = sharesRemaining_;
- userRequestSummaries[owner_].escrowedSharesTotal -= sharesToRemove_;
+ userEscrowedShares[owner_] -= sharesToRemove_;
emit RequestDecreased(requestId_, sharesToRemove_);
}
@@ -400,9 +401,9 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_removeRequest(request_.owner, requestId_);
} else {
// Update the withdrawal request.
- queue.requests[requestId_].shares = sharesRemaining_;
- userRequestSummaries[request_.owner].escrowedSharesTotal -= processedShares_;
-
+ queue.requests[requestId_].shares = sharesRemaining_;
+ userEscrowedShares[request_.owner] -= processedShares_;
+
emit RequestDecreased(requestId_, processedShares_);
}
@@ -420,10 +421,8 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function _removeRequest(address owner_, uint128 requestId_) internal {
- --userRequestSummaries[owner_].requestCount;
- userRequestSummaries[owner_].escrowedSharesTotal -= queue.requests[requestId_].shares;
-
- delete lastRequestIds[owner_];
+ userEscrowedShares[owner_] -= queue.requests[requestId_].shares;
+ SortedArray.remove(userRequests[owner_], requestId_);
delete queue.requests[requestId_];
emit RequestRemoved(requestId_);
@@ -499,50 +498,21 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
return ( redeemableAssets_, resultingShares_ ); // NOTE: Withdrawal not implemented use redeem instead
}
+ function requestIds(address owner_) external view override returns (uint128 requestId_) {
+ requestId_ = SortedArray.getLast(userRequests[owner_]);
+ }
+
function requests(uint128 requestId_) external view override returns (address owner_, uint256 shares_) {
owner_ = queue.requests[requestId_].owner;
shares_ = queue.requests[requestId_].shares;
}
- function requestsByOwner(address owner_)
- external view override returns (
- uint128[] memory requestIds_,
- uint256[] memory shares_
- )
- {
- ( requestIds_, shares_ ) = requestsByOwnerRange(owner_, queue.nextRequestId, queue.lastRequestId);
- }
-
- function requestsByOwnerRange(address owner_, uint128 firstRequestId_, uint128 lastRequestId_)
- public view override returns (
- uint128[] memory requestIds_,
- uint256[] memory shares_
- )
- {
- WithdrawalRequest memory request_;
- uint256 count_ = userRequestSummaries[owner_].requestCount;
- uint256 index_;
- uint256 requestsAdded_;
-
- requestIds_ = new uint128[](count_);
- shares_ = new uint256[](count_);
+ function requestsByOwner(address owner_) external view override returns (uint128[] memory requestIds_, uint256[] memory shares_) {
+ requestIds_ = SortedArray.getAllValues(userRequests[owner_]);
+ shares_ = new uint256[](requestIds_.length);
- while (firstRequestId_ <= lastRequestId_) {
-
- if(requestsAdded_ == count_) {
- break;
- }
-
- request_ = queue.requests[firstRequestId_];
-
- if (request_.owner == owner_) {
- requestIds_[index_] = firstRequestId_;
- shares_[index_] = request_.shares;
- ++index_;
- ++requestsAdded_;
- }
-
- ++firstRequestId_;
+ for (uint256 i = 0; i < requestIds_.length; ++i) {
+ shares_[i] = queue.requests[requestIds_[i]].shares;
}
}
diff --git a/contracts/interfaces/IMapleWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol
index d1ebecf..9d01d72 100644
--- a/contracts/interfaces/IMapleWithdrawalManager.sol
+++ b/contracts/interfaces/IMapleWithdrawalManager.sol
@@ -208,6 +208,14 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
*/
function previewWithdraw(address owner, uint256 assets) external view returns (uint256 redeemableAssets, uint256 resultingShares);
+ /**
+ * @dev Returns the last request id for a given owner.
+ * Function must exist for backwards compatibility with the old implementation where we supported only one request per owner.
+ * @param owner The account to check the last request id for.
+ * @return requestId The id of the last valid withdrawal request for the account.
+ */
+ function requestIds(address owner) external view returns (uint128 requestId);
+
/**
* @dev Returns the owner and amount of shares associated with a withdrawal request.
* @param requestId Identifier of the withdrawal request.
@@ -225,21 +233,6 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
*/
function requestsByOwner(address owner) external view returns (uint128[] memory requestIds, uint256[] memory shares);
- /**
- * @dev Returns the pending requests by owner and request ID range.
- * Only returns requests that belong to the specified owner.
- * @param owner Address of the account to check for pending requests.
- * @param firstRequestId First request ID to include in the result.
- * @param lastRequestId Last request ID to include in the result.
- * @return requestIds Array of request identifiers.
- * @return shares Array of shares associated with each request.
- */
- function requestsByOwnerRange(address owner, uint128 firstRequestId, uint128 lastRequestId)
- external view returns (
- uint128[] memory requestIds,
- uint256[] memory shares
- );
-
/**
* @dev Returns the address of the security admin.
* @param securityAdmin Address of the security admin.
diff --git a/contracts/interfaces/IMapleWithdrawalManagerStorage.sol b/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
index a076584..7288bc7 100644
--- a/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
+++ b/contracts/interfaces/IMapleWithdrawalManagerStorage.sol
@@ -3,13 +3,6 @@ pragma solidity ^0.8.7;
interface IMapleWithdrawalManagerStorage {
- /**
- * @dev Returns the identifier of the last withdrawal request made by a specific user.
- * @param account Address of the user.
- * @return requestId Identifier of the last withdrawal request made by the user.
- */
- function lastRequestIds(address account) external view returns (uint128 requestId);
-
/**
* @dev Returns the address of the pool contract.
* @return pool Address of the pool contract.
@@ -43,12 +36,11 @@ interface IMapleWithdrawalManagerStorage {
function manualSharesAvailable(address owner) external view returns (uint256 sharesAvailable);
/**
- * @dev Returns the number of pending withdrawal requests for a specific user.
- * @param account Account to retrieve the request count for.
- * @return requestCount Amount of requests pending redemption for the user.
- * @return escrowedSharesTotal Total amount of shares escrowed for the user.
+ * @dev Returns the amount of shares escrowed for a specific user yet to be processed.
+ * @param owner The address of the owner of shares.
+ * @return escrowedShares Amount of shares escrowed for the user.
*/
- function userRequestSummaries(address account) external view returns (uint256 requestCount, uint256 escrowedSharesTotal);
+ function userEscrowedShares(address owner) external view returns (uint256 escrowedShares);
/**
* @dev Returns the first and last withdrawal requests pending redemption.
@@ -56,4 +48,5 @@ interface IMapleWithdrawalManagerStorage {
* @return lastRequestId Identifier of the last created withdrawal request.
*/
function queue() external view returns (uint128 nextRequestId, uint128 lastRequestId);
+
}
diff --git a/contracts/proxy/MapleWithdrawalManagerStorage.sol b/contracts/proxy/MapleWithdrawalManagerStorage.sol
index 1f5ef7a..42510ad 100644
--- a/contracts/proxy/MapleWithdrawalManagerStorage.sol
+++ b/contracts/proxy/MapleWithdrawalManagerStorage.sol
@@ -3,6 +3,8 @@ pragma solidity ^0.8.7;
import { IMapleWithdrawalManagerStorage } from "../interfaces/IMapleWithdrawalManagerStorage.sol";
+import { SortedArray } from "../utils/SortedArray.sol";
+
contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
/**************************************************************************************************************************************/
@@ -20,11 +22,6 @@ contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
mapping(uint128 => WithdrawalRequest) requests; // Maps withdrawal requests to their positions in the queue.
}
- struct UserRequestSummary {
- uint256 requestCount;
- uint256 escrowedSharesTotal; // Escrowed shares yet to be processed.
- }
-
/**************************************************************************************************************************************/
/*** State Variables ***/
/**************************************************************************************************************************************/
@@ -40,9 +37,12 @@ contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
mapping(address => bool) public override isManualWithdrawal; // Defines which users use automated withdrawals (false by default).
- mapping(address => uint128) public override lastRequestIds; // Maps users to their last withdrawal request.
+ mapping(address => uint128) internal __deprecated_requestIds; // Maps users to their last withdrawal request.
mapping(address => uint256) public override manualSharesAvailable; // Shares available to withdraw for a given manual owner.
- mapping(address => UserRequestSummary) public override userRequestSummaries; // Maps users to their request summaries.
+ mapping(address => uint256) public override userEscrowedShares; // Maps users to their escrowed shares yet to be processed.
+
+ mapping(address => SortedArray.Array) internal userRequests; // Maps users to their withdrawal requests.
+
}
diff --git a/contracts/utils/SortedArray.sol b/contracts/utils/SortedArray.sol
new file mode 100644
index 0000000..d711c75
--- /dev/null
+++ b/contracts/utils/SortedArray.sol
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+// TODO: How to best handle library interfaces?
+library SortedArray {
+
+ struct Array {
+ uint128[] values;
+
+ mapping(uint128 => uint256) valueToIndex;
+ }
+
+ /**************************************************************************************************************************************/
+ /*** Write Functions ***/
+ /**************************************************************************************************************************************/
+
+ /**
+ * @dev Pushes a value to the array.
+ * It is expected that the value is biggest so far so it will be added at the end of the array.
+ * @param array_ The array to push the value to.
+ * @param value_ The value to push to the array.
+ */
+ function push(Array storage array_, uint128 value_) internal {
+ require(getLast(array_) < value_, "SA:P:NOT_LARGEST");
+
+ array_.values.push(value_);
+ array_.valueToIndex[value_] = length(array_) - 1;
+ }
+
+ /**
+ * @dev Removes a value from the array.
+ * It shifts the rest of the array to the left.
+ * It is expected by contract that uses this library to check that the value is present in the array before call of the function.
+ * @param array_ The array to remove the value from.
+ * @param value_ The value to remove from the array.
+ */
+ function remove(Array storage array_, uint128 value_) internal {
+ uint256 length_ = length(array_);
+
+ if (length_ == 1) {
+ _deleteValue(array_, value_);
+ return;
+ }
+
+ uint256 index_ = array_.valueToIndex[value_];
+
+ for (uint256 i = index_; i <= length_ - 2; i++) {
+ uint128 nextValue_ = array_.values[i + 1];
+ array_.values[i] = nextValue_;
+ array_.valueToIndex[nextValue_] = i;
+ }
+
+ _deleteValue(array_, value_);
+ }
+
+ /**************************************************************************************************************************************/
+ /*** View Functions ***/
+ /**************************************************************************************************************************************/
+
+ /**
+ * @dev Gets a value from the array at given index.
+ * @param array_ The array to get the value from.
+ * @param index_ The index of the value to get from the array.
+ * @return value_ The value at the given index.
+ */
+ function get(Array storage array_, uint256 index_) internal view returns (uint128 value_) {
+ require(index_ < length(array_), "SA:G:OUT_OF_BOUNDS");
+ value_ = array_.values[index_];
+ }
+
+ /**
+ * @dev Gets the length of the array.
+ * @param array_ The array to get the length of.
+ * @return length_ The length of the array.
+ */
+ function length(Array storage array_) internal view returns (uint256 length_) {
+ length_ = array_.values.length;
+ }
+
+ /**
+ * @dev Gets all values from the array.
+ * @param array_ The array to get the values from.
+ * @return values_ All values from the array.
+ */
+ function getAllValues(Array storage array_) internal view returns (uint128[] memory values_) {
+ values_ = array_.values;
+ }
+
+ /**
+ * @dev Gets the last value in the array.
+ * @param array_ The array to get the last value from.
+ * @return value_ The last value in the array.
+ */
+ function getLast(Array storage array_) internal view returns (uint128 value_) {
+ uint256 length_ = length(array_);
+ return length_ > 0 ? array_.values[length_ - 1] : 0;
+ }
+
+ /**************************************************************************************************************************************/
+ /*** Private Functions ***/
+ /**************************************************************************************************************************************/
+
+ /**
+ * @dev Deletes a value from the array.
+ * It is expected that array is already shifted so this will just pop the last and flag given value as not present.
+ * @param array_ The array to delete the value from.
+ * @param value_ The value to delete from the array.
+ */
+ function _deleteValue(Array storage array_, uint128 value_) private {
+ array_.values.pop();
+ delete array_.valueToIndex[value_];
+ }
+
+}
diff --git a/tests/unit/AddShares.t.sol b/tests/unit/AddShares.t.sol
index 5554ce2..8d148d8 100644
--- a/tests/unit/AddShares.t.sol
+++ b/tests/unit/AddShares.t.sol
@@ -39,10 +39,7 @@ contract AddSharesTests is TestBase {
assertEq(lastRequestId_, 2);
- ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 2);
- assertEq(escrowedSharesTotal_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
@@ -80,10 +77,7 @@ contract AddSharesTests is TestBase {
assertEq(lastRequestId_, 1);
- ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 1);
- assertEq(escrowedSharesTotal_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
}
function test_addShares_newRequestAddedToQueue_manual() external {
@@ -103,10 +97,7 @@ contract AddSharesTests is TestBase {
assertEq(lastRequestId_, 1);
- ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 1);
- assertEq(escrowedSharesTotal_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
}
function test_addShares_success() external {
@@ -122,10 +113,7 @@ contract AddSharesTests is TestBase {
assertEq(lastRequestId_, 1);
- ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 1);
- assertEq(escrowedSharesTotal_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
( requestId_,) = getLastRequestByOwner(owner_);
@@ -146,10 +134,7 @@ contract AddSharesTests is TestBase {
assertEq(lastRequestId_, 2);
- ( requestCount_, escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 1);
- assertEq(escrowedSharesTotal_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
( owner_, shares_ ) = withdrawalManager.requests(lastRequestId_);
diff --git a/tests/unit/ProcessExit.t.sol b/tests/unit/ProcessExit.t.sol
index 6d6da9c..611944e 100644
--- a/tests/unit/ProcessExit.t.sol
+++ b/tests/unit/ProcessExit.t.sol
@@ -86,7 +86,7 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setRequest(1, lp, sharesToRedeem);
withdrawalManager.__setTotalShares(sharesToRedeem);
- withdrawalManager.__setLastRequest(lp, 0);
+ withdrawalManager.__setLastRequest(lp, 2);
withdrawalManager.__setManualSharesAvailable(lp, sharesToRedeem);
assertEq(pool.balanceOf(lp), 0);
@@ -98,8 +98,12 @@ contract ProcessExitTests is TestBase {
assertEq(pool.balanceOf(lp), sharesToRedeem);
assertEq(pool.balanceOf(wm), 0);
+ ( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
+ assertEq(lastRequestId_, 2);
+
+ assertEq(withdrawalManager.requestIds(lp), lastRequestId_);
+
assertEq(withdrawalManager.totalShares(), 0);
- assertEq(withdrawalManager.lastRequestIds(lp), 0);
assertEq(withdrawalManager.manualSharesAvailable(lp), 0);
assertRequest({ requestId: 1, owner: lp, shares: sharesToRedeem });
@@ -123,9 +127,12 @@ contract ProcessExitTests is TestBase {
assertEq(pool.balanceOf(lp), sharesToRedeem / 2);
assertEq(pool.balanceOf(wm), sharesToRedeem / 2);
- assertEq(withdrawalManager.totalShares(), sharesToRedeem / 2);
+ ( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
+ assertEq(lastRequestId_, 1);
- assertEq(withdrawalManager.lastRequestIds(lp), 1);
+ assertEq(withdrawalManager.requestIds(lp), lastRequestId_);
+
+ assertEq(withdrawalManager.totalShares(), sharesToRedeem / 2);
assertRequest({ requestId: 1, owner: lp, shares: sharesToRedeem / 2 });
}
diff --git a/tests/unit/ProcessRedemptions.t.sol b/tests/unit/ProcessRedemptions.t.sol
index cf80d7f..3db5e2c 100644
--- a/tests/unit/ProcessRedemptions.t.sol
+++ b/tests/unit/ProcessRedemptions.t.sol
@@ -88,10 +88,7 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setQueue(1, 1);
withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
- ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 1);
- assertEq(escrowedSharesTotal_, sharesLocked);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked);
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
@@ -107,10 +104,7 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
- ( requestCount_, escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 0);
- assertEq(escrowedSharesTotal_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
function test_processRedemptions_manual_multiple_requests() external {
@@ -120,10 +114,7 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setQueue(1, 2);
withdrawalManager.__setUserRequestCount(lp, 2, sharesLocked);
- ( uint256 requestCount_, uint256 escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 2);
- assertEq(escrowedSharesTotal_, sharesLocked);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked);
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
@@ -140,10 +131,7 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 3, lastRequestId: 2 });
- ( requestCount_, escrowedSharesTotal_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCount_, 0);
- assertEq(escrowedSharesTotal_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
function test_processRedemptions_manual_multipleLps_multiple_requests() external {
@@ -163,18 +151,9 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setUserRequestCount(lp2, 1, sharesLocked / 4);
withdrawalManager.__setUserRequestCount(lp3, 1, sharesLocked / 4);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
- ( uint256 requestCountLp2_, uint256 escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2);
- ( uint256 requestCountLp3_, uint256 escrowedSharesTotalLp3_ ) = withdrawalManager.userRequestSummaries(lp3);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, sharesLocked / 2);
-
- assertEq(requestCountLp2_, 1);
- assertEq(escrowedSharesTotalLp2_, sharesLocked / 4);
-
- assertEq(requestCountLp3_, 1);
- assertEq(escrowedSharesTotalLp3_, sharesLocked / 4);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked / 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp2), sharesLocked / 4);
+ assertEq(withdrawalManager.userEscrowedShares(lp3), sharesLocked / 4);
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
@@ -195,18 +174,9 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 5, lastRequestId: 4 });
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
- ( requestCountLp2_, escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2);
- ( requestCountLp3_, escrowedSharesTotalLp3_ ) = withdrawalManager.userRequestSummaries(lp3);
-
- assertEq(requestCountLp_, 0);
- assertEq(escrowedSharesTotalLp_, 0);
-
- assertEq(requestCountLp2_, 0);
- assertEq(escrowedSharesTotalLp2_, 0);
-
- assertEq(requestCountLp3_, 0);
- assertEq(escrowedSharesTotalLp3_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp2), 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp3), 0);
}
function test_processRedemptions_manual_partial() external {
@@ -215,10 +185,7 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setQueue(1, 1);
withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, sharesLocked);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked);
// Only half of the liquidity is available.
asset.burn(address(pool), assetsDeposited / 2);
@@ -241,10 +208,7 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 1, lastRequestId: 1 });
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, sharesLocked / 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked / 2);
}
function test_processRedemptions_manual_overkill() external {
@@ -253,10 +217,7 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setQueue(1, 1);
withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, sharesLocked);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked);
// Add extra liquidity.
asset.mint(address(pool), assetsDeposited);
@@ -275,10 +236,7 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 0);
- assertEq(escrowedSharesTotalLp_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
function test_processRedemptions_automatic_complete() external {
@@ -286,10 +244,7 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setQueue(1, 1);
withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, sharesLocked);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked);
vm.expectEmit();
emit RequestProcessed(1, lp, sharesLocked, assetsDeposited);
@@ -310,10 +265,7 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 0);
- assertEq(escrowedSharesTotalLp_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
function test_processRedemptions_automatic_partial() external {
@@ -321,10 +273,7 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setQueue(1, 1);
withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, sharesLocked);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked);
// Only half of the liquidity is available.
asset.burn(address(pool), assetsDeposited / 2);
@@ -352,10 +301,7 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 1, lastRequestId: 1 });
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, sharesLocked / 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked / 2);
}
function test_processRedemptions_automatic_overkill() external {
@@ -363,10 +309,7 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setQueue(1, 1);
withdrawalManager.__setUserRequestCount(lp, 1, sharesLocked);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, sharesLocked);
+ assertEq(withdrawalManager.userEscrowedShares(lp), sharesLocked);
// Add extra liquidity.
asset.mint(address(pool), assetsDeposited);
@@ -390,10 +333,7 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 2, lastRequestId: 1 });
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 0);
- assertEq(escrowedSharesTotalLp_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
function test_processRedemptions_multiple() external {
@@ -406,14 +346,8 @@ contract ProcessRedemptionsTests is TestBase {
withdrawalManager.__setUserRequestCount(lp1, 1, 100e18);
withdrawalManager.__setUserRequestCount(lp2, 1, 150e18);
- ( uint256 requestCountLp1_, uint256 escrowedSharesTotalLp1_ ) = withdrawalManager.userRequestSummaries(lp1);
- ( uint256 requestCountLp2_, uint256 escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2);
-
- assertEq(requestCountLp1_, 1);
- assertEq(escrowedSharesTotalLp1_, 100e18);
-
- assertEq(requestCountLp2_, 1);
- assertEq(escrowedSharesTotalLp2_, 150e18);
+ assertEq(withdrawalManager.userEscrowedShares(lp1), 100e18);
+ assertEq(withdrawalManager.userEscrowedShares(lp2), 150e18);
vm.expectEmit();
emit RequestProcessed(1, lp1, 100e18, 40e18);
@@ -444,14 +378,8 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 3, lastRequestId: 2 });
- ( requestCountLp1_, escrowedSharesTotalLp1_ ) = withdrawalManager.userRequestSummaries(lp1);
- ( requestCountLp2_, escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2);
-
- assertEq(requestCountLp1_, 0);
- assertEq(escrowedSharesTotalLp1_, 0);
-
- assertEq(requestCountLp2_, 0);
- assertEq(escrowedSharesTotalLp2_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp1), 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp2), 0);
}
}
diff --git a/tests/unit/RemoveRequest.t.sol b/tests/unit/RemoveRequest.t.sol
index 3570d36..9b7f371 100644
--- a/tests/unit/RemoveRequest.t.sol
+++ b/tests/unit/RemoveRequest.t.sol
@@ -57,10 +57,7 @@ contract RemoveRequestTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(2, lp);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
( , uint128 lastRequestId_ ) = withdrawalManager.queue();
@@ -96,18 +93,16 @@ contract RemoveRequestTests is TestBase {
assertEq(lastRequestByOwner_, 0);
assertEq(withdrawalManager.totalShares(), 0);
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 0);
- assertEq(escrowedSharesTotalLp_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
function test_removeRequest_multiple_requests_success() external {
uint128 lastRequestIdLp_;
vm.startPrank(pm);
- withdrawalManager.addShares(2, lp);
- withdrawalManager.addShares(2, lp);
+ withdrawalManager.addShares(1, lp);
+ withdrawalManager.addShares(1, lp);
+ withdrawalManager.addShares(1, lp);
vm.stopPrank();
( , uint128 lastRequestId_ ) = withdrawalManager.queue();
@@ -116,22 +111,15 @@ contract RemoveRequestTests is TestBase {
( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
- assertEq(shares_, 2);
- assertEq(withdrawalManager.totalShares(), 4);
- assertEq(lastRequestId_, 2);
+ assertEq(shares_, 1);
+ assertEq(withdrawalManager.totalShares(), 3);
+ assertEq(lastRequestId_, 3);
assertEq(lastRequestIdLp_, lastRequestId_);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 3);
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, 4);
-
- uint128[] memory requestIds = new uint128[](2);
- requestIds[0] = 1;
- requestIds[1] = 2;
-
- vm.expectEmit();
- emit RequestRemoved(1);
+ uint128[] memory requestIds = new uint128[](1);
+ requestIds[0] = 2;
vm.expectEmit();
emit RequestRemoved(2);
@@ -146,17 +134,41 @@ contract RemoveRequestTests is TestBase {
( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
( uint128 lastRequestByOwner_, ) = getLastRequestByOwner(owner_);
- assertEq(lastRequestId_, 2);
+ assertEq(lastRequestId_, 3);
+ assertEq(shares_, 1);
+ assertEq(owner_, lp);
+ assertEq(lastRequestIdLp_, 3);
+ assertEq(lastRequestByOwner_, 3);
+ assertEq(withdrawalManager.totalShares(), 2);
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
+
+ requestIds = new uint128[](2);
+ requestIds[0] = 1;
+ requestIds[1] = 3;
+
+ vm.expectEmit();
+ emit RequestRemoved(1);
+ emit RequestRemoved(3);
+
+ vm.prank(poolDelegate);
+ withdrawalManager.removeRequest(lp, requestIds);
+
+ ( , lastRequestId_ ) = withdrawalManager.queue();
+
+ ( owner_, shares_ ) = withdrawalManager.requests(lastRequestId_);
+
+ ( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
+ ( lastRequestByOwner_, ) = getLastRequestByOwner(owner_);
+
+ assertEq(lastRequestId_, 3);
assertEq(shares_, 0);
assertEq(owner_, address(0));
assertEq(lastRequestIdLp_, 0);
assertEq(lastRequestByOwner_, 0);
assertEq(withdrawalManager.totalShares(), 0);
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 0);
- assertEq(escrowedSharesTotalLp_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
}
diff --git a/tests/unit/RemoveShares.t.sol b/tests/unit/RemoveShares.t.sol
index 645cb60..dd0f897 100644
--- a/tests/unit/RemoveShares.t.sol
+++ b/tests/unit/RemoveShares.t.sol
@@ -65,10 +65,7 @@ contract RemoveSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(2, lp);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
vm.expectEmit();
emit RequestDecreased(1, 1);
@@ -76,10 +73,7 @@ contract RemoveSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.removeShares(1, lp);
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
( , lastRequestId_ ) = withdrawalManager.queue();
@@ -98,10 +92,7 @@ contract RemoveSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(2, lp);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
vm.expectEmit();
emit RequestRemoved(1);
@@ -120,10 +111,7 @@ contract RemoveSharesTests is TestBase {
assertEq(shares_, 0);
assertEq(withdrawalManager.totalShares(), 0);
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 0);
- assertEq(escrowedSharesTotalLp_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
}
diff --git a/tests/unit/SortedArray/Get.t.sol b/tests/unit/SortedArray/Get.t.sol
new file mode 100644
index 0000000..5d661d1
--- /dev/null
+++ b/tests/unit/SortedArray/Get.t.sol
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+import { SortedArrayTestBase } from "../../utils/TestBase.sol";
+
+contract GetTests is SortedArrayTestBase {
+
+ function test_get_outOfBounds() external {
+ array.push(1);
+ vm.expectRevert("SA:G:OUT_OF_BOUNDS");
+ array.get(1);
+ }
+
+ function test_get_singleValue() external {
+ array.push(1);
+ assertEq(array.get(0), 1);
+ }
+
+ function test_get_multipleValues() external {
+ array.push(1);
+ array.push(2);
+ array.push(3);
+
+ assertEq(array.get(0), 1);
+ assertEq(array.get(1), 2);
+ assertEq(array.get(2), 3);
+ }
+
+}
diff --git a/tests/unit/SortedArray/Push.t.sol b/tests/unit/SortedArray/Push.t.sol
new file mode 100644
index 0000000..410183e
--- /dev/null
+++ b/tests/unit/SortedArray/Push.t.sol
@@ -0,0 +1,46 @@
+
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+import { SortedArrayTestBase } from "../../utils/TestBase.sol";
+
+// TODO: Update tests to use sequential numbers with gaps.
+contract PushTests is SortedArrayTestBase {
+
+ function test_push_failed_outOfOrder() external {
+ array.push(3);
+
+ vm.expectRevert("SA:P:NOT_LARGEST");
+ array.push(1);
+ }
+
+ function test_push_singleValue() external {
+ array.push(1);
+
+ uint128[] memory expectedValues = new uint128[](1);
+ expectedValues[0] = 1;
+
+ assertArray(expectedValues);
+ assertElementAtIndex(0, 1);
+
+ assertEq(array.length(), 1);
+ }
+
+ function test_push_multipleValues_inOrder() external {
+ array.push(1);
+ array.push(2);
+ array.push(3);
+
+ uint128[] memory expectedValues = new uint128[](3);
+ expectedValues[0] = 1;
+ expectedValues[1] = 2;
+ expectedValues[2] = 3;
+
+ assertArray(expectedValues);
+ assertElementAtIndex(0, 1);
+ assertElementAtIndex(1, 2);
+ assertElementAtIndex(2, 3);
+ assertEq(array.length(), 3);
+ }
+
+}
diff --git a/tests/unit/SortedArray/Remove.t.sol b/tests/unit/SortedArray/Remove.t.sol
new file mode 100644
index 0000000..fc8f39d
--- /dev/null
+++ b/tests/unit/SortedArray/Remove.t.sol
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+import { SortedArrayTestBase } from "../../utils/TestBase.sol";
+
+contract RemoveTests is SortedArrayTestBase {
+
+ function test_remove_singleValue() external {
+ array.push(1);
+ array.remove(1);
+
+ uint128[] memory expectedValues = new uint128[](0);
+ assertArray(expectedValues);
+
+ assertEq(array.length(), 0);
+ }
+
+ function test_remove_multipleValues() external {
+ array.push(1);
+ array.push(2);
+ array.push(3);
+
+ array.remove(2);
+
+ uint128[] memory expectedValues = new uint128[](2);
+ expectedValues[0] = 1;
+ expectedValues[1] = 3;
+
+ assertArray(expectedValues);
+ assertElementAtIndex(0, 1);
+ assertElementAtIndex(1, 3);
+
+ assertEq(array.length(), 2);
+
+ array.remove(1);
+
+ expectedValues = new uint128[](1);
+ expectedValues[0] = 3;
+
+ assertArray(expectedValues);
+ assertElementAtIndex(0, 3);
+
+ assertEq(array.length(), 1);
+
+ array.remove(3);
+
+ assertArray(new uint128[](0));
+ assertEq(array.length(), 0);
+ }
+
+}
diff --git a/tests/unit/UpdateShares.t.sol b/tests/unit/UpdateShares.t.sol
index ac2ebc1..bea8a77 100644
--- a/tests/unit/UpdateShares.t.sol
+++ b/tests/unit/UpdateShares.t.sol
@@ -99,10 +99,7 @@ contract UpdateSharesSuccessTests is TestBase {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
@@ -127,20 +124,14 @@ contract UpdateSharesSuccessTests is TestBase {
assertEq(requestIds_[0], 2, "Request ID should be 2");
assertEq(shares_[0], 2, "Shares should be 2");
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
}
function test_updateShares_remove_request() external{
vm.prank(pm);
uint128 requestId_ = withdrawalManager.addShares(1, lp);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
vm.prank(lp);
vm.expectEmit();
@@ -150,20 +141,14 @@ contract UpdateSharesSuccessTests is TestBase {
assertEq(newRequestId_, 0, "Request ID should be 0");
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 0);
- assertEq(escrowedSharesTotalLp_, 0);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
function test_updateShares_decrease() external {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
@@ -187,10 +172,7 @@ contract UpdateSharesSuccessTests is TestBase {
assertEq(requestIds_[0], 1, "Request ID should be 1");
assertEq(shares_[0], 1, "Shares should be 1");
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
}
function test_update_shares_multiple_lps_requestsByOwner() external {
@@ -237,20 +219,9 @@ contract UpdateSharesSuccessTests is TestBase {
vm.stopPrank();
- {
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
- ( uint256 requestCountLp2_, uint256 escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2_);
- ( uint256 requestCountLp3_, uint256 escrowedSharesTotalLp3_ ) = withdrawalManager.userRequestSummaries(lp3_);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, requestAmount1_ + requestAmount2_);
-
- assertEq(requestCountLp2_, 2);
- assertEq(escrowedSharesTotalLp2_, requestAmount1_ + requestAmount2_);
-
- assertEq(requestCountLp3_, 2);
- assertEq(escrowedSharesTotalLp3_, requestAmount1_ + requestAmount2_);
- }
+ assertEq(withdrawalManager.userEscrowedShares(lp), requestAmount1_ + requestAmount2_);
+ assertEq(withdrawalManager.userEscrowedShares(lp2_), requestAmount1_ + requestAmount2_);
+ assertEq(withdrawalManager.userEscrowedShares(lp3_), requestAmount1_ + requestAmount2_);
{
(uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
@@ -334,20 +305,9 @@ contract UpdateSharesSuccessTests is TestBase {
assertEq(sharesLp3_[1], requestAmount2_ + 10, "LP3 shares index 1 is incorrect");
}
- {
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
- ( uint256 requestCountLp2_, uint256 escrowedSharesTotalLp2_ ) = withdrawalManager.userRequestSummaries(lp2_);
- ( uint256 requestCountLp3_, uint256 escrowedSharesTotalLp3_ ) = withdrawalManager.userRequestSummaries(lp3_);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, requestAmount1_ - 1 + requestAmount2_);
-
- assertEq(requestCountLp2_, 1);
- assertEq(escrowedSharesTotalLp2_, requestAmount2_);
-
- assertEq(requestCountLp3_, 2);
- assertEq(escrowedSharesTotalLp3_, requestAmount2_ + 10 + requestAmount2_);
- }
+ assertEq(withdrawalManager.userEscrowedShares(lp), requestAmount1_ - 1 + requestAmount2_);
+ assertEq(withdrawalManager.userEscrowedShares(lp2_), requestAmount2_);
+ assertEq(withdrawalManager.userEscrowedShares(lp3_), requestAmount2_ + 10 + requestAmount2_);
}
}
@@ -442,10 +402,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
@@ -479,10 +436,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIds_[0], 2, "Request ID should be 2");
assertEq(shares_[0], 2, "Shares should be 2");
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
}
function test_updateSharesBatch_increase_multiple_requests() external {
@@ -491,10 +445,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(1, lp);
vm.stopPrank();
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
@@ -540,20 +491,14 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIds_[1], 4, "Request ID should be 4");
assertEq(shares_[1], 3, "Shares should be 3");
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, 5);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 5);
}
function test_updateSharesBatch_decrease_single_request() external {
vm.prank(pm);
uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
@@ -586,10 +531,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIds_[0], 1, "Request ID should be 1");
assertEq(shares_[0], 1, "Shares should be 1");
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 1);
- assertEq(escrowedSharesTotalLp_, 1);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
}
function test_updateSharesBatch_decrease_multiple_requests() external {
@@ -598,10 +540,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
vm.stopPrank();
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, 4);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 4);
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
@@ -645,10 +584,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIds_[1], 2, "Request ID should be 2");
assertEq(shares_[1], 1, "Shares should be 1");
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
}
function test_updateSharesBatch_increase_and_decrease_multiple_requests() external {
@@ -657,10 +593,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
vm.stopPrank();
- ( uint256 requestCountLp_, uint256 escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, 3);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 3);
(uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
@@ -705,10 +638,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIds_[1], 3, "Request ID should be 3");
assertEq(shares_[1], 2, "Shares should be 2");
- ( requestCountLp_, escrowedSharesTotalLp_ ) = withdrawalManager.userRequestSummaries(lp);
-
- assertEq(requestCountLp_, 2);
- assertEq(escrowedSharesTotalLp_, 3);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 3);
}
}
diff --git a/tests/unit/ViewFunctions.t.sol b/tests/unit/ViewFunctions.t.sol
index 1bf41d5..b66601a 100644
--- a/tests/unit/ViewFunctions.t.sol
+++ b/tests/unit/ViewFunctions.t.sol
@@ -78,60 +78,6 @@ contract ViewFunctionsTests is TestBase {
assertEq(sharesLp4_[0], assetsDeposited_);
}
- function test_requests_by_owner_and_range() external {
- address lp1 = makeAddr("lp1");
- address lp2 = makeAddr("lp2");
- address lp3 = makeAddr("lp3");
- address lp4 = makeAddr("lp4");
-
- uint256 assetsDeposited_ = 100e18;
-
- withdrawalManager.__setRequest(1, lp1, assetsDeposited_);
- withdrawalManager.__setRequest(2, lp2, assetsDeposited_);
- withdrawalManager.__setRequest(3, lp3, assetsDeposited_);
- withdrawalManager.__setRequest(4, lp4, assetsDeposited_);
- withdrawalManager.__setRequest(5, lp1, assetsDeposited_);
-
- withdrawalManager.__setUserRequestCount(lp1, 2, assetsDeposited_ * 2);
- withdrawalManager.__setUserRequestCount(lp2, 1, assetsDeposited_);
- withdrawalManager.__setUserRequestCount(lp3, 1, assetsDeposited_);
- withdrawalManager.__setUserRequestCount(lp4, 1, assetsDeposited_);
-
- withdrawalManager.__setQueue(1, 5);
-
- (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requestsByOwnerRange(lp1, 1, 5);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwnerRange(lp2, 1, 5);
- (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwnerRange(lp3, 1, 5);
- (uint128[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requestsByOwnerRange(lp4, 1, 5);
-
-
- assertEq(requestIdsLp1_.length, 2);
- assertEq(requestIdsLp1_[0], 1);
- assertEq(requestIdsLp1_[1], 5);
-
- assertEq(sharesLp1_.length, 2);
- assertEq(sharesLp1_[0], assetsDeposited_);
- assertEq(sharesLp1_[1], assetsDeposited_);
-
- assertEq(requestIdsLp2_.length, 1);
- assertEq(requestIdsLp2_[0], 2);
-
- assertEq(sharesLp2_.length, 1);
- assertEq(sharesLp2_[0], assetsDeposited_);
-
- assertEq(requestIdsLp3_.length, 1);
- assertEq(requestIdsLp3_[0], 3);
-
- assertEq(sharesLp3_.length, 1);
- assertEq(sharesLp3_[0], assetsDeposited_);
-
- assertEq(requestIdsLp4_.length, 1);
- assertEq(requestIdsLp4_[0], 4);
-
- assertEq(sharesLp4_.length, 1);
- assertEq(sharesLp4_[0], assetsDeposited_);
- }
-
function test_requests_by_requestId() external {
address lp2 = makeAddr("lp2");
address lp3 = makeAddr("lp3");
diff --git a/tests/utils/Harnesses.sol b/tests/utils/Harnesses.sol
index 33af81a..c6e7210 100644
--- a/tests/utils/Harnesses.sol
+++ b/tests/utils/Harnesses.sol
@@ -3,6 +3,8 @@ pragma solidity ^0.8.7;
import { MapleWithdrawalManager } from "../../contracts/MapleWithdrawalManager.sol";
+import { SortedArray } from "../../contracts/utils/SortedArray.sol";
+
contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
function locked() external view returns (uint256) {
@@ -18,7 +20,8 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
function __setLastRequest(address owner_, uint128 requestId_) external {
- lastRequestIds[owner_] = requestId_;
+ SortedArray.push(userRequests[owner_], requestId_);
+ queue.lastRequestId = requestId_;
}
function __setQueue(uint128 nextRequestId_, uint128 lastRequestId_) external {
@@ -27,8 +30,10 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
function __setRequest(uint128 requestId_, address owner_, uint256 shares_) external {
+ queue.lastRequestId = requestId_;
queue.requests[requestId_] = WithdrawalRequest(owner_, shares_);
- lastRequestIds[owner_] = requestId_;
+
+ SortedArray.push(userRequests[owner_], requestId_);
}
function __setTotalShares(uint256 totalShares_) external {
@@ -36,8 +41,49 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
function __setUserRequestCount(address owner_, uint256 requestCount_, uint256 escrowSharesTotal_) external {
- userRequestSummaries[owner_].requestCount = requestCount_;
- userRequestSummaries[owner_].escrowedSharesTotal = escrowSharesTotal_;
+ uint256 currentRequestCount_ = SortedArray.length(userRequests[owner_]);
+
+ if (requestCount_ < currentRequestCount_) {
+ for (uint256 i = requestCount_; i < currentRequestCount_; i++) {
+ SortedArray.remove(userRequests[owner_], uint128(i));
+ }
+ } else {
+ for (uint256 i = currentRequestCount_; i > requestCount_; i++) {
+ SortedArray.push(userRequests[owner_], uint128(i));
+ }
+ }
+
+ userEscrowedShares[owner_] = escrowSharesTotal_;
+ }
+
+}
+
+contract SortedArrayHarness {
+
+ SortedArray.Array array;
+
+ function push(uint128 value_) external {
+ SortedArray.push(array, value_);
+ }
+
+ function remove(uint128 value_) external {
+ SortedArray.remove(array, value_);
+ }
+
+ function get(uint256 index_) external view returns (uint128) {
+ return SortedArray.get(array, index_);
+ }
+
+ function length() external view returns (uint256) {
+ return SortedArray.length(array);
+ }
+
+ function getAllValues() external view returns (uint128[] memory) {
+ return SortedArray.getAllValues(array);
+ }
+
+ function getLast() external view returns (uint128) {
+ return SortedArray.getLast(array);
}
}
diff --git a/tests/utils/TestBase.sol b/tests/utils/TestBase.sol
index 2e9c296..a6a0a68 100644
--- a/tests/utils/TestBase.sol
+++ b/tests/utils/TestBase.sol
@@ -10,6 +10,8 @@ import { MapleWithdrawalManagerInitializer } from "../../contracts/proxy/MapleWi
import { MapleWithdrawalManagerHarness } from "./Harnesses.sol";
import { MockFactory, MockGlobals, MockPool, MockPoolManager } from "./Mocks.sol";
+import { SortedArrayHarness } from "./Harnesses.sol";
+
contract TestBase is Test {
address internal governor = makeAddr("governor");
@@ -92,3 +94,28 @@ contract TestBase is Test {
}
}
+
+contract SortedArrayTestBase is Test {
+
+ SortedArrayHarness public array;
+
+ function setUp() public virtual {
+ array = new SortedArrayHarness();
+ }
+
+ function assertArray(uint128[] memory values) internal {
+ assertEq(array.length(), values.length);
+
+ uint128[] memory arrayValues = array.getAllValues();
+
+ for (uint256 i = 0; i < values.length; i++) {
+ assertEq(arrayValues[i], values[i]);
+ }
+ }
+
+ function assertElementAtIndex(uint256 index, uint128 value) internal {
+ assertEq(array.get(index), value);
+ }
+
+}
+
From 97a6fcb927e5874202f52aef3da30e45953abac1 Mon Sep 17 00:00:00 2001
From: Danilo Kitanovic <116795362+kitanovicd@users.noreply.github.com>
Date: Wed, 30 Jul 2025 10:02:19 +0200
Subject: [PATCH 06/21] feat: Update removeShares to iterate over all user's
requests (SC-21057) (#35)
* feat: update removeShares to iterate over all user's requests
* chore: format
* chore: remove unnecessary return statement
* fix: update push tests for SortedArray library
* chore: formatting
---------
Co-authored-by: 0xfarhaan <59924029+0xfarhaan@users.noreply.github.com>
---
contracts/MapleWithdrawalManager.sol | 14 ++--
tests/unit/RemoveShares.t.sol | 109 +++++++++++++++++++++++++--
tests/unit/SortedArray/Push.t.sol | 17 ++---
3 files changed, 120 insertions(+), 20 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index c1f0533..0521eb5 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -156,15 +156,17 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
function removeShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint256 sharesReturned_) {
require(shares_ > 0, "WM:RS:ZERO_SHARES");
- uint128 lastRequestId_ = SortedArray.getLast(userRequests[owner_]);
+ uint256 totalEscrowedShares_ = userEscrowedShares[owner_];
- require(lastRequestId_ > 0, "WM:RS:NO_REQUESTS");
+ require(totalEscrowedShares_ >= shares_, "WM:RS:INSUFFICIENT_SHARES");
- WithdrawalRequest memory request_ = queue.requests[lastRequestId_];
+ while (sharesReturned_ < shares_) {
+ uint128 requestId_ = SortedArray.getLast(userRequests[owner_]);
+ WithdrawalRequest memory request_ = queue.requests[requestId_];
- require(request_.shares >= shares_, "WM:RS:INSUFFICIENT_SHARES");
-
- sharesReturned_ = _removeShares(lastRequestId_, shares_, request_.owner, request_.shares);
+ uint256 sharesToRemove_ = _min(shares_ - sharesReturned_, request_.shares);
+ sharesReturned_ += _removeShares(requestId_, sharesToRemove_, owner_, request_.shares);
+ }
}
/**************************************************************************************************************************************/
diff --git a/tests/unit/RemoveShares.t.sol b/tests/unit/RemoveShares.t.sol
index dd0f897..153575b 100644
--- a/tests/unit/RemoveShares.t.sol
+++ b/tests/unit/RemoveShares.t.sol
@@ -12,10 +12,10 @@ contract RemoveSharesTests is TestBase {
super.setUp();
// Simulate LP transfer into PM.
- pool.mint(pm, 2);
+ pool.mint(pm, 7);
vm.prank(pm);
- pool.approve(address(withdrawalManager), 2);
+ pool.approve(address(withdrawalManager), 7);
}
function test_removeShares_notPoolManager() external {
@@ -35,7 +35,7 @@ contract RemoveSharesTests is TestBase {
assertEq(lastRequestId_, 0);
vm.prank(pm);
- vm.expectRevert("WM:RS:NO_REQUESTS");
+ vm.expectRevert("WM:RS:INSUFFICIENT_SHARES");
withdrawalManager.removeShares(1, lp);
}
@@ -71,8 +71,9 @@ contract RemoveSharesTests is TestBase {
emit RequestDecreased(1, 1);
vm.prank(pm);
- withdrawalManager.removeShares(1, lp);
+ uint256 sharesReturned_ = withdrawalManager.removeShares(1, lp);
+ assertEq(sharesReturned_ , 1);
assertEq(withdrawalManager.userEscrowedShares(lp), 1);
( , lastRequestId_ ) = withdrawalManager.queue();
@@ -98,7 +99,9 @@ contract RemoveSharesTests is TestBase {
emit RequestRemoved(1);
vm.prank(pm);
- withdrawalManager.removeShares(2, lp);
+ uint256 sharesReturned_ = withdrawalManager.removeShares(2, lp);
+
+ assertEq(sharesReturned_, 2);
( , lastRequestId_ ) = withdrawalManager.queue();
@@ -114,4 +117,100 @@ contract RemoveSharesTests is TestBase {
assertEq(withdrawalManager.userEscrowedShares(lp), 0);
}
+ function test_removeShares_success_partial_multipleRequests() external {
+ vm.startPrank(pm);
+ withdrawalManager.addShares(2, lp);
+ withdrawalManager.addShares(4, lp);
+ withdrawalManager.addShares(1, lp);
+ vm.stopPrank();
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), 7);
+ assertEq(withdrawalManager.totalShares() , 7);
+
+ ( uint128[] memory requestIds_, uint256[] memory shares_ ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIds_.length, 3);
+ assertEq(shares_.length , 3);
+
+ ( uint128 lastRequestId_, uint256 lastShares_ ) = getLastRequestByOwner(lp);
+
+ assertEq(lastRequestId_, 3);
+ assertEq(lastShares_ , 1);
+
+ vm.prank(pm);
+ uint256 sharesReturned_ = withdrawalManager.removeShares(3, lp);
+
+ assertEq(sharesReturned_, 3);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 4);
+ assertEq(withdrawalManager.totalShares() , 4);
+
+ ( lastRequestId_, lastShares_ ) = getLastRequestByOwner(lp);
+
+ assertEq(lastRequestId_, 2);
+ assertEq(lastShares_ , 2);
+
+ ( requestIds_, shares_ ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIds_.length, 2);
+ assertEq(shares_.length , 2);
+
+ assertRequest(1, lp, 2);
+ assertRequest(2, lp, 2);
+
+ vm.prank(pm);
+ sharesReturned_ = withdrawalManager.removeShares(2, lp);
+
+ assertEq(sharesReturned_, 2);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
+ assertEq(withdrawalManager.totalShares() , 2);
+
+ ( requestIds_, shares_ ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIds_.length, 1);
+ assertEq(shares_.length , 1);
+
+ assertRequest(1, lp, 2);
+ }
+
+ function test_removeShares_success_cancelAllRequests() external {
+ vm.startPrank(pm);
+ withdrawalManager.addShares(2, lp);
+ withdrawalManager.addShares(4, lp);
+ withdrawalManager.addShares(1, lp);
+ vm.stopPrank();
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), 7);
+ assertEq(withdrawalManager.totalShares() , 7);
+
+ ( uint128[] memory requestIds_, uint256[] memory shares_ ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIds_.length, 3);
+ assertEq(shares_.length , 3);
+
+ ( uint128 lastRequestId_, uint256 lastShares_ ) = getLastRequestByOwner(lp);
+
+ assertEq(lastRequestId_, 3);
+ assertEq(lastShares_ , 1);
+
+ vm.prank(pm);
+ uint256 sharesReturned_ = withdrawalManager.removeShares(7, lp);
+
+ assertEq(sharesReturned_, 7);
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
+ assertEq(withdrawalManager.totalShares() , 0);
+
+ ( requestIds_, shares_ ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIds_.length, 0);
+ assertEq(shares_.length , 0);
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
+ assertEq(withdrawalManager.totalShares() , 0);
+
+ ( lastRequestId_, lastShares_ ) = getLastRequestByOwner(lp);
+
+ assertEq(lastRequestId_, 0);
+ assertEq(lastShares_ , 0);
+ }
+
}
diff --git a/tests/unit/SortedArray/Push.t.sol b/tests/unit/SortedArray/Push.t.sol
index 410183e..333bfbb 100644
--- a/tests/unit/SortedArray/Push.t.sol
+++ b/tests/unit/SortedArray/Push.t.sol
@@ -4,7 +4,6 @@ pragma solidity ^0.8.7;
import { SortedArrayTestBase } from "../../utils/TestBase.sol";
-// TODO: Update tests to use sequential numbers with gaps.
contract PushTests is SortedArrayTestBase {
function test_push_failed_outOfOrder() external {
@@ -15,31 +14,31 @@ contract PushTests is SortedArrayTestBase {
}
function test_push_singleValue() external {
- array.push(1);
+ array.push(3);
uint128[] memory expectedValues = new uint128[](1);
- expectedValues[0] = 1;
+ expectedValues[0] = 3;
assertArray(expectedValues);
- assertElementAtIndex(0, 1);
+ assertElementAtIndex(0, 3);
assertEq(array.length(), 1);
}
function test_push_multipleValues_inOrder() external {
array.push(1);
- array.push(2);
array.push(3);
+ array.push(7);
uint128[] memory expectedValues = new uint128[](3);
expectedValues[0] = 1;
- expectedValues[1] = 2;
- expectedValues[2] = 3;
+ expectedValues[1] = 3;
+ expectedValues[2] = 7;
assertArray(expectedValues);
assertElementAtIndex(0, 1);
- assertElementAtIndex(1, 2);
- assertElementAtIndex(2, 3);
+ assertElementAtIndex(1, 3);
+ assertElementAtIndex(2, 7);
assertEq(array.length(), 3);
}
From 01a636f2d0e146777cb7cb62e04ba7f1ae785572 Mon Sep 17 00:00:00 2001
From: Cal Mac Fadden <108666242+calmacfadden@users.noreply.github.com>
Date: Fri, 29 Aug 2025 17:08:27 +0400
Subject: [PATCH 07/21] feat: Added migrator for withdrawal manager storage
(sc-21217) (#37)
* feat: added migrator
* feat: added unit tests
Signed-off-by: calmacfadden
* feat: unit tests and migrator
Signed-off-by: calmacfadden
* chore: formatting
Signed-off-by: calmacfadden
* chore: formatting
Signed-off-by: calmacfadden
* chore: formatting
Signed-off-by: calmacfadden
* chore: formatting
Signed-off-by: calmacfadden
* feat: fixed storage layout bug
Signed-off-by: calmacfadden
* chore: rename and formatting
Signed-off-by: calmacfadden
* chore: removed file
Signed-off-by: calmacfadden
* fix: fix test
Signed-off-by: calmacfadden
---------
Signed-off-by: calmacfadden
---
.../MapleWithdrawalManagerMigratorV200.sol | 26 +++++++
.../unit/MapleWithdrawalManagerMigrator.t.sol | 77 +++++++++++++++++++
tests/utils/Harnesses.sol | 9 +++
tests/utils/Mocks.sol | 2 +-
4 files changed, 113 insertions(+), 1 deletion(-)
create mode 100644 contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
create mode 100644 tests/unit/MapleWithdrawalManagerMigrator.t.sol
diff --git a/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol b/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
new file mode 100644
index 0000000..bf2c7bf
--- /dev/null
+++ b/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+import { MapleProxiedInternals } from "../../modules/maple-proxy-factory/contracts/MapleProxiedInternals.sol";
+
+import { SortedArray } from "../utils/SortedArray.sol";
+
+import { MapleWithdrawalManagerStorage } from "./MapleWithdrawalManagerStorage.sol";
+
+contract MapleWithdrawalManagerMigratorV200 is MapleProxiedInternals, MapleWithdrawalManagerStorage {
+
+ fallback() external {
+ uint128 nextRequestId_ = queue.nextRequestId;
+ uint128 lastRequestId_ = queue.lastRequestId;
+
+ for (uint128 i = nextRequestId_; i <= lastRequestId_; ++i) {
+ WithdrawalRequest storage request = queue.requests[i];
+
+ if (request.owner != address(0)) {
+ userEscrowedShares[request.owner] += request.shares;
+ SortedArray.push(userRequests[request.owner], i);
+ }
+ }
+ }
+
+}
diff --git a/tests/unit/MapleWithdrawalManagerMigrator.t.sol b/tests/unit/MapleWithdrawalManagerMigrator.t.sol
new file mode 100644
index 0000000..7897339
--- /dev/null
+++ b/tests/unit/MapleWithdrawalManagerMigrator.t.sol
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+import { MapleWithdrawalManagerMigratorV200 } from "../../contracts/proxy/MapleWithdrawalManagerMigratorV200.sol";
+import { TestBase } from "../utils/TestBase.sol";
+
+contract WithdrawalManagerMigrateTests is TestBase {
+
+ address internal migrator;
+
+ function setUp() public override {
+ super.setUp();
+
+ migrator = address(new MapleWithdrawalManagerMigratorV200());
+ }
+
+ function test_migrate_notFactory() external {
+ vm.expectRevert("WM:M:NOT_FACTORY");
+ withdrawalManager.migrate(migrator, "");
+ }
+
+ function test_migrate_protocolPaused() external {
+ globals.__setFunctionPaused(true);
+
+ vm.expectRevert("WM:PAUSED");
+ withdrawalManager.migrate(migrator, "");
+ }
+
+ function test_migrate_success_xxx() external {
+ withdrawalManager.__setRequestLegacy(10, address(0x01), 1);
+ withdrawalManager.__setRequestLegacy(12, address(0x01), 2);
+ withdrawalManager.__setRequestLegacy(13, address(0x02), 4);
+ withdrawalManager.__setRequestLegacy(15, address(0x03), 5);
+
+ withdrawalManager.__setQueue(10, 15);
+
+ vm.prank(address(factory));
+ withdrawalManager.migrate(migrator,"");
+
+ ( uint128 nextRequestId_, uint128 lastRequestId_ ) = withdrawalManager.queue();
+
+ assert(nextRequestId_ == 10);
+ assert(lastRequestId_ == 15);
+
+ assertEq(withdrawalManager.userEscrowedShares(address(0x01)), 3);
+ assertEq(withdrawalManager.userEscrowedShares(address(0x02)), 4);
+ assertEq(withdrawalManager.userEscrowedShares(address(0x03)), 5);
+
+ address owner_;
+ uint256 shares_;
+
+ ( owner_, shares_ ) = withdrawalManager.requests(10);
+ assertEq(owner_, address(0x01));
+ assertEq(shares_, 1);
+
+ ( owner_, shares_ ) = withdrawalManager.requests(11);
+ assertEq(owner_, address(0));
+ assertEq(shares_, 0);
+
+ ( owner_, shares_ ) = withdrawalManager.requests(12);
+ assertEq(owner_, address(0x01));
+ assertEq(shares_, 2);
+
+ ( owner_, shares_ ) = withdrawalManager.requests(13);
+ assertEq(owner_, address(0x02));
+ assertEq(shares_, 4);
+
+ ( owner_, shares_ ) = withdrawalManager.requests(14);
+ assertEq(owner_, address(0));
+ assertEq(shares_, 0);
+
+ ( owner_, shares_ ) = withdrawalManager.requests(15);
+ assertEq(owner_, address(0x03));
+ assertEq(shares_, 5);
+ }
+
+}
diff --git a/tests/utils/Harnesses.sol b/tests/utils/Harnesses.sol
index c6e7210..3a2e78c 100644
--- a/tests/utils/Harnesses.sol
+++ b/tests/utils/Harnesses.sol
@@ -29,6 +29,15 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
queue.lastRequestId = lastRequestId_;
}
+ function __setRequestLegacy(uint128 requestId_, address owner_, uint256 shares_) external {
+ // Used to setup the withdrawal manager storage in the old format.
+ // This function is only used to unit test the migrator so we can setup wm storage without pushing to the sorted array.
+ queue.lastRequestId = requestId_;
+ queue.requests[requestId_] = WithdrawalRequest(owner_, shares_);
+
+ __deprecated_requestIds[owner_] = requestId_;
+ }
+
function __setRequest(uint128 requestId_, address owner_, uint256 shares_) external {
queue.lastRequestId = requestId_;
queue.requests[requestId_] = WithdrawalRequest(owner_, shares_);
diff --git a/tests/utils/Mocks.sol b/tests/utils/Mocks.sol
index facfe09..1963060 100644
--- a/tests/utils/Mocks.sol
+++ b/tests/utils/Mocks.sol
@@ -108,7 +108,7 @@ contract MockPool is MockERC20 {
asset_ = address(_asset);
}
- function redeem(uint256, address, address) external returns (uint256 assets_) {
+ function redeem(uint256, address, address) external view returns (uint256 assets_) {
assets_; // Ignore variable
}
From d33568448f55b4a144df25f920d40543300dfac0 Mon Sep 17 00:00:00 2001
From: Cal Mac Fadden <108666242+calmacfadden@users.noreply.github.com>
Date: Mon, 1 Sep 2025 12:19:55 +0400
Subject: [PATCH 08/21] feat: Changed wm interfaces to use uint256 (SC-21446)
(#38)
* feat: changed wm interfaces to use uint256
Signed-off-by: calmacfadden
* feat: more casting
Signed-off-by: calmacfadden
* pr fixes
Signed-off-by: calmacfadden
---------
Signed-off-by: calmacfadden
---
contracts/MapleWithdrawalManager.sol | 63 +++++----
.../interfaces/IMapleWithdrawalManager.sol | 24 ++--
contracts/utils/SortedArray.sol | 18 +--
tests/fuzz/AddSharesFuzz.t.sol | 4 +-
tests/fuzz/RemoveSharesFuzz.t.sol | 4 +-
tests/integration/EndToEndTests.t.sol | 14 +-
tests/unit/AddShares.t.sol | 14 +-
tests/unit/ProcessExit.t.sol | 6 +-
tests/unit/ProcessRedemptions.t.sol | 44 +++---
tests/unit/RemoveRequest.t.sol | 28 ++--
tests/unit/RemoveShares.t.sol | 18 +--
tests/unit/SortedArray/Push.t.sol | 4 +-
tests/unit/SortedArray/Remove.t.sol | 8 +-
tests/unit/UpdateShares.t.sol | 126 +++++++++---------
tests/unit/ViewFunctions.t.sol | 8 +-
tests/utils/Harnesses.sol | 26 ++--
tests/utils/TestBase.sol | 16 +--
17 files changed, 215 insertions(+), 210 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 0521eb5..970c66d 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -133,7 +133,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
/*** State-Changing Functions - OnlyPoolManager ***/
/**************************************************************************************************************************************/
- function addShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint128 lastRequestId_) {
+ function addShares(uint256 shares_, address owner_) external override onlyPoolManager returns (uint256 lastRequestId_) {
require(shares_ > 0, "WM:AS:ZERO_SHARES");
lastRequestId_ = _addRequest(owner_, shares_);
@@ -161,8 +161,8 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
require(totalEscrowedShares_ >= shares_, "WM:RS:INSUFFICIENT_SHARES");
while (sharesReturned_ < shares_) {
- uint128 requestId_ = SortedArray.getLast(userRequests[owner_]);
- WithdrawalRequest memory request_ = queue.requests[requestId_];
+ uint256 requestId_ = SortedArray.getLast(userRequests[owner_]);
+ WithdrawalRequest memory request_ = queue.requests[_toUint128(requestId_)];
uint256 sharesToRemove_ = _min(shares_ - sharesReturned_, request_.shares);
sharesReturned_ += _removeShares(requestId_, sharesToRemove_, owner_, request_.shares);
@@ -181,8 +181,8 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
// Revert if there are insufficient assets to redeem all shares.
require(maxSharesToProcess_ == redeemableShares_, "WM:PR:LOW_LIQUIDITY");
- uint128 nextRequestId_ = queue.nextRequestId;
- uint128 lastRequestId_ = queue.lastRequestId;
+ uint256 nextRequestId_ = queue.nextRequestId;
+ uint256 lastRequestId_ = queue.lastRequestId;
// Iterate through the loop and process as many requests as possible.
// Stop iterating when there are no more shares to process or if you have reached the end of the queue.
@@ -199,7 +199,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
// Adjust the new start of the queue.
- queue.nextRequestId = nextRequestId_;
+ queue.nextRequestId = _toUint128(nextRequestId_);
}
/**************************************************************************************************************************************/
@@ -208,7 +208,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
function removeRequest(
address owner_,
- uint128[] calldata requestIds_
+ uint256[] calldata requestIds_
) external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins
{
require(owner_ != address(0), "WM:RR:ZERO_OWNER");
@@ -219,7 +219,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
WithdrawalRequest memory withdrawalRequest_;
for (uint256 i = 0; i < requestIds_.length; ++i) {
- withdrawalRequest_ = queue.requests[requestIds_[i]];
+ withdrawalRequest_ = queue.requests[_toUint128(requestIds_[i])];
require(withdrawalRequest_.shares > 0, "WM:RR:NOT_IN_QUEUE");
require(withdrawalRequest_.owner == owner_, "WM:RR:NOT_OWNER");
@@ -245,11 +245,11 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
/**************************************************************************************************************************************/
function updateShares(
- uint128 requestId_,
+ uint256 requestId_,
uint256 newSharesTotal_
- ) public override whenProtocolNotPaused nonReentrant returns (uint128 updatedRequestId_)
+ ) public override whenProtocolNotPaused nonReentrant returns (uint256 updatedRequestId_)
{
- WithdrawalRequest memory request_ = queue.requests[requestId_];
+ WithdrawalRequest memory request_ = queue.requests[_toUint128(requestId_)];
require(request_.owner != address(0), "WM:US:INVALID_REQUEST");
require(request_.owner == msg.sender, "WM:US:NOT_OWNER");
@@ -267,9 +267,9 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function updateSharesBatch(
- uint128[] memory requestIds_,
+ uint256[] memory requestIds_,
uint256[] calldata newSharesTotals_
- ) external override whenProtocolNotPaused returns (uint128[] memory updatedRequestIds_)
+ ) external override whenProtocolNotPaused returns (uint256[] memory updatedRequestIds_)
{
require(requestIds_.length == newSharesTotals_.length, "WM:USB:ARRAY_LENGTH_MISMATCH");
@@ -284,10 +284,10 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
/*** Internal Functions ***/
/**************************************************************************************************************************************/
- function _addRequest(address owner_, uint256 shares_) internal returns (uint128 lastRequestId_) {
+ function _addRequest(address owner_, uint256 shares_) internal returns (uint256 lastRequestId_) {
lastRequestId_ = ++queue.lastRequestId;
- queue.requests[lastRequestId_] = WithdrawalRequest(owner_, shares_);
+ queue.requests[_toUint128(lastRequestId_)] = WithdrawalRequest(owner_, shares_);
userEscrowedShares[owner_] += shares_;
SortedArray.push(userRequests[owner_], lastRequestId_);
@@ -300,7 +300,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
emit RequestCreated(lastRequestId_, owner_, shares_);
}
- function _removeShares(uint128 requestId_,
+ function _removeShares(uint256 requestId_,
uint256 sharesToRemove_,
address owner_,
uint256 currentShares_
@@ -314,7 +314,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
if (sharesRemaining_ == 0) {
_removeRequest(owner_, requestId_);
} else {
- queue.requests[requestId_].shares = sharesRemaining_;
+ queue.requests[_toUint128(requestId_)].shares = sharesRemaining_;
userEscrowedShares[owner_] -= sharesToRemove_;
emit RequestDecreased(requestId_, sharesToRemove_);
@@ -370,7 +370,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function _processRequest(
- uint128 requestId_,
+ uint256 requestId_,
uint256 maximumSharesToProcess_
)
internal returns (
@@ -378,7 +378,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
bool isProcessed_
)
{
- WithdrawalRequest memory request_ = queue.requests[requestId_];
+ WithdrawalRequest memory request_ = queue.requests[_toUint128(requestId_)];
// If the request has already been cancelled, skip it.
if (request_.owner == address(0)) return (0, true);
@@ -403,7 +403,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_removeRequest(request_.owner, requestId_);
} else {
// Update the withdrawal request.
- queue.requests[requestId_].shares = sharesRemaining_;
+ queue.requests[_toUint128(requestId_)].shares = sharesRemaining_;
userEscrowedShares[request_.owner] -= processedShares_;
emit RequestDecreased(requestId_, processedShares_);
@@ -422,14 +422,19 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
}
- function _removeRequest(address owner_, uint128 requestId_) internal {
- userEscrowedShares[owner_] -= queue.requests[requestId_].shares;
+ function _removeRequest(address owner_, uint256 requestId_) internal {
+ userEscrowedShares[owner_] -= queue.requests[_toUint128(requestId_)].shares;
SortedArray.remove(userRequests[owner_], requestId_);
- delete queue.requests[requestId_];
+ delete queue.requests[_toUint128(requestId_)];
emit RequestRemoved(requestId_);
}
+ function _toUint128(uint256 input_) internal pure returns (uint128 output_) {
+ require(input_ <= uint256(type(uint128).max), "WM:TU:UINT256_CAST");
+ output_ = uint128(input_);
+ }
+
/**************************************************************************************************************************************/
/*** View Functions ***/
/**************************************************************************************************************************************/
@@ -500,21 +505,21 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
return ( redeemableAssets_, resultingShares_ ); // NOTE: Withdrawal not implemented use redeem instead
}
- function requestIds(address owner_) external view override returns (uint128 requestId_) {
+ function requestIds(address owner_) external view override returns (uint256 requestId_) {
requestId_ = SortedArray.getLast(userRequests[owner_]);
}
- function requests(uint128 requestId_) external view override returns (address owner_, uint256 shares_) {
- owner_ = queue.requests[requestId_].owner;
- shares_ = queue.requests[requestId_].shares;
+ function requests(uint256 requestId_) external view override returns (address owner_, uint256 shares_) {
+ owner_ = queue.requests[_toUint128(requestId_)].owner;
+ shares_ = queue.requests[_toUint128(requestId_)].shares;
}
- function requestsByOwner(address owner_) external view override returns (uint128[] memory requestIds_, uint256[] memory shares_) {
+ function requestsByOwner(address owner_) external view override returns (uint256[] memory requestIds_, uint256[] memory shares_) {
requestIds_ = SortedArray.getAllValues(userRequests[owner_]);
shares_ = new uint256[](requestIds_.length);
for (uint256 i = 0; i < requestIds_.length; ++i) {
- shares_[i] = queue.requests[requestIds_[i]].shares;
+ shares_[i] = queue.requests[_toUint128(requestIds_[i])].shares;
}
}
diff --git a/contracts/interfaces/IMapleWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol
index 9d01d72..eaa2b12 100644
--- a/contracts/interfaces/IMapleWithdrawalManager.sol
+++ b/contracts/interfaces/IMapleWithdrawalManager.sol
@@ -38,14 +38,14 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @param owner Address of the owner of the shares.
* @param shares Amount of shares requested for redemption.
*/
- event RequestCreated(uint128 indexed requestId, address indexed owner, uint256 shares);
+ event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
/**
* @dev Emitted when a withdrawal request is updated.
* @param requestId Identifier of the withdrawal request.
* @param shares Amount of shares reduced during a redemption request.
*/
- event RequestDecreased(uint128 indexed requestId, uint256 shares);
+ event RequestDecreased(uint256 indexed requestId, uint256 shares);
/**
* @dev Emitted when a withdrawal request is processed.
@@ -54,13 +54,13 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @param shares Amount of redeemable shares.
* @param assets Amount of withdrawable assets.
*/
- event RequestProcessed(uint128 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
+ event RequestProcessed(uint256 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
/**
* @dev Emitted when a withdrawal request is removed.
* @param requestId Identifier of the withdrawal request.
*/
- event RequestRemoved(uint128 indexed requestId);
+ event RequestRemoved(uint256 indexed requestId);
/**************************************************************************************************************************************/
/*** State-Changing Functions ***/
@@ -71,7 +71,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @param shares Amount of shares to add.
* @param owner Address of the owner of shares.
*/
- function addShares(uint256 shares, address owner) external returns (uint128 lastRequestId);
+ function addShares(uint256 shares, address owner) external returns (uint256 lastRequestId);
/**
* @dev Processes a withdrawal request.
@@ -105,7 +105,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @param owner Address of the owner of shares.
* @param requestIds Array of identifiers of the withdrawal requests to remove.
*/
- function removeRequest(address owner, uint128[] calldata requestIds) external;
+ function removeRequest(address owner, uint256[] calldata requestIds) external;
/**
* @dev Defines if an account will withdraw shares manually or automatically.
@@ -125,7 +125,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @param newSharesTotal New total amount of shares pending redemption.
* @return currentRequestId Identifier of the withdrawal request that was updated or created.
*/
- function updateShares(uint128 requestId, uint256 newSharesTotal) external returns (uint128 currentRequestId);
+ function updateShares(uint256 requestId, uint256 newSharesTotal) external returns (uint256 currentRequestId);
/**
* @dev Updates the total amount of shares pending redemption in batch.
@@ -134,9 +134,9 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @return currentRequestIds Array of identifiers of the withdrawal requests that were updated or created.
*/
function updateSharesBatch(
- uint128[] calldata requestIds,
+ uint256[] calldata requestIds,
uint256[] calldata newSharesTotals
- ) external returns (uint128[] memory currentRequestIds);
+ ) external returns (uint256[] memory currentRequestIds);
/**************************************************************************************************************************************/
/*** View Functions ***/
@@ -214,7 +214,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @param owner The account to check the last request id for.
* @return requestId The id of the last valid withdrawal request for the account.
*/
- function requestIds(address owner) external view returns (uint128 requestId);
+ function requestIds(address owner) external view returns (uint256 requestId);
/**
* @dev Returns the owner and amount of shares associated with a withdrawal request.
@@ -222,7 +222,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @return owner Address of the share owner.
* @return shares Amount of shares pending redemption.
*/
- function requests(uint128 requestId) external view returns (address owner, uint256 shares);
+ function requests(uint256 requestId) external view returns (address owner, uint256 shares);
/**
* @dev Returns the pending requests by owner.
@@ -231,7 +231,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
* @return requestIds Array of request identifiers.
* @return shares Array of shares associated with each request.
*/
- function requestsByOwner(address owner) external view returns (uint128[] memory requestIds, uint256[] memory shares);
+ function requestsByOwner(address owner) external view returns (uint256[] memory requestIds, uint256[] memory shares);
/**
* @dev Returns the address of the security admin.
diff --git a/contracts/utils/SortedArray.sol b/contracts/utils/SortedArray.sol
index d711c75..5ada4cf 100644
--- a/contracts/utils/SortedArray.sol
+++ b/contracts/utils/SortedArray.sol
@@ -5,9 +5,9 @@ pragma solidity ^0.8.7;
library SortedArray {
struct Array {
- uint128[] values;
+ uint256[] values;
- mapping(uint128 => uint256) valueToIndex;
+ mapping(uint256 => uint256) valueToIndex;
}
/**************************************************************************************************************************************/
@@ -20,7 +20,7 @@ library SortedArray {
* @param array_ The array to push the value to.
* @param value_ The value to push to the array.
*/
- function push(Array storage array_, uint128 value_) internal {
+ function push(Array storage array_, uint256 value_) internal {
require(getLast(array_) < value_, "SA:P:NOT_LARGEST");
array_.values.push(value_);
@@ -34,7 +34,7 @@ library SortedArray {
* @param array_ The array to remove the value from.
* @param value_ The value to remove from the array.
*/
- function remove(Array storage array_, uint128 value_) internal {
+ function remove(Array storage array_, uint256 value_) internal {
uint256 length_ = length(array_);
if (length_ == 1) {
@@ -45,7 +45,7 @@ library SortedArray {
uint256 index_ = array_.valueToIndex[value_];
for (uint256 i = index_; i <= length_ - 2; i++) {
- uint128 nextValue_ = array_.values[i + 1];
+ uint256 nextValue_ = array_.values[i + 1];
array_.values[i] = nextValue_;
array_.valueToIndex[nextValue_] = i;
}
@@ -63,7 +63,7 @@ library SortedArray {
* @param index_ The index of the value to get from the array.
* @return value_ The value at the given index.
*/
- function get(Array storage array_, uint256 index_) internal view returns (uint128 value_) {
+ function get(Array storage array_, uint256 index_) internal view returns (uint256 value_) {
require(index_ < length(array_), "SA:G:OUT_OF_BOUNDS");
value_ = array_.values[index_];
}
@@ -82,7 +82,7 @@ library SortedArray {
* @param array_ The array to get the values from.
* @return values_ All values from the array.
*/
- function getAllValues(Array storage array_) internal view returns (uint128[] memory values_) {
+ function getAllValues(Array storage array_) internal view returns (uint256[] memory values_) {
values_ = array_.values;
}
@@ -91,7 +91,7 @@ library SortedArray {
* @param array_ The array to get the last value from.
* @return value_ The last value in the array.
*/
- function getLast(Array storage array_) internal view returns (uint128 value_) {
+ function getLast(Array storage array_) internal view returns (uint256 value_) {
uint256 length_ = length(array_);
return length_ > 0 ? array_.values[length_ - 1] : 0;
}
@@ -106,7 +106,7 @@ library SortedArray {
* @param array_ The array to delete the value from.
* @param value_ The value to delete from the array.
*/
- function _deleteValue(Array storage array_, uint128 value_) private {
+ function _deleteValue(Array storage array_, uint256 value_) private {
array_.values.pop();
delete array_.valueToIndex[value_];
}
diff --git a/tests/fuzz/AddSharesFuzz.t.sol b/tests/fuzz/AddSharesFuzz.t.sol
index 2fb5d33..67d6bbc 100644
--- a/tests/fuzz/AddSharesFuzz.t.sol
+++ b/tests/fuzz/AddSharesFuzz.t.sol
@@ -10,9 +10,9 @@ contract AddSharesFuzzTests is TestBase {
}
function testFuzz_addShares(uint256[50] memory amount_, address[50] calldata account_) external {
- uint128 lastRequestId;
+ uint256 lastRequestId;
uint256 totalShares_;
- uint128 requestId_;
+ uint256 requestId_;
uint256 shares_;
for (uint256 i; i < account_.length; ++i) {
diff --git a/tests/fuzz/RemoveSharesFuzz.t.sol b/tests/fuzz/RemoveSharesFuzz.t.sol
index 86519a7..b44d15a 100644
--- a/tests/fuzz/RemoveSharesFuzz.t.sol
+++ b/tests/fuzz/RemoveSharesFuzz.t.sol
@@ -12,10 +12,10 @@ contract RemoveSharesFuzzTests is TestBase {
function testFuzz_removeShares(address[50] calldata account_, uint256[50] memory amount0_, uint256[50] memory amount1_) external {
address owner_;
- uint128 lastRequestId;
+ uint256 lastRequestId;
uint256 shares_;
uint256 totalShares_;
- uint128 requestId_;
+ uint256 requestId_;
for (uint256 i; i < account_.length; ++i) {
amount0_[i] = bound(amount0_[i], 1, 1e29);
diff --git a/tests/integration/EndToEndTests.t.sol b/tests/integration/EndToEndTests.t.sol
index 2f86064..3d2f24d 100644
--- a/tests/integration/EndToEndTests.t.sol
+++ b/tests/integration/EndToEndTests.t.sol
@@ -6,7 +6,7 @@ import { console, TestBase } from "../utils/TestBase.sol";
contract EndToEndTests is TestBase {
// Helper storage variable for fuzz test
- mapping(uint128 => address) lpsRequest;
+ mapping(uint256 => address) lpsRequest;
mapping(address => bool) manualLps;
mapping(address => uint256) lpShares;
@@ -26,7 +26,7 @@ contract EndToEndTests is TestBase {
uint256 totalInitialShares = shares1 + shares2 + shares3;
- uint128 requestId_;
+ uint256 requestId_;
// Simulate shares being sent to PM
pool.mint(pm, totalInitialShares);
@@ -134,8 +134,8 @@ contract EndToEndTests is TestBase {
function testFuzz_fullFLow_fixedExchangeRate(address[10] memory lps, bool[10] memory isManual, uint256[10] memory shares) external {
uint256 totalShares;
- uint128 inQueue;
- uint128 requestId_;
+ uint256 inQueue;
+ uint256 requestId_;
// Iterate through all users and add shares to pool manager
for (uint256 i = 0; i < 10; i++) {
@@ -178,12 +178,12 @@ contract EndToEndTests is TestBase {
withdrawalManager.processRedemptions(sharesToProcess);
// To avoid doing the same iteration as the function `processRedemptions`, fetch the queue state, then check it's integrity.
- ( uint128 nextRequestId_, uint128 lastRequestId_ ) = withdrawalManager.queue();
+ ( uint256 nextRequestId_, uint256 lastRequestId_ ) = withdrawalManager.queue();
uint256 sharesProcessed;
// First, check that the requests processes are correct
- for (uint128 i = 1; i < nextRequestId_; i++) {
+ for (uint256 i = 1; i < nextRequestId_; i++) {
address lp = lpsRequest[i];
assertRequest({ requestId: i, shares: 0, owner: address(0) });
@@ -205,7 +205,7 @@ contract EndToEndTests is TestBase {
}
// Finally, check that all subsequent requests are still on the queue.
- for (uint128 i = nextRequestId_ + 1; i <= lastRequestId_; i++) {
+ for (uint256 i = nextRequestId_ + 1; i <= lastRequestId_; i++) {
address lp = lpsRequest[i];
assertRequest({ requestId: i, shares: lpShares[lp], owner: lp });
diff --git a/tests/unit/AddShares.t.sol b/tests/unit/AddShares.t.sol
index 8d148d8..67e90e1 100644
--- a/tests/unit/AddShares.t.sol
+++ b/tests/unit/AddShares.t.sol
@@ -5,7 +5,7 @@ import { TestBase } from "../utils/TestBase.sol";
contract AddSharesTests is TestBase {
- event RequestCreated(uint128 indexed requestId, address indexed owner, uint256 shares);
+ event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
function setUp() public override {
super.setUp();
@@ -35,7 +35,7 @@ contract AddSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(1, lp);
- ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
+ ( , uint256 lastRequestId_ ) = withdrawalManager.queue();
assertEq(lastRequestId_, 2);
@@ -46,7 +46,7 @@ contract AddSharesTests is TestBase {
assertEq(owner_, lp);
assertEq(shares_, 1);
- (uint128[] memory requestIds, uint256[] memory requestShares) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIds, uint256[] memory requestShares) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds.length, 2);
assertEq(requestShares.length, 2);
@@ -63,7 +63,7 @@ contract AddSharesTests is TestBase {
}
function test_addShares_newRequestAddedToQueue() external {
- ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
+ ( , uint256 lastRequestId_ ) = withdrawalManager.queue();
assertEq(lastRequestId_, 0);
@@ -83,7 +83,7 @@ contract AddSharesTests is TestBase {
function test_addShares_newRequestAddedToQueue_manual() external {
withdrawalManager.__setManualWithdrawal(lp, true);
- ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
+ ( , uint256 lastRequestId_ ) = withdrawalManager.queue();
assertEq(lastRequestId_, 0);
@@ -101,7 +101,7 @@ contract AddSharesTests is TestBase {
}
function test_addShares_success() external {
- uint128 requestId_;
+ uint256 requestId_;
vm.expectEmit();
emit RequestCreated(1, lp, 1);
@@ -109,7 +109,7 @@ contract AddSharesTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(1, lp);
- ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
+ ( , uint256 lastRequestId_ ) = withdrawalManager.queue();
assertEq(lastRequestId_, 1);
diff --git a/tests/unit/ProcessExit.t.sol b/tests/unit/ProcessExit.t.sol
index 611944e..50bcc94 100644
--- a/tests/unit/ProcessExit.t.sol
+++ b/tests/unit/ProcessExit.t.sol
@@ -6,7 +6,7 @@ import { TestBase, console } from "../utils/TestBase.sol";
// TODO: Add ManualSharesDecreased event to tests
contract ProcessExitTests is TestBase {
- event RequestRemoved(uint128 indexed requestId);
+ event RequestRemoved(uint256 indexed requestId);
uint256 assetsDeposited = 100e18;
uint256 sharesToRedeem = 250e18;
@@ -98,7 +98,7 @@ contract ProcessExitTests is TestBase {
assertEq(pool.balanceOf(lp), sharesToRedeem);
assertEq(pool.balanceOf(wm), 0);
- ( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
+ ( uint256 lastRequestId_, ) = getLastRequestByOwner(lp);
assertEq(lastRequestId_, 2);
assertEq(withdrawalManager.requestIds(lp), lastRequestId_);
@@ -127,7 +127,7 @@ contract ProcessExitTests is TestBase {
assertEq(pool.balanceOf(lp), sharesToRedeem / 2);
assertEq(pool.balanceOf(wm), sharesToRedeem / 2);
- ( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
+ ( uint256 lastRequestId_, ) = getLastRequestByOwner(lp);
assertEq(lastRequestId_, 1);
assertEq(withdrawalManager.requestIds(lp), lastRequestId_);
diff --git a/tests/unit/ProcessRedemptions.t.sol b/tests/unit/ProcessRedemptions.t.sol
index 3db5e2c..f607c3c 100644
--- a/tests/unit/ProcessRedemptions.t.sol
+++ b/tests/unit/ProcessRedemptions.t.sol
@@ -7,9 +7,9 @@ import { TestBase } from "../utils/TestBase.sol";
// TODO: Add test case for reentrancy check
contract ProcessRedemptionsTests is TestBase {
- event RequestDecreased(uint128 indexed requestId, uint256 shares);
- event RequestProcessed(uint128 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
- event RequestRemoved(uint128 indexed requestId);
+ event RequestDecreased(uint256 indexed requestId, uint256 shares);
+ event RequestProcessed(uint256 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
+ event RequestRemoved(uint256 indexed requestId);
uint256 assetsDeposited = 100e18;
uint256 sharesLocked = 250e18;
@@ -93,7 +93,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
@@ -119,7 +119,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
@@ -158,7 +158,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked / 2);
@@ -193,8 +193,8 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked / 2);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
- ( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ ( uint256 lastRequestId_, ) = getLastRequestByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(lastRequestId_, 1);
@@ -225,7 +225,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(2 * sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked);
assertEq(withdrawalManager.manualSharesAvailable(lp), sharesLocked);
@@ -255,7 +255,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), 0);
assertEq(requestIdsLp_.length, 0);
@@ -287,8 +287,8 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked / 2);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
- ( uint128 lastRequestId_, ) = getLastRequestByOwner(lp);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ ( uint256 lastRequestId_, ) = getLastRequestByOwner(lp);
assertEq(withdrawalManager.totalShares(), sharesLocked / 2);
assertEq(lastRequestId_, 1);
@@ -323,7 +323,7 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(2 * sharesLocked);
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
assertEq(withdrawalManager.totalShares(), 0);
assertEq(requestIdsLp_.length, 0);
@@ -364,8 +364,8 @@ contract ProcessRedemptionsTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesLocked);
- (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requestsByOwner(lp);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2);
+ (uint256[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2);
assertEq(withdrawalManager.totalShares(), 0);
assertEq(requestIdsLp1_.length, 0);
@@ -386,9 +386,9 @@ contract ProcessRedemptionsTests is TestBase {
contract ComplexRedemptionTests is TestBase {
- event RequestDecreased(uint128 indexed requestId, uint256 shares);
- event RequestProcessed(uint128 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
- event RequestRemoved(uint128 indexed requestId);
+ event RequestDecreased(uint256 indexed requestId, uint256 shares);
+ event RequestProcessed(uint256 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
+ event RequestRemoved(uint256 indexed requestId);
function test_processRedemptions_complex() external {
uint256 totalAssets_ = 100e18;
@@ -431,10 +431,10 @@ contract ComplexRedemptionTests is TestBase {
vm.prank(poolDelegate);
withdrawalManager.processRedemptions(sharesToProcess_);
- (uint128[] memory requestIds2_, uint256[] memory shares2_) = withdrawalManager.requestsByOwner(address(2));
- (uint128[] memory requestIds3_, uint256[] memory shares3_) = withdrawalManager.requestsByOwner(address(3));
- (uint128[] memory requestIds5_, uint256[] memory shares5_) = withdrawalManager.requestsByOwner(address(5));
- (uint128[] memory requestIds6_, uint256[] memory shares6_) = withdrawalManager.requestsByOwner(address(6));
+ (uint256[] memory requestIds2_, uint256[] memory shares2_) = withdrawalManager.requestsByOwner(address(2));
+ (uint256[] memory requestIds3_, uint256[] memory shares3_) = withdrawalManager.requestsByOwner(address(3));
+ (uint256[] memory requestIds5_, uint256[] memory shares5_) = withdrawalManager.requestsByOwner(address(5));
+ (uint256[] memory requestIds6_, uint256[] memory shares6_) = withdrawalManager.requestsByOwner(address(6));
assertEq(requestIds2_.length, 0);
assertEq(shares2_.length, 0);
diff --git a/tests/unit/RemoveRequest.t.sol b/tests/unit/RemoveRequest.t.sol
index 9b7f371..20d8a02 100644
--- a/tests/unit/RemoveRequest.t.sol
+++ b/tests/unit/RemoveRequest.t.sol
@@ -5,7 +5,7 @@ import { TestBase } from "../utils/TestBase.sol";
contract RemoveRequestTests is TestBase {
- event RequestRemoved(uint128 indexed requestId);
+ event RequestRemoved(uint256 indexed requestId);
function setUp() public override {
super.setUp();
@@ -20,16 +20,16 @@ contract RemoveRequestTests is TestBase {
globals.__setFunctionPaused(true);
vm.expectRevert("WM:PAUSED");
- withdrawalManager.removeRequest(lp, new uint128[](0));
+ withdrawalManager.removeRequest(lp, new uint256[](0));
}
function test_removeRequest_notProtocolAdmin() external {
vm.expectRevert("WM:NOT_PD_OR_GOV_OR_OA");
- withdrawalManager.removeRequest(lp, new uint128[](0));
+ withdrawalManager.removeRequest(lp, new uint256[](0));
}
function test_removeRequest_notInQueue() external {
- uint128[] memory requestIds_ = new uint128[](1);
+ uint256[] memory requestIds_ = new uint256[](1);
requestIds_[0] = 1;
vm.prank(poolDelegate);
@@ -38,7 +38,7 @@ contract RemoveRequestTests is TestBase {
}
function test_removeRequest_failedTransfer() external {
- uint128[] memory requestIds_ = new uint128[](1);
+ uint256[] memory requestIds_ = new uint256[](1);
requestIds_[0] = 1;
vm.prank(pm);
@@ -52,14 +52,14 @@ contract RemoveRequestTests is TestBase {
}
function test_removeRequest_success() external {
- uint128 lastRequestIdLp_;
+ uint256 lastRequestIdLp_;
vm.prank(pm);
withdrawalManager.addShares(2, lp);
assertEq(withdrawalManager.userEscrowedShares(lp), 2);
- ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
+ ( , uint256 lastRequestId_ ) = withdrawalManager.queue();
( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
@@ -70,7 +70,7 @@ contract RemoveRequestTests is TestBase {
assertEq(lastRequestId_, 1);
assertEq(lastRequestIdLp_, lastRequestId_);
- uint128[] memory requestIds = new uint128[](1);
+ uint256[] memory requestIds = new uint256[](1);
requestIds[0] = lastRequestId_;
vm.expectEmit();
@@ -84,7 +84,7 @@ contract RemoveRequestTests is TestBase {
( owner_, shares_ ) = withdrawalManager.requests(lastRequestId_);
( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
- ( uint128 lastRequestByOwner_, ) = getLastRequestByOwner(owner_);
+ ( uint256 lastRequestByOwner_, ) = getLastRequestByOwner(owner_);
assertEq(lastRequestId_, 1);
assertEq(shares_, 0);
@@ -97,7 +97,7 @@ contract RemoveRequestTests is TestBase {
}
function test_removeRequest_multiple_requests_success() external {
- uint128 lastRequestIdLp_;
+ uint256 lastRequestIdLp_;
vm.startPrank(pm);
withdrawalManager.addShares(1, lp);
@@ -105,7 +105,7 @@ contract RemoveRequestTests is TestBase {
withdrawalManager.addShares(1, lp);
vm.stopPrank();
- ( , uint128 lastRequestId_ ) = withdrawalManager.queue();
+ ( , uint256 lastRequestId_ ) = withdrawalManager.queue();
( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
@@ -118,7 +118,7 @@ contract RemoveRequestTests is TestBase {
assertEq(withdrawalManager.userEscrowedShares(lp), 3);
- uint128[] memory requestIds = new uint128[](1);
+ uint256[] memory requestIds = new uint256[](1);
requestIds[0] = 2;
vm.expectEmit();
@@ -132,7 +132,7 @@ contract RemoveRequestTests is TestBase {
( owner_, shares_ ) = withdrawalManager.requests(lastRequestId_);
( lastRequestIdLp_, ) = getLastRequestByOwner(lp);
- ( uint128 lastRequestByOwner_, ) = getLastRequestByOwner(owner_);
+ ( uint256 lastRequestByOwner_, ) = getLastRequestByOwner(owner_);
assertEq(lastRequestId_, 3);
assertEq(shares_, 1);
@@ -143,7 +143,7 @@ contract RemoveRequestTests is TestBase {
assertEq(withdrawalManager.userEscrowedShares(lp), 2);
- requestIds = new uint128[](2);
+ requestIds = new uint256[](2);
requestIds[0] = 1;
requestIds[1] = 3;
diff --git a/tests/unit/RemoveShares.t.sol b/tests/unit/RemoveShares.t.sol
index 153575b..6737478 100644
--- a/tests/unit/RemoveShares.t.sol
+++ b/tests/unit/RemoveShares.t.sol
@@ -5,8 +5,8 @@ import { TestBase } from "../utils/TestBase.sol";
contract RemoveSharesTests is TestBase {
- event RequestDecreased(uint128 indexed requestId, uint256 shares);
- event RequestRemoved(uint128 indexed requestId);
+ event RequestDecreased(uint256 indexed requestId, uint256 shares);
+ event RequestRemoved(uint256 indexed requestId);
function setUp() public override {
super.setUp();
@@ -30,7 +30,7 @@ contract RemoveSharesTests is TestBase {
}
function test_removeShares_notInQueue() external {
- ( uint128 lastRequestId_,) = getLastRequestByOwner(pm);
+ ( uint256 lastRequestId_,) = getLastRequestByOwner(pm);
assertEq(lastRequestId_, 0);
@@ -60,7 +60,7 @@ contract RemoveSharesTests is TestBase {
}
function test_removeShares_success_decreaseRequest() external {
- uint128 lastRequestId_;
+ uint256 lastRequestId_;
vm.prank(pm);
withdrawalManager.addShares(2, lp);
@@ -88,7 +88,7 @@ contract RemoveSharesTests is TestBase {
}
function test_removeShares_success_cancelRequest() external {
- uint128 lastRequestId_;
+ uint256 lastRequestId_;
vm.prank(pm);
withdrawalManager.addShares(2, lp);
@@ -127,12 +127,12 @@ contract RemoveSharesTests is TestBase {
assertEq(withdrawalManager.userEscrowedShares(lp), 7);
assertEq(withdrawalManager.totalShares() , 7);
- ( uint128[] memory requestIds_, uint256[] memory shares_ ) = withdrawalManager.requestsByOwner(lp);
+ ( uint256[] memory requestIds_, uint256[] memory shares_ ) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 3);
assertEq(shares_.length , 3);
- ( uint128 lastRequestId_, uint256 lastShares_ ) = getLastRequestByOwner(lp);
+ ( uint256 lastRequestId_, uint256 lastShares_ ) = getLastRequestByOwner(lp);
assertEq(lastRequestId_, 3);
assertEq(lastShares_ , 1);
@@ -182,12 +182,12 @@ contract RemoveSharesTests is TestBase {
assertEq(withdrawalManager.userEscrowedShares(lp), 7);
assertEq(withdrawalManager.totalShares() , 7);
- ( uint128[] memory requestIds_, uint256[] memory shares_ ) = withdrawalManager.requestsByOwner(lp);
+ ( uint256[] memory requestIds_, uint256[] memory shares_ ) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 3);
assertEq(shares_.length , 3);
- ( uint128 lastRequestId_, uint256 lastShares_ ) = getLastRequestByOwner(lp);
+ ( uint256 lastRequestId_, uint256 lastShares_ ) = getLastRequestByOwner(lp);
assertEq(lastRequestId_, 3);
assertEq(lastShares_ , 1);
diff --git a/tests/unit/SortedArray/Push.t.sol b/tests/unit/SortedArray/Push.t.sol
index 333bfbb..d33d27b 100644
--- a/tests/unit/SortedArray/Push.t.sol
+++ b/tests/unit/SortedArray/Push.t.sol
@@ -16,7 +16,7 @@ contract PushTests is SortedArrayTestBase {
function test_push_singleValue() external {
array.push(3);
- uint128[] memory expectedValues = new uint128[](1);
+ uint256[] memory expectedValues = new uint256[](1);
expectedValues[0] = 3;
assertArray(expectedValues);
@@ -30,7 +30,7 @@ contract PushTests is SortedArrayTestBase {
array.push(3);
array.push(7);
- uint128[] memory expectedValues = new uint128[](3);
+ uint256[] memory expectedValues = new uint256[](3);
expectedValues[0] = 1;
expectedValues[1] = 3;
expectedValues[2] = 7;
diff --git a/tests/unit/SortedArray/Remove.t.sol b/tests/unit/SortedArray/Remove.t.sol
index fc8f39d..8bac4d0 100644
--- a/tests/unit/SortedArray/Remove.t.sol
+++ b/tests/unit/SortedArray/Remove.t.sol
@@ -9,7 +9,7 @@ contract RemoveTests is SortedArrayTestBase {
array.push(1);
array.remove(1);
- uint128[] memory expectedValues = new uint128[](0);
+ uint256[] memory expectedValues = new uint256[](0);
assertArray(expectedValues);
assertEq(array.length(), 0);
@@ -22,7 +22,7 @@ contract RemoveTests is SortedArrayTestBase {
array.remove(2);
- uint128[] memory expectedValues = new uint128[](2);
+ uint256[] memory expectedValues = new uint256[](2);
expectedValues[0] = 1;
expectedValues[1] = 3;
@@ -34,7 +34,7 @@ contract RemoveTests is SortedArrayTestBase {
array.remove(1);
- expectedValues = new uint128[](1);
+ expectedValues = new uint256[](1);
expectedValues[0] = 3;
assertArray(expectedValues);
@@ -44,7 +44,7 @@ contract RemoveTests is SortedArrayTestBase {
array.remove(3);
- assertArray(new uint128[](0));
+ assertArray(new uint256[](0));
assertEq(array.length(), 0);
}
diff --git a/tests/unit/UpdateShares.t.sol b/tests/unit/UpdateShares.t.sol
index bea8a77..9ce1a39 100644
--- a/tests/unit/UpdateShares.t.sol
+++ b/tests/unit/UpdateShares.t.sol
@@ -61,7 +61,7 @@ contract UpdateSharesFailureTests is TestBase {
vm.prank(pm);
withdrawalManager.addShares(1, lp);
- uint128[] memory requestIds = new uint128[](1);
+ uint256[] memory requestIds = new uint256[](1);
requestIds[0] = 1;
vm.prank(governor);
@@ -75,11 +75,11 @@ contract UpdateSharesFailureTests is TestBase {
contract UpdateSharesSuccessTests is TestBase {
- event RequestCreated(uint128 indexed requestId, address indexed owner, uint256 shares);
+ event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
- event RequestDecreased(uint128 indexed requestId, uint256 shares);
+ event RequestDecreased(uint256 indexed requestId, uint256 shares);
- event RequestRemoved(uint128 indexed requestId);
+ event RequestRemoved(uint256 indexed requestId);
function setUp() public override {
super.setUp();
@@ -97,11 +97,11 @@ contract UpdateSharesSuccessTests is TestBase {
function test_updateShares_increase() external {
vm.prank(pm);
- uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
+ uint256 requestIdBefore_ = withdrawalManager.addShares(1, lp);
assertEq(withdrawalManager.userEscrowedShares(lp), 1);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
@@ -114,9 +114,9 @@ contract UpdateSharesSuccessTests is TestBase {
emit RequestRemoved(requestIdBefore_);
emit RequestCreated(requestIdBefore_ + 1, lp, 2);
// Increase shares from 1 to 2 creates a new request and removes the old one.
- uint128 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 2);
+ uint256 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 2);
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(newRequestId_, 2, "Request ID should be 2");
assertEq(requestIds_.length, 1, "Request IDs length should be 1");
@@ -129,7 +129,7 @@ contract UpdateSharesSuccessTests is TestBase {
function test_updateShares_remove_request() external{
vm.prank(pm);
- uint128 requestId_ = withdrawalManager.addShares(1, lp);
+ uint256 requestId_ = withdrawalManager.addShares(1, lp);
assertEq(withdrawalManager.userEscrowedShares(lp), 1);
@@ -137,7 +137,7 @@ contract UpdateSharesSuccessTests is TestBase {
vm.expectEmit();
emit RequestRemoved(requestId_);
- uint128 newRequestId_ = withdrawalManager.updateShares(requestId_, 0);
+ uint256 newRequestId_ = withdrawalManager.updateShares(requestId_, 0);
assertEq(newRequestId_, 0, "Request ID should be 0");
@@ -146,11 +146,11 @@ contract UpdateSharesSuccessTests is TestBase {
function test_updateShares_decrease() external {
vm.prank(pm);
- uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
+ uint256 requestIdBefore_ = withdrawalManager.addShares(2, lp);
assertEq(withdrawalManager.userEscrowedShares(lp), 2);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID Before should be 1 (addShares)");
assertEq(requestIdsBefore_.length, 1, "Request IDs length before should be 1");
@@ -162,9 +162,9 @@ contract UpdateSharesSuccessTests is TestBase {
vm.expectEmit();
emit RequestDecreased(requestIdBefore_, 1);
// Decrease shares from 2 to 1 keeps the same request ID.
- uint128 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 1);
+ uint256 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 1);
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(newRequestId_, 1, "Request ID should be 1 (addShares)");
assertEq(requestIds_.length, 1, "Request IDs length should be 1");
@@ -224,9 +224,9 @@ contract UpdateSharesSuccessTests is TestBase {
assertEq(withdrawalManager.userEscrowedShares(lp3_), requestAmount1_ + requestAmount2_);
{
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
- (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3_);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
+ (uint256[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3_);
assertEq(requestIdsLp_.length, 2, "LP request IDs length should be 2");
assertEq(requestIdsLp2_.length, 2, "LP2 request IDs length should be 2");
@@ -253,36 +253,36 @@ contract UpdateSharesSuccessTests is TestBase {
{
// Decrease a request
vm.prank(lp);
- uint128 requestIdLp_ = 1;
+ uint256 requestIdLp_ = 1;
vm.expectEmit();
emit RequestDecreased(requestIdLp_, 1);
- uint128 updatedRequestIdLp_ = withdrawalManager.updateShares(requestIdLp_, requestAmount1_ - 1);
+ uint256 updatedRequestIdLp_ = withdrawalManager.updateShares(requestIdLp_, requestAmount1_ - 1);
assertEq(updatedRequestIdLp_, requestIdLp_, "Updated request ID should be 1");
// Remove a request
vm.prank(lp2_);
- uint128 requestIdLp2_ = 3;
+ uint256 requestIdLp2_ = 3;
vm.expectEmit();
emit RequestRemoved(requestIdLp2_);
- uint128 updatedRequestIdLp2_ = withdrawalManager.updateShares(requestIdLp2_, 0);
+ uint256 updatedRequestIdLp2_ = withdrawalManager.updateShares(requestIdLp2_, 0);
assertEq(updatedRequestIdLp2_, 0, "Updated request ID should be 0");
// Increase a request
vm.prank(lp3_);
- uint128 requestIdLp3_ = 5;
+ uint256 requestIdLp3_ = 5;
vm.expectEmit();
emit RequestRemoved(requestIdLp3_);
emit RequestCreated(7, lp3_, requestAmount2_ + 10);
- uint128 updatedRequestIdLp3 = withdrawalManager.updateShares(requestIdLp3_, requestAmount2_ + 10);
+ uint256 updatedRequestIdLp3 = withdrawalManager.updateShares(requestIdLp3_, requestAmount2_ + 10);
assertEq(updatedRequestIdLp3, 7, "Updated request ID should be 7");
vm.stopPrank();
}
{
- (uint128[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
- (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3_);
+ (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
+ (uint256[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3_);
assertEq(requestIdsLp_.length, 2, "LP request IDs length should be 2");
assertEq(requestIdsLp2_.length, 1, "LP2 request IDs length should be 1");
@@ -329,7 +329,7 @@ contract UpdateSharesBatchFailureTests is TestBase {
}
function test_updateSharesBatch_invalid_array_lengths() external {
- uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](1);
uint256[] memory sharesToUpdate_ = new uint256[](2);
vm.expectRevert("WM:USB:ARRAY_LENGTH_MISMATCH");
@@ -337,7 +337,7 @@ contract UpdateSharesBatchFailureTests is TestBase {
}
function test_updateSharesBatch_invalid_request() external {
- uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](1);
uint256[] memory sharesToUpdate_ = new uint256[](1);
vm.expectRevert("WM:US:INVALID_REQUEST");
@@ -345,11 +345,11 @@ contract UpdateSharesBatchFailureTests is TestBase {
}
function test_updateSharesBatch_invalid_owner() external {
- uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](1);
uint256[] memory sharesToUpdate_ = new uint256[](1);
vm.prank(pm);
- uint128 requestId_ = withdrawalManager.addShares(1, lp);
+ uint256 requestId_ = withdrawalManager.addShares(1, lp);
requestIdsToUpdate_[0] = requestId_;
sharesToUpdate_[0] = 2;
@@ -360,11 +360,11 @@ contract UpdateSharesBatchFailureTests is TestBase {
}
function test_updateSharesBatch_no_change_in_shares() external {
- uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](1);
uint256[] memory sharesToUpdate_ = new uint256[](1);
vm.prank(pm);
- uint128 requestId_ = withdrawalManager.addShares(1, lp);
+ uint256 requestId_ = withdrawalManager.addShares(1, lp);
requestIdsToUpdate_[0] = requestId_;
sharesToUpdate_[0] = 1;
@@ -377,11 +377,11 @@ contract UpdateSharesBatchFailureTests is TestBase {
contract UpdateSharesBatchSuccessTests is TestBase {
- event RequestCreated(uint128 indexed requestId, address indexed owner, uint256 shares);
+ event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
- event RequestDecreased(uint128 indexed requestId, uint256 shares);
+ event RequestDecreased(uint256 indexed requestId, uint256 shares);
- event RequestRemoved(uint128 indexed requestId);
+ event RequestRemoved(uint256 indexed requestId);
function setUp() public override {
super.setUp();
@@ -400,11 +400,11 @@ contract UpdateSharesBatchSuccessTests is TestBase {
function test_updateSharesBatch_increase_single_request() external {
vm.prank(pm);
- uint128 requestIdBefore_ = withdrawalManager.addShares(1, lp);
+ uint256 requestIdBefore_ = withdrawalManager.addShares(1, lp);
assertEq(withdrawalManager.userEscrowedShares(lp), 1);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
@@ -412,7 +412,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
assertEq(sharesBefore_[0], 1, "Shares should be 1");
- uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](1);
requestIdsToUpdate_[0] = requestIdBefore_;
uint256[] memory sharesToUpdate_ = new uint256[](1);
@@ -424,12 +424,12 @@ contract UpdateSharesBatchSuccessTests is TestBase {
emit RequestCreated(requestIdBefore_ + 1, lp, 2);
// Increase shares creates a new request and removes the old one.
- uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
assertEq(newRequestIds_.length, 1, "New request IDs length should be 1");
assertEq(newRequestIds_[0], 2, "New request ID should be 2");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 1, "Request IDs length should be 1");
assertEq(shares_.length, 1, "Shares length should be 1");
@@ -441,13 +441,13 @@ contract UpdateSharesBatchSuccessTests is TestBase {
function test_updateSharesBatch_increase_multiple_requests() external {
vm.startPrank(pm);
- uint128 requestId1Before_ = withdrawalManager.addShares(1, lp);
- uint128 requestId2Before_ = withdrawalManager.addShares(1, lp);
+ uint256 requestId1Before_ = withdrawalManager.addShares(1, lp);
+ uint256 requestId2Before_ = withdrawalManager.addShares(1, lp);
vm.stopPrank();
assertEq(withdrawalManager.userEscrowedShares(lp), 2);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
assertEq(requestId2Before_, 2, "Request ID should be 2");
@@ -458,7 +458,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
assertEq(sharesBefore_[1], 1, "Shares should be 1");
- uint128[] memory requestIdsToUpdate_ = new uint128[](2);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](2);
requestIdsToUpdate_[0] = requestId1Before_;
requestIdsToUpdate_[1] = requestId2Before_;
@@ -476,13 +476,13 @@ contract UpdateSharesBatchSuccessTests is TestBase {
emit RequestCreated(4, lp, 3);
// Increase shares creates a new request and removes the old one.
- uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
assertEq(newRequestIds_[0], 3, "New request ID should be 3");
assertEq(newRequestIds_[1], 4, "New request ID should be 4");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 2, "Request IDs length should be 2");
assertEq(shares_.length, 2, "Shares length should be 2");
@@ -496,11 +496,11 @@ contract UpdateSharesBatchSuccessTests is TestBase {
function test_updateSharesBatch_decrease_single_request() external {
vm.prank(pm);
- uint128 requestIdBefore_ = withdrawalManager.addShares(2, lp);
+ uint256 requestIdBefore_ = withdrawalManager.addShares(2, lp);
assertEq(withdrawalManager.userEscrowedShares(lp), 2);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIdBefore_, 1, "Request ID should be 1");
assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
@@ -508,7 +508,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
assertEq(sharesBefore_[0], 2, "Shares should be 2");
- uint128[] memory requestIdsToUpdate_ = new uint128[](1);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](1);
requestIdsToUpdate_[0] = requestIdBefore_;
uint256[] memory sharesToUpdate_ = new uint256[](1);
@@ -519,12 +519,12 @@ contract UpdateSharesBatchSuccessTests is TestBase {
emit RequestDecreased(requestIdBefore_, 1);
// Decrease in shares updates the existing request.
- uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
assertEq(newRequestIds_.length, 1, "New request IDs length should be 1");
assertEq(newRequestIds_[0], 1, "New request ID should be 1");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 1, "Request IDs length should be 1");
assertEq(shares_.length, 1, "Shares length should be 1");
@@ -536,13 +536,13 @@ contract UpdateSharesBatchSuccessTests is TestBase {
function test_updateSharesBatch_decrease_multiple_requests() external {
vm.startPrank(pm);
- uint128 requestId1Before_ = withdrawalManager.addShares(2, lp);
- uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
+ uint256 requestId1Before_ = withdrawalManager.addShares(2, lp);
+ uint256 requestId2Before_ = withdrawalManager.addShares(2, lp);
vm.stopPrank();
assertEq(withdrawalManager.userEscrowedShares(lp), 4);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
assertEq(requestId2Before_, 2, "Request ID should be 1");
@@ -553,7 +553,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
assertEq(sharesBefore_[1], 2, "Shares should be 2");
- uint128[] memory requestIdsToUpdate_ = new uint128[](2);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](2);
requestIdsToUpdate_[0] = requestId1Before_;
requestIdsToUpdate_[1] = requestId2Before_;
@@ -569,13 +569,13 @@ contract UpdateSharesBatchSuccessTests is TestBase {
emit RequestDecreased(requestId2Before_, 1);
// Decrease in shares updates the existing requests.
- uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
assertEq(newRequestIds_[0], 1, "New request ID should be 1");
assertEq(newRequestIds_[1], 2, "New request ID should be 2");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 2, "Request IDs length should be 2");
assertEq(shares_.length, 2, "Shares length should be 2");
@@ -589,13 +589,13 @@ contract UpdateSharesBatchSuccessTests is TestBase {
function test_updateSharesBatch_increase_and_decrease_multiple_requests() external {
vm.startPrank(pm);
- uint128 requestId1Before_ = withdrawalManager.addShares(1, lp);
- uint128 requestId2Before_ = withdrawalManager.addShares(2, lp);
+ uint256 requestId1Before_ = withdrawalManager.addShares(1, lp);
+ uint256 requestId2Before_ = withdrawalManager.addShares(2, lp);
vm.stopPrank();
assertEq(withdrawalManager.userEscrowedShares(lp), 3);
- (uint128[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestId1Before_, 1, "Request ID should be 1");
assertEq(requestId2Before_, 2, "Request ID should be 1");
@@ -606,7 +606,7 @@ contract UpdateSharesBatchSuccessTests is TestBase {
assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
assertEq(sharesBefore_[1], 2, "Shares should be 2");
- uint128[] memory requestIdsToUpdate_ = new uint128[](2);
+ uint256[] memory requestIdsToUpdate_ = new uint256[](2);
requestIdsToUpdate_[0] = requestId1Before_;
requestIdsToUpdate_[1] = requestId2Before_;
@@ -623,13 +623,13 @@ contract UpdateSharesBatchSuccessTests is TestBase {
emit RequestDecreased(requestId2Before_, 1);
// Decrease in shares updates the existing requests.
- uint128[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
+ uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
assertEq(newRequestIds_[0], 3, "New request ID should be 3");
assertEq(newRequestIds_[1], 2, "New request ID should be 2");
- (uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
+ (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
assertEq(requestIds_.length, 2, "Request IDs length should be 1");
assertEq(shares_.length, 2, "Shares length should be 1");
diff --git a/tests/unit/ViewFunctions.t.sol b/tests/unit/ViewFunctions.t.sol
index b66601a..0260240 100644
--- a/tests/unit/ViewFunctions.t.sol
+++ b/tests/unit/ViewFunctions.t.sol
@@ -45,10 +45,10 @@ contract ViewFunctionsTests is TestBase {
withdrawalManager.__setQueue(1, 5);
- (uint128[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requestsByOwner(lp1);
- (uint128[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2);
- (uint128[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3);
- (uint128[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requestsByOwner(lp4);
+ (uint256[] memory requestIdsLp1_, uint256[] memory sharesLp1_) = withdrawalManager.requestsByOwner(lp1);
+ (uint256[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2);
+ (uint256[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3);
+ (uint256[] memory requestIdsLp4_, uint256[] memory sharesLp4_) = withdrawalManager.requestsByOwner(lp4);
assertEq(requestIdsLp1_.length, 2);
diff --git a/tests/utils/Harnesses.sol b/tests/utils/Harnesses.sol
index 3a2e78c..7284b8c 100644
--- a/tests/utils/Harnesses.sol
+++ b/tests/utils/Harnesses.sol
@@ -19,14 +19,14 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
isManualWithdrawal[owner_] = isManual_;
}
- function __setLastRequest(address owner_, uint128 requestId_) external {
+ function __setLastRequest(address owner_, uint256 requestId_) external {
SortedArray.push(userRequests[owner_], requestId_);
- queue.lastRequestId = requestId_;
+ queue.lastRequestId = _toUint128(requestId_);
}
- function __setQueue(uint128 nextRequestId_, uint128 lastRequestId_) external {
- queue.nextRequestId = nextRequestId_;
- queue.lastRequestId = lastRequestId_;
+ function __setQueue(uint256 nextRequestId_, uint256 lastRequestId_) external {
+ queue.nextRequestId = _toUint128(nextRequestId_);
+ queue.lastRequestId = _toUint128(lastRequestId_);
}
function __setRequestLegacy(uint128 requestId_, address owner_, uint256 shares_) external {
@@ -54,11 +54,11 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
if (requestCount_ < currentRequestCount_) {
for (uint256 i = requestCount_; i < currentRequestCount_; i++) {
- SortedArray.remove(userRequests[owner_], uint128(i));
+ SortedArray.remove(userRequests[owner_], _toUint128(i));
}
} else {
for (uint256 i = currentRequestCount_; i > requestCount_; i++) {
- SortedArray.push(userRequests[owner_], uint128(i));
+ SortedArray.push(userRequests[owner_], _toUint128(i));
}
}
@@ -71,15 +71,15 @@ contract SortedArrayHarness {
SortedArray.Array array;
- function push(uint128 value_) external {
+ function push(uint256 value_) external {
SortedArray.push(array, value_);
}
- function remove(uint128 value_) external {
+ function remove(uint256 value_) external {
SortedArray.remove(array, value_);
}
- function get(uint256 index_) external view returns (uint128) {
+ function get(uint256 index_) external view returns (uint256) {
return SortedArray.get(array, index_);
}
@@ -87,12 +87,12 @@ contract SortedArrayHarness {
return SortedArray.length(array);
}
- function getAllValues() external view returns (uint128[] memory) {
+ function getAllValues() external view returns (uint256[] memory) {
return SortedArray.getAllValues(array);
}
- function getLast() external view returns (uint128) {
+ function getLast() external view returns (uint256) {
return SortedArray.getLast(array);
}
-}
+}
diff --git a/tests/utils/TestBase.sol b/tests/utils/TestBase.sol
index a6a0a68..0050d0c 100644
--- a/tests/utils/TestBase.sol
+++ b/tests/utils/TestBase.sol
@@ -73,22 +73,22 @@ contract TestBase is Test {
wm = address(withdrawalManager);
}
- function assertRequest(uint128 requestId, address owner, uint256 shares) internal {
+ function assertRequest(uint256 requestId, address owner, uint256 shares) internal {
( address owner_, uint256 shares_ ) = withdrawalManager.requests(requestId);
assertEq(owner_, owner);
assertEq(shares_, shares);
}
- function assertQueue(uint128 nextRequestId, uint128 lastRequestId) internal {
- ( uint128 nextRequestId_, uint128 lastRequestId_ ) = withdrawalManager.queue();
+ function assertQueue(uint256 nextRequestId, uint256 lastRequestId) internal {
+ ( uint256 nextRequestId_, uint256 lastRequestId_ ) = withdrawalManager.queue();
assertEq(nextRequestId_, nextRequestId);
assertEq(lastRequestId_, lastRequestId);
}
- function getLastRequestByOwner(address owner) internal view returns (uint128 lastRequestId, uint256 shares) {
- ( uint128[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(owner);
+ function getLastRequestByOwner(address owner) internal view returns (uint256 lastRequestId, uint256 shares) {
+ ( uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(owner);
return requestIds_.length == 0 ? (0,0) : (requestIds_[requestIds_.length - 1], shares_[shares_.length - 1]);
}
@@ -103,17 +103,17 @@ contract SortedArrayTestBase is Test {
array = new SortedArrayHarness();
}
- function assertArray(uint128[] memory values) internal {
+ function assertArray(uint256[] memory values) internal {
assertEq(array.length(), values.length);
- uint128[] memory arrayValues = array.getAllValues();
+ uint256[] memory arrayValues = array.getAllValues();
for (uint256 i = 0; i < values.length; i++) {
assertEq(arrayValues[i], values[i]);
}
}
- function assertElementAtIndex(uint256 index, uint128 value) internal {
+ function assertElementAtIndex(uint256 index, uint256 value) internal {
assertEq(array.get(index), value);
}
From cd8f632508694b1a05372834db1aec43586292ed Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Wed, 3 Sep 2025 09:17:01 +0400
Subject: [PATCH 09/21] chore: update format (#39)
---
contracts/MapleWithdrawalManager.sol | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 970c66d..21b545d 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -288,7 +288,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
lastRequestId_ = ++queue.lastRequestId;
queue.requests[_toUint128(lastRequestId_)] = WithdrawalRequest(owner_, shares_);
- userEscrowedShares[owner_] += shares_;
+ userEscrowedShares[owner_] += shares_;
SortedArray.push(userRequests[owner_], lastRequestId_);
@@ -315,7 +315,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_removeRequest(owner_, requestId_);
} else {
queue.requests[_toUint128(requestId_)].shares = sharesRemaining_;
- userEscrowedShares[owner_] -= sharesToRemove_;
+ userEscrowedShares[owner_] -= sharesToRemove_;
emit RequestDecreased(requestId_, sharesToRemove_);
}
@@ -404,7 +404,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
} else {
// Update the withdrawal request.
queue.requests[_toUint128(requestId_)].shares = sharesRemaining_;
- userEscrowedShares[request_.owner] -= processedShares_;
+ userEscrowedShares[request_.owner] -= processedShares_;
emit RequestDecreased(requestId_, processedShares_);
}
From 20aaf69fcf5742865562f389ec75ff88e1d4f92a Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Wed, 10 Sep 2025 17:42:00 +0400
Subject: [PATCH 10/21] feat: Add `removeSharesById()` Functionality (#40)
* feat: Add removeSharesById Functionality
* fix: err messages + test cases
* feat: Add additional require
* chore: format
* fix: test cases
* feat: Update Interface and Tests
* feat: Update Test with multiple LPs
* chore: fix formatting
* chore: fix stack too deep
* chore: fix test name
* chore: update impl
* chore: address PR comments
* chore: fix events
---
contracts/MapleWithdrawalManager.sol | 47 +-
.../interfaces/IMapleWithdrawalManager.sol | 33 +-
tests/unit/AddShares.t.sol | 6 +-
tests/unit/ProcessExit.t.sol | 4 +-
tests/unit/ProcessRedemptions.t.sol | 12 +-
tests/unit/RemoveRequest.t.sol | 2 -
tests/unit/RemoveShares.t.sol | 7 +-
tests/unit/RemoveSharesById.t.sol | 338 +++++++++
tests/unit/SetManualWithdrawal.t.sol | 2 -
tests/unit/UpdateShares.t.sol | 644 ------------------
tests/utils/Mocks.sol | 2 +-
tests/utils/TestBase.sol | 6 +
12 files changed, 377 insertions(+), 726 deletions(-)
create mode 100644 tests/unit/RemoveSharesById.t.sol
delete mode 100644 tests/unit/UpdateShares.t.sol
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 21b545d..67ca43e 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -209,7 +209,8 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
function removeRequest(
address owner_,
uint256[] calldata requestIds_
- ) external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins
+ )
+ external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins
{
require(owner_ != address(0), "WM:RR:ZERO_OWNER");
require(requestIds_.length > 0, "WM:RR:ZERO_REQUESTS");
@@ -244,40 +245,22 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
/*** Unprivileged External Functions ***/
/**************************************************************************************************************************************/
- function updateShares(
+ function removeSharesById(
uint256 requestId_,
- uint256 newSharesTotal_
- ) public override whenProtocolNotPaused nonReentrant returns (uint256 updatedRequestId_)
+ uint256 sharesToRemove_
+ )
+ public override whenProtocolNotPaused nonReentrant returns (uint256 sharesReturned_, uint256 sharesRemaining_)
{
WithdrawalRequest memory request_ = queue.requests[_toUint128(requestId_)];
- require(request_.owner != address(0), "WM:US:INVALID_REQUEST");
- require(request_.owner == msg.sender, "WM:US:NOT_OWNER");
- require(request_.shares != newSharesTotal_, "WM:US:NO_CHANGE");
-
- uint256 sharesToRemove_ = newSharesTotal_ < request_.shares ? request_.shares - newSharesTotal_ : request_.shares;
+ require(request_.owner != address(0), "WM:RSBI:INVALID_REQUEST");
+ require(request_.owner == msg.sender, "WM:RSBI:NOT_OWNER");
+ require(sharesToRemove_ != 0, "WM:RSBI:NO_CHANGE");
+ require(sharesToRemove_ <= request_.shares, "WM:RSBI:INSUFFICIENT_SHARES");
// Removes shares and will cancel the request if there are no shares remaining.
- _removeShares(requestId_, sharesToRemove_, request_.owner, request_.shares);
-
- if (newSharesTotal_ > request_.shares)
- updatedRequestId_ = _addRequest(request_.owner, newSharesTotal_);
- else
- updatedRequestId_ = newSharesTotal_ == 0 ? 0 : requestId_;
- }
-
- function updateSharesBatch(
- uint256[] memory requestIds_,
- uint256[] calldata newSharesTotals_
- ) external override whenProtocolNotPaused returns (uint256[] memory updatedRequestIds_)
- {
- require(requestIds_.length == newSharesTotals_.length, "WM:USB:ARRAY_LENGTH_MISMATCH");
-
- for (uint256 i = 0; i < requestIds_.length; ++i) {
- requestIds_[i] = updateShares(requestIds_[i], newSharesTotals_[i]);
- }
-
- updatedRequestIds_ = requestIds_;
+ sharesReturned_ = _removeShares(requestId_, sharesToRemove_, request_.owner, request_.shares);
+ sharesRemaining_ = request_.shares - sharesToRemove_;
}
/**************************************************************************************************************************************/
@@ -300,11 +283,13 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
emit RequestCreated(lastRequestId_, owner_, shares_);
}
- function _removeShares(uint256 requestId_,
+ function _removeShares(
+ uint256 requestId_,
uint256 sharesToRemove_,
address owner_,
uint256 currentShares_
- ) internal returns (uint256 sharesReturned_)
+ )
+ internal returns (uint256 sharesReturned_)
{
uint256 sharesRemaining_ = currentShares_ - sharesToRemove_;
diff --git a/contracts/interfaces/IMapleWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol
index eaa2b12..fb24d4f 100644
--- a/contracts/interfaces/IMapleWithdrawalManager.sol
+++ b/contracts/interfaces/IMapleWithdrawalManager.sol
@@ -99,6 +99,15 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
*/
function removeShares(uint256 shares, address owner) external returns (uint256 sharesReturned);
+ /**
+ * @dev Remove shares from a specific withdrawal request.
+ * @param requestId Identifier of the withdrawal request that is being updated.
+ * @param sharesToRemove Amount of shares to remove from the request.
+ * @return sharesReturned Amount of shares that were returned.
+ * @return sharesRemaining Amount of shares remaining in the request.
+ */
+ function removeSharesById(uint256 requestId, uint256 sharesToRemove) external returns (uint256 sharesReturned, uint256 sharesRemaining);
+
/**
* @dev Removes withdrawal requests from the queue.
* Can only be called by the pool delegate.
@@ -114,30 +123,6 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
*/
function setManualWithdrawal(address account, bool isManual) external;
-
- /**
- * @dev Updates the total amount of shares pending redemption.
- * 1. If newSharesTotal is zero then the request will be removed.
- * 2. If newSharesTotal is less than the current shares then the request will be decreased.
- * 3. If newSharesTotal is greater than the current shares then the current request will be cancelled
- * and a new request will be created at the end of the queue.
- * @param requestId Identifier of the withdrawal request that is being updated.
- * @param newSharesTotal New total amount of shares pending redemption.
- * @return currentRequestId Identifier of the withdrawal request that was updated or created.
- */
- function updateShares(uint256 requestId, uint256 newSharesTotal) external returns (uint256 currentRequestId);
-
- /**
- * @dev Updates the total amount of shares pending redemption in batch.
- * @param requestIds Array of identifiers of the withdrawal requests that are being updated.
- * @param newSharesTotals Array of new total amounts of shares pending redemption.
- * @return currentRequestIds Array of identifiers of the withdrawal requests that were updated or created.
- */
- function updateSharesBatch(
- uint256[] calldata requestIds,
- uint256[] calldata newSharesTotals
- ) external returns (uint256[] memory currentRequestIds);
-
/**************************************************************************************************************************************/
/*** View Functions ***/
/**************************************************************************************************************************************/
diff --git a/tests/unit/AddShares.t.sol b/tests/unit/AddShares.t.sol
index 67e90e1..f29233a 100644
--- a/tests/unit/AddShares.t.sol
+++ b/tests/unit/AddShares.t.sol
@@ -5,8 +5,6 @@ import { TestBase } from "../utils/TestBase.sol";
contract AddSharesTests is TestBase {
- event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
-
function setUp() public override {
super.setUp();
@@ -28,7 +26,7 @@ contract AddSharesTests is TestBase {
withdrawalManager.addShares(0, lp);
}
- function test_addShares_multiple_requests() external{
+ function test_addShares_multiple_requests() external{
vm.prank(pm);
withdrawalManager.addShares(1, lp);
@@ -117,7 +115,7 @@ contract AddSharesTests is TestBase {
( address owner_, uint256 shares_ ) = withdrawalManager.requests(lastRequestId_);
( requestId_,) = getLastRequestByOwner(owner_);
-
+
assertEq(shares_, 1);
assertEq(withdrawalManager.totalShares(), 1);
assertEq(requestId_, lastRequestId_);
diff --git a/tests/unit/ProcessExit.t.sol b/tests/unit/ProcessExit.t.sol
index 50bcc94..a05b1e9 100644
--- a/tests/unit/ProcessExit.t.sol
+++ b/tests/unit/ProcessExit.t.sol
@@ -6,8 +6,6 @@ import { TestBase, console } from "../utils/TestBase.sol";
// TODO: Add ManualSharesDecreased event to tests
contract ProcessExitTests is TestBase {
- event RequestRemoved(uint256 indexed requestId);
-
uint256 assetsDeposited = 100e18;
uint256 sharesToRedeem = 250e18;
@@ -114,7 +112,7 @@ contract ProcessExitTests is TestBase {
withdrawalManager.__setRequest(1, lp, sharesToRedeem / 2);
withdrawalManager.__setTotalShares(sharesToRedeem);
withdrawalManager.__setManualSharesAvailable(lp, sharesToRedeem / 2);
-
+
// Only half of the liquidity is available.
asset.burn(address(pool), assetsDeposited / 2);
diff --git a/tests/unit/ProcessRedemptions.t.sol b/tests/unit/ProcessRedemptions.t.sol
index f607c3c..d41130c 100644
--- a/tests/unit/ProcessRedemptions.t.sol
+++ b/tests/unit/ProcessRedemptions.t.sol
@@ -7,10 +7,6 @@ import { TestBase } from "../utils/TestBase.sol";
// TODO: Add test case for reentrancy check
contract ProcessRedemptionsTests is TestBase {
- event RequestDecreased(uint256 indexed requestId, uint256 shares);
- event RequestProcessed(uint256 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
- event RequestRemoved(uint256 indexed requestId);
-
uint256 assetsDeposited = 100e18;
uint256 sharesLocked = 250e18;
@@ -137,7 +133,7 @@ contract ProcessRedemptionsTests is TestBase {
function test_processRedemptions_manual_multipleLps_multiple_requests() external {
address lp2 = makeAddr("lp2");
- address lp3 = makeAddr("lp3");
+ address lp3 = makeAddr("lp3");
withdrawalManager.__setManualWithdrawal(lp, true);
withdrawalManager.__setManualWithdrawal(lp2, true);
@@ -386,10 +382,6 @@ contract ProcessRedemptionsTests is TestBase {
contract ComplexRedemptionTests is TestBase {
- event RequestDecreased(uint256 indexed requestId, uint256 shares);
- event RequestProcessed(uint256 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
- event RequestRemoved(uint256 indexed requestId);
-
function test_processRedemptions_complex() external {
uint256 totalAssets_ = 100e18;
uint256 totalShares_ = 250e18;
@@ -445,7 +437,7 @@ contract ComplexRedemptionTests is TestBase {
assertEq(shares5_.length, 1);
assertEq(requestIds5_[0], 5);
assertEq(shares5_[0], 25e18);
-
+
assertEq(requestIds6_.length, 1);
assertEq(shares6_.length, 1);
assertEq(requestIds6_[0], 6);
diff --git a/tests/unit/RemoveRequest.t.sol b/tests/unit/RemoveRequest.t.sol
index 20d8a02..b8bdadf 100644
--- a/tests/unit/RemoveRequest.t.sol
+++ b/tests/unit/RemoveRequest.t.sol
@@ -5,8 +5,6 @@ import { TestBase } from "../utils/TestBase.sol";
contract RemoveRequestTests is TestBase {
- event RequestRemoved(uint256 indexed requestId);
-
function setUp() public override {
super.setUp();
diff --git a/tests/unit/RemoveShares.t.sol b/tests/unit/RemoveShares.t.sol
index 6737478..2b93314 100644
--- a/tests/unit/RemoveShares.t.sol
+++ b/tests/unit/RemoveShares.t.sol
@@ -4,10 +4,7 @@ pragma solidity ^0.8.7;
import { TestBase } from "../utils/TestBase.sol";
contract RemoveSharesTests is TestBase {
-
- event RequestDecreased(uint256 indexed requestId, uint256 shares);
- event RequestRemoved(uint256 indexed requestId);
-
+
function setUp() public override {
super.setUp();
@@ -212,5 +209,5 @@ contract RemoveSharesTests is TestBase {
assertEq(lastRequestId_, 0);
assertEq(lastShares_ , 0);
}
-
+
}
diff --git a/tests/unit/RemoveSharesById.t.sol b/tests/unit/RemoveSharesById.t.sol
new file mode 100644
index 0000000..583000f
--- /dev/null
+++ b/tests/unit/RemoveSharesById.t.sol
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+import { TestBase } from "../utils/TestBase.sol";
+
+contract RemoveSharesByIdFailureTests is TestBase {
+
+ function setUp() public override {
+ super.setUp();
+ uint256 mintAmount = 200;
+
+ // Simulate LP transfer into PM.
+ pool.mint(pm, mintAmount);
+
+ vm.prank(pm);
+ pool.approve(address(withdrawalManager), mintAmount);
+ }
+
+ function test_removeSharesById_protocolPaused() external {
+ globals.__setFunctionPaused(true);
+
+ vm.prank(lp);
+ vm.expectRevert("WM:PAUSED");
+ withdrawalManager.removeSharesById(1, 1);
+ }
+
+ function test_removeSharesById_maxUint128Exceeded() external {
+ vm.prank(lp);
+ vm.expectRevert("WM:TU:UINT256_CAST");
+ withdrawalManager.removeSharesById(type(uint256).max, 1);
+ }
+
+ function test_removeSharesById_invalidRequest() external {
+ vm.expectRevert("WM:RSBI:INVALID_REQUEST");
+ withdrawalManager.removeSharesById(1, 1);
+ }
+
+ function test_removeSharesById_requestAlreadyRemoved() external {
+ vm.prank(pm);
+ withdrawalManager.addShares(1, lp);
+
+ uint256[] memory requestIds = new uint256[](1);
+ requestIds[0] = 1;
+
+ vm.prank(governor);
+ withdrawalManager.removeRequest(lp, requestIds);
+
+ vm.expectRevert("WM:RSBI:INVALID_REQUEST");
+ vm.prank(lp);
+ withdrawalManager.removeSharesById(1, 100);
+ }
+
+ function test_removeSharesById_zeroRequestId() external {
+ vm.prank(lp);
+ vm.expectRevert("WM:RSBI:INVALID_REQUEST");
+ withdrawalManager.removeSharesById(0, 1); // Request ID 0 should be invalid
+ }
+
+ function test_removeSharesById_notOwner() external {
+ vm.prank(pm);
+ uint256 requestId = withdrawalManager.addShares(1, lp);
+
+ vm.prank(address(0x12));
+ vm.expectRevert("WM:RSBI:NOT_OWNER");
+ withdrawalManager.removeSharesById(requestId, 1);
+ }
+
+ function test_removeSharesById_noChange() external {
+ vm.prank(pm);
+ withdrawalManager.addShares(1, lp);
+
+ vm.prank(lp);
+ vm.expectRevert("WM:RSBI:NO_CHANGE");
+ withdrawalManager.removeSharesById(1, 0);
+ }
+
+ function test_removeSharesById_insufficientShares() external {
+ vm.prank(pm);
+ withdrawalManager.addShares(5, lp);
+
+ // Try to remove 10 shares (more than the 5 available)
+ vm.prank(lp);
+ vm.expectRevert("WM:RSBI:INSUFFICIENT_SHARES");
+ withdrawalManager.removeSharesById(1, 10);
+ }
+
+ function test_removeSharesById_transferFail() external {
+ vm.prank(pm);
+ withdrawalManager.addShares(5, lp);
+
+ // Burn the tokens from the withdrawal manager to simulate transfer failure
+ pool.burn(address(withdrawalManager), 5);
+
+ vm.prank(lp);
+ vm.expectRevert("WM:RS:TRANSFER_FAIL");
+ withdrawalManager.removeSharesById(1, 1);
+ }
+
+}
+
+contract RemoveSharesByIdSuccessTests is TestBase {
+
+ address lp2 = makeAddr("lp2");
+
+ address ownerForRequest1;
+ address ownerForRequest2;
+ address ownerForRequest3;
+ address ownerForRequest4;
+
+ uint256 requestAmount1;
+ uint256 requestAmount2;
+
+ uint256 sharesForRequest1;
+ uint256 sharesForRequest2;
+ uint256 sharesForRequest3;
+ uint256 sharesForRequest4;
+
+ function setUp() public override {
+ super.setUp();
+ uint256 mintAmount = 200;
+
+ pool.mint(pm, mintAmount); // Simulate LP transfer into PM.
+ pool.mint(lp, mintAmount); // Give LP some shares to use to increase shares.
+
+ vm.prank(pm);
+ pool.approve(address(withdrawalManager), mintAmount);
+
+ vm.prank(lp);
+ pool.approve(address(withdrawalManager), mintAmount);
+ }
+
+ function test_removeSharesById_remove_request() external{
+ vm.prank(pm);
+ uint256 requestId = withdrawalManager.addShares(1, lp);
+
+ ( address ownerBefore, uint256 sharesBefore ) = withdrawalManager.requests(requestId);
+
+ assertEq(ownerBefore, lp);
+ assertEq(sharesBefore, 1);
+
+ ( uint256[] memory requestIdsBefore, uint256[] memory sharesPerRequestBefore ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIdsBefore.length, 1);
+ assertEq(sharesPerRequestBefore.length, 1);
+ assertEq(requestIdsBefore[0], requestId);
+ assertEq(sharesPerRequestBefore[0], 1);
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestRemoved(requestId);
+
+ ( uint256 sharesReturned_, uint256 sharesRemaining_ ) = withdrawalManager.removeSharesById(requestId, 1);
+
+ assertEq(sharesReturned_, 1);
+ assertEq(sharesRemaining_, 0);
+
+ ( address ownerAfter, uint256 sharesAfter ) = withdrawalManager.requests(requestId);
+
+ assertEq(ownerAfter, address(0));
+ assertEq(sharesAfter, 0);
+
+ ( uint256[] memory requestIdsAfter, uint256[] memory sharesPerRequestAfter ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIdsAfter.length, 0);
+ assertEq(sharesPerRequestAfter.length, 0);
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), 0);
+ }
+
+ function test_removeSharesById_decrease() external {
+ vm.prank(pm);
+ uint256 requestId = withdrawalManager.addShares(2, lp);
+
+ ( address ownerBefore, uint256 sharesBefore ) = withdrawalManager.requests(requestId);
+
+ assertEq(ownerBefore, lp);
+ assertEq(sharesBefore, 2);
+
+ ( uint256[] memory requestIdsBefore, uint256[] memory sharesPerRequestBefore ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIdsBefore.length, 1);
+ assertEq(sharesPerRequestBefore.length, 1);
+ assertEq(requestIdsBefore[0], requestId);
+ assertEq(sharesPerRequestBefore[0], 2);
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), 2);
+
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestDecreased(requestId, 1);
+
+ ( uint256 sharesReturned_, uint256 sharesRemaining_ ) = withdrawalManager.removeSharesById(requestId, 1);
+
+ assertEq(sharesReturned_, 1);
+ assertEq(sharesRemaining_, 1);
+
+ ( address ownerAfter, uint256 sharesAfter ) = withdrawalManager.requests(requestId);
+
+ assertEq(ownerAfter, lp);
+ assertEq(sharesAfter, 1);
+
+ ( uint256[] memory requestIdsAfter, uint256[] memory sharesPerRequestAfter ) = withdrawalManager.requestsByOwner(lp);
+
+ assertEq(requestIdsAfter.length, 1);
+ assertEq(sharesPerRequestAfter.length, 1);
+ assertEq(requestIdsAfter[0], requestId);
+ assertEq(sharesPerRequestAfter[0], 1);
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), 1);
+ }
+
+ function test_removeSharesById_multipleLPsWithMultipleRequests() external {
+ requestAmount1 = 10;
+ requestAmount2 = 20;
+
+ pool.mint(pm, 3000);
+ pool.mint(lp, 1000);
+ pool.mint(lp2, 1000);
+
+ // Create multiple requests for both LPs
+ vm.startPrank(pm);
+
+ vm.expectEmit();
+ emit RequestCreated(1, lp, requestAmount1);
+ withdrawalManager.addShares(requestAmount1, lp);
+
+ vm.expectEmit();
+ emit RequestCreated(2, lp, requestAmount2);
+ withdrawalManager.addShares(requestAmount2, lp);
+
+ vm.expectEmit();
+ emit RequestCreated(3, lp2, requestAmount1);
+ withdrawalManager.addShares(requestAmount1, lp2);
+
+ vm.expectEmit();
+ emit RequestCreated(4, lp2, requestAmount2);
+ withdrawalManager.addShares(requestAmount2, lp2);
+
+ vm.stopPrank();
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), requestAmount1 + requestAmount2);
+ assertEq(withdrawalManager.userEscrowedShares(lp2), requestAmount1 + requestAmount2);
+
+ ( ownerForRequest1, sharesForRequest1 ) = withdrawalManager.requests(1);
+ ( ownerForRequest2, sharesForRequest2 ) = withdrawalManager.requests(2);
+ ( ownerForRequest3, sharesForRequest3 ) = withdrawalManager.requests(3);
+ ( ownerForRequest4, sharesForRequest4 ) = withdrawalManager.requests(4);
+
+ assertEq(ownerForRequest1, lp);
+ assertEq(ownerForRequest2, lp);
+ assertEq(ownerForRequest3, lp2);
+ assertEq(ownerForRequest4, lp2);
+
+ assertEq(sharesForRequest1, requestAmount1);
+ assertEq(sharesForRequest2, requestAmount2);
+ assertEq(sharesForRequest3, requestAmount1);
+ assertEq(sharesForRequest4, requestAmount2);
+
+ ( uint256[] memory requestIdsLp1Before, uint256[] memory sharesLp1Before ) = withdrawalManager.requestsByOwner(lp);
+ ( uint256[] memory requestIdsLp2Before, uint256[] memory sharesLp2Before ) = withdrawalManager.requestsByOwner(lp2);
+
+ assertEq(requestIdsLp1Before.length, 2);
+ assertEq(sharesLp1Before.length, 2);
+
+ assertEq(requestIdsLp2Before.length, 2);
+ assertEq(sharesLp2Before.length, 2);
+
+ assertEq(requestIdsLp1Before[0], 1);
+ assertEq(requestIdsLp1Before[1], 2);
+
+ assertEq(requestIdsLp2Before[0], 3);
+ assertEq(requestIdsLp2Before[1], 4);
+
+ assertEq(sharesLp1Before[0], requestAmount1);
+ assertEq(sharesLp1Before[1], requestAmount2);
+
+ assertEq(sharesLp2Before[0], requestAmount1);
+ assertEq(sharesLp2Before[1], requestAmount2);
+
+ // Decrease LP1's first request
+ vm.prank(lp);
+ vm.expectEmit();
+ emit RequestDecreased(1, 1);
+ ( uint256 sharesReturnedLp, uint256 sharesRemainingLp ) = withdrawalManager.removeSharesById(1, 1);
+
+ assertEq(sharesReturnedLp, 1);
+ assertEq(sharesRemainingLp, requestAmount1 - 1);
+
+ // Remove LP2's first request completely
+ vm.prank(lp2);
+ vm.expectEmit();
+ emit RequestRemoved(3);
+ ( uint256 sharesReturnedLp2, uint256 sharesRemainingLp2 ) = withdrawalManager.removeSharesById(3, requestAmount1);
+
+ assertEq(sharesReturnedLp2, requestAmount1);
+ assertEq(sharesRemainingLp2, 0);
+
+ assertEq(withdrawalManager.userEscrowedShares(lp), requestAmount1 - 1 + requestAmount2);
+ assertEq(withdrawalManager.userEscrowedShares(lp2), requestAmount2);
+
+ ( ownerForRequest1, sharesForRequest1 ) = withdrawalManager.requests(1);
+ ( ownerForRequest2, sharesForRequest2 ) = withdrawalManager.requests(2);
+ ( ownerForRequest3, sharesForRequest3 ) = withdrawalManager.requests(3);
+ ( ownerForRequest4, sharesForRequest4 ) = withdrawalManager.requests(4);
+
+ assertEq(ownerForRequest1, lp); // LP's first request should be decreased but still exist
+ assertEq(ownerForRequest2, lp); // LP's second request should be unchanged
+ assertEq(ownerForRequest3, address(0)); // LP2's first request should be removed
+ assertEq(ownerForRequest4, lp2); // LP2's second request should be unchanged
+
+ assertEq(sharesForRequest1, requestAmount1 - 1); // LP's first request decreased
+ assertEq(sharesForRequest2, requestAmount2); // LP's second request unchanged
+ assertEq(sharesForRequest3, 0); // LP2's first request removed
+ assertEq(sharesForRequest4, requestAmount2); // LP2's second request unchanged
+
+ ( uint256[] memory requestIdsLp1After, uint256[] memory sharesLp1After ) = withdrawalManager.requestsByOwner(lp);
+ ( uint256[] memory requestIdsLp2After, uint256[] memory sharesLp2After ) = withdrawalManager.requestsByOwner(lp2);
+
+ assertEq(requestIdsLp1After.length, 2);
+ assertEq(sharesLp1After.length, 2);
+
+ assertEq(requestIdsLp2After.length, 1);
+ assertEq(sharesLp2After.length, 1);
+
+ assertEq(requestIdsLp1After[0], 1);
+ assertEq(requestIdsLp1After[1], 2);
+
+ assertEq(requestIdsLp2After[0], 4);
+
+ assertEq(sharesLp1After[0], requestAmount1 - 1);
+ assertEq(sharesLp1After[1], requestAmount2);
+
+ assertEq(sharesLp2After[0], requestAmount2);
+ }
+
+}
diff --git a/tests/unit/SetManualWithdrawal.t.sol b/tests/unit/SetManualWithdrawal.t.sol
index 465aac7..c301ec8 100644
--- a/tests/unit/SetManualWithdrawal.t.sol
+++ b/tests/unit/SetManualWithdrawal.t.sol
@@ -5,8 +5,6 @@ import { TestBase } from "../utils/TestBase.sol";
contract SetManualWithdrawalTests is TestBase {
- event ManualWithdrawalSet(address indexed account, bool isManual);
-
function test_setManualWithdrawal_protocolPaused() external {
globals.__setFunctionPaused(true);
diff --git a/tests/unit/UpdateShares.t.sol b/tests/unit/UpdateShares.t.sol
deleted file mode 100644
index 9ce1a39..0000000
--- a/tests/unit/UpdateShares.t.sol
+++ /dev/null
@@ -1,644 +0,0 @@
-// SPDX-License-Identifier: BUSL-1.1
-pragma solidity ^0.8.7;
-
-import { TestBase } from "../utils/TestBase.sol";
-
-contract UpdateSharesFailureTests is TestBase {
-
- function setUp() public override {
- super.setUp();
- uint256 mintAmount = 200;
-
- // Simulate LP transfer into PM.
- pool.mint(pm, mintAmount);
-
- vm.prank(pm);
- pool.approve(address(withdrawalManager), mintAmount);
- }
-
- function test_updateShares_invalidRequest() external {
- vm.expectRevert("WM:US:INVALID_REQUEST");
- withdrawalManager.updateShares(1, 1);
- }
-
- function test_updateShares_notOwner() external {
- vm.prank(pm);
- withdrawalManager.addShares(1, lp);
-
- vm.prank(address(0x12));
- vm.expectRevert("WM:US:NOT_OWNER");
- withdrawalManager.updateShares(1, 1);
- }
-
- function test_updateShares_noChange() external {
- vm.prank(pm);
- withdrawalManager.addShares(1, lp);
-
- vm.prank(lp);
- vm.expectRevert("WM:US:NO_CHANGE");
- withdrawalManager.updateShares(1, 1);
- }
-
- function test_updateShares_failed_increase_insufficient_shares() external{
- vm.prank(pm);
- withdrawalManager.addShares(1, lp);
-
- vm.expectRevert("WM:AS:FAILED_TRANSFER");
- vm.prank(lp);
- withdrawalManager.updateShares(1, 100);
- }
-
- function test_updateShares_failed_transfer_insufficient_tokens() external{
- vm.prank(pm);
- withdrawalManager.addShares(1, lp);
-
- vm.expectRevert("WM:AS:FAILED_TRANSFER");
- vm.prank(lp);
- withdrawalManager.updateShares(1, 100);
- }
-
- function test_updateShares_failed_update_request_already_removed() external{
- vm.prank(pm);
- withdrawalManager.addShares(1, lp);
-
- uint256[] memory requestIds = new uint256[](1);
- requestIds[0] = 1;
-
- vm.prank(governor);
- withdrawalManager.removeRequest(lp, requestIds);
-
- vm.expectRevert("WM:US:INVALID_REQUEST");
- vm.prank(lp);
- withdrawalManager.updateShares(1, 100);
- }
-}
-
-contract UpdateSharesSuccessTests is TestBase {
-
- event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
-
- event RequestDecreased(uint256 indexed requestId, uint256 shares);
-
- event RequestRemoved(uint256 indexed requestId);
-
- function setUp() public override {
- super.setUp();
- uint256 mintAmount = 200;
-
- pool.mint(pm, mintAmount); // Simulate LP transfer into PM.
- pool.mint(lp, mintAmount); // Give LP some shares to use to increase shares.
-
- vm.prank(pm);
- pool.approve(address(withdrawalManager), mintAmount);
-
- vm.prank(lp);
- pool.approve(address(withdrawalManager), mintAmount);
- }
-
- function test_updateShares_increase() external {
- vm.prank(pm);
- uint256 requestIdBefore_ = withdrawalManager.addShares(1, lp);
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 1);
-
- (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIdBefore_, 1, "Request ID should be 1");
- assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
- assertEq(sharesBefore_.length, 1, "Shares length should be 1");
- assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
- assertEq(sharesBefore_[0], 1, "Shares should be 1");
-
- vm.prank(lp);
- vm.expectEmit();
- emit RequestRemoved(requestIdBefore_);
- emit RequestCreated(requestIdBefore_ + 1, lp, 2);
- // Increase shares from 1 to 2 creates a new request and removes the old one.
- uint256 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 2);
-
- (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(newRequestId_, 2, "Request ID should be 2");
- assertEq(requestIds_.length, 1, "Request IDs length should be 1");
- assertEq(shares_.length, 1, "Shares length should be 1");
- assertEq(requestIds_[0], 2, "Request ID should be 2");
- assertEq(shares_[0], 2, "Shares should be 2");
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 2);
- }
-
- function test_updateShares_remove_request() external{
- vm.prank(pm);
- uint256 requestId_ = withdrawalManager.addShares(1, lp);
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 1);
-
- vm.prank(lp);
- vm.expectEmit();
- emit RequestRemoved(requestId_);
-
- uint256 newRequestId_ = withdrawalManager.updateShares(requestId_, 0);
-
- assertEq(newRequestId_, 0, "Request ID should be 0");
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 0);
- }
-
- function test_updateShares_decrease() external {
- vm.prank(pm);
- uint256 requestIdBefore_ = withdrawalManager.addShares(2, lp);
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 2);
-
- (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIdBefore_, 1, "Request ID Before should be 1 (addShares)");
- assertEq(requestIdsBefore_.length, 1, "Request IDs length before should be 1");
- assertEq(sharesBefore_.length, 1, "Shares length before should be 1");
- assertEq(requestIdsBefore_[0], 1, "Request ID before should be 1");
- assertEq(sharesBefore_[0], 2, "Shares before should be 2");
-
- vm.prank(lp);
- vm.expectEmit();
- emit RequestDecreased(requestIdBefore_, 1);
- // Decrease shares from 2 to 1 keeps the same request ID.
- uint256 newRequestId_ = withdrawalManager.updateShares(requestIdBefore_, 1);
-
- (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(newRequestId_, 1, "Request ID should be 1 (addShares)");
- assertEq(requestIds_.length, 1, "Request IDs length should be 1");
- assertEq(shares_.length, 1, "Shares length should be 1");
- assertEq(requestIds_[0], 1, "Request ID should be 1");
- assertEq(shares_[0], 1, "Shares should be 1");
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 1);
- }
-
- function test_update_shares_multiple_lps_requestsByOwner() external {
- address lp2_ = makeAddr("lp2");
- address lp3_ = makeAddr("lp3");
-
- uint256 requestAmount1_ = 10;
- uint256 requestAmount2_ = 20;
-
- pool.mint(pm, 3000);
- pool.mint(lp, 1000);
- pool.mint(lp2_, 1000);
- pool.mint(lp3_, 1000);
-
- vm.prank(lp3_);
- pool.approve(address(withdrawalManager), 1000);
-
- vm.startPrank(pm);
-
- vm.expectEmit();
- emit RequestCreated(1, lp, requestAmount1_);
- withdrawalManager.addShares(requestAmount1_, lp);
-
- vm.expectEmit();
- emit RequestCreated(2, lp, requestAmount2_);
- withdrawalManager.addShares(requestAmount2_, lp);
-
- vm.expectEmit();
- emit RequestCreated(3, lp2_, requestAmount1_);
- withdrawalManager.addShares(requestAmount1_, lp2_);
-
- vm.expectEmit();
- emit RequestCreated(4, lp2_, requestAmount2_);
- withdrawalManager.addShares(requestAmount2_, lp2_);
-
- vm.expectEmit();
- emit RequestCreated(5, lp3_, requestAmount1_);
- withdrawalManager.addShares(requestAmount1_, lp3_);
-
- vm.expectEmit();
- emit RequestCreated(6, lp3_, requestAmount2_);
- withdrawalManager.addShares(requestAmount2_, lp3_);
-
- vm.stopPrank();
-
-
- assertEq(withdrawalManager.userEscrowedShares(lp), requestAmount1_ + requestAmount2_);
- assertEq(withdrawalManager.userEscrowedShares(lp2_), requestAmount1_ + requestAmount2_);
- assertEq(withdrawalManager.userEscrowedShares(lp3_), requestAmount1_ + requestAmount2_);
-
- {
- (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
- (uint256[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
- (uint256[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3_);
-
- assertEq(requestIdsLp_.length, 2, "LP request IDs length should be 2");
- assertEq(requestIdsLp2_.length, 2, "LP2 request IDs length should be 2");
- assertEq(requestIdsLp3_.length, 2, "LP3 request IDs length should be 2");
-
- assertEq(sharesLp_.length, 2, "LP shares length should be 2");
- assertEq(sharesLp2_.length, 2, "LP2 shares length should be 2");
- assertEq(sharesLp3_.length, 2, "LP3 shares length should be 2");
-
- assertEq(requestIdsLp_[0], 1, "LP request ID index 0 should be 1");
- assertEq(requestIdsLp_[1], 2, "LP request ID index 1 should be 2");
- assertEq(requestIdsLp2_[0], 3, "LP2 request ID index 0 should be 3");
- assertEq(requestIdsLp2_[1], 4, "LP2 request ID index 1 should be 4");
- assertEq(requestIdsLp3_[0], 5, "LP3 request ID index 0 should be 5");
- assertEq(requestIdsLp3_[1], 6, "LP3 request ID index 1 should be 6");
-
- assertEq(sharesLp_[0], requestAmount1_, "LP shares index 0 is incorrect");
- assertEq(sharesLp_[1], requestAmount2_, "LP shares index 1 is incorrect");
- assertEq(sharesLp2_[0], requestAmount1_, "LP2 shares index 0 is incorrect");
- assertEq(sharesLp2_[1], requestAmount2_, "LP2 shares index 1 is incorrect");
- assertEq(sharesLp3_[0], requestAmount1_, "LP3 shares index 0 is incorrect");
- assertEq(sharesLp3_[1], requestAmount2_, "LP3 shares index 1 is incorrect");
- }
- {
- // Decrease a request
- vm.prank(lp);
- uint256 requestIdLp_ = 1;
- vm.expectEmit();
- emit RequestDecreased(requestIdLp_, 1);
- uint256 updatedRequestIdLp_ = withdrawalManager.updateShares(requestIdLp_, requestAmount1_ - 1);
- assertEq(updatedRequestIdLp_, requestIdLp_, "Updated request ID should be 1");
-
- // Remove a request
- vm.prank(lp2_);
- uint256 requestIdLp2_ = 3;
- vm.expectEmit();
- emit RequestRemoved(requestIdLp2_);
- uint256 updatedRequestIdLp2_ = withdrawalManager.updateShares(requestIdLp2_, 0);
- assertEq(updatedRequestIdLp2_, 0, "Updated request ID should be 0");
-
- // Increase a request
- vm.prank(lp3_);
- uint256 requestIdLp3_ = 5;
- vm.expectEmit();
- emit RequestRemoved(requestIdLp3_);
- emit RequestCreated(7, lp3_, requestAmount2_ + 10);
- uint256 updatedRequestIdLp3 = withdrawalManager.updateShares(requestIdLp3_, requestAmount2_ + 10);
- assertEq(updatedRequestIdLp3, 7, "Updated request ID should be 7");
-
- vm.stopPrank();
- }
-
- {
- (uint256[] memory requestIdsLp_, uint256[] memory sharesLp_) = withdrawalManager.requestsByOwner(lp);
- (uint256[] memory requestIdsLp2_, uint256[] memory sharesLp2_) = withdrawalManager.requestsByOwner(lp2_);
- (uint256[] memory requestIdsLp3_, uint256[] memory sharesLp3_) = withdrawalManager.requestsByOwner(lp3_);
-
- assertEq(requestIdsLp_.length, 2, "LP request IDs length should be 2");
- assertEq(requestIdsLp2_.length, 1, "LP2 request IDs length should be 1");
- assertEq(requestIdsLp3_.length, 2, "LP3 request IDs length should be 2");
-
- assertEq(sharesLp_.length, 2, "LP shares length should be 2");
- assertEq(sharesLp2_.length, 1, "LP2 shares length should be 2");
- assertEq(sharesLp3_.length, 2, "LP3 shares length should be 2");
-
- assertEq(requestIdsLp_[0], 1, "LP request ID index 0 should be 1");
- assertEq(requestIdsLp_[1], 2, "LP request ID index 1 should be 2");
- assertEq(requestIdsLp2_[0], 4, "LP2 request ID index 1 should be 4");
- assertEq(requestIdsLp3_[0], 6, "LP3 request ID index 0 should be 5");
- assertEq(requestIdsLp3_[1], 7, "LP3 request ID index 1 should be 7");
-
- assertEq(sharesLp_[0], requestAmount1_ - 1, "LP shares index 0 is incorrect");
- assertEq(sharesLp_[1], requestAmount2_, "LP shares index 1 is incorrect");
- assertEq(sharesLp2_[0], requestAmount2_, "LP2 shares index 0 is incorrect");
- assertEq(sharesLp3_[0], requestAmount2_, "LP3 shares index 0 is incorrect");
- assertEq(sharesLp3_[1], requestAmount2_ + 10, "LP3 shares index 1 is incorrect");
- }
-
- assertEq(withdrawalManager.userEscrowedShares(lp), requestAmount1_ - 1 + requestAmount2_);
- assertEq(withdrawalManager.userEscrowedShares(lp2_), requestAmount2_);
- assertEq(withdrawalManager.userEscrowedShares(lp3_), requestAmount2_ + 10 + requestAmount2_);
- }
-
-}
-
-contract UpdateSharesBatchFailureTests is TestBase {
-
- function setUp() public override {
- super.setUp();
- uint256 mintAmount = 200;
-
- pool.mint(pm, mintAmount); // Simulate LP transfer into PM.
- pool.mint(lp, mintAmount); // Give LP some shares to use to increase shares.
-
- vm.prank(pm);
- pool.approve(address(withdrawalManager), mintAmount);
-
- vm.prank(lp);
- pool.approve(address(withdrawalManager), mintAmount);
- }
-
- function test_updateSharesBatch_invalid_array_lengths() external {
- uint256[] memory requestIdsToUpdate_ = new uint256[](1);
- uint256[] memory sharesToUpdate_ = new uint256[](2);
-
- vm.expectRevert("WM:USB:ARRAY_LENGTH_MISMATCH");
- withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
- }
-
- function test_updateSharesBatch_invalid_request() external {
- uint256[] memory requestIdsToUpdate_ = new uint256[](1);
- uint256[] memory sharesToUpdate_ = new uint256[](1);
-
- vm.expectRevert("WM:US:INVALID_REQUEST");
- withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
- }
-
- function test_updateSharesBatch_invalid_owner() external {
- uint256[] memory requestIdsToUpdate_ = new uint256[](1);
- uint256[] memory sharesToUpdate_ = new uint256[](1);
-
- vm.prank(pm);
- uint256 requestId_ = withdrawalManager.addShares(1, lp);
-
- requestIdsToUpdate_[0] = requestId_;
- sharesToUpdate_[0] = 2;
-
- vm.prank(address(0x12));
- vm.expectRevert("WM:US:NOT_OWNER");
- withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
- }
-
- function test_updateSharesBatch_no_change_in_shares() external {
- uint256[] memory requestIdsToUpdate_ = new uint256[](1);
- uint256[] memory sharesToUpdate_ = new uint256[](1);
-
- vm.prank(pm);
- uint256 requestId_ = withdrawalManager.addShares(1, lp);
-
- requestIdsToUpdate_[0] = requestId_;
- sharesToUpdate_[0] = 1;
-
- vm.prank(lp);
- vm.expectRevert("WM:US:NO_CHANGE");
- withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
- }
-}
-
-contract UpdateSharesBatchSuccessTests is TestBase {
-
- event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
-
- event RequestDecreased(uint256 indexed requestId, uint256 shares);
-
- event RequestRemoved(uint256 indexed requestId);
-
- function setUp() public override {
- super.setUp();
- uint256 mintAmount = 200;
-
- pool.mint(pm, mintAmount); // Simulate LP transfer into PM.
- pool.mint(lp, mintAmount); // Give LP some shares to use to increase shares.
-
- vm.prank(pm);
- pool.approve(address(withdrawalManager), mintAmount);
-
- vm.prank(lp);
- pool.approve(address(withdrawalManager), mintAmount);
- }
-
-
- function test_updateSharesBatch_increase_single_request() external {
- vm.prank(pm);
- uint256 requestIdBefore_ = withdrawalManager.addShares(1, lp);
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 1);
-
- (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIdBefore_, 1, "Request ID should be 1");
- assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
- assertEq(sharesBefore_.length, 1, "Shares length should be 1");
- assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
- assertEq(sharesBefore_[0], 1, "Shares should be 1");
-
- uint256[] memory requestIdsToUpdate_ = new uint256[](1);
- requestIdsToUpdate_[0] = requestIdBefore_;
-
- uint256[] memory sharesToUpdate_ = new uint256[](1);
- sharesToUpdate_[0] = 2;
-
- vm.prank(lp);
- vm.expectEmit();
- emit RequestRemoved(requestIdBefore_);
- emit RequestCreated(requestIdBefore_ + 1, lp, 2);
-
- // Increase shares creates a new request and removes the old one.
- uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
-
- assertEq(newRequestIds_.length, 1, "New request IDs length should be 1");
- assertEq(newRequestIds_[0], 2, "New request ID should be 2");
-
- (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIds_.length, 1, "Request IDs length should be 1");
- assertEq(shares_.length, 1, "Shares length should be 1");
- assertEq(requestIds_[0], 2, "Request ID should be 2");
- assertEq(shares_[0], 2, "Shares should be 2");
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 2);
- }
-
- function test_updateSharesBatch_increase_multiple_requests() external {
- vm.startPrank(pm);
- uint256 requestId1Before_ = withdrawalManager.addShares(1, lp);
- uint256 requestId2Before_ = withdrawalManager.addShares(1, lp);
- vm.stopPrank();
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 2);
-
- (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestId1Before_, 1, "Request ID should be 1");
- assertEq(requestId2Before_, 2, "Request ID should be 2");
- assertEq(requestIdsBefore_.length, 2, "Request IDs length should be 2");
- assertEq(sharesBefore_.length, 2, "Shares length should be 2");
- assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
- assertEq(sharesBefore_[0], 1, "Shares should be 1");
- assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
- assertEq(sharesBefore_[1], 1, "Shares should be 1");
-
- uint256[] memory requestIdsToUpdate_ = new uint256[](2);
- requestIdsToUpdate_[0] = requestId1Before_;
- requestIdsToUpdate_[1] = requestId2Before_;
-
- uint256[] memory sharesToUpdate_ = new uint256[](2);
- sharesToUpdate_[0] = 2;
- sharesToUpdate_[1] = 3;
-
- vm.prank(lp);
- vm.expectEmit();
- emit RequestRemoved(requestId1Before_);
- emit RequestCreated(3, lp, 2);
-
- vm.expectEmit();
- emit RequestRemoved(requestId2Before_);
- emit RequestCreated(4, lp, 3);
-
- // Increase shares creates a new request and removes the old one.
- uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
-
- assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
- assertEq(newRequestIds_[0], 3, "New request ID should be 3");
- assertEq(newRequestIds_[1], 4, "New request ID should be 4");
-
- (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIds_.length, 2, "Request IDs length should be 2");
- assertEq(shares_.length, 2, "Shares length should be 2");
- assertEq(requestIds_[0], 3, "Request ID should be 3");
- assertEq(shares_[0], 2, "Shares should be 2");
- assertEq(requestIds_[1], 4, "Request ID should be 4");
- assertEq(shares_[1], 3, "Shares should be 3");
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 5);
- }
-
- function test_updateSharesBatch_decrease_single_request() external {
- vm.prank(pm);
- uint256 requestIdBefore_ = withdrawalManager.addShares(2, lp);
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 2);
-
- (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIdBefore_, 1, "Request ID should be 1");
- assertEq(requestIdsBefore_.length, 1, "Request IDs length should be 1");
- assertEq(sharesBefore_.length, 1, "Shares length should be 1");
- assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
- assertEq(sharesBefore_[0], 2, "Shares should be 2");
-
- uint256[] memory requestIdsToUpdate_ = new uint256[](1);
- requestIdsToUpdate_[0] = requestIdBefore_;
-
- uint256[] memory sharesToUpdate_ = new uint256[](1);
- sharesToUpdate_[0] = 1;
-
- vm.prank(lp);
- vm.expectEmit();
- emit RequestDecreased(requestIdBefore_, 1);
-
- // Decrease in shares updates the existing request.
- uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
-
- assertEq(newRequestIds_.length, 1, "New request IDs length should be 1");
- assertEq(newRequestIds_[0], 1, "New request ID should be 1");
-
- (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIds_.length, 1, "Request IDs length should be 1");
- assertEq(shares_.length, 1, "Shares length should be 1");
- assertEq(requestIds_[0], 1, "Request ID should be 1");
- assertEq(shares_[0], 1, "Shares should be 1");
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 1);
- }
-
- function test_updateSharesBatch_decrease_multiple_requests() external {
- vm.startPrank(pm);
- uint256 requestId1Before_ = withdrawalManager.addShares(2, lp);
- uint256 requestId2Before_ = withdrawalManager.addShares(2, lp);
- vm.stopPrank();
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 4);
-
- (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestId1Before_, 1, "Request ID should be 1");
- assertEq(requestId2Before_, 2, "Request ID should be 1");
- assertEq(requestIdsBefore_.length, 2, "Request IDs length should be 2");
- assertEq(sharesBefore_.length, 2, "Shares length should be 2");
- assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
- assertEq(sharesBefore_[0], 2, "Shares should be 2");
- assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
- assertEq(sharesBefore_[1], 2, "Shares should be 2");
-
- uint256[] memory requestIdsToUpdate_ = new uint256[](2);
- requestIdsToUpdate_[0] = requestId1Before_;
- requestIdsToUpdate_[1] = requestId2Before_;
-
- uint256[] memory sharesToUpdate_ = new uint256[](2);
- sharesToUpdate_[0] = 1;
- sharesToUpdate_[1] = 1;
-
- vm.prank(lp);
- vm.expectEmit();
- emit RequestDecreased(requestId1Before_, 1);
-
- vm.expectEmit();
- emit RequestDecreased(requestId2Before_, 1);
-
- // Decrease in shares updates the existing requests.
- uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
-
- assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
- assertEq(newRequestIds_[0], 1, "New request ID should be 1");
- assertEq(newRequestIds_[1], 2, "New request ID should be 2");
-
- (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIds_.length, 2, "Request IDs length should be 2");
- assertEq(shares_.length, 2, "Shares length should be 2");
- assertEq(requestIds_[0], 1, "Request ID should be 1");
- assertEq(shares_[0], 1, "Shares should be 1");
- assertEq(requestIds_[1], 2, "Request ID should be 2");
- assertEq(shares_[1], 1, "Shares should be 1");
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 2);
- }
-
- function test_updateSharesBatch_increase_and_decrease_multiple_requests() external {
- vm.startPrank(pm);
- uint256 requestId1Before_ = withdrawalManager.addShares(1, lp);
- uint256 requestId2Before_ = withdrawalManager.addShares(2, lp);
- vm.stopPrank();
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 3);
-
- (uint256[] memory requestIdsBefore_, uint256[] memory sharesBefore_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestId1Before_, 1, "Request ID should be 1");
- assertEq(requestId2Before_, 2, "Request ID should be 1");
- assertEq(requestIdsBefore_.length, 2, "Request IDs length should be 2");
- assertEq(sharesBefore_.length, 2, "Shares length should be 2");
- assertEq(requestIdsBefore_[0], 1, "Request ID should be 1");
- assertEq(sharesBefore_[0], 1, "Shares should be 1");
- assertEq(requestIdsBefore_[1], 2, "Request ID should be 2");
- assertEq(sharesBefore_[1], 2, "Shares should be 2");
-
- uint256[] memory requestIdsToUpdate_ = new uint256[](2);
- requestIdsToUpdate_[0] = requestId1Before_;
- requestIdsToUpdate_[1] = requestId2Before_;
-
- uint256[] memory sharesToUpdate_ = new uint256[](2);
- sharesToUpdate_[0] = 2;
- sharesToUpdate_[1] = 1;
-
- vm.prank(lp);
- vm.expectEmit();
- emit RequestRemoved(requestId1Before_);
- emit RequestCreated(3, lp, 2);
-
- vm.expectEmit();
- emit RequestDecreased(requestId2Before_, 1);
-
- // Decrease in shares updates the existing requests.
- uint256[] memory newRequestIds_ = withdrawalManager.updateSharesBatch(requestIdsToUpdate_, sharesToUpdate_);
-
- assertEq(newRequestIds_.length, 2, "New request IDs length should be 2");
- assertEq(newRequestIds_[0], 3, "New request ID should be 3");
- assertEq(newRequestIds_[1], 2, "New request ID should be 2");
-
- (uint256[] memory requestIds_, uint256[] memory shares_) = withdrawalManager.requestsByOwner(lp);
-
- assertEq(requestIds_.length, 2, "Request IDs length should be 1");
- assertEq(shares_.length, 2, "Shares length should be 1");
- assertEq(requestIds_[0], 2, "Request ID should be 2");
- assertEq(shares_[0], 1, "Shares should be 1");
- assertEq(requestIds_[1], 3, "Request ID should be 3");
- assertEq(shares_[1], 2, "Shares should be 2");
-
- assertEq(withdrawalManager.userEscrowedShares(lp), 3);
- }
-
-}
diff --git a/tests/utils/Mocks.sol b/tests/utils/Mocks.sol
index 1963060..4706c38 100644
--- a/tests/utils/Mocks.sol
+++ b/tests/utils/Mocks.sol
@@ -108,7 +108,7 @@ contract MockPool is MockERC20 {
asset_ = address(_asset);
}
- function redeem(uint256, address, address) external view returns (uint256 assets_) {
+ function redeem(uint256, address, address) external pure returns (uint256 assets_) {
assets_; // Ignore variable
}
diff --git a/tests/utils/TestBase.sol b/tests/utils/TestBase.sol
index 0050d0c..3ea0383 100644
--- a/tests/utils/TestBase.sol
+++ b/tests/utils/TestBase.sol
@@ -35,6 +35,12 @@ contract TestBase is Test {
MapleWithdrawalManagerFactory internal factory;
MapleWithdrawalManagerHarness internal withdrawalManager;
+ event ManualWithdrawalSet(address indexed account, bool isManual);
+ event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
+ event RequestDecreased(uint256 indexed requestId, uint256 shares);
+ event RequestProcessed(uint256 indexed requestId, address indexed owner, uint256 shares, uint256 assets);
+ event RequestRemoved(uint256 indexed requestId);
+
function setUp() public virtual {
// Create all mocks.
asset = new MockERC20("Wrapped Ether", "WETH", 18);
From 7b31fbd66f1b165caa6191bd0e4cd09dafdb7c60 Mon Sep 17 00:00:00 2001
From: Cal Mac Fadden <108666242+calmacfadden@users.noreply.github.com>
Date: Thu, 18 Sep 2025 16:53:37 +0400
Subject: [PATCH 11/21] feat: WithdrawalManager access control changes
(SC-21760) (#41)
* feat: changed access control
Signed-off-by: calmacfadden
* feat: added tests
Signed-off-by: calmacfadden
---------
Signed-off-by: calmacfadden
---
contracts/MapleWithdrawalManager.sol | 20 ++++++++++----------
tests/unit/ProcessRedemptions.t.sol | 22 +++++++---------------
tests/unit/RemoveRequest.t.sol | 10 ++++++++--
tests/unit/RemoveSharesById.t.sol | 2 +-
tests/unit/SetManualWithdrawal.t.sol | 10 ++++++++--
5 files changed, 34 insertions(+), 30 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 67ca43e..75e38dc 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -62,27 +62,22 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
modifier onlyRedeemer {
- address globals_ = globals();
-
require(
msg.sender == IPoolManagerLike(poolManager).poolDelegate() ||
- msg.sender == IGlobalsLike(globals_).governor() ||
- msg.sender == IGlobalsLike(globals_).operationalAdmin() ||
- IGlobalsLike(globals_).isInstanceOf("WITHDRAWAL_REDEEMER", msg.sender),
+ IGlobalsLike(globals()).isInstanceOf("WITHDRAWAL_REDEEMER", msg.sender),
"WM:NOT_REDEEMER"
);
_;
}
- modifier onlyPoolDelegateOrProtocolAdmins {
+ modifier onlyPoolDelegateOrOperationalAdmin {
address globals_ = globals();
require(
msg.sender == IPoolManagerLike(poolManager).poolDelegate() ||
- msg.sender == IGlobalsLike(globals_).governor() ||
msg.sender == IGlobalsLike(globals_).operationalAdmin(),
- "WM:NOT_PD_OR_GOV_OR_OA"
+ "WM:NOT_POOL_DELEG_OR_OPS_ADMIN"
);
_;
@@ -210,7 +205,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
address owner_,
uint256[] calldata requestIds_
)
- external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins
+ external override whenProtocolNotPaused onlyPoolDelegateOrOperationalAdmin
{
require(owner_ != address(0), "WM:RR:ZERO_OWNER");
require(requestIds_.length > 0, "WM:RR:ZERO_REQUESTS");
@@ -235,7 +230,12 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
totalShares -= sharesToRemove_;
}
- function setManualWithdrawal(address owner_, bool isManual_) external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins {
+ function setManualWithdrawal(
+ address owner_,
+ bool isManual_
+ )
+ external override whenProtocolNotPaused onlyPoolDelegateOrOperationalAdmin
+ {
isManualWithdrawal[owner_] = isManual_;
emit ManualWithdrawalSet(owner_, isManual_);
diff --git a/tests/unit/ProcessRedemptions.t.sol b/tests/unit/ProcessRedemptions.t.sol
index d41130c..83549a4 100644
--- a/tests/unit/ProcessRedemptions.t.sol
+++ b/tests/unit/ProcessRedemptions.t.sol
@@ -18,6 +18,8 @@ contract ProcessRedemptionsTests is TestBase {
poolManager.__setTotalAssets(assetsDeposited);
withdrawalManager.__setTotalShares(sharesLocked);
+
+ globals.__setIsInstanceOf(false);
}
function test_processRedemptions_protocolPaused() external {
@@ -28,8 +30,12 @@ contract ProcessRedemptionsTests is TestBase {
}
function test_processRedemptions_notRedeemer() external {
- globals.__setIsInstanceOf(false);
+ vm.expectRevert("WM:NOT_REDEEMER");
+ withdrawalManager.processRedemptions(sharesLocked);
+ }
+ function test_processRedemptions_governorNotAllowed() external {
+ vm.prank(governor);
vm.expectRevert("WM:NOT_REDEEMER");
withdrawalManager.processRedemptions(sharesLocked);
}
@@ -55,20 +61,6 @@ contract ProcessRedemptionsTests is TestBase {
assertQueue({ nextRequestId: 1, lastRequestId: 0 });
}
- function test_processRedemptions_governor() external {
- vm.prank(governor);
- withdrawalManager.processRedemptions(sharesLocked);
-
- assertQueue({ nextRequestId: 1, lastRequestId: 0 });
- }
-
- function test_processRedemptions_operationalAdmin() external {
- vm.prank(operationalAdmin);
- withdrawalManager.processRedemptions(sharesLocked);
-
- assertQueue({ nextRequestId: 1, lastRequestId: 0 });
- }
-
function test_processRedemptions_bot() external {
globals.__setIsInstanceOf(true);
diff --git a/tests/unit/RemoveRequest.t.sol b/tests/unit/RemoveRequest.t.sol
index b8bdadf..964e104 100644
--- a/tests/unit/RemoveRequest.t.sol
+++ b/tests/unit/RemoveRequest.t.sol
@@ -21,8 +21,14 @@ contract RemoveRequestTests is TestBase {
withdrawalManager.removeRequest(lp, new uint256[](0));
}
- function test_removeRequest_notProtocolAdmin() external {
- vm.expectRevert("WM:NOT_PD_OR_GOV_OR_OA");
+ function test_removeRequest_notPoolDelegateOrAdmin() external {
+ vm.expectRevert("WM:NOT_POOL_DELEG_OR_OPS_ADMIN");
+ withdrawalManager.removeRequest(lp, new uint256[](0));
+ }
+
+ function test_removeRequest_governorNotAllowed() external {
+ vm.prank(governor);
+ vm.expectRevert("WM:NOT_POOL_DELEG_OR_OPS_ADMIN");
withdrawalManager.removeRequest(lp, new uint256[](0));
}
diff --git a/tests/unit/RemoveSharesById.t.sol b/tests/unit/RemoveSharesById.t.sol
index 583000f..ad81aeb 100644
--- a/tests/unit/RemoveSharesById.t.sol
+++ b/tests/unit/RemoveSharesById.t.sol
@@ -42,7 +42,7 @@ contract RemoveSharesByIdFailureTests is TestBase {
uint256[] memory requestIds = new uint256[](1);
requestIds[0] = 1;
- vm.prank(governor);
+ vm.prank(operationalAdmin);
withdrawalManager.removeRequest(lp, requestIds);
vm.expectRevert("WM:RSBI:INVALID_REQUEST");
diff --git a/tests/unit/SetManualWithdrawal.t.sol b/tests/unit/SetManualWithdrawal.t.sol
index c301ec8..c946221 100644
--- a/tests/unit/SetManualWithdrawal.t.sol
+++ b/tests/unit/SetManualWithdrawal.t.sol
@@ -12,8 +12,14 @@ contract SetManualWithdrawalTests is TestBase {
withdrawalManager.setManualWithdrawal(lp, true);
}
- function test_setManualWithdrawal_notProtocolAdmin() external {
- vm.expectRevert("WM:NOT_PD_OR_GOV_OR_OA");
+ function test_setManualWithdrawal_notPoolDelegateOrOpsAdmin() external {
+ vm.expectRevert("WM:NOT_POOL_DELEG_OR_OPS_ADMIN");
+ withdrawalManager.setManualWithdrawal(lp, true);
+ }
+
+ function test_setManualWithdrawal_governotNotAllowed() external {
+ vm.prank(governor);
+ vm.expectRevert("WM:NOT_POOL_DELEG_OR_OPS_ADMIN");
withdrawalManager.setManualWithdrawal(lp, true);
}
From d2725904f8c681b450da60c1bcaf63c17060b046 Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Thu, 25 Sep 2025 13:36:58 +0400
Subject: [PATCH 12/21] feat: Internal Audit fixes (#42)
* feat: update event per OC request
* feat: Internal audit fixes
* feat: fix foundry TOML
* fix: comment out fuzz tests
* fix: comment out fuzz tests
* fix: comment out fuzz tests
* fix: Alignment
---
contracts/MapleWithdrawalManager.sol | 16 ++---
.../interfaces/IMapleWithdrawalManager.sol | 3 +-
.../MapleWithdrawalManagerMigratorV200.sol | 13 ++--
.../proxy/MapleWithdrawalManagerStorage.sol | 2 +-
contracts/utils/SortedArray.sol | 65 +++++++++----------
foundry.toml | 4 +-
tests/fuzz/AddSharesFuzz.t.sol | 4 +-
tests/fuzz/RemoveSharesFuzz.t.sol | 4 +-
tests/utils/Harnesses.sol | 12 ++--
9 files changed, 62 insertions(+), 61 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 75e38dc..2bec7ed 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -156,7 +156,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
require(totalEscrowedShares_ >= shares_, "WM:RS:INSUFFICIENT_SHARES");
while (sharesReturned_ < shares_) {
- uint256 requestId_ = SortedArray.getLast(userRequests[owner_]);
+ uint256 requestId_ = SortedArray.getLast(_userRequests[owner_]);
WithdrawalRequest memory request_ = queue.requests[_toUint128(requestId_)];
uint256 sharesToRemove_ = _min(shares_ - sharesReturned_, request_.shares);
@@ -233,8 +233,8 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
function setManualWithdrawal(
address owner_,
bool isManual_
- )
- external override whenProtocolNotPaused onlyPoolDelegateOrOperationalAdmin
+ )
+ external override whenProtocolNotPaused onlyPoolDelegateOrOperationalAdmin
{
isManualWithdrawal[owner_] = isManual_;
@@ -273,7 +273,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
queue.requests[_toUint128(lastRequestId_)] = WithdrawalRequest(owner_, shares_);
userEscrowedShares[owner_] += shares_;
- SortedArray.push(userRequests[owner_], lastRequestId_);
+ SortedArray.push(_userRequests[owner_], lastRequestId_);
// Increase the number of shares locked.
totalShares += shares_;
@@ -398,7 +398,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
if (isManualWithdrawal[request_.owner]) {
manualSharesAvailable[request_.owner] += processedShares_;
- emit ManualSharesIncreased(request_.owner, processedShares_);
+ emit ManualSharesIncreased(requestId_, request_.owner, processedShares_);
} else {
// Otherwise, just adjust totalShares and perform the redeem.
totalShares -= processedShares_;
@@ -409,7 +409,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
function _removeRequest(address owner_, uint256 requestId_) internal {
userEscrowedShares[owner_] -= queue.requests[_toUint128(requestId_)].shares;
- SortedArray.remove(userRequests[owner_], requestId_);
+ SortedArray.remove(_userRequests[owner_], requestId_);
delete queue.requests[_toUint128(requestId_)];
emit RequestRemoved(requestId_);
@@ -491,7 +491,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function requestIds(address owner_) external view override returns (uint256 requestId_) {
- requestId_ = SortedArray.getLast(userRequests[owner_]);
+ requestId_ = SortedArray.getLast(_userRequests[owner_]);
}
function requests(uint256 requestId_) external view override returns (address owner_, uint256 shares_) {
@@ -500,7 +500,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function requestsByOwner(address owner_) external view override returns (uint256[] memory requestIds_, uint256[] memory shares_) {
- requestIds_ = SortedArray.getAllValues(userRequests[owner_]);
+ requestIds_ = SortedArray.getAllValues(_userRequests[owner_]);
shares_ = new uint256[](requestIds_.length);
for (uint256 i = 0; i < requestIds_.length; ++i) {
diff --git a/contracts/interfaces/IMapleWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol
index fb24d4f..50b8fd1 100644
--- a/contracts/interfaces/IMapleWithdrawalManager.sol
+++ b/contracts/interfaces/IMapleWithdrawalManager.sol
@@ -20,10 +20,11 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
/**
* @dev Emitted when a manual redemption is processed.
+ * @param requestId Identifier of the withdrawal request.
* @param owner Address of the account.
* @param sharesAdded Amount of shares added to the redeemable amount.
*/
- event ManualSharesIncreased(address indexed owner, uint256 sharesAdded);
+ event ManualSharesIncreased(uint256 indexed requestId, address indexed owner, uint256 sharesAdded);
/**
* @dev Emitted when the withdrawal type of an account is updated.
diff --git a/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol b/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
index bf2c7bf..1c4316c 100644
--- a/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
+++ b/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
@@ -14,12 +14,13 @@ contract MapleWithdrawalManagerMigratorV200 is MapleProxiedInternals, MapleWithd
uint128 lastRequestId_ = queue.lastRequestId;
for (uint128 i = nextRequestId_; i <= lastRequestId_; ++i) {
- WithdrawalRequest storage request = queue.requests[i];
-
- if (request.owner != address(0)) {
- userEscrowedShares[request.owner] += request.shares;
- SortedArray.push(userRequests[request.owner], i);
- }
+ WithdrawalRequest memory request_ = queue.requests[i];
+
+ if (request_.owner == address(0)) continue;
+
+ userEscrowedShares[request_.owner] += request_.shares;
+
+ SortedArray.push(_userRequests[request_.owner], i);
}
}
diff --git a/contracts/proxy/MapleWithdrawalManagerStorage.sol b/contracts/proxy/MapleWithdrawalManagerStorage.sol
index 42510ad..00faadc 100644
--- a/contracts/proxy/MapleWithdrawalManagerStorage.sol
+++ b/contracts/proxy/MapleWithdrawalManagerStorage.sol
@@ -43,6 +43,6 @@ contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
mapping(address => uint256) public override userEscrowedShares; // Maps users to their escrowed shares yet to be processed.
- mapping(address => SortedArray.Array) internal userRequests; // Maps users to their withdrawal requests.
+ mapping(address => SortedArray.Array) internal _userRequests; // Maps users to their withdrawal requests.
}
diff --git a/contracts/utils/SortedArray.sol b/contracts/utils/SortedArray.sol
index 5ada4cf..e030850 100644
--- a/contracts/utils/SortedArray.sol
+++ b/contracts/utils/SortedArray.sol
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.7;
-// TODO: How to best handle library interfaces?
library SortedArray {
struct Array {
@@ -17,40 +16,40 @@ library SortedArray {
/**
* @dev Pushes a value to the array.
* It is expected that the value is biggest so far so it will be added at the end of the array.
- * @param array_ The array to push the value to.
+ * @param array The array to push the value to.
* @param value_ The value to push to the array.
*/
- function push(Array storage array_, uint256 value_) internal {
- require(getLast(array_) < value_, "SA:P:NOT_LARGEST");
+ function push(Array storage array, uint256 value_) internal {
+ require(getLast(array) < value_, "SA:P:NOT_LARGEST");
- array_.values.push(value_);
- array_.valueToIndex[value_] = length(array_) - 1;
+ array.values.push(value_);
+ array.valueToIndex[value_] = length(array) - 1;
}
/**
* @dev Removes a value from the array.
* It shifts the rest of the array to the left.
* It is expected by contract that uses this library to check that the value is present in the array before call of the function.
- * @param array_ The array to remove the value from.
+ * @param array The array to remove the value from.
* @param value_ The value to remove from the array.
*/
- function remove(Array storage array_, uint256 value_) internal {
- uint256 length_ = length(array_);
+ function remove(Array storage array, uint256 value_) internal {
+ uint256 length_ = length(array);
if (length_ == 1) {
- _deleteValue(array_, value_);
+ _deleteValue(array, value_);
return;
}
- uint256 index_ = array_.valueToIndex[value_];
+ uint256 index_ = array.valueToIndex[value_];
for (uint256 i = index_; i <= length_ - 2; i++) {
- uint256 nextValue_ = array_.values[i + 1];
- array_.values[i] = nextValue_;
- array_.valueToIndex[nextValue_] = i;
+ uint256 nextValue_ = array.values[i + 1];
+ array.values[i] = nextValue_;
+ array.valueToIndex[nextValue_] = i;
}
- _deleteValue(array_, value_);
+ _deleteValue(array, value_);
}
/**************************************************************************************************************************************/
@@ -59,41 +58,41 @@ library SortedArray {
/**
* @dev Gets a value from the array at given index.
- * @param array_ The array to get the value from.
+ * @param array The array to get the value from.
* @param index_ The index of the value to get from the array.
* @return value_ The value at the given index.
*/
- function get(Array storage array_, uint256 index_) internal view returns (uint256 value_) {
- require(index_ < length(array_), "SA:G:OUT_OF_BOUNDS");
- value_ = array_.values[index_];
+ function get(Array storage array, uint256 index_) internal view returns (uint256 value_) {
+ require(index_ < length(array), "SA:G:OUT_OF_BOUNDS");
+ value_ = array.values[index_];
}
/**
* @dev Gets the length of the array.
- * @param array_ The array to get the length of.
+ * @param array The array to get the length of.
* @return length_ The length of the array.
*/
- function length(Array storage array_) internal view returns (uint256 length_) {
- length_ = array_.values.length;
+ function length(Array storage array) internal view returns (uint256 length_) {
+ length_ = array.values.length;
}
/**
* @dev Gets all values from the array.
- * @param array_ The array to get the values from.
+ * @param array The array to get the values from.
* @return values_ All values from the array.
*/
- function getAllValues(Array storage array_) internal view returns (uint256[] memory values_) {
- values_ = array_.values;
+ function getAllValues(Array storage array) internal view returns (uint256[] memory values_) {
+ values_ = array.values;
}
/**
* @dev Gets the last value in the array.
- * @param array_ The array to get the last value from.
+ * @param array The array to get the last value from.
* @return value_ The last value in the array.
*/
- function getLast(Array storage array_) internal view returns (uint256 value_) {
- uint256 length_ = length(array_);
- return length_ > 0 ? array_.values[length_ - 1] : 0;
+ function getLast(Array storage array) internal view returns (uint256 value_) {
+ uint256 length_ = length(array);
+ return length_ > 0 ? array.values[length_ - 1] : 0;
}
/**************************************************************************************************************************************/
@@ -103,12 +102,12 @@ library SortedArray {
/**
* @dev Deletes a value from the array.
* It is expected that array is already shifted so this will just pop the last and flag given value as not present.
- * @param array_ The array to delete the value from.
+ * @param array The array to delete the value from.
* @param value_ The value to delete from the array.
*/
- function _deleteValue(Array storage array_, uint256 value_) private {
- array_.values.pop();
- delete array_.valueToIndex[value_];
+ function _deleteValue(Array storage array, uint256 value_) private {
+ array.values.pop();
+ delete array.valueToIndex[value_];
}
}
diff --git a/foundry.toml b/foundry.toml
index 05293fe..172f501 100644
--- a/foundry.toml
+++ b/foundry.toml
@@ -8,8 +8,8 @@ verbosity = 3 # The verbosity of tests
block_timestamp = 1_622_400_000 # Timestamp for tests (non-zero)
fuzz_runs = 100 # Number of fuzz runs
-[profile.deep]
-fuzz_runs = 1000
+[profile.ci.fuzz]
+runs = 1000
[profile.super_deep]
fuzz_runs = 50000
diff --git a/tests/fuzz/AddSharesFuzz.t.sol b/tests/fuzz/AddSharesFuzz.t.sol
index 67d6bbc..0c079c4 100644
--- a/tests/fuzz/AddSharesFuzz.t.sol
+++ b/tests/fuzz/AddSharesFuzz.t.sol
@@ -9,7 +9,7 @@ contract AddSharesFuzzTests is TestBase {
super.setUp();
}
- function testFuzz_addShares(uint256[50] memory amount_, address[50] calldata account_) external {
+ function testFuzz_addShares(uint256[10] memory amount_, address[10] calldata account_) external {
uint256 lastRequestId;
uint256 totalShares_;
uint256 requestId_;
@@ -17,7 +17,7 @@ contract AddSharesFuzzTests is TestBase {
for (uint256 i; i < account_.length; ++i) {
amount_[i] = bound(amount_[i], 1, 1e29);
-
+
pool.mint(pm, amount_[i]);
vm.startPrank(pm);
diff --git a/tests/fuzz/RemoveSharesFuzz.t.sol b/tests/fuzz/RemoveSharesFuzz.t.sol
index b44d15a..52eed7d 100644
--- a/tests/fuzz/RemoveSharesFuzz.t.sol
+++ b/tests/fuzz/RemoveSharesFuzz.t.sol
@@ -9,14 +9,14 @@ contract RemoveSharesFuzzTests is TestBase {
super.setUp();
}
- function testFuzz_removeShares(address[50] calldata account_, uint256[50] memory amount0_, uint256[50] memory amount1_) external {
+ function testFuzz_removeShares(address[10] calldata account_, uint256[10] memory amount0_, uint256[10] memory amount1_) external {
address owner_;
uint256 lastRequestId;
uint256 shares_;
uint256 totalShares_;
uint256 requestId_;
-
+
for (uint256 i; i < account_.length; ++i) {
amount0_[i] = bound(amount0_[i], 1, 1e29);
amount1_[i] = bound(amount1_[i], 1, 1e29);
diff --git a/tests/utils/Harnesses.sol b/tests/utils/Harnesses.sol
index 7284b8c..44689f0 100644
--- a/tests/utils/Harnesses.sol
+++ b/tests/utils/Harnesses.sol
@@ -20,7 +20,7 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
function __setLastRequest(address owner_, uint256 requestId_) external {
- SortedArray.push(userRequests[owner_], requestId_);
+ SortedArray.push(_userRequests[owner_], requestId_);
queue.lastRequestId = _toUint128(requestId_);
}
@@ -42,7 +42,7 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
queue.lastRequestId = requestId_;
queue.requests[requestId_] = WithdrawalRequest(owner_, shares_);
- SortedArray.push(userRequests[owner_], requestId_);
+ SortedArray.push(_userRequests[owner_], requestId_);
}
function __setTotalShares(uint256 totalShares_) external {
@@ -50,15 +50,15 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
function __setUserRequestCount(address owner_, uint256 requestCount_, uint256 escrowSharesTotal_) external {
- uint256 currentRequestCount_ = SortedArray.length(userRequests[owner_]);
+ uint256 currentRequestCount_ = SortedArray.length(_userRequests[owner_]);
if (requestCount_ < currentRequestCount_) {
for (uint256 i = requestCount_; i < currentRequestCount_; i++) {
- SortedArray.remove(userRequests[owner_], _toUint128(i));
+ SortedArray.remove(_userRequests[owner_], _toUint128(i));
}
} else {
for (uint256 i = currentRequestCount_; i > requestCount_; i++) {
- SortedArray.push(userRequests[owner_], _toUint128(i));
+ SortedArray.push(_userRequests[owner_], _toUint128(i));
}
}
@@ -95,4 +95,4 @@ contract SortedArrayHarness {
return SortedArray.getLast(array);
}
-}
+}
From 31481c14edd9f95f0ad8a6f189ff500e3f88c5ff Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Mon, 13 Oct 2025 15:35:30 +0400
Subject: [PATCH 13/21] feat: Use external over public (#44)
---
contracts/MapleWithdrawalManager.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 2bec7ed..5268f92 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -249,7 +249,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
uint256 requestId_,
uint256 sharesToRemove_
)
- public override whenProtocolNotPaused nonReentrant returns (uint256 sharesReturned_, uint256 sharesRemaining_)
+ external override whenProtocolNotPaused nonReentrant returns (uint256 sharesReturned_, uint256 sharesRemaining_)
{
WithdrawalRequest memory request_ = queue.requests[_toUint128(requestId_)];
From 27385213a4c363f9ad909676fff9857e8466650b Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Mon, 13 Oct 2025 15:35:49 +0400
Subject: [PATCH 14/21] Audit: Fix for loop usage for removing shares via admin
(#45)
---
contracts/MapleWithdrawalManager.sol | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 5268f92..9d1da8a 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -223,10 +223,10 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_removeRequest(owner_, requestIds_[i]);
sharesToRemove_ += withdrawalRequest_.shares;
-
- require(ERC20Helper.transfer(pool, owner_, withdrawalRequest_.shares), "WM:RR:TRANSFER_FAIL");
}
+ require(ERC20Helper.transfer(pool, owner_, sharesToRemove_), "WM:RR:TRANSFER_FAIL");
+
totalShares -= sharesToRemove_;
}
From 335ea608e8344dba2d85f832804e46c4f8aabaa0 Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Mon, 13 Oct 2025 15:40:06 +0400
Subject: [PATCH 15/21] Audit: Add comment clarifying usage for `removeRequest`
on WM (#46)
* Audit: Add comment clarifying usage for removeRequest on WM
* chore: add comment
---
contracts/MapleWithdrawalManager.sol | 1 +
contracts/interfaces/IMapleWithdrawalManager.sol | 1 +
2 files changed, 2 insertions(+)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 9d1da8a..06053ac 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -201,6 +201,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
/*** State-Changing Functions - Admin functions ***/
/**************************************************************************************************************************************/
+ // NOTE: Not to be used in a router based system where the router is managing user requests.
function removeRequest(
address owner_,
uint256[] calldata requestIds_
diff --git a/contracts/interfaces/IMapleWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol
index 50b8fd1..e51637b 100644
--- a/contracts/interfaces/IMapleWithdrawalManager.sol
+++ b/contracts/interfaces/IMapleWithdrawalManager.sol
@@ -112,6 +112,7 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
/**
* @dev Removes withdrawal requests from the queue.
* Can only be called by the pool delegate.
+ * NOTE: Not to be used in a router based system where the router is managing user requests.
* @param owner Address of the owner of shares.
* @param requestIds Array of identifiers of the withdrawal requests to remove.
*/
From 74cf0c348ee3606a97eb7e2c35d38b67a1474f48 Mon Sep 17 00:00:00 2001
From: Vedran Bidin
Date: Wed, 15 Oct 2025 11:22:45 +0700
Subject: [PATCH 16/21] fix: Add bot support for `removeRequest()` (SC-22023)
(#47)
* fix: add bot support for removeRequests()
* chore: Fix format
---------
Co-authored-by: 0xfarhaan <59924029+0xfarhaan@users.noreply.github.com>
---
contracts/MapleWithdrawalManager.sol | 31 ++++++++++++++--------------
tests/unit/RemoveRequest.t.sol | 10 +++------
2 files changed, 18 insertions(+), 23 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 06053ac..287b881 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -61,16 +61,6 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_locked = 1;
}
- modifier onlyRedeemer {
- require(
- msg.sender == IPoolManagerLike(poolManager).poolDelegate() ||
- IGlobalsLike(globals()).isInstanceOf("WITHDRAWAL_REDEEMER", msg.sender),
- "WM:NOT_REDEEMER"
- );
-
- _;
- }
-
modifier onlyPoolDelegateOrOperationalAdmin {
address globals_ = globals();
@@ -89,6 +79,19 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
_;
}
+ modifier onlyRedeemer {
+ address globals_ = globals();
+
+ require(
+ IGlobalsLike(globals_).isInstanceOf("WITHDRAWAL_REDEEMER", msg.sender) ||
+ msg.sender == IPoolManagerLike(poolManager).poolDelegate() ||
+ msg.sender == IGlobalsLike(globals_).operationalAdmin(),
+ "WM:NOT_REDEEMER"
+ );
+
+ _;
+ }
+
modifier whenProtocolNotPaused() {
require(!IGlobalsLike(globals()).isFunctionPaused(msg.sig), "WM:PAUSED");
_;
@@ -165,7 +168,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
/**************************************************************************************************************************************/
- /*** State-Changing Functions - onlyRedeemer ***/
+ /*** State-Changing Functions - OnlyRedeemer ***/
/**************************************************************************************************************************************/
function processRedemptions(uint256 maxSharesToProcess_) external override whenProtocolNotPaused nonReentrant onlyRedeemer {
@@ -197,16 +200,12 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
queue.nextRequestId = _toUint128(nextRequestId_);
}
- /**************************************************************************************************************************************/
- /*** State-Changing Functions - Admin functions ***/
- /**************************************************************************************************************************************/
-
// NOTE: Not to be used in a router based system where the router is managing user requests.
function removeRequest(
address owner_,
uint256[] calldata requestIds_
)
- external override whenProtocolNotPaused onlyPoolDelegateOrOperationalAdmin
+ external override whenProtocolNotPaused onlyRedeemer
{
require(owner_ != address(0), "WM:RR:ZERO_OWNER");
require(requestIds_.length > 0, "WM:RR:ZERO_REQUESTS");
diff --git a/tests/unit/RemoveRequest.t.sol b/tests/unit/RemoveRequest.t.sol
index 964e104..50a98ce 100644
--- a/tests/unit/RemoveRequest.t.sol
+++ b/tests/unit/RemoveRequest.t.sol
@@ -21,14 +21,10 @@ contract RemoveRequestTests is TestBase {
withdrawalManager.removeRequest(lp, new uint256[](0));
}
- function test_removeRequest_notPoolDelegateOrAdmin() external {
- vm.expectRevert("WM:NOT_POOL_DELEG_OR_OPS_ADMIN");
- withdrawalManager.removeRequest(lp, new uint256[](0));
- }
+ function test_removeRequest_notAuthorized() external {
+ globals.__setIsInstanceOf(false);
- function test_removeRequest_governorNotAllowed() external {
- vm.prank(governor);
- vm.expectRevert("WM:NOT_POOL_DELEG_OR_OPS_ADMIN");
+ vm.expectRevert("WM:NOT_REDEEMER");
withdrawalManager.removeRequest(lp, new uint256[](0));
}
From 28b18e0fb23f9f7ed763d22e0233991fc9794494 Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Thu, 16 Oct 2025 09:02:28 +0400
Subject: [PATCH 17/21] Audit: Swap Array to LinkedList (#43)
* feat: Swap Array to LinkedList
* feat: update sorted linked list
* fix: rename array to list
* fix: remove get function from library
* fix: cache list size
* chore: format:
* fix: remove unneccesary casting
---------
Co-authored-by: Danilo
---
contracts/MapleWithdrawalManager.sol | 21 +--
.../MapleWithdrawalManagerMigratorV200.sol | 4 +-
.../proxy/MapleWithdrawalManagerStorage.sol | 4 +-
contracts/utils/SortedArray.sol | 113 ---------------
contracts/utils/SortedLinkedList.sol | 132 ++++++++++++++++++
tests/unit/SortedArray/Get.t.sol | 29 ----
tests/unit/SortedArray/Push.t.sol | 42 +++---
tests/unit/SortedArray/Remove.t.sol | 46 +++---
tests/utils/Harnesses.sol | 38 +++--
tests/utils/TestBase.sol | 20 ++-
10 files changed, 221 insertions(+), 228 deletions(-)
delete mode 100644 contracts/utils/SortedArray.sol
create mode 100644 contracts/utils/SortedLinkedList.sol
delete mode 100644 tests/unit/SortedArray/Get.t.sol
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 287b881..515390d 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -16,7 +16,7 @@ import {
import { MapleWithdrawalManagerStorage } from "./proxy/MapleWithdrawalManagerStorage.sol";
-import { SortedArray } from "./utils/SortedArray.sol";
+import { SortedLinkedList } from "./utils/SortedLinkedList.sol";
/*
@@ -159,7 +159,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
require(totalEscrowedShares_ >= shares_, "WM:RS:INSUFFICIENT_SHARES");
while (sharesReturned_ < shares_) {
- uint256 requestId_ = SortedArray.getLast(_userRequests[owner_]);
+ uint256 requestId_ = SortedLinkedList.getLast(_userRequests[owner_]);
WithdrawalRequest memory request_ = queue.requests[_toUint128(requestId_)];
uint256 sharesToRemove_ = _min(shares_ - sharesReturned_, request_.shares);
@@ -273,7 +273,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
queue.requests[_toUint128(lastRequestId_)] = WithdrawalRequest(owner_, shares_);
userEscrowedShares[owner_] += shares_;
- SortedArray.push(_userRequests[owner_], lastRequestId_);
+ SortedLinkedList.push(_userRequests[owner_], _toUint128(lastRequestId_));
// Increase the number of shares locked.
totalShares += shares_;
@@ -409,7 +409,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
function _removeRequest(address owner_, uint256 requestId_) internal {
userEscrowedShares[owner_] -= queue.requests[_toUint128(requestId_)].shares;
- SortedArray.remove(_userRequests[owner_], requestId_);
+ SortedLinkedList.remove(_userRequests[owner_], _toUint128(requestId_));
delete queue.requests[_toUint128(requestId_)];
emit RequestRemoved(requestId_);
@@ -491,7 +491,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function requestIds(address owner_) external view override returns (uint256 requestId_) {
- requestId_ = SortedArray.getLast(_userRequests[owner_]);
+ requestId_ = SortedLinkedList.getLast(_userRequests[owner_]);
}
function requests(uint256 requestId_) external view override returns (address owner_, uint256 shares_) {
@@ -500,11 +500,14 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
}
function requestsByOwner(address owner_) external view override returns (uint256[] memory requestIds_, uint256[] memory shares_) {
- requestIds_ = SortedArray.getAllValues(_userRequests[owner_]);
- shares_ = new uint256[](requestIds_.length);
+ uint128[] memory requestIdsByOwner_ = SortedLinkedList.getAllValues(_userRequests[owner_]);
- for (uint256 i = 0; i < requestIds_.length; ++i) {
- shares_[i] = queue.requests[_toUint128(requestIds_[i])].shares;
+ requestIds_ = new uint256[](requestIdsByOwner_.length);
+ shares_ = new uint256[](requestIdsByOwner_.length);
+
+ for (uint256 i = 0; i < requestIdsByOwner_.length; ++i) {
+ requestIds_[i] = requestIdsByOwner_[i];
+ shares_[i] = queue.requests[requestIdsByOwner_[i]].shares;
}
}
diff --git a/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol b/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
index 1c4316c..e5d3034 100644
--- a/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
+++ b/contracts/proxy/MapleWithdrawalManagerMigratorV200.sol
@@ -3,7 +3,7 @@ pragma solidity ^0.8.7;
import { MapleProxiedInternals } from "../../modules/maple-proxy-factory/contracts/MapleProxiedInternals.sol";
-import { SortedArray } from "../utils/SortedArray.sol";
+import { SortedLinkedList } from "../utils/SortedLinkedList.sol";
import { MapleWithdrawalManagerStorage } from "./MapleWithdrawalManagerStorage.sol";
@@ -20,7 +20,7 @@ contract MapleWithdrawalManagerMigratorV200 is MapleProxiedInternals, MapleWithd
userEscrowedShares[request_.owner] += request_.shares;
- SortedArray.push(_userRequests[request_.owner], i);
+ SortedLinkedList.push(_userRequests[request_.owner], i);
}
}
diff --git a/contracts/proxy/MapleWithdrawalManagerStorage.sol b/contracts/proxy/MapleWithdrawalManagerStorage.sol
index 00faadc..ca92292 100644
--- a/contracts/proxy/MapleWithdrawalManagerStorage.sol
+++ b/contracts/proxy/MapleWithdrawalManagerStorage.sol
@@ -3,7 +3,7 @@ pragma solidity ^0.8.7;
import { IMapleWithdrawalManagerStorage } from "../interfaces/IMapleWithdrawalManagerStorage.sol";
-import { SortedArray } from "../utils/SortedArray.sol";
+import { SortedLinkedList } from "../utils/SortedLinkedList.sol";
contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
@@ -43,6 +43,6 @@ contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage {
mapping(address => uint256) public override userEscrowedShares; // Maps users to their escrowed shares yet to be processed.
- mapping(address => SortedArray.Array) internal _userRequests; // Maps users to their withdrawal requests.
+ mapping(address => SortedLinkedList.List) internal _userRequests; // Maps users to their withdrawal requests.
}
diff --git a/contracts/utils/SortedArray.sol b/contracts/utils/SortedArray.sol
deleted file mode 100644
index e030850..0000000
--- a/contracts/utils/SortedArray.sol
+++ /dev/null
@@ -1,113 +0,0 @@
-// SPDX-License-Identifier: BUSL-1.1
-pragma solidity ^0.8.7;
-
-library SortedArray {
-
- struct Array {
- uint256[] values;
-
- mapping(uint256 => uint256) valueToIndex;
- }
-
- /**************************************************************************************************************************************/
- /*** Write Functions ***/
- /**************************************************************************************************************************************/
-
- /**
- * @dev Pushes a value to the array.
- * It is expected that the value is biggest so far so it will be added at the end of the array.
- * @param array The array to push the value to.
- * @param value_ The value to push to the array.
- */
- function push(Array storage array, uint256 value_) internal {
- require(getLast(array) < value_, "SA:P:NOT_LARGEST");
-
- array.values.push(value_);
- array.valueToIndex[value_] = length(array) - 1;
- }
-
- /**
- * @dev Removes a value from the array.
- * It shifts the rest of the array to the left.
- * It is expected by contract that uses this library to check that the value is present in the array before call of the function.
- * @param array The array to remove the value from.
- * @param value_ The value to remove from the array.
- */
- function remove(Array storage array, uint256 value_) internal {
- uint256 length_ = length(array);
-
- if (length_ == 1) {
- _deleteValue(array, value_);
- return;
- }
-
- uint256 index_ = array.valueToIndex[value_];
-
- for (uint256 i = index_; i <= length_ - 2; i++) {
- uint256 nextValue_ = array.values[i + 1];
- array.values[i] = nextValue_;
- array.valueToIndex[nextValue_] = i;
- }
-
- _deleteValue(array, value_);
- }
-
- /**************************************************************************************************************************************/
- /*** View Functions ***/
- /**************************************************************************************************************************************/
-
- /**
- * @dev Gets a value from the array at given index.
- * @param array The array to get the value from.
- * @param index_ The index of the value to get from the array.
- * @return value_ The value at the given index.
- */
- function get(Array storage array, uint256 index_) internal view returns (uint256 value_) {
- require(index_ < length(array), "SA:G:OUT_OF_BOUNDS");
- value_ = array.values[index_];
- }
-
- /**
- * @dev Gets the length of the array.
- * @param array The array to get the length of.
- * @return length_ The length of the array.
- */
- function length(Array storage array) internal view returns (uint256 length_) {
- length_ = array.values.length;
- }
-
- /**
- * @dev Gets all values from the array.
- * @param array The array to get the values from.
- * @return values_ All values from the array.
- */
- function getAllValues(Array storage array) internal view returns (uint256[] memory values_) {
- values_ = array.values;
- }
-
- /**
- * @dev Gets the last value in the array.
- * @param array The array to get the last value from.
- * @return value_ The last value in the array.
- */
- function getLast(Array storage array) internal view returns (uint256 value_) {
- uint256 length_ = length(array);
- return length_ > 0 ? array.values[length_ - 1] : 0;
- }
-
- /**************************************************************************************************************************************/
- /*** Private Functions ***/
- /**************************************************************************************************************************************/
-
- /**
- * @dev Deletes a value from the array.
- * It is expected that array is already shifted so this will just pop the last and flag given value as not present.
- * @param array The array to delete the value from.
- * @param value_ The value to delete from the array.
- */
- function _deleteValue(Array storage array, uint256 value_) private {
- array.values.pop();
- delete array.valueToIndex[value_];
- }
-
-}
diff --git a/contracts/utils/SortedLinkedList.sol b/contracts/utils/SortedLinkedList.sol
new file mode 100644
index 0000000..97f1f1e
--- /dev/null
+++ b/contracts/utils/SortedLinkedList.sol
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+library SortedLinkedList {
+
+ struct Node {
+ uint128 next;
+ uint128 prev;
+ bool exists;
+ }
+
+ struct List {
+ uint128 head;
+ uint128 tail;
+ uint256 size;
+
+ mapping(uint128 => Node) nodes;
+ }
+
+ /**************************************************************************************************************************************/
+ /*** Write Functions ***/
+ /**************************************************************************************************************************************/
+
+ /**
+ * @dev Pushes a value to the list.
+ * It is expected that the value is biggest so far so it will be added at the end of the list.
+ * @param list The list to push the value to.
+ * @param value_ The value to push to the list.
+ */
+ function push(List storage list, uint128 value_) internal {
+ uint128 tail_ = list.tail;
+
+ require(value_ > 0, "SLL:P:ZERO_VALUE");
+ require(!contains(list, value_), "SLL:P:VALUE_EXISTS");
+ require(value_ > tail_, "SLL:P:NOT_LARGEST");
+
+ list.nodes[value_] = Node({
+ next: 0,
+ prev: tail_,
+ exists: true
+ });
+
+ if (tail_ != 0) {
+ list.nodes[tail_].next = value_;
+ }
+
+ list.tail = value_;
+
+ if (list.head == 0) {
+ list.head = value_;
+ }
+
+ list.size++;
+ }
+
+ /**
+ * @dev Removes a value from the list in O(1) time.
+ * @param list The list to remove the value from.
+ * @param value_ The value to remove from the list.
+ */
+ function remove(List storage list, uint128 value_) internal {
+ require(contains(list, value_), "SLL:R:VALUE_NOT_EXISTS");
+
+ uint128 prev_ = list.nodes[value_].prev;
+ uint128 next_ = list.nodes[value_].next;
+
+ if (prev_ != 0) {
+ list.nodes[prev_].next = next_;
+ } else {
+ list.head = next_;
+ }
+
+ if (next_ != 0) {
+ list.nodes[next_].prev = prev_;
+ } else {
+ list.tail = prev_;
+ }
+
+ delete list.nodes[value_];
+ list.size--;
+ }
+
+ /**************************************************************************************************************************************/
+ /*** View Functions ***/
+ /**************************************************************************************************************************************/
+
+ /**
+ * @dev Gets the length of the list.
+ * @param list The list to get the length of.
+ * @return length_ The length of the list.
+ */
+ function length(List storage list) internal view returns (uint256 length_) {
+ length_ = list.size;
+ }
+
+ /**
+ * @dev Gets all values from the list.
+ * @param list The list to get the values from.
+ * @return values_ All values from the list.
+ */
+ function getAllValues(List storage list) internal view returns (uint128[] memory values_) {
+ values_ = new uint128[](list.size);
+
+ uint128 current_ = list.head;
+ uint256 size_ = list.size;
+
+ for (uint256 i = 0; i < size_; i++) {
+ values_[i] = current_;
+ current_ = list.nodes[current_].next;
+ }
+ }
+
+ /**
+ * @dev Gets the last value in the list.
+ * @param list The list to get the last value from.
+ * @return value_ The last value in the list.
+ */
+ function getLast(List storage list) internal view returns (uint128 value_) {
+ value_ = list.tail;
+ }
+
+ /**
+ * @dev Checks if a value exists in the list.
+ * @param list The list to check.
+ * @param value_ The value to check for.
+ * @return exists_ True if the value exists in the list.
+ */
+ function contains(List storage list, uint128 value_) internal view returns (bool exists_) {
+ exists_ = list.nodes[value_].exists;
+ }
+
+}
diff --git a/tests/unit/SortedArray/Get.t.sol b/tests/unit/SortedArray/Get.t.sol
deleted file mode 100644
index 5d661d1..0000000
--- a/tests/unit/SortedArray/Get.t.sol
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: BUSL-1.1
-pragma solidity ^0.8.7;
-
-import { SortedArrayTestBase } from "../../utils/TestBase.sol";
-
-contract GetTests is SortedArrayTestBase {
-
- function test_get_outOfBounds() external {
- array.push(1);
- vm.expectRevert("SA:G:OUT_OF_BOUNDS");
- array.get(1);
- }
-
- function test_get_singleValue() external {
- array.push(1);
- assertEq(array.get(0), 1);
- }
-
- function test_get_multipleValues() external {
- array.push(1);
- array.push(2);
- array.push(3);
-
- assertEq(array.get(0), 1);
- assertEq(array.get(1), 2);
- assertEq(array.get(2), 3);
- }
-
-}
diff --git a/tests/unit/SortedArray/Push.t.sol b/tests/unit/SortedArray/Push.t.sol
index d33d27b..7406194 100644
--- a/tests/unit/SortedArray/Push.t.sol
+++ b/tests/unit/SortedArray/Push.t.sol
@@ -2,44 +2,52 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.7;
-import { SortedArrayTestBase } from "../../utils/TestBase.sol";
+import { SortedLinkedListTestBase } from "../../utils/TestBase.sol";
-contract PushTests is SortedArrayTestBase {
+contract PushTests is SortedLinkedListTestBase {
+
+ function test_push_failed_zeroValue() external {
+ vm.expectRevert("SLL:P:ZERO_VALUE");
+ list.push(0);
+ }
+
+ function test_push_failed_valueExists() external {
+ list.push(1);
+
+ vm.expectRevert("SLL:P:VALUE_EXISTS");
+ list.push(1);
+ }
function test_push_failed_outOfOrder() external {
- array.push(3);
+ list.push(3);
- vm.expectRevert("SA:P:NOT_LARGEST");
- array.push(1);
+ vm.expectRevert("SLL:P:NOT_LARGEST");
+ list.push(1);
}
function test_push_singleValue() external {
- array.push(3);
+ list.push(3);
uint256[] memory expectedValues = new uint256[](1);
expectedValues[0] = 3;
- assertArray(expectedValues);
- assertElementAtIndex(0, 3);
+ assertList(expectedValues);
- assertEq(array.length(), 1);
+ assertEq(list.length(), 1);
}
function test_push_multipleValues_inOrder() external {
- array.push(1);
- array.push(3);
- array.push(7);
+ list.push(1);
+ list.push(3);
+ list.push(7);
uint256[] memory expectedValues = new uint256[](3);
expectedValues[0] = 1;
expectedValues[1] = 3;
expectedValues[2] = 7;
- assertArray(expectedValues);
- assertElementAtIndex(0, 1);
- assertElementAtIndex(1, 3);
- assertElementAtIndex(2, 7);
- assertEq(array.length(), 3);
+ assertList(expectedValues);
+ assertEq(list.length(), 3);
}
}
diff --git a/tests/unit/SortedArray/Remove.t.sol b/tests/unit/SortedArray/Remove.t.sol
index 8bac4d0..2e4ce16 100644
--- a/tests/unit/SortedArray/Remove.t.sol
+++ b/tests/unit/SortedArray/Remove.t.sol
@@ -1,51 +1,51 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.7;
-import { SortedArrayTestBase } from "../../utils/TestBase.sol";
+import { SortedLinkedListTestBase } from "../../utils/TestBase.sol";
-contract RemoveTests is SortedArrayTestBase {
+contract RemoveTests is SortedLinkedListTestBase {
+
+ function test_remove_failed_valueNotExists() external {
+ vm.expectRevert("SLL:R:VALUE_NOT_EXISTS");
+ list.remove(1);
+ }
function test_remove_singleValue() external {
- array.push(1);
- array.remove(1);
+ list.push(1);
+ list.remove(1);
uint256[] memory expectedValues = new uint256[](0);
- assertArray(expectedValues);
+ assertList(expectedValues);
- assertEq(array.length(), 0);
+ assertEq(list.length(), 0);
}
function test_remove_multipleValues() external {
- array.push(1);
- array.push(2);
- array.push(3);
+ list.push(1);
+ list.push(2);
+ list.push(3);
- array.remove(2);
+ list.remove(2);
uint256[] memory expectedValues = new uint256[](2);
expectedValues[0] = 1;
expectedValues[1] = 3;
- assertArray(expectedValues);
- assertElementAtIndex(0, 1);
- assertElementAtIndex(1, 3);
+ assertList(expectedValues);
+ assertEq(list.length(), 2);
- assertEq(array.length(), 2);
-
- array.remove(1);
+ list.remove(1);
expectedValues = new uint256[](1);
expectedValues[0] = 3;
- assertArray(expectedValues);
- assertElementAtIndex(0, 3);
-
- assertEq(array.length(), 1);
+ assertList(expectedValues);
+ assertEq(list.length(), 1);
- array.remove(3);
+ list.remove(3);
- assertArray(new uint256[](0));
- assertEq(array.length(), 0);
+ assertList(new uint256[](0));
+ assertEq(list.length(), 0);
}
}
diff --git a/tests/utils/Harnesses.sol b/tests/utils/Harnesses.sol
index 44689f0..acf1b8f 100644
--- a/tests/utils/Harnesses.sol
+++ b/tests/utils/Harnesses.sol
@@ -3,7 +3,7 @@ pragma solidity ^0.8.7;
import { MapleWithdrawalManager } from "../../contracts/MapleWithdrawalManager.sol";
-import { SortedArray } from "../../contracts/utils/SortedArray.sol";
+import { SortedLinkedList } from "../../contracts/utils/SortedLinkedList.sol";
contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
@@ -20,7 +20,7 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
function __setLastRequest(address owner_, uint256 requestId_) external {
- SortedArray.push(_userRequests[owner_], requestId_);
+ SortedLinkedList.push(_userRequests[owner_], _toUint128(requestId_));
queue.lastRequestId = _toUint128(requestId_);
}
@@ -42,7 +42,7 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
queue.lastRequestId = requestId_;
queue.requests[requestId_] = WithdrawalRequest(owner_, shares_);
- SortedArray.push(_userRequests[owner_], requestId_);
+ SortedLinkedList.push(_userRequests[owner_], _toUint128(requestId_));
}
function __setTotalShares(uint256 totalShares_) external {
@@ -50,15 +50,15 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
function __setUserRequestCount(address owner_, uint256 requestCount_, uint256 escrowSharesTotal_) external {
- uint256 currentRequestCount_ = SortedArray.length(_userRequests[owner_]);
+ uint256 currentRequestCount_ = SortedLinkedList.length(_userRequests[owner_]);
if (requestCount_ < currentRequestCount_) {
for (uint256 i = requestCount_; i < currentRequestCount_; i++) {
- SortedArray.remove(_userRequests[owner_], _toUint128(i));
+ SortedLinkedList.remove(_userRequests[owner_], _toUint128(i));
}
} else {
for (uint256 i = currentRequestCount_; i > requestCount_; i++) {
- SortedArray.push(_userRequests[owner_], _toUint128(i));
+ SortedLinkedList.push(_userRequests[owner_], _toUint128(i));
}
}
@@ -67,32 +67,28 @@ contract MapleWithdrawalManagerHarness is MapleWithdrawalManager {
}
-contract SortedArrayHarness {
+contract SortedLinkedListHarness {
- SortedArray.Array array;
+ SortedLinkedList.List list;
- function push(uint256 value_) external {
- SortedArray.push(array, value_);
+ function push(uint128 value_) external {
+ SortedLinkedList.push(list, value_);
}
- function remove(uint256 value_) external {
- SortedArray.remove(array, value_);
- }
-
- function get(uint256 index_) external view returns (uint256) {
- return SortedArray.get(array, index_);
+ function remove(uint128 value_) external {
+ SortedLinkedList.remove(list, value_);
}
function length() external view returns (uint256) {
- return SortedArray.length(array);
+ return SortedLinkedList.length(list);
}
- function getAllValues() external view returns (uint256[] memory) {
- return SortedArray.getAllValues(array);
+ function getAllValues() external view returns (uint128[] memory) {
+ return SortedLinkedList.getAllValues(list);
}
- function getLast() external view returns (uint256) {
- return SortedArray.getLast(array);
+ function getLast() external view returns (uint128) {
+ return SortedLinkedList.getLast(list);
}
}
diff --git a/tests/utils/TestBase.sol b/tests/utils/TestBase.sol
index 3ea0383..bfa6f09 100644
--- a/tests/utils/TestBase.sol
+++ b/tests/utils/TestBase.sol
@@ -10,7 +10,7 @@ import { MapleWithdrawalManagerInitializer } from "../../contracts/proxy/MapleWi
import { MapleWithdrawalManagerHarness } from "./Harnesses.sol";
import { MockFactory, MockGlobals, MockPool, MockPoolManager } from "./Mocks.sol";
-import { SortedArrayHarness } from "./Harnesses.sol";
+import { SortedLinkedListHarness } from "./Harnesses.sol";
contract TestBase is Test {
@@ -101,27 +101,23 @@ contract TestBase is Test {
}
-contract SortedArrayTestBase is Test {
+contract SortedLinkedListTestBase is Test {
- SortedArrayHarness public array;
+ SortedLinkedListHarness public list;
function setUp() public virtual {
- array = new SortedArrayHarness();
+ list = new SortedLinkedListHarness();
}
- function assertArray(uint256[] memory values) internal {
- assertEq(array.length(), values.length);
+ function assertList(uint256[] memory values) internal {
+ assertEq(list.length(), values.length);
- uint256[] memory arrayValues = array.getAllValues();
+ uint128[] memory listValues = list.getAllValues();
for (uint256 i = 0; i < values.length; i++) {
- assertEq(arrayValues[i], values[i]);
+ assertEq(uint256(listValues[i]), values[i]);
}
}
- function assertElementAtIndex(uint256 index, uint256 value) internal {
- assertEq(array.get(index), value);
- }
-
}
From 316aa79aaf752299ca69c0ba17ec8e87948bcf4b Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Fri, 17 Oct 2025 16:39:35 +0400
Subject: [PATCH 18/21] feat: Internal audit fixes (#48)
* feat: Interal audit fixes
* feat: ASCII Art
---
contracts/MapleWithdrawalManager.sol | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 515390d..07e45e8 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -27,7 +27,6 @@ import { SortedLinkedList } from "./utils/SortedLinkedList.sol";
██║ ╚═╝ ██║██║ ██║██║ ███████╗███████╗
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚══════╝╚══════╝
-
██╗ ██╗██╗████████╗██╗ ██╗██████╗ ██████╗ █████╗ ██╗ ██╗ █████╗ ██╗
██║ ██║██║╚══██╔══╝██║ ██║██╔══██╗██╔══██╗██╔══██╗██║ ██║██╔══██╗██║
██║ █╗ ██║██║ ██║ ███████║██║ ██║██████╔╝███████║██║ █╗ ██║███████║██║
@@ -35,7 +34,6 @@ import { SortedLinkedList } from "./utils/SortedLinkedList.sol";
╚███╔███╔╝██║ ██║ ██║ ██║██████╔╝██║ ██║██║ ██║╚███╔███╔╝██║ ██║███████╗
╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚══════╝
-
███╗ ███╗ █████╗ ███╗ ██╗ █████╗ ██████╗ ███████╗██████╗
████╗ ████║██╔══██╗████╗ ██║██╔══██╗██╔════╝ ██╔════╝██╔══██╗
██╔████╔██║███████║██╔██╗ ██║███████║██║ ███╗█████╗ ██████╔╝
@@ -43,6 +41,13 @@ import { SortedLinkedList } from "./utils/SortedLinkedList.sol";
██║ ╚═╝ ██║██║ ██║██║ ╚████║██║ ██║╚██████╔╝███████╗██║ ██║
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝
+ ██╗ ██╗██████╗ ██████╗ ██████╗
+ ██║ ██║╚════██╗██╔═████╗██╔═████╗
+ ██║ ██║ █████╔╝██║██╔██║██║██╔██║
+ ╚██╗ ██╔╝██╔═══╝ ████╔╝██║████╔╝██║
+ ╚████╔╝ ███████╗╚██████╔╝╚██████╔╝
+ ╚═══╝ ╚══════╝ ╚═════╝ ╚═════╝
+
*/
contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManagerStorage , MapleProxiedInternals {
@@ -480,7 +485,7 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
require(shares_ <= sharesAvailable_, "WM:PR:TOO_MANY_SHARES");
- ( redeemableShares_, resultingAssets_ ) = _calculateRedemption(shares_);
+ ( redeemableShares_, resultingAssets_ ) = _calculateRedemption(shares_); // NOTE: Recommend using convertToExitAssets instead
}
function previewWithdraw(address owner_, uint256 assets_)
From 2536a8a1719cd001182e51971e5513d39e52a866 Mon Sep 17 00:00:00 2001
From: Cal Mac Fadden <108666242+calmacfadden@users.noreply.github.com>
Date: Sun, 19 Oct 2025 18:13:07 +0400
Subject: [PATCH 19/21] feat: added unit tests (#50)
Signed-off-by: calmacfadden
---
tests/unit/ViewFunctions.t.sol | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/tests/unit/ViewFunctions.t.sol b/tests/unit/ViewFunctions.t.sol
index 0260240..3c05524 100644
--- a/tests/unit/ViewFunctions.t.sol
+++ b/tests/unit/ViewFunctions.t.sol
@@ -5,6 +5,22 @@ import { TestBase } from "../utils/TestBase.sol";
contract ViewFunctionsTests is TestBase {
+ function test_asset() external {
+ assertEq(withdrawalManager.asset(), address(asset));
+ }
+
+ function test_globals() external {
+ assertEq(withdrawalManager.globals(), address(globals));
+ }
+
+ function test_governor() external {
+ assertEq(withdrawalManager.governor(), governor);
+ }
+
+ function test_securityAdmin() external {
+ assertEq(withdrawalManager.securityAdmin(), securityAdmin);
+ }
+
function testFuzz_isInExitWindow(address account_) external {
assertTrue(withdrawalManager.isInExitWindow(account_));
}
From 76a8ba7e0a7c51a312df395593c71ee01881b16e Mon Sep 17 00:00:00 2001
From: Farhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Mon, 20 Oct 2025 13:42:24 +0400
Subject: [PATCH 20/21] audit: Fix DOS Issue due to large number of empty
requests (#49)
* audit: Fix DOS Issue
* chore: order event
* chore: order functions
* fix: PR comments
* fix: PR comments
* fix: code comment
---
contracts/MapleWithdrawalManager.sol | 26 ++
.../interfaces/IMapleWithdrawalManager.sol | 15 +
tests/unit/ProcessEmptyRedemptions.t.sol | 301 ++++++++++++++++++
tests/utils/TestBase.sol | 1 +
4 files changed, 343 insertions(+)
create mode 100644 tests/unit/ProcessEmptyRedemptions.t.sol
diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol
index 07e45e8..a1ccc62 100644
--- a/contracts/MapleWithdrawalManager.sol
+++ b/contracts/MapleWithdrawalManager.sol
@@ -176,6 +176,32 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag
/*** State-Changing Functions - OnlyRedeemer ***/
/**************************************************************************************************************************************/
+ function processEmptyRedemptions(uint256 numberOfRequests_) external override whenProtocolNotPaused onlyRedeemer {
+ require(numberOfRequests_ > 0, "WM:PER:ZERO_REQUESTS");
+
+ uint256 nextRequestId_ = queue.nextRequestId;
+ uint256 lastRequestId_ = queue.lastRequestId;
+ uint256 requestsProcessed_ = 0;
+
+ // Iterate through the queue and process empty requests, if the owner is address(0).
+ while (requestsProcessed_ < numberOfRequests_ && nextRequestId_ <= lastRequestId_) {
+ address owner_ = queue.requests[_toUint128(nextRequestId_)].owner;
+
+ if (owner_ != address(0)) {
+ // Stop if we encounter a non-empty request.
+ break;
+ }
+
+ ++nextRequestId_;
+ ++requestsProcessed_;
+ }
+
+ // Update the queue's next request ID.
+ queue.nextRequestId = _toUint128(nextRequestId_);
+
+ emit EmptyRedemptionsProcessed(requestsProcessed_);
+ }
+
function processRedemptions(uint256 maxSharesToProcess_) external override whenProtocolNotPaused nonReentrant onlyRedeemer {
require(maxSharesToProcess_ > 0, "WM:PR:ZERO_SHARES");
diff --git a/contracts/interfaces/IMapleWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol
index e51637b..5bb096a 100644
--- a/contracts/interfaces/IMapleWithdrawalManager.sol
+++ b/contracts/interfaces/IMapleWithdrawalManager.sol
@@ -11,6 +11,12 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
/*** Events ***/
/**************************************************************************************************************************************/
+ /**
+ * @dev Emitted when empty redemption requests are processed.
+ * @param numberOfRequestsProcessed Number of empty requests that were processed.
+ */
+ event EmptyRedemptionsProcessed(uint256 numberOfRequestsProcessed);
+
/**
* @dev Emitted when a manual redemption takes place.
* @param owner Address of the account.
@@ -74,6 +80,15 @@ interface IMapleWithdrawalManager is IMapleWithdrawalManagerStorage, IMapleProxi
*/
function addShares(uint256 shares, address owner) external returns (uint256 lastRequestId);
+ /**
+ * @dev Processes empty redemption requests at the front of the queue.
+ * Iterates through the queue starting from the front and advances the queue's nextRequestId
+ * for each empty request encountered. Stops when a non-empty request is found or the
+ * specified number of requests has been processed.
+ * @param numberOfRequests Maximum number of empty requests to process.
+ */
+ function processEmptyRedemptions(uint256 numberOfRequests) external;
+
/**
* @dev Processes a withdrawal request.
* Uses the current exchange rate to calculate the amount of assets withdrawn.
diff --git a/tests/unit/ProcessEmptyRedemptions.t.sol b/tests/unit/ProcessEmptyRedemptions.t.sol
new file mode 100644
index 0000000..4b2394f
--- /dev/null
+++ b/tests/unit/ProcessEmptyRedemptions.t.sol
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.7;
+
+import { TestBase } from "../utils/TestBase.sol";
+
+contract ProcessEmptyRedemptionsFailureTests is TestBase {
+
+ function setUp() public override {
+ super.setUp();
+
+ globals.__setIsInstanceOf(false);
+ }
+
+ function test_processEmptyRedemptions_protocolPaused() external {
+ globals.__setFunctionPaused(true);
+
+ vm.prank(poolDelegate);
+ vm.expectRevert("WM:PAUSED");
+ withdrawalManager.processEmptyRedemptions(1);
+ }
+
+ function test_processEmptyRedemptions_notRedeemer() external {
+ vm.prank(lp);
+ vm.expectRevert("WM:NOT_REDEEMER");
+ withdrawalManager.processEmptyRedemptions(1);
+ }
+
+ function test_processEmptyRedemptions_governorNotAllowed() external {
+ vm.prank(governor);
+ vm.expectRevert("WM:NOT_REDEEMER");
+ withdrawalManager.processEmptyRedemptions(1);
+ }
+
+ function test_processEmptyRedemptions_zeroRequests() external {
+ vm.prank(poolDelegate);
+ vm.expectRevert("WM:PER:ZERO_REQUESTS");
+ withdrawalManager.processEmptyRedemptions(0);
+ }
+
+}
+
+contract ProcessEmptyRedemptionsSuccessTests is TestBase {
+
+ function setUp() public override {
+ super.setUp();
+
+ // Setup pool with assets and shares
+ pool.mint(pm, 1000e18);
+
+ vm.prank(pm);
+ pool.approve(address(withdrawalManager), 1000e18);
+ }
+
+ function test_processEmptyRedemptions_poolDelegate() external {
+ // Add a request
+ vm.prank(pm);
+ uint256 requestId = withdrawalManager.addShares(100, lp);
+
+ assertQueue({ nextRequestId: 1, lastRequestId: 1 });
+ assertRequest({ requestId: 1, owner: lp, shares: 100 });
+
+ // Remove the request using removeSharesById
+ vm.prank(lp);
+ withdrawalManager.removeSharesById(requestId, 100);
+
+ // Request should now be empty
+ assertRequest({ requestId: 1, owner: address(0), shares: 0 });
+ assertQueue({ nextRequestId: 1, lastRequestId: 1 });
+
+ // Process empty redemptions as pool delegate
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(1);
+ withdrawalManager.processEmptyRedemptions(1);
+
+ // Queue should have moved forward
+ assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+ }
+
+ function test_processEmptyRedemptions_operationalAdmin() external {
+ // Add a request
+ vm.prank(pm);
+ uint256 requestId = withdrawalManager.addShares(100, lp);
+
+ // Remove the request
+ vm.prank(lp);
+ withdrawalManager.removeSharesById(requestId, 100);
+
+ // Process empty redemptions as operational admin
+ vm.prank(operationalAdmin);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(1);
+ withdrawalManager.processEmptyRedemptions(1);
+
+ assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+ }
+
+ function test_processEmptyRedemptions_redeemer() external {
+ globals.__setIsInstanceOf(true);
+
+ // Add a request
+ vm.prank(pm);
+ uint256 requestId = withdrawalManager.addShares(100, lp);
+
+ // Remove the request
+ vm.prank(lp);
+ withdrawalManager.removeSharesById(requestId, 100);
+
+ // Process empty redemptions as redeemer bot
+ vm.prank(redeemer);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(1);
+ withdrawalManager.processEmptyRedemptions(1);
+
+ assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+ }
+
+ function test_processEmptyRedemptions_multipleEmptyRequests() external {
+ // Create 5 requests
+ vm.startPrank(pm);
+ uint256 requestId1 = withdrawalManager.addShares(100, lp);
+ uint256 requestId2 = withdrawalManager.addShares(200, lp);
+ uint256 requestId3 = withdrawalManager.addShares(300, lp);
+ withdrawalManager.addShares(400, lp); // requestId4
+ withdrawalManager.addShares(500, lp); // requestId5
+ vm.stopPrank();
+
+ assertQueue({ nextRequestId: 1, lastRequestId: 5 });
+
+ // Remove first 3 requests
+ vm.startPrank(lp);
+ withdrawalManager.removeSharesById(requestId1, 100);
+ withdrawalManager.removeSharesById(requestId2, 200);
+ withdrawalManager.removeSharesById(requestId3, 300);
+ vm.stopPrank();
+
+ // All three should now be empty
+ assertRequest({ requestId: 1, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 2, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 3, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 4, owner: lp, shares: 400 });
+ assertRequest({ requestId: 5, owner: lp, shares: 500 });
+
+ // Process 3 empty redemptions
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(3);
+ withdrawalManager.processEmptyRedemptions(3);
+
+ // Queue should have advanced by 3
+ assertQueue({ nextRequestId: 4, lastRequestId: 5 });
+ }
+
+ function test_processEmptyRedemptions_stopsAtNonEmptyRequest() external {
+ // Create 5 requests
+ vm.startPrank(pm);
+ uint256 requestId1 = withdrawalManager.addShares(100, lp);
+ uint256 requestId2 = withdrawalManager.addShares(200, lp);
+ withdrawalManager.addShares(300, lp); // requestId3 - keep this one
+ withdrawalManager.addShares(400, lp); // requestId4
+ withdrawalManager.addShares(500, lp); // requestId5
+ vm.stopPrank();
+
+ assertQueue({ nextRequestId: 1, lastRequestId: 5 });
+
+ // Remove only first 2 requests (leave request 3)
+ vm.startPrank(lp);
+ withdrawalManager.removeSharesById(requestId1, 100);
+ withdrawalManager.removeSharesById(requestId2, 200);
+ vm.stopPrank();
+
+ assertRequest({ requestId: 1, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 2, owner: address(0), shares: 0 });
+ assertRequest({ requestId: 3, owner: lp, shares: 300 });
+ assertRequest({ requestId: 4, owner: lp, shares: 400 });
+ assertRequest({ requestId: 5, owner: lp, shares: 500 });
+
+ // Try to process 5 empty redemptions, but should stop at request 3
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(2); // Only 2 were processed
+ withdrawalManager.processEmptyRedemptions(5);
+
+ // Queue should only advance by 2 (stopped at non-empty request 3)
+ assertQueue({ nextRequestId: 3, lastRequestId: 5 });
+
+ // Now remove requests 3, 4, and 5
+ vm.startPrank(lp);
+ withdrawalManager.removeSharesById(3, 300);
+ withdrawalManager.removeSharesById(4, 400);
+ withdrawalManager.removeSharesById(5, 500);
+ vm.stopPrank();
+
+ // Process the remaining empty requests
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(3);
+ withdrawalManager.processEmptyRedemptions(10); // Request more than available
+
+ // Queue should now be fully processed
+ assertQueue({ nextRequestId: 6, lastRequestId: 5 });
+ }
+
+ function test_processEmptyRedemptions_noEmptyRequests() external {
+ // Create 3 requests but don't remove any
+ vm.startPrank(pm);
+ withdrawalManager.addShares(100, lp);
+ withdrawalManager.addShares(200, lp);
+ withdrawalManager.addShares(300, lp);
+ vm.stopPrank();
+
+ assertQueue({ nextRequestId: 1, lastRequestId: 3 });
+
+ // Try to process empty redemptions - should process 0 requests
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(0);
+ withdrawalManager.processEmptyRedemptions(5);
+
+ // Queue should not move
+ assertQueue({ nextRequestId: 1, lastRequestId: 3 });
+ }
+
+ function test_processEmptyRedemptions_emptyQueueNoStateChange() external {
+ // Queue is empty initially
+ assertQueue({ nextRequestId: 1, lastRequestId: 0 });
+
+ // Try to process empty redemptions on empty queue
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(0);
+ withdrawalManager.processEmptyRedemptions(10);
+
+ // Queue should remain unchanged
+ assertQueue({ nextRequestId: 1, lastRequestId: 0 });
+ }
+
+ function test_processEmptyRedemptions_fullyProcessedQueueNoStateChange() external {
+ // Create and remove a request
+ vm.prank(pm);
+ uint256 requestId = withdrawalManager.addShares(100, lp);
+
+ vm.prank(lp);
+ withdrawalManager.removeSharesById(requestId, 100);
+
+ // Process the empty request
+ vm.prank(poolDelegate);
+ withdrawalManager.processEmptyRedemptions(1);
+
+ assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+
+ // Try to process again - queue is fully processed
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(0);
+ withdrawalManager.processEmptyRedemptions(10);
+
+ // Queue should remain unchanged
+ assertQueue({ nextRequestId: 2, lastRequestId: 1 });
+ }
+
+ function test_processEmptyRedemptions_chunkProcessing() external {
+ // Create 10 requests
+ vm.startPrank(pm);
+ for (uint256 i = 1; i <= 10; i++) {
+ withdrawalManager.addShares(i * 100, lp);
+ }
+ vm.stopPrank();
+
+ // Remove first 5 requests
+ vm.startPrank(lp);
+ for (uint256 i = 1; i <= 5; i++) {
+ withdrawalManager.removeSharesById(i, i * 100);
+ }
+ vm.stopPrank();
+
+ assertQueue({ nextRequestId: 1, lastRequestId: 10 });
+
+ // Process only 3 empty requests
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(3);
+ withdrawalManager.processEmptyRedemptions(3);
+
+ assertQueue({ nextRequestId: 4, lastRequestId: 10 });
+
+ // Process remaining 2 empty requests
+ vm.prank(poolDelegate);
+ vm.expectEmit();
+ emit EmptyRedemptionsProcessed(2);
+ withdrawalManager.processEmptyRedemptions(2);
+
+ assertQueue({ nextRequestId: 6, lastRequestId: 10 });
+
+ // Requests 6-10 should still exist
+ for (uint256 i = 6; i <= 10; i++) {
+ assertRequest({ requestId: i, owner: lp, shares: i * 100 });
+ }
+ }
+
+}
diff --git a/tests/utils/TestBase.sol b/tests/utils/TestBase.sol
index bfa6f09..e044fc5 100644
--- a/tests/utils/TestBase.sol
+++ b/tests/utils/TestBase.sol
@@ -35,6 +35,7 @@ contract TestBase is Test {
MapleWithdrawalManagerFactory internal factory;
MapleWithdrawalManagerHarness internal withdrawalManager;
+ event EmptyRedemptionsProcessed(uint256 numberOfRequestsProcessed);
event ManualWithdrawalSet(address indexed account, bool isManual);
event RequestCreated(uint256 indexed requestId, address indexed owner, uint256 shares);
event RequestDecreased(uint256 indexed requestId, uint256 shares);
From e1114052544defeadf2cf0463c5a1df3c45eb86c Mon Sep 17 00:00:00 2001
From: 0xfarhaan <59924029+0xfarhaan@users.noreply.github.com>
Date: Thu, 27 Nov 2025 17:04:17 +0400
Subject: [PATCH 21/21] chore: Update README
---
README.md | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 6814524..3cb7988 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# Withdrawal Manager Queue
-
+
[](https://maplefinance.gitbook.io/maple/maple-for-developers/protocol-overview)
[![Foundry][foundry-badge]][foundry]
-[](https://github.com/maple-labs/withdrawal-manager-queue-private/blob/main/LICENSE)
+[](https://github.com/maple-labs/withdrawal-manager-queue/blob/main/LICENSE)
[foundry]: https://getfoundry.sh/
[foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg
@@ -23,12 +23,13 @@ Versions of dependencies can be checked with `git submodule status`.
This project was built using [Foundry](https://book.getfoundry.sh/). Refer to installation instructions [here](https://github.com/foundry-rs/foundry#installation).
```sh
-git clone git@github.com:maple-labs/withdrawal-manager-queue-private.git
-cd withdrawal-manager-queue-private
+git clone git@github.com:maple-labs/withdrawal-manager-queue.git
+cd withdrawal-manager-queue
forge install
```
## Audit Reports
+For all audit reports please refer to https://docs.maple.finance/technical-resources/security/security
## Bug Bounty