Skip to content

Commit d013286

Browse files
Merge branch 'dev' into dev-1149-weethModule-proxy
2 parents dd48227 + 5c67f4e commit d013286

File tree

6 files changed

+103
-39
lines changed

6 files changed

+103
-39
lines changed

src/MainnetController.sol

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { ERC4626Lib } from "./libraries/ERC4626Lib.sol";
2424
import { LayerZeroLib } from "./libraries/LayerZeroLib.sol";
2525
import { IDaiUsdsLike, IPSMLike, PSMLib } from "./libraries/PSMLib.sol";
2626
import { UniswapV4Lib } from "./libraries/UniswapV4Lib.sol";
27-
import { WeETHLib } from "./libraries/WeETHLib.sol";
27+
import { WEETHLib } from "./libraries/WEETHLib.sol";
2828

2929
import { RateLimitHelpers } from "./RateLimitHelpers.sol";
3030

@@ -165,9 +165,9 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
165165
bytes32 public LIMIT_USDE_MINT = keccak256("LIMIT_USDE_MINT");
166166
bytes32 public LIMIT_USDS_MINT = keccak256("LIMIT_USDS_MINT");
167167
bytes32 public LIMIT_USDS_TO_USDC = keccak256("LIMIT_USDS_TO_USDC");
168-
bytes32 public LIMIT_WEETH_CLAIM_WITHDRAW = WeETHLib.LIMIT_WEETH_CLAIM_WITHDRAW;
169-
bytes32 public LIMIT_WEETH_DEPOSIT = WeETHLib.LIMIT_WEETH_DEPOSIT;
170-
bytes32 public LIMIT_WEETH_REQUEST_WITHDRAW = WeETHLib.LIMIT_WEETH_REQUEST_WITHDRAW;
168+
bytes32 public LIMIT_WEETH_CLAIM_WITHDRAW = WEETHLib.LIMIT_WEETH_CLAIM_WITHDRAW;
169+
bytes32 public LIMIT_WEETH_DEPOSIT = WEETHLib.LIMIT_WEETH_DEPOSIT;
170+
bytes32 public LIMIT_WEETH_REQUEST_WITHDRAW = WEETHLib.LIMIT_WEETH_REQUEST_WITHDRAW;
171171
bytes32 public LIMIT_WSTETH_DEPOSIT = keccak256("LIMIT_WSTETH_DEPOSIT");
172172
bytes32 public LIMIT_WSTETH_REQUEST_WITHDRAW = keccak256("LIMIT_WSTETH_REQUEST_WITHDRAW");
173173

@@ -281,7 +281,7 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
281281

282282
// Prevent rotating buffer while a swap is pending and not ready
283283
require(otc.sentTimestamp == 0 || isOtcSwapReady(exchange), "MC/swap-in-progress");
284-
284+
285285
emit OTCBufferSet(exchange, otc.buffer, otcBuffer);
286286
otc.buffer = otcBuffer;
287287
}
@@ -493,13 +493,19 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
493493
/*** weETH Integration ***/
494494
/**********************************************************************************************/
495495

