Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ jobs:
- name: Run tests on chain ${{ matrix.chain }}
run: forge test --chain ${{ matrix.chain }}
env:
ALCHEMY_KEY: ${{ secrets.ALCHEMY_KEY }}
ALCHEMY_KEY: ${{ secrets.PROTOCOL_ALCHEMY_KEY }}
9 changes: 3 additions & 6 deletions src/adapters/GeneralAdapter1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,21 @@ contract GeneralAdapter1 is CoreAdapter {
// their permissioned counterparts and access permissioned markets on Morpho. Permissioned tokens can be built
// using: https://github.com/morpho-org/erc20-permissioned

/// @notice Wraps underlying tokens to wrapped token.
/// @notice Wraps underlying tokens to wrapped token and sends them to the initiator.
/// @dev Underlying tokens must have been previously sent to the adapter.
/// @dev Assumes that `wrapper` implements the `ERC20Wrapper` interface.
/// @param wrapper The address of the ERC20 wrapper contract.
/// @param receiver The account receiving the wrapped tokens.
/// @param amount The amount of underlying tokens to deposit. Pass `type(uint).max` to deposit the adapter's
/// underlying balance.
function erc20WrapperDepositFor(address wrapper, address receiver, uint256 amount) external onlyBundler3 {
require(receiver != address(0), ErrorsLib.ZeroAddress());

function erc20WrapperDepositFor(address wrapper, uint256 amount) external onlyBundler3 {
IERC20 underlying = ERC20Wrapper(wrapper).underlying();
if (amount == type(uint256).max) amount = underlying.balanceOf(address(this));

require(amount != 0, ErrorsLib.ZeroAmount());

SafeERC20.forceApprove(underlying, wrapper, type(uint256).max);

require(ERC20Wrapper(wrapper).depositFor(receiver, amount), ErrorsLib.DepositFailed());
require(ERC20Wrapper(wrapper).depositFor(initiator(), amount), ErrorsLib.DepositFailed());

SafeERC20.forceApprove(underlying, wrapper, 0);
}
Expand Down
35 changes: 15 additions & 20 deletions test/ERC20WrapperAdapterLocalTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,32 @@ contract ERC20WrapperAdapterLocalTest is LocalTest {
loanWrapper = new ERC20WrapperMock(loanToken, "Wrapped Loan Token", "WLT");
}

function testErc20WrapperDepositForZeroAdress(uint256 amount) public {
function testErc20WrapperDepositFor(uint256 amount, address initiator) public {
vm.assume(initiator != address(0));
vm.assume(initiator != address(loanWrapper));
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);

bundle.push(_erc20WrapperDepositFor(address(loanWrapper), address(0), amount));

vm.expectRevert(ErrorsLib.ZeroAddress.selector);
bundler3.multicall(bundle);
}

function testErc20WrapperDepositFor(uint256 amount, address receiver) public {
vm.assume(receiver != address(0));
vm.assume(receiver != address(loanWrapper));
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);

bundle.push(_erc20WrapperDepositFor(address(loanWrapper), address(receiver), amount));
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), amount));

deal(address(loanToken), address(generalAdapter1), amount);

vm.prank(initiator);
bundler3.multicall(bundle);

assertEq(loanToken.balanceOf(address(generalAdapter1)), 0, "loan.balanceOf(generalAdapter1)");
assertEq(loanWrapper.balanceOf(receiver), amount, "loanWrapper.balanceOf(receiver)");
assertEq(loanWrapper.balanceOf(initiator), amount, "loanWrapper.balanceOf(initiator)");
assertEq(
loanToken.allowance(address(generalAdapter1), address(loanWrapper)),
0,
"loanToken.allowance(generalAdapter1, loanWrapper)"
);
}

function testErc20WrapperDepositForZeroAmount() public {
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), address(RECEIVER), 0));
function testErc20WrapperDepositForZeroAmount(address initiator) public {
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), 0));

vm.expectRevert(ErrorsLib.ZeroAmount.selector);
vm.prank(initiator);
bundler3.multicall(bundle);
}

Expand Down Expand Up @@ -96,11 +89,12 @@ contract ERC20WrapperAdapterLocalTest is LocalTest {
bundler3.multicall(bundle);
}

