CapToken

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": {
    "contracts/token/CapToken.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import { Vault } from "../vault/Vault.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

/// @title Cap Token
/// @author kexley, Cap Labs
/// @notice Token representing the basket of underlying assets
contract CapToken is UUPSUpgradeable, Vault {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /// @notice Initialize the Cap token
    /// @param _name Name of the cap token
    /// @param _symbol Symbol of the cap token
    /// @param _accessControl Access controller
    /// @param _feeAuction Fee auction address
    /// @param _oracle Oracle address
    /// @param _assets Asset addresses to mint Cap token with
    /// @param _insuranceFund Insurance fund
    function initialize(
        string memory _name,
        string memory _symbol,
        address _accessControl,
        address _feeAuction,
        address _oracle,
        address[] calldata _assets,
        address _insuranceFund
    ) external initializer {
        __Vault_init(_name, _symbol, _accessControl, _feeAuction, _oracle, _assets, _insuranceFund);
        __UUPSUpgradeable_init();
    }

    /// @inheritdoc UUPSUpgradeable
    function _authorizeUpgrade(address) internal view override checkAccess(bytes4(0)) { }
}
"
    },
    "contracts/vault/Vault.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import { Access } from "../access/Access.sol";

import { IMinter } from "../interfaces/IMinter.sol";
import { IVault } from "../interfaces/IVault.sol";
import { VaultStorageUtils } from "../storage/VaultStorageUtils.sol";
import { FractionalReserve } from "./FractionalReserve.sol";
import { Minter } from "./Minter.sol";
import { VaultLogic } from "./libraries/VaultLogic.sol";
import {
    ERC20PermitUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

/// @title Vault for storing the backing for cTokens
/// @author kexley, Cap Labs
/// @notice Tokens are supplied by cToken minters and borrowed by covered agents
/// @dev Supplies, borrows and utilization rates are tracked. Interest rates should be computed and
/// charged on the external contracts, only the principle amount is counted on this contract.
abstract contract Vault is
    IVault,
    ERC20PermitUpgradeable,
    PausableUpgradeable,
    Access,
    Minter,
    FractionalReserve,
    VaultStorageUtils
{
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @inheritdoc IVault
    function mint(address _asset, uint256 _amountIn, uint256 _minAmountOut, address _receiver, uint256 _deadline)
        external
        whenNotPaused
        returns (uint256 amountOut)
    {
        uint256 fee;
        uint256 remainingMintCapacity = getRemainingMintCapacity(_asset);
        if (_amountIn > remainingMintCapacity) _amountIn = remainingMintCapacity;
        (amountOut, fee) = Minter.getMintAmount(_asset, _amountIn);
        VaultLogic.mint(
            getVaultStorage(),
            MintBurnParams({
                asset: _asset,
                amountIn: _amountIn,
                amountOut: amountOut,
                minAmountOut: _minAmountOut,
                receiver: _receiver,
                deadline: _deadline,
                fee: fee
            })
        );
        _mint(_receiver, amountOut);
        if (fee > 0) _mint(getVaultStorage().insuranceFund, fee);
    }

    /// @inheritdoc IVault
    function burn(address _asset, uint256 _amountIn, uint256 _minAmountOut, address _receiver, uint256 _deadline)
        external
        whenNotPaused
        returns (uint256 amountOut)
    {
        uint256 fee;
        (amountOut, fee) = getBurnAmount(_asset, _amountIn);

        _burn(msg.sender, _amountIn);

        divest(_asset, amountOut + fee);

        VaultLogic.burn(
            getVaultStorage(),
            MintBurnParams({
                asset: _asset,
                amountIn: _amountIn,
                amountOut: amountOut,
                minAmountOut: _minAmountOut,
                receiver: _receiver,
                deadline: _deadline,
                fee: fee
            })
        );
    }

    /// @inheritdoc IVault
    function redeem(uint256 _amountIn, uint256[] calldata _minAmountsOut, address _receiver, uint256 _deadline)
        external
        whenNotPaused
        returns (uint256[] memory amountsOut)
    {
        uint256[] memory fees;
        (amountsOut, fees) = getRedeemAmount(_amountIn);

        _burn(msg.sender, _amountIn);

        uint256[] memory totalDivestAmounts = new uint256[](amountsOut.length);
        for (uint256 i; i < amountsOut.length; i++) {
            totalDivestAmounts[i] = amountsOut[i] + fees[i];
        }
        divestMany(assets(), totalDivestAmounts);

        VaultLogic.redeem(
            getVaultStorage(),
            RedeemParams({
                amountIn: _amountIn,
                amountsOut: amountsOut,
                minAmountsOut: _minAmountsOut,
                receiver: _receiver,
                deadline: _deadline,
                fees: fees
            })
        );
    }

    /// @inheritdoc IVault
    function borrow(address _asset, uint256 _amount, address _receiver)
        external
        whenNotPaused
        checkAccess(this.borrow.selector)
    {
        divest(_asset, _amount);
        VaultLogic.borrow(getVaultStorage(), BorrowParams({ asset: _asset, amount: _amount, receiver: _receiver }));
    }

    /// @inheritdoc IVault
    function repay(address _asset, uint256 _amount) external whenNotPaused checkAccess(this.repay.selector) {
        VaultLogic.repay(getVaultStorage(), RepayParams({ asset: _asset, amount: _amount }));
    }

    /// @inheritdoc IVault
    function addAsset(address _asset) external checkAccess(this.addAsset.selector) {
        VaultLogic.addAsset(getVaultStorage(), _asset);
    }

    /// @inheritdoc IVault
    function removeAsset(address _asset) external checkAccess(this.removeAsset.selector) {
        VaultLogic.removeAsset(getVaultStorage(), _asset);
    }

    /// @inheritdoc IVault
    function pauseAsset(address _asset) external checkAccess(this.pauseAsset.selector) {
        VaultLogic.pause(getVaultStorage(), _asset);
    }

    /// @inheritdoc IVault
    function unpauseAsset(address _asset) external checkAccess(this.unpauseAsset.selector) {
        VaultLogic.unpause(getVaultStorage(), _asset);
    }

    /// @inheritdoc IVault
    function pauseProtocol() external checkAccess(this.pauseProtocol.selector) {
        _pause();
    }

    /// @inheritdoc IVault
    function unpauseProtocol() external checkAccess(this.unpauseProtocol.selector) {
        _unpause();
    }

    /// @inheritdoc IVault
    function setInsuranceFund(address _insuranceFund) external checkAccess(this.setInsuranceFund.selector) {
        VaultLogic.setInsuranceFund(getVaultStorage(), _insuranceFund);
    }

    /// @inheritdoc IVault
    function rescueERC20(address _asset, address _receiver) external checkAccess(this.rescueERC20.selector) {
        VaultLogic.rescueERC20(getVaultStorage(), getFractionalReserveStorage(), _asset, _receiver);
    }

    /// @inheritdoc IVault
    function assets() public view returns (address[] memory assetList) {
        assetList = getVaultStorage().assets.values();
    }

    /// @inheritdoc IVault
    function totalSupplies(address _asset) public view returns (uint256 _totalSupply) {
        _totalSupply = getVaultStorage().totalSupplies[_asset];
    }

    /// @inheritdoc IVault
    function totalBorrows(address _asset) external view returns (uint256 totalBorrow) {
        totalBorrow = getVaultStorage().totalBorrows[_asset];
    }

    /// @inheritdoc IVault
    function paused(address _asset) external view returns (bool isPaused) {
        isPaused = getVaultStorage().paused[_asset];
    }

    /// @inheritdoc IVault
    function availableBalance(address _asset) external view returns (uint256 amount) {
        amount = VaultLogic.availableBalance(getVaultStorage(), _asset);
    }

    /// @inheritdoc IVault
    function utilization(address _asset) external view returns (uint256 ratio) {
        ratio = VaultLogic.utilization(getVaultStorage(), _asset);
    }

    /// @inheritdoc IVault
    function currentUtilizationIndex(address _asset) external view returns (uint256 index) {
        index = VaultLogic.currentUtilizationIndex(getVaultStorage(), _asset);
    }

    /// @inheritdoc IVault
    function insuranceFund() external view returns (address) {
        return getVaultStorage().insuranceFund;
    }

    /// @inheritdoc IMinter
    function getMintAmount(address _asset, uint256 _amountIn)
        public
        view
        override
        returns (uint256 amountOut, uint256 fee)
    {
        uint256 remainingMintCapacity = getRemainingMintCapacity(_asset);
        if (_amountIn > remainingMintCapacity) _amountIn = remainingMintCapacity;
        (amountOut, fee) = Minter.getMintAmount(_asset, _amountIn);
    }

    /// @inheritdoc IVault
    function getRemainingMintCapacity(address _asset) public view returns (uint256 remainingMintCapacity) {
        uint256 totalSupply = totalSupplies(_asset);
        uint256 cap = depositCap(_asset);
        if (cap > totalSupply) remainingMintCapacity = cap - totalSupply;
    }

    /// @dev Initialize the assets
    /// @param _name Name of the cap token
    /// @param _symbol Symbol of the cap token
    /// @param _accessControl Access control address
    /// @param _feeAuction Fee auction address
    /// @param _oracle Oracle address
    /// @param _assets Asset addresses
    /// @param _insuranceFund Insurance fund
    function __Vault_init(
        string memory _name,
        string memory _symbol,
        address _accessControl,
        address _feeAuction,
        address _oracle,
        address[] calldata _assets,
        address _insuranceFund
    ) internal onlyInitializing {
        __ERC20_init(_name, _symbol);
        __ERC20Permit_init(_name);
        __Access_init(_accessControl);
        __FractionalReserve_init(_feeAuction);
        __Minter_init(_oracle);
        __Vault_init_unchained(_assets, _insuranceFund);
    }

    /// @dev Initialize unchained
    /// @param _assets Asset addresses
    /// @param _insuranceFund Insurance fund
    function __Vault_init_unchained(address[] calldata _assets, address _insuranceFund) internal onlyInitializing {
        VaultStorage storage $ = getVaultStorage();
        uint256 length = _assets.length;
        for (uint256 i; i < length; ++i) {
            $.assets.add(_assets[i]);
        }
        $.insuranceFund = _insuranceFund;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.22;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}
"
    },
    "contracts/access/Access.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import { IAccess } from "../interfaces/IAccess.sol";
import { IAccessControl } from "../interfaces/IAccessControl.sol";

import { AccessStorageUtils } from "../storage/AccessStorageUtils.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/// @title Access
/// @author kexley, Cap Labs
/// @notice Inheritable access
abstract contract Access is IAccess, Initializable, AccessStorageUtils {
    /// @dev Check caller has permissions for a function, revert if call is not allowed
    /// @param _selector Function selector
    modifier checkAccess(bytes4 _selector) {
        _checkAccess(_selector);
        _;
    }

    /// @dev Initialize the access control address
    /// @param _accessControl Access control address
    function __Access_init(address _accessControl) internal onlyInitializing {
        __Access_init_unchained(_accessControl);
    }

    /// @dev Initialize unchained
    /// @param _accessControl Access control address
    function __Access_init_unchained(address _accessControl) internal onlyInitializing {
        getAccessStorage().accessControl = _accessControl;
    }

    /// @dev Check caller has access to a function, revert overwise
    /// @param _selector Function selector
    function _checkAccess(bytes4 _selector) internal view {
        bool hasAccess =
            IAccessControl(getAccessStorage().accessControl).checkAccess(_selector, address(this), msg.sender);
        if (!hasAccess) revert AccessDenied();
    }
}
"
    },
    "contracts/interfaces/IMinter.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

