SiloDeployer

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "silo-core/contracts/SiloDeployer.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {Clones} from "openzeppelin5/proxy/Clones.sol";

import {ISiloConfig} from "silo-core/contracts/interfaces/ISiloConfig.sol";
import {ISiloFactory} from "silo-core/contracts/interfaces/ISiloFactory.sol";
import {IInterestRateModelV2} from "silo-core/contracts/interfaces/IInterestRateModelV2.sol";
import {IInterestRateModelV2Factory} from "silo-core/contracts/interfaces/IInterestRateModelV2Factory.sol";
import {IDynamicKinkModelFactory} from "silo-core/contracts/interfaces/IDynamicKinkModelFactory.sol";
import {IInterestRateModel} from "silo-core/contracts/interfaces/IInterestRateModel.sol";
import {IHookReceiver} from "silo-core/contracts/interfaces/IHookReceiver.sol";
import {ISiloDeployer} from "silo-core/contracts/interfaces/ISiloDeployer.sol";
import {SiloConfig} from "silo-core/contracts/SiloConfig.sol";
import {CloneDeterministic} from "silo-core/contracts/lib/CloneDeterministic.sol";
import {Views} from "silo-core/contracts/lib/Views.sol";
import {Create2Factory} from "common/utils/Create2Factory.sol";

