Skip to content

Commit

Permalink
Merge pull request #1164 from morpho-labs/feat/extract-compound-tests
Browse files Browse the repository at this point in the history
Add Compound live tests
  • Loading branch information
Rubilmax authored Aug 24, 2022
2 parents adbe439 + 0d102c5 commit a223210
Show file tree
Hide file tree
Showing 14 changed files with 1,286 additions and 131 deletions.
76 changes: 42 additions & 34 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,49 @@ NETWORK?=eth-mainnet
FOUNDRY_SRC=contracts/${PROTOCOL}/
FOUNDRY_TEST=test-foundry/${PROTOCOL}/
FOUNDRY_REMAPPINGS=@config/=config/${NETWORK}/${PROTOCOL}/
FOUNDRY_ETH_RPC_URL?=https://${NETWORK}.g.alchemy.com/v2/${ALCHEMY_KEY}

FOUNDRY_PRIVATE_KEY?=${DEPLOYER_PRIVATE_KEY}

ifeq (${NETWORK}, eth-mainnet)
FOUNDRY_CHAIN_ID=1
FOUNDRY_FORK_BLOCK_NUMBER=14292587
endif
ifdef FOUNDRY_ETH_RPC_URL
FOUNDRY_TEST=test-foundry/prod/${PROTOCOL}/
FOUNDRY_FUZZ_RUNS=4096
FOUNDRY_FUZZ_MAX_LOCAL_REJECTS=16384
FOUNDRY_FUZZ_MAX_GLOBAL_REJECTS=1048576
else
FOUNDRY_ETH_RPC_URL=https://${NETWORK}.g.alchemy.com/v2/${ALCHEMY_KEY}

ifeq (${NETWORK}, eth-ropsten)
FOUNDRY_CHAIN_ID=3
endif
ifeq (${NETWORK}, eth-mainnet)
FOUNDRY_CHAIN_ID=1
FOUNDRY_FORK_BLOCK_NUMBER?=14292587
endif

ifeq (${NETWORK}, eth-goerli)
FOUNDRY_CHAIN_ID=5
endif
ifeq (${NETWORK}, eth-ropsten)
FOUNDRY_CHAIN_ID=3
endif

ifeq (${NETWORK}, polygon-mainnet)
FOUNDRY_CHAIN_ID=137
FOUNDRY_FORK_BLOCK_NUMBER=22116728
ifeq (${NETWORK}, eth-goerli)
FOUNDRY_CHAIN_ID=5
endif

ifeq (${PROTOCOL}, aave-v3)
FOUNDRY_FORK_BLOCK_NUMBER=29116728
FOUNDRY_CONTRACT_PATTERN_INVERSE=(Fees|IncentivesVault|Rewards)
ifeq (${NETWORK}, polygon-mainnet)
ifeq (${PROTOCOL}, aave-v3)
FOUNDRY_FORK_BLOCK_NUMBER?=29116728
FOUNDRY_CONTRACT_PATTERN_INVERSE=(Fees|IncentivesVault|Rewards)
endif

FOUNDRY_CHAIN_ID=137
FOUNDRY_FORK_BLOCK_NUMBER?=22116728
endif
endif

ifeq (${NETWORK}, avalanche-mainnet)
FOUNDRY_CHAIN_ID=43114
FOUNDRY_ETH_RPC_URL=https://api.avax.network/ext/bc/C/rpc
FOUNDRY_FORK_BLOCK_NUMBER=12675271
ifeq (${NETWORK}, avalanche-mainnet)
ifeq (${PROTOCOL}, aave-v3)
FOUNDRY_FORK_BLOCK_NUMBER?=15675271
endif

ifeq (${PROTOCOL}, aave-v3)
FOUNDRY_FORK_BLOCK_NUMBER=15675271
FOUNDRY_CHAIN_ID=43114
FOUNDRY_ETH_RPC_URL=https://api.avax.network/ext/bc/C/rpc
FOUNDRY_FORK_BLOCK_NUMBER?=12675271
endif
else
endif

ifeq (${SMODE}, local)
Expand Down Expand Up @@ -70,44 +78,44 @@ create-market:
./scripts/${PROTOCOL}/create-market.sh

