Skip to content

Commit

Permalink
Merge pull request #469 from morpho-dao/test/setup-2
Browse files Browse the repository at this point in the history
Improve test setup again again
  • Loading branch information
Rubilmax authored Feb 8, 2023
2 parents 48e7b0b + dee8a8a commit 9330625
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 130 deletions.
10 changes: 2 additions & 8 deletions .github/actions/forge-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ runs:
with:
version: nightly

- name: Foundry fork cache
uses: actions/cache@v3
with:
path: ~/.foundry/cache
key: foundry-${{ hashFiles(format('config/{0}.json', inputs.network)) }} # where fork block numbers & RPC are stored

- name: Foundry compilation cache
uses: actions/cache@v3
with:
Expand All @@ -49,7 +43,7 @@ runs:
shell: bash
env:
NETWORK: ${{ inputs.network }}
FOUNDRY_FUZZ_RUNS: 32768
FOUNDRY_FUZZ_RUNS: 16384

- name: Run internal tests
run: make test-internal
Expand All @@ -63,7 +57,7 @@ runs:
shell: bash
env:
NETWORK: ${{ inputs.network }}
FOUNDRY_FUZZ_RUNS: 128
FOUNDRY_FUZZ_RUNS: 64

- name: Generate lcov coverage report
if: ${{ inputs.codecovToken != '' }}
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ install:
forge install

contracts:
FOUNDRY_TEST=/dev/null forge build --via-ir --sizes --force
FOUNDRY_TEST=/dev/null forge build --via-ir --extra-output-files irOptimized --sizes --force


test:
Expand Down
6 changes: 0 additions & 6 deletions config/avalanche-mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
"rpc": "https://rpc.ankr.com/avalanche",
"chainId": 43114,
"testBlock": 24800000,
"testMarkets": [
"DAI",
"USDC",
"WBTC",
"WETH"
],
"addressesProvider": "0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb",
"DAI": "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70",
"FRAX": "0xD24C2Ad096400B6FBcd2ad8B24E7acBc21A1da64",
Expand Down
1 change: 1 addition & 0 deletions test/helpers/BaseTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.0;
import {Types} from "src/libraries/Types.sol";
import {Events} from "src/libraries/Events.sol";
import {Errors} from "src/libraries/Errors.sol";
import {Constants} from "src/libraries/Constants.sol";
import {SafeTransferLib, ERC20} from "@solmate/utils/SafeTransferLib.sol";

import {Math} from "@morpho-utils/math/Math.sol";
Expand Down
79 changes: 48 additions & 31 deletions test/helpers/ForkTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import {IPool, IPoolAddressesProvider} from "@aave-v3-core/interfaces/IPool.sol"
import {IVariableDebtToken} from "@aave-v3-core/interfaces/IVariableDebtToken.sol";
import {DataTypes} from "@aave-v3-core/protocol/libraries/types/DataTypes.sol";

import {Types} from "src/libraries/Types.sol";
import {Events} from "src/libraries/Events.sol";
import {Errors} from "src/libraries/Errors.sol";
import {TestConfig, TestConfigLib} from "test/helpers/TestConfigLib.sol";
import {DataTypes} from "@aave-v3-core/protocol/libraries/types/DataTypes.sol";
import {Errors as AaveErrors} from "@aave-v3-core/protocol/libraries/helpers/Errors.sol";
import {ReserveConfiguration} from "@aave-v3-core/protocol/libraries/configuration/ReserveConfiguration.sol";

