ReservoirFarm

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": {
    "src/integrations/farms/ReservoirFarm.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";

import {Farm} from "@integrations/Farm.sol";
import {IFarm} from "@interfaces/IFarm.sol";
import {CoreRoles} from "@libraries/CoreRoles.sol";
import {Accounting} from "@finance/Accounting.sol";
import {IMaturityFarm} from "@interfaces/IMaturityFarm.sol";
import {MultiAssetFarm} from "@integrations/MultiAssetFarm.sol";
import {CoWSwapFarmBase} from "@integrations/farms/CoWSwapFarmBase.sol";

interface IReservoirPSM {
    function redeem(address receiver, uint256 amount) external;
}

interface IReservoirCreditEnforcer {
    function mintStablecoin(uint256 amount) external;
}

/// @title Reservoir Farm
/// @notice This contract can hold USDC, rUSD, and wsrUSD.
contract ReservoirFarm is CoWSwapFarmBase, IMaturityFarm {
    using SafeERC20 for IERC20;
    using FixedPointMathLib for uint256;

    uint256 private constant _RUSD_TO_USDC_SCALE = 1e12;

    address public constant _USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    address public constant _RUSD = 0x09D4214C03D01F49544C0448DBE3A27f768F2b34;
    address public constant _WSRUSD = 0xd3fD63209FA2D55B07A0f6db36C2f43900be3094;
    address public constant _PSM = 0x4809010926aec940b550D34a46A52739f996D75D;
    address public constant _CREDIT_ENFORCER = 0x04716DB62C085D9e08050fcF6F7D775A03d07720;
    address public constant _COW_SETTLEMENT = 0x9008D19f58AAbD9eD0D60971565AA8510560ab41;
    address public constant _COW_VAULT_RELAYER = 0xC92E8bdf79f0507f65a392b0ab4667716BFE0110;

    constructor(address _core, address _accounting)
        MultiAssetFarm(_core, _USDC, _accounting)
        CoWSwapFarmBase(_COW_SETTLEMENT, _COW_VAULT_RELAYER)
    {
        maxSlippage = 0.999e18; // default: max 0.1% slippage
    }

    function assetTokens() public pure override returns (address[] memory) {
        address[] memory tokens = new address[](3);
        tokens[0] = _USDC;
        tokens[1] = _RUSD;
        tokens[2] = _WSRUSD;
        return tokens;
    }

    function isAssetSupported(address _asset) public pure override returns (bool) {
        return _asset == _USDC || _asset == _RUSD || _asset == _WSRUSD;
    }

    function maturity() public view virtual override returns (uint256) {
        return block.timestamp + 7 days;
    }

    /// @notice Wrap USDC to rUSD.
    function wrap(uint256 _usdcIn) external whenNotPaused onlyCoreRole(CoreRoles.FARM_SWAP_CALLER) {
        IERC20(_USDC).approve(_PSM, _usdcIn);
        IReservoirCreditEnforcer(_CREDIT_ENFORCER).mintStablecoin(_usdcIn);
    }

    /// @notice Unwrap rUSD to USDC.
    function unwrap(uint256 _rUsdIn) external whenNotPaused onlyCoreRole(CoreRoles.FARM_SWAP_CALLER) {
        IERC20(_RUSD).approve(_PSM, _rUsdIn);
        IReservoirPSM(_PSM).redeem(address(this), _rUsdIn / _RUSD_TO_USDC_SCALE);
        IERC20(_RUSD).approve(_PSM, 0); // reset approval due to rounding above
    }

    /// @notice Stake rUSD to wsrUSD.
    function stake(uint256 _rUsdIn) external whenNotPaused onlyCoreRole(CoreRoles.FARM_SWAP_CALLER) {
        IERC20(_RUSD).approve(_WSRUSD, _rUsdIn);
        ERC4626(_WSRUSD).deposit(_rUsdIn, address(this));
    }

    /// @notice Unstake wsrUSD to rUSD.
    function unstake(uint256 _wsrUsdIn) external whenNotPaused onlyCoreRole(CoreRoles.FARM_SWAP_CALLER) {
        ERC4626(_WSRUSD).redeem(_wsrUsdIn, address(this), address(this));
    }

    /// @notice swap a token in [USDC, rUSD, wsrUSD] to a token out [USDC, rUSD, wsrUSD]
    function signSwapOrder(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _minAmountOut)
        external
        whenNotPaused
        onlyCoreRole(CoreRoles.FARM_SWAP_CALLER)
        returns (bytes memory)
    {
        require(_tokenIn == _USDC || _tokenIn == _RUSD || _tokenIn == _WSRUSD, InvalidToken(_tokenIn));
        require(_tokenOut == _USDC || _tokenOut == _RUSD || _tokenOut == _WSRUSD, InvalidToken(_tokenOut));
        require(_tokenIn != _tokenOut, InvalidToken(_tokenOut));

        return _checkSwapApproveAndSignOrder(_tokenIn, _tokenOut, _amountIn, _minAmountOut, maxSlippage);
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/ERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol";
import {SafeERC20} from "../utils/SafeERC20.sol";
import {IERC4626} from "../../../interfaces/IERC4626.sol";
import {Math} from "../../../utils/math/Math.sol";

/**
 * @dev Implementation of the ERC-4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC-20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC-20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * [CAUTION]
 * ====
 * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
 * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
 * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
 * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
 * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
 * verifying the amount received is as expected, using a wrapper that performs these checks such as
 * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
 *
 * Since v4.9, this implementation introduces configurable virtual assets and shares to help developers mitigate that risk.
 * The `_decimalsOffset()` corresponds to an offset in the decimal representation between the underlying asset's decimals
 * and the vault decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which
 * itself determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default
 * offset (0) makes it non-profitable even if an attacker is able to capture value from multiple user deposits, as a result
 * of the value being captured by the virtual shares (out of the attacker's donation) matching the attacker's expected gains.
 * With a larger offset, the attack becomes orders of magnitude more expensive than it is profitable. More details about the
 * underlying math can be found xref:ROOT:erc4626.adoc#inflation-attack[here].
 *
 * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
 * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
 * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
 * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
 * `_convertToShares` and `_convertToAssets` functions.
 *
 * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
 * ====
 */
abstract contract ERC4626 is ERC20, IERC4626 {
    using Math for uint256;

    IERC20 private immutable _asset;
    uint8 private immutable _underlyingDecimals;

    /**
     * @dev Attempted to deposit more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);

    /**
     * @dev Attempted to mint more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);

    /**
     * @dev Attempted to withdraw more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);

    /**
     * @dev Attempted to redeem more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);

    /**
     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC-20 or ERC-777).
     */
    constructor(IERC20 asset_) {
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        _underlyingDecimals = success ? assetDecimals : 18;
        _asset = asset_;
    }

    /**
     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
     */
    function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool ok, uint8 assetDecimals) {
        (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
            abi.encodeCall(IERC20Metadata.decimals, ())
        );
        if (success && encodedDecimals.length >= 32) {
            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
            if (returnedDecimals <= type(uint8).max) {
                return (true, uint8(returnedDecimals));
            }
        }
        return (false, 0);
    }

    /**
     * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
     * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
     * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
     *
     * See {IERC20Metadata-decimals}.
     */
    function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
        return _underlyingDecimals + _decimalsOffset();
    }

    /** @dev See {IERC4626-asset}. */
    function asset() public view virtual returns (address) {
        return address(_asset);
    }

    /** @dev See {IERC4626-totalAssets}. */
    function totalAssets() public view virtual returns (uint256) {
        return IERC20(asset()).balanceOf(address(this));
    }

    /** @dev See {IERC4626-convertToShares}. */
    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-convertToAssets}. */
    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxDeposit}. */
    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxMint}. */
    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxRedeem}. */
    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf(owner);
    }

    /** @dev See {IERC4626-previewDeposit}. */
    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-previewMint}. */
    function previewMint(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-deposit}. */
    function deposit(uint256 assets, address receiver) public virtual returns (uint256) {
        uint256 maxAssets = maxDeposit(receiver);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets);
        }

        uint256 shares = previewDeposit(assets);
        _deposit(_msgSender(), receiver, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-mint}. */
    function mint(uint256 shares, address receiver) public virtual returns (uint256) {
        uint256 maxShares = maxMint(receiver);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
        }

        uint256 assets = previewMint(shares);
        _deposit(_msgSender(), receiver, assets, shares);

        return assets;
    }

    /** @dev See {IERC4626-withdraw}. */
    function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxAssets = maxWithdraw(owner);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
        }

        uint256 shares = previewWithdraw(assets);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-redeem}. */
    function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxShares = maxRedeem(owner);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);
        }

        uint256 assets = previewRedeem(shares);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }

    /**
     * @dev Deposit/mint common workflow.
     */
    function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {
        // If asset() is ERC-777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
        // assets are transferred and before the shares are minted, which is a valid state.
        // slither-disable-next-line reentrancy-no-eth
        SafeERC20.safeTransferFrom(IERC20(asset()), caller, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(caller, receiver, assets, shares);
    }

    /**
     * @dev Withdraw/redeem common workflow.
     */
    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        // If asset() is ERC-777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
        // shares are burned and after the assets are transferred, which is a valid state.
        _burn(owner, shares);
        SafeERC20.safeTransfer(IERC20(asset()), receiver, assets);

        emit Withdraw(caller, receiver, owner, assets, shares);
    }

    function _decimalsOffset() internal view virtual returns (uint8) {
        return 0;
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
"
    },
    "lib/solmate/src/utils/FixedPointMathLib.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}