anvil:
@echo Starting fork of ${NETWORK}
@anvil --fork-url ${FOUNDRY_ETH_RPC_URL}
@echo Starting fork of ${NETWORK} at block ${FOUNDRY_FORK_BLOCK_NUMBER}
@anvil --fork-url ${FOUNDRY_ETH_RPC_URL} --fork-block-number ${FOUNDRY_FORK_BLOCK_NUMBER}

script-%:
@echo Running script $* of ${PROTOCOL} on ${NETWORK} with script mode: ${SMODE}
@forge script scripts/${PROTOCOL}/$*.s.sol:$* --broadcast -vvvv

test:
@echo Running all ${PROTOCOL} tests on ${NETWORK}
@echo Running all ${PROTOCOL} tests on ${NETWORK} at block ${FOUNDRY_FORK_BLOCK_NUMBER}
@forge test -vv | tee trace.ansi

coverage:
@echo Create coverage report for ${PROTOCOL} tests on ${NETWORK}
@echo Create coverage report for ${PROTOCOL} tests on ${NETWORK} at block ${FOUNDRY_FORK_BLOCK_NUMBER}
@forge coverage

coverage-lcov:
@echo Create coverage lcov for ${PROTOCOL} tests on ${NETWORK}
@echo Create coverage lcov for ${PROTOCOL} tests on ${NETWORK} at block ${FOUNDRY_FORK_BLOCK_NUMBER}
@forge coverage --report lcov

fuzz:
$(eval FOUNDRY_TEST=test-foundry/fuzzing/${PROTOCOL}/)
@echo Running all ${PROTOCOL} fuzzing tests on ${NETWORK}
@echo Running all ${PROTOCOL} fuzzing tests on ${NETWORK} at block ${FOUNDRY_FORK_BLOCK_NUMBER}
@forge test -vv

gas-report:
@echo Creating gas report for ${PROTOCOL} on ${NETWORK}
@echo Creating gas report for ${PROTOCOL} on ${NETWORK} at block ${FOUNDRY_FORK_BLOCK_NUMBER}
@forge test --gas-report

test-common:
@echo Running all common tests on ${NETWORK}
@FOUNDRY_TEST=test-foundry/common forge test -vvv

contract-% c-%:
@echo Running tests for contract $* of ${PROTOCOL} on ${NETWORK}
@echo Running tests for contract $* of ${PROTOCOL} on ${NETWORK} at block ${FOUNDRY_FORK_BLOCK_NUMBER}
@forge test -vvv --match-contract $* | tee trace.ansi

single-% s-%:
@echo Running single test $* of ${PROTOCOL} on ${NETWORK}
@echo Running single test $* of ${PROTOCOL} on ${NETWORK} at block ${FOUNDRY_FORK_BLOCK_NUMBER}
@forge test -vvv --match-test $* | tee trace.ansi

storage-layout-generate:
Expand Down
40 changes: 38 additions & 2 deletions config/eth-mainnet/compound/Config.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity 0.8.13;

import "@contracts/compound/interfaces/compound/ICompound.sol";
import {IIncentivesVault} from "@contracts/compound/interfaces/IIncentivesVault.sol";
import {IPositionsManager} from "@contracts/compound/interfaces/IPositionsManager.sol";
import {IInterestRatesManager} from "@contracts/compound/interfaces/IInterestRatesManager.sol";

import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import {ERC20} from "@rari-capital/solmate/src/tokens/ERC20.sol";

import {RewardsManager} from "@contracts/compound/RewardsManager.sol";
import {PositionsManager} from "@contracts/compound/PositionsManager.sol";
import {InterestRatesManager} from "@contracts/compound/InterestRatesManager.sol";
import {IncentivesVault} from "@contracts/compound/IncentivesVault.sol";
import {Lens} from "@contracts/compound/lens/Lens.sol";
import {Morpho} from "@contracts/compound/Morpho.sol";