/// @title IMinter
/// @author kexley, Cap Labs
/// @notice Interface for Minter contract
interface IMinter {
    /// @dev Minter storage
    /// @param oracle Oracle address
    /// @param redeemFee Redeem fee
    /// @param fees Fees for each asset
    /// @param whitelist Whitelist for users
    /// @param depositCap Deposit cap for each asset
    struct MinterStorage {
        address oracle;
        uint256 redeemFee;
        mapping(address => FeeData) fees;
        mapping(address => bool) whitelist;
        mapping(address => uint256) depositCap;
    }

    /// @dev Fee data set for an asset in a vault
    /// @param minMintFee Minimum mint fee
    /// @param slope0 Slope 0
    /// @param slope1 Slope 1
    /// @param mintKinkRatio Mint kink ratio
    /// @param burnKinkRatio Burn kink ratio
    /// @param optimalRatio Optimal ratio
    struct FeeData {
        uint256 minMintFee;
        uint256 slope0;
        uint256 slope1;
        uint256 mintKinkRatio;
        uint256 burnKinkRatio;
        uint256 optimalRatio;
    }

    /// @dev Parameters for applying fee slopes
    /// @param mint True if applying to mint, false if applying to burn
    /// @param amount Amount of asset to apply fee to
    /// @param ratio Ratio of fee to apply
    struct FeeSlopeParams {
        bool mint;
        uint256 amount;
        uint256 ratio;
    }