/// @notice Silo Deployer
contract SiloDeployer is Create2Factory, ISiloDeployer {
    // solhint-disable var-name-mixedcase
    IInterestRateModelV2Factory public immutable IRM_CONFIG_FACTORY;
    IDynamicKinkModelFactory public immutable DYNAMIC_KINK_MODEL_FACTORY;
    ISiloFactory public immutable SILO_FACTORY;
    address public immutable SILO_IMPL;
    address public immutable SHARE_PROTECTED_COLLATERAL_TOKEN_IMPL;
    address public immutable SHARE_DEBT_TOKEN_IMPL;
    // solhint-enable var-name-mixedcase

    constructor(
        IInterestRateModelV2Factory _irmConfigFactory,
        IDynamicKinkModelFactory _dynamicKinkModelFactory,
        ISiloFactory _siloFactory,
        address _siloImpl,
        address _shareProtectedCollateralTokenImpl,
        address _shareDebtTokenImpl
    ) {
        IRM_CONFIG_FACTORY = _irmConfigFactory;
        DYNAMIC_KINK_MODEL_FACTORY = _dynamicKinkModelFactory;
        SILO_FACTORY = _siloFactory;
        SILO_IMPL = _siloImpl;
        SHARE_PROTECTED_COLLATERAL_TOKEN_IMPL = _shareProtectedCollateralTokenImpl;
        SHARE_DEBT_TOKEN_IMPL = _shareDebtTokenImpl;
    }

    /// @inheritdoc ISiloDeployer
    function deploy(
        Oracles calldata _oracles,
        bytes calldata _irmConfigData0,
        bytes calldata _irmConfigData1,
        ClonableHookReceiver calldata _clonableHookReceiver,
        ISiloConfig.InitData memory _siloInitData
    )
        external
        returns (ISiloConfig siloConfig)
    {
        // setUp IRMs (create if needed) and update `_siloInitData`
        _setUpIRMs(_irmConfigData0, _irmConfigData1, _siloInitData);
        // create oracles and update `_siloInitData`
        _createOracles(_siloInitData, _oracles);
        // clone hook receiver if needed
        _cloneHookReceiver(_siloInitData, _clonableHookReceiver.implementation);
        // deploy `SiloConfig` (with predicted addresses)
        siloConfig = _deploySiloConfig(_siloInitData);
        // create silo
        SILO_FACTORY.createSilo({
            _siloConfig: siloConfig,
            _siloImpl: SILO_IMPL,
            _shareProtectedCollateralTokenImpl: SHARE_PROTECTED_COLLATERAL_TOKEN_IMPL,
            _shareDebtTokenImpl: SHARE_DEBT_TOKEN_IMPL,
            _deployer: _siloInitData.deployer,
            _creator: msg.sender
        });
        // initialize hook receiver only if it was cloned
        _initializeHookReceiver(_siloInitData, siloConfig, _clonableHookReceiver);

        emit SiloCreated(siloConfig);
    }

    /// @notice Deploy `SiloConfig` with predicted addresses
    /// @param _siloInitData Silo configuration for the silo creation
    /// @return siloConfig Deployed `SiloConfig`
    // solhint-disable-next-line function-max-lines
    function _deploySiloConfig(ISiloConfig.InitData memory _siloInitData) internal returns (ISiloConfig siloConfig) {
        uint256 creatorSiloCounter = SILO_FACTORY.creatorSiloCounter(msg.sender);

        ISiloConfig.ConfigData memory configData0;
        ISiloConfig.ConfigData memory configData1;

        (configData0, configData1) = Views.copySiloConfig(
            _siloInitData,
            SILO_FACTORY.daoFeeRange(),
            SILO_FACTORY.maxDeployerFee(),
            SILO_FACTORY.maxFlashloanFee(),
            SILO_FACTORY.maxLiquidationFee()
        );

        configData0.silo = CloneDeterministic.predictSilo0Addr(
            SILO_IMPL,
            creatorSiloCounter,
            address(SILO_FACTORY),
            msg.sender
        );

        configData1.silo = CloneDeterministic.predictSilo1Addr(
            SILO_IMPL,
            creatorSiloCounter,
            address(SILO_FACTORY),
            msg.sender
        );

        configData0.collateralShareToken = configData0.silo;
        configData1.collateralShareToken = configData1.silo;

        configData0.protectedShareToken = CloneDeterministic.predictShareProtectedCollateralToken0Addr(
            SHARE_PROTECTED_COLLATERAL_TOKEN_IMPL,
            creatorSiloCounter,
            address(SILO_FACTORY),
            msg.sender
        );

        configData1.protectedShareToken = CloneDeterministic.predictShareProtectedCollateralToken1Addr(
            SHARE_PROTECTED_COLLATERAL_TOKEN_IMPL,
            creatorSiloCounter,
            address(SILO_FACTORY),
            msg.sender
        );

        configData0.debtShareToken = CloneDeterministic.predictShareDebtToken0Addr(
            SHARE_DEBT_TOKEN_IMPL,
            creatorSiloCounter,
            address(SILO_FACTORY),
            msg.sender
        );

        configData1.debtShareToken = CloneDeterministic.predictShareDebtToken1Addr(
            SHARE_DEBT_TOKEN_IMPL,
            creatorSiloCounter,
            address(SILO_FACTORY),
            msg.sender
        );

        uint256 nextSiloId = SILO_FACTORY.getNextSiloId();

        siloConfig = ISiloConfig(address(new SiloConfig{salt: _salt()}(nextSiloId, configData0, configData1)));
    }

    /// @notice Create IRMs and update `_siloInitData`
    /// @param _irmConfigData0 IRM config data for a silo `_TOKEN0`
    /// @param _irmConfigData1 IRM config data for a silo `_TOKEN1`
    /// @param _siloInitData Silo configuration for the silo creation
    function _setUpIRMs(
        bytes calldata _irmConfigData0,
        bytes calldata _irmConfigData1,
        ISiloConfig.InitData memory _siloInitData
    ) internal {
        bytes32 salt = _salt();

        if (_siloInitData.interestRateModel0 == address(IRM_CONFIG_FACTORY)) {
            _siloInitData.interestRateModel0 = _createInterestRateModel(_irmConfigData0, salt);
        }

        if (_siloInitData.interestRateModel1 == address(IRM_CONFIG_FACTORY)) {
            _siloInitData.interestRateModel1 = _createInterestRateModel(_irmConfigData1, salt);
        }

        uint256 creatorSiloCounter = SILO_FACTORY.creatorSiloCounter(msg.sender);

        if (_siloInitData.interestRateModel0 == address(DYNAMIC_KINK_MODEL_FACTORY)) {
            address silo = CloneDeterministic.predictSilo0Addr(
                SILO_IMPL,
                creatorSiloCounter,
                address(SILO_FACTORY),
                msg.sender
            );

            _siloInitData.interestRateModel0 = _createDKinkIRM(_irmConfigData0, silo, salt);
        }

        if (_siloInitData.interestRateModel1 == address(DYNAMIC_KINK_MODEL_FACTORY)) {
            address silo = CloneDeterministic.predictSilo1Addr(
                SILO_IMPL,
                creatorSiloCounter,
                address(SILO_FACTORY),
                msg.sender
            );

            _siloInitData.interestRateModel1 = _createDKinkIRM(_irmConfigData1, silo, salt);
        }
    }

    /// @notice Create an interest rate model
    /// @param _irmConfigData IRM config data
    /// @return interestRateModel Deployed interest rate model
    function _createInterestRateModel(bytes memory _irmConfigData, bytes32 _salt) internal returns (address) {
        IInterestRateModelV2.Config memory config = abi.decode(_irmConfigData, (IInterestRateModelV2.Config));
        (, IInterestRateModelV2 interestRateModel) = IRM_CONFIG_FACTORY.create(config, _salt);

        return address(interestRateModel);
    }

    /// @notice Create a DKinkIRM
    /// @param _irmConfigData DKinkIRM config data
    /// @param _silo Silo address
    /// @return interestRateModel Deployed DKinkIRM
    function _createDKinkIRM(bytes memory _irmConfigData, address _silo, bytes32 _salt) internal returns (address) {
        DKinkIRMConfig memory dkink = abi.decode(_irmConfigData, (DKinkIRMConfig));

        IInterestRateModel interestRateModel = DYNAMIC_KINK_MODEL_FACTORY.create(
            dkink.config,
            dkink.immutableArgs,
            dkink.initialOwner,
            _silo,
            _salt
        );

        return address(interestRateModel);
    }

    /// @notice Create an oracle if it is not specified in the `_siloInitData` and has tx details for the creation
    /// @param _siloInitData Silo configuration for the silo creation
    /// @param _oracles Oracles creation details (factory and creation tx input)
    function _createOracles(ISiloConfig.InitData memory _siloInitData, Oracles memory _oracles) internal {
        if (_siloInitData.solvencyOracle0 == address(0)) {
            _siloInitData.solvencyOracle0 = _createOracle(_oracles.solvencyOracle0);
        }

        if (_siloInitData.maxLtvOracle0 == address(0)) {
            _siloInitData.maxLtvOracle0 = _createOracle(_oracles.maxLtvOracle0);
        }

        if (_siloInitData.solvencyOracle1 == address(0)) {
            _siloInitData.solvencyOracle1 = _createOracle(_oracles.solvencyOracle1);
        }

        if (_siloInitData.maxLtvOracle1 == address(0)) {
            _siloInitData.maxLtvOracle1 = _createOracle(_oracles.maxLtvOracle1);
        }
    }

    /// @notice Create an oracle
    /// @param _txData Oracle creation details (factory and creation tx input)
    function _createOracle(OracleCreationTxData memory _txData) internal returns (address _oracle) {
        if (_txData.deployed != address(0)) return _txData.deployed;

        address factory = _txData.factory;

        if (factory == address(0)) return address(0);

        _updateSalt(_txData.txInput);

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory data) = factory.call(_txData.txInput);

        require(success && data.length == 32, FailedToCreateAnOracle(factory));

        _oracle = address(uint160(uint256(bytes32(data))));
    }

    /// @notice Clone hook receiver if it is provided
    /// @param _siloInitData Silo configuration for the silo creation
    /// @param _hookReceiverImplementation Hook receiver implementation to clone
    function _cloneHookReceiver(
        ISiloConfig.InitData memory _siloInitData,
        address _hookReceiverImplementation
    ) internal {
        require(
            _hookReceiverImplementation == address(0) || _siloInitData.hookReceiver == address(0),
            HookReceiverMisconfigured()
        );

        if (_hookReceiverImplementation != address(0)) {
            _siloInitData.hookReceiver = Clones.cloneDeterministic(_hookReceiverImplementation, _salt());
        }
    }

    /// @notice Initialize hook receiver if it was cloned
    /// @param _siloInitData Silo configuration for the silo creation
    /// (where _siloInitData.hookReceiver is the cloned hook receiver)
    /// @param _siloConfig Configuration of the created silo
    /// @param _clonableHookReceiver Hook receiver implementation and initialization data
    function _initializeHookReceiver(
        ISiloConfig.InitData memory _siloInitData,
        ISiloConfig _siloConfig,
        ClonableHookReceiver calldata _clonableHookReceiver
    ) internal {
        if (_clonableHookReceiver.implementation != address(0)) {
            IHookReceiver(_siloInitData.hookReceiver).initialize(
                _siloConfig,
                _clonableHookReceiver.initializationData
            );
        }
    }

    /// @notice Update the salt of the tx input
    /// @param _txInput The tx input for the oracle factory
    function _updateSalt(bytes memory _txInput) internal {
        bytes32 salt = _salt();

        assembly { // solhint-disable-line no-inline-assembly
            let pointer := add(add(_txInput, 0x20), sub(mload(_txInput), 0x20))
            mstore(pointer, salt)
        }
    }
}
"
    },
    "gitmodules/openzeppelin-contracts-5/contracts/proxy/Clones.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol)

pragma solidity ^0.8.20;