function testErc20WrapperDepositForUnauthorized(uint256 amount) public {
function testErc20WrapperDepositForUnauthorized(uint256 amount, address initiator) public {
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);

vm.expectRevert(ErrorsLib.UnauthorizedSender.selector);
generalAdapter1.erc20WrapperDepositFor(address(loanWrapper), address(RECEIVER), amount);
vm.prank(initiator);
generalAdapter1.erc20WrapperDepositFor(address(loanWrapper), amount);
}

function testErc20WrapperWithdrawToUnauthorized(uint256 amount) public {
Expand All @@ -110,15 +104,16 @@ contract ERC20WrapperAdapterLocalTest is LocalTest {
generalAdapter1.erc20WrapperWithdrawTo(address(loanWrapper), RECEIVER, amount);
}

function testErc20WrapperDepositToFailed(uint256 amount) public {
function testErc20WrapperDepositToFailed(uint256 amount, address initiator) public {
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
deal(address(loanToken), address(generalAdapter1), amount);

bundle.push(_erc20WrapperDepositFor(address(loanWrapper), address(RECEIVER), amount));
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), amount));

vm.mockCall(address(loanWrapper), abi.encodeWithSelector(ERC20Wrapper.depositFor.selector), abi.encode(false));

vm.expectRevert(ErrorsLib.DepositFailed.selector);
vm.prank(initiator);
bundler3.multicall(bundle);
}

Expand Down
134 changes: 134 additions & 0 deletions test/fork/ERC20PermissionedWrappersForkTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

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

import {ERC20Wrapper} from "../helpers/mocks/ERC20WrapperMock.sol";

import "./helpers/ForkTest.sol";

/* WBIB01 INTERFACES */

error NonWhitelistedToAddress(address to);

interface IWrappedBackedToken {
function whitelistControllerAggregator() external view returns (address);
}

interface IWhitelistControllerAggregator {
function isWhitelisted(address) external view returns (bool, address);
}

/* VER_USDC INTERFACES */

error NoPermission(address account);

interface IPermissionedERC20Wrapper {
function memberlist() external view returns (address);
}

interface IMemberList {
function isMember(address) external view returns (bool);
}

/* TEST */

contract Erc20PermissionedWrappersForkTest is ForkTest {
address internal immutable WBIB01 = getAddress("WBIB01");
address internal immutable VER_USDC = getAddress("VER_USDC");

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

if (block.chainid == 1) {
_whitelistForWbib01(address(generalAdapter1));
} else if (block.chainid == 8453) {
console.log("VEr", VER_USDC);
console.log("VEr", VER_USDC.code.length);
_whitelistForVerUsdc(address(generalAdapter1));
}
}

function testWbib01NotUsableWithoutPermission(uint256 amount, address initiator) public onlyEthereum {
vm.assume(initiator != address(0));
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);

deal(address(ERC20Wrapper(WBIB01).underlying()), address(generalAdapter1), amount);

bundle.push(_erc20WrapperDepositFor(address(WBIB01), amount));

vm.expectRevert(abi.encodeWithSelector(NonWhitelistedToAddress.selector, initiator));
vm.prank(initiator);
bundler3.multicall(bundle);
}

function testWbibUsableWithPermission(uint256 amount, address initiator) public onlyEthereum {
vm.assume(initiator != address(0));
_whitelistForWbib01(initiator);

amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);

vm.prank(initiator);
IERC20(WBIB01).approve(address(generalAdapter1), amount);

deal(address(ERC20Wrapper(WBIB01).underlying()), address(generalAdapter1), amount, true);

bundle.push(_erc20WrapperDepositFor(address(WBIB01), amount));
bundle.push(_erc20TransferFrom(address(WBIB01), address(generalAdapter1), amount));

vm.prank(initiator);
bundler3.multicall(bundle);

vm.assertGt(IERC20(WBIB01).balanceOf(address(generalAdapter1)), 0);
}

function testVerUsdcNotUsableWithoutPermission(uint256 amount, address initiator) public onlyBase {
vm.assume(initiator != address(0));
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);

deal(address(ERC20Wrapper(VER_USDC).underlying()), address(generalAdapter1), amount);

bundle.push(_erc20WrapperDepositFor(address(VER_USDC), amount));