"
    },
    "src/integrations/Farm.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";

import {IFarm} from "@interfaces/IFarm.sol";
import {CoreRoles} from "@libraries/CoreRoles.sol";
import {CoreControlled} from "@core/CoreControlled.sol";

/// @notice InfiniFi Farm base contract
abstract contract Farm is CoreControlled, IFarm {
    using FixedPointMathLib for uint256;

    address public immutable assetToken;

    /// @notice cap on the amount of assets that can be deposited into the farm
    uint256 public cap;

    /// @notice Max slippage for depositing and witdhrawing assets from the farm.
    /// @dev Stored as a percentage with 18 decimals of precision, of the minimum
    /// position size compared to the previous position size (so actually 1 - slippage).
    /// @dev Set to 0 to disable slippage checks.
    uint256 public maxSlippage;

    error CapExceeded(uint256 newAmount, uint256 cap);
    error SlippageTooHigh(uint256 minAssetsOut, uint256 assetsReceived);

    event CapUpdated(uint256 newCap);
    event MaxSlippageUpdated(uint256 newMaxSlippage);

    constructor(address _core, address _assetToken) CoreControlled(_core) {
        assetToken = _assetToken;
        cap = type(uint256).max;

        // default to 99.9999%
        // most farms should not have deposits/withdrawals fees, unless explicitly
        // implemented, and should at worst round against depositors which should
        // only cause some wei of losses when our farms do a deposit/withdraw.
        maxSlippage = 0.999999e18;
    }

    /// @notice set the deposit cap of the farm
    function setCap(uint256 _newCap) external onlyCoreRole(CoreRoles.PROTOCOL_PARAMETERS) {
        cap = _newCap;
        emit CapUpdated(_newCap);
    }

    /// @notice set the max tolerated slippage for depositing and witdhrawing assets from the farm
    function setMaxSlippage(uint256 _maxSlippage) external onlyCoreRole(CoreRoles.PROTOCOL_PARAMETERS) {
        maxSlippage = _maxSlippage;
        emit MaxSlippageUpdated(_maxSlippage);
    }

    // --------------------------------------------------------------------
    // Accounting
    // --------------------------------------------------------------------

    function assets() public view virtual returns (uint256);

    // --------------------------------------------------------------------
    // Adapter logic
    // --------------------------------------------------------------------

    function maxDeposit() external view virtual returns (uint256) {
        uint256 currentAssets = assets();
        if (currentAssets >= cap) {
            return 0;
        }
        return cap - currentAssets;
    }

    function deposit() external virtual onlyCoreRole(CoreRoles.FARM_MANAGER) whenNotPaused {
        uint256 assetsToDeposit = ERC20(assetToken).balanceOf(address(this));
        uint256 assetsBefore = assets();

        if (assetsBefore + assetsToDeposit > cap) {
            revert CapExceeded(assetsBefore + assetsToDeposit, cap);
        }

        _deposit(assetsToDeposit);

        uint256 assetsAfter = assets();
        uint256 assetsReceived = assetsAfter - assetsBefore;

        // check slippage
        uint256 minAssetsOut = assetsToDeposit.mulWadDown(maxSlippage);
        require(assetsReceived >= minAssetsOut, SlippageTooHigh(minAssetsOut, assetsReceived));

        emit AssetsUpdated(block.timestamp, assetsBefore, assetsAfter);
    }

    function _deposit(uint256 assetsToDeposit) internal virtual;

    function withdraw(uint256 amount, address to) external virtual onlyCoreRole(CoreRoles.FARM_MANAGER) whenNotPaused {
        uint256 assetsBefore = assets();

        _withdraw(amount, to);

        uint256 assetsAfter = assets();

        uint256 assetsSpent = assetsBefore - assetsAfter;

        uint256 minAssetsOut = assetsSpent.mulWadDown(maxSlippage);
        require(amount >= minAssetsOut, SlippageTooHigh(minAssetsOut, amount));

        emit AssetsUpdated(block.timestamp, assetsBefore, assetsAfter);
    }

    function _withdraw(uint256, address) internal virtual;
}
"
    },
    "src/interfaces/IFarm.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @notice Interface for an InfiniFi Farm contract