import {Errors} from "../utils/Errors.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        return clone(implementation, 0);
    }

    /**
     * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
     * to the new contract.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function clone(address implementation, uint256 value) internal returns (address instance) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Stores the bytecode after address
            mstore(0x20, 0x5af43d82803e903d91602b57fd5bf3)
            // implementation address
            mstore(0x11, implementation)
            // Packs the first 3 bytes of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            instance := create(value, 0x09, 0x37)
        }
        if (instance == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        return cloneDeterministic(implementation, salt, 0);
    }

    /**
     * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
     * a `value` parameter to send native currency to the new contract.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function cloneDeterministic(
        address implementation,
        bytes32 salt,
        uint256 value
    ) internal returns (address instance) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Stores the bytecode after address
            mstore(0x20, 0x5af43d82803e903d91602b57fd5bf3)
            // implementation address
            mstore(0x11, implementation)
            // Packs the first 3 bytes of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            instance := create2(value, 0x09, 0x37, salt)
        }
        if (instance == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}
"
    },
    "silo-core/contracts/interfaces/ISiloConfig.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

import {ISilo} from "./ISilo.sol";
import {ICrossReentrancyGuard} from "./ICrossReentrancyGuard.sol";

interface ISiloConfig is ICrossReentrancyGuard {
    struct InitData {
        /// @notice Can be address zero if deployer fees are not to be collected. If deployer address is zero then
        /// deployer fee must be zero as well. Deployer will be minted an NFT that gives the right to claim deployer
        /// fees. NFT can be transferred with the right to claim.
        address deployer;

        /// @notice Address of the hook receiver called on every before/after action on Silo. Hook contract also
        /// implements liquidation logic and veSilo gauge connection.
        address hookReceiver;

        /// @notice Deployer's fee in 18 decimals points. Deployer will earn this fee based on the interest earned
        /// by the Silo. Max deployer fee is set by the DAO. At deployment it is 15%.
        uint256 deployerFee;

        /// @notice DAO's fee in 18 decimals points. DAO will earn this fee based on the interest earned
        /// by the Silo. Acceptable fee range fee is set by the DAO. Default at deployment is 5% - 50%.
        uint256 daoFee;

        /// @notice Address of the first token
        address token0;

        /// @notice Address of the solvency oracle. Solvency oracle is used to calculate LTV when deciding if borrower
        /// is solvent or should be liquidated. Solvency oracle is optional and if not set price of 1 will be assumed.
        address solvencyOracle0;

        /// @notice Address of the maxLtv oracle. Max LTV oracle is used to calculate LTV when deciding if borrower
        /// can borrow given amount of assets. Max LTV oracle is optional and if not set it defaults to solvency
        /// oracle. If neither is set price of 1 will be assumed.
        address maxLtvOracle0;

        /// @notice Address of the interest rate model
        address interestRateModel0;

        /// @notice Maximum LTV for first token. maxLTV is in 18 decimals points and is used to determine, if borrower
        /// can borrow given amount of assets. MaxLtv is in 18 decimals points. MaxLtv must be lower or equal to LT.
        uint256 maxLtv0;

        /// @notice Liquidation threshold for first token. LT is used to calculate solvency. LT is in 18 decimals
        /// points. LT must not be lower than maxLTV.
        uint256 lt0;

        /// @notice minimal acceptable LTV after liquidation, in 18 decimals points
        uint256 liquidationTargetLtv0;

        /// @notice Liquidation fee for the first token in 18 decimals points. Liquidation fee is what liquidator earns
        /// for repaying insolvent loan.
        uint256 liquidationFee0;

        /// @notice Flashloan fee sets the cost of taking a flashloan in 18 decimals points
        uint256 flashloanFee0;

        /// @notice Indicates if a beforeQuote on oracle contract should be called before quoting price
        bool callBeforeQuote0;

        /// @notice Address of the second token
        address token1;

        /// @notice Address of the solvency oracle. Solvency oracle is used to calculate LTV when deciding if borrower
        /// is solvent or should be liquidated. Solvency oracle is optional and if not set price of 1 will be assumed.
        address solvencyOracle1;

        /// @notice Address of the maxLtv oracle. Max LTV oracle is used to calculate LTV when deciding if borrower
        /// can borrow given amount of assets. Max LTV oracle is optional and if not set it defaults to solvency
        /// oracle. If neither is set price of 1 will be assumed.
        address maxLtvOracle1;

        /// @notice Address of the interest rate model
        address interestRateModel1;

        /// @notice Maximum LTV for first token. maxLTV is in 18 decimals points and is used to determine,
        /// if borrower can borrow given amount of assets. maxLtv is in 18 decimals points
        uint256 maxLtv1;

        /// @notice Liquidation threshold for first token. LT is used to calculate solvency. LT is in 18 decimals points
        uint256 lt1;

        /// @notice minimal acceptable LTV after liquidation, in 18 decimals points
        uint256 liquidationTargetLtv1;

        /// @notice Liquidation fee is what liquidator earns for repaying insolvent loan.
        uint256 liquidationFee1;

        /// @notice Flashloan fee sets the cost of taking a flashloan in 18 decimals points
        uint256 flashloanFee1;

        /// @notice Indicates if a beforeQuote on oracle contract should be called before quoting price
        bool callBeforeQuote1;
    }

    struct ConfigData {
        uint256 daoFee;
        uint256 deployerFee;
        address silo;
        address token;
        address protectedShareToken;
        address collateralShareToken;
        address debtShareToken;
        address solvencyOracle;
        address maxLtvOracle;
        address interestRateModel;
        uint256 maxLtv;
        uint256 lt;
        uint256 liquidationTargetLtv;
        uint256 liquidationFee;
        uint256 flashloanFee;
        address hookReceiver;
        bool callBeforeQuote;
    }

    struct DepositConfig {
        address silo;
        address token;
        address collateralShareToken;
        address protectedShareToken;
        uint256 daoFee;
        uint256 deployerFee;
        address interestRateModel;
    }

    error OnlySilo();
    error OnlySiloOrTokenOrHookReceiver();
    error WrongSilo();
    error OnlyDebtShareToken();
    error DebtExistInOtherSilo();
    error FeeTooHigh();

    /// @dev It should be called on debt transfer (debt share token transfer).
    /// In the case if the`_recipient` doesn't have configured a collateral silo,
    /// it will be set to the collateral silo of the `_sender`.
    /// @param _sender sender address
    /// @param _recipient recipient address
    function onDebtTransfer(address _sender, address _recipient) external;

    /// @notice Set collateral silo.
    /// @dev Revert if msg.sender is not a SILO_0 or SILO_1.
    /// @dev Always set collateral silo the same as msg.sender.
    /// @param _borrower borrower address
    /// @return collateralSiloChanged TRUE if collateral silo changed
    function setThisSiloAsCollateralSilo(address _borrower) external returns (bool collateralSiloChanged);

    /// @notice Set collateral silo
    /// @dev Revert if msg.sender is not a SILO_0 or SILO_1.
    /// @dev Always set collateral silo opposite to the msg.sender.
    /// @param _borrower borrower address
    /// @return collateralSiloChanged TRUE if collateral silo changed
    function setOtherSiloAsCollateralSilo(address _borrower) external returns (bool collateralSiloChanged);

    /// @notice Accrue interest for the silo
    /// @param _silo silo for which accrue interest
    function accrueInterestForSilo(address _silo) external;

    /// @notice Accrue interest for both silos (SILO_0 and SILO_1 in a config)
    function accrueInterestForBothSilos() external;

    /// @notice Retrieves the collateral silo for a specific borrower.
    /// @dev As a user can deposit into `Silo0` and `Silo1`, this property specifies which Silo
    /// will be used as collateral for the debt. Later on, it will be used for max LTV and solvency checks.
    /// After being set, the collateral silo is never set to `address(0)` again but such getters as
    /// `getConfigsForSolvency`, `getConfigsForBorrow`, `getConfigsForWithdraw` will return empty
    /// collateral silo config if borrower doesn't have debt.
    ///
    /// In the SiloConfig collateral silo is set by the following functions:
    /// `onDebtTransfer` - only if the recipient doesn't have collateral silo set (inherits it from the sender)
    /// This function is called on debt share token transfer (debt transfer).
    /// `setThisSiloAsCollateralSilo` - sets the same silo as the one that calls the function.
    /// `setOtherSiloAsCollateralSilo` - sets the opposite silo as collateral from the one that calls the function.
    ///
    /// In the Silo collateral silo is set by the following functions:
    /// `borrow` - always sets opposite silo as collateral.
    /// If Silo0 borrows, then Silo1 will be collateral and vice versa.
    /// `borrowSameAsset` - always sets the same silo as collateral.
    /// `switchCollateralToThisSilo` - always sets the same silo as collateral.
    /// @param _borrower The address of the borrower for which the collateral silo is being retrieved
    /// @return collateralSilo The address of the collateral silo for the specified borrower
    function borrowerCollateralSilo(address _borrower) external view returns (address collateralSilo);

    /// @notice Retrieves the silo ID
    /// @dev Each silo is assigned a unique ID. ERC-721 token is minted with identical ID to deployer.
    /// An owner of that token receives the deployer fees.
    /// @return siloId The ID of the silo
    function SILO_ID() external view returns (uint256 siloId); // solhint-disable-line func-name-mixedcase

    /// @notice Retrieves the addresses of the two silos
    /// @return silo0 The address of the first silo
    /// @return silo1 The address of the second silo
    function getSilos() external view returns (address silo0, address silo1);

    /// @notice Retrieves the asset associated with a specific silo
    /// @dev This function reverts for incorrect silo address input
    /// @param _silo The address of the silo for which the associated asset is being retrieved
    /// @return asset The address of the asset associated with the specified silo
    function getAssetForSilo(address _silo) external view returns (address asset);

    /// @notice Verifies if the borrower has debt in other silo by checking the debt share token balance
    /// @param _thisSilo The address of the silo in respect of which the debt is checked
    /// @param _borrower The address of the borrower for which the debt is checked
    /// @return hasDebt true if the borrower has debt in other silo
    function hasDebtInOtherSilo(address _thisSilo, address _borrower) external view returns (bool hasDebt);

    /// @notice Retrieves the debt silo associated with a specific borrower
    /// @dev This function reverts if debt present in two silo (should not happen)
    /// @param _borrower The address of the borrower for which the debt silo is being retrieved
    function getDebtSilo(address _borrower) external view returns (address debtSilo);

    /// @notice Retrieves configuration data for both silos. First config is for the silo that is asking for configs.
    /// @param borrower borrower address for which debtConfig will be returned
    /// @return collateralConfig The configuration data for collateral silo (empty if there is no debt).
    /// @return debtConfig The configuration data for debt silo (empty if there is no debt).
    function getConfigsForSolvency(address borrower)
        external
        view
        returns (ConfigData memory collateralConfig, ConfigData memory debtConfig);

    /// @notice Retrieves configuration data for a specific silo
    /// @dev This function reverts for incorrect silo address input.
    /// @param _silo The address of the silo for which configuration data is being retrieved
    /// @return config The configuration data for the specified silo
    function getConfig(address _silo) external view returns (ConfigData memory config);

    /// @notice Retrieves configuration data for a specific silo for withdraw fn.
    /// @dev This function reverts for incorrect silo address input.
    /// @param _silo The address of the silo for which configuration data is being retrieved
    /// @return depositConfig The configuration data for the specified silo (always config for `_silo`)
    /// @return collateralConfig The configuration data for the collateral silo (empty if there is no debt)
    /// @return debtConfig The configuration data for the debt silo (empty if there is no debt)
    function getConfigsForWithdraw(address _silo, address _borrower) external view returns (
        DepositConfig memory depositConfig,
        ConfigData memory collateralConfig,
        ConfigData memory debtConfig
    );

    /// @notice Retrieves configuration data for a specific silo for borrow fn.
    /// @dev This function reverts for incorrect silo address input.
    /// @param _debtSilo The address of the silo for which configuration data is being retrieved
    /// @return collateralConfig The configuration data for the collateral silo (always other than `_debtSilo`)
    /// @return debtConfig The configuration data for the debt silo (always config for `_debtSilo`)
    function getConfigsForBorrow(address _debtSilo)
        external
        view
        returns (ConfigData memory collateralConfig, ConfigData memory debtConfig);

    /// @notice Retrieves fee-related information for a specific silo
    /// @dev This function reverts for incorrect silo address input
    /// @param _silo The address of the silo for which fee-related information is being retrieved.
    /// @return daoFee The DAO fee percentage in 18 decimals points.
    /// @return deployerFee The deployer fee percentage in 18 decimals points.
    /// @return flashloanFee The flashloan fee percentage in 18 decimals points.
    /// @return asset The address of the asset associated with the specified silo.
    function getFeesWithAsset(address _silo)
        external
        view
        returns (uint256 daoFee, uint256 deployerFee, uint256 flashloanFee, address asset);

    /// @notice Retrieves share tokens associated with a specific silo
    /// @dev This function reverts for incorrect silo address input
    /// @param _silo The address of the silo for which share tokens are being retrieved
    /// @return protectedShareToken The address of the protected (non-borrowable) share token
    /// @return collateralShareToken The address of the collateral share token
    /// @return debtShareToken The address of the debt share token
    function getShareTokens(address _silo)
        external
        view
        returns (address protectedShareToken, address collateralShareToken, address debtShareToken);

    /// @notice Retrieves the share token and the silo token associated with a specific silo
    /// @param _silo The address of the silo for which the share token and silo token are being retrieved
    /// @param _collateralType The type of collateral
    /// @return shareToken The address of the share token (collateral or protected collateral)
    /// @return asset The address of the silo token
    function getCollateralShareTokenAndAsset(address _silo, ISilo.CollateralType _collateralType)
        external
        view
        returns (address shareToken, address asset);

    /// @notice Retrieves the share token and the silo token associated with a specific silo
    /// @param _silo The address of the silo for which the share token and silo token are being retrieved
    /// @return shareToken The address of the share token (debt)
    /// @return asset The address of the silo token
    function getDebtShareTokenAndAsset(address _silo)
        external
        view
        returns (address shareToken, address asset);
}
"
    },
    "silo-core/contracts/interfaces/ISiloFactory.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

import {IERC721} from "openzeppelin5/interfaces/IERC721.sol";
import {ISiloConfig} from "./ISiloConfig.sol";

interface ISiloFactory is IERC721 {
    struct Range {
        uint128 min;
        uint128 max;
    }

    /// @notice Emitted on the creation of a Silo.
    /// @param implementation Address of the Silo implementation.
    /// @param token0 Address of the first Silo token.
    /// @param token1 Address of the second Silo token.
    /// @param silo0 Address of the first Silo.
    /// @param silo1 Address of the second Silo.
    /// @param siloConfig Address of the SiloConfig.
    event NewSilo(
        address indexed implementation,
        address indexed token0,
        address indexed token1,
        address silo0,
        address silo1,
        address siloConfig
    );

    event BaseURI(string newBaseURI);

    /// @notice Emitted on the update of DAO fee.
    /// @param minDaoFee Value of the new minimal DAO fee.
    /// @param maxDaoFee Value of the new maximal DAO fee.
    event DaoFeeChanged(uint128 minDaoFee, uint128 maxDaoFee);

    /// @notice Emitted on the update of max deployer fee.
    /// @param maxDeployerFee Value of the new max deployer fee.
    event MaxDeployerFeeChanged(uint256 maxDeployerFee);

    /// @notice Emitted on the update of max flashloan fee.
    /// @param maxFlashloanFee Value of the new max flashloan fee.
    event MaxFlashloanFeeChanged(uint256 maxFlashloanFee);

    /// @notice Emitted on the update of max liquidation fee.
    /// @param maxLiquidationFee Value of the new max liquidation fee.
    event MaxLiquidationFeeChanged(uint256 maxLiquidationFee);

    /// @notice Emitted on the change of DAO fee receiver.
    /// @param daoFeeReceiver Address of the new DAO fee receiver.
    event DaoFeeReceiverChanged(address daoFeeReceiver);

    /// @notice Emitted on the change of DAO fee receiver for particular silo
    /// @param silo Address for which new DAO fee receiver is set.
    /// @param daoFeeReceiver Address of the new DAO fee receiver.
    event DaoFeeReceiverChangedForSilo(address silo, address daoFeeReceiver);

    /// @notice Emitted on the change of DAO fee receiver for particular asset
    /// @param asset Address for which new DAO fee receiver is set.
    /// @param daoFeeReceiver Address of the new DAO fee receiver.
    event DaoFeeReceiverChangedForAsset(address asset, address daoFeeReceiver);

    error MissingHookReceiver();
    error ZeroAddress();
    error DaoFeeReceiverZeroAddress();
    error SameDaoFeeReceiver();
    error EmptyToken0();
    error EmptyToken1();
    error MaxFeeExceeded();
    error InvalidFeeRange();
    error SameAsset();
    error SameRange();
    error InvalidIrm();
    error InvalidMaxLtv();
    error InvalidLt();
    error InvalidDeployer();
    error DaoMinRangeExceeded();
    error DaoMaxRangeExceeded();
    error MaxDeployerFeeExceeded();
    error MaxFlashloanFeeExceeded();
    error MaxLiquidationFeeExceeded();
    error InvalidCallBeforeQuote();
    error OracleMisconfiguration();
    error InvalidQuoteToken();
    error HookIsZeroAddress();
    error LiquidationTargetLtvTooHigh();
    error NotYourSilo();
    error ConfigMismatchSilo();
    error ConfigMismatchShareProtectedToken();
    error ConfigMismatchShareDebtToken();
    error ConfigMismatchShareCollateralToken();

    /// @notice Create a new Silo.
    /// @param _siloConfig Silo configuration.
    /// @param _siloImpl Address of the `Silo` implementation.
    /// @param _shareProtectedCollateralTokenImpl Address of the `ShareProtectedCollateralToken` implementation.
    /// @param _shareDebtTokenImpl Address of the `ShareDebtToken` implementation.
    /// @param _deployer Address of the deployer.
    /// @param _creator Address of the creator.
    function createSilo(
        ISiloConfig _siloConfig,
        address _siloImpl,
        address _shareProtectedCollateralTokenImpl,
        address _shareDebtTokenImpl,
        address _deployer,
        address _creator
    )
        external;

    /// @notice NFT ownership represents the deployer fee receiver for the each Silo ID.  After burning, 
    /// the deployer fee is sent to the DAO. Burning doesn't affect Silo's behavior. It is only about fee distribution.
    /// @param _siloIdToBurn silo ID to burn.
    function burn(uint256 _siloIdToBurn) external;

    /// @notice Update the value of DAO fee. Updated value will be used only for a new Silos.
    /// Previously deployed SiloConfigs are immutable.
    /// @param _minFee Value of the new DAO minimal fee.
    /// @param _maxFee Value of the new DAO maximal fee.
    function setDaoFee(uint128 _minFee, uint128 _maxFee) external;

    /// @notice Set the default DAO fee receiver.
    /// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
    function setDaoFeeReceiver(address _newDaoFeeReceiver) external;

    /// @notice Set the new DAO fee receiver for asset, this setup will be used when fee receiver for silo is empty.
    /// @param _asset Address for which new DAO fee receiver is set.
    /// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
    function setDaoFeeReceiverForAsset(address _asset, address _newDaoFeeReceiver) external;

    /// @notice Set the new DAO fee receiver for silo. This setup has highest priority.
    /// @param _silo Address for which new DAO fee receiver is set.
    /// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
    function setDaoFeeReceiverForSilo(address _silo, address _newDaoFeeReceiver) external;

    /// @notice Update the value of max deployer fee. Updated value will be used only for a new Silos max deployer
    /// fee validation. Previously deployed SiloConfigs are immutable.
    /// @param _newMaxDeployerFee Value of the new max deployer fee.
    function setMaxDeployerFee(uint256 _newMaxDeployerFee) external;

    /// @notice Update the value of max flashloan fee. Updated value will be used only for a new Silos max flashloan
    /// fee validation. Previously deployed SiloConfigs are immutable.
    /// @param _newMaxFlashloanFee Value of the new max flashloan fee.
    function setMaxFlashloanFee(uint256 _newMaxFlashloanFee) external;

    /// @notice Update the value of max liquidation fee. Updated value will be used only for a new Silos max
    /// liquidation fee validation. Previously deployed SiloConfigs are immutable.
    /// @param _newMaxLiquidationFee Value of the new max liquidation fee.
    function setMaxLiquidationFee(uint256 _newMaxLiquidationFee) external;
   
    /// @notice Update the base URI.
    /// @param _newBaseURI Value of the new base URI.
    function setBaseURI(string calldata _newBaseURI) external;

    /// @notice Acceptable DAO fee range for new Silos. Denominated in 18 decimals points. 1e18 == 100%.
    function daoFeeRange() external view returns (Range memory);

    /// @notice Max deployer fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
    function maxDeployerFee() external view returns (uint256);

    /// @notice Max flashloan fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
    function maxFlashloanFee() external view returns (uint256);

    /// @notice Max liquidation fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
    function maxLiquidationFee() external view returns (uint256);

    /// @notice The recipient of DAO fees.
    function daoFeeReceiver() external view returns (address);

    /// @notice Get SiloConfig address by Silo id.
    function idToSiloConfig(uint256 _id) external view returns (address);

    /// @notice Get the counter of silos created by the wallet.
    function creatorSiloCounter(address _creator) external view returns (uint256);

    /// @notice Do not use this method to check if silo is secure. Anyone can deploy silo with any configuration
    /// and implementation. Most critical part of verification would be to check who deployed it.
    /// @dev True if the address was deployed using SiloFactory.
    function isSilo(address _silo) external view returns (bool);

    /// @notice Id of a next Silo to be deployed. This is an ID of non-existing Silo outside of createSilo
    /// function call. ID of a first Silo is 1.
    function getNextSiloId() external view returns (uint256);

    /// @notice Get the DAO and deployer fee receivers for a particular Silo address.
    /// @param _silo Silo address.
    /// @return dao DAO fee receiver.
    /// @return deployer Deployer fee receiver.
    function getFeeReceivers(address _silo) external view returns (address dao, address deployer);

    /// @notice Validate InitData for a new Silo. Config will be checked for the fee limits, missing parameters.
    /// @param _initData Silo init data.
    function validateSiloInitData(ISiloConfig.InitData memory _initData) external view returns (bool);
}
"
    },
    "silo-core/contracts/interfaces/IInterestRateModelV2.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

import {IInterestRateModelV2Config} from "./IInterestRateModelV2Config.sol";

interface IInterestRateModelV2 {
    struct Config {
        // uopt ∈ (0, 1) – optimal utilization;
        int256 uopt;
        // ucrit ∈ (uopt, 1) – threshold of large utilization;
        int256 ucrit;
        // ulow ∈ (0, uopt) – threshold of low utilization
        int256 ulow;
        // ki > 0 – integrator gain
        int256 ki;
        // kcrit > 0 – proportional gain for large utilization
        int256 kcrit;
        // klow ≥ 0 – proportional gain for low utilization
        int256 klow;
        // klin ≥ 0 – coefficient of the lower linear bound
        int256 klin;
        // beta ≥ 0 - a scaling factor
        int256 beta;
        // ri ≥ 0 – initial value of the integrator
        int112 ri;
        // Tcrit ≥ 0 - initial value of the time during which the utilization exceeds the critical value
        int112 Tcrit;
    }

    struct Setup {
        // ri ≥ 0 – the integrator
        int112 ri;
        // Tcrit ≥ 0 - the time during which the utilization exceeds the critical value
        int112 Tcrit;
        // flag that informs if setup is initialized
        bool initialized;
    }
    /* solhint-enable */

    error AddressZero();
    error DeployConfigFirst();
    error AlreadyInitialized();

    error InvalidBeta();
    error InvalidKcrit();
    error InvalidKi();
    error InvalidKlin();
    error InvalidKlow();
    error InvalidTcrit();
    error InvalidTimestamps();
    error InvalidUcrit();
    error InvalidUlow();
    error InvalidUopt();
    error InvalidRi();

    /// @dev Get config for given asset in a Silo.
    /// @param _silo Silo address for which config should be set
    /// @return Config struct for asset in Silo
    function getConfig(address _silo) external view returns (Config memory);

    /// @notice get the flag to detect rcomp restriction (zero current interest) due to overflow
    /// overflow boolean flag to detect rcomp restriction
    function overflowDetected(address _silo, uint256 _blockTimestamp)
        external
        view
        returns (bool overflow);

    /// @dev pure function that calculates current annual interest rate
    /// @param _c configuration object, IInterestRateModel.Config
    /// @param _totalBorrowAmount current total borrows for asset
    /// @param _totalDeposits current total deposits for asset
    /// @param _interestRateTimestamp timestamp of last interest rate update
    /// @param _blockTimestamp current block timestamp
    /// @return rcur current annual interest rate (1e18 == 100%)
    function calculateCurrentInterestRate(
        Config calldata _c,
        uint256 _totalDeposits,
        uint256 _totalBorrowAmount,
        uint256 _interestRateTimestamp,
        uint256 _blockTimestamp
    ) external pure returns (uint256 rcur);

    /// @dev pure function that calculates interest rate based on raw input data
    /// @param _c configuration object, IInterestRateModel.Config
    /// @param _totalBorrowAmount current total borrows for asset
    /// @param _totalDeposits current total deposits for asset
    /// @param _interestRateTimestamp timestamp of last interest rate update
    /// @param _blockTimestamp current block timestamp
    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)
    /// @return ri current integral part of the rate
    /// @return Tcrit time during which the utilization exceeds the critical value
    /// @return overflow boolean flag to detect rcomp restriction
    function calculateCompoundInterestRateWithOverflowDetection(
        Config memory _c,
        uint256 _totalDeposits,
        uint256 _totalBorrowAmount,
        uint256 _interestRateTimestamp,
        uint256 _blockTimestamp
    )
        external
        pure
        returns (
            uint256 rcomp,
            int256 ri,
            int256 Tcrit,
            bool overflow
        );

    /// @dev pure function that calculates interest rate based on raw input data
    /// @param _c configuration object, IInterestRateModel.Config
    /// @param _totalBorrowAmount current total borrows for asset
    /// @param _totalDeposits current total deposits for asset
    /// @param _interestRateTimestamp timestamp of last interest rate update
    /// @param _blockTimestamp current block timestamp
    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)
    /// @return ri current integral part of the rate
    /// @return Tcrit time during which the utilization exceeds the critical value
    function calculateCompoundInterestRate(
        Config memory _c,
        uint256 _totalDeposits,
        uint256 _totalBorrowAmount,
        uint256 _interestRateTimestamp,
        uint256 _blockTimestamp
    ) external pure returns (uint256 rcomp, int256 ri, int256 Tcrit);
}
"
    },
    "silo-core/contracts/interfaces/IInterestRateModelV2Factory.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