    /// @dev Parameters for minting or burning
    /// @param mint True if minting, false if burning
    /// @param asset Asset address
    /// @param amount Amount of asset to mint or burn
    struct AmountOutParams {
        bool mint;
        address asset;
        uint256 amount;
    }

    /// @dev Parameters for redeeming
    /// @param amount Amount of cap token to redeem
    struct RedeemAmountOutParams {
        uint256 amount;
    }

    /// @dev Fee data set for an asset in a vault
    event SetFeeData(address asset, FeeData feeData);

    /// @dev Redeem fee set
    event SetRedeemFee(uint256 redeemFee);

    /// @dev Whitelist set
    event SetWhitelist(address user, bool whitelisted);

    /// @dev Deposit cap set
    event SetDepositCap(address asset, uint256 cap);

    /// @dev Invalid minimum mint fee
    error InvalidMinMintFee();

    /// @dev Invalid mint kink ratio
    error InvalidMintKinkRatio();

    /// @dev Invalid burn kink ratio
    error InvalidBurnKinkRatio();

    /// @dev Invalid optimal ratio
    error InvalidOptimalRatio();

    /// @notice Get the fee data for an asset
    /// @param _asset Asset address
    /// @return feeData Fee data for the asset
    function getFeeData(address _asset) external view returns (FeeData memory feeData);

    /// @notice Set the allocation slopes and ratios for an asset
    /// @param _asset Asset address
    /// @param _feeData Fee slopes and ratios for the asset in the vault
    function setFeeData(address _asset, FeeData calldata _feeData) external;

    /// @notice Get the redeem fee
    /// @return redeemFee Redeem fee amount
    function getRedeemFee() external view returns (uint256 redeemFee);

    /// @notice Set the redeem fee
    /// @param _redeemFee Redeem fee amount
    function setRedeemFee(uint256 _redeemFee) external;

    /// @notice Set the whitelist for a user
    /// @param _user User address
    /// @param _whitelisted Whitelist state
    function setWhitelist(address _user, bool _whitelisted) external;

    /// @notice Set the deposit cap for an asset
    /// @param _asset Asset address
    /// @param _cap Deposit cap for the asset
    function setDepositCap(address _asset, uint256 _cap) external;

    /// @notice Get the mint amount for a given asset
    /// @param _asset Asset address
    /// @param _amountIn Amount of asset to use
    /// @return amountOut Amount minted
    /// @return fee Fee applied
    function getMintAmount(address _asset, uint256 _amountIn) external view returns (uint256 amountOut, uint256 fee);

    /// @notice Get the burn amount for a given asset
    /// @param _asset Asset address to withdraw
    /// @param _amountIn Amount of cap token to burn
    /// @return amountOut Amount of the asset withdrawn
    /// @return fee Fee applied
    function getBurnAmount(address _asset, uint256 _amountIn) external view returns (uint256 amountOut, uint256 fee);

    /// @notice Get the redeem amount
    /// @param _amountIn Amount of cap token to burn
    /// @return amountsOut Amounts of assets to be withdrawn
    /// @return redeemFees Amounts of redeem fees to be applied
    function getRedeemAmount(uint256 _amountIn)
        external
        view
        returns (uint256[] memory amountsOut, uint256[] memory redeemFees);

    /// @notice Is user whitelisted
    /// @param _user User address
    /// @return isWhitelisted Whitelist state
    function whitelisted(address _user) external view returns (bool isWhitelisted);

    /// @notice Get the deposit cap for an asset
    /// @param _asset Asset address
    /// @return cap Deposit cap for the asset
    function depositCap(address _asset) external view returns (uint256 cap);
}
"
    },
    "contracts/interfaces/IVault.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

/// @title Vault interface for storing the backing for cTokens
/// @author kexley, Cap Labs
/// @notice Interface for the Vault contract which handles supplies, borrows and utilization tracking
interface IVault {
    /// @dev Storage for the vault
    /// @param assets List of assets
    /// @param totalSupplies Total supplies of an asset
    /// @param totalBorrows Total borrows of an asset
    /// @param utilizationIndex Utilization index of an asset
    /// @param lastUpdate Last update time of an asset
    /// @param paused Pause state of an asset
    /// @param insuranceFund Insurance fund address
    struct VaultStorage {
        EnumerableSet.AddressSet assets;
        mapping(address => uint256) totalSupplies;
        mapping(address => uint256) totalBorrows;
        mapping(address => uint256) utilizationIndex;
        mapping(address => uint256) lastUpdate;
        mapping(address => bool) paused;
        address insuranceFund;
    }

