From b3579787963db563157fc5dbe44e7d8c115f5c33 Mon Sep 17 00:00:00 2001 From: Joao Gabriel Carvalho Date: Fri, 26 Aug 2022 11:59:28 -0300 Subject: [PATCH] feat: Upgrade to V4 (#66) * feat: added function to set lenders * fix: removed extra line * feat: added migrators for debtLOcker * feat: created debt locker v4 * fix: removed extra line * Update IDebtLocker.sol * Update DebtLocker.t.sol * chore: move submodules to ssh * fix: add forge support, fix ci * fix: update yaml * fix: add gitignore * Delete solidity-files-cache.json Co-authored-by: Lucas Manuel --- .circleci/config.yml | 24 --- .github/PULL_REQUEST_TEMPLATE.md | 16 -- .github/workflows/forge-pr.yaml | 41 +++++ .github/workflows/forge.yaml | 43 +++++ .gitignore | 1 + .gitmodules | 10 +- Makefile | 20 ++- build.sh | 18 --- config/ci.json | 36 ----- config/dev.json | 38 ----- config/prod.json | 37 ----- package.yaml => configs/package.yaml | 5 +- contracts/DebtLocker.sol | 10 ++ contracts/DebtLockerStorage.sol | 1 + contracts/DebtLockerV4Migrator.sol | 27 ++++ contracts/interfaces/IDebtLocker.sol | 11 ++ .../interfaces/IDebtLockerV4Migrator.sol | 11 ++ contracts/interfaces/Interfaces.sol | 2 + contracts/test/accounts/Governor.sol | 6 - foundry.toml | 16 ++ release.sh | 42 ----- scripts/build.sh | 14 ++ scripts/release.sh | 1 + scripts/test.sh | 20 +++ test.sh | 30 ---- {contracts/test => tests}/DebtLocker.t.sol | 151 ++++++++++++++++-- tests/accounts/Governor.sol | 6 + tests/accounts/LoanMigrator.sol | 16 ++ .../test => tests}/accounts/PoolDelegate.sol | 4 +- .../mocks/DebtLockerHarness.sol | 2 +- .../mocks/ManipulatableDebtLocker.sol | 4 +- {contracts/test => tests}/mocks/Mocks.sol | 10 +- 32 files changed, 391 insertions(+), 282 deletions(-) delete mode 100644 .circleci/config.yml delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/forge-pr.yaml create mode 100644 .github/workflows/forge.yaml delete mode 100755 build.sh delete mode 100644 config/ci.json delete mode 100644 config/dev.json delete mode 100644 config/prod.json rename package.yaml => configs/package.yaml (54%) create mode 100644 contracts/DebtLockerV4Migrator.sol create mode 100644 contracts/interfaces/IDebtLockerV4Migrator.sol delete mode 100644 contracts/test/accounts/Governor.sol create mode 100644 foundry.toml delete mode 100755 release.sh create mode 100755 scripts/build.sh create mode 100755 scripts/release.sh create mode 100755 scripts/test.sh delete mode 100755 test.sh rename {contracts/test => tests}/DebtLocker.t.sol (87%) create mode 100644 tests/accounts/Governor.sol create mode 100644 tests/accounts/LoanMigrator.sol rename {contracts/test => tests}/accounts/PoolDelegate.sol (95%) rename {contracts/test => tests}/mocks/DebtLockerHarness.sol (89%) rename {contracts/test => tests}/mocks/ManipulatableDebtLocker.sol (85%) rename {contracts/test => tests}/mocks/Mocks.sol (88%) diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 69a9f44..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: 2.1 - -jobs: - dapp_test: - docker: - - image: bakii0499/dapptools:0.48.0-solc-0.8.7 - steps: - - run: - name: Checkout debt-locker - command: | - GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git clone git@github.com:maple-labs/debt-locker.git . - git checkout $CIRCLE_BRANCH - - run: - name: Build and test contracts - command: | - GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git submodule update --init --recursive - ./test.sh -c ./config/ci.json - -workflows: - version: 2 - test_all: - jobs: - - dapp_test: - context: seth diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index bb7c3fa..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,16 +0,0 @@ -# Description - -# Integrations Checklist - -- [ ] Have any function signatures changed? If yes, outline below. -- [ ] Have any features changed or been added? If yes, outline below. -- [ ] Have any events changed or been added? If yes, outline below. -- [ ] Has all documentation been updated? - -# Changelog -## Function Signature Changes - -## Features - -## Events - diff --git a/.github/workflows/forge-pr.yaml b/.github/workflows/forge-pr.yaml new file mode 100644 index 0000000..53516a8 --- /dev/null +++ b/.github/workflows/forge-pr.yaml @@ -0,0 +1,41 @@ +name: Forge Tests (PR) + +on: [pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Checkout liquidations private submodule + env: + SSH_KEY_LIQUIDATIONS: ${{secrets.SSH_KEY_LIQUIDATIONS}} + shell: bash + run: | + mkdir $HOME/.ssh + echo "$SSH_KEY_LIQUIDATIONS" > $HOME/.ssh/id_rsa + chmod 600 $HOME/.ssh/id_rsa + git submodule update --init --recursive modules/liquidations + + - name: Checkout loan private submodule + env: + SSH_KEY_LOAN: ${{secrets.SSH_KEY_LOAN}} + shell: bash + run: | + echo "$SSH_KEY_LOAN" > $HOME/.ssh/id_rsa + chmod 600 $HOME/.ssh/id_rsa + git submodule update --init --recursive modules/loan + + - name: Checkout public 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 diff --git a/.github/workflows/forge.yaml b/.github/workflows/forge.yaml new file mode 100644 index 0000000..2536fdf --- /dev/null +++ b/.github/workflows/forge.yaml @@ -0,0 +1,43 @@ +name: Forge Tests + +on: + push: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Checkout liquidations private submodule + env: + SSH_KEY_LIQUIDATIONS: ${{secrets.SSH_KEY_LIQUIDATIONS}} + shell: bash + run: | + mkdir $HOME/.ssh + echo "$SSH_KEY_LIQUIDATIONS" > $HOME/.ssh/id_rsa + chmod 600 $HOME/.ssh/id_rsa + git submodule update --init --recursive modules/liquidations + + - name: Checkout loan private submodule + env: + SSH_KEY_LOAN: ${{secrets.SSH_KEY_LOAN}} + shell: bash + run: | + echo "$SSH_KEY_LOAN" > $HOME/.ssh/id_rsa + chmod 600 $HOME/.ssh/id_rsa + git submodule update --init --recursive modules/loan + + - name: Checkout public 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 super_deep diff --git a/.gitignore b/.gitignore index f1a8f62..eae834a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ hevm* artifacts/* docs/* metadata.json +cache diff --git a/.gitmodules b/.gitmodules index 48f5877..8946bd9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,18 +1,18 @@ [submodule "modules/erc20"] path = modules/erc20 - url = https://github.com/maple-labs/erc20.git + url = git@github.com:maple-labs/erc20.git [submodule "modules/erc20-helper"] path = modules/erc20-helper - url = https://github.com/maple-labs/erc20-helper.git + url = git@github.com:maple-labs/erc20-helper.git [submodule "modules/contract-test-utils"] path = modules/contract-test-utils - url = https://github.com/maple-labs/contract-test-utils.git + url = git@github.com:maple-labs/contract-test-utils.git [submodule "modules/liquidations"] path = modules/liquidations - url = https://github.com/maple-labs/liquidations + url = git@github.com:maple-labs/liquidations.git [submodule "modules/maple-proxy-factory"] path = modules/maple-proxy-factory url = git@github.com:maple-labs/maple-proxy-factory.git [submodule "modules/loan"] path = modules/loan - url = https://github.com/maple-labs/loan + url = git@github.com:maple-labs/loan.git diff --git a/Makefile b/Makefile index f75d72e..b65c658 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,14 @@ -prod :; ./build.sh -c ./config/prod.json -dev :; ./build.sh -c ./config/dev.json -ci :; ./build.sh -c ./config/ci.json -clean :; dapp clean -test :; ./test.sh -release :; ./release.sh +install: + @git submodule update --init --recursive + +update: + @forge update + +build: + @scripts/build.sh -p default + +test: + @scripts/test.sh + +clean: + @forge clean diff --git a/build.sh b/build.sh deleted file mode 100755 index 4f0ee21..0000000 --- a/build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -set -e - -while getopts t:r:b:v:c: flag -do - case "${flag}" in - c) config=${OPTARG};; - esac -done - -config=$([ -z "$config" ] && echo "./config/prod.json" || echo "$config") - -export DAPP_SOLC_VERSION=0.8.7 -export DAPP_SRC="contracts" -export DAPP_LINK_TEST_LIBRARIES=0 -export DAPP_STANDARD_JSON=$config - -dapp --use solc:0.8.7 build diff --git a/config/ci.json b/config/ci.json deleted file mode 100644 index ef0d0d1..0000000 --- a/config/ci.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "contracts/DebtLocker.sol": { - "urls": ["contracts/DebtLocker.sol"] - }, - "contracts/DebtLockerFactory.sol": { - "urls": ["contracts/DebtLockerFactory.sol"] - }, - "contracts/DebtLockerInitializer.sol": { - "urls": ["contracts/DebtLockerInitializer.sol"] - }, - "contracts/test/DebtLocker.t.sol": { - "urls": ["contracts/test/DebtLocker.t.sol"] - } - }, - "settings": { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode" - ], - "": ["ast"] - } - }, - "metadata": { - "bytecodeHash": "none" - } - } -} diff --git a/config/dev.json b/config/dev.json deleted file mode 100644 index 3180aa0..0000000 --- a/config/dev.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "contracts/DebtLocker.sol": { - "urls": ["contracts/DebtLocker.sol"] - }, - "contracts/DebtLockerFactory.sol": { - "urls": ["contracts/DebtLockerFactory.sol"] - }, - "contracts/DebtLockerInitializer.sol": { - "urls": ["contracts/DebtLockerInitializer.sol"] - }, - "contracts/test/DebtLocker.t.sol": { - "urls": ["contracts/test/DebtLocker.t.sol"] - } - }, - "settings": { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "metadata", - "evm.bytecode", - "evm.deployedBytecode", - "evm.gasEstimates" - ], - "": ["ast"] - } - }, - "metadata": { - "bytecodeHash": "none" - } - } -} diff --git a/config/prod.json b/config/prod.json deleted file mode 100644 index 55e8b0f..0000000 --- a/config/prod.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "language": "Solidity", - "sources": { - "contracts/DebtLocker.sol": { - "urls": ["contracts/DebtLocker.sol"] - }, - "contracts/DebtLockerFactory.sol": { - "urls": ["contracts/DebtLockerFactory.sol"] - }, - "contracts/DebtLockerInitializer.sol": { - "urls": ["contracts/DebtLockerInitializer.sol"] - } - }, - "settings": { - "optimizer": { - "enabled": true, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "devdoc", - "userdoc", - "metadata", - "evm.bytecode", - "evm.deployedBytecode", - "evm.gasEstimates" - ], - "": ["ast"] - } - }, - "metadata": { - "bytecodeHash": "none" - } - } -} diff --git a/package.yaml b/configs/package.yaml similarity index 54% rename from package.yaml rename to configs/package.yaml index cd5e561..3ed33cf 100644 --- a/package.yaml +++ b/configs/package.yaml @@ -1,14 +1,11 @@ name: debt-locker -version: 2.0.0 +version: 4.0.0-alpha.0 source: contracts packages: - path: contracts/DebtLocker.sol contractName: DebtLocker - customChecksum: 0x852d096582581e400aa000467ea9e59e7939c20e4cbbfe0c0f3d04ac4286f600 - path: contracts/DebtLockerFactory.sol contractName: DebtLockerFactory - customChecksum: 0x90aa9362f8cf6d72464bb112ecddba515337250c33bff5c86e55831f005776d3 - path: contracts/DebtLockerInitializer.sol contractName: DebtLockerInitializer - customChecksum: 0xc47b9628c0eaa75890eaced74c259ae3613b4cb0f5090a2925c2fae166f59486 customDescription: Maple Debt Locker Artifacts and ABIs diff --git a/contracts/DebtLocker.sol b/contracts/DebtLocker.sol index 48635fa..4d6ebf3 100644 --- a/contracts/DebtLocker.sol +++ b/contracts/DebtLocker.sol @@ -110,6 +110,12 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxiedInternals { emit FundsToCaptureSet(_fundsToCapture = amount_); } + function setPendingLender(address newLender_) override external whenProtocolNotPaused { + require(msg.sender == _loanMigrator, "DL:SPL:NOT_MIGRATOR"); + + IMapleLoanLike(_loan).setPendingLender(newLender_); + } + function setMinRatio(uint256 minRatio_) external override whenProtocolNotPaused { require(msg.sender == _getPoolDelegate(), "DL:SMR:NOT_PD"); @@ -280,6 +286,10 @@ contract DebtLocker is IDebtLocker, DebtLockerStorage, MapleProxiedInternals { return _loan; } + function loanMigrator() external view override returns (address loan_) { + return _loanMigrator; + } + function minRatio() external view override returns (uint256 minRatio_) { return _minRatio; } diff --git a/contracts/DebtLockerStorage.sol b/contracts/DebtLockerStorage.sol index ba842f2..663c743 100644 --- a/contracts/DebtLockerStorage.sol +++ b/contracts/DebtLockerStorage.sol @@ -16,4 +16,5 @@ contract DebtLockerStorage { uint256 internal _minRatio; uint256 internal _principalRemainingAtLastClaim; + address internal _loanMigrator; } diff --git a/contracts/DebtLockerV4Migrator.sol b/contracts/DebtLockerV4Migrator.sol new file mode 100644 index 0000000..326fd00 --- /dev/null +++ b/contracts/DebtLockerV4Migrator.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.7; + +import { IDebtLockerV4Migrator } from "./interfaces/IDebtLockerV4Migrator.sol"; + +import { DebtLockerStorage } from "./DebtLockerStorage.sol"; + +/// @title DebtLockerV4Migrator is intended to initialize the storage of a DebtLocker proxy. +contract DebtLockerV4Migrator is IDebtLockerV4Migrator, DebtLockerStorage { + + function encodeArguments(address migrator_) external pure override returns (bytes memory encodedArguments_) { + return abi.encode(migrator_); + } + + function decodeArguments(bytes calldata encodedArguments_) public pure override returns (address migrator_) { + ( migrator_ ) = abi.decode(encodedArguments_, (address)); + } + + fallback() external { + + // Taking the migrator_ address as argument for now, but ideally this would be hardcoded in the debtLocker migrator registered in the factory + ( address migrator_ ) = decodeArguments(msg.data); + + _loanMigrator = migrator_; + } + +} diff --git a/contracts/interfaces/IDebtLocker.sol b/contracts/interfaces/IDebtLocker.sol index 88a0e74..3bf0ac0 100644 --- a/contracts/interfaces/IDebtLocker.sol +++ b/contracts/interfaces/IDebtLocker.sol @@ -124,6 +124,12 @@ interface IDebtLocker is IMapleProxied { */ function setFundsToCapture(uint256 amount_) external; + /** + * @dev Sets the pending lender on a Maple Loan. + * @param newLender_ The address of the new lender. + */ + function setPendingLender(address newLender_) external; + /** * @dev Called by the PoolDelegate in case of a DoS, where a user transfers small amounts of collateralAsset into the Liquidator * to make `_isLiquidationActive` remain true. @@ -140,6 +146,11 @@ interface IDebtLocker is IMapleProxied { */ function loan() external view returns (address loan_); + /** + * @dev The address of the entity allowed to migrate loan ownership. + */ + function loanMigrator() external view returns (address loan_); + /** * @dev The address of the liquidator. */ diff --git a/contracts/interfaces/IDebtLockerV4Migrator.sol b/contracts/interfaces/IDebtLockerV4Migrator.sol new file mode 100644 index 0000000..dad824b --- /dev/null +++ b/contracts/interfaces/IDebtLockerV4Migrator.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.7; + +/// @title DebtLockerInitializer is intended to initialize the storage of a DebtLocker proxy. +interface IDebtLockerV4Migrator { + + function encodeArguments(address migrator_) external pure returns (bytes memory encodedArguments_); + + function decodeArguments(bytes calldata encodedArguments_) external pure returns (address migrator_); + +} diff --git a/contracts/interfaces/Interfaces.sol b/contracts/interfaces/Interfaces.sol index ccebf11..a0518fe 100644 --- a/contracts/interfaces/Interfaces.sol +++ b/contracts/interfaces/Interfaces.sol @@ -58,6 +58,8 @@ interface IMapleLoanLike { function rejectNewTerms(address refinancer_, uint256 deadline_, bytes[] calldata calls_) external; + function setPendingLender(address pendingLender_) external; + } interface IPoolLike { diff --git a/contracts/test/accounts/Governor.sol b/contracts/test/accounts/Governor.sol deleted file mode 100644 index 4308975..0000000 --- a/contracts/test/accounts/Governor.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity 0.8.7; - -import { Governor as ProxyGovernor } from "../../../modules/maple-proxy-factory/contracts/test/accounts/Governor.sol"; - -contract Governor is ProxyGovernor {} diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..1866f94 --- /dev/null +++ b/foundry.toml @@ -0,0 +1,16 @@ +[profile.default] +contracts = 'contracts' # The source 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 = false # Enable or disable the solc optimizer +optimizer_runs = 200 # The number of optimizer runs +verbosity = 3 # The verbosity of tests +fuzz_runs = 100 # Number of fuzz runs +block_timestamp = 1622400000 # Timestamp for tests (non-zero) + +[profile.deep] +fuzz_runs = 1000 + +[profile.super_deep] +fuzz_runs = 50000 diff --git a/release.sh b/release.sh deleted file mode 100755 index 2b4ed4e..0000000 --- a/release.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -set -e - -version=$(cat ./package.yaml | grep "version: " | sed -r 's/.{9}//') -name=$(cat ./package.yaml | grep "name: " | sed -r 's/.{6}//') -customDescription=$(cat ./package.yaml | grep "customDescription: " | sed -r 's/.{19}//') - -./build.sh -c ./config/prod.json - -rm -rf ./package -mkdir -p package - -echo "{ - \"name\": \"@maplelabs/${name}\", - \"version\": \"${version}\", - \"description\": \"${customDescription}\", - \"author\": \"Maple Labs\", - \"license\": \"AGPLv3\", - \"repository\": { - \"type\": \"git\", - \"url\": \"https://github.com/maple-labs/${name}.git\" - }, - \"bugs\": { - \"url\": \"https://github.com/maple-labs/${name}/issues\" - }, - \"homepage\": \"https://github.com/maple-labs/${name}\" -}" > package/package.json - -mkdir -p package/artifacts -mkdir -p package/abis - -paths=($(cat ./package.yaml | grep " - path:" | sed -r 's/.{10}//')) -names=($(cat ./package.yaml | grep " contractName:" | sed -r 's/.{18}//')) - -for i in "${!paths[@]}"; do - cat ./out/dapp.sol.json | jq ".contracts | .\"${paths[i]}\" | .${names[i]}" > package/artifacts/${names[i]}.json - cat ./out/dapp.sol.json | jq ".contracts | .\"${paths[i]}\" | .${names[i]} | .abi" > package/abis/${names[i]}.json -done - -npm publish ./package --access public - -rm -rf ./package diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..4947336 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +while getopts p: flag +do + case "${flag}" in + p) profile=${OPTARG};; + esac +done + +export FOUNDRY_PROFILE=$profile +echo Using profile: $FOUNDRY_PROFILE + +forge build diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..4640904 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1 @@ +# TODO diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..05c58d2 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -e + +while getopts p:t: flag +do + case "${flag}" in + p) profile=${OPTARG};; + t) test=${OPTARG};; + esac +done + +export FOUNDRY_PROFILE=$profile +echo Using profile: $FOUNDRY_PROFILE + +if [ -z "$test" ]; +then + forge test --match-path "tests/*"; +else + forge test --match "$test"; +fi diff --git a/test.sh b/test.sh deleted file mode 100755 index 246fa39..0000000 --- a/test.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -set -e - -while getopts t:r:b:v:c: flag -do - case "${flag}" in - t) test=${OPTARG};; - r) runs=${OPTARG};; - b) build=${OPTARG};; - c) config=${OPTARG};; - esac -done - -runs=$([ -z "$runs" ] && echo "1" || echo "$runs") -build=$([ -z "$build" ] && echo "1" || echo "$build") -config=$([ -z "$config" ] && echo "./config/dev.json" || echo "$config") -skip_build=$([ "$build" == "0" ] && echo "1" || echo "0") - -export DAPP_SOLC_VERSION=0.8.7 -export DAPP_SRC="contracts" -export DAPP_LINK_TEST_LIBRARIES=0 -export DAPP_STANDARD_JSON=$config - -if [ "$skip_build" = "1" ]; then export DAPP_SKIP_BUILD=1; fi - -if [ -z "$test" ]; then match="[contracts/test/*.t.sol]"; dapp_test_verbosity=1; else match=$test; dapp_test_verbosity=2; fi - -echo LANG=C.UTF-8 dapp test --match "$match" --verbosity $dapp_test_verbosity --fuzz-runs $runs - -LANG=C.UTF-8 dapp test --match "$match" --verbosity $dapp_test_verbosity --fuzz-runs $runs diff --git a/contracts/test/DebtLocker.t.sol b/tests/DebtLocker.t.sol similarity index 87% rename from contracts/test/DebtLocker.t.sol rename to tests/DebtLocker.t.sol index 107777e..8fdb753 100644 --- a/contracts/test/DebtLocker.t.sol +++ b/tests/DebtLocker.t.sol @@ -1,21 +1,23 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; -import { TestUtils } from "../../modules/contract-test-utils/contracts/test.sol"; -import { MockERC20 } from "../../modules/erc20/contracts/test/mocks/MockERC20.sol"; -import { MapleLoan } from "../../modules/loan/contracts/MapleLoan.sol"; -import { MapleLoanFactory } from "../../modules/loan/contracts/MapleLoanFactory.sol"; -import { MapleLoanInitializer } from "../../modules/loan/contracts/MapleLoanInitializer.sol"; -import { Refinancer } from "../../modules/loan/contracts/Refinancer.sol"; +import { TestUtils } from "../modules/contract-test-utils/contracts/test.sol"; +import { MockERC20 } from "../modules/erc20/contracts/test/mocks/MockERC20.sol"; +import { MapleLoan } from "../modules/loan/contracts/MapleLoan.sol"; +import { MapleLoanFactory } from "../modules/loan/contracts/MapleLoanFactory.sol"; +import { MapleLoanInitializer } from "../modules/loan/contracts/MapleLoanInitializer.sol"; +import { Refinancer } from "../modules/loan/contracts/Refinancer.sol"; -import { ILiquidatorLike } from "../interfaces/Interfaces.sol"; +import { ILiquidatorLike } from "../contracts/interfaces/Interfaces.sol"; -import { DebtLocker } from "../DebtLocker.sol"; -import { DebtLockerFactory } from "../DebtLockerFactory.sol"; -import { DebtLockerInitializer } from "../DebtLockerInitializer.sol"; +import { DebtLocker } from "../contracts/DebtLocker.sol"; +import { DebtLockerFactory } from "../contracts/DebtLockerFactory.sol"; +import { DebtLockerInitializer } from "../contracts/DebtLockerInitializer.sol"; +import { DebtLockerV4Migrator } from "../contracts/DebtLockerV4Migrator.sol"; import { Governor } from "./accounts/Governor.sol"; import { PoolDelegate } from "./accounts/PoolDelegate.sol"; +import { LoanMigrator } from "./accounts/LoanMigrator.sol"; import { DebtLockerHarness } from "./mocks/DebtLockerHarness.sol"; import { ManipulatableDebtLocker } from "./mocks/ManipulatableDebtLocker.sol"; @@ -1041,3 +1043,132 @@ contract DebtLockerTests is TestUtils { } } + +contract DebtLockerV4Migration is TestUtils { + + DebtLockerFactory internal dlFactory; + Governor internal governor; + MapleLoanFactory internal loanFactory; + MapleLoanInitializer internal loanInitializer; + MockERC20 internal collateralAsset; + MockERC20 internal fundsAsset; + MockGlobals internal globals; + MockPool internal pool; + MockPoolFactory internal poolFactory; + PoolDelegate internal notPoolDelegate; + PoolDelegate internal poolDelegate; + + Hevm internal hevm = Hevm(address(bytes20(uint160(uint256(keccak256("hevm cheat code")))))); + + function setUp() external { + governor = new Governor(); + notPoolDelegate = new PoolDelegate(); + poolDelegate = new PoolDelegate(); + + globals = new MockGlobals(address(governor)); + poolFactory = new MockPoolFactory(address(globals)); + dlFactory = new DebtLockerFactory(address(globals)); + loanFactory = new MapleLoanFactory(address(globals)); + + pool = MockPool(poolFactory.createPool(address(poolDelegate))); + + collateralAsset = new MockERC20("Collateral Asset", "CA", 18); + fundsAsset = new MockERC20("Funds Asset", "FA", 18); + + globals.setValidCollateralAsset(address(collateralAsset), true); + globals.setValidLiquidityAsset(address(fundsAsset), true); + + // Deploying and registering DebtLocker implementation and initializer + address debtLockerImplementation = address(new DebtLocker()); + address debtLockerImplementation2 = address(new DebtLocker()); + address debtLockerInitializer = address(new DebtLockerInitializer()); + address debtLockerV4Migrator = address(new DebtLockerV4Migrator()); + + governor.mapleProxyFactory_registerImplementation(address(dlFactory), 1, debtLockerImplementation, debtLockerInitializer); + governor.mapleProxyFactory_setDefaultVersion(address(dlFactory), 1); + + governor.mapleProxyFactory_registerImplementation(address(dlFactory), 2, debtLockerImplementation2, debtLockerInitializer); + governor.mapleProxyFactory_enableUpgradePath(address(dlFactory), 1, 2, debtLockerV4Migrator); + + // Deploying and registering DebtLocker implementation and initializer + address loanImplementation = address(new MapleLoan()); + loanInitializer = new MapleLoanInitializer(); + + governor.mapleProxyFactory_registerImplementation(address(loanFactory), 1, loanImplementation, address(loanInitializer)); + governor.mapleProxyFactory_setDefaultVersion(address(loanFactory), 1); + + globals.setPrice(address(collateralAsset), 10 * 10 ** 8); // 10 USD + globals.setPrice(address(fundsAsset), 1 * 10 ** 8); // 1 USD + } + + function test_acl_upgradeToV4() external { + MapleLoan loan_ = _createLoan(1e18, 1e18); + + DebtLocker debtLocker_ = DebtLocker(pool.createDebtLocker(address(dlFactory), address(loan_))); + + address loanMigrator = address(5); + + assertTrue(!notPoolDelegate.try_debtLocker_upgrade(address(debtLocker_), 2, abi.encode(loanMigrator))); + assertTrue( poolDelegate.try_debtLocker_upgrade(address(debtLocker_), 2, abi.encode(loanMigrator))); + + assertEq(debtLocker_.loanMigrator(), loanMigrator); + } + + + function test_acl_setPendingLender() external { + ( MapleLoan loan, DebtLocker debtLocker ) = _createFundAndDrawdownLoan(1_000_000, 30_000); + + LoanMigrator loanMigrator = new LoanMigrator(); + LoanMigrator notLoanMigrator = new LoanMigrator(); + + address newLender = address(3); + + poolDelegate.debtLocker_upgrade(address(debtLocker), 2, abi.encode(address(loanMigrator))); + + assertEq(loan.pendingLender(), address(0)); + + assertTrue(!notLoanMigrator.try_debtLocker_setPendingLender(address(debtLocker), newLender)); + assertTrue( loanMigrator.try_debtLocker_setPendingLender(address(debtLocker), newLender)); + + assertEq(loan.pendingLender(), newLender); + } + + function _createLoan(uint256 principalRequested_, uint256 collateralRequired_) internal returns (MapleLoan loan_) { + address[2] memory assets = [address(collateralAsset), address(fundsAsset)]; + uint256[3] memory termDetails = [uint256(10 days), uint256(30 days), 6]; + uint256[3] memory amounts = [collateralRequired_, principalRequested_, 0]; + uint256[4] memory rates = [uint256(0.10e18), uint256(0), uint256(0), uint256(0)]; + + bytes memory arguments = loanInitializer.encodeArguments(address(this), assets, termDetails, amounts, rates); + bytes32 salt = keccak256(abi.encodePacked("salt")); + + // Create Loan + loan_ = MapleLoan(loanFactory.createInstance(arguments, salt)); + } + + function _fundAndDrawdownLoan(address loan_, address debtLocker_) internal { + MapleLoan loan = MapleLoan(loan_); + + uint256 principalRequested = loan.principalRequested(); + uint256 collateralRequired = loan.collateralRequired(); + + fundsAsset.mint(address(this), principalRequested); + fundsAsset.approve(loan_, principalRequested); + + loan.fundLoan(debtLocker_, principalRequested); + + collateralAsset.mint(address(this), collateralRequired); + collateralAsset.approve(loan_, collateralRequired); + + loan.drawdownFunds(loan.drawableFunds(), address(1)); // Drawdown to empty funds from loan + } + + function _createFundAndDrawdownLoan(uint256 principalRequested_, uint256 collateralRequired_) internal returns (MapleLoan loan_, DebtLocker debtLocker_) { + loan_ = _createLoan(principalRequested_, collateralRequired_); + + debtLocker_ = DebtLocker(pool.createDebtLocker(address(dlFactory), address(loan_))); + + _fundAndDrawdownLoan(address(loan_), address(debtLocker_)); + } + +} diff --git a/tests/accounts/Governor.sol b/tests/accounts/Governor.sol new file mode 100644 index 0000000..9afd8c5 --- /dev/null +++ b/tests/accounts/Governor.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.7; + +import { Governor as ProxyGovernor } from "../../modules/maple-proxy-factory/contracts/test/accounts/Governor.sol"; + +contract Governor is ProxyGovernor {} diff --git a/tests/accounts/LoanMigrator.sol b/tests/accounts/LoanMigrator.sol new file mode 100644 index 0000000..2a6f267 --- /dev/null +++ b/tests/accounts/LoanMigrator.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.7; + +import { IDebtLocker } from "../../contracts/interfaces/IDebtLocker.sol"; + +contract LoanMigrator { + + function debtLocker_setPendingLender(address debtLocker_, address newLender_) external { + IDebtLocker(debtLocker_).setPendingLender(newLender_); + } + + function try_debtLocker_setPendingLender(address debtLocker_, address newLender_) external returns (bool ok_) { + ( ok_, ) = debtLocker_.call(abi.encodeWithSelector(IDebtLocker.setPendingLender.selector, newLender_)); + } + +} diff --git a/contracts/test/accounts/PoolDelegate.sol b/tests/accounts/PoolDelegate.sol similarity index 95% rename from contracts/test/accounts/PoolDelegate.sol rename to tests/accounts/PoolDelegate.sol index 7a681f7..45b27ca 100644 --- a/contracts/test/accounts/PoolDelegate.sol +++ b/tests/accounts/PoolDelegate.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; -import { User as ProxyUser } from "../../../modules/maple-proxy-factory/contracts/test/accounts/User.sol"; +import { User as ProxyUser } from "../../modules/maple-proxy-factory/contracts/test/accounts/User.sol"; -import { IDebtLocker, IMapleProxied } from "../../interfaces/IDebtLocker.sol"; +import { IDebtLocker, IMapleProxied } from "../../contracts/interfaces/IDebtLocker.sol"; contract PoolDelegate is ProxyUser { diff --git a/contracts/test/mocks/DebtLockerHarness.sol b/tests/mocks/DebtLockerHarness.sol similarity index 89% rename from contracts/test/mocks/DebtLockerHarness.sol rename to tests/mocks/DebtLockerHarness.sol index 6d71a20..cb9ada3 100644 --- a/contracts/test/mocks/DebtLockerHarness.sol +++ b/tests/mocks/DebtLockerHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; -import { DebtLocker } from "../../DebtLocker.sol"; +import { DebtLocker } from "../../contracts/DebtLocker.sol"; contract DebtLockerHarness is DebtLocker { diff --git a/contracts/test/mocks/ManipulatableDebtLocker.sol b/tests/mocks/ManipulatableDebtLocker.sol similarity index 85% rename from contracts/test/mocks/ManipulatableDebtLocker.sol rename to tests/mocks/ManipulatableDebtLocker.sol index 08833d8..c24abc0 100644 --- a/contracts/test/mocks/ManipulatableDebtLocker.sol +++ b/tests/mocks/ManipulatableDebtLocker.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; -import { IMapleLoanLike } from "../../interfaces/Interfaces.sol"; +import { IMapleLoanLike } from "../../contracts/interfaces/Interfaces.sol"; -import { DebtLocker } from "../../DebtLocker.sol"; +import { DebtLocker } from "../../contracts/DebtLocker.sol"; contract ManipulatableDebtLocker is DebtLocker { diff --git a/contracts/test/mocks/Mocks.sol b/tests/mocks/Mocks.sol similarity index 88% rename from contracts/test/mocks/Mocks.sol rename to tests/mocks/Mocks.sol index bed20f5..39187b5 100644 --- a/contracts/test/mocks/Mocks.sol +++ b/tests/mocks/Mocks.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.8.7; -import { ILiquidatorLike } from "../../../modules/liquidations/contracts/interfaces/Interfaces.sol"; +import { ILiquidatorLike } from "../../modules/liquidations/contracts/interfaces/Interfaces.sol"; -import { ERC20Helper } from "../../../modules/erc20-helper/src/ERC20Helper.sol"; -import { MockERC20 } from "../../../modules/erc20/contracts/test/mocks/MockERC20.sol"; +import { ERC20Helper } from "../../modules/erc20-helper/src/ERC20Helper.sol"; +import { MockERC20 } from "../../modules/erc20/contracts/test/mocks/MockERC20.sol"; -import { IDebtLocker } from "../../interfaces/IDebtLocker.sol"; -import { IDebtLockerFactory } from "../../interfaces/IDebtLockerFactory.sol"; +import { IDebtLocker } from "../../contracts/interfaces/IDebtLocker.sol"; +import { IDebtLockerFactory } from "../../contracts/interfaces/IDebtLockerFactory.sol"; contract MockPoolFactory {