import {IInterestRateModelV2} from "./IInterestRateModelV2.sol";

interface IInterestRateModelV2Factory {
    /// @dev config hash and IRM should be easily accessible directly from oracle contract
    event NewInterestRateModelV2(bytes32 indexed configHash, IInterestRateModelV2 indexed irm);

    /// @dev verifies config and creates IRM config contract
    /// @notice it can be used in separate tx eg config can be prepared before it will be used for Silo creation
    /// @param _config IRM configuration
    /// @param _externalSalt external salt for the create2 call
    /// @return configHash the hashed config used as a key for IRM contract
    /// @return irm deployed (or existing one, depends on the config) contract address
    function create(IInterestRateModelV2.Config calldata _config, bytes32 _externalSalt)
        external
        returns (bytes32 configHash, IInterestRateModelV2 irm);

    /// @dev DP is 18 decimal points used for integer calculations
    // solhint-disable-next-line func-name-mixedcase
    function DP() external view returns (uint256);

    /// @dev verifies if config has correct values for a model, throws on invalid `_config`
    /// @param _config config that will ve verified
    function verifyConfig(IInterestRateModelV2.Config calldata _config) external view;

    /// @dev hashes IRM config
    /// @param _config IRM config
    /// @return configId hash of `_config`
    function hashConfig(IInterestRateModelV2.Config calldata _config) external pure returns (bytes32 configId);
}
"
    },
    "silo-core/contracts/interfaces/IDynamicKinkModelFactory.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