contract Config {
address constant aave = 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9;
address constant dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
Expand All @@ -26,10 +42,10 @@ contract Config {
address constant cUsdt = 0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9;
address constant cWbtc2 = 0xccF4429DB6322D5C611ee964527D42E5d685DD6a;
address constant cEth = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;
address constant cComp = 0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4;
address constant cBat = 0x6C8c6b02E7b2BE14d4fA6022Dfd6d75921D90E4E;
address constant cTusd = 0x12392F67bdf24faE0AF363c24aC620a2f67DAd86;
address constant cUni = 0x35A18000230DA775CAc24873d00Ff85BccdeD550;
address constant cComp = 0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4;
address constant cZrx = 0xB3319f5D18Bc0D84dD1b4825Dcde5d5f7266d407;
address constant cLink = 0xFAce851a4921ce59e912d19329929CE6da6EB0c7;
address constant cMkr = 0x95b4eF2869eBD94BEb4eEE400a99824BF5DC325b;
Expand All @@ -38,5 +54,25 @@ contract Config {
address constant cUsdp = 0x041171993284df560249B57358F931D9eB7b925D;
address constant cSushi = 0x4B0181102A0112A2ef11AbEE5563bb4a3176c9d7;

address constant comptrollerAddress = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B;
address public morphoDao = 0xcBa28b38103307Ec8dA98377ffF9816C164f9AFa;
IComptroller public comptroller = IComptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);

ProxyAdmin public proxyAdmin = ProxyAdmin(0x99917ca0426fbC677e84f873Fb0b726Bb4799cD8);

TransparentUpgradeableProxy public lensProxy =
TransparentUpgradeableProxy(payable(0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67));
TransparentUpgradeableProxy public morphoProxy =
TransparentUpgradeableProxy(payable(0x8888882f8f843896699869179fB6E4f7e3B58888));
TransparentUpgradeableProxy public rewardsManagerProxy;

Lens public lensImplV1;
Morpho public morphoImplV1;
RewardsManager public rewardsManagerImplV1;

Lens public lens;
Morpho public morpho;
RewardsManager public rewardsManager;
IIncentivesVault public incentivesVault;
IPositionsManager public positionsManager;
IInterestRatesManager public interestRatesManager;
}
10 changes: 10 additions & 0 deletions contracts/compound/interfaces/IIncentivesVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ pragma solidity ^0.8.0;
import "./IOracle.sol";

interface IIncentivesVault {
function isPaused() external view returns (bool);

function bonus() external view returns (uint256);

function MAX_BASIS_POINTS() external view returns (uint256);

function incentivesTreasuryVault() external view returns (address);

function oracle() external view returns (IOracle);

function setOracle(IOracle _newOracle) external;

function setIncentivesTreasuryVault(address _newIncentivesTreasuryVault) external;
Expand Down
2 changes: 1 addition & 1 deletion test-foundry/compound/TestGovernance.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ contract TestGovernance is TestSetup {

function testOnlyOwnerShouldSetIncentivesVault() public {
IIncentivesVault incentivesVaultV2 = new IncentivesVault(
IComptroller(comptrollerAddress),
comptroller,
IMorpho(address(morpho)),
morphoToken,
address(1),
Expand Down
68 changes: 15 additions & 53 deletions test-foundry/compound/TestIncentivesVault.t.sol
Original file line number Diff line number Diff line change
@@ -1,51 +1,11 @@
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity 0.8.13;

import "@contracts/compound/interfaces/compound/ICompound.sol";
import "@contracts/compound/interfaces/IOracle.sol";
import "@contracts/compound/IncentivesVault.sol";
import "./setup/TestSetup.sol";

import "@rari-capital/solmate/src/utils/SafeTransferLib.sol";

import "../common/helpers/MorphoToken.sol";
import "./helpers/DumbOracle.sol";
import "@forge-std/Test.sol";
import "@config/Config.sol";

contract TestIncentivesVault is Test, Config {
contract TestIncentivesVault is TestSetup {
using SafeTransferLib for ERC20;

Vm public hevm = Vm(HEVM_ADDRESS);
address public constant COMP = 0xc00e94Cb662C3520282E6f5717214004A7f26888;
address public incentivesTreasuryVault = address(1);
address public morpho = address(3);
IncentivesVault public incentivesVault;
MorphoToken public morphoToken;
DumbOracle public dumbOracle;

function setUp() public {
morphoToken = new MorphoToken(address(this));
dumbOracle = new DumbOracle();

incentivesVault = new IncentivesVault(
IComptroller(comptrollerAddress),
IMorpho(address(morpho)),
morphoToken,
incentivesTreasuryVault,
dumbOracle
);
ERC20(morphoToken).transfer(
address(incentivesVault),
ERC20(morphoToken).balanceOf(address(this))
);

hevm.label(address(morphoToken), "MORPHO");
hevm.label(address(dumbOracle), "DumbOracle");
hevm.label(address(incentivesVault), "IncentivesVault");
hevm.label(COMP, "COMP");
hevm.label(morpho, "morpho");
}

function testShouldNotSetBonusAboveMaxBasisPoints() public {
uint256 moreThanMaxBasisPoints = incentivesVault.MAX_BASIS_POINTS() + 1;
hevm.expectRevert(abi.encodeWithSelector(IncentivesVault.ExceedsMaxBasisPoints.selector));
Expand All @@ -64,6 +24,8 @@ contract TestIncentivesVault is Test, Config {
}

function testOnlyOwnerShouldSetIncentivesTreasuryVault() public {
address incentivesTreasuryVault = address(1);

hevm.prank(address(0));
hevm.expectRevert("Ownable: caller is not the owner");
incentivesVault.setIncentivesTreasuryVault(incentivesTreasuryVault);
Expand Down Expand Up @@ -101,51 +63,51 @@ contract TestIncentivesVault is Test, Config {
incentivesVault.transferTokensToDao(address(morphoToken), 1);

incentivesVault.transferTokensToDao(address(morphoToken), 1);
assertEq(ERC20(morphoToken).balanceOf(incentivesTreasuryVault), 1);
assertEq(ERC20(morphoToken).balanceOf(address(treasuryVault)), 1);
}

function testFailWhenContractNotActive() public {
incentivesVault.setPauseStatus(true);

hevm.prank(morpho);
hevm.prank(address(morpho));
incentivesVault.tradeCompForMorphoTokens(address(1), 0);
}

function testOnlyMorphoShouldTriggerCompConvertFunction() public {
incentivesVault.setIncentivesTreasuryVault(address(1));
uint256 amount = 100;
deal(COMP, address(morpho), amount);
deal(comp, address(morpho), amount);

hevm.prank(morpho);
ERC20(COMP).safeApprove(address(incentivesVault), amount);
hevm.prank(address(morpho));
ERC20(comp).safeApprove(address(incentivesVault), amount);

hevm.expectRevert(abi.encodeWithSignature("OnlyMorpho()"));
incentivesVault.tradeCompForMorphoTokens(address(2), amount);

hevm.prank(morpho);
hevm.prank(address(morpho));
incentivesVault.tradeCompForMorphoTokens(address(2), amount);
}

function testShouldGiveTheRightAmountOfRewards() public {
incentivesVault.setIncentivesTreasuryVault(address(1));
uint256 toApprove = 1_000 ether;
deal(COMP, address(morpho), toApprove);
deal(comp, address(morpho), toApprove);

hevm.prank(morpho);
ERC20(COMP).safeApprove(address(incentivesVault), toApprove);
hevm.prank(address(morpho));
ERC20(comp).safeApprove(address(incentivesVault), toApprove);
uint256 amount = 100;

// O% bonus.
uint256 balanceBefore = ERC20(morphoToken).balanceOf(address(2));
hevm.prank(morpho);
hevm.prank(address(morpho));
incentivesVault.tradeCompForMorphoTokens(address(2), amount);
uint256 balanceAfter = ERC20(morphoToken).balanceOf(address(2));
assertEq(balanceAfter - balanceBefore, 100);

// 10% bonus.
incentivesVault.setBonus(1_000);
balanceBefore = ERC20(morphoToken).balanceOf(address(2));
hevm.prank(morpho);
hevm.prank(address(morpho));
incentivesVault.tradeCompForMorphoTokens(address(2), amount);
balanceAfter = ERC20(morphoToken).balanceOf(address(2));
assertEq(balanceAfter - balanceBefore, 110);
Expand Down
Loading

0 comments on commit a223210

Please sign in to comment.