    /// @dev Parameters for minting or burning
    /// @param asset Asset to mint or burn
    /// @param amountIn Amount of asset to use in the minting or burning
    /// @param amountOut Amount of cap token to mint or burn
    /// @param minAmountOut Minimum amount to mint or burn
    /// @param receiver Receiver of the minting or burning
    /// @param deadline Deadline of the tx
    /// @param fee Fee paid to the insurance fund
    struct MintBurnParams {
        address asset;
        uint256 amountIn;
        uint256 amountOut;
        uint256 minAmountOut;
        address receiver;
        uint256 deadline;
        uint256 fee;
    }

    /// @dev Parameters for redeeming
    /// @param amountIn Amount of cap token to burn
    /// @param amountsOut Amounts of assets to withdraw
    /// @param minAmountsOut Minimum amounts of assets to withdraw
    /// @param receiver Receiver of the withdrawal
    /// @param deadline Deadline of the tx
    /// @param fees Fees paid to the insurance fund
    struct RedeemParams {
        uint256 amountIn;
        uint256[] amountsOut;
        uint256[] minAmountsOut;
        address receiver;
        uint256 deadline;
        uint256[] fees;
    }

    /// @dev Parameters for borrowing
    /// @param asset Asset to borrow
    /// @param amount Amount of asset to borrow
    /// @param receiver Receiver of the borrow
    struct BorrowParams {
        address asset;
        uint256 amount;
        address receiver;
    }

    /// @dev Parameters for repaying
    /// @param asset Asset to repay
    /// @param amount Amount of asset to repay
    struct RepayParams {
        address asset;
        uint256 amount;
    }

    /// @notice Mint the cap token using an asset
    /// @dev This contract must have approval to move asset from msg.sender
    /// @dev The amount in is capped by the deposit cap of the asset
    /// @param _asset Whitelisted asset to deposit
    /// @param _amountIn Amount of asset to use in the minting
    /// @param _minAmountOut Minimum amount to mint
    /// @param _receiver Receiver of the minting
    /// @param _deadline Deadline of the tx
    function mint(address _asset, uint256 _amountIn, uint256 _minAmountOut, address _receiver, uint256 _deadline)
        external
        returns (uint256 amountOut);

    /// @notice Burn the cap token for an asset
    /// @dev Asset is withdrawn from the reserve or divested from the underlying vault
    /// @param _asset Asset to withdraw
    /// @param _amountIn Amount of cap token to burn
    /// @param _minAmountOut Minimum amount out to receive
    /// @param _receiver Receiver of the withdrawal
    /// @param _deadline Deadline of the tx
    function burn(address _asset, uint256 _amountIn, uint256 _minAmountOut, address _receiver, uint256 _deadline)
        external
        returns (uint256 amountOut);

    /// @notice Redeem the Cap token for a bundle of assets
    /// @dev Assets are withdrawn from the reserve or divested from the underlying vault
    /// @param _amountIn Amount of Cap token to burn
    /// @param _minAmountsOut Minimum amounts of assets to withdraw
    /// @param _receiver Receiver of the withdrawal
    /// @param _deadline Deadline of the tx
    /// @return amountsOut Amount of assets withdrawn
    function redeem(uint256 _amountIn, uint256[] calldata _minAmountsOut, address _receiver, uint256 _deadline)
        external
        returns (uint256[] memory amountsOut);

    /// @notice Borrow an asset
    /// @dev Whitelisted agents can borrow any amount, LTV is handled by Agent contracts
    /// @param _asset Asset to borrow
    /// @param _amount Amount of asset to borrow
    /// @param _receiver Receiver of the borrow
    function borrow(address _asset, uint256 _amount, address _receiver) external;

    /// @notice Repay an asset
    /// @param _asset Asset to repay
    /// @param _amount Amount of asset to repay
    function repay(address _asset, uint256 _amount) external;

    /// @notice Add an asset to the vault list
    /// @param _asset Asset address
    function addAsset(address _asset) external;

    /// @notice Remove an asset from the vault list
    /// @param _asset Asset address
    function removeAsset(address _asset) external;

    /// @notice Pause an asset
    /// @param _asset Asset address
    function pauseAsset(address _asset) external;

    /// @notice Unpause an asset
    /// @param _asset Asset address
    function unpauseAsset(address _asset) external;

    /// @notice Pause all protocol operations
    function pauseProtocol() external;

    /// @notice Unpause all protocol operations
    function unpauseProtocol() external;

    /// @notice Set the insurance fund
    /// @param _insuranceFund Insurance fund address
    function setInsuranceFund(address _insuranceFund) external;

    /// @notice Rescue an unsupported asset
    /// @param _asset Asset to rescue
    /// @param _receiver Receiver of the rescue
    function rescueERC20(address _asset, address _receiver) external;

    /// @notice Get the list of assets supported by the vault
    /// @return assetList List of assets
    function assets() external view returns (address[] memory assetList);

    /// @notice Get the total supplies of an asset
    /// @param _asset Asset address
    /// @return totalSupply Total supply
    function totalSupplies(address _asset) external view returns (uint256 totalSupply);

    /// @notice Get the total borrows of an asset
    /// @param _asset Asset address
    /// @return totalBorrow Total borrow
    function totalBorrows(address _asset) external view returns (uint256 totalBorrow);

    /// @notice Get the pause state of an asset
    /// @param _asset Asset address
    /// @return isPaused Pause state
    function paused(address _asset) external view returns (bool isPaused);

    /// @notice Available balance to borrow
    /// @param _asset Asset to borrow
    /// @return amount Amount available
    function availableBalance(address _asset) external view returns (uint256 amount);

    /// @notice Utilization rate of an asset
    /// @dev Utilization scaled by 1e27
    /// @param _asset Utilized asset
    /// @return ratio Utilization ratio
    function utilization(address _asset) external view returns (uint256 ratio);

    /// @notice Up to date cumulative utilization index of an asset
    /// @dev Utilization scaled by 1e27
    /// @param _asset Utilized asset
    /// @return index Utilization ratio index
    function currentUtilizationIndex(address _asset) external view returns (uint256 index);

    /// @notice Get the insurance fund
    /// @return insuranceFund Insurance fund
    function insuranceFund() external view returns (address);

