@@ -29,8 +29,31 @@ interface IBuidlRedeemLike {
29
29
function redeem (uint256 usdcAmount ) external ;
30
30
}
31
31
32
+ interface ICurvePoolLike {
33
+ function add_liquidity (
34
+ uint256 [] memory amounts ,
35
+ uint256 minMintAmount ,
36
+ address receiver
37
+ ) external ;
38
+ function coins (uint256 index ) external returns (address );
39
+ function exchange (
40
+ int128 inputIndex ,
41
+ int128 outputIndex ,
42
+ uint256 amountIn ,
43
+ uint256 minAmountOut ,
44
+ address receiver
45
+ ) external returns (uint256 tokensOut );
46
+ function N_COINS () external view returns (uint256 );
47
+ function remove_liquidity (
48
+ uint256 burnAmount ,
49
+ uint256 [] memory minAmounts ,
50
+ address receiver
51
+ ) external ;
52
+ function stored_rates () external view returns (uint256 [] memory );
53
+ }
54
+
32
55
interface IDaiUsdsLike {
33
- function dai () external view returns (address );
56
+ function dai () external view returns (address );
34
57
function daiToUsds (address usr , uint256 wad ) external ;
35
58
function usdsToDai (address usr , uint256 wad ) external ;
36
59
}
@@ -57,7 +80,7 @@ interface IMapleTokenLike is IERC4626 {
57
80
interface IPSMLike {
58
81
function buyGemNoFee (address usr , uint256 usdcAmount ) external returns (uint256 usdsAmount );
59
82
function fill () external returns (uint256 wad );
60
- function gem () external view returns (address );
83
+ function gem () external view returns (address );
61
84
function sellGemNoFee (address usr , uint256 usdcAmount ) external returns (uint256 usdsAmount );
62
85
function to18ConversionFactor () external view returns (uint256 );
63
86
}
@@ -79,7 +102,7 @@ interface IUSTBLike is IERC20 {
79
102
}
80
103
81
104
interface IVaultLike {
82
- function buffer () external view returns (address );
105
+ function buffer () external view returns (address );
83
106
function draw (uint256 usdsAmount ) external ;
84
107
function wipe (uint256 usdsAmount ) external ;
85
108
}
@@ -98,8 +121,8 @@ contract MainnetController is AccessControl {
98
121
uint256 usdcAmount
99
122
);
100
123
124
+ event MaxSlippageSet (address indexed pool , uint256 maxSlippage );
101
125
event MintRecipientSet (uint32 indexed destinationDomain , bytes32 mintRecipient );
102
-
103
126
event RelayerRemoved (address indexed relayer );
104
127
105
128
/**********************************************************************************************/
@@ -117,6 +140,9 @@ contract MainnetController is AccessControl {
117
140
bytes32 public constant LIMIT_AAVE_WITHDRAW = keccak256 ("LIMIT_AAVE_WITHDRAW " );
118
141
bytes32 public constant LIMIT_ASSET_TRANSFER = keccak256 ("LIMIT_ASSET_TRANSFER " );
119
142
bytes32 public constant LIMIT_BUIDL_REDEEM_CIRCLE = keccak256 ("LIMIT_BUIDL_REDEEM_CIRCLE " );
143
+ bytes32 public constant LIMIT_CURVE_DEPOSIT = keccak256 ("LIMIT_CURVE_DEPOSIT " );
144
+ bytes32 public constant LIMIT_CURVE_SWAP = keccak256 ("LIMIT_CURVE_SWAP " );
145
+ bytes32 public constant LIMIT_CURVE_WITHDRAW = keccak256 ("LIMIT_CURVE_WITHDRAW " );
120
146
bytes32 public constant LIMIT_MAPLE_REDEEM = keccak256 ("LIMIT_MAPLE_REDEEM " );
121
147
bytes32 public constant LIMIT_SUPERSTATE_REDEEM = keccak256 ("LIMIT_SUPERSTATE_REDEEM " );
122
148
bytes32 public constant LIMIT_SUPERSTATE_SUBSCRIBE = keccak256 ("LIMIT_SUPERSTATE_SUBSCRIBE " );
@@ -149,6 +175,8 @@ contract MainnetController is AccessControl {
149
175
150
176
uint256 public immutable psmTo18ConversionFactor;
151
177
178
+ mapping (address pool = > uint256 maxSlippage ) public maxSlippages; // 1e18 precision
179
+
152
180
mapping (uint32 destinationDomain = > bytes32 mintRecipient ) public mintRecipients;
153
181
154
182
/**********************************************************************************************/
@@ -198,6 +226,12 @@ contract MainnetController is AccessControl {
198
226
emit MintRecipientSet (destinationDomain, mintRecipient);
199
227
}
200
228
229
+ function setMaxSlippage (address pool , uint256 maxSlippage ) external {
230
+ _checkRole (DEFAULT_ADMIN_ROLE);
231
+ maxSlippages[pool] = maxSlippage;
232
+ emit MaxSlippageSet (pool, maxSlippage);
233
+ }
234
+
201
235
/**********************************************************************************************/
202
236
/*** Freezer functions ***/
203
237
/**********************************************************************************************/
@@ -511,6 +545,182 @@ contract MainnetController is AccessControl {
511
545
);
512
546
}
513
547
548
+ /**********************************************************************************************/
549
+ /*** Relayer Curve StableSwap functions ***/
550
+ /**********************************************************************************************/
551
+
552
+ function swapCurve (
553
+ address pool ,
554
+ uint256 inputIndex ,
555
+ uint256 outputIndex ,
556
+ uint256 amountIn ,
557
+ uint256 minAmountOut
558
+ )
559
+ external returns (uint256 amountOut )
560
+ {
561
+ _checkRole (RELAYER);
562
+
563
+ uint256 maxSlippage = maxSlippages[pool];
564
+
565
+ require (maxSlippage != 0 , "MainnetController/max-slippage-not-set " );
566
+
567
+ ICurvePoolLike curvePool = ICurvePoolLike (pool);
568
+
569
+ // Normalized to provide 36 decimal precision when multiplied by asset amount
570
+ uint256 [] memory rates = curvePool.stored_rates ();
571
+
572
+ // Below code is simplified from the following:
573
+ // valueIn = amountIn * rates[inputIndex] / 1e18 // 18 decimal precision, USD
574
+ // tokensOut = valueIn * 1e18 / rates[outputIndex] // Token precision, token amount
575
+ // result = tokensOut * maxSlippage / 1e18
576
+ uint256 minimumMinAmountOut = (amountIn * rates[inputIndex] / rates[outputIndex])
577
+ * maxSlippage
578
+ / 1e18 ;
579
+
580
+ require (
581
+ minAmountOut >= minimumMinAmountOut,
582
+ "MainnetController/min-amount-not-met "
583
+ );
584
+
585
+ rateLimits.triggerRateLimitDecrease (
586
+ RateLimitHelpers.makeAssetKey (LIMIT_CURVE_SWAP, pool),
587
+ amountIn * rates[inputIndex] / 1e18
588
+ );
589
+
590
+ _approve (curvePool.coins (inputIndex), pool, amountIn);
591
+
592
+ amountOut = abi.decode (
593
+ proxy.doCall (
594
+ pool,
595
+ abi.encodeCall (
596
+ curvePool.exchange,
597
+ (
598
+ int128 (int256 (inputIndex)), // Assuming safe cast because of 8 token max
599
+ int128 (int256 (outputIndex)), // Assuming safe cast because of 8 token max
600
+ amountIn,
601
+ minAmountOut,
602
+ address (proxy)
603
+ )
604
+ )
605
+ ),
606
+ (uint256 )
607
+ );
608
+ }
609
+
610
+ function addLiquidityCurve (
611
+ address pool ,
612
+ uint256 [] memory depositAmounts ,
613
+ uint256 minLpAmount
614
+ )
615
+ external returns (uint256 shares )
616
+ {
617
+ _checkRole (RELAYER);
618
+
619
+ uint256 maxSlippage = maxSlippages[pool];
620
+
621
+ require (maxSlippage != 0 , "MainnetController/max-slippage-not-set " );
622
+
623
+ ICurvePoolLike curvePool = ICurvePoolLike (pool);
624
+
625
+ uint256 numCoins = curvePool.N_COINS ();
626
+
627
+ require (depositAmounts.length == numCoins, "MainnetController/invalid-deposit-amounts " );
628
+
629
+ // Normalized to provide 36 decimal precision when multiplied by asset amount
630
+ uint256 [] memory rates = curvePool.stored_rates ();
631
+
632
+ // Aggregate the value of the deposited assets (e.g. USD)
633
+ uint256 valueDeposited;
634
+ for (uint256 i = 0 ; i < depositAmounts.length ; i++ ) {
635
+ _approve (curvePool.coins (i), pool, depositAmounts[i]);
636
+ valueDeposited += depositAmounts[i] * rates[i] / 1e18 ;
637
+ }
638
+
639
+ // Ensure minimum LP amount expected is greater than max slippage amount
640
+ // (assumes that the pool assets are pegged to the same value (e.g. USD))
641
+ require (
642
+ minLpAmount >= valueDeposited * maxSlippage / 1e18 ,
643
+ "MainnetController/min-amount-not-met "
644
+ );
645
+
646
+ // Reduce the rate limit by the aggregated underlying asset value of the deposit (e.g. USD)
647
+ rateLimits.triggerRateLimitDecrease (
648
+ RateLimitHelpers.makeAssetKey (LIMIT_CURVE_DEPOSIT, pool),
649
+ valueDeposited
650
+ );
651
+
652
+ shares = abi.decode (
653
+ proxy.doCall (
654
+ pool,
655
+ abi.encodeCall (
656
+ curvePool.add_liquidity,
657
+ (depositAmounts, minLpAmount, address (proxy))
658
+ )
659
+ ),
660
+ (uint256 )
661
+ );
662
+ }
663
+
664
+ function removeLiquidityCurve (
665
+ address pool ,
666
+ uint256 lpBurnAmount ,
667
+ uint256 [] memory minWithdrawAmounts
668
+ )
669
+ external returns (uint256 [] memory withdrawnTokens )
670
+ {
671
+ _checkRole (RELAYER);
672
+
673
+ uint256 maxSlippage = maxSlippages[pool];
674
+
675
+ require (maxSlippage != 0 , "MainnetController/max-slippage-not-set " );
676
+
677
+ ICurvePoolLike curvePool = ICurvePoolLike (pool);
678
+
679
+ uint256 numCoins = curvePool.N_COINS ();
680
+
681
+ require (
682
+ minWithdrawAmounts.length == numCoins,
683
+ "MainnetController/invalid-min-withdraw-amounts "
684
+ );
685
+
686
+ // Normalized to provide 36 decimal precision when multiplied by asset amount
687
+ uint256 [] memory rates = curvePool.stored_rates ();
688
+
689
+ // Aggregate the minimum values of the withdrawn assets (e.g. USD)
690
+ uint256 valueMinWithdrawn;
691
+ for (uint256 i = 0 ; i < numCoins; i++ ) {
692
+ valueMinWithdrawn += minWithdrawAmounts[i] * rates[i] / 1e18 ;
693
+ }
694
+
695
+ // Check that the aggregated minimums are greater than the max slippage amount
696
+ require (
697
+ valueMinWithdrawn >= lpBurnAmount * maxSlippage / 1e18 ,
698
+ "MainnetController/min-amount-not-met "
699
+ );
700
+
701
+ withdrawnTokens = abi.decode (
702
+ proxy.doCall (
703
+ pool,
704
+ abi.encodeCall (
705
+ curvePool.remove_liquidity,
706
+ (lpBurnAmount, minWithdrawAmounts, address (proxy))
707
+ )
708
+ ),
709
+ (uint256 [])
710
+ );
711
+
712
+ // Aggregate value withdrawn to reduce the rate limit
713
+ uint256 valueWithdrawn;
714
+ for (uint256 i = 0 ; i < withdrawnTokens.length ; i++ ) {
715
+ valueWithdrawn += withdrawnTokens[i] * rates[i] / 1e18 ;
716
+ }
717
+
718
+ rateLimits.triggerRateLimitDecrease (
719
+ RateLimitHelpers.makeAssetKey (LIMIT_CURVE_WITHDRAW, pool),
720
+ valueWithdrawn
721
+ );
722
+ }
723
+
514
724
/**********************************************************************************************/
515
725
/*** Relayer Ethena functions ***/
516
726
/**********************************************************************************************/
@@ -611,41 +821,6 @@ contract MainnetController is AccessControl {
611
821
);
612
822
}
613
823
614
- /**********************************************************************************************/
615
- /*** Relayer Morpho functions ***/
616
- /**********************************************************************************************/
617
-
618
- function setSupplyQueueMorpho (address morphoVault , Id[] memory newSupplyQueue ) external {
619
- _checkRole (RELAYER);
620
- _rateLimitExists (RateLimitHelpers.makeAssetKey (LIMIT_4626_DEPOSIT, morphoVault));
621
-
622
- proxy.doCall (
623
- morphoVault,
624
- abi.encodeCall (IMetaMorpho (morphoVault).setSupplyQueue, (newSupplyQueue))
625
- );
626
- }
627
-
628
- function updateWithdrawQueueMorpho (address morphoVault , uint256 [] calldata indexes ) external {
629
- _checkRole (RELAYER);
630
- _rateLimitExists (RateLimitHelpers.makeAssetKey (LIMIT_4626_DEPOSIT, morphoVault));
631
- proxy.doCall (
632
- morphoVault,
633
- abi.encodeCall (IMetaMorpho (morphoVault).updateWithdrawQueue, (indexes))
634
- );
635
- }
636
-
637
- function reallocateMorpho (address morphoVault , MarketAllocation[] calldata allocations )
638
- external
639
- {
640
- _checkRole (RELAYER);
641
- _rateLimitExists (RateLimitHelpers.makeAssetKey (LIMIT_4626_DEPOSIT, morphoVault));
642
-
643
- proxy.doCall (
644
- morphoVault,
645
- abi.encodeCall (IMetaMorpho (morphoVault).reallocate, (allocations))
646
- );
647
- }
648
-
649
824
/**********************************************************************************************/
650
825
/*** Relayer Superstate functions ***/
651
826
/**********************************************************************************************/
0 commit comments