// vm.expectRevert(abi.encodeWithSelector(NoPermission.selector, initiator));
vm.expectRevert("PermissionedERC20Wrapper/no-attestation-found");
vm.prank(initiator);
bundler3.multicall(bundle);
}

function testVerUsdcUsableWithPermission(uint256 amount, address initiator) public onlyBase {
vm.assume(initiator != address(0));
_whitelistForVerUsdc(initiator);

amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);

vm.prank(initiator);
IERC20(VER_USDC).approve(address(generalAdapter1), amount);

deal(address(ERC20Wrapper(VER_USDC).underlying()), address(generalAdapter1), amount, true);

bundle.push(_erc20WrapperDepositFor(address(VER_USDC), amount));
bundle.push(_erc20TransferFrom(address(VER_USDC), address(generalAdapter1), amount));

vm.prank(initiator);
bundler3.multicall(bundle);

vm.assertGt(IERC20(VER_USDC).balanceOf(address(generalAdapter1)), 0);
}

/* WHITELISTING HELPERS */

function _whitelistForWbib01(address account) internal {
address controller = IWrappedBackedToken(WBIB01).whitelistControllerAggregator();
vm.mockCall(
controller,
abi.encodeCall(IWhitelistControllerAggregator.isWhitelisted, (account)),
abi.encode(true, address(0))
);
}

function _whitelistForVerUsdc(address account) internal {
address memberList = IPermissionedERC20Wrapper(VER_USDC).memberlist();
vm.mockCall(memberList, abi.encodeCall(IMemberList.isMember, (account)), abi.encode(true));
}
}
5 changes: 5 additions & 0 deletions test/fork/helpers/ForkTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ abstract contract ForkTest is CommonTest, NetworkConfig {
_;
}

modifier onlyBase() {
vm.skip(block.chainid != 8453);
_;
}

function _randomMarketParams(uint256 seed) internal view returns (MarketParams memory) {
return allMarketParams[seed % allMarketParams.length];
}
Expand Down
4 changes: 3 additions & 1 deletion test/fork/helpers/NetworkConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ abstract contract NetworkConfig is CommonBase {
setAddress("MORPHO_TOKEN", 0x58D97B57BB95320F9a05dC918Aef65434969c2B2);
setAddress("AUGUSTUS_V6_2", 0x6A000F20005980200259B80c5102003040001068);
setAddress("AUGUSTUS_REGISTRY", 0xa68bEA62Dc4034A689AA0F58A76681433caCa663);
setAddress("WBIB01", 0xcA2A7068e551d5C4482eb34880b194E4b945712F);

/* BASE NETWORK */
} else if (config.chainid == 8453) {
config.network = "base";
config.blockNumber = 14000000;
config.blockNumber = 25641890;
config.markets.push(ConfigMarket({collateralToken: "WETH", loanToken: "WETH", lltv: 800000000000000000}));

setAddress("DAI", 0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb);
Expand All @@ -70,6 +71,7 @@ abstract contract NetworkConfig is CommonBase {
setAddress("C_WETH_V3", 0x46e6b214b524310239732D51387075E0e70970bf);
setAddress("AUGUSTUS_V6_2", 0x6A000F20005980200259B80c5102003040001068);
setAddress("AUGUSTUS_REGISTRY", 0x7E31B336F9E8bA52ba3c4ac861b033Ba90900bb3);
setAddress("VER_USDC", 0x59aaF835D34b1E3dF2170e4872B785f11E2a964b);
}
}

Expand Down
8 changes: 2 additions & 6 deletions test/helpers/CommonTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,8 @@ abstract contract CommonTest is Test {

/* ERC20 WRAPPER ACTIONS */

function _erc20WrapperDepositFor(address token, address receiver, uint256 amount)
internal
view
returns (Call memory)
{
return _call(generalAdapter1, abi.encodeCall(GeneralAdapter1.erc20WrapperDepositFor, (token, receiver, amount)));
function _erc20WrapperDepositFor(address token, uint256 amount) internal view returns (Call memory) {
return _call(generalAdapter1, abi.encodeCall(GeneralAdapter1.erc20WrapperDepositFor, (token, amount)));
}

function _erc20WrapperWithdrawTo(address token, address receiver, uint256 amount)
Expand Down