interface IFarm {
    /// @notice emitted when there is a deposit of withdrawal from the farm
    event AssetsUpdated(uint256 timestamp, uint256 assetsBefore, uint256 assetsAfter);

    // --------------------------------------------------------------------
    // Accounting
    // --------------------------------------------------------------------

    /// @notice the cap of the farm
    function cap() external view returns (uint256);

    /// @notice the asset used by deposits and withdrawals in the farm
    function assetToken() external view returns (address);

    /// @notice the total assets in the farm, reported as a balance of asset()
    function assets() external view returns (uint256);

    // --------------------------------------------------------------------
    // Adapter logic
    // --------------------------------------------------------------------
    /// @notice deposit all asset() held by the contract into the farm
    function deposit() external;

    /// @notice Returns the max deposit amount for the underlying protocol
    function maxDeposit() external view returns (uint256);

    /// @notice withdraw an amount of the asset() from the farm
    /// @param amount Amount of assets to withdraw
    /// @param to Address to receive the withdrawn assets
    function withdraw(uint256 amount, address to) external;

    /// @notice available number of assetToken() withdrawable instantly from the farm
    function liquidity() external view returns (uint256);
}
"
    },
    "src/libraries/CoreRoles.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @notice Holds a complete list of all roles which can be held by contracts inside the InfiniFi protocol.