import {IDynamicKinkModel} from "./IDynamicKinkModel.sol";
import {IInterestRateModel} from "./IInterestRateModel.sol";

/// @title IDynamicKinkModelFactory
/// @notice Factory interface for creating Dynamic Kink Interest Rate Model instances
/// @dev This factory creates and manages DynamicKinkModel instances using a clone pattern.
///      It provides utilities for configuration generation, validation, and deterministic
///      address prediction using CREATE2.
/// 
///      Key Features:
///      - Creates DynamicKinkModel instances via cloning
///      - Converts user-friendly configs to internal model parameters
///      - Validates configurations before deployment
///      - Predicts deterministic addresses for CREATE2 deployments
///      - Tracks which models were created by this factory
/// 
///      Usage Flow:
///      1. Use generateConfig() to convert UserFriendlyConfig to internal Config
///      2. Use verifyConfig() to validate the configuration
///      3. Use create() to deploy a new DynamicKinkModel instance
///      4. Use predictAddress() to determine the deployment address beforehand
interface IDynamicKinkModelFactory {
    /// @notice Emitted when a new DynamicKinkModel instance is created
    /// @param irm The address of the newly created DynamicKinkModel instance
    event NewDynamicKinkModel(IDynamicKinkModel indexed irm);

    /// @notice Thrown when trying to predict address with zero deployer address
    error DeployerCannotBeZero();

