RefinanceModule

Description:

Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/infinite-proxy/interfaces/IProxy.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IProxy {
    function setAdmin(address newAdmin_) external;

    function setDummyImplementation(address newDummyImplementation_) external;

    function addImplementation(
        address implementation_,
        bytes4[] calldata sigs_
    ) external;

    function removeImplementation(address implementation_) external;

    function getAdmin() external view returns (address);

    function getDummyImplementation() external view returns (address);

    function getImplementationSigs(
        address impl_
    ) external view returns (bytes4[] memory);

    function getSigsImplementation(bytes4 sig_) external view returns (address);

    function readFromStorage(
        bytes32 slot_
    ) external view returns (uint256 result_);
}
"
    },
    "contracts/vault/common/interfaces/IDSA.sol": {
      "content": "//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IDSA {
    function cast(
        string[] calldata _targetNames,
        bytes[] calldata _datas,
        address _origin
    ) external payable returns (bytes32);
}
"
    },
    "contracts/vault/common/interfaces/IToken.sol": {
      "content": "//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IToken {
    function approve(address, uint256) external;

    function transfer(address, uint) external;

    function transferFrom(address, address, uint) external;

    function deposit() external payable;

    function withdraw(uint) external;

    function balanceOf(address) external view returns (uint);

    function decimals() external view returns (uint);

    function totalSupply() external view returns (uint);

    function allowance(
        address owner,
        address spender
    ) external view returns (uint256);
}
"
    },
    "contracts/vault/common/interfaces/IVaultV3.sol": {
      "content": "//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IVaultV3 {
    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
    function getWithdrawFee(uint256 amount_) external view returns (uint256);
    function getProtocolRatio(uint8 protocolId_) external view returns (uint256 ratio_);
    function getNetAssets() external view returns (uint256 totalAssets_, uint256 totalDebt_, uint256 netAssets_, uint256 aggregatedRatio_);
    function getTokenExchangeRate(address tokenAddress_) external view returns (uint256 exchangeRate_);
}
"
    },
    "contracts/vault/common/variables/constants.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

contract Constants {
    address internal constant _TEAM_MULTISIG = 0x4F6F977aCDD1177DCD81aB83074855EcB9C2D49e;
    address internal constant _INSTA_INDEX_ADDRESS = 0x2971AdFa57b20E5a416aE5a708A8655A9c74f723;
    address internal constant _USDT_ADDRESS = 0xdAC17F958D2ee523a2206206994597C13D831ec7; // 6 decimals
}

"
    },
    "contracts/vault/common/variables/primaryHelpers.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {Constants} from "./constants.sol";
import {StorageVariables} from "./storageVariables.sol";
import {IProxy} from "../../../infinite-proxy/interfaces/IProxy.sol";
import {Structs} from "./structs.sol";
import {IVaultV3} from "../interfaces/IVaultV3.sol";
import {IToken} from "../interfaces/IToken.sol";