    /// @notice Get the remaining mint capacity of an asset
    /// @param _asset Asset address
    /// @return remainingMintCapacity Remaining mint capacity
    function getRemainingMintCapacity(address _asset) external view returns (uint256 remainingMintCapacity);
}
"
    },
    "contracts/storage/VaultStorageUtils.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import { IVault } from "../interfaces/IVault.sol";

/// @title VaultStorageUtils
/// @author kexley, Cap Labs
/// @notice Storage utilities for Vault contract
abstract contract VaultStorageUtils {
    /// @dev keccak256(abi.encode(uint256(keccak256("cap.storage.Vault")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 constant VaultStorageLocation = 0xe912a1b0cc7579bc5827e495c2ce52587bc3871751e3281fc5599b38c3bfc400;

    /// @notice Get vault storage
    /// @return $ Storage pointer
    function getVaultStorage() internal pure returns (IVault.VaultStorage storage $) {
        assembly {
            $.slot := VaultStorageLocation
        }
    }
}
"
    },
    "contracts/vault/FractionalReserve.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { Access } from "../access/Access.sol";

import { IFractionalReserve } from "../interfaces/IFractionalReserve.sol";
import { FractionalReserveStorageUtils } from "../storage/FractionalReserveStorageUtils.sol";
import { FractionalReserveLogic } from "./libraries/FractionalReserveLogic.sol";

import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

/// @title Fractional Reserve
/// @author kexley, Cap Labs
/// @notice Idle capital is put to work in fractional reserve vaults and can be recalled when withdrawing, redeeming or borrowing.
abstract contract FractionalReserve is IFractionalReserve, Access, FractionalReserveStorageUtils {
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @inheritdoc IFractionalReserve
    function investAll(address _asset) external checkAccess(this.investAll.selector) {
        FractionalReserveLogic.invest(getFractionalReserveStorage(), _asset);
    }

    /// @inheritdoc IFractionalReserve
    function divestAll(address _asset) external checkAccess(this.divestAll.selector) {
        FractionalReserveLogic.divest(getFractionalReserveStorage(), _asset);
    }

    /// @inheritdoc IFractionalReserve
    function setFractionalReserveVault(address _asset, address _vault)
        external
        checkAccess(this.setFractionalReserveVault.selector)
    {
        FractionalReserveStorage storage $ = getFractionalReserveStorage();
        FractionalReserveLogic.divest($, _asset);
        FractionalReserveLogic.setFractionalReserveVault($, _asset, _vault);
    }

    /// @inheritdoc IFractionalReserve
    function setReserve(address _asset, uint256 _reserve) external checkAccess(this.setReserve.selector) {
        FractionalReserveLogic.setReserve(getFractionalReserveStorage(), _asset, _reserve);
    }

    /// @inheritdoc IFractionalReserve
    function realizeInterest(address _asset) external {
        FractionalReserveLogic.realizeInterest(getFractionalReserveStorage(), _asset);
    }

    /// @inheritdoc IFractionalReserve
    function claimableInterest(address _asset) external view returns (uint256 interest) {
        interest = FractionalReserveLogic.claimableInterest(getFractionalReserveStorage(), _asset);
    }

    /// @inheritdoc IFractionalReserve
    function fractionalReserveVault(address _asset) external view returns (address vaultAddress) {
        vaultAddress = getFractionalReserveStorage().vault[_asset];
    }

    /// @inheritdoc IFractionalReserve
    function fractionalReserveVaults() external view returns (address[] memory vaultAddresses) {
        vaultAddresses = getFractionalReserveStorage().vaults.values();
    }

    /// @inheritdoc IFractionalReserve
    function reserve(address _asset) external view returns (uint256 reserveAmount) {
        reserveAmount = getFractionalReserveStorage().reserve[_asset];
    }

    /// @inheritdoc IFractionalReserve
    function loaned(address _asset) external view returns (uint256 loanedAmount) {
        loanedAmount = getFractionalReserveStorage().loaned[_asset];
    }

    /// @inheritdoc IFractionalReserve
    function interestReceiver() external view returns (address _interestReceiver) {
        _interestReceiver = getFractionalReserveStorage().interestReceiver;
    }

    /// @dev Initialize unchained
    /// @param _interestReceiver Interest receiver address
    function __FractionalReserve_init(address _interestReceiver) internal onlyInitializing {
        getFractionalReserveStorage().interestReceiver = _interestReceiver;
    }

    /// @dev Divest an asset from a fractional reserve vault
    /// @param _asset Asset address
    /// @param _amountOut Amount to divest
    function divest(address _asset, uint256 _amountOut) internal {
        FractionalReserveLogic.divest(getFractionalReserveStorage(), _asset, _amountOut);
    }

    /// @dev Divest many assets from a fractional reserve vault
    /// @param _assets Assets to divest
    /// @param _amountsOut Amounts to divest
    function divestMany(address[] memory _assets, uint256[] memory _amountsOut) internal {
        FractionalReserveStorage storage $ = getFractionalReserveStorage();
        for (uint256 i; i < _assets.length; ++i) {
            FractionalReserveLogic.divest($, _assets[i], _amountsOut[i]);
        }
    }
}
"
    },
    "contracts/vault/Minter.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import { Access } from "../access/Access.sol";

import { IMinter } from "../interfaces/IMinter.sol";
import { MinterStorageUtils } from "../storage/MinterStorageUtils.sol";
import { MinterLogic } from "./libraries/MinterLogic.sol";