    /// @notice Creates a new DynamicKinkModel instance using CREATE2
    /// @dev This function verifies the configuration, creates a clone of the implementation,
    ///      initializes it with the provided parameters, and tracks it in the factory.
    ///      The same salt will always produce the same address, enabling deterministic deployments.
    /// 
    ///      The function can be used in separate transactions - configurations can be prepared
    ///      and validated before being used for Silo creation.
    /// 
    /// @param _config Updatable configuration parameters
    /// @param _immutableArgs Immutable configuration arguments
    /// @param _initialOwner Address that will own and control the created model instance
    /// @param _silo Address of the Silo contract this model will serve
    /// @param _externalSalt External salt for the CREATE2 deterministic deployment
    /// @return irm The deployed DynamicKinkModel instance (IInterestRateModel interface)
    function create(
        IDynamicKinkModel.Config calldata _config, 
        IDynamicKinkModel.ImmutableArgs calldata _immutableArgs,
        address _initialOwner,
        address _silo,
        bytes32 _externalSalt
    )
        external
        returns (IInterestRateModel irm);

    /// @notice Converts user-friendly configuration to internal model parameters
    /// @dev This function takes intuitive configuration parameters and converts them to the
    ///      internal mathematical parameters used by the DynamicKinkModel. It performs
    ///      validation to ensure the configuration is mathematically sound and within
    ///      acceptable limits.
    /// 
    ///      The conversion includes:
    ///      - Converting annual rates to per-second rates
    ///      - Calculating slope parameters (kmin, kmax) from rate ranges
    ///      - Computing time-based coefficients (c1, c2, cminus, cplus)
    ///      - Validating parameter relationships and constraints
    /// 
    /// @param _default User-friendly configuration parameters (utilization thresholds, rates, times)
    /// @return config Internal configuration parameters ready for model initialization
    function generateConfig(IDynamicKinkModel.UserFriendlyConfig calldata _default)
        external
        view
        returns (IDynamicKinkModel.Config memory config);

