Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC4626 standard property tests #1

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/create3-factory"]
path = lib/create3-factory
url = https://github.com/zeframlou/create3-factory
[submodule "lib/erc4626-tests"]
path = lib/erc4626-tests
url = [email protected]:a16z/erc4626-tests.git
1 change: 1 addition & 0 deletions lib/erc4626-tests
Submodule erc4626-tests added at 0c20d2
54 changes: 54 additions & 0 deletions src/test/aave-v2/ERC4626.st.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import "erc4626-tests/ERC4626.test.sol";

import {ERC20Mock} from "../mocks/ERC20Mock.sol";
import {AaveMiningMock} from "./mocks/AaveMiningMock.sol";
import {LendingPoolMock} from "./mocks/LendingPoolMock.sol";
import {AaveV2ERC4626} from "../../aave-v2/AaveV2ERC4626.sol";
import {IAaveMining} from "../../aave-v2/external/IAaveMining.sol";
import {ILendingPool} from "../../aave-v2/external/ILendingPool.sol";
import {AaveV2ERC4626Factory} from "../../aave-v2/AaveV2ERC4626Factory.sol";

contract ERC4626StdTest is ERC4626Test {
address public constant rewardRecipient = address(0x01);

// copied from AaveV2ERC4626.t.sol
ERC20Mock public aave;
ERC20Mock public aToken;
AaveV2ERC4626 public vault;
ERC20Mock public underlying;
IAaveMining public aaveMining;
LendingPoolMock public lendingPool;
AaveV2ERC4626Factory public factory;

function setUp() public override {
// copied from AaveV2ERC4626.t.sol
aave = new ERC20Mock();
aToken = new ERC20Mock();
underlying = new ERC20Mock();
lendingPool = new LendingPoolMock();
aaveMining = new AaveMiningMock(address(aave));
factory = new AaveV2ERC4626Factory(aaveMining, rewardRecipient, lendingPool);
lendingPool.setReserveAToken(address(underlying), address(aToken));
vault = AaveV2ERC4626(address(factory.createERC4626(underlying)));

// for ERC4626Test setup
__underlying__ = address(underlying);
__vault__ = address(vault);
__delta__ = 0;
}

// custom setup for yield
function setupYield(Init memory init) public override {
// setup initial yield
if (init.yield >= 0) {
uint gain = uint(init.yield);
try underlying.mint(address(lendingPool), gain) {} catch { vm.assume(false); }
try aToken.mint(address(vault), gain) {} catch { vm.assume(false); }
} else {
vm.assume(false); // TODO: test negative yield scenario
}
}
}
54 changes: 54 additions & 0 deletions src/test/aave-v3/ERC4626.st.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import "erc4626-tests/ERC4626.test.sol";

import {PoolMock} from "./mocks/PoolMock.sol";
import {ERC20Mock} from "../mocks/ERC20Mock.sol";
import {IPool} from "../../aave-v3/external/IPool.sol";
import {AaveV3ERC4626} from "../../aave-v3/AaveV3ERC4626.sol";
import {RewardsControllerMock} from "./mocks/RewardsControllerMock.sol";
import {AaveV3ERC4626Factory} from "../../aave-v3/AaveV3ERC4626Factory.sol";
import {IRewardsController} from "../../aave-v3/external/IRewardsController.sol";