library CoreRoles {
    /// ----------- Core roles for access control --------------

    /// @notice the all-powerful role. Controls all other roles and protocol functionality.
    bytes32 internal constant GOVERNOR = keccak256("GOVERNOR");

    /// @notice Can pause contracts in an emergency.
    bytes32 internal constant PAUSE = keccak256("PAUSE");

    /// @notice Can unpause contracts after an emergency.
    bytes32 internal constant UNPAUSE = keccak256("UNPAUSE");

    /// @notice can tweak protocol parameters
    bytes32 internal constant PROTOCOL_PARAMETERS = keccak256("PROTOCOL_PARAMETERS");

    /// @notice can manage minor roles
    bytes32 internal constant MINOR_ROLES_MANAGER = keccak256("MINOR_ROLES_MANAGER");

    /// ----------- User Flow Management -----------------------

    /// @notice Granted to the user entry point of the system
    bytes32 internal constant ENTRY_POINT = keccak256("ENTRY_POINT");

    /// ----------- Token Management ---------------------------

    /// @notice can mint DebtToken arbitrarily
    bytes32 internal constant RECEIPT_TOKEN_MINTER = keccak256("RECEIPT_TOKEN_MINTER");

    /// @notice can burn DebtToken tokens
    bytes32 internal constant RECEIPT_TOKEN_BURNER = keccak256("RECEIPT_TOKEN_BURNER");

    /// @notice can mint arbitrarily & burn held LockedPositionToken
    bytes32 internal constant LOCKED_TOKEN_MANAGER = keccak256("LOCKED_TOKEN_MANAGER");

    /// @notice can prevent transfers of LockedPositionToken
    bytes32 internal constant TRANSFER_RESTRICTOR = keccak256("TRANSFER_RESTRICTOR");

    /// ----------- Funds Management & Accounting --------------

    /// @notice contract that can allocate funds between farms
    bytes32 internal constant FARM_MANAGER = keccak256("FARM_MANAGER");

    /// @notice addresses who can use the manual rebalancer
    bytes32 internal constant MANUAL_REBALANCER = keccak256("MANUAL_REBALANCER");

    /// @notice addresses who can use the periodic rebalancer
    bytes32 internal constant PERIODIC_REBALANCER = keccak256("PERIODIC_REBALANCER");

    /// @notice addresses who can move funds from farms to a safe address
    bytes32 internal constant EMERGENCY_WITHDRAWAL = keccak256("EMERGENCY_WITHDRAWAL");

    /// @notice addresses who can trigger swaps in Farms
    bytes32 internal constant FARM_SWAP_CALLER = keccak256("FARM_SWAP_CALLER");

    /// @notice can set oracles references within the system
    bytes32 internal constant ORACLE_MANAGER = keccak256("ORACLE_MANAGER");

    /// @notice trusted to report profit and losses in the system.
    /// This role can be used to slash depositors in case of losses, and
    /// can also deposit profits for distribution to end users.
    bytes32 internal constant FINANCE_MANAGER = keccak256("FINANCE_MANAGER");

    /// ----------- Timelock management ------------------------
    /// The hashes are the same as OpenZeppelins's roles in TimelockController

    /// @notice can propose new actions in timelocks
    bytes32 internal constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");

    /// @notice can execute actions in timelocks after their delay
    bytes32 internal constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    /// @notice can cancel actions in timelocks
    bytes32 internal constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE");
}
"
    },
    "src/finance/Accounting.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";

