Skip to content

Commit 3f47c41

Browse files
authored
Merge pull request #233 from morpho-org/fix/restrict-receiver-on-deposit
restrict depositFor to initiator
2 parents 9e44f80 + 239e65f commit 3f47c41

File tree

6 files changed

+160
-33
lines changed

6 files changed

+160
-33
lines changed

src/adapters/GeneralAdapter1.sol

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,24 +47,22 @@ contract GeneralAdapter1 is CoreAdapter {
4747
// their permissioned counterparts and access permissioned markets on Morpho. Permissioned tokens can be built
4848
// using: https://github.com/morpho-org/erc20-permissioned
4949

50-
/// @notice Wraps underlying tokens to wrapped token.
50+
/// @notice Wraps underlying tokens to wrapped token and sends them to the initiator.
5151
/// @dev Underlying tokens must have been previously sent to the adapter.
5252
/// @dev Assumes that `wrapper` implements the `ERC20Wrapper` interface.
53+
/// @dev The account is hardcoded to the initiator to prevent unauthorized wrapping.
5354
/// @param wrapper The address of the ERC20 wrapper contract.
54-
/// @param receiver The account receiving the wrapped tokens.
5555
/// @param amount The amount of underlying tokens to deposit. Pass `type(uint).max` to deposit the adapter's
5656
/// underlying balance.
57-
function erc20WrapperDepositFor(address wrapper, address receiver, uint256 amount) external onlyBundler3 {
58-
require(receiver != address(0), ErrorsLib.ZeroAddress());
59-
57+
function erc20WrapperDepositFor(address wrapper, uint256 amount) external onlyBundler3 {
6058
IERC20 underlying = ERC20Wrapper(wrapper).underlying();
6159
if (amount == type(uint256).max) amount = underlying.balanceOf(address(this));
6260

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

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

67-
require(ERC20Wrapper(wrapper).depositFor(receiver, amount), ErrorsLib.DepositFailed());
65+
require(ERC20Wrapper(wrapper).depositFor(initiator(), amount), ErrorsLib.DepositFailed());
6866

6967
SafeERC20.forceApprove(underlying, wrapper, 0);
7068
}

test/ERC20WrapperAdapterLocalTest.sol

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,32 @@ contract ERC20WrapperAdapterLocalTest is LocalTest {
1616
loanWrapper = new ERC20WrapperMock(loanToken, "Wrapped Loan Token", "WLT");
1717
}
1818

19-
function testErc20WrapperDepositForZeroAdress(uint256 amount) public {
19+
function testErc20WrapperDepositFor(uint256 amount, address initiator) public {
20+
vm.assume(initiator != address(0));
21+
vm.assume(initiator != address(loanWrapper));
2022
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
2123

22-
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), address(0), amount));
23-
24-
vm.expectRevert(ErrorsLib.ZeroAddress.selector);
25-
bundler3.multicall(bundle);
26-
}
27-
28-
function testErc20WrapperDepositFor(uint256 amount, address receiver) public {
29-
vm.assume(receiver != address(0));
30-
vm.assume(receiver != address(loanWrapper));
31-
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
32-
33-
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), address(receiver), amount));
24+
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), amount));
3425

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

28+
vm.prank(initiator);
3729
bundler3.multicall(bundle);
3830

3931
assertEq(loanToken.balanceOf(address(generalAdapter1)), 0, "loan.balanceOf(generalAdapter1)");
40-
assertEq(loanWrapper.balanceOf(receiver), amount, "loanWrapper.balanceOf(receiver)");
32+
assertEq(loanWrapper.balanceOf(initiator), amount, "loanWrapper.balanceOf(initiator)");
4133
assertEq(
4234
loanToken.allowance(address(generalAdapter1), address(loanWrapper)),
4335
0,
4436
"loanToken.allowance(generalAdapter1, loanWrapper)"
4537
);
4638
}
4739

48-
function testErc20WrapperDepositForZeroAmount() public {
49-
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), address(RECEIVER), 0));
40+
function testErc20WrapperDepositForZeroAmount(address initiator) public {
41+
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), 0));
5042

5143
vm.expectRevert(ErrorsLib.ZeroAmount.selector);
44+
vm.prank(initiator);
5245
bundler3.multicall(bundle);
5346
}
5447

@@ -96,11 +89,12 @@ contract ERC20WrapperAdapterLocalTest is LocalTest {
9689
bundler3.multicall(bundle);
9790
}
9891