contract PrimaryHelpers is Constants, StorageVariables {
    using Structs for Structs.AuthTypes;

    /***********************************|
    |              ERRORS               |
    |__________________________________*/
    error Helpers__UnsupportedProtocolId();
    error Helpers__NotRebalancer();
    error Helpers__NotPrimaryRebalancer();
    error Helpers__Reentrant();
    error Helpers__NotAuth();
    error Helpers__InvalidAuthType();
    error Helpers__NotEnoughSwapLimit();

    function _auth(
        Structs.AuthTypes authType_,
        address account_
    ) internal view {
        address admin_ = IProxy(address(this)).getAdmin();
        if (authType_ == Structs.AuthTypes.Owner) {
            if (admin_ != account_) {
                revert Helpers__NotAuth();
            }
        } else if (authType_ == Structs.AuthTypes.SecondaryAuth) {
            if (
                secondaryAuth != account_ &&
                admin_ != account_
            ) {
                revert Helpers__NotAuth();
            }
        } else if (authType_ == Structs.AuthTypes.PrimaryRebalancer) {
            if (!isPrimaryRebalancer[account_] && admin_ != account_) {
                revert Helpers__NotAuth();
            }
        } else if (authType_ == Structs.AuthTypes.Rebalancer) {
            if (
                !isSecondaryRebalancer[account_] &&
                !isPrimaryRebalancer[account_] &&
                admin_ != account_
            ) {
                revert Helpers__NotAuth();
            }
        } else {
            revert Helpers__InvalidAuthType();
        }
    }

    /***********************************|
    |              MODIFIERS            |
    |__________________________________*/
    /// @notice reverts if msg.sender is not auth.
    modifier onlyAuth() {
        _auth(Structs.AuthTypes.Owner, msg.sender);
        _;
    }

    /// @notice reverts if msg.sender is not secondaryAuth or auth
    modifier onlySecondaryAuth() {
        _auth(Structs.AuthTypes.SecondaryAuth, msg.sender);
        _;
    }

    /// @notice reverts if msg.sender is not rebalancer or auth
    modifier onlyRebalancer() {
        _auth(Structs.AuthTypes.Rebalancer, msg.sender);
        _;
    }

    /// @notice reverts if msg.sender is not primaryRebalancer or auth
    modifier onlyPrimaryRebalancer() {
        _auth(Structs.AuthTypes.PrimaryRebalancer, msg.sender);
        _;
    }

    /**
     * @dev reentrancy gaurd.
     */
    modifier nonReentrant() {
        if (_status == 2) revert Helpers__Reentrant();
        _status = 2;
        _;
        _status = 1;
    }

    /// @notice Implements a method to read uint256 data from storage at a bytes32 storage slot key.
    function readFromStorage(
        bytes32 slot_
    ) public view returns (uint256 result_) {
        assembly {
            result_ := sload(slot_) // read value from the storage slot
        }
    }

    function _getAmountInUsd(
        address tokenAddress_,
        uint256 amount_,
        uint256 exchangeRate_
    ) internal view returns (uint256 amountInUsd_) {
        uint256 tokenDecimals_ = IToken(tokenAddress_).decimals();
        amountInUsd_ =
            (amount_ * exchangeRate_) /
            10 ** (2 * tokenDecimals_ - 6);
    }

    /// @notice Checks the available swap limit.
    /// @return availableSwapLimit_ The available swap limit.
    function checkAvailableSwapLimit()
        public
        view
        returns (uint256 availableSwapLimit_)
    {
        uint256 timeElapsed_ = block.timestamp - lastSwapTimestamp;
        availableSwapLimit_ = availableSwapLimit;

        /// @dev If time has elapsed, calculate the refill.
        if (timeElapsed_ > 0) {
            uint256 refill_ = (timeElapsed_ * maxDailySwapLimit) /
                (24 * 60 * 60);

            availableSwapLimit_ += refill_;

            availableSwapLimit_ = availableSwapLimit_ > maxDailySwapLimit
                ? maxDailySwapLimit
                : availableSwapLimit_;
        }
    }

    function _handleSwapLimitCheck(uint256 amount_) internal {
        availableSwapLimit = checkAvailableSwapLimit();

        if (availableSwapLimit < amount_) {
            revert Helpers__NotEnoughSwapLimit();
        }

        availableSwapLimit -= amount_;
        lastSwapTimestamp = block.timestamp;
    }
}
"
    },
    "contracts/vault/common/variables/storageVariables.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {IDSA} from "../../common/interfaces/IDSA.sol";
import {Structs} from "../../common/variables/structs.sol";