import {IFarm} from "@interfaces/IFarm.sol";
import {IOracle} from "@interfaces/IOracle.sol";
import {CoreRoles} from "@libraries/CoreRoles.sol";
import {FarmRegistry} from "@integrations/FarmRegistry.sol";
import {CoreControlled} from "@core/CoreControlled.sol";
import {FixedPriceOracle} from "@finance/oracles/FixedPriceOracle.sol";

/// @notice InfiniFi Accounting contract
contract Accounting is CoreControlled {
    using FixedPointMathLib for uint256;

    event PriceSet(uint256 indexed timestamp, address indexed asset, uint256 price);
    event OracleSet(uint256 indexed timestamp, address indexed asset, address oracle);

    /// @notice reference to the farm registry
    address public immutable farmRegistry;

    constructor(address _core, address _farmRegistry) CoreControlled(_core) {
        farmRegistry = _farmRegistry;
    }

    /// @notice mapping from asset to oracle
    mapping(address => address) public oracle;

    /// @notice returns the price of an asset
    function price(address _asset) external view returns (uint256) {
        return IOracle(oracle[_asset]).price();
    }

    /// @notice set the oracle for an asset
    function setOracle(address _asset, address _oracle) external onlyCoreRole(CoreRoles.ORACLE_MANAGER) {
        oracle[_asset] = _oracle;
        emit OracleSet(block.timestamp, _asset, _oracle);
    }

    /// -------------------------------------------------------------------------------------------
    /// Reference token getters (e.g. USD for iUSD, ETH for iETH, ...)
    /// @dev note that the "USD" token does not exist, it is just an abstract unit of account
    /// used in the protocol to represent stablecoins pegged to USD, that allows to uniformly
    /// account for a diverse reserve composed of USDC, DAI, FRAX, etc.
    /// -------------------------------------------------------------------------------------------

    /// @notice returns the sum of the value of all assets held on protocol contracts listed in the farm registry.
    function totalAssetsValue() external view returns (uint256 _totalValue) {
        address[] memory assets = FarmRegistry(farmRegistry).getEnabledAssets();
        for (uint256 i = 0; i < assets.length; i++) {
            uint256 assetPrice = IOracle(oracle[assets[i]]).price();
            uint256 _assets = _calculateTotalAssets(FarmRegistry(farmRegistry).getAssetFarms(assets[i]));
            _totalValue += _assets.mulWadDown(assetPrice);
        }
    }

    /// @notice returns the sum of the value of all liquid assets held on protocol contracts listed in the farm registry.
    /// @dev see totalAssetsValue()
    function totalAssetsValueOf(uint256 _type) external view returns (uint256 _totalValue) {
        address[] memory assets = FarmRegistry(farmRegistry).getEnabledAssets();
        for (uint256 i = 0; i < assets.length; i++) {
            uint256 assetPrice = IOracle(oracle[assets[i]]).price();
            address[] memory assetFarms = FarmRegistry(farmRegistry).getAssetTypeFarms(assets[i], uint256(_type));
            uint256 _assets = _calculateTotalAssets(assetFarms);
            _totalValue += _assets.mulWadDown(assetPrice);
        }
    }

    /// -------------------------------------------------------------------------------------------
    /// Specific asset getters (e.g. USDC, DAI, ...)
    /// -------------------------------------------------------------------------------------------

    /// @notice returns the sum of the balance of all farms of a given asset.
    function totalAssets(address _asset) external view returns (uint256) {
        return _calculateTotalAssets(FarmRegistry(farmRegistry).getAssetFarms(_asset));
    }

    function totalAssetsOf(address _asset, uint256 _type) external view returns (uint256) {
        return _calculateTotalAssets(FarmRegistry(farmRegistry).getAssetTypeFarms(_asset, uint256(_type)));
    }

    /// -------------------------------------------------------------------------------------------
    /// Internal helpers
    /// -------------------------------------------------------------------------------------------

    function _calculateTotalAssets(address[] memory _farms) internal view returns (uint256 _totalAssets) {
        uint256 length = _farms.length;
        for (uint256 index = 0; index < length; index++) {
            _totalAssets += IFarm(_farms[index]).assets();
        }
    }
}
"
    },
    "src/interfaces/IMaturityFarm.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IFarm} from "@interfaces/IFarm.sol";