/// @title Minter/burner for cap tokens
/// @author kexley, Cap Labs
/// @notice Cap tokens are minted or burned in exchange for collateral ratio of the backing tokens
/// @dev Dynamic fees are applied according to the allocation of assets in the basket. Increasing
/// the supply of a excessive asset or burning for an scarce asset will charge fees on a kinked
/// slope. Redeem can be used to avoid these fees by burning for the current ratio of assets.
abstract contract Minter is IMinter, Access, MinterStorageUtils {
    /// @inheritdoc IMinter
    function getFeeData(address _asset) external view returns (FeeData memory feeData) {
        feeData = getMinterStorage().fees[_asset];
    }

    /// @inheritdoc IMinter
    function setFeeData(address _asset, FeeData calldata _feeData) external checkAccess(this.setFeeData.selector) {
        if (_feeData.minMintFee >= 0.05e27) revert InvalidMinMintFee();
        if (_feeData.mintKinkRatio >= 1e27 || _feeData.mintKinkRatio == 0) revert InvalidMintKinkRatio();
        if (_feeData.burnKinkRatio >= 1e27 || _feeData.burnKinkRatio == 0) revert InvalidBurnKinkRatio();
        if (_feeData.optimalRatio >= 1e27 || _feeData.optimalRatio == 0) revert InvalidOptimalRatio();
        if (_feeData.optimalRatio == _feeData.mintKinkRatio || _feeData.optimalRatio == _feeData.burnKinkRatio) {
            revert InvalidOptimalRatio();
        }
        getMinterStorage().fees[_asset] = _feeData;
        emit SetFeeData(_asset, _feeData);
    }

    /// @inheritdoc IMinter
    function getRedeemFee() external view returns (uint256 redeemFee) {
        redeemFee = getMinterStorage().redeemFee;
    }

    /// @inheritdoc IMinter
    function setRedeemFee(uint256 _redeemFee) external checkAccess(this.setRedeemFee.selector) {
        getMinterStorage().redeemFee = _redeemFee;
        emit SetRedeemFee(_redeemFee);
    }

    /// @inheritdoc IMinter
    function setWhitelist(address _user, bool _whitelisted) external checkAccess(this.setWhitelist.selector) {
        getMinterStorage().whitelist[_user] = _whitelisted;
        emit SetWhitelist(_user, _whitelisted);
    }

    /// @inheritdoc IMinter
    function setDepositCap(address _asset, uint256 _cap) external checkAccess(this.setDepositCap.selector) {
        getMinterStorage().depositCap[_asset] = _cap;
        emit SetDepositCap(_asset, _cap);
    }

    /// @inheritdoc IMinter
    function getMintAmount(address _asset, uint256 _amountIn)
        public
        view
        virtual
        returns (uint256 amountOut, uint256 fee)
    {
        (amountOut, fee) = MinterLogic.amountOut(
            getMinterStorage(), AmountOutParams({ mint: true, asset: _asset, amount: _amountIn })
        );
    }

    /// @inheritdoc IMinter
    function getBurnAmount(address _asset, uint256 _amountIn) public view returns (uint256 amountOut, uint256 fee) {
        (amountOut, fee) = MinterLogic.amountOut(
            getMinterStorage(), AmountOutParams({ mint: false, asset: _asset, amount: _amountIn })
        );
    }

    /// @inheritdoc IMinter
    function getRedeemAmount(uint256 _amountIn)
        public
        view
        returns (uint256[] memory amountsOut, uint256[] memory fees)
    {
        (amountsOut, fees) = MinterLogic.redeemAmountOut(
            getMinterStorage(), RedeemAmountOutParams({ amount: _amountIn })
        );
    }

    /// @inheritdoc IMinter
    function whitelisted(address _user) external view returns (bool isWhitelisted) {
        isWhitelisted = getMinterStorage().whitelist[_user];
    }

    /// @inheritdoc IMinter
    function depositCap(address _asset) public view returns (uint256 cap) {
        cap = getMinterStorage().depositCap[_asset];
    }

    /// @dev Initialize unchained
    /// @param _oracle Oracle address
    function __Minter_init(address _oracle) internal onlyInitializing {
        getMinterStorage().oracle = _oracle;
    }
}
"
    },
    "contracts/vault/libraries/VaultLogic.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import { IFractionalReserve } from "../../interfaces/IFractionalReserve.sol";
import { IVault } from "../../interfaces/IVault.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

