Skip to content

Commit

Permalink
Merge branch 'main' of github.com:morpho-labs/morpho-stack into refac…
Browse files Browse the repository at this point in the history
…tor/tests
  • Loading branch information
Rubilmax committed Sep 15, 2023
2 parents a5655ad + 83fae40 commit dd351b7
Show file tree
Hide file tree
Showing 17 changed files with 173 additions and 33 deletions.
16 changes: 2 additions & 14 deletions .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,9 @@ jobs:
- type: "slow"
fuzz-runs: 25000
max-test-rejects: 10000
invariant-runs: 64
invariant-depth: 1024
- type: "fast"
fuzz-runs: 4096
fuzz-runs: 2048
max-test-rejects: 65536
invariant-runs: 16
invariant-depth: 256

steps:
- name: Generate a token
Expand All @@ -110,8 +106,6 @@ jobs:
env:
FOUNDRY_FUZZ_RUNS: ${{ matrix.fuzz-runs }}
FOUNDRY_FUZZ_MAX_TEST_REJECTS: ${{ matrix.max-test-rejects }}
FOUNDRY_INVARIANT_RUNS: ${{ matrix.invariant-runs }}
FOUNDRY_INVARIANT_DEPTH: ${{ matrix.invariant-depth }}
FOUNDRY_FUZZ_SEED: 0x${{ github.event.pull_request.base.sha || github.sha }}

test-mainnet:
Expand All @@ -128,13 +122,9 @@ jobs:
- type: "slow"
fuzz-runs: 128
max-test-rejects: 65536
invariant-runs: 16
invariant-depth: 1024
- type: "fast"
fuzz-runs: 32
fuzz-runs: 16
max-test-rejects: 65536
invariant-runs: 4
invariant-depth: 256

steps:
- name: Generate a token
Expand All @@ -158,6 +148,4 @@ jobs:
ALCHEMY_KEY: ${{ secrets.ALCHEMY_KEY }}
FOUNDRY_FUZZ_RUNS: ${{ matrix.fuzz-runs }}
FOUNDRY_FUZZ_MAX_TEST_REJECTS: ${{ matrix.max-test-rejects }}
FOUNDRY_INVARIANT_RUNS: ${{ matrix.invariant-runs }}
FOUNDRY_INVARIANT_DEPTH: ${{ matrix.invariant-depth }}
FOUNDRY_FUZZ_SEED: 0x${{ github.event.pull_request.base.sha || github.sha }}
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
path = lib/morpho-blue
url = https://github.com/morpho-labs/morpho-blue
branch = 0.8.21
[submodule "lib/universal-rewards-distributor"]
path = lib/universal-rewards-distributor
url = https://github.com/morpho-org/universal-rewards-distributor
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/transmissions11/solmate
Expand All @@ -32,3 +35,6 @@
[submodule "lib/morpho-v1"]
path = lib/morpho-v1
url = https://github.com/morpho-org/morpho-v1
[submodule "lib/murky"]
path = lib/murky
url = https://github.com/dmfxyz/murky
2 changes: 1 addition & 1 deletion contracts/ERC4626Bundler.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC4626} from "@openzeppelin/interfaces/IERC4626.sol";

import {Math} from "@morpho-utils/math/Math.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
Expand Down
5 changes: 3 additions & 2 deletions contracts/EVMBundler.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;

import {URDBundler} from "./URDBundler.sol";
import {MorphoBundler} from "./MorphoBundler.sol";
import {Permit2Bundler} from "./Permit2Bundler.sol";
import {ERC4626Bundler} from "./ERC4626Bundler.sol";
Expand All @@ -9,6 +10,6 @@ import {ERC4626Bundler} from "./ERC4626Bundler.sol";
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Common bundler layer guaranteeing it can be deployed to the same address on all EVM-compatible chains.
contract EVMBundler is Permit2Bundler, ERC4626Bundler, MorphoBundler {
constructor(address morpho) MorphoBundler(morpho) {}
contract EVMBundler is Permit2Bundler, ERC4626Bundler, URDBundler, MorphoBundler {
constructor(address urd, address morpho) URDBundler(urd) MorphoBundler(morpho) {}
}
32 changes: 32 additions & 0 deletions contracts/URDBundler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;

import {IUniversalRewardsDistributor} from "@universal-rewards-distributor/interfaces/IUniversalRewardsDistributor.sol";

import {ErrorsLib} from "./libraries/ErrorsLib.sol";

import {BaseBundler} from "./BaseBundler.sol";

/// @title URDBudnler
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Bundler that allows to claim token rewards on the Universal Rewards Distributor.
contract URDBundler is BaseBundler {
IUniversalRewardsDistributor public immutable URD;

constructor(address urd) {
require(urd != address(0), ErrorsLib.ZERO_ADDRESS);

URD = IUniversalRewardsDistributor(urd);
}

function claim(uint256 distributionId, address account, address reward, uint256 claimable, bytes32[] calldata proof)
external
payable
{
require(account != address(this), ErrorsLib.BUNDLER_ADDRESS);
require(account != address(0), ErrorsLib.ZERO_ADDRESS);

URD.claim(distributionId, account, reward, claimable, proof);
}
}
2 changes: 1 addition & 1 deletion contracts/ethereum-mainnet/EthereumBundler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ import {WNativeBundler} from "../WNativeBundler.sol";
contract EthereumBundler is EVMBundler, WNativeBundler, StEthBundler {
/* CONSTRUCTOR */

constructor(address morpho) EVMBundler(morpho) WNativeBundler(WETH) {}
constructor(address urd, address morpho) EVMBundler(urd, morpho) WNativeBundler(WETH) {}
}
2 changes: 1 addition & 1 deletion contracts/mocks/ERC20Mock.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20} from "@openzeppelin/token/ERC20/ERC20.sol";