    /// @notice Validates that configuration parameters are within acceptable limits
    /// @dev This function checks if all configuration parameters are within the safe operating ranges
    ///      defined by the model whitepaper. Some limits are narrower than the original whitepaper
    ///      due to additional research and safety considerations.
    /// 
    ///      For detailed limits, see:
    ///      https://silofinance.atlassian.net/wiki/spaces/SF/pages/347963393/DynamicKink+model+config+limits+V1
    /// 
    /// @param _config The configuration to validate (does not include model state)
    /// @custom:throws Reverts if any parameter is outside acceptable limits
    function verifyConfig(IDynamicKinkModel.Config calldata _config) external view;

    /// @notice Predicts the deterministic address of a DynamicKinkModel that would be created
    /// @dev This function calculates the address that would be generated by CREATE2 when
    ///      creating a DynamicKinkModel with the given deployer and salt. This enables
    ///      front-running protection and allows users to know the address before deployment.
    /// 
    ///      The same deployer and salt will always produce the same predicted address.
    /// 
    /// @param _deployer Address of the account that will deploy the model
    /// @param _externalSalt External salt for the CREATE2 deterministic deployment
    /// @return predictedAddress The address where the DynamicKinkModel would be deployed
    function predictAddress(address _deployer, bytes32 _externalSalt)
        external
        view
        returns (address predictedAddress);

    /// @notice Checks if a DynamicKinkModel was created by this factory
    /// @dev This function verifies whether a given address corresponds to a DynamicKinkModel
    ///      instance that was deployed through this factory. This is useful for:
    ///      - Verifying the authenticity of model instances
    ///      - Implementing access controls based on factory creation
    ///      - Tracking and managing factory-deployed models
    /// 
    /// @param _irm Address of the DynamicKinkModel contract to check
    /// @return isCreated True if the model was created by this factory, false otherwise
    function createdByFactory(address _irm) external view returns (bool isCreated);
}
"
    },
    "silo-core/contracts/interfaces/IInterestRateModel.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

interface IInterestRateModel {
    event InterestRateModelError();

    /// @dev Sets config address for all Silos that will use this model
    /// @param _irmConfig address of IRM config contract
    function initialize(address _irmConfig) external;

    /// @dev get compound interest rate and update model storage for current block.timestamp
    /// @param _collateralAssets total silo collateral assets
    /// @param _debtAssets total silo debt assets
    /// @param _interestRateTimestamp last IRM timestamp
    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)
    function getCompoundInterestRateAndUpdate(
        uint256 _collateralAssets,
        uint256 _debtAssets,
        uint256 _interestRateTimestamp
    )
        external
        returns (uint256 rcomp);

    /// @dev get compound interest rate
    /// @param _silo address of Silo for which interest rate should be calculated
    /// @param _blockTimestamp current block timestamp
    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)
    function getCompoundInterestRate(address _silo, uint256 _blockTimestamp)
        external
        view
        returns (uint256 rcomp);

    /// @dev get current annual interest rate
    /// @param _silo address of Silo for which interest rate should be calculated
    /// @param _blockTimestamp current block timestamp
    /// @return rcur current annual interest rate (1e18 == 100%)
    function getCurrentInterestRate(address _silo, uint256 _blockTimestamp)
        external
        view
        returns (uint256 rcur);

    /// @dev returns decimal points used by model
    function decimals() external view returns (uint256);
}
"
    },
    "silo-core/contracts/interfaces/IHookReceiver.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