/// @notice Interface for an InfiniFi Farm contract that has a maturity date
/// @dev These farms represent illiquid farm/asset class
interface IMaturityFarm is IFarm {
    /// @notice timestamp at which more funds can be made available for withdrawal
    function maturity() external view returns (uint256);
}
"
    },
    "src/integrations/MultiAssetFarm.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";

import {CoreRoles} from "@libraries/CoreRoles.sol";
import {Accounting} from "@finance/Accounting.sol";
import {Farm, IFarm} from "@integrations/Farm.sol";

/// @notice InfiniFi Farm that can hold multiple asset tokens.
abstract contract MultiAssetFarm is Farm {
    using SafeERC20 for IERC20;
    using FixedPointMathLib for uint256;

    /// @notice reference to the accounting contract
    address public immutable accounting;

    error InvalidAsset(address asset);
    error InvalidFarm(address farm);

    constructor(address _core, address _assetToken, address _accounting) Farm(_core, _assetToken) {
        accounting = _accounting;
    }

    /// @notice the asset tokens that the farm can hold.
    /// @dev MUST include the assetToken of the farm.
    /// @dev MUST only include tokens that can be freely airdropped to the farm
    /// while being accounted properly in the assets() function.
    function assetTokens() public view virtual returns (address[] memory);

    /// @notice return true if the farm can hold the given asset token.
    function isAssetSupported(address _asset) public view virtual returns (bool);

    /// @dev note that there may be conversion fees between supported assets and the assetToken.
    /// This is not reflected in the amount returned by assets().
    function assets() public view virtual override returns (uint256) {
        uint256 assetTokenBalance = IERC20(assetToken).balanceOf(address(this));
        uint256 assetTokenPrice = Accounting(accounting).price(assetToken);

        address[] memory supportedAssets = assetTokens();
        for (uint256 i = 0; i < supportedAssets.length; i++) {
            if (supportedAssets[i] == assetToken) continue;
            uint256 balance = IERC20(supportedAssets[i]).balanceOf(address(this));
            uint256 price = Accounting(accounting).price(supportedAssets[i]);
            assetTokenBalance += balance.mulDivDown(price, assetTokenPrice);
        }

        return assetTokenBalance;
    }

    /// @notice Current liquidity of the farm is the held reference assetToken.
    function liquidity() public view override returns (uint256) {
        return IERC20(assetToken).balanceOf(address(this));
    }

    /// @dev Deposit does nothing, assetTokens are just held on this farm.
    /// @dev There should be other functions to do conversions between the assetTokens or deploying
    /// the funds to a productive yield source.
    function _deposit(uint256) internal view virtual override {}

    function deposit() external virtual override onlyCoreRole(CoreRoles.FARM_MANAGER) whenNotPaused {
        uint256 currentAssets = assets();
        if (currentAssets > cap) {
            revert CapExceeded(currentAssets, cap);
        }

        _deposit(0);

        /// @dev note that in airdrops we do not know the amount of assets before the deposit,
        /// therefore we emit an event that contains twice the assets after the deposit.
        emit AssetsUpdated(block.timestamp, currentAssets, currentAssets);
    }

    /// @dev Withdrawal can only handle the reference assetToken (i.e. the liquidity()).
    /// @dev There should be other functions to do conversions between the assetTokens or pulling
    /// the funds out of a productive yield source.
    function _withdraw(uint256 _amount, address _to) internal virtual override {
        IERC20(assetToken).safeTransfer(_to, _amount);
    }

    /// @notice withdraw the reference assetToken.
    function withdraw(uint256 amount, address to)
        external
        virtual
        override
        onlyCoreRole(CoreRoles.FARM_MANAGER)
        whenNotPaused
    {
        uint256 assetsBefore = assets();
        _withdraw(amount, to);

        emit AssetsUpdated(block.timestamp, assetsBefore, assetsBefore - amount);
    }

    /// @notice function used to withdraw any supported assetTokens.
    function withdrawSecondaryAsset(address _asset, uint256 _amount, address _to)
        external
        onlyCoreRole(CoreRoles.FARM_MANAGER)
        whenNotPaused
    {
        require(isAssetSupported(_asset) && _asset != assetToken, InvalidAsset(_asset));

        uint256 assetsBefore = assets();
        IERC20(_asset).safeTransfer(_to, _amount);
        uint256 assetsAfter = assets();

        emit AssetsUpdated(block.timestamp, assetsBefore, assetsAfter);
    }
}
"
    },
    "src/integrations/farms/CoWSwapFarmBase.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {GPv2Settlement} from "@cowprotocol/contracts/GPv2Settlement.sol";
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol";
import {GPv2Order, IERC20 as ICoWERC20} from "@cowprotocol/contracts/libraries/GPv2Order.sol";