contract StorageVariables {
    using Structs for Structs.FluidVaultDetails;
    /****************************************************************************|
    |   @notice Protocol IDs                                                     |
    |   // AAVE-V3 : 1  (SUSDe, USDe, USDC, USDT, GHO, USDS)                     |
    |   // FLUID-WSTUSR-USDC : 2  (wstUSR, USDC)                                 |
    |   // FLUID-WSTUSR-USDT : 3  (wstUSR, USDT)                                 |
    |   // FLUID-WSTUSR-GHO : 4  (wstUSR, GHO)                                   |
    |   // FLUID-SUSDE-USDC : 5  (SUSDe, USDC)                                   |
    |   // FLUID-SUSDE-USDT : 6  (SUSDe, USDT)                                   |
    |   // FLUID-SUSDE-GHO : 7  (SUSDe, GHO)                                     |        
    |   // FLUID-syrupUSDC-USDC : 8  (syrupUSDC, USDC)                           |
    |   // FLUID-syrupUSDC-USDT : 9  (syrupUSDC, USDT)                           |
    |   // FLUID-syrupUSDC-GHO : 10  (syrupUSDC, GHO)                            |
    |___________________________________________________________________________*/

    /***********************************|
    |           STATE VARIABLES         |
    |__________________________________*/
    // 1: open
    // 2: closed
    uint8 internal _status;

    IDSA public vaultDSA;

    /// @notice Secondary auth that only has the power to reduce max risk ratio.
    address public secondaryAuth;

    /// @notice Current exchange price.
    uint256 public exchangePrice;

    /// @notice Last timestamp the exchange price was updated
    /// @dev This is used to calculate the rate of the vault
    uint256 public lastExchangePriceUpdatedAt;

    /// @notice Mapping to store allowed primary rebalancers
    /// @dev Primary rebalancers are the ones that can perform swap related actions
    /// Modifiable by auth
    mapping(address => bool) public isPrimaryRebalancer;

    /// @notice Mapping to store allowed secondary rebalancers
    /// @dev Secondary rebalancers are the ones that can perform all rebalancer actions except swap related actions
    /// Modifiable by auth
    mapping(address => bool) public isSecondaryRebalancer;

    // Mapping of protocol id => max risk ratio, scaled to use basis points. i.e. 1e4 = 100%, 1e2 = 1%
    // 1: AAVE-V3
    // 2: FLUID-WSTUSR-USDC
    // 3: FLUID-WSTUSR-USDT
    // 4: FLUID-WSTUSR-GHO
    // 5: FLUID-SUSDE-USDC
    // 6: FLUID-SUSDE-USDT
    // 7: FLUID-SUSDE-GHO
    mapping(uint8 => uint256) public maxRiskRatio;

    // Max aggregated risk ratio of the vault that can be reached, scaled to use basis points. i.e. 1e4 = 100%, 1e2 = 1%
    // i.e. 1e4 = 100%, 1e2 = 1%
    uint256 public aggrMaxVaultRatio;

    /// @notice withdraw fee is either amount in percentage or absolute minimum.
    /// @dev This var defines the percentage in basis points. i.e. 1e4 = 100%, 1e2 = 1%
    /// Modifiable by owner
    uint256 public withdrawalFeePercentage;

    /// @notice withdraw fee is either amount in percentage or absolute minimum. This var defines the absolute minimum
    /// this number is given in decimals for the respective asset of the vault.
    /// Modifiable by owner
    uint256 public withdrawFeeAbsoluteMin; // in underlying base asset, i.e. USDT

    // charge from the profits, scaled to use basis points. i.e. 1e4 = 100%, 1e2 = 1%
    uint256 public revenueFeePercentage;

    /// @notice Stores reserves for the vault (previously revenue)
    /// @dev Reserves - also serve a purpose to cover unknown users losses
    /// @dev Reserves can be negative if there is not enough revenue to cover the losses
    int256 public reserves;

    /// @notice Min APR for the vault. This is the minimum APR the vault must yield.
    /// @dev Can be modified by the owner / secondary auth.
    uint256 public minRate;

    /// @notice Max APR for the vault. This is the maximum APR the vault can yield.
    /// @dev Can be modified by the owner / secondary auth.
    uint256 public maxRate;

    /// @notice Revenue will be transffered to this address upon collection.
    address public treasury;

    ///@notice Mapping to store fluid vault details
    /// @dev Protocol ID => Fluid Vault Details (VaultAddress, NFTId)
    /// 2: FLUID-WSTUSR-USDC
    /// 3: FLUID-WSTUSR-USDT
    /// 4: FLUID-WSTUSR-GHO
    /// 5: FLUID-SUSDE-USDC
    /// 6: FLUID-SUSDE-USDT
    /// 7: FLUID-SUSDE-GHO
    /// 8: FLUID-syrupUSDC-USDC
    /// 9: FLUID-syrupUSDC-USDT
    /// 10: FLUID-syrupUSDC-GHO
    mapping(uint8 => Structs.FluidVaultDetails) public fluidVaultDetails;

    /// @notice Daily swap limit of the vault.
    /// @dev This is used to prevent abuse of the swap functionality.
    /// @dev Team multisig can update this value.
    uint256 public maxDailySwapLimit;

    /// @notice Available swap limit of the vault.
    /// @dev This is used to track the available swap limit of the vault.
    uint256 public availableSwapLimit;

    /// @notice Last timestamp the swap limit was recalculated.
    uint256 public lastSwapTimestamp;

    /// @notice Maximum loss in USD that can be incurred during a swap.
    /// In Percentage, scaled to use the basis points. i.e. 1e4 = 100%, 1e2 = 1%
    uint256 public maxSwapLossPercentage;
}
"
    },
    "contracts/vault/common/variables/structs.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

library Structs {
    struct FluidVaultDetails {
        address vaultAddress;
        uint256 nftId;
    }

    enum AuthTypes {
        Owner,
        SecondaryAuth,
        PrimaryRebalancer,
        Rebalancer
    }
}
"
    },
    "contracts/vault/common/variables/variablesBuffer.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/// @title      VariablesBuffer
/// @notice     Allocates space of 151 slots to maintain storage
///             consistency with imported variables in VariablesPrimaryHelper.

contract VariablesBuffer {
    uint[151] internal __buffergap;
}
"
    },
    "contracts/vault/common/variables/variablesBufferHelper.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/// @title      VariablesBufferHelper
/// @notice     Buffer Helper for variables that imports all the primary
///             helpers from the storage slot 152.

import {VariablesBuffer} from "./variablesBuffer.sol";
import {PrimaryHelpers} from "./primaryHelpers.sol";