import {ISiloConfig} from "./ISiloConfig.sol";

interface IHookReceiver {
    struct HookConfig {
        uint24 hooksBefore;
        uint24 hooksAfter;
    }

    event HookConfigured(address silo, uint24 hooksBefore, uint24 hooksAfter);

    /// @dev Revert if provided silo configuration during initialization is empty
    error EmptySiloConfig();
    /// @dev Revert if the hook receiver is already configured/initialized
    error AlreadyConfigured();
    /// @dev Revert if the caller is not a silo
    error OnlySilo();
    /// @dev Revert if the caller is not a silo or a share token
    error OnlySiloOrShareToken();

    /// @notice Initialize a hook receiver
    /// @param _siloConfig Silo configuration with all the details about the silo
    /// @param _data Data to initialize the hook receiver (if needed)
    function initialize(ISiloConfig _siloConfig, bytes calldata _data) external;

    /// @notice state of Silo before action, can be also without interest, if you need them, call silo.accrueInterest()
    function beforeAction(address _silo, uint256 _action, bytes calldata _input) external;

    function afterAction(address _silo, uint256 _action, bytes calldata _inputAndOutput) external;

    /// @notice return hooksBefore and hooksAfter configuration
    function hookReceiverConfig(address _silo) external view returns (uint24 hooksBefore, uint24 hooksAfter);
}
"
    },
    "silo-core/contracts/interfaces/ISiloDeployer.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IInterestRateModelV2} from "./IInterestRateModelV2.sol";
import {IDynamicKinkModel} from "./IDynamicKinkModel.sol";
import {ISiloConfig} from "./ISiloConfig.sol";

/// @notice Silo Deployer
interface ISiloDeployer {
    /// @dev Details of the oracle creation transaction
    struct OracleCreationTxData {
        address deployed; // if oracle is already deployed, this will be the address to use
        address factory; // oracle factory (chainlinkV3, uniswapV3, etc)
        bytes txInput; // fn input `abi.encodeCall(fn, params...)`
    }

    /// @dev Hook receiver to be cloned and initialized during the Silo creation
    struct ClonableHookReceiver {
        address implementation;
        bytes initializationData;
    }

    /// @dev Oracles to be create during the Silo creation.
    /// If an oracle for the provided config is already created an oracle factory will return its address.
    struct Oracles {
        OracleCreationTxData solvencyOracle0;
        OracleCreationTxData maxLtvOracle0;
        OracleCreationTxData solvencyOracle1;
        OracleCreationTxData maxLtvOracle1;
    }

    /// @dev DKinkIRM config to be created during the Silo creation.
    struct DKinkIRMConfig {
        IDynamicKinkModel.Config config;
        IDynamicKinkModel.ImmutableArgs immutableArgs;
        address initialOwner;
    }

    /// @dev Emit after the Silo creation
    event SiloCreated(ISiloConfig siloConfig);

    /// @dev Revert if an oracle factory fails to create an oracle
    error FailedToCreateAnOracle(address _factory);

    /// @dev Revert if for the deployment provided both hook receiver and hook receiver implementation
    error HookReceiverMisconfigured();

    /// @notice Deploy silo
    /// @param _oracles Oracles to be create during the silo creation
    /// @param _irmConfigData0 IRM config data for a silo `_TOKEN0`
    /// @param _irmConfigData1 IRM config data for a silo `_TOKEN1`
    /// @param _clonableHookReceiver Hook receiver implementation to clone (ignored if implementation has address(0))
    /// @param _siloInitData Silo configuration for the silo creation
    function deploy(
        Oracles calldata _oracles,
        bytes calldata _irmConfigData0,
        bytes calldata _irmConfigData1,
        ClonableHookReceiver calldata _clonableHookReceiver,
        ISiloConfig.InitData memory _siloInitData
    )
        external
        returns (ISiloConfig siloConfig);
}
"
    },
    "silo-core/contracts/SiloConfig.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {IERC20} from "openzeppelin5/token/ERC20/IERC20.sol";

import {ISilo} from "./interfaces/ISilo.sol";
import {ISiloConfig} from "./interfaces/ISiloConfig.sol";
import {CrossReentrancyGuard} from "./utils/CrossReentrancyGuard.sol";
import {Hook} from "./lib/Hook.sol";

/// @notice SiloConfig stores full configuration of Silo in immutable manner
/// @dev Immutable contract is more expensive to deploy than minimal proxy however it provides nearly 10x cheaper
/// data access using immutable variables.
contract SiloConfig is ISiloConfig, CrossReentrancyGuard {
    using Hook for uint256;
    
    uint256 public immutable SILO_ID;

    uint256 internal immutable _DAO_FEE;
    uint256 internal immutable _DEPLOYER_FEE;
    address internal immutable _HOOK_RECEIVER;

    // TOKEN #0

    address internal immutable _SILO0;

    address internal immutable _TOKEN0;

    /// @dev Token that represents a share in total protected deposits of Silo
    address internal immutable _PROTECTED_COLLATERAL_SHARE_TOKEN0;
    /// @dev Token that represents a share in total deposits of Silo
    address internal immutable _COLLATERAL_SHARE_TOKEN0;
    /// @dev Token that represents a share in total debt of Silo
    address internal immutable _DEBT_SHARE_TOKEN0;

    address internal immutable _SOLVENCY_ORACLE0;
    address internal immutable _MAX_LTV_ORACLE0;

    address internal immutable _INTEREST_RATE_MODEL0;

    uint256 internal immutable _MAX_LTV0;
    uint256 internal immutable _LT0;
    /// @dev target LTV after liquidation
    uint256 internal immutable _LIQUIDATION_TARGET_LTV0;
    uint256 internal immutable _LIQUIDATION_FEE0;
    uint256 internal immutable _FLASHLOAN_FEE0;

    bool internal immutable _CALL_BEFORE_QUOTE0;

    // TOKEN #1

    address internal immutable _SILO1;

    address internal immutable _TOKEN1;

    /// @dev Token that represents a share in total protected deposits of Silo
    address internal immutable _PROTECTED_COLLATERAL_SHARE_TOKEN1;
    /// @dev Token that represents a share in total deposits of Silo
    address internal immutable _COLLATERAL_SHARE_TOKEN1;
    /// @dev Token that represents a share in total debt of Silo
    address internal immutable _DEBT_SHARE_TOKEN1;

    address internal immutable _SOLVENCY_ORACLE1;
    address internal immutable _MAX_LTV_ORACLE1;

    address internal immutable _INTEREST_RATE_MODEL1;

    uint256 inte

Tags:
ERC20, ERC721, ERC165, Multisig, Mintable, Burnable, Non-Fungible, Liquidity, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xb627bdf951889deaafbe4cf1e8a8ae6ded8338f8|verified:true|block:23620902|tx:0x40b691ba13bd9378fd9789860bf1a3e737579e8c11eb8190087ecdf48b70a9e4|first_check:1761034729

Submitted on: 2025-10-21 10:18:52

Comments

Log in to comment.

No comments yet.