From 25d9ff308626d00831cd15e1829e4c2ea9365a0d Mon Sep 17 00:00:00 2001 From: vbidin Date: Sat, 14 Jan 2023 00:30:33 +0200 Subject: [PATCH 01/10] feat: Update README (#62) --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f995402..3fcac32 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Withdrawal Manager +![Foundry CI](https://github.com/maple-labs/withdrawal-manager-private/actions/workflows/forge.yml/badge.svg) +[![GitBook - Documentation](https://img.shields.io/badge/GitBook-Documentation-orange?logo=gitbook&logoColor=white)](https://maplefinance.gitbook.io/maple/maple-for-developers/protocol-overview) [![Foundry][foundry-badge]][foundry] -![Foundry CI](https://github.com/maple-labs/withdrawal-manager/actions/workflows/forge.yml/badge.svg) +[![License: BUSL 1.1](https://img.shields.io/badge/License-BUSL%201.1-blue.svg)](https://github.com/maple-labs/withdrawal-manager-private/blob/main/LICENSE) [foundry]: https://getfoundry.sh/ [foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg @@ -10,8 +12,6 @@ The `WithdrawalManager` is an upgradable contract used by the Maple V2 protocol to facilitate withdrawals of liquidity from a Maple pool via a cyclical withdrawal mechanism. It also enables pro-rata distribution of cash in the event of insufficient liquidity. -For more information about the `WithdrawalManager` contract in the context of the Maple V2 protocol, please refer to the Withdrawal section of the protocol [wiki](https://github.com/maple-labs/maple-core-v2/wiki/Withdrawal-Mechanism). - ## Dependencies/Inheritance Contracts in this repo inherit and import code from: @@ -31,8 +31,8 @@ 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.git -cd withdrawal-manager +git clone git@github.com:maple-labs/withdrawal-manager-private.git +cd withdrawal-manager-private forge install ``` @@ -59,8 +59,6 @@ For all information related to the ongoing bug bounty for these contracts run by [Maple Finance](https://maple.finance/) is a decentralized corporate credit market. Maple provides capital to institutional borrowers through globally accessible fixed-income yield opportunities. -For all technical documentation related to the Maple V2 protocol, please refer to the GitHub [wiki](https://github.com/maple-labs/maple-core-v2/wiki). - ---

From ade87306b4ef6ec489940c7624ecf46d0e1f2d09 Mon Sep 17 00:00:00 2001 From: Vedran Bidin Date: Tue, 23 May 2023 23:14:11 +0300 Subject: [PATCH 02/10] feat: add codeowners (#64) --- .github/workflows/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/workflows/CODEOWNERS diff --git a/.github/workflows/CODEOWNERS b/.github/workflows/CODEOWNERS new file mode 100644 index 0000000..c5affea --- /dev/null +++ b/.github/workflows/CODEOWNERS @@ -0,0 +1,2 @@ +# These owners will be the default owners for everything in the repo. +* @maple-labs/smart-contracts-admin From dcde843080c4ef26bd812a156574b27da6e6f5e8 Mon Sep 17 00:00:00 2001 From: Michael De Luca <35537333+deluca-mike@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:10:06 -0400 Subject: [PATCH 03/10] chore: Cleanup and bytecode size check (SC-12382) (#65) * chore: cleanup and bytecode size check (SC-12382) * fix: coverage report and size check --- .github/workflows/forge-pr.yml | 57 ++++++++++++++++++++++++++++++++-- .github/workflows/forge.yml | 54 +++++++++++++++++++++++++++++++- Makefile | 16 +++------- configs/package.yaml | 4 +-- foundry.toml | 10 ++++-- scripts/check-sizes.sh | 33 ++++++++++++++++++++ scripts/release.sh | 2 +- scripts/test.sh | 2 +- 8 files changed, 156 insertions(+), 22 deletions(-) create mode 100755 scripts/check-sizes.sh diff --git a/.github/workflows/forge-pr.yml b/.github/workflows/forge-pr.yml index 54bc7aa..c7d3f53 100644 --- a/.github/workflows/forge-pr.yml +++ b/.github/workflows/forge-pr.yml @@ -1,20 +1,71 @@ name: Forge Tests (PR) -on: - pull_request: +on: [pull_request] jobs: test: + name: Test with "deep" profile runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Install Foundry uses: onbjerg/foundry-toolchain@v1 with: version: nightly + - name: Install submodules run: | git config --global url."https://github.com/".insteadOf "git@github.com:" git submodule update --init --recursive + - name: Run Forge tests - run: ./scripts/test.sh -p shallow + run: ./scripts/test.sh -p deep + + coverage_report: + name: Generate coverage report + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Install submodules + run: | + git config --global url."https://github.com/".insteadOf "git@github.com:" + git submodule update --init --recursive + - name: Generate coverage report + run: | + forge coverage --report lcov + sudo apt-get install lcov + lcov --remove lcov.info -o lcov.info 'tests/*' + - name: Report code coverage + uses: zgosalvez/github-actions-report-lcov@v1 + with: + coverage-files: lcov.info + minimum-coverage: 90 + artifact-name: code-coverage-report + github-token: ${{ secrets.GITHUB_TOKEN }} + working-directory: ./ + + size_check: + name: Check contracts sizes + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Install submodules + run: | + git config --global url."https://github.com/".insteadOf "git@github.com:" + git submodule update --init --recursive + + - name: Check contract sizes + run: ./scripts/check-sizes.sh diff --git a/.github/workflows/forge.yml b/.github/workflows/forge.yml index 4814551..1f38411 100644 --- a/.github/workflows/forge.yml +++ b/.github/workflows/forge.yml @@ -6,16 +6,68 @@ on: jobs: test: + name: Test with "deep" profile runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Install Foundry uses: onbjerg/foundry-toolchain@v1 with: version: nightly + - name: Install submodules run: | git config --global url."https://github.com/".insteadOf "git@github.com:" git submodule update --init --recursive + - name: Run Forge tests - run: ./scripts/test.sh -p deep + run: ./scripts/test.sh -p super_deep + + coverage_report: + name: Generate coverage report + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Install submodules + run: | + git config --global url."https://github.com/".insteadOf "git@github.com:" + git submodule update --init --recursive + - name: Generate coverage report + run: | + forge coverage --report lcov + sudo apt-get install lcov + lcov --remove lcov.info -o lcov.info 'tests/*' + - name: Report code coverage + uses: zgosalvez/github-actions-report-lcov@v1 + with: + coverage-files: lcov.info + minimum-coverage: 90 + artifact-name: code-coverage-report + github-token: ${{ secrets.GITHUB_TOKEN }} + working-directory: ./ + + size_check: + name: Check contracts sizes + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Install submodules + run: | + git config --global url."https://github.com/".insteadOf "git@github.com:" + git submodule update --init --recursive + + - name: Check contract sizes + run: ./scripts/check-sizes.sh diff --git a/Makefile b/Makefile index 365b1be..bf5f435 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,11 @@ -install: - @git submodule update --init --recursive - -update: - @forge update - build: - @scripts/build.sh + @scripts/build.sh -p production release: @scripts/release.sh -test: - @scripts/test.sh +size: + @scripts/check-sizes.sh -clean: - @forge clean +test: + @scripts/test.sh -p default diff --git a/configs/package.yaml b/configs/package.yaml index 0ac0a8c..f4b9662 100644 --- a/configs/package.yaml +++ b/configs/package.yaml @@ -1,5 +1,5 @@ name: withdrawal-manager -version: 1.0.0-beta.0 +version: 1.0.0 source: contracts packages: - path: contracts/WithdrawalManager.sol @@ -8,6 +8,4 @@ packages: contractName: WithdrawalManagerFactory - path: contracts/WithdrawalManagerInitializer.sol contractName: WithdrawalManagerInitializer - - path: contracts/WithdrawalManagerStorage.sol - contractName: WithdrawalManagerStorage customDescription: Withdrawal Manager Artifacts and ABIs diff --git a/foundry.toml b/foundry.toml index e86fb6e..027ef6e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,8 +3,7 @@ contracts = 'contracts' # The contract directory test = 'tests' # The test directory libs = ['modules'] # A list of library directories solc_version = '0.8.7' # Override for the solc version (setting this ignores `auto_detect_solc`) -optimizer = true # Enable or disable the solc optimizer -optimizer_runs = 200 # The number of optimizer runs +optimizer = false # Enable or disable the solc optimizer verbosity = 3 # The verbosity of tests block_timestamp = 1_622_400_000 # Timestamp for tests (non-zero) fuzz_runs = 100 # Number of fuzz runs @@ -14,3 +13,10 @@ fuzz_runs = 1000 [profile.deep] fuzz_runs = 50000 + +[profile.super_deep] +fuzz_runs = 50000 + +[profile.production] +optimizer = true # Enable or disable the solc optimizer +optimizer_runs = 200 # The number of optimizer runs diff --git a/scripts/check-sizes.sh b/scripts/check-sizes.sh new file mode 100755 index 0000000..0ba6bc1 --- /dev/null +++ b/scripts/check-sizes.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +while getopts p: flag +do + case "${flag}" in + p) profile=${OPTARG};; + esac +done + +export FOUNDRY_PROFILE=production + +sizes=$(forge build --sizes) + +names=($(cat ./configs/package.yaml | grep " contractName:" | sed -r 's/.{18}//')) + +fail=false + +for i in "${!names[@]}"; do + line=$(echo "$sizes" | grep -w "${names[i]}") + + if [[ $line == *"-"* ]]; then + echo "${names[i]} is too large" + fail=true + fi +done + +if $fail + then + echo "Contract size check failed" + exit 1 + else + echo "Contract size check passed" +fi diff --git a/scripts/release.sh b/scripts/release.sh index dd0ee23..09b7475 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -5,7 +5,7 @@ version=$(cat ./configs/package.yaml | grep "version: " | sed -r 's/.{9}//') name=$(cat ./configs/package.yaml | grep "name: " | sed -r 's/.{6}//') customDescription=$(cat ./configs/package.yaml | grep "customDescription: " | sed -r 's/.{19}//') -./scripts/build.sh +./scripts/build.sh -p production rm -rf ./package mkdir -p package diff --git a/scripts/test.sh b/scripts/test.sh index e02687e..157477f 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -14,7 +14,7 @@ echo Using profile: $FOUNDRY_PROFILE if [ -z "$test" ]; then - forge test + forge test --match-path "tests/*"; else forge test --match-test "$test"; fi From a0263dabb4cd960f888fe762a9e54e77566317cc Mon Sep 17 00:00:00 2001 From: Michael De Luca <35537333+deluca-mike@users.noreply.github.com> Date: Mon, 26 Jun 2023 06:07:41 -0400 Subject: [PATCH 04/10] chore: Maple prefix naming (SC-12781) (#66) --- ...Manager.sol => MapleWithdrawalManager.sol} | 22 +++++++--- ....sol => MapleWithdrawalManagerFactory.sol} | 6 +-- ... => MapleWithdrawalManagerInitializer.sol} | 8 ++-- ....sol => MapleWithdrawalManagerStorage.sol} | 6 +-- ...anager.sol => IMapleWithdrawalManager.sol} | 4 +- ....sol => IMapleWithdrawalManagerEvents.sol} | 2 +- ...=> IMapleWithdrawalManagerInitializer.sol} | 2 +- ...sol => IMapleWithdrawalManagerStorage.sol} | 2 +- contracts/interfaces/Interfaces.sol | 14 +++--- ...ger.t.sol => MapleWithdrawalManager.t.sol} | 44 +++++++++---------- ...ol => MapleWithdrawalManagerFactory.t.sol} | 18 ++++---- 11 files changed, 68 insertions(+), 60 deletions(-) rename contracts/{WithdrawalManager.sol => MapleWithdrawalManager.sol} (94%) rename contracts/{WithdrawalManagerFactory.sol => MapleWithdrawalManagerFactory.sol} (67%) rename contracts/{WithdrawalManagerInitializer.sol => MapleWithdrawalManagerInitializer.sol} (82%) rename contracts/{WithdrawalManagerStorage.sol => MapleWithdrawalManagerStorage.sol} (58%) rename contracts/interfaces/{IWithdrawalManager.sol => IMapleWithdrawalManager.sol} (97%) rename contracts/interfaces/{IWithdrawalManagerEvents.sol => IMapleWithdrawalManagerEvents.sol} (97%) rename contracts/interfaces/{IWithdrawalManagerInitializer.sol => IMapleWithdrawalManagerInitializer.sol} (90%) rename contracts/interfaces/{IWithdrawalManagerStorage.sol => IMapleWithdrawalManagerStorage.sol} (98%) rename tests/{WithdrawalManager.t.sol => MapleWithdrawalManager.t.sol} (96%) rename tests/{WithdrawalManagerFactory.t.sol => MapleWithdrawalManagerFactory.t.sol} (80%) diff --git a/contracts/WithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol similarity index 94% rename from contracts/WithdrawalManager.sol rename to contracts/MapleWithdrawalManager.sol index e65b550..6d2f4bb 100644 --- a/contracts/WithdrawalManager.sol +++ b/contracts/MapleWithdrawalManager.sol @@ -5,14 +5,22 @@ import { ERC20Helper } from "../modules/erc20-helper/src/ERC20Helper.s import { IMapleProxyFactory } from "../modules/maple-proxy-factory/contracts/interfaces/IMapleProxyFactory.sol"; import { MapleProxiedInternals } from "../modules/maple-proxy-factory/contracts/MapleProxiedInternals.sol"; -import { IERC20Like, IMapleGlobalsLike, IPoolLike, IPoolManagerLike } from "./interfaces/Interfaces.sol"; +import { IERC20Like, IGlobalsLike, IPoolLike, IPoolManagerLike } from "./interfaces/Interfaces.sol"; -import { IWithdrawalManager } from "./interfaces/IWithdrawalManager.sol"; +import { IMapleWithdrawalManager } from "./interfaces/IMapleWithdrawalManager.sol"; -import { WithdrawalManagerStorage } from "./WithdrawalManagerStorage.sol"; +import { MapleWithdrawalManagerStorage } from "./MapleWithdrawalManagerStorage.sol"; /* + ███╗ ███╗ █████╗ ██████╗ ██╗ ███████╗ + ████╗ ████║██╔══██╗██╔══██╗██║ ██╔════╝ + ██╔████╔██║███████║██████╔╝██║ █████╗ + ██║╚██╔╝██║██╔══██║██╔═══╝ ██║ ██╔══╝ + ██║ ╚═╝ ██║██║ ██║██║ ███████╗███████╗ + ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚══════╝╚══════╝ + + ██╗ ██╗██╗████████╗██╗ ██╗██████╗ ██████╗ █████╗ ██╗ ██╗ █████╗ ██╗ ██║ ██║██║╚══██╔══╝██║ ██║██╔══██╗██╔══██╗██╔══██╗██║ ██║██╔══██╗██║ ██║ █╗ ██║██║ ██║ ███████║██║ ██║██████╔╝███████║██║ █╗ ██║███████║██║ @@ -58,14 +66,14 @@ import { WithdrawalManagerStorage } from "./WithdrawalManagerStorage.sol"; */ -contract WithdrawalManager is IWithdrawalManager, WithdrawalManagerStorage, MapleProxiedInternals { +contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManagerStorage, MapleProxiedInternals { // NOTE: The following functions already check for paused state in the pool, therefore no need to check here. // * addShares // * removeShares // * processExit modifier whenProtocolNotPaused() { - require(!IMapleGlobalsLike(globals()).protocolPaused(), "WM:PROTOCOL_PAUSED"); + require(!IGlobalsLike(globals()).protocolPaused(), "WM:PROTOCOL_PAUSED"); _; } @@ -88,7 +96,7 @@ contract WithdrawalManager is IWithdrawalManager, WithdrawalManagerStorage, Mapl require(msg.sender == poolDelegate_ || msg.sender == governor(), "WM:U:NOT_AUTHORIZED"); - IMapleGlobalsLike mapleGlobals_ = IMapleGlobalsLike(globals()); + IGlobalsLike mapleGlobals_ = IGlobalsLike(globals()); if (msg.sender == poolDelegate_) { require(mapleGlobals_.isValidScheduledCall(msg.sender, address(this), "WM:UPGRADE", msg.data), "WM:U:INVALID_SCHED_CALL"); @@ -386,7 +394,7 @@ contract WithdrawalManager is IWithdrawalManager, WithdrawalManagerStorage, Mapl } function governor() public view override returns (address governor_) { - governor_ = IMapleGlobalsLike(globals()).governor(); + governor_ = IGlobalsLike(globals()).governor(); } function implementation() external view override returns (address implementation_) { diff --git a/contracts/WithdrawalManagerFactory.sol b/contracts/MapleWithdrawalManagerFactory.sol similarity index 67% rename from contracts/WithdrawalManagerFactory.sol rename to contracts/MapleWithdrawalManagerFactory.sol index 72f0e76..e5b4156 100644 --- a/contracts/WithdrawalManagerFactory.sol +++ b/contracts/MapleWithdrawalManagerFactory.sol @@ -3,14 +3,14 @@ pragma solidity 0.8.7; import { MapleProxyFactory } from "../modules/maple-proxy-factory/contracts/MapleProxyFactory.sol"; -import { IMapleGlobalsLike } from "./interfaces/Interfaces.sol"; +import { IGlobalsLike } from "./interfaces/Interfaces.sol"; -contract WithdrawalManagerFactory is MapleProxyFactory { +contract MapleWithdrawalManagerFactory is MapleProxyFactory { constructor(address globals_) MapleProxyFactory(globals_) {} function createInstance(bytes calldata arguments_, bytes32 salt_) public override(MapleProxyFactory) returns (address instance_) { - require(IMapleGlobalsLike(mapleGlobals).isPoolDeployer(msg.sender), "WMF:CI:NOT_DEPLOYER"); + require(IGlobalsLike(mapleGlobals).isPoolDeployer(msg.sender), "WMF:CI:NOT_DEPLOYER"); isInstance[instance_ = super.createInstance(arguments_, salt_)] = true; } diff --git a/contracts/WithdrawalManagerInitializer.sol b/contracts/MapleWithdrawalManagerInitializer.sol similarity index 82% rename from contracts/WithdrawalManagerInitializer.sol rename to contracts/MapleWithdrawalManagerInitializer.sol index 8bea28c..4eb1008 100644 --- a/contracts/WithdrawalManagerInitializer.sol +++ b/contracts/MapleWithdrawalManagerInitializer.sol @@ -3,12 +3,12 @@ pragma solidity 0.8.7; import { MapleProxiedInternals } from "../modules/maple-proxy-factory/contracts/MapleProxiedInternals.sol"; -import { IPoolLike } from "./interfaces/Interfaces.sol"; -import { IWithdrawalManagerInitializer } from "./interfaces/IWithdrawalManagerInitializer.sol"; +import { IPoolLike } from "./interfaces/Interfaces.sol"; +import { IMapleWithdrawalManagerInitializer } from "./interfaces/IMapleWithdrawalManagerInitializer.sol"; -import { WithdrawalManagerStorage } from "./WithdrawalManagerStorage.sol"; +import { MapleWithdrawalManagerStorage } from "./MapleWithdrawalManagerStorage.sol"; -contract WithdrawalManagerInitializer is IWithdrawalManagerInitializer, WithdrawalManagerStorage, MapleProxiedInternals { +contract MapleWithdrawalManagerInitializer is IMapleWithdrawalManagerInitializer, MapleWithdrawalManagerStorage, MapleProxiedInternals { fallback() external { ( address pool_, uint256 cycleDuration_, uint256 windowDuration_ ) = decodeArguments(msg.data); diff --git a/contracts/WithdrawalManagerStorage.sol b/contracts/MapleWithdrawalManagerStorage.sol similarity index 58% rename from contracts/WithdrawalManagerStorage.sol rename to contracts/MapleWithdrawalManagerStorage.sol index f9a2a8f..51ca7e9 100644 --- a/contracts/WithdrawalManagerStorage.sol +++ b/contracts/MapleWithdrawalManagerStorage.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.7; -import { IWithdrawalManagerEvents } from "./interfaces/IWithdrawalManagerEvents.sol"; -import { IWithdrawalManagerStorage } from "./interfaces/IWithdrawalManagerStorage.sol"; +import { IMapleWithdrawalManagerEvents } from "./interfaces/IMapleWithdrawalManagerEvents.sol"; +import { IMapleWithdrawalManagerStorage } from "./interfaces/IMapleWithdrawalManagerStorage.sol"; -abstract contract WithdrawalManagerStorage is IWithdrawalManagerStorage, IWithdrawalManagerEvents { +abstract contract MapleWithdrawalManagerStorage is IMapleWithdrawalManagerStorage, IMapleWithdrawalManagerEvents { address public override pool; address public override poolManager; diff --git a/contracts/interfaces/IWithdrawalManager.sol b/contracts/interfaces/IMapleWithdrawalManager.sol similarity index 97% rename from contracts/interfaces/IWithdrawalManager.sol rename to contracts/interfaces/IMapleWithdrawalManager.sol index 4ec3917..a3cafc9 100644 --- a/contracts/interfaces/IWithdrawalManager.sol +++ b/contracts/interfaces/IMapleWithdrawalManager.sol @@ -3,9 +3,9 @@ pragma solidity 0.8.7; import { IMapleProxied } from "../../modules/maple-proxy-factory/contracts/interfaces/IMapleProxied.sol"; -import { IWithdrawalManagerStorage } from "./IWithdrawalManagerStorage.sol"; +import { IMapleWithdrawalManagerStorage } from "./IMapleWithdrawalManagerStorage.sol"; -interface IWithdrawalManager is IMapleProxied, IWithdrawalManagerStorage { +interface IMapleWithdrawalManager is IMapleProxied, IMapleWithdrawalManagerStorage { /**************************************************************************************************************************************/ /*** State Changing Functions ***/ diff --git a/contracts/interfaces/IWithdrawalManagerEvents.sol b/contracts/interfaces/IMapleWithdrawalManagerEvents.sol similarity index 97% rename from contracts/interfaces/IWithdrawalManagerEvents.sol rename to contracts/interfaces/IMapleWithdrawalManagerEvents.sol index 512e550..cc7a965 100644 --- a/contracts/interfaces/IWithdrawalManagerEvents.sol +++ b/contracts/interfaces/IMapleWithdrawalManagerEvents.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.7; -interface IWithdrawalManagerEvents { +interface IMapleWithdrawalManagerEvents { /** * @dev Emitted when the withdrawal configuration is updated. diff --git a/contracts/interfaces/IWithdrawalManagerInitializer.sol b/contracts/interfaces/IMapleWithdrawalManagerInitializer.sol similarity index 90% rename from contracts/interfaces/IWithdrawalManagerInitializer.sol rename to contracts/interfaces/IMapleWithdrawalManagerInitializer.sol index 4f261c0..af104b2 100644 --- a/contracts/interfaces/IWithdrawalManagerInitializer.sol +++ b/contracts/interfaces/IMapleWithdrawalManagerInitializer.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.7; -interface IWithdrawalManagerInitializer { +interface IMapleWithdrawalManagerInitializer { event Initialized(address pool_, uint256 cycleDuration_, uint256 windowDuration_); diff --git a/contracts/interfaces/IWithdrawalManagerStorage.sol b/contracts/interfaces/IMapleWithdrawalManagerStorage.sol similarity index 98% rename from contracts/interfaces/IWithdrawalManagerStorage.sol rename to contracts/interfaces/IMapleWithdrawalManagerStorage.sol index f741b21..c126525 100644 --- a/contracts/interfaces/IWithdrawalManagerStorage.sol +++ b/contracts/interfaces/IMapleWithdrawalManagerStorage.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.7; -interface IWithdrawalManagerStorage { +interface IMapleWithdrawalManagerStorage { struct CycleConfig { uint64 initialCycleId; // Identifier of the first withdrawal cycle using this configuration. diff --git a/contracts/interfaces/Interfaces.sol b/contracts/interfaces/Interfaces.sol index 3243294..9ad4256 100644 --- a/contracts/interfaces/Interfaces.sol +++ b/contracts/interfaces/Interfaces.sol @@ -1,7 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.7; -interface IMapleGlobalsLike { +interface IERC20Like { + + function balanceOf(address account_) external view returns (uint256 balance_); + +} + +interface IGlobalsLike { function governor() external view returns (address governor_); @@ -20,12 +26,6 @@ interface IMapleGlobalsLike { } -interface IERC20Like { - - function balanceOf(address account_) external view returns (uint256 balance_); - -} - interface IPoolLike { function asset() external view returns (address asset_); diff --git a/tests/WithdrawalManager.t.sol b/tests/MapleWithdrawalManager.t.sol similarity index 96% rename from tests/WithdrawalManager.t.sol rename to tests/MapleWithdrawalManager.t.sol index e63f7e3..bdadb49 100644 --- a/tests/WithdrawalManager.t.sol +++ b/tests/MapleWithdrawalManager.t.sol @@ -4,13 +4,13 @@ pragma solidity 0.8.7; import { Address, TestUtils } from "../modules/contract-test-utils/contracts/test.sol"; import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; -import { WithdrawalManager } from "../contracts/WithdrawalManager.sol"; -import { WithdrawalManagerFactory } from "../contracts/WithdrawalManagerFactory.sol"; -import { WithdrawalManagerInitializer } from "../contracts/WithdrawalManagerInitializer.sol"; +import { MapleWithdrawalManager } from "../contracts/MapleWithdrawalManager.sol"; +import { MapleWithdrawalManagerFactory } from "../contracts/MapleWithdrawalManagerFactory.sol"; +import { MapleWithdrawalManagerInitializer } from "../contracts/MapleWithdrawalManagerInitializer.sol"; import { MockGlobals, MockPool, MockPoolManager, MockWithdrawalManagerMigrator } from "./mocks/Mocks.sol"; -contract WithdrawalManagerTestBase is TestUtils { +contract TestBase is TestUtils { address internal governor; address internal implementation; @@ -26,14 +26,14 @@ contract WithdrawalManagerTestBase is TestUtils { MockPool internal pool; MockPoolManager internal poolManager; - WithdrawalManager internal withdrawalManager; + MapleWithdrawalManager internal withdrawalManager; - WithdrawalManagerFactory internal factory; + MapleWithdrawalManagerFactory internal factory; function setUp() public virtual { governor = address(new Address()); - implementation = address(new WithdrawalManager()); - initializer = address(new WithdrawalManagerInitializer()); + implementation = address(new MapleWithdrawalManager()); + initializer = address(new MapleWithdrawalManagerInitializer()); lp = address(new Address()); poolDelegate = address(new Address()); @@ -49,7 +49,7 @@ contract WithdrawalManagerTestBase is TestUtils { // Create factory and register implementation. vm.startPrank(governor); - factory = new WithdrawalManagerFactory(address(globals)); + factory = new MapleWithdrawalManagerFactory(address(globals)); factory.registerImplementation(1, implementation, initializer); factory.setDefaultVersion(1); @@ -60,7 +60,7 @@ contract WithdrawalManagerTestBase is TestUtils { vm.warp(start); // Create the withdrawal manager instance. - withdrawalManager = WithdrawalManager(factory.createInstance({ + withdrawalManager = MapleWithdrawalManager(factory.createInstance({ arguments_: abi.encode(address(pool), 1 weeks, 2 days), salt_: "SALT" })); @@ -96,7 +96,7 @@ contract WithdrawalManagerTestBase is TestUtils { } -contract MigrateTests is WithdrawalManagerTestBase { +contract MigrateTests is TestBase { address internal migrator; @@ -128,14 +128,14 @@ contract MigrateTests is WithdrawalManagerTestBase { } -contract SetImplementationTests is WithdrawalManagerTestBase { +contract SetImplementationTests is TestBase { address internal newImplementation; function setUp() public override { super.setUp(); - newImplementation = address(new WithdrawalManager()); + newImplementation = address(new MapleWithdrawalManager()); } function test_setImplementation_notFactory() external { @@ -154,7 +154,7 @@ contract SetImplementationTests is WithdrawalManagerTestBase { } -contract UpgradeTests is WithdrawalManagerTestBase { +contract UpgradeTests is TestBase { address internal migrator; address internal newImplementation; @@ -163,7 +163,7 @@ contract UpgradeTests is WithdrawalManagerTestBase { super.setUp(); migrator = address(new MockWithdrawalManagerMigrator()); - newImplementation = address(new WithdrawalManager()); + newImplementation = address(new MapleWithdrawalManager()); vm.startPrank(governor); factory.registerImplementation(2, newImplementation, initializer); @@ -213,7 +213,7 @@ contract UpgradeTests is WithdrawalManagerTestBase { } -contract SetExitConfigTests is WithdrawalManagerTestBase { +contract SetExitConfigTests is TestBase { function test_setExitConfig_failWhenPaused() external { globals.__setProtocolPaused(true); @@ -476,7 +476,7 @@ contract SetExitConfigTests is WithdrawalManagerTestBase { } -contract AddSharesTests is WithdrawalManagerTestBase { +contract AddSharesTests is TestBase { address internal pm; @@ -647,7 +647,7 @@ contract AddSharesTests is WithdrawalManagerTestBase { } -contract RemoveSharesTests is WithdrawalManagerTestBase { +contract RemoveSharesTests is TestBase { address internal pm; @@ -787,7 +787,7 @@ contract RemoveSharesTests is WithdrawalManagerTestBase { } -contract ProcessExitTests is WithdrawalManagerTestBase { +contract ProcessExitTests is TestBase { address internal pm; @@ -960,7 +960,7 @@ contract ProcessExitTests is WithdrawalManagerTestBase { } -contract LockedLiquidityTests is WithdrawalManagerTestBase { +contract LockedLiquidityTests is TestBase { address internal pm; @@ -1021,7 +1021,7 @@ contract LockedLiquidityTests is WithdrawalManagerTestBase { } -contract ProcessExitWithMultipleUsers is WithdrawalManagerTestBase { +contract ProcessExitWithMultipleUsers is TestBase { address internal lp2; address internal lp3; @@ -1170,7 +1170,7 @@ contract ProcessExitWithMultipleUsers is WithdrawalManagerTestBase { } -contract ViewFunctionTests is WithdrawalManagerTestBase { +contract ViewFunctionTests is TestBase { function setUp() public override { super.setUp(); diff --git a/tests/WithdrawalManagerFactory.t.sol b/tests/MapleWithdrawalManagerFactory.t.sol similarity index 80% rename from tests/WithdrawalManagerFactory.t.sol rename to tests/MapleWithdrawalManagerFactory.t.sol index 6965c3e..9b1064f 100644 --- a/tests/WithdrawalManagerFactory.t.sol +++ b/tests/MapleWithdrawalManagerFactory.t.sol @@ -4,13 +4,13 @@ pragma solidity 0.8.7; import { Address, TestUtils } from "../modules/contract-test-utils/contracts/test.sol"; import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; -import { WithdrawalManager } from "../contracts/WithdrawalManager.sol"; -import { WithdrawalManagerFactory } from "../contracts/WithdrawalManagerFactory.sol"; -import { WithdrawalManagerInitializer } from "../contracts/WithdrawalManagerInitializer.sol"; +import { MapleWithdrawalManager } from "../contracts/MapleWithdrawalManager.sol"; +import { MapleWithdrawalManagerFactory } from "../contracts/MapleWithdrawalManagerFactory.sol"; +import { MapleWithdrawalManagerInitializer } from "../contracts/MapleWithdrawalManagerInitializer.sol"; import { MockGlobals, MockPool } from "./mocks/Mocks.sol"; -contract WithdrawalManagerFactoryTests is TestUtils { +contract MapleWithdrawalManagerFactoryTests is TestUtils { address internal governor; address internal poolDelegate; @@ -22,12 +22,12 @@ contract WithdrawalManagerFactoryTests is TestUtils { MockGlobals internal globals; MockPool internal pool; - WithdrawalManagerFactory internal factory; + MapleWithdrawalManagerFactory internal factory; function setUp() external { governor = address(new Address()); - implementation = address(new WithdrawalManager()); - initializer = address(new WithdrawalManagerInitializer()); + implementation = address(new MapleWithdrawalManager()); + initializer = address(new MapleWithdrawalManagerInitializer()); poolDelegate = address(new Address()); asset = new MockERC20("Wrapped Ether", "WETH", 18); @@ -35,7 +35,7 @@ contract WithdrawalManagerFactoryTests is TestUtils { pool = new MockPool("Maple Pool", "MP-WETH", 18, address(asset), poolDelegate); vm.startPrank(governor); - factory = new WithdrawalManagerFactory(address(globals)); + factory = new MapleWithdrawalManagerFactory(address(globals)); factory.registerImplementation(1, implementation, initializer); factory.setDefaultVersion(1); @@ -85,7 +85,7 @@ contract WithdrawalManagerFactoryTests is TestUtils { function test_createInstance_success() external { bytes memory calldata_ = abi.encode(address(pool), 1, 1); - WithdrawalManager withdrawalManager_ = WithdrawalManager(factory.createInstance(calldata_, "SALT")); + MapleWithdrawalManager withdrawalManager_ = MapleWithdrawalManager(factory.createInstance(calldata_, "SALT")); ( uint64 initialCycleId_, From 7b3bfa13e57bd1dca2debdfc1ea86a309f272bc5 Mon Sep 17 00:00:00 2001 From: Preethi Senthil Date: Thu, 7 Sep 2023 07:34:10 -0700 Subject: [PATCH 05/10] refactor: Use with forge-std instead of contract-test-utils (SC-13517) (#68) * refactor: use with forge-std instead of contract-test-utils (SC-13517) * refactor: use with forge-std instead of contract-test-utils resolved comments (SC-13517) * refactor: use with forge-std instead of contract-test-utils resolved spacing (SC-13517) * refactor: use with forge-std instead of contract-test-utils resolved modules (SC-13517) * refactor: use with forge-std instead of contract-test-utils resolved modules (SC-13517) * fix: resolve comments (SC-13517) * fix: resolve spacing (SC-13517) --- .gitmodules | 12 ++++++------ modules/contract-test-utils | 1 - modules/forge-std | 1 + tests/MapleWithdrawalManager.t.sol | 17 +++++++++-------- tests/MapleWithdrawalManagerFactory.t.sol | 13 +++++++------ 5 files changed, 23 insertions(+), 21 deletions(-) delete mode 160000 modules/contract-test-utils create mode 160000 modules/forge-std diff --git a/.gitmodules b/.gitmodules index 27f7e7d..b49b3eb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,12 @@ -[submodule "modules/contract-test-utils"] - path = modules/contract-test-utils - url = https://github.com/maple-labs/contract-test-utils [submodule "modules/erc20"] path = modules/erc20 - url = https://github.com/maple-labs/erc20 + url = git@github.com:maple-labs/erc20.git [submodule "modules/erc20-helper"] path = modules/erc20-helper - url = https://github.com/maple-labs/erc20-helper + url = git@github.com:maple-labs/erc20-helper.git [submodule "modules/maple-proxy-factory"] path = modules/maple-proxy-factory - url = https://github.com/maple-labs/maple-proxy-factory.git + url = git@github.com:maple-labs/maple-proxy-factory.git +[submodule "modules/forge-std"] + path = modules/forge-std + url = git@github.com:foundry-rs/forge-std.git diff --git a/modules/contract-test-utils b/modules/contract-test-utils deleted file mode 160000 index ec57e3d..0000000 --- a/modules/contract-test-utils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ec57e3d420e028069337dae7a8bb298ba946184d diff --git a/modules/forge-std b/modules/forge-std new file mode 160000 index 0000000..74cfb77 --- /dev/null +++ b/modules/forge-std @@ -0,0 +1 @@ +Subproject commit 74cfb77e308dd188d2f58864aaf44963ae6b88b1 diff --git a/tests/MapleWithdrawalManager.t.sol b/tests/MapleWithdrawalManager.t.sol index bdadb49..3b4f8c9 100644 --- a/tests/MapleWithdrawalManager.t.sol +++ b/tests/MapleWithdrawalManager.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.7; -import { Address, TestUtils } from "../modules/contract-test-utils/contracts/test.sol"; -import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; +import { Test } from "../modules/forge-std/src/Test.sol"; +import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; import { MapleWithdrawalManager } from "../contracts/MapleWithdrawalManager.sol"; import { MapleWithdrawalManagerFactory } from "../contracts/MapleWithdrawalManagerFactory.sol"; @@ -10,7 +10,7 @@ import { MapleWithdrawalManagerInitializer } from "../contracts/MapleWithdrawalM import { MockGlobals, MockPool, MockPoolManager, MockWithdrawalManagerMigrator } from "./mocks/Mocks.sol"; -contract TestBase is TestUtils { +contract TestBase is Test { address internal governor; address internal implementation; @@ -31,11 +31,12 @@ contract TestBase is TestUtils { MapleWithdrawalManagerFactory internal factory; function setUp() public virtual { - governor = address(new Address()); + governor = makeAddr("governor"); + lp = makeAddr("lp"); + poolDelegate = makeAddr("poolDelegate"); + implementation = address(new MapleWithdrawalManager()); initializer = address(new MapleWithdrawalManagerInitializer()); - lp = address(new Address()); - poolDelegate = address(new Address()); start = 1641164400; @@ -1029,8 +1030,8 @@ contract ProcessExitWithMultipleUsers is TestBase { function setUp() public override{ super.setUp(); - lp2 = address(new Address()); - lp3 = address(new Address()); + lp2 = makeAddr("lp2"); + lp3 = makeAddr("lp3"); pool.mint(address(poolManager), 800); diff --git a/tests/MapleWithdrawalManagerFactory.t.sol b/tests/MapleWithdrawalManagerFactory.t.sol index 9b1064f..577000d 100644 --- a/tests/MapleWithdrawalManagerFactory.t.sol +++ b/tests/MapleWithdrawalManagerFactory.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.7; -import { Address, TestUtils } from "../modules/contract-test-utils/contracts/test.sol"; -import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; +import { Test } from "../modules/forge-std/src/Test.sol"; +import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; import { MapleWithdrawalManager } from "../contracts/MapleWithdrawalManager.sol"; import { MapleWithdrawalManagerFactory } from "../contracts/MapleWithdrawalManagerFactory.sol"; @@ -10,7 +10,7 @@ import { MapleWithdrawalManagerInitializer } from "../contracts/MapleWithdrawalM import { MockGlobals, MockPool } from "./mocks/Mocks.sol"; -contract MapleWithdrawalManagerFactoryTests is TestUtils { +contract MapleWithdrawalManagerFactoryTests is Test { address internal governor; address internal poolDelegate; @@ -25,11 +25,12 @@ contract MapleWithdrawalManagerFactoryTests is TestUtils { MapleWithdrawalManagerFactory internal factory; function setUp() external { - governor = address(new Address()); + governor = makeAddr("governor"); + poolDelegate = makeAddr("poolDelegate"); + implementation = address(new MapleWithdrawalManager()); initializer = address(new MapleWithdrawalManagerInitializer()); - poolDelegate = address(new Address()); - + asset = new MockERC20("Wrapped Ether", "WETH", 18); globals = new MockGlobals(address(governor)); pool = new MockPool("Maple Pool", "MP-WETH", 18, address(asset), poolDelegate); From 95212a0adb2853420e60d094c01323274979cb75 Mon Sep 17 00:00:00 2001 From: Vedran Bidin Date: Fri, 8 Sep 2023 10:12:48 +0300 Subject: [PATCH 06/10] feat: Add a configurable starting time to the `WithdrawalManager` (SC-13476) (#67) * feat: add starting time to wm initializer * fix: update success test case * feat: default to initial cycle * test: add test cases for view functions --- contracts/MapleWithdrawalManager.sol | 9 +- .../MapleWithdrawalManagerInitializer.sol | 23 ++- .../IMapleWithdrawalManagerInitializer.sol | 4 +- tests/MapleWithdrawalManager.t.sol | 189 +++++++++++++----- tests/MapleWithdrawalManagerFactory.t.sol | 25 ++- 5 files changed, 172 insertions(+), 78 deletions(-) diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol index 6d2f4bb..f5219ba 100644 --- a/contracts/MapleWithdrawalManager.sol +++ b/contracts/MapleWithdrawalManager.sol @@ -329,17 +329,22 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag function getCurrentConfig() public view override returns (CycleConfig memory config_) { uint256 configId_ = latestConfigId; - while (block.timestamp < cycleConfigs[configId_].initialCycleTime) { + while (configId_ != 0 && block.timestamp < cycleConfigs[configId_].initialCycleTime) { --configId_; } config_ = cycleConfigs[configId_]; } + // NOTE: If the first cycle has not been reached yet it will still be returned as the current one. function getCurrentCycleId() public view override returns (uint256 cycleId_) { CycleConfig memory config_ = getCurrentConfig(); - cycleId_ = config_.initialCycleId + (block.timestamp - config_.initialCycleTime) / config_.cycleDuration; + cycleId_ = config_.initialCycleId; + + if (block.timestamp > config_.initialCycleTime) { + cycleId_ += (block.timestamp - config_.initialCycleTime) / config_.cycleDuration; + } } function getRedeemableAmounts(uint256 lockedShares_, address owner_) diff --git a/contracts/MapleWithdrawalManagerInitializer.sol b/contracts/MapleWithdrawalManagerInitializer.sol index 4eb1008..1555dbc 100644 --- a/contracts/MapleWithdrawalManagerInitializer.sol +++ b/contracts/MapleWithdrawalManagerInitializer.sol @@ -11,42 +11,45 @@ import { MapleWithdrawalManagerStorage } from "./MapleWithdrawalManagerStorage.s contract MapleWithdrawalManagerInitializer is IMapleWithdrawalManagerInitializer, MapleWithdrawalManagerStorage, MapleProxiedInternals { fallback() external { - ( address pool_, uint256 cycleDuration_, uint256 windowDuration_ ) = decodeArguments(msg.data); + ( address pool_, uint256 startTime_, uint256 cycleDuration_, uint256 windowDuration_ ) = decodeArguments(msg.data); - _initialize(pool_, cycleDuration_, windowDuration_); + _initialize(pool_, startTime_, cycleDuration_, windowDuration_); } function decodeArguments(bytes calldata encodedArguments_) public pure override returns ( address pool_, + uint256 startTime_, uint256 cycleDuration_, uint256 windowDuration_ ) { - ( pool_, cycleDuration_, windowDuration_ ) = abi.decode(encodedArguments_, (address, uint256, uint256)); + ( pool_, startTime_, cycleDuration_, windowDuration_ ) = abi.decode(encodedArguments_, (address, uint256, uint256, uint256)); } function encodeArguments( address pool_, + uint256 startTime_, uint256 cycleDuration_, uint256 windowDuration_ ) public pure override returns (bytes memory encodedArguments_) { - encodedArguments_ = abi.encode(pool_, cycleDuration_, windowDuration_); + encodedArguments_ = abi.encode(pool_, startTime_, cycleDuration_, windowDuration_); } - function _initialize(address pool_, uint256 cycleDuration_, uint256 windowDuration_) internal { - require(pool_ != address(0), "WMI:ZERO_POOL"); - require(windowDuration_ != 0, "WMI:ZERO_WINDOW"); - require(windowDuration_ <= cycleDuration_, "WMI:WINDOW_OOB"); + function _initialize(address pool_, uint256 startTime_, uint256 cycleDuration_, uint256 windowDuration_) internal { + require(pool_ != address(0), "WMI:ZERO_POOL"); + require(startTime_ >= block.timestamp, "WMI:INVALID_START"); + require(windowDuration_ != 0, "WMI:ZERO_WINDOW"); + require(windowDuration_ <= cycleDuration_, "WMI:WINDOW_OOB"); pool = pool_; poolManager = IPoolLike(pool_).manager(); cycleConfigs[0] = CycleConfig({ initialCycleId: 1, - initialCycleTime: uint64(block.timestamp), + initialCycleTime: uint64(startTime_), cycleDuration: uint64(cycleDuration_), windowDuration: uint64(windowDuration_) }); @@ -55,7 +58,7 @@ contract MapleWithdrawalManagerInitializer is IMapleWithdrawalManagerInitializer emit ConfigurationUpdated({ configId_: 0, initialCycleId_: 1, - initialCycleTime_: uint64(block.timestamp), + initialCycleTime_: uint64(startTime_), cycleDuration_: uint64(cycleDuration_), windowDuration_: uint64(windowDuration_) }); diff --git a/contracts/interfaces/IMapleWithdrawalManagerInitializer.sol b/contracts/interfaces/IMapleWithdrawalManagerInitializer.sol index af104b2..3fc53cb 100644 --- a/contracts/interfaces/IMapleWithdrawalManagerInitializer.sol +++ b/contracts/interfaces/IMapleWithdrawalManagerInitializer.sol @@ -6,9 +6,9 @@ interface IMapleWithdrawalManagerInitializer { event Initialized(address pool_, uint256 cycleDuration_, uint256 windowDuration_); function decodeArguments(bytes calldata encodedArguments_) external pure - returns (address pool_, uint256 cycleDuration_, uint256 windowDuration_); + returns (address pool_, uint256 startTime_, uint256 cycleDuration_, uint256 windowDuration_); - function encodeArguments(address pool_, uint256 cycleDuration_, uint256 windowDuration_) external pure + function encodeArguments(address pool_, uint256 startTime_, uint256 cycleDuration_, uint256 windowDuration_) external pure returns (bytes memory encodedArguments_); } diff --git a/tests/MapleWithdrawalManager.t.sol b/tests/MapleWithdrawalManager.t.sol index 3b4f8c9..35cb67b 100644 --- a/tests/MapleWithdrawalManager.t.sol +++ b/tests/MapleWithdrawalManager.t.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.7; import { Test } from "../modules/forge-std/src/Test.sol"; import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; +import { IMapleWithdrawalManagerStorage } from "../contracts/interfaces/IMapleWithdrawalManagerStorage.sol"; + import { MapleWithdrawalManager } from "../contracts/MapleWithdrawalManager.sol"; import { MapleWithdrawalManagerFactory } from "../contracts/MapleWithdrawalManagerFactory.sol"; import { MapleWithdrawalManagerInitializer } from "../contracts/MapleWithdrawalManagerInitializer.sol"; @@ -20,6 +22,7 @@ contract TestBase is Test { address internal wm; uint256 internal start; + uint256 internal delay; MockERC20 internal asset; MockGlobals internal globals; @@ -39,6 +42,7 @@ contract TestBase is Test { initializer = address(new MapleWithdrawalManagerInitializer()); start = 1641164400; + delay = 3.5 days; // Create all mocks. asset = new MockERC20("Wrapped Ether", "WETH", 18); @@ -62,7 +66,7 @@ contract TestBase is Test { // Create the withdrawal manager instance. withdrawalManager = MapleWithdrawalManager(factory.createInstance({ - arguments_: abi.encode(address(pool), 1 weeks, 2 days), + arguments_: abi.encode(address(pool), start + delay, 1 weeks, 2 days), salt_: "SALT" })); @@ -287,8 +291,8 @@ contract SetExitConfigTests is TestBase { assertEq(withdrawalManager.latestConfigId(), 1); assertConfig({ configurationId: 1, - initialCycleId: 1 + 3, - initialCycleTime: start + 3 weeks, + initialCycleId: 1 + 3, + initialCycleTime: start + delay + 3 weeks, cycleDuration: 1 weeks, windowDuration: 1 days }); @@ -313,8 +317,8 @@ contract SetExitConfigTests is TestBase { assertConfig({ configurationId: 1, - initialCycleId: 1 + 3, - initialCycleTime: start + (1 weeks * 3), + initialCycleId: 1 + 3, + initialCycleTime: start + delay + (1 weeks * 3), cycleDuration: 2 weeks, windowDuration: 2 days }); @@ -328,7 +332,7 @@ contract SetExitConfigTests is TestBase { }); // Wait until a new cycle begins. - vm.warp(start + 1 weeks); + vm.warp(start + delay + 1 weeks); vm.prank(poolDelegate); withdrawalManager.setExitConfig(3 weeks, 3 days); @@ -336,22 +340,22 @@ contract SetExitConfigTests is TestBase { assertConfig({ configurationId: 1, - initialCycleId: 1 + 3, - initialCycleTime: start + (1 weeks * 3), + initialCycleId: 1 + 3, + initialCycleTime: start + delay + (1 weeks * 3), cycleDuration: 2 weeks, windowDuration: 2 days }); assertConfig({ configurationId: 2, - initialCycleId: 1 + 3 + 1, - initialCycleTime: start + (1 weeks * 3) + 2 weeks, // 3 weeks for cycles 1-3 + 2 weeks for cycle 4 + initialCycleId: 1 + 3 + 1, + initialCycleTime: start + delay + (1 weeks * 3) + 2 weeks, // 3 weeks for cycles 1-3 + 2 weeks for cycle 4 cycleDuration: 3 weeks, windowDuration: 3 days }); // Update the configuration again within the same cycle in order to overwrite it. - vm.warp(start + 2 weeks - 1); + vm.warp(start + delay + 2 weeks - 1 seconds); vm.prank(poolDelegate); withdrawalManager.setExitConfig(3 weeks, 1 days); @@ -359,16 +363,16 @@ contract SetExitConfigTests is TestBase { assertConfig({ configurationId: 1, - initialCycleId: 1 + 3, - initialCycleTime: start + (1 weeks * 3), + initialCycleId: 1 + 3, + initialCycleTime: start + delay + (1 weeks * 3), cycleDuration: 2 weeks, windowDuration: 2 days }); assertConfig({ configurationId: 2, - initialCycleId: 1 + 3 + 1, - initialCycleTime: start + (1 weeks * 3) + 2 weeks, + initialCycleId: 1 + 3 + 1, + initialCycleTime: start + delay + (1 weeks * 3) + 2 weeks, cycleDuration: 3 weeks, windowDuration: 1 days }); @@ -386,7 +390,7 @@ contract SetExitConfigTests is TestBase { }); // 1 full cycles goes by - Current cycle is 2. - vm.warp(start + 1 weeks + 1); + vm.warp(start + delay + 1 weeks + 1 seconds); // Add a new configuration. vm.prank(poolDelegate); @@ -396,8 +400,8 @@ contract SetExitConfigTests is TestBase { assertConfig({ configurationId: 1, - initialCycleId: 2 + 3, - initialCycleTime: start + 1 weeks + (1 weeks * 3), // Starting at cycle 2 + 3 cycles + initialCycleId: 2 + 3, + initialCycleTime: start + delay + 1 weeks + (1 weeks * 3), // Starting at cycle 2 + 3 cycles cycleDuration: 2 weeks, windowDuration: 5 days }); @@ -411,14 +415,14 @@ contract SetExitConfigTests is TestBase { // Still schedule to start the same time as before, but with different configurations, meaning the config was updated. assertConfig({ configurationId: 1, - initialCycleId: 2 + 3, - initialCycleTime: start + 1 weeks + (1 weeks * 3), // Starting at cycle 2 + 3 cycles + initialCycleId: 2 + 3, + initialCycleTime: start + delay + 1 weeks + (1 weeks * 3), // Starting at cycle 2 + 3 cycles cycleDuration: 4 weeks, windowDuration: 1 days }); // Another cycle goes by - current cycle is 3. - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); // Add a new configuration. vm.prank(poolDelegate); @@ -429,22 +433,22 @@ contract SetExitConfigTests is TestBase { // The previous config is still schedule to start at the correct time. assertConfig({ configurationId: 1, - initialCycleId: 2 + 3, - initialCycleTime: start + 1 weeks + (1 weeks * 3), // Starting at cycle 2 + 3 cycles + initialCycleId: 2 + 3, + initialCycleTime: start + delay + 1 weeks + (1 weeks * 3), // Starting at cycle 2 + 3 cycles cycleDuration: 4 weeks, windowDuration: 1 days }); assertConfig({ configurationId: 2, - initialCycleId: 3 + 3, - initialCycleTime: start + 1 weeks + (1 weeks * 3) + 4 weeks, // Starting at cycle 3 + 3 cycles (2 at config 0 one at config 1) + initialCycleId: 3 + 3, + initialCycleTime: start + delay + 1 weeks + (1 weeks * 3) + 4 weeks, // Starting at cycle 3 + 3 cycles (2 at config 0 one at config 1) cycleDuration: 3 weeks, windowDuration: 1 days }); // Warp another cycle - Making the current one 4 - vm.warp(start + 3 weeks); + vm.warp(start + delay + 3 weeks); // Add yet another config vm.prank(poolDelegate); @@ -454,8 +458,8 @@ contract SetExitConfigTests is TestBase { assertConfig({ configurationId: 3, - initialCycleId: 4 + 3, - initialCycleTime: start + 1 weeks + (1 weeks * 3) + 4 weeks + 3 weeks, // Starting at cycle 4 + 3 cycles (1 at config 0, 1 at config 1, one at config 2) + initialCycleId: 4 + 3, + initialCycleTime: start + delay + 1 weeks + (1 weeks * 3) + 4 weeks + 3 weeks, // Starting at cycle 4 + 3 cycles (1 at config 0, 1 at config 1, one at config 2) cycleDuration: 4 weeks, windowDuration: 4 days }); @@ -468,8 +472,8 @@ contract SetExitConfigTests is TestBase { assertConfig({ configurationId: 3, - initialCycleId: 4 + 3, - initialCycleTime: start + 1 weeks + (1 weeks * 3) + 4 weeks + 3 weeks, // Starting at cycle 4 + 3 cycles (1 at config 0, 1 at config 1, one at config 2) + initialCycleId: 4 + 3, + initialCycleTime: start + delay + 1 weeks + (1 weeks * 3) + 4 weeks + 3 weeks, // Starting at cycle 4 + 3 cycles (1 at config 0, 1 at config 1, one at config 2) cycleDuration: 2 weeks, windowDuration: 2 days }); @@ -502,7 +506,7 @@ contract AddSharesTests is TestBase { vm.prank(pm); withdrawalManager.addShares(1, lp); - vm.warp(start + 2 weeks - 1 seconds); + vm.warp(start + delay + 2 weeks - 1 seconds); vm.prank(pm); vm.expectRevert("WM:AS:WITHDRAWAL_PENDING"); withdrawalManager.addShares(1, lp); @@ -562,7 +566,7 @@ contract AddSharesTests is TestBase { assertEq(pool.balanceOf(wm), 1); assertEq(pool.allowance(pm, wm), 1); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); withdrawalManager.addShares(0, lp); @@ -597,7 +601,7 @@ contract AddSharesTests is TestBase { assertEq(pool.balanceOf(wm), 1); assertEq(pool.allowance(pm, wm), 1); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); withdrawalManager.addShares(1, lp); @@ -632,7 +636,7 @@ contract AddSharesTests is TestBase { assertEq(pool.balanceOf(wm), 1); assertEq(pool.allowance(pm, wm), 1); - vm.warp(start + 3 weeks); + vm.warp(start + delay + 3 weeks); vm.prank(pm); withdrawalManager.addShares(0, lp); @@ -673,7 +677,7 @@ contract RemoveSharesTests is TestBase { vm.prank(pm); withdrawalManager.addShares(1, lp); - vm.warp(start + 2 weeks - 1 seconds); + vm.warp(start + delay + 2 weeks - 1 seconds); vm.prank(pm); vm.expectRevert("WM:RS:WITHDRAWAL_PENDING"); withdrawalManager.removeShares(1, lp); @@ -683,7 +687,7 @@ contract RemoveSharesTests is TestBase { vm.prank(pm); withdrawalManager.addShares(1, lp); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); vm.expectRevert("WM:RS:SHARES_OOB"); withdrawalManager.removeShares(0, lp); @@ -693,7 +697,7 @@ contract RemoveSharesTests is TestBase { vm.prank(pm); withdrawalManager.addShares(1, lp); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); vm.expectRevert("WM:RS:SHARES_OOB"); withdrawalManager.removeShares(2, lp); @@ -705,7 +709,7 @@ contract RemoveSharesTests is TestBase { pool.burn(address(withdrawalManager), 1); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); vm.expectRevert("WM:RS:TRANSFER_FAIL"); withdrawalManager.removeShares(1, lp); @@ -723,7 +727,7 @@ contract RemoveSharesTests is TestBase { assertEq(pool.balanceOf(wm), 2); assertEq(pool.balanceOf(lp), 0); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); withdrawalManager.removeShares(1, lp); @@ -748,7 +752,7 @@ contract RemoveSharesTests is TestBase { assertEq(pool.balanceOf(wm), 2); assertEq(pool.balanceOf(lp), 0); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); withdrawalManager.removeShares(2, lp); @@ -773,7 +777,7 @@ contract RemoveSharesTests is TestBase { assertEq(pool.balanceOf(wm), 2); assertEq(pool.balanceOf(lp), 0); - vm.warp(start + 3 weeks); + vm.warp(start + delay + 3 weeks); vm.prank(pm); withdrawalManager.removeShares(1, lp); @@ -833,7 +837,7 @@ contract ProcessExitTests is TestBase { vm.prank(pm); withdrawalManager.addShares(1, lp); - vm.warp(start + 2 weeks - 1); + vm.warp(start + delay + 2 weeks - 1); vm.prank(pm); vm.expectRevert("WM:PE:NOT_IN_WINDOW"); withdrawalManager.processExit(1, lp); @@ -843,7 +847,7 @@ contract ProcessExitTests is TestBase { vm.prank(pm); withdrawalManager.addShares(1, lp); - vm.warp(start + 2 weeks + 2 days); + vm.warp(start + delay + 2 weeks + 2 days); vm.prank(pm); vm.expectRevert("WM:PE:NOT_IN_WINDOW"); @@ -859,7 +863,7 @@ contract ProcessExitTests is TestBase { pool.burn(address(withdrawalManager), 1); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); vm.expectRevert("WM:PE:TRANSFER_FAIL"); withdrawalManager.processExit(1, lp); @@ -879,7 +883,7 @@ contract ProcessExitTests is TestBase { assertEq(pool.balanceOf(wm), 1); assertEq(pool.balanceOf(lp), 0); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); ( uint256 redeemableShares, uint256 resultingAssets ) = withdrawalManager.processExit(1, lp); @@ -910,7 +914,7 @@ contract ProcessExitTests is TestBase { assertEq(pool.balanceOf(wm), 2); assertEq(pool.balanceOf(lp), 0); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); // Only can redeem 1 share of 2 at 2:1 @@ -942,7 +946,7 @@ contract ProcessExitTests is TestBase { assertEq(pool.balanceOf(wm), 1); assertEq(pool.balanceOf(lp), 0); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); vm.prank(pm); ( uint256 redeemableShares, uint256 resultingAssets ) = withdrawalManager.processExit(1, lp); @@ -980,25 +984,25 @@ contract LockedLiquidityTests is TestBase { } function test_lockedLiquidity_beforeWindow() external { - vm.warp(start + 2 weeks - 1); + vm.warp(start + delay + 2 weeks - 1); assertEq(withdrawalManager.lockedLiquidity(), 0); } function test_lockedLiquidity_afterWindow() external { - vm.warp(start + 2 weeks + 2 days); + vm.warp(start + delay + 2 weeks + 2 days); assertEq(withdrawalManager.lockedLiquidity(), 0); } function test_lockedLiquidity_duringWindow() external { - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); assertEq(withdrawalManager.lockedLiquidity(), 1); - vm.warp(start + 2 weeks + 2 days - 1); + vm.warp(start + delay + 2 weeks + 2 days - 1); assertEq(withdrawalManager.lockedLiquidity(), 1); } function test_lockedLiquidity_duringWindowWithdrawal() external { - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); assertEq(withdrawalManager.lockedLiquidity(), 1); vm.prank(pm); @@ -1011,7 +1015,7 @@ contract LockedLiquidityTests is TestBase { poolManager.__setTotalAssets(2); poolManager.__setUnrealizedLosses(1); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); assertEq(withdrawalManager.lockedLiquidity(), 1); vm.prank(pm); @@ -1022,6 +1026,81 @@ contract LockedLiquidityTests is TestBase { } +contract GetCurrentConfigTests is TestBase { + + function test_getCurrentConfig_beforeInitialCycle() external { + vm.warp(start); + IMapleWithdrawalManagerStorage.CycleConfig memory config = withdrawalManager.getCurrentConfig(); + + assertEq(config.initialCycleId, 1); + assertEq(config.initialCycleTime, start + delay); + assertEq(config.cycleDuration, 1 weeks); + assertEq(config.windowDuration, 2 days); + } + + function test_getCurrentConfig_onInitialCycleStart() external { + vm.warp(start + delay); + IMapleWithdrawalManagerStorage.CycleConfig memory config = withdrawalManager.getCurrentConfig(); + + assertEq(config.initialCycleId, 1); + assertEq(config.initialCycleTime, start + delay); + assertEq(config.cycleDuration, 1 weeks); + assertEq(config.windowDuration, 2 days); + } + + function test_getCurrentConfig_duringInitialCycle() external { + vm.warp(start + delay + 1 days); + IMapleWithdrawalManagerStorage.CycleConfig memory config = withdrawalManager.getCurrentConfig(); + + assertEq(config.initialCycleId, 1); + assertEq(config.initialCycleTime, start + delay); + assertEq(config.cycleDuration, 1 weeks); + assertEq(config.windowDuration, 2 days); + } + + function test_getCurrentConfig_afterInitialCycle() external { + vm.warp(start + delay + 10 days); + IMapleWithdrawalManagerStorage.CycleConfig memory config = withdrawalManager.getCurrentConfig(); + + assertEq(config.initialCycleId, 1); + assertEq(config.initialCycleTime, start + delay); + assertEq(config.cycleDuration, 1 weeks); + assertEq(config.windowDuration, 2 days); + } + +} + +contract GetCurrentCycleIdTests is TestBase { + + function test_getCurrentCycleId_beforeInitialCycle() external { + vm.warp(start); + uint256 cycleId = withdrawalManager.getCurrentCycleId(); + + assertEq(cycleId, 1); + } + + function test_getCurrentCycleId_onInitialCycleStart() external { + vm.warp(start + delay); + uint256 cycleId = withdrawalManager.getCurrentCycleId(); + + assertEq(cycleId, 1); + } + + function test_getCurrentCycleId_duringInitialCycle() external { + vm.warp(start + delay + 1 days); + uint256 cycleId = withdrawalManager.getCurrentCycleId(); + + assertEq(cycleId, 1); + } + + function test_getCurrentCycleId_afterInitialCycle() external { + vm.warp(start + delay + 10 days); + uint256 cycleId = withdrawalManager.getCurrentCycleId(); + + assertEq(cycleId, 2); + } +} + contract ProcessExitWithMultipleUsers is TestBase { address internal lp2; @@ -1063,7 +1142,7 @@ contract ProcessExitWithMultipleUsers is TestBase { assertEq(pool.balanceOf(lp2), 0); assertEq(pool.balanceOf(lp3), 0); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); // Process all exits vm.startPrank(address(poolManager)); @@ -1134,7 +1213,7 @@ contract ProcessExitWithMultipleUsers is TestBase { assertEq(pool.balanceOf(lp), 0); assertEq(pool.balanceOf(lp2), 0); - vm.warp(start + 2 weeks); + vm.warp(start + delay + 2 weeks); // Process all exits vm.startPrank(address(poolManager)); diff --git a/tests/MapleWithdrawalManagerFactory.t.sol b/tests/MapleWithdrawalManagerFactory.t.sol index 577000d..04d3d87 100644 --- a/tests/MapleWithdrawalManagerFactory.t.sol +++ b/tests/MapleWithdrawalManagerFactory.t.sol @@ -45,7 +45,7 @@ contract MapleWithdrawalManagerFactoryTests is Test { } function test_createInstance_notPoolDeployer() external { - bytes memory calldata_ = abi.encode(address(pool), 1, 1); + bytes memory calldata_ = abi.encode(address(pool), block.timestamp, 1, 1); MockGlobals(globals).setValidPoolDeployer(address(this), false); vm.expectRevert("WMF:CI:NOT_DEPLOYER"); @@ -56,35 +56,42 @@ contract MapleWithdrawalManagerFactoryTests is Test { } function test_createInstance_zeroPool() external { - bytes memory calldata_ = abi.encode(address(0), 1, 1); + bytes memory calldata_ = abi.encode(address(0), block.timestamp, 1, 1); + + vm.expectRevert("MPF:CI:FAILED"); + factory.createInstance(calldata_, "SALT"); + } + + function test_createInstance_invalidStart() external { + bytes memory calldata_ = abi.encode(address(0), block.timestamp - 1 seconds, 1, 1); vm.expectRevert("MPF:CI:FAILED"); factory.createInstance(calldata_, "SALT"); } function test_createInstance_zeroWindow() external { - bytes memory calldata_ = abi.encode(address(pool), 1, 0); + bytes memory calldata_ = abi.encode(address(pool), block.timestamp, 1, 0); vm.expectRevert("MPF:CI:FAILED"); factory.createInstance(calldata_, "SALT"); } function test_createInstance_windowOutOfBounds() external { - bytes memory calldata_ = abi.encode(address(pool), 1, 2); + bytes memory calldata_ = abi.encode(address(pool), block.timestamp, 1, 2); vm.expectRevert("MPF:CI:FAILED"); factory.createInstance(calldata_, "SALT"); } function testFail_createInstance_collision() external { - bytes memory calldata_ = abi.encode(address(pool), 1, 1); + bytes memory calldata_ = abi.encode(address(pool), block.timestamp, 1, 1); factory.createInstance(calldata_, "SALT"); factory.createInstance(calldata_, "SALT"); } function test_createInstance_success() external { - bytes memory calldata_ = abi.encode(address(pool), 1, 1); + bytes memory calldata_ = abi.encode(address(pool), block.timestamp + 1 weeks, 7 days, 2 days); MapleWithdrawalManager withdrawalManager_ = MapleWithdrawalManager(factory.createInstance(calldata_, "SALT")); @@ -99,9 +106,9 @@ contract MapleWithdrawalManagerFactoryTests is Test { assertEq(withdrawalManager_.latestConfigId(), 0); assertEq(initialCycleId_, 1); - assertEq(initialCycleTime_, block.timestamp); - assertEq(cycleDuration_, 1); - assertEq(windowDuration_, 1); + assertEq(initialCycleTime_, block.timestamp + 1 weeks); + assertEq(cycleDuration_, 7 days); + assertEq(windowDuration_, 2 days); } } From 9b7d7f8d23596569c24fff9a1ca2694e06ea4e96 Mon Sep 17 00:00:00 2001 From: Vedran Bidin Date: Mon, 20 Nov 2023 20:31:43 +0300 Subject: [PATCH 07/10] fix: Enable protocol admins to set exit config (SC-14706) (#69) --- contracts/MapleWithdrawalManager.sol | 21 ++++++++++++-- contracts/interfaces/Interfaces.sol | 2 ++ tests/MapleWithdrawalManager.t.sol | 41 ++++++++++++++++------------ tests/mocks/Mocks.sol | 5 ++++ 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol index f5219ba..c5d1600 100644 --- a/contracts/MapleWithdrawalManager.sol +++ b/contracts/MapleWithdrawalManager.sol @@ -77,6 +77,19 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag _; } + modifier onlyPoolDelegateOrProtocolAdmins { + 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" + ); + + _; + } + /**************************************************************************************************************************************/ /*** Proxy Functions ***/ /**************************************************************************************************************************************/ @@ -111,8 +124,12 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag /*** Administrative Functions ***/ /**************************************************************************************************************************************/ - function setExitConfig(uint256 cycleDuration_, uint256 windowDuration_) external override whenProtocolNotPaused { - require(msg.sender == poolDelegate(), "WM:SEC:NOT_AUTHORIZED"); + function setExitConfig( + uint256 cycleDuration_, + uint256 windowDuration_ + ) + external override whenProtocolNotPaused onlyPoolDelegateOrProtocolAdmins + { require(windowDuration_ != 0, "WM:SEC:ZERO_WINDOW"); require(windowDuration_ <= cycleDuration_, "WM:SEC:WINDOW_OOB"); diff --git a/contracts/interfaces/Interfaces.sol b/contracts/interfaces/Interfaces.sol index 9ad4256..8852c57 100644 --- a/contracts/interfaces/Interfaces.sol +++ b/contracts/interfaces/Interfaces.sol @@ -20,6 +20,8 @@ interface IGlobalsLike { bytes calldata callData_ ) external view returns (bool isValid_); + function operationalAdmin() external view returns (address operationalAdmin_); + function protocolPaused() external view returns (bool protocolPaused_); function unscheduleCall(address caller_, bytes32 functionId_, bytes calldata callData_) external; diff --git a/tests/MapleWithdrawalManager.t.sol b/tests/MapleWithdrawalManager.t.sol index 35cb67b..ae97dc1 100644 --- a/tests/MapleWithdrawalManager.t.sol +++ b/tests/MapleWithdrawalManager.t.sol @@ -18,6 +18,7 @@ contract TestBase is Test { address internal implementation; address internal initializer; address internal lp; + address internal operationalAdmin; address internal poolDelegate; address internal wm; @@ -34,9 +35,10 @@ contract TestBase is Test { MapleWithdrawalManagerFactory internal factory; function setUp() public virtual { - governor = makeAddr("governor"); - lp = makeAddr("lp"); - poolDelegate = makeAddr("poolDelegate"); + governor = makeAddr("governor"); + lp = makeAddr("lp"); + operationalAdmin = makeAddr("operationalAdmin"); + poolDelegate = makeAddr("poolDelegate"); implementation = address(new MapleWithdrawalManager()); initializer = address(new MapleWithdrawalManagerInitializer()); @@ -51,6 +53,7 @@ contract TestBase is Test { poolManager = new MockPoolManager(address(pool), poolDelegate, address(globals)); pool.__setPoolManager(address(poolManager)); + globals.__setOperationalAdmin(operationalAdmin); // Create factory and register implementation. vm.startPrank(governor); @@ -233,21 +236,8 @@ contract SetExitConfigTests is TestBase { withdrawalManager.setExitConfig(1, 1); } - function test_setExitConfig_governor() external { - // Governor should not be allowed. - vm.prank(governor); - vm.expectRevert("WM:SEC:NOT_AUTHORIZED"); - withdrawalManager.setExitConfig(1, 1); - - vm.prank(poolDelegate); - withdrawalManager.setExitConfig(1, 1); - } - - function test_setExitConfig_notPoolDelegate() external { - vm.expectRevert("WM:SEC:NOT_AUTHORIZED"); - withdrawalManager.setExitConfig(1, 1); - - vm.prank(poolDelegate); + function test_setExitConfig_notAuthorized() external { + vm.expectRevert("WM:NOT_PD_OR_GOV_OR_OA"); withdrawalManager.setExitConfig(1, 1); } @@ -271,6 +261,21 @@ contract SetExitConfigTests is TestBase { withdrawalManager.setExitConfig(type(uint64).max, 2 days); } + function test_setExitConfig_poolDelegate() external { + vm.prank(poolDelegate); + withdrawalManager.setExitConfig(1, 1); + } + + function test_setExitConfig_governor() external { + vm.prank(governor); + withdrawalManager.setExitConfig(1, 1); + } + + function test_setExitConfig_operationalAdmin() external { + vm.prank(operationalAdmin); + withdrawalManager.setExitConfig(1, 1); + } + // NOTE: test_setExitConfig_windowDurationCastOob is not reachable because // withdrawalManager.setExitConfig(uint256(type(uint64).max), uint256(type(uint64).max) + 1); causes "WM:SEC:WINDOW_OOB" diff --git a/tests/mocks/Mocks.sol b/tests/mocks/Mocks.sol index 47dde95..dcf4184 100644 --- a/tests/mocks/Mocks.sol +++ b/tests/mocks/Mocks.sol @@ -6,6 +6,7 @@ import { MockERC20 } from "../../modules/erc20/contracts/test/mocks/MockERC20.so contract MockGlobals { address public governor; + address public operationalAdmin; bool internal _isValidScheduledCall; @@ -31,6 +32,10 @@ contract MockGlobals { _isValidScheduledCall = isValid_; } + function __setOperationalAdmin(address operationalAdmin_) external { + operationalAdmin = operationalAdmin_; + } + function __setProtocolPaused(bool protocolPaused_) external { protocolPaused = protocolPaused_; } From 7219811ad66c6866de2e244e223b82f53c27222e Mon Sep 17 00:00:00 2001 From: Farhaan <59924029+0xfarhaan@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:47:11 +0400 Subject: [PATCH 08/10] feat: Add safe casting for WM-C init (#70) * feat: Add safe casting for WM-Q init * fix: test * fix: test --- .../MapleWithdrawalManagerInitializer.sol | 17 +++++++++------ tests/MapleWithdrawalManagerFactory.t.sol | 21 +++++++++++++++++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/contracts/MapleWithdrawalManagerInitializer.sol b/contracts/MapleWithdrawalManagerInitializer.sol index 1555dbc..1dfe012 100644 --- a/contracts/MapleWithdrawalManagerInitializer.sol +++ b/contracts/MapleWithdrawalManagerInitializer.sol @@ -49,19 +49,24 @@ contract MapleWithdrawalManagerInitializer is IMapleWithdrawalManagerInitializer cycleConfigs[0] = CycleConfig({ initialCycleId: 1, - initialCycleTime: uint64(startTime_), - cycleDuration: uint64(cycleDuration_), - windowDuration: uint64(windowDuration_) + initialCycleTime: _uint64(startTime_), + cycleDuration: _uint64(cycleDuration_), + windowDuration: _uint64(windowDuration_) }); emit Initialized(pool_, cycleDuration_, windowDuration_); emit ConfigurationUpdated({ configId_: 0, initialCycleId_: 1, - initialCycleTime_: uint64(startTime_), - cycleDuration_: uint64(cycleDuration_), - windowDuration_: uint64(windowDuration_) + initialCycleTime_: _uint64(startTime_), + cycleDuration_: _uint64(cycleDuration_), + windowDuration_: _uint64(windowDuration_) }); } + function _uint64(uint256 input_) internal pure returns (uint64 output_) { + require(input_ <= type(uint64).max, "WMI:UINT64"); + output_ = uint64(input_); + } + } diff --git a/tests/MapleWithdrawalManagerFactory.t.sol b/tests/MapleWithdrawalManagerFactory.t.sol index 04d3d87..879124a 100644 --- a/tests/MapleWithdrawalManagerFactory.t.sol +++ b/tests/MapleWithdrawalManagerFactory.t.sol @@ -27,10 +27,10 @@ contract MapleWithdrawalManagerFactoryTests is Test { function setUp() external { governor = makeAddr("governor"); poolDelegate = makeAddr("poolDelegate"); - + implementation = address(new MapleWithdrawalManager()); initializer = address(new MapleWithdrawalManagerInitializer()); - + asset = new MockERC20("Wrapped Ether", "WETH", 18); globals = new MockGlobals(address(governor)); pool = new MockPool("Maple Pool", "MP-WETH", 18, address(asset), poolDelegate); @@ -90,6 +90,23 @@ contract MapleWithdrawalManagerFactoryTests is Test { factory.createInstance(calldata_, "SALT"); } + function test_createInstance_safeCastOutOfBounds() external { + bytes memory calldata_ = abi.encode(address(pool), type(uint128).max, 7 days, 2 days); + + vm.expectRevert("MPF:CI:FAILED"); + factory.createInstance(calldata_, "SALT"); + + calldata_ = abi.encode(address(pool), block.timestamp + 1 weeks, type(uint128).max, 2 days); + + vm.expectRevert("MPF:CI:FAILED"); + factory.createInstance(calldata_, "SALT"); + + calldata_ = abi.encode(address(pool), block.timestamp + 1 weeks, 7 days, type(uint128).max); + + vm.expectRevert("MPF:CI:FAILED"); + factory.createInstance(calldata_, "SALT"); + } + function test_createInstance_success() external { bytes memory calldata_ = abi.encode(address(pool), block.timestamp + 1 weeks, 7 days, 2 days); From 1d05b3db083a1f0c2b5bbba8f40294d77ba5c98f Mon Sep 17 00:00:00 2001 From: Joao Gabriel Carvalho Date: Mon, 11 Dec 2023 15:43:28 -0300 Subject: [PATCH 09/10] feat: Change access control on upgrade (#71) --- contracts/MapleWithdrawalManager.sol | 4 ++-- contracts/interfaces/Interfaces.sol | 2 ++ tests/MapleWithdrawalManager.t.sol | 7 +++++-- tests/mocks/Mocks.sol | 5 +++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/contracts/MapleWithdrawalManager.sol b/contracts/MapleWithdrawalManager.sol index c5d1600..366a9d9 100644 --- a/contracts/MapleWithdrawalManager.sol +++ b/contracts/MapleWithdrawalManager.sol @@ -107,10 +107,10 @@ contract MapleWithdrawalManager is IMapleWithdrawalManager, MapleWithdrawalManag function upgrade(uint256 version_, bytes calldata arguments_) external override { address poolDelegate_ = poolDelegate(); - require(msg.sender == poolDelegate_ || msg.sender == governor(), "WM:U:NOT_AUTHORIZED"); - IGlobalsLike mapleGlobals_ = IGlobalsLike(globals()); + require(msg.sender == poolDelegate_ || msg.sender == mapleGlobals_.securityAdmin(), "WM:U:NOT_AUTHORIZED"); + if (msg.sender == poolDelegate_) { require(mapleGlobals_.isValidScheduledCall(msg.sender, address(this), "WM:UPGRADE", msg.data), "WM:U:INVALID_SCHED_CALL"); diff --git a/contracts/interfaces/Interfaces.sol b/contracts/interfaces/Interfaces.sol index 8852c57..2e6686b 100644 --- a/contracts/interfaces/Interfaces.sol +++ b/contracts/interfaces/Interfaces.sol @@ -24,6 +24,8 @@ interface IGlobalsLike { function protocolPaused() external view returns (bool protocolPaused_); + function securityAdmin() external view returns (address securityAdmin_); + function unscheduleCall(address caller_, bytes32 functionId_, bytes calldata callData_) external; } diff --git a/tests/MapleWithdrawalManager.t.sol b/tests/MapleWithdrawalManager.t.sol index ae97dc1..2203d10 100644 --- a/tests/MapleWithdrawalManager.t.sol +++ b/tests/MapleWithdrawalManager.t.sol @@ -20,6 +20,7 @@ contract TestBase is Test { address internal lp; address internal operationalAdmin; address internal poolDelegate; + address internal securityAdmin; address internal wm; uint256 internal start; @@ -39,6 +40,7 @@ contract TestBase is Test { lp = makeAddr("lp"); operationalAdmin = makeAddr("operationalAdmin"); poolDelegate = makeAddr("poolDelegate"); + securityAdmin = makeAddr("securityAdmin"); implementation = address(new MapleWithdrawalManager()); initializer = address(new MapleWithdrawalManagerInitializer()); @@ -54,6 +56,7 @@ contract TestBase is Test { pool.__setPoolManager(address(poolManager)); globals.__setOperationalAdmin(operationalAdmin); + globals.__setSecurityAdmin(securityAdmin); // Create factory and register implementation. vm.startPrank(governor); @@ -179,11 +182,11 @@ contract UpgradeTests is TestBase { vm.stopPrank(); } - function test_upgrade_notGovernor() external { + function test_upgrade_notSecurityAdmin() external { vm.expectRevert("WM:U:NOT_AUTHORIZED"); withdrawalManager.upgrade(2, ""); - vm.prank(governor); + vm.prank(securityAdmin); withdrawalManager.upgrade(2, abi.encode(address(0))); } diff --git a/tests/mocks/Mocks.sol b/tests/mocks/Mocks.sol index dcf4184..768d3f0 100644 --- a/tests/mocks/Mocks.sol +++ b/tests/mocks/Mocks.sol @@ -7,6 +7,7 @@ contract MockGlobals { address public governor; address public operationalAdmin; + address public securityAdmin; bool internal _isValidScheduledCall; @@ -40,6 +41,10 @@ contract MockGlobals { protocolPaused = protocolPaused_; } + function __setSecurityAdmin(address securityAdmin_) external { + securityAdmin = securityAdmin_; + } + } contract MockPool is MockERC20 { From 329be5f9797ff14399654cb321374a1d01992bd9 Mon Sep 17 00:00:00 2001 From: JG Carvalho Date: Tue, 19 Dec 2023 19:16:56 -0300 Subject: [PATCH 10/10] feat: update package and revert private readme --- README.md | 10 ++++++---- configs/package.yaml | 14 +++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3fcac32..9de549a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Withdrawal Manager -![Foundry CI](https://github.com/maple-labs/withdrawal-manager-private/actions/workflows/forge.yml/badge.svg) +![Foundry CI](https://github.com/maple-labs/withdrawal-manager-cyclical/actions/workflows/forge.yml/badge.svg) [![GitBook - Documentation](https://img.shields.io/badge/GitBook-Documentation-orange?logo=gitbook&logoColor=white)](https://maplefinance.gitbook.io/maple/maple-for-developers/protocol-overview) [![Foundry][foundry-badge]][foundry] -[![License: BUSL 1.1](https://img.shields.io/badge/License-BUSL%201.1-blue.svg)](https://github.com/maple-labs/withdrawal-manager-private/blob/main/LICENSE) +[![License: BUSL 1.1](https://img.shields.io/badge/License-BUSL%201.1-blue.svg)](https://github.com/maple-labs/withdrawal-manager-cyclical/blob/main/LICENSE) [foundry]: https://getfoundry.sh/ [foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg @@ -31,8 +31,8 @@ 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-private.git -cd withdrawal-manager-private +git clone git@github.com:maple-labs/withdrawal-manager-cyclical.git +cd withdrawal-manager-cyclical forge install ``` @@ -50,6 +50,8 @@ forge install | Trail of Bits | [`2022-08-24 - Trail of Bits Report`](https://docs.google.com/viewer?url=https://github.com/maple-labs/maple-v2-audits/files/10246688/Maple.Finance.v2.-.Final.Report.-.Fixed.-.2022.pdf) | | Spearbit | [`2022-10-17 - Spearbit Report`](https://docs.google.com/viewer?url=https://github.com/maple-labs/maple-v2-audits/files/10223545/Maple.Finance.v2.-.Spearbit.pdf) | | Three Sigma | [`2022-10-24 - Three Sigma Report`](https://docs.google.com/viewer?url=https://github.com/maple-labs/maple-v2-audits/files/10223541/three-sigma_maple-finance_code-audit_v1.1.1.pdf) | +| Three Sigma | [`2023-11-06 - Three Sigma Report`](https://docs.google.com/viewer?url=https://github.com/maple-labs/maple-v2-audits/files/13707288/Maple-Q4-Three-Sigma-Audit.pdf) | +| 0xMacro | [`2023-11-27 - 0xMacro Report`](https://docs.google.com/viewer?url=https://github.com/maple-labs/maple-v2-audits/files/13707291/Maple-Q4-0xMacro-Audit.pdf) | ## Bug Bounty diff --git a/configs/package.yaml b/configs/package.yaml index f4b9662..0da3303 100644 --- a/configs/package.yaml +++ b/configs/package.yaml @@ -1,11 +1,11 @@ name: withdrawal-manager -version: 1.0.0 +version: 1.0.1 source: contracts packages: - - path: contracts/WithdrawalManager.sol - contractName: WithdrawalManager - - path: contracts/WithdrawalManagerFactory.sol - contractName: WithdrawalManagerFactory - - path: contracts/WithdrawalManagerInitializer.sol - contractName: WithdrawalManagerInitializer + - path: contracts/MapleWithdrawalManager.sol + contractName: MapleWithdrawalManager + - path: contracts/MapleWithdrawalManagerFactory.sol + contractName: MapleWithdrawalManagerFactory + - path: contracts/MapleWithdrawalManagerInitializer.sol + contractName: MapleWithdrawalManagerInitializer customDescription: Withdrawal Manager Artifacts and ABIs