99-
function testErc20WrapperDepositForUnauthorized(uint256 amount) public {
92+
function testErc20WrapperDepositForUnauthorized(uint256 amount, address initiator) public {
10093
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
10194

10295
vm.expectRevert(ErrorsLib.UnauthorizedSender.selector);
103-
generalAdapter1.erc20WrapperDepositFor(address(loanWrapper), address(RECEIVER), amount);
96+
vm.prank(initiator);
97+
generalAdapter1.erc20WrapperDepositFor(address(loanWrapper), amount);
10498
}
10599

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

113-
function testErc20WrapperDepositToFailed(uint256 amount) public {
107+
function testErc20WrapperDepositToFailed(uint256 amount, address initiator) public {
114108
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
115109
deal(address(loanToken), address(generalAdapter1), amount);
116110

117-
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), address(RECEIVER), amount));
111+
bundle.push(_erc20WrapperDepositFor(address(loanWrapper), amount));
118112

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

121115
vm.expectRevert(ErrorsLib.DepositFailed.selector);
116+
vm.prank(initiator);
122117
bundler3.multicall(bundle);
123118
}
124119

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity ^0.8.0;
3+
4+
import {ErrorsLib} from "../../src/libraries/ErrorsLib.sol";
5+
6+
import {ERC20Wrapper} from "../helpers/mocks/ERC20WrapperMock.sol";
7+
8+
import "./helpers/ForkTest.sol";
9+
10+
/* WBIB01 INTERFACES */
11+
12+
error NonWhitelistedToAddress(address to);
13+
14+
interface IWrappedBackedToken {
15+
function whitelistControllerAggregator() external view returns (address);
16+
}
17+
18+
interface IWhitelistControllerAggregator {
19+
function isWhitelisted(address) external view returns (bool, address);
20+
}
21+
22+
/* VER_USDC INTERFACES */
23+
24+
error NoPermission(address account);
25+
26+
interface IPermissionedERC20Wrapper {
27+
function memberlist() external view returns (address);
28+
}
29+
30+
interface IMemberList {
31+
function isMember(address) external view returns (bool);
32+
}
33+
34+
/* TEST */
35+
36+
contract Erc20PermissionedWrappersForkTest is ForkTest {
37+
address internal immutable WBIB01 = getAddress("WBIB01");
38+
address internal immutable VER_USDC = getAddress("VER_USDC");
39+
40+
function setUp() public override {
41+
super.setUp();
42+
43+
if (block.chainid == 1) {
44+
_whitelistForWbib01(address(generalAdapter1));
45+
} else if (block.chainid == 8453) {
46+
_whitelistForVerUsdc(address(generalAdapter1));
47+
}
48+
}
49+
50+
function testWbib01NotUsableWithoutPermission(uint256 amount, address initiator) public onlyEthereum {
51+
vm.assume(initiator != address(0));
52+
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
53+
54+
deal(address(ERC20Wrapper(WBIB01).underlying()), address(generalAdapter1), amount);
55+
56+
bundle.push(_erc20WrapperDepositFor(address(WBIB01), amount));
57+
58+
vm.expectRevert(abi.encodeWithSelector(NonWhitelistedToAddress.selector, initiator));
59+
vm.prank(initiator);
60+
bundler3.multicall(bundle);
61+
}
62+
63+
function testWbibUsableWithPermission(uint256 amount, address initiator) public onlyEthereum {
64+
vm.assume(initiator != address(0));
65+
_whitelistForWbib01(initiator);
66+
67+
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
68+
69+
vm.prank(initiator);
70+
IERC20(WBIB01).approve(address(generalAdapter1), amount);
71+
72+
deal(address(ERC20Wrapper(WBIB01).underlying()), address(generalAdapter1), amount, true);
73+
74+
bundle.push(_erc20WrapperDepositFor(address(WBIB01), amount));
75+
bundle.push(_erc20TransferFrom(address(WBIB01), address(generalAdapter1), amount));
76+
77+
vm.prank(initiator);
78+
bundler3.multicall(bundle);
79+
80+
vm.assertGt(IERC20(WBIB01).balanceOf(address(generalAdapter1)), 0);
81+
}
82+
83+
function testVerUsdcNotUsableWithoutPermission(uint256 amount, address initiator) public onlyBase {
84+
vm.assume(initiator != address(0));
85+
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
86+
87+
deal(address(ERC20Wrapper(VER_USDC).underlying()), address(generalAdapter1), amount);
88+
89+
bundle.push(_erc20WrapperDepositFor(address(VER_USDC), amount));
90+
91+
vm.expectRevert("PermissionedERC20Wrapper/no-attestation-found");
92+
vm.prank(initiator);
93+
bundler3.multicall(bundle);
94+
}
95+
96+
function testVerUsdcUsableWithPermission(uint256 amount, address initiator) public onlyBase {
97+
vm.assume(initiator != address(0));
98+
_whitelistForVerUsdc(initiator);
99+
100+
amount = bound(amount, MIN_AMOUNT, MAX_AMOUNT);
101+
102+
vm.prank(initiator);
103+
IERC20(VER_USDC).approve(address(generalAdapter1), amount);
104+
105+
deal(address(ERC20Wrapper(VER_USDC).underlying()), address(generalAdapter1), amount, true);
106+
107+
bundle.push(_erc20WrapperDepositFor(address(VER_USDC), amount));
108+
bundle.push(_erc20TransferFrom(address(VER_USDC), address(generalAdapter1), amount));
109+
110+
vm.prank(initiator);
111+
bundler3.multicall(bundle);
112+
113+
vm.assertGt(IERC20(VER_USDC).balanceOf(address(generalAdapter1)), 0);
114+
}
115+
116+
/* WHITELISTING HELPERS */
117+
118+
function _whitelistForWbib01(address account) internal {
119+
address controller = IWrappedBackedToken(WBIB01).whitelistControllerAggregator();
120+
vm.mockCall(
121+
controller,
122+
abi.encodeCall(IWhitelistControllerAggregator.isWhitelisted, (account)),
123+
abi.encode(true, address(0))
124+
);
125+
}
126+
127+
function _whitelistForVerUsdc(address account) internal {
128+
address memberList = IPermissionedERC20Wrapper(VER_USDC).memberlist();
129+
vm.mockCall(memberList, abi.encodeCall(IMemberList.isMember, (account)), abi.encode(true));
130+
}
131+
}