import {Farm} from "@integrations/Farm.sol";
import {Accounting} from "@finance/Accounting.sol";
import {MultiAssetFarm} from "@integrations/MultiAssetFarm.sol";

/// @title CoWSwap Farm Base
abstract contract CoWSwapFarmBase is MultiAssetFarm {
    using SafeERC20 for IERC20;
    using FixedPointMathLib for uint256;

    event OrderSigned(
        uint256 indexed timestamp, bytes orderUid, GPv2Order.Data order, uint32 validTo, uint256 buyAmount
    );

    error SwapCooldown();
    error InvalidToken(address token);
    error InvalidAmountIn(uint256 amountIn);
    error InvalidAmountOut(uint256 minOut, uint256 provided);

    /// @notice timestamp of last order
    uint256 public lastOrderSignTimestamp = 1;
    /// @notice cooldown period between order signings
    uint256 public constant _SIGN_COOLDOWN = 20 minutes;

    /// @notice address of the GPv2Settlement contract
    address public immutable settlementContract;

    /// @notice address of the GPv2VaultRelayer contract
    address public immutable vaultRelayer;

    constructor(address _settlementContract, address _vaultRelayer) {
        settlementContract = _settlementContract;
        vaultRelayer = _vaultRelayer;
    }

    /// @notice Converts a number of tokens to another token based on oracle rates.
    function convert(address _tokenIn, address _tokenOut, uint256 _amountIn) public view returns (uint256) {
        uint256 tokenInPrice = Accounting(accounting).price(_tokenIn);
        uint256 tokenOutPrice = Accounting(accounting).price(_tokenOut);
        return _amountIn.mulDivDown(tokenInPrice, tokenOutPrice);
    }

    function _checkSwapApproveAndSignOrder(
        address _tokenIn,
        address _tokenOut,
        uint256 _amountIn,
        uint256 _minAmountOut,
        uint256 _maxSlippage
    ) internal returns (bytes memory) {
        require(_amountIn > 0 && _amountIn <= IERC20(_tokenIn).balanceOf(address(this)), InvalidAmountIn(_amountIn));
        require(isAssetSupported(_tokenOut), InvalidToken(_tokenOut));

        require(block.timestamp > lastOrderSignTimestamp + _SIGN_COOLDOWN, SwapCooldown());
        lastOrderSignTimestamp = block.timestamp;

        uint256 minOutSlippage = convert(_tokenIn, _tokenOut, _amountIn).mulWadDown(_maxSlippage);
        require(_minAmountOut >= minOutSlippage, InvalidAmountOut(minOutSlippage, _minAmountOut));

        IERC20(_tokenIn).forceApprove(vaultRelayer, _amountIn);
        return _signOrder(_order(_tokenIn, _tokenOut, _amountIn, _minAmountOut));
    }

    function _order(address _tokenIn, address _tokenOut, uint256 _amountIn, uint256 _minAmountOut)
        internal
        view
        returns (GPv2Order.Data memory)
    {
        return GPv2Order.Data({
            sellToken: ICoWERC20(_tokenIn),
            buyToken: ICoWERC20(_tokenOut),
            receiver: address(this),
            sellAmount: _amountIn,
            buyAmount: _minAmountOut,
            validTo: uint32(block.timestamp + _SIGN_COOLDOWN),
            // keccak256 {"appCode":"infiniFi","version":"1.0.0","metadata":{}}
            appData: 0x3cac71ef99d0dfbf5b937334b5b7ab672b679ba2bbd4d6fe8e0c54a2dab31109,
            feeAmount: 0,
            kind: GPv2Order.KIND_SELL,
            partiallyFillable: false,
            sellTokenBalance: GPv2Order.BALANCE_ERC20,
            buyTokenBalance: GPv2Order.BALANCE_ERC20
        });
    }

    function _signOrder(GPv2Order.Data memory order) internal returns (bytes memory) {
        GPv2Settlement settlement = GPv2Settlement(payable(settlementContract));
        bytes32 orderDigest = GPv2Order.hash(order, settlement.domainSeparator());
        bytes memory orderUid = new bytes(GPv2Order.UID_LENGTH);
        GPv2Order.packOrderUidParams(orderUid, orderDigest, address(this), order.validTo);
        settlement.setPreSignature(orderUid, true);

        emit OrderSigned(block.timestamp, orderUid,

Tags:
ERC20, ERC165, Multisig, Mintable, Pausable, Swap, Liquidity, Yield, Voting, Timelock, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x6f53c12243d2fe281cfdfc63601f8b4e713d86de|verified:true|block:23576504|tx:0xf5e6b14ee4a62e303fdd4979344c7a5af5f122dad7447cffe18c9f12dca14565|first_check:1760452241

Submitted on: 2025-10-14 16:30:41

Comments

Log in to comment.

No comments yet.