contract ERC4626StdTest is ERC4626Test {
address public constant rewardRecipient = address(0x01);

// copied from AaveV3ERC4626.t.sol
ERC20Mock public aave;
ERC20Mock public aToken;
AaveV3ERC4626 public vault;
ERC20Mock public underlying;
PoolMock public lendingPool;
AaveV3ERC4626Factory public factory;
IRewardsController public rewardsController;

function setUp() public override {
// copied from AaveV3ERC4626.t.sol
aave = new ERC20Mock();
aToken = new ERC20Mock();
underlying = new ERC20Mock();
lendingPool = new PoolMock();
rewardsController = new RewardsControllerMock(address(aave));
factory = new AaveV3ERC4626Factory(lendingPool, rewardRecipient, rewardsController);
lendingPool.setReserveAToken(address(underlying), address(aToken));
vault = AaveV3ERC4626(address(factory.createERC4626(underlying)));

// for ERC4626Test setup
__underlying__ = address(underlying);
__vault__ = address(vault);
__delta__ = 0;
}

// custom setup for yield
function setupYield(Init memory init) public override {
// setup initial yield
if (init.yield >= 0) {
uint gain = uint(init.yield);
try underlying.mint(address(lendingPool), gain) {} catch { vm.assume(false); }
try aToken.mint(address(vault), gain) {} catch { vm.assume(false); }
} else {
vm.assume(false); // TODO: test negative yield scenario
}
}
}
75 changes: 75 additions & 0 deletions src/test/euler/ERC4626.st.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import "erc4626-tests/ERC4626.test.sol";

import {EulerMock} from "./mocks/EulerMock.sol";
import {ERC20Mock} from "../mocks/ERC20Mock.sol";
import {EulerERC4626} from "../../euler/EulerERC4626.sol";
import {EulerETokenMock} from "./mocks/EulerETokenMock.sol";
import {EulerMarketsMock} from "./mocks/EulerMarketsMock.sol";
import {EulerERC4626Factory} from "../../euler/EulerERC4626Factory.sol";

contract ERC4626StdTest is ERC4626Test {
// copied from EulerERC4626.t.sol
EulerMock public euler;
EulerERC4626 public vault;
ERC20Mock public underlying;
EulerETokenMock public eToken;
EulerMarketsMock public markets;
EulerERC4626Factory public factory;

function setUp() public override {
// copied from EulerERC4626.t.sol
euler = new EulerMock();
underlying = new ERC20Mock();
eToken = new EulerETokenMock(underlying, euler);
markets = new EulerMarketsMock();
factory = new EulerERC4626Factory(address(euler), markets);
markets.setETokenForUnderlying(address(underlying), address(eToken));
vault = EulerERC4626(address(factory.createERC4626(underlying)));

// for ERC4626Test setup
__underlying__ = address(underlying);
__vault__ = address(vault);
__delta__ = 0;
}

// custom setup for yield
function setupYield(Init memory init) public override {
// setup initial yield
if (init.yield >= 0) {
uint gain = uint(init.yield);
try underlying.mint(address(eToken), gain) {} catch { vm.assume(false); }
} else {
vm.assume(false); // TODO: test negative yield scenario
}
}

// NOTE: The following tests are relaxed to consider only smaller values (of type uint120),
// since the totalAssets(), maxWithdraw(), and maxRedeem() functions fail with large values (due to overflow).

function test_totalAssets(Init memory init) public override {
init = clamp(init, type(uint120).max);
super.test_totalAssets(init);
}

function test_maxWithdraw(Init memory init) public override {
init = clamp(init, type(uint120).max);
super.test_maxWithdraw(init);
}

function test_maxRedeem(Init memory init) public override {
init = clamp(init, type(uint120).max);
super.test_maxRedeem(init);
}

function clamp(Init memory init, uint max) internal pure returns (Init memory) {
for (uint i = 0; i < N; i++) {
init.share[i] = init.share[i] % max;
init.asset[i] = init.asset[i] % max;
}
init.yield = init.yield % int(max);
return init;
}
}
4 changes: 3 additions & 1 deletion src/test/euler/mocks/EulerETokenMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ contract EulerETokenMock is IEulerEToken, ERC20("EulerETokenMock", "eMOCK", 18)
}

function deposit(uint256, uint256 amount) external override {
uint shares = convertToShares(amount);

// call EulerMock to transfer tokens from sender
euler.transferTokenFrom(underlying, msg.sender, address(this), amount);

// mint shares
_mint(msg.sender, convertToShares(amount));
_mint(msg.sender, shares);
}

function withdraw(uint256, uint256 amount) external override {
Expand Down