test/fork/helpers/ForkTest.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ abstract contract ForkTest is CommonTest, NetworkConfig {
9898
_;
9999
}
100100

101+
modifier onlyBase() {
102+
vm.skip(block.chainid != 8453);
103+
_;
104+
}
105+
101106
function _randomMarketParams(uint256 seed) internal view returns (MarketParams memory) {
102107
return allMarketParams[seed % allMarketParams.length];
103108
}

test/fork/helpers/NetworkConfig.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ abstract contract NetworkConfig is CommonBase {
5555
setAddress("MORPHO_TOKEN", 0x58D97B57BB95320F9a05dC918Aef65434969c2B2);
5656
setAddress("AUGUSTUS_V6_2", 0x6A000F20005980200259B80c5102003040001068);
5757
setAddress("AUGUSTUS_REGISTRY", 0xa68bEA62Dc4034A689AA0F58A76681433caCa663);
58+
setAddress("WBIB01", 0xcA2A7068e551d5C4482eb34880b194E4b945712F);
5859

5960
/* BASE NETWORK */
6061
} else if (config.chainid == 8453) {
6162
config.network = "base";
62-
config.blockNumber = 14000000;
63+
config.blockNumber = 25641890;
6364
config.markets.push(ConfigMarket({collateralToken: "WETH", loanToken: "WETH", lltv: 800000000000000000}));
6465

6566
setAddress("DAI", 0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb);
@@ -70,6 +71,7 @@ abstract contract NetworkConfig is CommonBase {
7071
setAddress("C_WETH_V3", 0x46e6b214b524310239732D51387075E0e70970bf);
7172
setAddress("AUGUSTUS_V6_2", 0x6A000F20005980200259B80c5102003040001068);
7273
setAddress("AUGUSTUS_REGISTRY", 0x7E31B336F9E8bA52ba3c4ac861b033Ba90900bb3);
74+
setAddress("VER_USDC", 0x59aaF835D34b1E3dF2170e4872B785f11E2a964b);
7375
}
7476
}
7577

test/helpers/CommonTest.sol

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,8 @@ abstract contract CommonTest is Test {
245245

246246
/* ERC20 WRAPPER ACTIONS */
247247

248-
function _erc20WrapperDepositFor(address token, address receiver, uint256 amount)
249-
internal
250-
view
251-
returns (Call memory)
252-
{
253-
return _call(generalAdapter1, abi.encodeCall(GeneralAdapter1.erc20WrapperDepositFor, (token, receiver, amount)));
248+
function _erc20WrapperDepositFor(address token, uint256 amount) internal view returns (Call memory) {
249+
return _call(generalAdapter1, abi.encodeCall(GeneralAdapter1.erc20WrapperDepositFor, (token, amount)));
254250
}
255251

256252
function _erc20WrapperWithdrawTo(address token, address receiver, uint256 amount)

0 commit comments

Comments
 (0)