import {PriceOracleSentinelMock} from "test/mocks/PriceOracleSentinelMock.sol";
import {AaveOracleMock} from "test/mocks/AaveOracleMock.sol";
import {PoolAdminMock} from "test/mocks/PoolAdminMock.sol";
import "./BaseTest.sol";
Expand Down Expand Up @@ -53,15 +53,17 @@ contract ForkTest is BaseTest {
address internal aclAdmin;
AaveOracleMock internal oracle;
PoolAdminMock internal poolAdmin;
PriceOracleSentinelMock oracleSentinel;

uint256 snapshotId = type(uint256).max;

constructor() {
_initConfig();
_loadConfig();

_mockOracle();
_mockPoolAdmin();
_mockOracle();
_mockOracleSentinel();

_setBalances(address(this), type(uint256).max);
}
Expand All @@ -87,29 +89,40 @@ contract ForkTest is BaseTest {
function _loadConfig() internal {
forkId = config.createFork();

addressesProvider = IPoolAddressesProvider(config.getAddress("addressesProvider"));
addressesProvider = IPoolAddressesProvider(config.getAddressesProvider());
pool = IPool(addressesProvider.getPool());

aclAdmin = addressesProvider.getACLAdmin();
aclManager = IACLManager(addressesProvider.getACLManager());
poolConfigurator = IPoolConfigurator(addressesProvider.getPoolConfigurator());
poolDataProvider = IPoolDataProvider(addressesProvider.getPoolDataProvider());

dai = config.getAddress("DAI");
frax = config.getAddress("FRAX");
mai = config.getAddress("MAI");
usdc = config.getAddress("USDC");
usdt = config.getAddress("USDT");
aave = config.getAddress("AAVE");
btcb = config.getAddress("BTCb");
link = config.getAddress("LINK");
sAvax = config.getAddress("sAVAX");
wavax = config.getAddress("WAVAX");
wbtc = config.getAddress("WBTC");
weth = config.getAddress("WETH");
wNative = config.getAddress("wrappedNative");

allUnderlyings = config.getTestMarkets();
dai = config.getAddress("$.DAI");
frax = config.getAddress("$.FRAX");
mai = config.getAddress("$.MAI");
usdc = config.getAddress("$.USDC");
usdt = config.getAddress("$.USDT");
aave = config.getAddress("$.AAVE");
btcb = config.getAddress("$.BTCb");
link = config.getAddress("$.LINK");
sAvax = config.getAddress("$.sAVAX");
wavax = config.getAddress("$.WAVAX");
wbtc = config.getAddress("$.WBTC");
weth = config.getAddress("$.WETH");
wNative = config.getAddress("$.wrappedNative");

allUnderlyings.push(dai);
allUnderlyings.push(frax);
allUnderlyings.push(mai);
allUnderlyings.push(usdc);
allUnderlyings.push(usdt);
allUnderlyings.push(aave);
allUnderlyings.push(btcb);
allUnderlyings.push(link);
allUnderlyings.push(sAvax);
allUnderlyings.push(wavax);
allUnderlyings.push(wbtc);
allUnderlyings.push(weth);
}

function _label() internal virtual {
Expand Down Expand Up @@ -137,26 +150,30 @@ contract ForkTest is BaseTest {
vm.label(wNative, "wNative");
}

function _mockOracle() internal {
oracle = new AaveOracleMock(IAaveOracle(addressesProvider.getPriceOracle()), pool.getReservesList());

vm.store(
address(addressesProvider),
keccak256(abi.encode(bytes32("PRICE_ORACLE"), 2)),
bytes32(uint256(uint160(address(oracle))))
);
}

function _mockPoolAdmin() internal {
poolAdmin = new PoolAdminMock(poolConfigurator);

vm.startPrank(aclAdmin);
aclManager.addPoolAdmin(address(poolAdmin));
aclManager.addEmergencyAdmin(address(poolAdmin));
aclManager.addRiskAdmin(address(poolAdmin));
aclManager.addEmergencyAdmin(address(poolAdmin));
vm.stopPrank();
}

function _mockOracle() internal {
oracle = new AaveOracleMock(IAaveOracle(addressesProvider.getPriceOracle()), pool.getReservesList());

vm.prank(aclAdmin);
addressesProvider.setPriceOracle(address(oracle));
}

function _mockOracleSentinel() internal {
oracleSentinel = new PriceOracleSentinelMock(address(addressesProvider));

vm.prank(aclAdmin);
addressesProvider.setPriceOracleSentinel(address(oracleSentinel));
}

function _setBalances(address user, uint256 balance) internal {
deal(dai, user, balance);
deal(frax, user, balance);
Expand Down
57 changes: 41 additions & 16 deletions test/helpers/IntegrationTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ contract IntegrationTest is ForkTest {
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
using TestMarketLib for TestMarket;

uint8 internal constant E_MODE_CATEGORY_ID = 0;
uint256 internal constant INITIAL_BALANCE = 10_000_000_000 ether;
uint256 internal constant MIN_USD_AMOUNT = 0.0001e8; // AaveV3 base currency is USD, 8 decimals on all L2s.
uint256 internal constant MAX_USD_AMOUNT = 500_000_000e8; // AaveV3 base currency is USD, 8 decimals on all L2s.

// AaveV3 base currency is USD, 8 decimals on all L2s.
uint256 internal constant MIN_USD_AMOUNT = 0.01e8; // 0.01$
uint256 internal constant MAX_USD_AMOUNT = 500_000_000e8; // 500m$

IMorpho internal morpho;
IPositionsManager internal positionsManager;
Expand All @@ -42,6 +45,7 @@ contract IntegrationTest is ForkTest {
mapping(address => TestMarket) internal testMarkets;

address[] internal underlyings;
address[] internal collateralUnderlyings;
address[] internal borrowableUnderlyings;

function setUp() public virtual override {
Expand Down Expand Up @@ -80,8 +84,8 @@ contract IntegrationTest is ForkTest {
}

function _deploy() internal {
positionsManager = new PositionsManager(address(addressesProvider), 0);
morphoImpl = new Morpho(address(addressesProvider), 0);
positionsManager = new PositionsManager(address(addressesProvider), E_MODE_CATEGORY_ID);
morphoImpl = new Morpho(address(addressesProvider), E_MODE_CATEGORY_ID);

proxyAdmin = new ProxyAdmin();
morphoProxy = new TransparentUpgradeableProxy(payable(address(morphoImpl)), address(proxyAdmin), "");
Expand Down Expand Up @@ -134,19 +138,22 @@ contract IntegrationTest is ForkTest {
market.supplyCap = type(uint256).max;
market.borrowCap = type(uint256).max;

market.isBorrowable = reserve.configuration.getBorrowingEnabled() && !reserve.configuration.getSiloedBorrowing()
&& !reserve.configuration.getBorrowableInIsolation()
&& (E_MODE_CATEGORY_ID == 0 || E_MODE_CATEGORY_ID == reserve.configuration.getEModeCategory());

vm.label(reserve.aTokenAddress, string.concat("a", market.symbol));
vm.label(reserve.variableDebtTokenAddress, string.concat("d", market.symbol));
vm.label(reserve.variableDebtTokenAddress, string.concat("vd", market.symbol));
vm.label(reserve.stableDebtTokenAddress, string.concat("sd", market.symbol));
}

function _createMarket(address underlying, uint16 reserveFactor, uint16 p2pIndexCursor) internal {
(TestMarket storage market, DataTypes.ReserveData memory reserve) =
_initMarket(underlying, reserveFactor, p2pIndexCursor);

underlyings.push(underlying);
if (
market.ltv > 0 && reserve.configuration.getBorrowingEnabled() && !reserve.configuration.getSiloedBorrowing()
&& !reserve.configuration.getBorrowableInIsolation() && market.borrowGap() > 0
) borrowableUnderlyings.push(underlying);
if (market.ltv > 0) collateralUnderlyings.push(underlying);
if (market.isBorrowable) borrowableUnderlyings.push(underlying);

morpho.createMarket(market.underlying, market.reserveFactor, market.p2pIndexCursor);
}
Expand Down Expand Up @@ -179,7 +186,25 @@ contract IntegrationTest is ForkTest {
return bound(amount, market.minAmount, Math.min(market.maxAmount, market.supplyGap()));
}

/// @dev Bounds the input between 0 and the maximum borrowable quantity, without exceeding the market's liquidity nor its borrow cap.
/// @dev Bounds the input so that the amount returned can collateralize a debt between
/// the minimum & the maximum USD amount expected in tests, without exceeding the market's supply cap.
function _boundCollateral(TestMarket storage collateralMarket, uint256 amount, TestMarket storage borrowedMarket)
internal
view
returns (uint256)
{
return bound(
amount,
collateralMarket.minBorrowCollateral(borrowedMarket, borrowedMarket.minAmount),
Math.min(
collateralMarket.minBorrowCollateral(borrowedMarket, borrowedMarket.maxAmount),
collateralMarket.supplyGap()
)
);
}

/// @dev Bounds the input between the minimum USD amount expected in tests
/// and the maximum borrowable quantity, without exceeding the market's liquidity nor its borrow cap.
function _boundBorrow(TestMarket storage market, uint256 amount) internal view returns (uint256) {
return bound(
amount, market.minAmount, Math.min(market.maxAmount, Math.min(market.liquidity(), market.borrowGap()))
Expand All @@ -200,7 +225,7 @@ contract IntegrationTest is ForkTest {
vm.prank(borrower);
borrowed = morpho.borrow(market.underlying, amount, onBehalf, receiver, maxIterations);

_deposit(dai, testMarkets[dai].minCollateral(market, borrowed), address(morpho)); // Make Morpho solvent with default collateral market, having no supply cap.
_deposit(dai, testMarkets[dai].minBorrowCollateral(market, borrowed), address(morpho)); // Make Morpho able to borrow again with dai collateral.

oracle.setAssetPrice(market.underlying, market.price);
}
Expand All @@ -216,7 +241,7 @@ contract IntegrationTest is ForkTest {

try promoter.borrow(market.underlying, borrowed) {
market.resetPreviousIndex(address(morpho)); // Enable borrow/repay in same block.
_deposit(dai, testMarkets[dai].minCollateral(market, borrowed), address(morpho)); // Make Morpho solvent with default collateral market, having no supply cap.
_deposit(dai, testMarkets[dai].minBorrowCollateral(market, borrowed), address(morpho)); // Make Morpho able to borrow again with dai collateral.
} catch {
borrowed = 0;
}
Expand Down Expand Up @@ -250,8 +275,8 @@ contract IntegrationTest is ForkTest {
// Set the supply cap as exceeded.
_setSupplyCap(market, market.totalSupply());

user.approve(market.underlying, amount);
user.repay(market.underlying, amount, onBehalf);
hacker.approve(market.underlying, amount);
hacker.repay(market.underlying, amount, onBehalf);

return amount;
}
Expand All @@ -271,8 +296,8 @@ contract IntegrationTest is ForkTest {
// Set the max iterations to 0 upon repay to skip demotion and fallback to supply delta.
morpho.setDefaultMaxIterations(Types.MaxIterations({repay: 0, withdraw: 10}));

user.approve(market.underlying, amount);
user.repay(market.underlying, amount, onBehalf);
hacker.approve(market.underlying, amount);
hacker.repay(market.underlying, amount, onBehalf);

return amount;
}
Expand Down
2 changes: 1 addition & 1 deletion test/helpers/InternalTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ import "./ForkTest.sol";
contract InternalTest is ForkTest, MorphoStorage {
using TestConfigLib for TestConfig;

constructor() MorphoStorage(_initConfig().getAddress("addressesProvider"), 0) {}
constructor() MorphoStorage(_initConfig().getAddressesProvider(), 0) {}
}
29 changes: 14 additions & 15 deletions test/helpers/TestConfigLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ library TestConfigLib {

Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));

string public constant RPC_PATH = "$.rpc";
string public constant CHAIN_ID_PATH = "$.chainId";
string public constant TEST_BLOCK_PATH = "$.testBlock";
string public constant USES_RPC_PREFIX_PATH = "$.usesRpcPrefix";
string public constant ADDRESSES_PROVIDER_PATH = "$.addressesProvider";

function load(TestConfig storage config, string memory network) internal returns (TestConfig storage) {
string memory root = vm.projectRoot();
string memory path = string(abi.encodePacked(root, "/config/", network, ".json"));
string memory path = string.concat(root, "/config/", network, ".json");

config.json = vm.readFile(path);

Expand All @@ -26,24 +32,17 @@ library TestConfigLib {
return config.json.readAddress(string(abi.encodePacked(key)));
}

function getTestMarkets(TestConfig storage config) internal view returns (address[] memory) {
string[] memory marketNames = config.json.readStringArray(string(abi.encodePacked("testMarkets")));
address[] memory markets = new address[](marketNames.length);

for (uint256 i; i < markets.length; i++) {
markets[i] = getAddress(config, marketNames[i]);
}

return markets;
function getAddressesProvider(TestConfig storage config) internal view returns (address) {
return getAddress(config, ADDRESSES_PROVIDER_PATH);
}

function createFork(TestConfig storage config) internal returns (uint256 forkId) {
bool rpcPrefixed = stdJson.readBool(config.json, string(abi.encodePacked("usesRpcPrefix")));
bool rpcPrefixed = stdJson.readBool(config.json, USES_RPC_PREFIX_PATH);
string memory endpoint = rpcPrefixed
? string(abi.encodePacked(config.json.readString(string(abi.encodePacked("rpc"))), vm.envString("ALCHEMY_KEY")))
: config.json.readString(string(abi.encodePacked("rpc")));
? string.concat(config.json.readString(RPC_PATH), vm.envString("ALCHEMY_KEY"))
: config.json.readString(RPC_PATH);

forkId = vm.createSelectFork(endpoint, config.json.readUint(string(abi.encodePacked("testBlock"))));
vm.chainId(config.json.readUint(string(abi.encodePacked("chainId"))));
forkId = vm.createSelectFork(endpoint, config.json.readUint(TEST_BLOCK_PATH));
vm.chainId(config.json.readUint(CHAIN_ID_PATH));
}
}
Loading

0 comments on commit 9330625

Please sign in to comment.