/// @title Vault for storing the backing for cTokens
/// @author kexley, Cap Labs
/// @notice Tokens are supplied by cToken minters and borrowed by covered agents
/// @dev Supplies, borrows and utilization rates are tracked. Interest rates should be computed and
/// charged on the external contracts, only the principle amount is counted on this contract.
library VaultLogic {
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @dev Cap token minted
    event Mint(
        address indexed minter,
        address receiver,
        address indexed asset,
        uint256 amountIn,
        uint256 amountOut,
        uint256 fee
    );

    /// @dev Cap token burned
    event Burn(
        address indexed burner,
        address receiver,
        address indexed asset,
        uint256 amountIn,
        uint256 amountOut,
        uint256 fee
    );

    /// @dev Cap token redeemed
    event Redeem(address indexed redeemer, address receiver, uint256 amountIn, uint256[] amountsOut, uint256[] fees);

    /// @dev Borrow made
    event Borrow(address indexed borrower, address indexed asset, uint256 amount);

    /// @dev Repayment made
    event Repay(address indexed repayer, address indexed asset, uint256 amount);

    /// @dev Add asset
    event AddAsset(address asset);

    /// @dev Remove asset
    event RemoveAsset(address asset);

    /// @dev Asset paused
    event PauseAsset(address asset);

    /// @dev Asset unpaused
    event UnpauseAsset(address asset);

    /// @dev Rescue unsupported ERC20 tokens
    event RescueERC20(address asset, address receiver);

    /// @dev Set the insurance fund
    event SetInsuranceFund(address insuranceFund);

    /// @dev Timestamp is past the deadline
    error PastDeadline();

    /// @dev Amount out is less than required
    error Slippage(address asset, uint256 amountOut, uint256 minAmountOut);

    /// @dev Amount out is 0
    error InvalidAmount();

    /// @dev Paused assets cannot be supplied or borrowed
    error AssetPaused(address asset);

    /// @dev Only whitelisted assets can be supplied or borrowed
    error AssetNotSupported(address asset);

    /// @dev Asset is already listed
    error AssetAlreadySupported(address asset);

    /// @dev Asset has supplies
    error AssetHasSupplies(address asset);

    /// @dev Only non-supported assets can be rescued
    error AssetNotRescuable(address asset);

    /// @dev Invalid min amounts out as they dont match the number of assets
    error InvalidMinAmountsOut();

    /// @dev Insufficient reserves
    error InsufficientReserves(address asset, uint256 balanceBefore, uint256 amount);

    /// @dev Modifier to only allow supplies and borrows when not paused
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    modifier whenNotPaused(IVault.VaultStorage storage $, address _asset) {
        _whenNotPaused($, _asset);
        _;
    }

    /// @dev Modifier to update the utilization index
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    modifier updateIndex(IVault.VaultStorage storage $, address _asset) {
        _updateIndex($, _asset);
        _;
    }

    /// @notice Mint the cap token using an asset
    /// @dev This contract must have approval to move asset from msg.sender
    /// @param $ Vault storage pointer
    /// @param params Mint parameters
    function mint(IVault.VaultStorage storage $, IVault.MintBurnParams memory params)
        external
        whenNotPaused($, params.asset)
        updateIndex($, params.asset)
    {
        if (params.deadline < block.timestamp) revert PastDeadline();
        if (params.amountOut < params.minAmountOut) {
            revert Slippage(address(this), params.amountOut, params.minAmountOut);
        }
        if (params.amountOut == 0) revert InvalidAmount();

        $.totalSupplies[params.asset] += params.amountIn;

        IERC20(params.asset).safeTransferFrom(msg.sender, address(this), params.amountIn);

        emit Mint(msg.sender, params.receiver, params.asset, params.amountIn, params.amountOut, params.fee);
    }

    /// @notice Burn the cap token for an asset
    /// @dev Can only withdraw up to the amount remaining on this contract
    /// @param $ Vault storage pointer
    /// @param params Burn parameters
    function burn(IVault.VaultStorage storage $, IVault.MintBurnParams memory params)
        external
        updateIndex($, params.asset)
    {
        if (params.deadline < block.timestamp) revert PastDeadline();
        if (params.amountOut < params.minAmountOut) {
            revert Slippage(params.asset, params.amountOut, params.minAmountOut);
        }
        if (params.amountOut == 0) revert InvalidAmount();

        _verifyBalance($, params.asset, params.amountOut + params.fee);

        $.totalSupplies[params.asset] -= params.amountOut + params.fee;

        IERC20(params.asset).safeTransfer(params.receiver, params.amountOut);
        if (params.fee > 0) IERC20(params.asset).safeTransfer($.insuranceFund, params.fee);

        emit Burn(msg.sender, params.receiver, params.asset, params.amountIn, params.amountOut, params.fee);
    }

    /// @notice Redeem the Cap token for a bundle of assets
    /// @dev Can only withdraw up to the amount remaining on this contract
    /// @param $ Vault storage pointer
    /// @param params Redeem parameters
    function redeem(IVault.VaultStorage storage $, IVault.RedeemParams memory params) external {
        if (params.amountsOut.length != params.minAmountsOut.length) revert InvalidMinAmountsOut();
        if (params.deadline < block.timestamp) revert PastDeadline();

        uint256 length = $.assets.length();
        for (uint256 i; i < length; ++i) {
            address asset = $.assets.at(i);
            if (params.amountsOut[i] < params.minAmountsOut[i]) {
                revert Slippage(asset, params.amountsOut[i], params.minAmountsOut[i]);
            }
            _verifyBalance($, asset, params.amountsOut[i] + params.fees[i]);
            _updateIndex($, asset);
            $.totalSupplies[asset] -= params.amountsOut[i] + params.fees[i];
            IERC20(asset).safeTransfer(params.receiver, params.amountsOut[i]);
            if (params.fees[i] > 0) IERC20(asset).safeTransfer($.insuranceFund, params.fees[i]);
        }

        emit Redeem(msg.sender, params.receiver, params.amountIn, params.amountsOut, params.fees);
    }

    /// @notice Borrow an asset
    /// @dev Whitelisted agents can borrow any amount, LTV is handled by Agent contracts
    /// @param $ Vault storage pointer
    /// @param params Borrow parameters
    function borrow(IVault.VaultStorage storage $, IVault.BorrowParams memory params)
        external
        whenNotPaused($, params.asset)
        updateIndex($, params.asset)
    {
        _verifyBalance($, params.asset, params.amount);

        $.totalBorrows[params.asset] += params.amount;
        IERC20(params.asset).safeTransfer(params.receiver, params.amount);

        emit Borrow(msg.sender, params.asset, params.amount);
    }

    /// @notice Repay an asset
    /// @param $ Vault storage pointer
    /// @param params Repay parameters
    function repay(IVault.VaultStorage storage $, IVault.RepayParams memory params)
        external
        updateIndex($, params.asset)
    {
        $.totalBorrows[params.asset] -= params.amount;
        IERC20(params.asset).safeTransferFrom(msg.sender, address(this), params.amount);

        emit Repay(msg.sender, params.asset, params.amount);
    }

    /// @notice Add an asset to the vault list
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    function addAsset(IVault.VaultStorage storage $, address _asset) external {
        if (!$.assets.add(_asset)) revert AssetNotSupported(_asset);
        emit AddAsset(_asset);
    }

    /// @notice Remove an asset from the vault list
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    function removeAsset(IVault.VaultStorage storage $, address _asset) external {
        if ($.totalSupplies[_asset] > 0) revert AssetHasSupplies(_asset);
        if (!$.assets.remove(_asset)) revert AssetNotSupported(_asset);
        emit RemoveAsset(_asset);
    }

    /// @notice Pause an asset
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    function pause(IVault.VaultStorage storage $, address _asset) external {
        $.paused[_asset] = true;
        emit PauseAsset(_asset);
    }

    /// @notice Unpause an asset
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    function unpause(IVault.VaultStorage storage $, address _asset) external {
        $.paused[_asset] = false;
        emit UnpauseAsset(_asset);
    }

    /// @notice Set the insurance fund
    /// @param $ Vault storage pointer
    /// @param _insuranceFund Insurance fund address
    function setInsuranceFund(IVault.VaultStorage storage $, address _insuranceFund) external {
        $.insuranceFund = _insuranceFund;
        emit SetInsuranceFund(_insuranceFund);
    }

    /// @notice Rescue an unsupported asset
    /// @param $ Vault storage pointer
    /// @param reserve Fractional reserve storage pointer
    /// @param _asset Asset to rescue
    /// @param _receiver Receiver of the rescue
    function rescueERC20(
        IVault.VaultStorage storage $,
        IFractionalReserve.FractionalReserveStorage storage reserve,
        address _asset,
        address _receiver
    ) external {
        if (_listed($, _asset) || reserve.vaults.contains(_asset)) revert AssetNotRescuable(_asset);
        IERC20(_asset).safeTransfer(_receiver, IERC20(_asset).balanceOf(address(this)));
        emit RescueERC20(_asset, _receiver);
    }

    /// @notice Calculate the available balance of an asset
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    /// @return balance Available balance
    function availableBalance(IVault.VaultStorage storage $, address _asset) public view returns (uint256 balance) {
        balance = $.totalSupplies[_asset] - $.totalBorrows[_asset];
    }

    /// @notice Calculate the utilization ratio of an asset
    /// @dev Returns the ratio of borrowed assets to total supply, scaled to ray (1e27)
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    /// @return ratio Utilization ratio in ray (1e27)
    function utilization(IVault.VaultStorage storage $, address _asset) public view returns (uint256 ratio) {
        ratio = $.totalSupplies[_asset] != 0 ? $.totalBorrows[_asset] * 1e27 / $.totalSupplies[_asset] : 0;
    }

    /// @notice Up to date cumulative utilization index of an asset
    /// @dev Utilization and index are both scaled in ray (1e27)
    /// @param $ Vault storage pointer
    /// @param _asset Utilized asset
    /// @return index Utilization ratio index in ray (1e27)
    function currentUtilizationIndex(IVault.VaultStorage storage $, address _asset)
        external
        view
        returns (uint256 index)
    {
        index = $.utilizationIndex[_asset] + (utilization($, _asset) * (block.timestamp - $.lastUpdate[_asset]));
    }

    /// @dev Validate that an asset is listed
    /// @param $ Vault storage pointer
    /// @param _asset Asset to check
    /// @return isListed Asset is listed or not
    function _listed(IVault.VaultStorage storage $, address _asset) internal view returns (bool isListed) {
        isListed = $.assets.contains(_asset);
    }

    /// @dev Verify that an asset has enough balance
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    /// @param _amount Amount to verify
    function _verifyBalance(IVault.VaultStorage storage $, address _asset, uint256 _amount) internal view {
        uint256 balance = availableBalance($, _asset);
        if (balance < _amount) {
            revert InsufficientReserves(_asset, balance, _amount);
        }
    }

    /// @dev Only allow supplies and borrows when not paused
    /// @param $ Vault storage pointer
    /// @param _asset Asset address
    function _whenNotPaused(IVault.VaultStorage storage $, address _asset) private view {
        if ($.paused[_asset]) revert AssetPaused(_asset);
    }

    /// @dev Update the cumulative utilization index of an asset
    /// @param $ Vault storage pointer
    /// @param _asset Utilized asset
    function _updateIndex(IVault.VaultStorage storage $, address _asset) internal {
        if (!_listed($, _asset)) revert AssetNotSupported(_asset);
        $.utilizationIndex[_asset] += utilization($, _asset) * (block.timestamp - $.lastUpdate[_asset]);
        $.lastUpdate[_asset] = block.timestamp;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/ERC20Permit.sol)

pragma solidity ^0.8.20;

import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {ERC20Upgradeable} from "../ERC20Upgradeable.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {EIP712Upgradeable} from "../../../utils/cryptography/EIP712Upgradeable.sol";
import {NoncesUpgradeable} from "../../../utils/NoncesUpgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20Permit, EIP712Upgradeable, NoncesUpgradeable {
    bytes32 private constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    /**
     * @dev Permit deadline has expired.
     */
    error ERC2612ExpiredSignature(uint256 deadline);

    /**
     * @dev Mismatched signature.
     */
    error ERC2612InvalidSigner(address signer, address owner);

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC-20 token name.
     */
    function __ERC20Permit_init(string memory name) internal onlyInitializing {
        __EIP712_init_unchained(name, "1");
    }

    function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {}

    /**
     * @inheritdoc IERC20Permit
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        if (block.timestamp > deadline) {
            revert ERC2612ExpiredSignature(deadline);
        }

        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        if (signer != owner) {
            revert ERC2612InvalidSigner(signer, owner);
        }

        _approve(owner, spender, value);
    }

    /**
     * @inheritdoc IERC20Permit
     */
    function nonces(address owner) public view virtual override(IERC20Permit, NoncesUpgradeable) returns (uint256) {
        return super.nonces(owner);
    }

    /**
     * @inheritdoc IERC20Permit
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
        return _domainSeparatorV4();
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
    struct PausableStorage {
        bool _paused;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;

    function _getPausableStorage() private pure returns (PausableStorage storage $) {
        assembly {
            $.slot := PausableStorageLocation
        }
    }

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        PausableStorage storage $ = _getPausableStorage();
        return $._paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
        emit Unpaused(_msgSender());
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/structs/EnumerableSet.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type 

Tags:
ERC20, ERC165, Multisig, Mintable, Burnable, Pausable, Swap, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x5c47b527d98f057680156d61c1b997f14b9b4f82|verified:true|block:23742432|tx:0x70b12a21052270239477ebf57aea1c88591801dc1db7f62eb20ff5a6d8b0a26c|first_check:1762508087

Submitted on: 2025-11-07 10:34:48

Comments

Log in to comment.

No comments yet.