// Buffer & variables
contract VariablesBufferHelper is VariablesBuffer, PrimaryHelpers {}
"
    },
    "contracts/vault/modules/refinance-module/events.sol": {
      "content": "//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

contract Events {
    event LogRefinance(
        uint8 indexed fromProtocolId,
        uint8 indexed toProtocolId,
        address fromDebtToken,
        address toDebtToken,
        address fromCollateralToken,
        address toCollateralToken,
        uint256 fromColAmount,
        uint256 fromDebtAmount,
        uint256 route
    );
}
"
    },
    "contracts/vault/modules/refinance-module/helpers.sol": {
      "content": "//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {RefinanceStructs} from "./interfaces.sol";
import {VariablesBufferHelper} from "../../common/variables/variablesBufferHelper.sol";
import {Events} from "./events.sol";
import {IVaultV3} from "../../common/interfaces/IVaultV3.sol";

contract RefinanceHelpers is VariablesBufferHelper, Events {
    using RefinanceStructs for RefinanceStructs.RefinanceVariables;
    using RefinanceStructs for RefinanceStructs.RefinanceParams;
    using RefinanceStructs for RefinanceStructs.RefinanceProtocolBools;

    /***********************************|
    |              ERRORS               |
    |__________________________________*/
    error RefinanceHelpers__PreventSameProtocol();
    error RefinanceHelpers__InvalidRefinance();
    error RefinanceHelpers__InvalidProtocolId();
    error RefinanceHelpers__Unsafe();

    function _preventSameProtocol(
        uint8 fromProtocolId_,
        uint8 toProtocolId_
    ) internal pure {
        if (fromProtocolId_ == toProtocolId_) {
            revert RefinanceHelpers__PreventSameProtocol();
        }
    }

    function _isRefinanceAllowedProtocolId(uint8 protocolId_) internal pure {
        if (
            protocolId_ != 1 &&
            protocolId_ != 2 &&
            protocolId_ != 3 &&
            protocolId_ != 4 &&
            protocolId_ != 5 &&
            protocolId_ != 6 &&
            protocolId_ != 7
        ) {
            revert RefinanceHelpers__InvalidProtocolId();
        }
    }

    function _getFromAaveV3Spells(
        address debtTokenAddress_,
        address colTokenAddress_,
        uint256 paybackAmount_,
        uint256 withdrawAmount_
    )
        internal
        pure
        returns (string[] memory targets_, bytes[] memory calldatas_)
    {
        uint256 spellLength_ = 2;
        targets_ = new string[](spellLength_);
        calldatas_ = new bytes[](spellLength_);

        targets_[0] = "AAVE-V3";
        calldatas_[0] = abi.encodeWithSignature(
            "payback(address,uint256,uint256,uint256,uint256)",
            debtTokenAddress_,
            paybackAmount_,
            2,
            0,
            0
        );

        targets_[1] = "AAVE-V3";
        calldatas_[1] = abi.encodeWithSignature(
            "withdraw(address,uint256,uint256,uint256)",
            colTokenAddress_,
            withdrawAmount_,
            2,
            0
        );
    }

    function _getFromFluidT1Spells(
        address fromVaultAddress_,
        uint256 fromNftId_,
        uint256 withdrawAmount_,
        uint256 paybackAmount_
    )
        internal
        pure
        returns (string[] memory targets_, bytes[] memory calldatas_)
    {
        uint256 spellLength_ = 1;
        targets_ = new string[](spellLength_);
        calldatas_ = new bytes[](spellLength_);
        targets_[0] = "FLUID-A";
        calldatas_[0] = abi.encodeWithSignature(
            "operate(address,uint256,int256,int256,uint256)",
            fromVaultAddress_,
            fromNftId_,
            // forge-lint: disable-next-line(unsafe-typecast)
            -int256(withdrawAmount_),
            // forge-lint: disable-next-line(unsafe-typecast)
            -int256(paybackAmount_),
            paybackAmount_
        );
    }

    function _getToAaveV3Spells(
        address colTokenAddress_,
        address debtTokenAddress_,
        uint256 depositAmount_,
        uint256 borrowAmount_
    )
        internal
        pure
        returns (string[] memory targets_, bytes[] memory calldatas_)
    {
        uint256 spellLength_ = 2;
        targets_ = new string[](spellLength_);
        calldatas_ = new bytes[](spellLength_);

        targets_[0] = "AAVE-V3";
        calldatas_[0] = abi.encodeWithSignature(
            "deposit(address,uint256,uint256,uint256)",
            colTokenAddress_,
            depositAmount_,
            2,
            0
        );

        targets_[1] = "AAVE-V3";
        calldatas_[1] = abi.encodeWithSignature(
            "borrow(address,uint256,uint256,uint256,uint256)",
            debtTokenAddress_,
            borrowAmount_,
            2,
            0,
            0
        );
    }

    function _getToFluidT1Spells(
        address toVaultAddress_,
        uint256 toNftId_,
        uint256 depositAmount_,
        uint256 borrowAmount_
    )
        internal
        pure
        returns (string[] memory targets_, bytes[] memory calldatas_)
    {
        uint256 spellLength_ = 1;
        targets_ = new string[](spellLength_);
        calldatas_ = new bytes[](spellLength_);
        targets_[0] = "FLUID-A";
        calldatas_[0] = abi.encodeWithSignature(
            "operate(address,uint256,int256,int256,uint256)",
            toVaultAddress_,
            toNftId_,
            depositAmount_,
            borrowAmount_,
            0
        );
    }

    function _refinanceHelper(
        RefinanceStructs.RefinanceParams memory refinanceParams_,
        bool isDebtTokenDifferent_,
        bool isCollateralTokenDifferent_
    ) internal {
        RefinanceStructs.RefinanceVariables memory refinanceVars_;
        RefinanceStructs.RefinanceProtocolBools memory refinanceProtocolBools_;

        refinanceProtocolBools_.isFromAaveV3ToFluid =
            refinanceParams_.fromProtocolId == 1 &&
            (refinanceParams_.toProtocolId == 2 ||
                refinanceParams_.toProtocolId == 3 ||
                refinanceParams_.toProtocolId == 4 ||
                refinanceParams_.toProtocolId == 5 ||
                refinanceParams_.toProtocolId == 6 ||
                refinanceParams_.toProtocolId == 7);

        refinanceProtocolBools_.isFromFluidToAaveV3 =
            refinanceParams_.toProtocolId == 1 &&
            (refinanceParams_.fromProtocolId == 2 ||
                refinanceParams_.fromProtocolId == 3 ||
                refinanceParams_.fromProtocolId == 4 ||
                refinanceParams_.fromProtocolId == 5 ||
                refinanceParams_.fromProtocolId == 6 ||
                refinanceParams_.fromProtocolId == 7);

        refinanceVars_.spellsLength = 5;
        refinanceVars_.spellIndex = 0;

        if (isDebtTokenDifferent_ && isCollateralTokenDifferent_) {
            refinanceVars_.spellsLength++;
        }

        refinanceVars_.targets = new string[](refinanceVars_.spellsLength);
        refinanceVars_.calldatas = new bytes[](refinanceVars_.spellsLength);

        if (refinanceProtocolBools_.isFromAaveV3ToFluid) {
            // Repay Debt in fromDebtToken_ & withdraw collateral from Aave V3
            {
                (
                    string[] memory _targets,
                    bytes[] memory _calldatas
                ) = _getFromAaveV3Spells(
                        refinanceParams_.fromDebtToken,
                        refinanceParams_.fromCollateralToken,
                        refinanceParams_.fromDebtAmount,
                        refinanceParams_.fromColAmount
                    );

                refinanceVars_.targets[refinanceVars_.spellIndex] = _targets[0];
                refinanceVars_.calldatas[
                    refinanceVars_.spellIndex
                ] = _calldatas[0];

                refinanceVars_.spellIndex++;

                refinanceVars_.targets[refinanceVars_.spellIndex] = _targets[1];
                refinanceVars_.calldatas[
                    refinanceVars_.spellIndex
                ] = _calldatas[1];

                refinanceVars_.spellIndex++;
            }

            if (isCollateralTokenDifferent_) {
                // Swap fromCollateralToken_ to toCollateralToken_
                refinanceVars_.sellTokenExchangeRateCol = IVaultV3(address(this))
                    .getTokenExchangeRate(refinanceParams_.fromCollateralToken);
                
                refinanceVars_.buyTokenExchangeRateCol = IVaultV3(address(this))
                    .getTokenExchangeRate(refinanceParams_.toCollateralToken);
                
                refinanceVars_.swapAmountInUsdCol = _getAmountInUsd(
                    refinanceParams_.fromCollateralToken,
                    refinanceParams_.fromColAmount,
                    refinanceVars_.sellTokenExchangeRateCol
                );

                _handleSwapLimitCheck(refinanceVars_.swapAmountInUsdCol);
                
                refinanceVars_.targets[
                    refinanceVars_.spellIndex
                ] = "SWAP-AGGREGATOR-B";
                refinanceVars_.calldatas[refinanceVars_.spellIndex] = abi
                    .encodeWithSignature(
                        "swap(address,address,uint256,uint256,uin256,uint256,string[],bytes[])",
                        refinanceParams_.fromCollateralToken,
                        refinanceParams_.toCollateralToken,
                        refinanceParams_.minBuyAmountCol,
                        refinanceVars_.sellTokenExchangeRateCol,
                        refinanceVars_.buyTokenExchangeRateCol,
                        maxSwapLossPercentage,
                        refinanceParams_.swapConnectorsCol,
                        refinanceParams_.swapCallDatasCol
                    );

                refinanceVars_.spellIndex++;
            }

            // Supply collateral to Fluid & borrow debt in toDebtToken_
            {
                (
                    string[] memory _targets,
                    bytes[] memory _calldatas
                ) = _getToFluidT1Spells(
                        fluidVaultDetails[refinanceParams_.toProtocolId]
                            .vaultAddress,
                        fluidVaultDetails[refinanceParams_.toProtocolId].nftId,
                        refinanceParams_.toColAmount,
                        refinanceParams_.toDebtAmount
                    );

                refinanceVars_.targets[refinanceVars_.spellIndex] = _targets[0];
                refinanceVars_.calldatas[
                    refinanceVars_.spellIndex
                ] = _calldatas[0];

                refinanceVars_.spellIndex++;
            }

            if (isDebtTokenDifferent_) {
                // Swap toDebtToken_ to fromDebtToken_
                refinanceVars_.sellTokenExchangeRateDebt = IVaultV3(address(this))
                    .getTokenExchangeRate(refinanceParams_.toDebtToken);
                
                refinanceVars_.buyTokenExchangeRateDebt = IVaultV3(address(this))
                    .getTokenExchangeRate(refinanceParams_.fromDebtToken);
                
                refinanceVars_.swapAmountInUsdDebt = _getAmountInUsd(
                    refinanceParams_.toDebtToken,
                    refinanceParams_.toDebtAmount,
                    refinanceVars_.sellTokenExchangeRateDebt
                );

                _handleSwapLimitCheck(refinanceVars_.swapAmountInUsdDebt);
                
                refinanceVars_.targets[
                    refinanceVars_.spellIndex
                ] = "SWAP-AGGREGATOR-B";
                refinanceVars_.calldatas[refinanceVars_.spellIndex] = abi
                    .encodeWithSignature(
                        "swap(address,address,uint256,uint256,uin256,uint256,string[],bytes[])",
                        refinanceParams_.toDebtToken,
                        refinanceParams_.fromDebtToken,
                        refinanceParams_.minBuyAmountDebt,
                        refinanceVars_.sellTokenExchangeRateDebt,
                        refinanceVars_.buyTokenExchangeRateDebt,
                        maxSwapLossPercentage,
                        refinanceParams_.swapConnectorsDebt,
                        refinanceParams_.swapCallDatasDebt
                    );

                refinanceVars_.spellIndex++;
            }

            // Repay Flashloan
            {
                refinanceVars_.targets[
                    refinanceVars_.spellIndex
                ] = "INSTAPOOL-D";
                refinanceVars_.calldatas[refinanceVars_.spellIndex] = abi
                    .encodeWithSignature(
                        "flashPayback(address,uint256,uint256,uint256)",
                        refinanceParams_.fromDebtToken,
                        refinanceParams_.fromDebtAmount,
                        0,
                        0
                    );

                refinanceVars_.spellIndex++;
            }
        } else if (refinanceProtocolBools_.isFromFluidToAaveV3) {
            // Payback and Withdraw collateral from Fluid
            {
                (
                    string[] memory _targets,
                    bytes[] memory _calldatas
                ) = _getFromFluidT1Spells(
                        fluidVaultDetails[refinanceParams_.fromProtocolId]
                            .vaultAddress,
                        fluidVaultDetails[refinanceParams_.fromProtocolId]
                            .nftId,
                        refinanceParams_.fromColAmount,
                        refinanceParams_.fromDebtAmount
                    );

                refinanceVars_.targets[refinanceVars_.spellIndex] = _targets[0];
                refinanceVars_.calldatas[
                    refinanceVars_.spellIndex
                ] = _calldatas[0];

                refinanceVars_.spellIndex++;
            }

            if (isCollateralTokenDifferent_) {
                // Swap fromCollateralToken_ to toCollateralToken_
                refinanceVars_.sellTokenExchangeRateCol = IVaultV3(address(this))
                    .getTokenExchangeRate(refinanceParams_.fromCollateralToken);
                
                refinanceVars_.buyTokenExchangeRateCol = IVaultV3(address(this))
                    .getTokenExchangeRate(refinanceParams_.toCollateralToken);
                
                refinanceVars_.swapAmountInUsdCol = _getAmountInUsd(
                    refinanceParams_.fromCollateralToken,
                    refinanceParams_.fromColAmount,
                    refinanceVars_.sellTokenExchangeRateCol
                );

                _handleSwapLimitCheck(refinanceVars_.swapAmountInUsdCol);

                refinanceVars_.targets[
                    refinanceVars_.spellIndex
                ] = "SWAP-AGGREGATOR-B";
                refinanceVars_.calldatas[refinanceVars_.spellIndex] = abi
                    .encodeWithSignature(
                        "swap(address,address,uint256,uint256,uin256,uint256,string[],bytes[])",
                        refinanceParams_.fromCollateralToken,
                        refinanceParams_.toCollateralToken,
                        refinanceParams_.minBuyAmountCol,
                        refinanceVars_.sellTokenExchangeRateCol,
                        refinanceVars_.buyTokenExchangeRateCol,
                        maxSwapLossPercentage,
                        refinanceParams_.swapConnectorsCol,
                        refinanceParams_.swapCallDatasCol
                    );

                refinanceVars_.spellIndex++;
            }

            // Deposit collateral to AaveV3 & borrow debt in toDebtToken_
            {
                (
                    string[] memory _targets,
                    bytes[] memory _calldatas
                ) = _getToAaveV3Spells(
                        refinanceParams_.toCollateralToken,
                        refinanceParams_.toDebtToken,
                        refinanceParams_.toColAmount,
                        refinanceParams_.toDebtAmount
                    );

                refinanceVars_.targets[refinanceVars_.spellIndex] = _targets[0];
                refinanceVars_.calldatas[
                    refinanceVars_.spellIndex
                ] = _calldatas[0];

                refinanceVars_.spellIndex++;

                refinanceVars_.targets[refinanceVars_.spellIndex] = _targets[1];
                refinanceVars_.calldatas[
                    refinanceVars_.spellIndex
                ] = _calldatas[1];

                refinanceVars_.spellIndex++;
            }

            if (isDebtTokenDifferent_) {
                // Swap toDebtToken_ to fromDebtToken_
                refinanceVars_.sellTokenExchangeRateDebt = IVaultV3(address(this))
                    .getTokenExchangeRate(refinanceParams_.toDebtToken);
                
                refinanceVars_.buyTokenExchangeRateDebt = IVaultV3(address(this))
                    .getTokenExchangeRate(refinanceParams_.fromDebtToken);
                
                refinanceVars_.swapAmountInUsdDebt = _getAmountInUsd(
                    refinanceParams_.toDebtToken,
                    refinanceParams_.toDebtAmount,
                    refinanceVars_.sellTokenExchangeRateDebt
                );
                
                _handleSwapLimitCheck(refinanceVars_.swapAmountInUsdDebt);
                
                refinanceVars_.targets[
                    refinanceVars_.spellIndex
                ] = "SWAP-AGGREGATOR-B";
                refinanceVars_.calldatas[refinanceVars_.spellIndex] = abi
                    .encodeWithSignature(
                        "swap(address,address,uint256,uint256,uin256,uint256,string[],bytes[])",
                        refinanceParams_.toDebtToken,
                        refinanceParams_.fromDebtToken,
                        refinanceParams_.minBuyAmountDebt,
                        refinanceVars_.sellTokenExchangeRateDebt,
                        refinanceVars_.buyTokenExchangeRateDebt,
                        maxSwapLossPercentage,
                        refinanceParams_.swapConnectorsDebt,
                        refinanceParams_.swapCallDatasDebt
                    );

                refinanceVars_.spellIndex++;
            }

            // Repay Flashloan
            {
                refinanceVars_.targets[
                    refinanceVars_.spellIndex
                ] = "INSTAPOOL-D";
                refinanceVars_.calldatas[refinanceVars_.spellIndex] = abi
                    .encodeWithSignature(
                        "flashPayback(address,uint256,uint256,uint256)",
                        refinanceParams_.fromCollateralToken,
                        refinanceParams_.fromColAmount,
                        0,
                        0
                    );

                refinanceVars_.spellIndex++;
            }
        } else {
            revert RefinanceHelpers__InvalidRefinance();
        }

        refinanceVars_.flashTarget = new string[](1);
        refinanceVars_.flashCalldata = new bytes[](1);

        bytes memory encodedFlashData_ = abi.encode(
            refinanceVars_.targets,
            refinanceVars_.calldatas
        );

        refinanceVars_.flashTarget[0] = "INSTAPOOL-D";
        refinanceVars_.flashCalldata[0] = abi.encodeWithSignature(
            "flashBorrowAndCast(address,uint256,uint256,bytes,bytes)",
            refinanceParams_.fromCollateralToken,
            refinanceParams_.fromColAmount,
            refinanceParams_.route,
            encodedFlashData_,
            "0x"
        );

        (, , refinanceVars_.beforeNetAssets, ) = IVaultV3(address(this))
            .getNetAssets();

        vaultDSA.cast(
            refinanceVars_.flashTarget,
            refinanceVars_.flashCalldata,
            address(this)
        );

        (
            ,
            ,
            refinanceVars_.afterNetAssets,
            refinanceVars_.aggregatedRatio
        ) = IVaultV3(address(this)).getNetAssets();

        // Net Assets Checks
        if (refinanceVars_.afterNetAssets > refinanceVars_.beforeNetAssets) {
            reserves += int256(
                refinanceVars_.afterNetAssets - refinanceVars_.beforeNetAssets
            );
        } else if (
            (((refinanceVars_.beforeNetAssets * (1e8 - 1)) / 1e8) >
                refinanceVars_.afterNetAssets)
        ) {
            revert RefinanceHelpers__Unsafe();
        }

        // Aggregated Ratio Checks
        if (refinanceVars_.aggregatedRatio > aggrMaxVaultRatio) {
            revert RefinanceHelpers__Unsafe();
        }

        emit LogRefinance(
            refinanceParams_.fromProtocolId,
            refinanceParams_.toProtocolId,
            refinanceParams_.fromDebtToken,
            refinanceParams_.toDebtToken,
            refinanceParams_.fromCollateralToken,
            refinanceParams_.toCollateralToken,
            refinanceParams_.fromColAmount,
            refinanceParams_.fromDebtAmount,
            refinanceParams_.route
        );
    }
}
"
    },
    "contracts/vault/modules/refinance-module/interfaces.sol": {
      "content": "//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

library RefinanceStructs {
    struct RefinanceParams {
        uint8 fromProtocolId;
        uint8 toProtocolId;
        address fromDebtToken;
        address toDebtToken;
        address fromCollateralToken;
        address toCollateralToken;
        uint256 fromColAmount;
        uint256 toColAmount;
        uint256 fromDebtAmount;
        uint256 toDebtAmount;
        uint256 route;
        uint256 minBuyAmountDebt;
        uint256 minBuyAmountCol;
        string[] swapConnectorsDebt;
        bytes[] swapCallDatasDebt;
        string[] swapConnectorsCol;
        bytes[] swapCallDatasCol;
    }

    struct RefinanceVariables {
        uint256 spellIndex;
        uint256 spellsLength;
        string[] targets;
        bytes[] calldatas;
        string[] flashTarget;
        bytes[] flashCalldata;
        uint256 sellTokenExchangeRateDebt;
        uint256 buyTokenExchangeRateCol;
        uint256 sellTokenExchangeRateCol;
        uint256 buyTokenExchangeRateDebt;
        uint256 swapAmountInUsdDebt;
        uint256 swapAmountInUsdCol;
        uint256 beforeNetAssets;
        uint256 afterNetAssets;
        uint256 aggregatedRatio;
    }

    struct RefinanceProtocolBools {
        bool isFromAaveV3ToFluid;
        bool isFromFluidToAaveV3;
    }
}"
    },
    "contracts/vault/modules/refinance-module/main.sol": {
      "content": "//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {RefinanceHelpers} from "./helpers.sol";
import {RefinanceStructs} from "./interfaces.sol";

contract RefinanceModule is RefinanceHelpers {
    using RefinanceStructs for RefinanceStructs.RefinanceParams;

    /***********************************|
    |             MODIFIERS             |
    |__________________________________*/
    modifier refinanceAllowedProtocolId(uint8 protocolId_) {
        _isRefinanceAllowedProtocolId(protocolId_);
        _;
    }

    modifier preventSameProtocol(uint8 fromProtocolId_, uint8 toProtocolId_) {
        _preventSameProtocol(fromProtocolId_, toProtocolId_);
        _;
    }

    // @notice Refinance without swap
    // @dev Refinance from one protocol to another protocol
    // @param _refinanceWithoutSwapParams The parameters for the refinance without swap
    // @dev The refinance without swap is only allowed for the following protocols:
    function refinanceWithoutSwap(
        RefinanceStructs.RefinanceParams
            memory refinanceParams_
    )
        external
        onlyRebalancer
        nonReentrant
        refinanceAllowedProtocolId(refinanceParams_.fromProtocolId)
        refinanceAllowedProtocolId(refinanceParams_.toProtocolId)
        preventSameProtocol(
            refinanceParams_.fromProtocolId,
            refinanceParams_.toProtocolId
        )
    {
        _refinanceHelper(refinanceParams_, false, false);
    }

    // @notice Refinance with swap
    // @dev Refinance from one protocol to another protocol with swap
    // @param _refinanceWithSwapParams The parameters for the refinance with swap
    // @dev Debt token is different, E.g:(USDe-USDT) on Aave V3 -> (USDe-USDC) on Fluid
    // @dev Collateral token is different, E.g:(USDe-USDT) on Aave V3 -> (wstUSR-USDT) on Fluid
    // @dev Both tokens are different, E.g:(USDe-USDT) on Aave V3 -> (wstUSR-USDC) on Fluid
    function refinanceWithSwap(
        RefinanceStructs.RefinanceParams memory refinanceParams_
    )
        external
        onlyPrimaryRebalancer
        nonReentrant
        refinanceAllowedProtocolId(refinanceParams_.fromProtocolId)
        refinanceAllowedProtocolId(refinanceParams_.toProtocolId)
        preventSameProtocol(
            refinanceParams_.fromProtocolId,
            refinanceParams_.toProtocolId
        )
    {
        bool isDebtTokenDifferent = refinanceParams_.fromDebtToken !=
            refinanceParams_.toDebtToken;
        bool isCollateralTokenDifferent = refinanceParams_
            .fromCollateralToken != refinanceParams_.toCollateralToken;

        _refinanceHelper(
            refinanceParams_,
            isDebtTokenDifferent,
            isCollateralTokenDifferent
        );
    }
}
"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "evmVersion": "paris",
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "metadata": {
      "useLiteralContent": true
    }
  }
}}

Tags:
ERC20, Proxy, Swap, Yield, Upgradeable, Factory|addr:0x60c68e5428d346bdaa7a947da8886ec8e8053861|verified:true|block:23689535|tx:0x1a5882d85d93a3a92d7bfc7857d54e8b8752f5d6b3e4f88756d420c68dce3c0b|first_check:1761829701

Submitted on: 2025-10-30 14:08:24

Comments

Log in to comment.

No comments yet.