contract ERC20Mock is ERC20 {
constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {}
Expand Down
6 changes: 3 additions & 3 deletions contracts/mocks/ERC4626Mock.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import {IERC20} from "@openzeppelin/token/ERC20/IERC20.sol";
import {ERC20} from "@openzeppelin/token/ERC20/ERC20.sol";
import {ERC4626} from "@openzeppelin/token/ERC20/extensions/ERC4626.sol";

contract ERC4626Mock is ERC4626 {
constructor(address asset, string memory name, string memory symbol) ERC4626(IERC20(asset)) ERC20(name, symbol) {}
Expand Down
3 changes: 0 additions & 3 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ fs_permissions = [
[profile.default.fuzz]
runs = 32

[profile.default.invariant]
runs = 16
depth = 4

[profile.default.rpc_endpoints]
mainnet = "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"
Expand Down
1 change: 1 addition & 0 deletions lib/murky
Submodule murky added at 40de6e
1 change: 1 addition & 0 deletions lib/universal-rewards-distributor
6 changes: 5 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ test/=test/
solmate/=lib/solmate/

@forge-std/=lib/morpho-blue/lib/forge-std/src/
@solmate/=lib/solmate/src/
@morpho-v1/=lib/morpho-v1/src/
@morpho-blue/=lib/morpho-blue/src/
@morpho-utils/=lib/morpho-utils/src/
@universal-rewards-distributor/=lib/universal-rewards-distributor/src/
@morpho-aave-v3/=lib/morpho-aave-v3/src
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@openzeppelin/=lib/openzeppelin-contracts/contracts/
@permit2/=lib/permit2/src/

@uniswap/v3-core=lib/v3-core/contracts/
@uniswap/v3-periphery=lib/v3-periphery/contracts/

@aave/v3-core=lib/aave-v3-core/contracts/

@murky/=lib/murky/
111 changes: 109 additions & 2 deletions test/forge/EVMBundlerLocalTest.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {UniversalRewardsDistributor} from "@universal-rewards-distributor/UniversalRewardsDistributor.sol";

import {SigUtils} from "./helpers/SigUtils.sol";
import {ErrorsLib} from "contracts/libraries/ErrorsLib.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {ECDSA} from "@openzeppelin/utils/cryptography/ECDSA.sol";

import {Merkle} from "@murky/src/Merkle.sol";

import {BaseBundler} from "contracts/BaseBundler.sol";
import "contracts/EVMBundler.sol";
Expand All @@ -18,14 +22,19 @@ contract EVMBundlerLocalTest is LocalTest {

uint256 internal constant SIG_DEADLINE = type(uint32).max;

UniversalRewardsDistributor private urd;
EVMBundler private bundler;

bytes[] private bundle;

Merkle merkle;

function setUp() public override {
super.setUp();

bundler = new EVMBundler(address(morpho));
urd = new UniversalRewardsDistributor();
bundler = new EVMBundler(address(urd),address(morpho));
merkle = new Merkle();

vm.startPrank(USER);
borrowableToken.approve(address(morpho), type(uint256).max);
Expand Down Expand Up @@ -771,4 +780,102 @@ contract EVMBundlerLocalTest is LocalTest {
assertEq(borrowableToken.balanceOf(address(bundler)), 0, "Bundler's borrowable token balance");
assertEq(borrowableToken.balanceOf(address(morpho)), amount, "Morpho's borrowable token balance");
}

/* TESTS URDBUNDLER */

function testClaimRewardsZeroAddress(uint256 claimable) public {
claimable = bound(claimable, MIN_AMOUNT, MAX_AMOUNT);

bytes32 root;
bytes32[] memory proof;

uint256 distribution = urd.createDistribution(0, root);

bytes[] memory zeroAddressdata = new bytes[](1);
zeroAddressdata[0] =
abi.encodeCall(URDBundler.claim, (distribution, address(0), address(borrowableToken), claimable, proof));

vm.prank(USER);
vm.expectRevert(bytes(ErrorsLib.ZERO_ADDRESS));
bundler.multicall(block.timestamp, zeroAddressdata);
}

function testClaimRewardsBundlerAddress(uint256 claimable) public {
claimable = bound(claimable, MIN_AMOUNT, MAX_AMOUNT);

bytes32 root;
bytes32[] memory proof;

uint256 distribution = urd.createDistribution(0, root);

bytes[] memory bundlerAddressdata = new bytes[](1);
bundlerAddressdata[0] = abi.encodeCall(
URDBundler.claim, (distribution, address(bundler), address(borrowableToken), claimable, proof)
);

vm.prank(USER);
vm.expectRevert(bytes(ErrorsLib.BUNDLER_ADDRESS));
bundler.multicall(block.timestamp, bundlerAddressdata);
}

function testClaimRewards(uint256 claimable, uint8 size) public {
claimable = bound(claimable, 1 ether, 1000 ether);
uint256 boundedSize = bound(size, 2, 20);

(bytes32[] memory proofs, bytes32 root) = _setupRewards(claimable, boundedSize);

borrowableToken.setBalance(address(this), claimable);
borrowableToken.approve(address(urd), type(uint256).max);
collateralToken.setBalance(address(this), claimable);
collateralToken.approve(address(urd), type(uint256).max);

uint256 distribution = urd.createDistribution(0, root);

bytes32[] memory borrowableTokenProof = merkle.getProof(proofs, 0);
bytes32[] memory collateralTokenProof = merkle.getProof(proofs, 1);

bytes[] memory data = new bytes[](2);
data[0] = abi.encodeCall(
URDBundler.claim, (distribution, USER, address(borrowableToken), claimable, borrowableTokenProof)
);
data[1] = abi.encodeCall(
URDBundler.claim, (distribution, USER, address(collateralToken), claimable, collateralTokenProof)
);

vm.prank(USER);
bundler.multicall(block.timestamp, data);

assertEq(borrowableToken.balanceOf(USER), claimable, "User's borrowable balance");
assertEq(collateralToken.balanceOf(USER), claimable, "User's collateral balance");
}

function _setupRewards(uint256 claimable, uint256 size)
internal
view
returns (bytes32[] memory data, bytes32 root)
{
data = new bytes32[](size);

data[0] = keccak256(bytes.concat(keccak256(abi.encode(USER, address(borrowableToken), claimable))));
data[1] = keccak256(bytes.concat(keccak256(abi.encode(USER, address(collateralToken), claimable))));

uint256 i = 2;
while (i < size / 2) {
uint256 index = i + 1;
data[i] = keccak256(
bytes.concat(
keccak256(abi.encode(vm.addr(index), address(borrowableToken), uint256(claimable / index)))
)
);
data[i + 1] = keccak256(
bytes.concat(
keccak256(abi.encode(vm.addr(index), address(collateralToken), uint256(claimable / index)))
)
);

i += 2;
}

root = merkle.getRoot(data);
}
}
7 changes: 5 additions & 2 deletions test/forge/ethereum-mainnet/EthereumBundlerEthereumTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ pragma solidity ^0.8.0;

import {IAllowanceTransfer} from "@permit2/interfaces/IAllowanceTransfer.sol";

import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {ECDSA} from "@openzeppelin/utils/cryptography/ECDSA.sol";
import {UniversalRewardsDistributor} from "@universal-rewards-distributor/UniversalRewardsDistributor.sol";

import "contracts/ethereum-mainnet/EthereumBundler.sol";
import {MorphoBundler} from "contracts/MorphoBundler.sol";
Expand All @@ -18,12 +19,14 @@ contract EthereumBundlerEthereumTest is EthereumTest {
using MarketParamsLib for MarketParams;
using SafeTransferLib for ERC20;

UniversalRewardsDistributor private urd;
EthereumBundler private bundler;

function setUp() public override {
super.setUp();

bundler = new EthereumBundler(address(morpho));
urd = new UniversalRewardsDistributor();
bundler = new EthereumBundler(address(urd), address(morpho));

vm.prank(USER);
morpho.setAuthorization(address(bundler), true);
Expand Down
2 changes: 1 addition & 1 deletion test/forge/ethereum-mainnet/Permit2BundlerEthereumTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
import {IAllowanceTransfer} from "@permit2/interfaces/IAllowanceTransfer.sol";

import {ErrorsLib} from "contracts/libraries/ErrorsLib.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {ECDSA} from "@openzeppelin/utils/cryptography/ECDSA.sol";

import "contracts/mocks/bundlers/Permit2BundlerMock.sol";

Expand Down
2 changes: 1 addition & 1 deletion test/forge/ethereum-mainnet/StEthBundlerEthereumTest.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {ECDSA} from "@openzeppelin/utils/cryptography/ECDSA.sol";
import {SigUtils} from "test/forge/helpers/SigUtils.sol";
import {ErrorsLib} from "contracts/libraries/ErrorsLib.sol";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.0;
import {IAllowanceTransfer} from "@permit2/interfaces/IAllowanceTransfer.sol";

import {SafeTransferLib, ERC20} from "solmate/src/utils/SafeTransferLib.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {ECDSA} from "@openzeppelin/utils/cryptography/ECDSA.sol";
import {MarketParamsLib} from "@morpho-blue/libraries/MarketParamsLib.sol";
import {MorphoLib} from "@morpho-blue/libraries/periphery/MorphoLib.sol";
import {MorphoBalancesLib} from "@morpho-blue/libraries/periphery/MorphoBalancesLib.sol";
Expand Down

0 comments on commit dd351b7

Please sign in to comment.