@@ -18,13 +18,16 @@ import {
18
18
import { IBaseRouter } from "src/interfaces/IBaseRouter.sol " ;
19
19
import { ISuperformRouterPlus, IERC20 } from "src/interfaces/ISuperformRouterPlus.sol " ;
20
20
import { ISuperformRouterPlusAsync } from "src/interfaces/ISuperformRouterPlusAsync.sol " ;
21
+ import { LiqRequest } from "src/types/DataTypes.sol " ;
22
+ import { IBridgeValidator } from "src/interfaces/IBridgeValidator.sol " ;
21
23
22
24
/// @title SuperformRouterPlus
23
25
/// @dev Performs rebalances and deposits on the Superform platform
24
26
/// @author Zeropoint Labs
25
27
contract SuperformRouterPlus is ISuperformRouterPlus , BaseSuperformRouterPlus {
26
28
using SafeERC20 for IERC20 ;
27
29
30
+ uint256 public GLOBAL_SLIPPAGE;
28
31
uint256 public ROUTER_PLUS_PAYLOAD_ID;
29
32
30
33
/// @dev Tolerance constant to account for tokens with rounding issues on transfer
@@ -34,7 +37,10 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus {
34
37
// CONSTRUCTOR //
35
38
//////////////////////////////////////////////////////////////
36
39
37
- constructor (address superRegistry_ ) BaseSuperformRouterPlus (superRegistry_) { }
40
+ constructor (address superRegistry_ ) BaseSuperformRouterPlus (superRegistry_) {
41
+ /// @dev default to 0.1% slippage as a start
42
+ GLOBAL_SLIPPAGE = 10 ;
43
+ }
38
44
39
45
//////////////////////////////////////////////////////////////
40
46
// EXTERNAL WRITE FUNCTIONS //
@@ -357,9 +363,8 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus {
357
363
358
364
/// @inheritdoc ISuperformRouterPlus
359
365
function deposit4626 (address [] calldata vaults_ , Deposit4626Args[] calldata args ) external payable {
360
-
361
366
uint256 length = vaults_.length ;
362
-
367
+
363
368
if (length != args.length ) {
364
369
revert Error.ARRAY_LENGTH_MISMATCH ();
365
370
}
@@ -390,6 +395,19 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus {
390
395
}
391
396
}
392
397
398
+ /// @inheritdoc ISuperformRouterPlus
399
+ function setGlobalSlippage (uint256 slippage_ ) external {
400
+ if (! _hasRole (keccak256 ("EMERGENCY_ADMIN_ROLE " ), msg .sender )) {
401
+ revert Error.NOT_PRIVILEGED_CALLER (keccak256 ("EMERGENCY_ADMIN_ROLE " ));
402
+ }
403
+
404
+ if (slippage_ > ENTIRE_SLIPPAGE || slippage_ == 0 ) {
405
+ revert INVALID_GLOBAL_SLIPPAGE ();
406
+ }
407
+
408
+ GLOBAL_SLIPPAGE = slippage_;
409
+ }
410
+
393
411
//////////////////////////////////////////////////////////////
394
412
// INTERNAL FUNCTIONS //
395
413
//////////////////////////////////////////////////////////////
@@ -439,6 +457,7 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus {
439
457
if (req.superformData.liqRequests[i].liqDstChainId != CHAIN_ID) {
440
458
revert REBALANCE_MULTI_POSITIONS_DIFFERENT_CHAIN ();
441
459
}
460
+
442
461
if (req.superformData.amounts[i] != args.sharesToRedeem[i]) {
443
462
revert REBALANCE_MULTI_POSITIONS_DIFFERENT_AMOUNTS ();
444
463
}
@@ -450,23 +469,29 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus {
450
469
/// @dev send SPs to router
451
470
_callSuperformRouter (router_, callData, args.rebalanceFromMsgValue);
452
471
453
- uint256 amountToDeposit = interimAsset.balanceOf (address (this )) - args.balanceBefore;
472
+ uint256 availableBalanceToDeposit = interimAsset.balanceOf (address (this )) - args.balanceBefore;
454
473
455
- if (amountToDeposit == 0 ) revert Error.ZERO_AMOUNT ();
474
+ if (availableBalanceToDeposit == 0 ) revert Error.ZERO_AMOUNT ();
456
475
457
476
if (
458
- ENTIRE_SLIPPAGE * amountToDeposit
477
+ ENTIRE_SLIPPAGE * availableBalanceToDeposit
459
478
< ((args.expectedAmountToReceivePostRebalanceFrom * (ENTIRE_SLIPPAGE - args.slippage)))
460
479
) {
461
480
revert Error.VAULT_IMPLEMENTATION_FAILED ();
462
481
}
463
482
464
- /// @dev step 3: rebalance into a new superform with rebalanceCallData
465
- if ( ! whitelistedSelectors[Actions.DEPOSIT][ _parseSelectorMem (rebalanceToCallData)]) {
466
- revert INVALID_DEPOSIT_SELECTOR ( );
467
- }
483
+ uint256 amountIn = _validateAndGetAmountIn (rebalanceToCallData, availableBalanceToDeposit);
484
+
485
+ _deposit (router_, interimAsset, amountIn, args.rebalanceToMsgValue, rebalanceToCallData );
486
+ }
468
487
469
- _deposit (router_, interimAsset, amountToDeposit, args.rebalanceToMsgValue, rebalanceToCallData);
488
+ function _takeAmountIn (LiqRequest memory liqReq , uint256 sfDataAmount ) internal view returns (uint256 amountIn ) {
489
+ bytes memory txData = liqReq.txData;
490
+ if (txData.length == 0 ) {
491
+ amountIn = sfDataAmount;
492
+ } else {
493
+ amountIn = IBridgeValidator (superRegistry.getBridgeValidator (liqReq.bridgeId)).decodeAmountIn (txData, false );
494
+ }
470
495
}
471
496
472
497
function _transferSuperPositions (
@@ -523,9 +548,7 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus {
523
548
if (assets < TOLERANCE_CONSTANT || balanceDifference < assets - TOLERANCE_CONSTANT) revert TOLERANCE_EXCEEDED ();
524
549
525
550
/// @dev validate the slippage
526
- if (
527
- (ENTIRE_SLIPPAGE * assets < ((expectedOutputAmount_ * (ENTIRE_SLIPPAGE - maxSlippage_))))
528
- ) {
551
+ if ((ENTIRE_SLIPPAGE * assets < ((expectedOutputAmount_ * (ENTIRE_SLIPPAGE - maxSlippage_))))) {
529
552
revert ASSETS_RECEIVED_OUT_OF_SLIPPAGE ();
530
553
}
531
554
}
@@ -604,7 +627,7 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus {
604
627
/// @notice deposits ERC4626 vault shares into superform
605
628
/// @param vault_ The ERC4626 vault to redeem from
606
629
/// @param args Rest of the arguments to deposit 4626
607
- function _deposit4626 (address vault_ , Deposit4626Args calldata args , uint256 arrayLength ) internal {
630
+ function _deposit4626 (address vault_ , Deposit4626Args calldata args , uint256 arrayLength ) internal {
608
631
_transferERC20In (IERC20 (vault_), args.receiverAddressSP, args.amount);
609
632
IERC4626 vault = IERC4626 (vault_);
610
633
address assetAdr = vault.asset ();
@@ -614,12 +637,88 @@ contract SuperformRouterPlus is ISuperformRouterPlus, BaseSuperformRouterPlus {
614
637
615
638
uint256 amountRedeemed = _redeemShare (vault, assetAdr, args.amount, args.expectedOutputAmount, args.maxSlippage);
616
639
640
+ uint256 amountIn = _validateAndGetAmountIn (args.depositCallData, amountRedeemed);
641
+
617
642
uint256 msgValue = msg .value / arrayLength;
618
643
address router = _getAddress (keccak256 ("SUPERFORM_ROUTER " ));
619
- _deposit (router, asset, amountRedeemed, msgValue, args.depositCallData);
644
+
645
+ _deposit (router, asset, amountIn, msgValue, args.depositCallData);
620
646
621
647
_tokenRefunds (router, assetAdr, args.receiverAddressSP, balanceBefore);
622
648
623
649
emit Deposit4626Completed (args.receiverAddressSP, vault_);
624
650
}
651
+
652
+ function _validateAndGetAmountIn (
653
+ bytes calldata rebalanceToCallData ,
654
+ uint256 availableBalanceToDeposit
655
+ )
656
+ internal
657
+ view
658
+ returns (uint256 amountIn )
659
+ {
660
+ bytes4 rebalanceToSelector = _parseSelectorMem (rebalanceToCallData);
661
+
662
+ if (! whitelistedSelectors[Actions.DEPOSIT][rebalanceToSelector]) {
663
+ revert INVALID_DEPOSIT_SELECTOR ();
664
+ }
665
+
666
+ uint256 amountInTemp;
667
+
668
+ if (rebalanceToSelector == IBaseRouter.singleDirectSingleVaultDeposit.selector ) {
669
+ SingleVaultSFData memory sfData =
670
+ abi.decode (_parseCallData (rebalanceToCallData), (SingleDirectSingleVaultStateReq)).superformData;
671
+ amountIn = _takeAmountIn (sfData.liqRequest, sfData.amount);
672
+ } else if (rebalanceToSelector == IBaseRouter.singleXChainSingleVaultDeposit.selector ) {
673
+ SingleVaultSFData memory sfData =
674
+ abi.decode (_parseCallData (rebalanceToCallData), (SingleXChainSingleVaultStateReq)).superformData;
675
+ amountIn = _takeAmountIn (sfData.liqRequest, sfData.amount);
676
+ } else if (rebalanceToSelector == IBaseRouter.singleDirectMultiVaultDeposit.selector ) {
677
+ MultiVaultSFData memory sfData =
678
+ abi.decode (_parseCallData (rebalanceToCallData), (SingleDirectMultiVaultStateReq)).superformData;
679
+ uint256 len = sfData.liqRequests.length ;
680
+
681
+ for (uint256 i; i < len; ++ i) {
682
+ amountInTemp = _takeAmountIn (sfData.liqRequests[i], sfData.amounts[i]);
683
+ amountIn += amountInTemp;
684
+ }
685
+ } else if (rebalanceToSelector == IBaseRouter.singleXChainMultiVaultDeposit.selector ) {
686
+ MultiVaultSFData memory sfData =
687
+ abi.decode (_parseCallData (rebalanceToCallData), (SingleXChainMultiVaultStateReq)).superformsData;
688
+ uint256 len = sfData.liqRequests.length ;
689
+ for (uint256 i; i < len; ++ i) {
690
+ amountInTemp = _takeAmountIn (sfData.liqRequests[i], sfData.amounts[i]);
691
+ amountIn += amountInTemp;
692
+ }
693
+ } else if (rebalanceToSelector == IBaseRouter.multiDstSingleVaultDeposit.selector ) {
694
+ SingleVaultSFData[] memory sfData =
695
+ abi.decode (_parseCallData (rebalanceToCallData), (MultiDstSingleVaultStateReq)).superformsData;
696
+ uint256 lenDst = sfData.length ;
697
+ for (uint256 i; i < lenDst; ++ i) {
698
+ amountInTemp = _takeAmountIn (sfData[i].liqRequest, sfData[i].amount);
699
+ amountIn += amountInTemp;
700
+ }
701
+ } else if (rebalanceToSelector == IBaseRouter.multiDstMultiVaultDeposit.selector ) {
702
+ MultiVaultSFData[] memory sfData =
703
+ abi.decode (_parseCallData (rebalanceToCallData), (MultiDstMultiVaultStateReq)).superformsData;
704
+ uint256 lenDst = sfData.length ;
705
+ for (uint256 i; i < lenDst; ++ i) {
706
+ uint256 len = sfData[i].liqRequests.length ;
707
+ for (uint256 j; j < len; ++ j) {
708
+ amountInTemp = _takeAmountIn (sfData[i].liqRequests[j], sfData[i].amounts[j]);
709
+ amountIn += amountInTemp;
710
+ }
711
+ }
712
+ }
713
+
714
+ /// @dev amountIn must be artificially off-chain reduced to be less than availableBalanceToDeposit otherwise the
715
+ /// @dev approval to transfer tokens to SuperformRouter won't work
716
+ if (amountIn > availableBalanceToDeposit) revert AMOUNT_IN_NOT_EQUAL_OR_LOWER_THAN_BALANCE ();
717
+
718
+ /// @dev check amountIn against availableBalanceToDeposit (available balance) via a GLOBAL_SLIPPAGE to prevent a
719
+ /// @dev malicious keeper from sending a low amountIn
720
+ if (ENTIRE_SLIPPAGE * amountIn < ((availableBalanceToDeposit * (ENTIRE_SLIPPAGE - GLOBAL_SLIPPAGE)))) {
721
+ revert ASSETS_RECEIVED_OUT_OF_SLIPPAGE ();
722
+ }
723
+ }
625
724
}
0 commit comments