496-
function depositToWeETH(uint256 amount) external nonReentrant returns (uint256 shares) {
496+
function depositToWeETH(
497+
uint256 amount,
498+
uint256 minSharesOut
499+
)
500+
external nonReentrant returns (uint256 shares)
501+
{
497502
_checkRole(RELAYER);
498503

499-
shares = WeETHLib.deposit({
500-
proxy : proxy,
501-
rateLimits : rateLimits,
502-
amount : amount
504+
shares = WEETHLib.deposit({
505+
proxy : proxy,
506+
rateLimits : rateLimits,
507+
amount : amount,
508+
minSharesOut : minSharesOut
503509
});
504510
}
505511

@@ -511,7 +517,7 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
511517
{
512518
_checkRole(RELAYER);
513519

514-
requestId = WeETHLib.requestWithdraw({
520+
requestId = WEETHLib.requestWithdraw({
515521
proxy : proxy,
516522
rateLimits : rateLimits,
517523
weETHShares : weETHShares,
@@ -527,7 +533,7 @@ contract MainnetController is ReentrancyGuard, AccessControlEnumerable {
527533
{
528534
_checkRole(RELAYER);
529535

530-
ethReceived = WeETHLib.claimWithdrawal({
536+
ethReceived = WEETHLib.claimWithdrawal({
531537
proxy : proxy,
532538
rateLimits : rateLimits,
533539
requestId : requestId,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import { UUPSUpgradeable } from "openzeppelin-contracts-upgradeable/contracts/pr
1010

1111
import { Ethereum } from "spark-address-registry/Ethereum.sol";
1212

13-
import { IEETHLike, ILiquidityPoolLike, IWETHLike, IWEETHLike } from "./libraries/WeETHLib.sol";
13+
import { IEETHLike, ILiquidityPoolLike, IWETHLike, IWEETHLike } from "./libraries/WEETHLib.sol";
1414

1515
interface IWithdrawRequestNFTLike {
1616
function claimWithdraw(uint256 requestId) external;
1717
function isFinalized(uint256 requestId) external view returns (bool);
1818
function isValid(uint256 requestId) external view returns (bool);
1919
}
2020

21-
contract WeEthModule is AccessControlEnumerableUpgradeable, UUPSUpgradeable {
21+
contract WEETHModule is AccessControlEnumerableUpgradeable, UUPSUpgradeable {
2222

2323
address public almProxy;
2424

src/libraries/LayerZeroLib.sol

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
// SPDX-License-Identifier: AGPL-3.0-or-later
22
pragma solidity ^0.8.21;
33

4-
import { ILayerZero, SendParam, OFTReceipt, MessagingFee } from "../interfaces/ILayerZero.sol";
5-
import { IRateLimits } from "../interfaces/IRateLimits.sol";
6-
import { IALMProxy } from "../interfaces/IALMProxy.sol";
4+
import {
5+
ILayerZero,
6+
SendParam,
7+
OFTReceipt,
8+
MessagingFee,
9+
OFTLimit,
10+
OFTFeeDetail
11+
} from "../interfaces/ILayerZero.sol";
12+
13+
import { IRateLimits } from "../interfaces/IRateLimits.sol";
14+
import { IALMProxy } from "../interfaces/IALMProxy.sol";
715

816
import { ApproveLib } from "./ApproveLib.sol";
917

@@ -12,7 +20,7 @@ import { OptionsBuilder } from "layerzerolabs/oapp-evm/contracts/oapp/libs/Optio
1220
library LayerZeroLib {
1321

1422
using OptionsBuilder for bytes;
15-
23+
1624
bytes32 public constant LIMIT_LAYERZERO_TRANSFER = keccak256("LIMIT_LAYERZERO_TRANSFER");
1725

1826
/**********************************************************************************************/
@@ -66,7 +74,14 @@ library LayerZeroLib {
6674
});
6775

6876
// Query the min amount received on the destination chain and set it.
69-
( , , OFTReceipt memory receipt ) = ILayerZero(oftAddress).quoteOFT(sendParams);
77+
( ,, OFTReceipt memory receipt ) = abi.decode(
78+
proxy.doCall(
79+
oftAddress,
80+
abi.encodeCall(ILayerZero.quoteOFT, (sendParams))
81+
),
82+
(OFTLimit, OFTFeeDetail[], OFTReceipt)
83+
);
84+
7085
sendParams.minAmountLD = receipt.amountReceivedLD;
7186

7287
MessagingFee memory fee = ILayerZero(oftAddress).quoteSend(sendParams, false);

src/libraries/PSMLib.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ library PSMLib {
126126
/*** Helper functions ***/
127127
/**********************************************************************************************/
128128

129-
// NOTE: As swaps are only done between USDC and USDS and vice versa, using `_forceApprove`
129+
// NOTE: As swaps are only done between USDC and USDS and vice versa, using `_forceApprove`
130130
// is unnecessary.
131131
function _approve(
132132
IALMProxy proxy,
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ interface IWETHLike {
4040
function withdraw(uint256 amount) external;
4141
}
4242

43-
library WeETHLib {
43+
library WEETHLib {
4444

4545
bytes32 public constant LIMIT_WEETH_CLAIM_WITHDRAW = keccak256("LIMIT_WEETH_CLAIM_WITHDRAW");
4646
bytes32 public constant LIMIT_WEETH_DEPOSIT = keccak256("LIMIT_WEETH_DEPOSIT");
@@ -53,7 +53,8 @@ library WeETHLib {
5353
function deposit(
5454
IALMProxy proxy,
5555
IRateLimits rateLimits,
56-
uint256 amount
56+
uint256 amount,
57+
uint256 minSharesOut
5758
) external returns (uint256 shares) {
5859
_rateLimited(rateLimits, LIMIT_WEETH_DEPOSIT, amount);
5960

@@ -88,6 +89,8 @@ library WeETHLib {
8889
),
8990
(uint256)
9091
);
92+
93+
require(shares >= minSharesOut, "MC/slippage-too-high");
9194
}
9295

9396
function requestWithdraw(

test/mainnet-fork/weETH.t.sol

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@ import { ReentrancyGuard } from "../../lib/openzeppelin-contracts/contracts/util
55

66
import { ERC1967Proxy } from "../../lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";
77

8-
import { IWEETHLike, ILiquidityPoolLike, IEETHLike } from "../../src/libraries/WeETHLib.sol";
8+
import { IWEETHLike, IEETHLike } from "../../src/libraries/WeETHLib.sol";
99

10-
import { WeEthModule } from "../../src/WeEthModule.sol";
10+
import { WEETHModule } from "../../src/WEETHModule.sol";
1111

1212
import "./ForkTestBase.t.sol";
1313

14+
interface ILiquidityPoolLike {
15+
function amountForShare(uint256 shareAmount) external view returns (uint256);
16+
function sharesForAmount(uint256 amount) external view returns (uint256);
17+
function withdrawRequestNFT() external view returns (address);
18+
}
19+
1420
interface IWithdrawRequestNFTLike {
1521
function finalizeRequests(uint256 requestId) external;
1622
function getClaimableAmount(uint256 requestId) external view returns (uint256);
@@ -43,9 +49,9 @@ contract MainnetControllerWeETHTestBase is ForkTestBase {
4349

4450
weETHModule = address(
4551
new ERC1967Proxy(
46-
address(new WeEthModule()),
52+
address(new WEETHModule()),
4753
abi.encodeCall(
48-
WeEthModule.initialize,
54+
WEETHModule.initialize,
4955
(Ethereum.SPARK_PROXY, address(almProxy))
5056
)
5157
)
@@ -56,14 +62,18 @@ contract MainnetControllerWeETHTestBase is ForkTestBase {
5662
return 23469772; // September 29, 2025
5763
}
5864

65+
function _getMinSharesOut(uint256 amount) internal view returns (uint256) {
66+
return liquidityPool.sharesForAmount(amount) - 1;
67+
}
68+
5969
}
6070

6171
contract MainnetControllerDepositToWeETHFailureTests is MainnetControllerWeETHTestBase {
6272

6373
function test_depositToWeETH_reentrancy() external {
6474
_setControllerEntered();
6575
vm.expectRevert(ReentrancyGuard.ReentrancyGuardReentrantCall.selector);
66-
mainnetController.depositToWeETH(1e18);
76+
mainnetController.depositToWeETH(1e18, 0);
6777
}
6878

6979
function test_depositToWeETH_notRelayer() external {
@@ -72,13 +82,13 @@ contract MainnetControllerDepositToWeETHFailureTests is MainnetControllerWeETHTe
7282
address(this),
7383
RELAYER
7484
));
75-
mainnetController.depositToWeETH(1e18);
85+
mainnetController.depositToWeETH(1e18, 0);
7686
}
7787

7888
function test_depositToWeETH_zeroMaxAmount() external {
7989
vm.prank(relayer);
8090
vm.expectRevert("RateLimits/zero-maxAmount");
81-
mainnetController.depositToWeETH(1e18);
91+
mainnetController.depositToWeETH(1e18, 0);
8292
}
8393

8494
function test_depositToWeETH_rateLimitsBoundary() external {
@@ -91,10 +101,28 @@ contract MainnetControllerDepositToWeETHFailureTests is MainnetControllerWeETHTe
91101

92102
vm.prank(relayer);
93103
vm.expectRevert("RateLimits/rate-limit-exceeded");
94-
mainnetController.depositToWeETH(1_000e18 + 1);
104+
mainnetController.depositToWeETH(1_000e18 + 1, 0);
95105

96106
vm.prank(relayer);
97-
mainnetController.depositToWeETH(1_000e18);
107+
mainnetController.depositToWeETH(1_000e18, 0);
108+
}
109+
110+
function test_depositToWeETH_slippageTooHighBoundary() external {
111+
bytes32 key = mainnetController.LIMIT_WEETH_DEPOSIT();
112+
113+
vm.prank(Ethereum.SPARK_PROXY);
114+
rateLimits.setRateLimitData(key, 1_000e18, uint256(1_000e18) / 1 days);
115+
116+
deal(Ethereum.WETH, address(almProxy), 1_000e18);
117+
118+
uint256 minSharesOut = _getMinSharesOut(1_000e18);
119+
120+
vm.prank(relayer);
121+
vm.expectRevert("MC/slippage-too-high");
122+
mainnetController.depositToWeETH(1_000e18, minSharesOut + 1);
123+
124+
vm.prank(relayer);
125+
mainnetController.depositToWeETH(1_000e18, minSharesOut);
98126
}
99127

100128
}
@@ -119,10 +147,12 @@ contract MainnetControllerDepositToWeETHTests is MainnetControllerWeETHTestBase
119147
assertEq(weETH.balanceOf(address(almProxy)), 0);
120148
assertEq(address(liquidityPool).balance, initialLiquidityPoolBalance);
121149

150+
uint256 minSharesOut = _getMinSharesOut(1_000e18);
151+
122152
vm.record();
123153

124154
vm.prank(relayer);
125-
uint256 shares = mainnetController.depositToWeETH(1_000e18);
155+
uint256 shares = mainnetController.depositToWeETH(1_000e18, minSharesOut);
126156

127157
_assertReentrancyGuardWrittenToTwice();
128158

@@ -207,8 +237,10 @@ contract MainnetControllerRequestWithdrawFromWeETHTests is MainnetControllerWeET
207237

208238
deal(Ethereum.WETH, address(almProxy), 1_000e18);
209239

240+
uint256 minSharesOut = _getMinSharesOut(1_000e18);
241+
210242
vm.prank(relayer);
211-
mainnetController.depositToWeETH(1_000e18);
243+
mainnetController.depositToWeETH(1_000e18, minSharesOut);
212244

213245
uint256 initialWeETHBalance = weETH.balanceOf(address(almProxy));
214246

@@ -239,7 +271,7 @@ contract MainnetControllerRequestWithdrawFromWeETHTests is MainnetControllerWeET
239271

240272
vm.prank(WITHDRAW_REQUEST_NFT_ADMIN);
241273
IWithdrawRequestNFTLike(withdrawRequestNFT).finalizeRequests(requestId);
242-
274+
243275
assertEq(withdrawRequestNFT.isFinalized(requestId), true);
244276
assertEq(withdrawRequestNFT.getClaimableAmount(requestId), expectedEEthBalance - 1); // Rounding error
245277

@@ -284,8 +316,10 @@ contract MainnetControllerClaimWithdrawalFromWeETHFailureTests is MainnetControl
284316

285317
deal(Ethereum.WETH, address(almProxy), 1_000e18);
286318

319+
uint256 minSharesOut = _getMinSharesOut(1_000e18);
320+
287321
vm.prank(relayer);
288-
mainnetController.depositToWeETH(1_000e18);
322+
mainnetController.depositToWeETH(1_000e18, minSharesOut);
289323

290324
vm.record();
291325

@@ -331,8 +365,10 @@ contract MainnetControllerClaimWithdrawalFromWeETHFailureTests is MainnetControl
331365

332366
deal(Ethereum.WETH, address(almProxy), 1_000e18);
333367

368+
uint256 minSharesOut = _getMinSharesOut(1_000e18);
369+
334370
vm.prank(relayer);
335-
mainnetController.depositToWeETH(1_000e18);
371+
mainnetController.depositToWeETH(1_000e18, minSharesOut);
336372

337373
vm.record();
338374

@@ -345,7 +381,7 @@ contract MainnetControllerClaimWithdrawalFromWeETHFailureTests is MainnetControl
345381

346382
vm.prank(WITHDRAW_REQUEST_NFT_ADMIN);
347383
IWithdrawRequestNFTLike(withdrawRequestNFT).invalidateRequest(requestId);
348-
384+
349385
vm.prank(relayer);
350386
vm.expectRevert("WeEthModule/invalid-request-id");
351387
mainnetController.claimWithdrawalFromWeETH(weETHModule, requestId);
@@ -372,8 +408,10 @@ contract MainnetControllerClaimWithdrawalFromWeETHFailureTests is MainnetControl
372408

373409
deal(Ethereum.WETH, address(almProxy), 1_000e18);
374410

411+
uint256 minSharesOut = _getMinSharesOut(1_000e18);
412+
375413
vm.prank(relayer);
376-
mainnetController.depositToWeETH(1_000e18);
414+
mainnetController.depositToWeETH(1_000e18, minSharesOut);
377415

378416
vm.record();
379417

@@ -415,8 +453,10 @@ contract MainnetControllerClaimWithdrawalFromWeETHTests is MainnetControllerWeET
415453

416454
deal(Ethereum.WETH, address(almProxy), 1_000e18);
417455

456+
uint256 minSharesOut = _getMinSharesOut(1_000e18);
457+
418458
vm.prank(relayer);
419-
mainnetController.depositToWeETH(1_000e18);
459+
mainnetController.depositToWeETH(1_000e18, minSharesOut);
420460

421461
vm.record();
422462

0 commit comments

Comments
 (0)