S Price: $0.514573 (-6.30%)

Contract

0x69947083379Fec458477c0613c2d6bba2fcD34E1

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Request Credit79600402025-02-15 7:03:1815 hrs ago1739602998IN
0x69947083...a2fcD34E1
0 S0.015451755.01
Send Report79600322025-02-15 7:03:1015 hrs ago1739602990IN
0x69947083...a2fcD34E1
0 S0.0241466355.01
Request Credit78319022025-02-14 9:43:1637 hrs ago1739526196IN
0x69947083...a2fcD34E1
0 S0.0154570355.01
Send Report78318952025-02-14 9:43:0837 hrs ago1739526188IN
0x69947083...a2fcD34E1
0 S0.0251363255.01
Harvest78318752025-02-14 9:42:5637 hrs ago1739526176IN
0x69947083...a2fcD34E1
0 S0.026723760
Request Credit76747852025-02-13 9:26:322 days ago1739438792IN
0x69947083...a2fcD34E1
0 S0.0154541255.01
Send Report76747722025-02-13 9:26:222 days ago1739438782IN
0x69947083...a2fcD34E1
0 S0.0241396555.01
Harvest76747492025-02-13 9:26:072 days ago1739438767IN
0x69947083...a2fcD34E1
0 S0.0254928955.01
Request Credit75248722025-02-12 12:45:493 days ago1739364349IN
0x69947083...a2fcD34E1
0 S0.0183791755.01
Transfer Ownersh...75222702025-02-12 12:11:373 days ago1739362297IN
0x69947083...a2fcD34E1
0 S0.0014872550
Enable Guardian75222702025-02-12 12:11:373 days ago1739362297IN
0x69947083...a2fcD34E1
0 S0.002455650

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SiloV2Adapter

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 29 : SiloV2Adapter.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity 0.8.27;

import { ERC4626AdapterHarvestable } from "src/abstracts/ERC4626AdapterHarvestable.sol";
import { ISiloIncentivesController } from "interfaces/silo/ISilo.sol";

contract SiloV2Adapter is ERC4626AdapterHarvestable {

    /// @notice The Silo incentives controller.
    ISiloIncentivesController public incentivesController;

    /*//////////////////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Constructor for the strategy adapter.
    /// @param _multistrategy The address of the multi-strategy contract.
    /// @param _asset The address of the asset.
    /// @param _vault The address of the ERC4626 vault.
    /// @param _name The name of this Strategy Adapter.
    /// @param _id The type identifier of this Strategy Adapter.
    constructor(
        address _multistrategy,
        address _asset,
        address _vault,
        address _incentivesController,
        HarvestAddresses memory _harvestAddresses,
        string memory _name,
        string memory _id
    )
        ERC4626AdapterHarvestable(
            _multistrategy,
            _asset,
            _vault,
            _harvestAddresses,
            _name,
            _id
        )
    {
        incentivesController = ISiloIncentivesController(_incentivesController);
    }

    /*//////////////////////////////////////////////////////////////////////////
                            INTERNAL NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Claims the rewards.
    function _claim() internal override {
        incentivesController.claimRewards(address(this));
    }
}

File 2 of 29 : ERC4626AdapterHarvestable.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity 0.8.27;

import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { StrategyAdapterHarvestable } from "./StrategyAdapterHarvestable.sol";
import { Errors } from "src/infra/libraries/Errors.sol";

abstract contract ERC4626AdapterHarvestable is StrategyAdapterHarvestable {
    using SafeERC20 for IERC20;
    using Math for uint256;

    /// @notice The ERC4626 Vault
    IERC4626 public vault;

    /*//////////////////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Constructor for the strategy adapter.
    /// @param _multistrategy The address of the multi-strategy contract.
    /// @param _asset The address of the asset.
    /// @param _vault The address of the ERC4626 vault.
    /// @param _name The name of this Strategy Adapter.
    /// @param _id The type identifier of this Strategy Adapter.
    constructor(
        address _multistrategy,
        address _asset,
        address _vault,
        HarvestAddresses memory _harvestAddresses,
        string memory _name,
        string memory _id
    ) 
        StrategyAdapterHarvestable(_multistrategy, _asset, _harvestAddresses, _name, _id)
    {   
        vault = IERC4626(_vault);
        _giveAllowances();
    }

    /*//////////////////////////////////////////////////////////////////////////
                            INTERNAL CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Returns the total amount of assets held in this adapter.
    function _totalAssets() internal override view returns(uint256) {
        uint256 sharesBalance = vault.balanceOf(address(this));
        uint256 assetsSupplied = vault.convertToAssets(sharesBalance);
        uint256 assetBalance = IERC20(asset).balanceOf(address(this));

        uint256 total = assetsSupplied + assetBalance;
        return total > 0 ? total - 1 : total;
    }

    /// @inheritdoc StrategyAdapterHarvestable
    function _verifyRewardToken(address _token) internal view override {
        require(_token != address(vault), Errors.InvalidRewardToken(_token));
    }

    /*//////////////////////////////////////////////////////////////////////////
                            INTERNAL NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Deposits all the liquidity into the ERC4626 vault.
    function _deposit() internal override {
        uint256 balance = IERC20(asset).balanceOf(address(this));
        vault.deposit(balance, address(this));
    }

    /// @notice Withdraws a specified amount of assets from the ERC4626 vault.
    /// @param _amount The amount of assets to withdraw from the Silo.
    function _withdraw(uint256 _amount) internal override {
        vault.withdraw(_amount, address(this), address(this));
    }

    /// @notice Performs an emergency withdrawal of all assets from the ERC4626 vault.
    /// This function is intended for emergency situations where all assets need to be withdrawn immediately.
    function _emergencyWithdraw() internal override {
        uint256 maxWithdraw = vault.maxWithdraw(address(this));
        if (maxWithdraw > 0) {
            vault.withdraw(maxWithdraw, address(this), address(this));
        }
    }

    /// @notice Sets the maximum allowance of the base asset for the ERC4626 vault.
    function _giveAllowances() internal override {
        IERC20(asset).forceApprove(address(vault), type(uint).max);
        IERC20(wrappedGas).forceApprove(swapper, type(uint).max);
    }

    /// @notice Revokes the allowance of the base asset for the ERC4626 vault.
    function _revokeAllowances() internal override {
        IERC20(asset).forceApprove(address(vault), 0);
        IERC20(wrappedGas).forceApprove(swapper, 0);
    }
}

File 3 of 29 : ISilo.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin-4/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-4/contracts/token/ERC20/utils/SafeERC20.sol";

interface ISilo {
    function deposit(address asset, uint amount, bool collateralOnly) external;

    function withdraw(address asset, uint amount, bool collateralOnly) external;

    function balanceOf(address user) external view returns (uint256);
}

interface ISiloCollateralToken {
    function asset() external view returns (address);
}

interface ISiloLens {
    function balanceOfUnderlying(
        uint256 _assetTotalDeposits,
        address _shareToken,
        address _user
    ) external view returns (uint256);

    function totalDepositsWithInterest(
        address _silo,
        address _asset
    ) external view returns (uint256 _totalDeposits);
}

interface ISiloRewards {
    function claimRewardsToSelf(
        address[] memory assets,
        uint256 amount
    ) external;
}

interface ISiloIncentivesController {
    function claimRewards(address to) external;
}

File 4 of 29 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 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 Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    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.
     */
    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.
     */
    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 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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

File 5 of 29 : IERC4626.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

File 6 of 29 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 7 of 29 : StrategyAdapterHarvestable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity 0.8.27;

import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IStrategyAdapterHarvestable } from "interfaces/infra/multistrategy/IStrategyAdapterHarvestable.sol";
import { IGoatSwapper } from "interfaces/infra/IGoatSwapper.sol";
import { StrategyAdapter } from "src/abstracts/StrategyAdapter.sol";
import { Errors } from "src/infra/libraries/Errors.sol";

abstract contract StrategyAdapterHarvestable is IStrategyAdapterHarvestable, StrategyAdapter {
    using SafeERC20 for IERC20;

    struct HarvestAddresses {
        address swapper;
        address wrappedGas;
    }
    
    /// @notice The timestamp of the last successful harvest.
    uint256 public lastHarvest;

    /// @notice An array of reward token addresses that can be claimed and swapped.
    address[] public rewards;

    /// @notice The address of the Wrapped Gas token used as an intermediary for swaps.
    /// Used because it has high liquidity.
    address internal wrappedGas;

    /// @notice The address of the swapper contract used to swap reward tokens.
    address swapper;

    /// @notice A mapping of minimum amounts for each reward token before it can be swapped.
    /// @dev The key is the reward token address, and the value is the minimum amount required for swapping.
    mapping(address token => uint minimumAmount) public minimumAmounts;

    /*//////////////////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Constructor for the strategy adapter.
    /// @param _multistrategy The address of the multi-strategy contract.
    /// @param _asset The address of the asset.
    /// @param _harvestAddresses Struct of Protocol Addresses.
    /// @param _name The name of this Strategy Adapter.
    /// @param _id The type identifier of this Strategy Adapter.
    constructor(
        address _multistrategy,
        address _asset,
        HarvestAddresses memory _harvestAddresses,
        string memory _name,
        string memory _id
    ) 
        StrategyAdapter(_multistrategy, _asset, _name, _id)
    {
        swapper = _harvestAddresses.swapper;
        wrappedGas = _harvestAddresses.wrappedGas;
    }

    /*//////////////////////////////////////////////////////////////////////////
                        USER FACING CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IStrategyAdapterHarvestable
    function rewardsLength() external view returns (uint256) {
        return rewards.length;
    }

    /*//////////////////////////////////////////////////////////////////////////
                        USER FACING NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IStrategyAdapterHarvestable
    function harvest() external whenNotPaused {
        _claim();
        _swapRewardsToWrappedGas();
        if (IERC20(wrappedGas).balanceOf(address(this)) > minimumAmounts[wrappedGas]) {
            _swapWrappedGasToAsset();
            uint256 assetsHarvested = IERC20(asset).balanceOf(address(this));
            _deposit();
            lastHarvest = block.timestamp;

            emit AdapterHarvested(msg.sender, assetsHarvested, _totalAssets());
        }
    }

    /// @inheritdoc IStrategyAdapterHarvestable
    function addReward(address _token) external onlyOwner {
        require(_token != asset && _token != wrappedGas, Errors.InvalidRewardToken(_token));
        _verifyRewardToken(_token);

        rewards.push(_token);
        IERC20(_token).forceApprove(swapper, type(uint256).max);
    }

    /// @inheritdoc IStrategyAdapterHarvestable
    function resetRewards() external onlyOwner {
        for (uint i; i < rewards.length; ++i) {
            IERC20(rewards[i]).forceApprove(swapper, 0);
        }
        delete rewards;
    }

    /// @inheritdoc IStrategyAdapterHarvestable
    function setRewardMinimumAmount(address _token, uint _minAmount) external onlyOwner {
        minimumAmounts[_token] = _minAmount;
    }

    /// @inheritdoc IStrategyAdapterHarvestable
    function updateSwapper(address _swapper) external onlyOwner {
        for (uint i; i < rewards.length; ++i) {
            address token = rewards[i];
            IERC20(token).forceApprove(swapper, 0);
            IERC20(token).forceApprove(_swapper, type(uint256).max);
        }
        IERC20(wrappedGas).forceApprove(swapper, 0);
        IERC20(wrappedGas).forceApprove(_swapper, type(uint256).max);
        swapper = _swapper;
        emit SwapperUpdated(_swapper);
    }

    /*//////////////////////////////////////////////////////////////////////////
                            INTERNAL NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Swaps all reward tokens to Wrapped Gas.
    /// @dev This function checks if the balance of each reward token exceeds the minimum amount before swapping.
    function _swapRewardsToWrappedGas() internal virtual {
        for (uint i; i < rewards.length; ++i) {
            address token = rewards[i];
            uint256 amount = IERC20(token).balanceOf(address(this));
            if (amount > minimumAmounts[token]) {
                IGoatSwapper(swapper).swap(token, wrappedGas, amount);
            }
        }
    }

    /// @notice Swaps Wrapped Gas to `asset`.
    function _swapWrappedGasToAsset() internal virtual {
        if (asset != wrappedGas) _swap(wrappedGas, asset);
    }

    /// @notice Swaps one token for another using the swapper contract.
    /// @param tokenFrom The address of the token to swap from.
    /// @param tokenTo The address of the token to swap to.
    function _swap(address tokenFrom, address tokenTo) internal {
        uint amount = IERC20(tokenFrom).balanceOf(address(this));
        IGoatSwapper(swapper).swap(tokenFrom, tokenTo, amount);
    }

    /// @notice Claims rewards.
    /// @dev This function is meant to be overridden by child contracts to implement reward claiming logic.
    function _claim() internal virtual {}

    /// @notice Verifies if a token is a valid reward token.
    /// @dev This function is meant to be overridden by child contracts to implement token verification logic.
    /// @param _token The address of the token to verify.
    function _verifyRewardToken(address _token) internal view virtual {}
}

File 8 of 29 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

/// @title Errors
/// @notice Library containing all custom errors the protocol may revert with.
library Errors {
    /*//////////////////////////////////////////////////////////////////////////
                                      GENERICS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Thrown when `msg.sender` is not the manager.
    error CallerNotManager(address caller);

    /// @notice Thrown when `msg.sender` is not a guardian.
    error CallerNotGuardian(address caller);

    /// @notice Thrown when `amount` is zero.
    error ZeroAmount(uint256 amount);

    /// @notice Thrown when setting an address to the zero address.
    error ZeroAddress();

    /// @notice Thrown when `currentBalance` is lower than `amount`.
    error InsufficientBalance(uint256 currentBalance, uint256 amount);

    /// @notice Thrown when `addr` is an unexpected address.
    error InvalidAddress(address addr);

    /*//////////////////////////////////////////////////////////////////////////
                                    MULTISTRATEGY
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Thrown when performing an action on a non-active strategy.
    error StrategyNotActive(address strategy);

    /// @notice Thrown when performing an action on an active strategy.
    error StrategyAlreadyActive(address strategy);

    /// @notice Thrown when a strategy is reporting a gain and a loss simultaneously.
    error GainLossMismatch();

    /// @notice Thrown when a deposit would exceed the depositLimit
    error DepositLimit();

    /// @notice Thrown when the owner tries to set a fee above the maximum permitted fee.
    error ExcessiveFee(uint256 fee);

    /// @notice Thrown when the debtRatio of a strategy or a multistrategy is above 100%.
    error DebtRatioAboveMaximum(uint256 debtRatio);

    /// @notice Thrown when trying to remove a strategy from `withdrawOrder` that still has outstanding debt.
    error StrategyWithOutstandingDebt();

    /// @notice Thrown when minDebtDelta is above maxDebtDelta or maxDebtDelta is below minDebtDelta.
    error InvalidDebtDelta();

    /// @notice Thrown when a strategy is reporting a loss higher than its total debt.
    error InvalidStrategyLoss();

    /// @notice Thrown when there is non-Zero Address following a Zero Address in withdrawOrder.
    error InvalidWithdrawOrder();

    /// @notice Thrown when trying to add a new strategy to the multistrategy but it already reached the
    /// maximum amount of strategies.
    error MaximumAmountStrategies();

    /// @notice Thrown when trying to remove a strategy that has a `debtRatio` greater than 0.
    error StrategyNotRetired();

    /// @notice Thrown when there isn't enough liquidity to cover a withdraw
    /// @param assets The amount of assets requested.
    /// @param liquidity The current liquidity available in the contract.
    error InsufficientLiquidity(uint256 assets, uint256 liquidity);

    /// @notice Thrown when depositing / minting on a retired multistrategy.
    error Retired();

    /*//////////////////////////////////////////////////////////////////////////
                                STRATEGY ADAPTER
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Thrown when the caller is not the Multistrategy.
    error CallerNotMultistrategy(address caller);

    /// @notice Thrown when the `_asset` parameter on the constructor doesn't match 
    /// the `deposit` token on Multistrategy.
    error AssetMismatch(address multAsset, address stratAsset);

    /// @notice Thrown when the requested slippage limit exceeds the maximum permitted value.
    /// @param slippageLimit The slippage limit in basis points (BPS).
    error SlippageLimitExceeded(uint256 slippageLimit);

    /// @notice Thrown when the actual slippage exceeds the allowed slippage.
    /// @param amount0 The expected amount after accounting for allowed slippage.
    /// @param amount1 The actual amount obtained.
    error SlippageCheckFailed(uint256 amount0, uint256 amount1);

    /// @notice Thrown when the reward added is the base asset of the Strategy.
    error InvalidRewardToken(address rewardToken);

    /*//////////////////////////////////////////////////////////////////////////
                                    ERC-4626
    //////////////////////////////////////////////////////////////////////////*/

    /**
     * @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);
}

File 9 of 29 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount) external returns (bool);
}

File 10 of 29 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @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.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 11 of 29 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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);
}

File 12 of 29 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 13 of 29 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

File 14 of 29 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 15 of 29 : IStrategyAdapterHarvestable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

interface IStrategyAdapterHarvestable {
    /// @notice Emitted when the adapter successfully harvests rewards.
    /// @param caller The address of the caller who triggered the harvest.
    /// @param amountHarvested The amount of `want` tokens harvested.
    /// @param totalAssets The total assets held by the adapter after the harvest.
    event AdapterHarvested(address caller, uint256 amountHarvested, uint256 totalAssets);

    /// @notice Emitted when the swapper contract is updated.
    /// @param newSwapper The address of the new swapper contract.
    event SwapperUpdated(address newSwapper);

    /// @notice Returns the amount of different reward tokens existent in the `rewards` array
    function rewardsLength() external view returns (uint256);

    /// @notice Harvests rewards, swaps them to WETH, and then to the desired asset.
    /// @dev This function claims rewards, swaps them to WETH, and then converts WETH to the `want` token.
    function harvest() external;

    /// @notice Adds a new reward token to be claimed.
    /// @param token The address of the reward token to add.
    /// @dev The token must not be the same as `want` or `weth`.
    function addReward(address token) external;

    /// @notice Resets the list of reward tokens.
    /// @dev This function removes all reward tokens and revokes their approvals.
    function resetRewards() external;

    /// @notice Sets the minimum amount of a reward token required for swapping.
    /// @param token The address of the reward token.
    /// @param minAmount The minimum amount of the token required for swapping.
    function setRewardMinimumAmount(address token, uint256 minAmount) external;

    /// @notice Updates the swapper contract used for swapping tokens.
    /// @param swapper The address of the new swapper contract.
    /// @dev This function revokes approvals from the old swapper and grants them to the new swapper.
    function updateSwapper(address swapper) external;
}

File 16 of 29 : IGoatSwapper.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IGoatSwapper {
    function swap(
        address fromToken,
        address toToken,
        uint256 amountIn
    ) external returns (uint256 amountOut);

    function swap(
        address fromToken,
        address toToken,
        uint256 amountIn,
        uint256 minAmountOut
    ) external returns (uint256 amountOut);

    function getAmountOut(
        address _fromToken,
        address _toToken,
        uint256 _amountIn
    ) external view returns (uint256 amountOut);

    struct SwapInfo {
        address router;
        bytes data;
        uint256 amountIndex;
    }

    function setSwapInfo(
        address _fromToken, 
        address _toToken, 
        SwapInfo calldata _swapInfo
    ) external;

    function setSwapInfos(
        address[] calldata _fromTokens, 
        address[] calldata _toTokens, 
        SwapInfo[] calldata _swapInfos
    ) external;
}

File 17 of 29 : StrategyAdapter.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity 0.8.27;

import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { StrategyAdapterAdminable } from "src/abstracts/StrategyAdapterAdminable.sol";
import { IStrategyAdapter } from "interfaces/infra/multistrategy/IStrategyAdapter.sol";
import { IMultistrategy } from "interfaces/infra/multistrategy/IMultistrategy.sol";
import { Errors } from "src/infra/libraries/Errors.sol";

abstract contract StrategyAdapter is IStrategyAdapter, StrategyAdapterAdminable {
    using SafeERC20 for IERC20;
    using Math for uint256;

    /// @dev 100% in BPS, setting the slippage to 100% means no slippage protection.
    uint256 constant MAX_SLIPPAGE = 10_000;

    /// @inheritdoc IStrategyAdapter
    address public immutable multistrategy;

    /// @inheritdoc IStrategyAdapter
    address public immutable asset;

    /// @inheritdoc IStrategyAdapter
    uint256 public slippageLimit;

    /// @notice Name of this Strategy Adapter
    string public name;

    /// @notice Identifier of this Strategy Adapter
    string public id;

    /*//////////////////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
    //////////////////////////////////////////////////////////////////////////*/
    
    /// @dev Reverts if `_asset` doesn't match `asset` on the Multistrategy.
    /// @param _multistrategy Address of the multistrategy this strategy will belongs to.
    /// @param _asset Address of the token used to deposit and withdraw on this strategy.
    constructor(address _multistrategy, address _asset, string memory _name, string memory _id) StrategyAdapterAdminable(msg.sender) {
        require(_asset == IERC4626(_multistrategy).asset(), Errors.AssetMismatch(IERC4626(_multistrategy).asset(), _asset));

        multistrategy = _multistrategy;
        asset = _asset;
        slippageLimit = 0;
        name = _name;
        id = _id;

        IERC20(asset).forceApprove(multistrategy, type(uint256).max);
    }

    /// @dev Reverts if called by any account other than the Multistrategy this strategy belongs to.
    modifier onlyMultistrategy() {
        require(msg.sender == multistrategy, Errors.CallerNotMultistrategy(msg.sender));
        _;
    }

    /*//////////////////////////////////////////////////////////////////////////
                        USER FACING CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IStrategyAdapter
    function totalAssets() external view returns (uint256) {
        return _totalAssets();
    }

    /// @inheritdoc IStrategyAdapter
    function currentPnL() external view returns (uint256, uint256) {
        return _calculateGainAndLoss(_totalAssets());
    }

    /*//////////////////////////////////////////////////////////////////////////
                        USER FACING NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IStrategyAdapter
    function requestCredit() external onlyOwner whenNotPaused {
        uint256 credit = IMultistrategy(multistrategy).requestCredit();
        if(credit > 0) {
            _deposit();
        }
    }

    /// @inheritdoc IStrategyAdapter
    function setSlippageLimit(uint256 _slippageLimit) external onlyOwner {
        require(_slippageLimit <= MAX_SLIPPAGE, Errors.SlippageLimitExceeded(_slippageLimit));
        
        slippageLimit = _slippageLimit;

        emit SlippageLimitSet(_slippageLimit);
    }
    
    /// @inheritdoc IStrategyAdapter
    function sendReport(uint256 _repayAmount) external onlyOwner whenNotPaused {
        _sendReport(_repayAmount);
    }

    /// @inheritdoc IStrategyAdapter
    function askReport() external onlyMultistrategy whenNotPaused {
        _sendReport(0);
    }

    /// @inheritdoc IStrategyAdapter
    function sendReportPanicked() external onlyOwner whenPaused {
        _sendReportPanicked();
    }

    /// @inheritdoc IStrategyAdapter
    /// @dev Any surplus on the withdraw won't be sent to the multistrategy.
    /// It will be eventually reported back as gain when sendReport is called.
    function withdraw(uint256 _amount) external onlyMultistrategy whenNotPaused returns (uint256) {
        _tryWithdraw(_amount);
        uint256 withdrawn = Math.min(_amount, _liquidity());
        IERC20(asset).safeTransfer(multistrategy, withdrawn);

        return withdrawn;
    }

    /// @inheritdoc IStrategyAdapter
    function panic() external onlyGuardian {
        _emergencyWithdraw();
        _revokeAllowances();
        _pause();
    }

    /// @inheritdoc IStrategyAdapter
    function pause() external onlyGuardian {
        _revokeAllowances();
        _pause();
    }

    /// @inheritdoc IStrategyAdapter
    function unpause() external onlyOwner {
        _unpause();
        _giveAllowances();
    }

    /*//////////////////////////////////////////////////////////////////////////
                            INTERNAL CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Calculates the gain and loss based on current assets.
    /// 
    /// This function performs the following actions:
    /// - Retrieves the total debt of the strategy from the multi-strategy contract.
    /// - Determines whether the current assets are greater than or equal to the total debt to calculate the gain.
    /// - If the current assets are less than the total debt, calculates the loss.
    /// 
    /// @param _currentAssets The current assets held by the strategy.
    /// @return gain The calculated gain.
    /// @return loss The calculated loss.
    function _calculateGainAndLoss(uint256 _currentAssets) internal view returns (uint256, uint256) {
        uint256 totalDebt = IMultistrategy(multistrategy).strategyTotalDebt(address(this));
        uint256 gain = 0;
        uint256 loss = 0;

        if(_currentAssets >= totalDebt) {
            gain = _currentAssets - totalDebt;
        } else {
            loss = totalDebt - _currentAssets;
        }

        return (gain, loss);
    }

    /// @notice Calculates the amount to be withdrawn from the strategy.
    /// 
    /// This function performs the following actions:
    /// - Retrieves the exceeding debt of the strategy from the multi-strategy contract.
    /// - If there is exceeding debt and a repayment amount is specified:
    ///   - Calculates the amount to be withdrawn to repay the exceeding debt at maximum slippage.
    ///   - Ensures the amount to be withdrawn does not exceed the repayment amount, adding any strategy gain.
    /// - If there is no exceeding debt or no repayment amount, returns the strategy gain as the amount to be withdrawn.
    ///   - Note that slippage calculations are not applied here as any slippage would be considered a loss and subtracted from the gain.
    /// 
    /// @param _repayAmount The amount to be repaid.
    /// @param _strategyGain The gain of the strategy.
    /// @return The amount to be withdrawn from the strategy.
    function _calculateAmountToBeWithdrawn(uint256 _repayAmount, uint256 _strategyGain) internal view returns (uint256) {   
        uint256 exceedingDebt = IMultistrategy(multistrategy).debtExcess(address(this));
        if(exceedingDebt > 0 && _repayAmount > 0) {
            if(slippageLimit == MAX_SLIPPAGE) return _repayAmount + _strategyGain;
            
            uint256 exceedingDebtWithSlippage = exceedingDebt.mulDiv(MAX_SLIPPAGE, MAX_SLIPPAGE - slippageLimit);
            return Math.min(_repayAmount, exceedingDebtWithSlippage) + _strategyGain;
        } 

        return _strategyGain;
    }

    /// @notice Calculates the adjusted gain and loss after accounting for slippage.
    /// 
    /// This function performs the following actions:
    /// - Calculates the slippage loss as the difference between the amount intended to be withdrawn and the actual amount withdrawn.
    /// - If there is no slippage loss, returns the original gain and loss.
    /// - If there is slippage loss:
    ///   - Deducts the slippage loss from the gain.
    ///   - If the slippage loss exceeds the gain, the remaining slippage loss is added to the loss.
    /// - Returns the adjusted gain and loss after accounting for slippage.
    /// 
    /// @param _gain The initial gain before slippage.
    /// @param _loss The initial loss before slippage.
    /// @param _currentBalance The current balance of asset in this contract.
    /// @param _toBeWithdrawn The amount intended to be withdrawn.
    /// @return The adjusted gain and loss after slippage.
    function _calculateGainAndLossAfterSlippage(
        uint256 _gain, 
        uint256 _loss, 
        uint256 _currentBalance, 
        uint256 _toBeWithdrawn
        ) internal pure returns (uint256, uint256) {

        uint256 slippageLoss = (_toBeWithdrawn > _currentBalance) ? _toBeWithdrawn - _currentBalance : 0;
        if(slippageLoss == 0) return (_gain, _loss);
        if(slippageLoss > _gain) {
            slippageLoss -= _gain;
            _gain = 0;
        } else {
            _gain -= slippageLoss;
            slippageLoss = 0;
        }

        _loss += slippageLoss;
        return (_gain, _loss);
    }

    /// @notice Returns the current balance of asset in this contract.
    function _liquidity() internal view returns (uint256) {
        return IERC20(asset).balanceOf(address(this));
    }

    /// @notice Returns the amount of `asset` the underlying strategy holds. In the case this strategy
    /// has swapped `asset` for another asset, it should return the most approximate value.
    /// @dev Child contract must implement the logic to calculate the amount of assets.
    function _totalAssets() internal virtual view returns (uint256) {}

    /*//////////////////////////////////////////////////////////////////////////
                            INTERNAL NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Sends a report on the strategy's performance.
    /// 
    /// This function performs the following actions:
    /// - Calculates the current assets of the strategy.
    /// - Attempts to withdraw the repayment amount plus any gain.
    /// - Ensures that the gain is not used to repay the debt.
    /// - Reports the available amount for repayment, the gain, and the loss to the multi-strategy.
    /// 
    /// @param _repayAmount The amount to be repaid to the multi-strategy.
    function _sendReport(uint256 _repayAmount) internal {
        uint256 currentAssets = _totalAssets();
        (uint256 gain, uint256 loss) = _calculateGainAndLoss(currentAssets);
        uint256 toBeWithdrawn = _calculateAmountToBeWithdrawn(_repayAmount, gain);

        _tryWithdraw(toBeWithdrawn);
        (gain, loss) = _calculateGainAndLossAfterSlippage(gain, loss, _liquidity(), toBeWithdrawn);
        uint256 availableForRepay = _liquidity() - gain;
        
        IMultistrategy(multistrategy).strategyReport(availableForRepay, gain, loss);
    }

    /// @notice Sends a report on the strategy's performance after the strategy has been panicked.
    /// 
    /// This function performs the following actions:
    /// - Retrieves the current balance of the asset held by the contract.
    /// - Calculates the gain and loss based on the current assets.
    /// - Ensures that the gain is not used to repay the debt.
    /// - Reports the available amount for repayment, the gain, and the loss to the multi-strategy.
    function _sendReportPanicked() internal {
        uint256 currentAssets = _liquidity();
        (uint256 gain, uint256 loss) = _calculateGainAndLoss(currentAssets);

        uint256 availableForRepay = currentAssets - gain;

        IMultistrategy(multistrategy).strategyReport(availableForRepay, gain, loss);
    }

    /// @notice Attempts to withdraw a specified amount from the strategy.
    /// 
    /// This function performs the following actions:
    /// - Calls the internal `_withdraw` function to withdraw the desired amount.
    /// - Checks the current balance of the contract after the withdrawal.
    /// - If the balance is less than the desired amount, it reverts with an insufficient balance error.
    /// 
    /// @param _amount The amount to withdraw from the strategy.
    function _tryWithdraw(uint256 _amount) internal {
        if(_amount == 0 || _amount <= _liquidity()) return;

        // Liquidity is considered as amount already withdrawn, this amount doesn't need
        // to be withdrawn.
        _withdraw(_amount - _liquidity());

        uint256 currentBalance = _liquidity();
        uint256 desiredBalance = _amount.mulDiv(MAX_SLIPPAGE - slippageLimit, MAX_SLIPPAGE);
        
        require(currentBalance >= desiredBalance, Errors.SlippageCheckFailed(desiredBalance, currentBalance));
    }

    /// @notice Deposits the entire balance of `asset` this contract holds into the underlying strategy. 
    /// @dev Child contract must implement the logic that will put the funds to work.
    function _deposit() internal virtual {}

    /// @notice Withdraws the specified `_amount` of `asset` from the underlying strategy. 
    /// @dev Child contract must implement the logic that will withdraw the funds.
    /// @param _amount The amount of `asset` to withdraw.
    function _withdraw(uint256 _amount) internal virtual {}

    /// @notice Withdraws as much funds as possible from the underlying strategy.
    /// @dev Child contract must implement the logic to withdraw as much funds as possible.
    /// The withdraw process shouldn't have a slippage check, as it is in an emergency situation.
    /// 
    function _emergencyWithdraw() internal virtual {}

    /// @dev Grants allowance for `asset` to the contracts used by the strategy adapter.
    /// It should be overridden by derived contracts to specify the exact contracts and amounts for the allowances.
    function _giveAllowances() internal virtual {}

    /// @dev Revokes all previously granted allowances for `asset`.
    /// It should be overridden by derived contracts to specify the exact contracts from which allowances are revoked.
    function _revokeAllowances() internal virtual {}
}

File 18 of 29 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 19 of 29 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 20 of 29 : StrategyAdapterAdminable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity 0.8.27;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";
import { IStrategyAdapterAdminable } from "interfaces/infra/multistrategy/IStrategyAdapterAdminable.sol";
import { Errors } from "src/infra/libraries/Errors.sol";

abstract contract StrategyAdapterAdminable is IStrategyAdapterAdminable, Ownable, Pausable {
    /*//////////////////////////////////////////////////////////////////////////
                                       STORAGE
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IStrategyAdapterAdminable
    mapping(address guardianAddress => bool isActive) public guardians;

    constructor(address _owner) Ownable(_owner) {}

    /*//////////////////////////////////////////////////////////////////////////
                                    MODIFIERS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Reverts if called by any account other than the owner, the manager, or a guardian.
    modifier onlyGuardian() {
        require(msg.sender == owner() || guardians[msg.sender], Errors.CallerNotGuardian(msg.sender));
        _;
    }

    /*//////////////////////////////////////////////////////////////////////////
                               USER-FACING NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @inheritdoc IStrategyAdapterAdminable
    function enableGuardian(address _guardian) external onlyOwner {
        guardians[_guardian] = true;
        emit GuardianEnabled(_guardian);
    }

    /// @inheritdoc IStrategyAdapterAdminable
    function revokeGuardian(address _guardian) external onlyOwner {
        guardians[_guardian] = false;
        emit GuardianRevoked(_guardian);
    }
}

File 21 of 29 : IStrategyAdapter.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

interface IStrategyAdapter {
    /// @notice Emitted when the slippage limit is set.
    /// @param slippageLimit The new slippage limit in basis points (BPS).
    event SlippageLimitSet(uint256 slippageLimit);

    /// @notice Returns the address of the multistrategy this Strategy belongs to.
    function multistrategy() external view returns (address);

    /// @notice Returns the address of the token used tby this strategy.
    /// @dev it should be the same as the token used by the multistrategy.
    function asset() external view returns (address);

    /// @notice Returns the identifier of this Strategy Adapter.
    function id() external view returns (string memory);

    /// @notice Returns the name of this Strategy Adapter.
    function name() external view returns (string memory);

    /// @notice Returns the current slippage limit in basis points (BPS).
    /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%.
    /// @return The maximum allowable slippage in basis points.
    function slippageLimit() external view returns (uint256);

    /// @notice Sets the maximum allowable slippage limit for withdrawals.
    /// @dev Slippage limit is expressed in basis points (BPS), where 10,000 BPS equals 100%.
    /// This limit represents the tolerated difference between the expected withdrawal amount
    /// and the actual amount withdrawn from the strategy.
    /// @param _slippageLimit The maximum allowable slippage in basis points.
    function setSlippageLimit(uint256 _slippageLimit) external;

    /// @notice Requests a credit to the multistrategy. The multistrategy will send the
    /// maximum amount of credit available for this strategy.
    function requestCredit() external;

    /// @notice Sends a report to the Multistrategy of any gain or loss this strategy has
    ///         made along an amount to be withdrawn and be used for debt repayment.
    /// @dev Only the owner can call it
    /// @param _amountToWithdraw Amount that will be withdrawn from the strategy and will
    ///         be available for debt repayment.
    function sendReport(uint256 _amountToWithdraw) external;

    /// @notice Sends a report to the Multistrategy of any gain or loss this strategy has made.
    /// @dev This report wont withdraw any funds to repay debt to the Multistrategy.
    /// Only the multistrategy can call it
    function askReport() external;

    /// @notice Sends a report to the Multistrategy after this strategy has been panicked.
    ///         Reporting any gains or loss based on the balance the could be emergency withdrawn
    /// @dev This function should only be called after a strategy has been retired.
    function sendReportPanicked() external;

    /// @notice Withdraws `asset` from the strategy.
    /// @dev Only callable by the multistrategy.
    /// @param _amount Amount of tokens to withdraw from the strategy.
    function withdraw(uint256 _amount) external returns (uint256);

    /// @notice Returns the amount of `asset` this strategy holds.
    function totalAssets() external view returns (uint256);

    /// @notice Returns the gain or loss this strategy has made since the last report.
    /// @dev The returned values will account for max slippage.
    function currentPnL() external view returns (uint256, uint256);

    /// @notice Starts the panic process for this strategy.
    /// The panic process consists of:
    ///     - Withdraw as much funds as possible from the underlying strategy.
    ///     - Report back to the multistrategy with the available funds.
    ///     - Revoke the allowance that this adapter has given to the underlying strategy.
    ///     - Pauses this contract.
    function panic() external;

    /// @notice Pauses the smart contract.
    /// @dev Functions that implement the `paused` modifier will revert when called.
    /// Guardians and Owner can call this function
    function pause() external;

    /// @notice Unpauses the smart contract.
    /// @dev Functions that implement the `paused` won't revert when called.
    /// Only the Owner can call this function
    function unpause() external;
}

File 22 of 29 : IMultistrategy.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

import { IMultistrategyManageable } from "interfaces/infra/multistrategy/IMultistrategyManageable.sol";

interface IMultistrategy is IMultistrategyManageable {
    /// @notice Emitted when an account has made a deposit.
    /// @param amount Amount of asset that has been deposited.
    /// @param recipient Address that will receive the receipt tokens.
    event Deposit(uint256 amount, address indexed recipient);

    /// @notice Emitted when an account has made a withdraw.
    /// @param amount Amount of shares that have been withdrawn.
    event Withdraw(uint256 amount);

    /// @notice Emitted when a strategy has requested a credit.
    /// @param strategy Address of the strategy that requested the credit.
    /// @param amount Amount of credit that has been granted to the strategy.
    event CreditRequested(address indexed strategy, uint256 amount);

    /// @notice Emitted when a strategy has submitted a report.
    /// @param strategy Address of the strategy that has submitted the report.
    /// @param debtRepaid Amount of debt that has been repaid by the strategy.
    /// @param gain Amount of gain that the strategy has reported.
    /// @param loss Amount of loss that the strategy has reported.
    event StrategyReported(address indexed strategy, uint256 debtRepaid, uint256 gain, uint256 loss);

    /// @notice Timestamp of the last report made by a strategy.
    function lastReport() external view returns (uint256);

    /// @notice Amount of tokens that are locked as "locked profit" and can't be withdrawn.
    function lockedProfit() external view returns (uint256);

    /// @notice Rate at which the locked profit gets unlocked per second.
    function lockedProfitDegradation() external view returns (uint256);

    /// @notice Returns the value of a share in `asset` value.
    function pricePerShare() external view returns (uint256);

    /// @notice Returns the amount of tokens a strategy can borrow from this Multistrategy.
    /// @param strategy Address of the strategy we want to know the credit available for.
    function creditAvailable(address strategy) external view returns (uint256);

    /// @notice Returns the excess of debt a strategy currently holds.
    /// @param strategy Address of the strategy we want to know if it has any debt excess.
    function debtExcess(address strategy) external view returns (uint256);

    /// @notice Returns the total debt of `strategy`.
    /// @param strategy Address of the strategy we want to know the `totalDebt`.
    function strategyTotalDebt(address strategy) external view returns (uint256);

    /// @notice Returns the aggregate PnL of all strategies at max slippage.
    function currentPnL() external view returns (uint256, uint256);
    
    /// @notice Send the available credit of the caller to the caller.
    /// @dev Reverts if the caller is *NOT* an active strategy
    function requestCredit() external returns (uint256);

    /// @notice Report the profit or loss of a strategy along any debt the strategy is willing to pay back.
    /// @dev Can only be called by an active strategy.
    /// @param _debtRepayment Amount that the strategy will send back to the multistrategy as debt repayment.
    /// @param _gain Amount that the strategy has realized as a gain since the last report and will send it
    ///                to this Multistrategy as earnings. 
    /// @param _loss Amount that the strategy has realized as a loss since the last report. 
    function strategyReport(uint256 _debtRepayment, uint256 _gain, uint256 _loss) external;

    /// @notice Emergency function to rescue tokens not related to the Multistrategy sent to the contract by mistake.
    /// @param token Address of the token that will be rescued.
    /// @param recipient Address of who will receive the tokens.
    function rescueToken(address token, address recipient) external;
}

File 23 of 29 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 24 of 29 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.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 Pausable is Context {
    bool private _paused;

    /**
     * @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.
     */
    constructor() {
        _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) {
        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 {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 25 of 29 : IStrategyAdapterAdminable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

interface IStrategyAdapterAdminable {
    /// @notice Emitted when a new guardian has been granted access.
    /// @param _guardian The address of the guardian.
    event GuardianEnabled(address indexed _guardian);

    /// @notice Emitted when a the access of a guardian has been revoked.
    /// @param _guardian The address of the guardian.
    event GuardianRevoked(address indexed _guardian);

    /// @notice List of addresses enabled as guardian.
    /// @param _guardian The address to check if it is a guardian.
    function guardians(address _guardian) external view returns (bool);

    /// @notice Enables an address to be a guardian.
    /// @dev Doesn't revert if:
    /// - guardian address is zero address.
    /// - guardian address is already enabled as guardian.
    /// @param _guardian The address of the guardian.
    function enableGuardian(address _guardian) external;

    /// @notice Revokes an address to be a guardian.
    /// @dev Doesn't revert if:
    /// - guardian address is zero address.
    /// - guardian address is already revoked.
    /// @param _guardian The address of the guardian.
    function revokeGuardian(address _guardian) external;
}

File 26 of 29 : IMultistrategyManageable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

import { IMultistrategyAdminable } from "interfaces/infra/multistrategy/IMultistrategyAdminable.sol";
import { MStrat } from "src/types/DataTypes.sol";

interface IMultistrategyManageable is IMultistrategyAdminable {
    /// @notice Emitted when the protocol fee recipient is set.
    /// @param _protocolFeeRecipient The address that will receive the protocol fee.
    event ProtocolFeeRecipientSet(address indexed _protocolFeeRecipient);

    /// @notice Emitted when the performance fee is set.
    /// @param _performanceFee The new performance fee value.
    event PerformanceFeeSet(uint256 _performanceFee);

    /// @notice Emitted when the deposit limit is set.
    /// @param _depositLimit The new deposit limit value.
    event DepositLimitSet(uint256 _depositLimit);

    /// @notice Emitted when the slippage limit is set.
    /// @param _slippageLimit The new slippage limit value.
    event SlippageLimitSet(uint256 _slippageLimit);

    /// @notice Emitted when a new withdrawal order has been set.
    event WithdrawOrderSet();

    /// @notice Emitted when the debt ratio for a specific strategy is set.
    /// @param _strategy The address of the strategy whose debt ratio was updated.
    /// @param _debtRatio The new debt ratio value for the specified strategy.
    event StrategyDebtRatioSet(address indexed _strategy, uint256 _debtRatio);

    /// @notice Emitted when the minimum debt delta for a specific strategy is set.
    /// @param _strategy The address of the strategy whose minimum debt delta was updated.
    /// @param _minDebtDelta The new minimum debt delta value for the specified strategy.
    event StrategyMinDebtDeltaSet(address indexed _strategy, uint256 _minDebtDelta);

    /// @notice Emitted when the maximum debt delta for a specific strategy is set.
    /// @param _strategy The address of the strategy whose maximum debt delta was updated.
    /// @param _maxDebtDelta The new maximum debt delta value for the specified strategy.
    event StrategyMaxDebtDeltaSet(address indexed _strategy, uint256 _maxDebtDelta);

    /// @notice Emitted when a new strategy is added.
    /// @param _strategy The address of the newly added strategy.
    event StrategyAdded(address indexed _strategy);

    /// @notice Emitted when a strategy is retired.
    /// @param _strategy The address of the retired strategy.
    event StrategyRetired(address indexed _strategy);

    /// @notice Emitted when a strategy is removed.
    /// @param _strategy The address of the removed strategy.
    event StrategyRemoved(address indexed _strategy);

    /// @notice Emitted when the deposits into this multistrategy are paused.
    event MultistrategyRetired();

    /// @notice Address that will receive performance fee.
    function protocolFeeRecipient() external view returns (address);

    /// @notice Fee on the yield generated (in BPS).
    /// @dev Performance fee is taken on `strategyReport()` function on the Multistrategy contract.
    function performanceFee() external view returns (uint256);

    /// @notice Limit for total assets the multistrategy can hold.
    function depositLimit() external view returns (uint256);

    /// @notice Debt ratio of the multistrategy across all strategies (in BPS).
    /// @dev The debt ratio cannot exceed 10_000 BPS (100 %).
    function debtRatio() external view returns (uint256);

    /// @notice Amount of tokens that the strategies have borrowed in total.
    function totalDebt() external view returns (uint256);

    /// @notice Returns the current slippage limit in basis points (BPS).
    /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%.
    function slippageLimit() external view returns (uint256);

    /// @notice Amount of active strategies.
    function activeStrategies() external view returns (uint8);

    /// @notice Returns true if multistrategy has been retired. 
    function retired() external view returns (bool);

    /// @notice Returns the withdraw order.
    function getWithdrawOrder() external view returns (address[] memory);

    /// @notice Returns the strategy params of `strategy`
    /// @param _strategy Address of the strategy the it will returns the parameters of.
    function getStrategyParameters(address _strategy) external view returns (MStrat.StrategyParams calldata);

    /// @notice Sets the recipient address of the performance fee.
    /// @dev Emits a `SetProtocolFeeRecipient` event.
    /// @param _protocolFeeRecipient Address that will receive the fees.
    function setProtocolFeeRecipient(address _protocolFeeRecipient) external;

    /// @notice Sets the performance fee in BPS.
    /// @dev Reverts if `performanceFee` is above MAX_PERFORMANCE_FEE
    /// @dev Emits a `SetPerformanceFee` event.
    /// @param _performanceFee New performance fee.
    function setPerformanceFee(uint256 _performanceFee) external;

    /// @notice Sets the deposit limit.
    /// @dev Emits a `SetDepositLimit` event.
    /// @param _depositLimit New deposit limit.
    function setDepositLimit(uint256 _depositLimit) external;

    /// @notice Sets the slippage limit of this Multistrategy.
    /// @dev The slippage limit is expressed in BPS, where 10,000 BPS equals 100%.
    /// @param _slippageLimit New slippage limit.
    function setSlippageLimit(uint256 _slippageLimit) external;

    /// @notice Sets the withdraw order. First position in the array will be the first strategy that it will get the funds withdrawn
    /// @dev It will revert if a strategy in the array is not active or if the array contains duplicate addresses.
    /// @param _strategies Array of strategy addresses
    function setWithdrawOrder(address[] memory _strategies) external;

    /// @notice Adds a strategy to the multistrategy.
    /// @dev The strategy will be appended to `withdrawOrder`.
    /// @param _strategy The address of the strategy.
    /// @param _debtRatio The share of total assets in the Multistrategy this strategy will have access to.
    /// @param _minDebtDelta Lower limit on the increase of debt.
    /// @param _maxDebtDelta Upper limit on the increase of debt.
    function addStrategy(
        address _strategy,
        uint256 _debtRatio,
        uint256 _minDebtDelta,
        uint256 _maxDebtDelta
    ) external;

    /// @notice Sets the strategy debtRatio to 0, which prevents any further deposits into the strategy.
    /// @dev Retiring a strategy will set the approval of `asset` to the retired strategy to 0.
    /// @param _strategy The address of the strategy that will be retired.
    function retireStrategy(address _strategy) external;

    /// @notice Removes a strategy from `withdrawOrder`.
    /// @param _strategy The address of the strategy that will be removed.
    function removeStrategy(address _strategy) external;

    /// @notice Change the debt ratio of a strategy.
    /// @param _strategy Address of the strategy.
    /// @param _debtRatio New debt ratio.
    function setStrategyDebtRatio(address _strategy, uint256 _debtRatio) external;

    /// @notice Change the minimum amount of debt a strategy can take.
    /// @dev Used to limit the minimum amount of debt a strategy should take. 
    ///      Taking a small credit wouldn't be optimal gas-wise.
    /// @param _strategy Address of the strategy.
    /// @param _minDebtDelta Lower limit of the change of debt.
    function setStrategyMinDebtDelta(address _strategy, uint256 _minDebtDelta) external;

    /// @notice Change the maximum amount of debt a strategy can take at once.
    /// @dev Used to protect large debt repayments or withdraws. Risks are IL, or low liquidity.
    /// @param _strategy Address of the strategy.
    /// @param _maxDebtDelta Upper limit of the change of debt.
    function setStrategyMaxDebtDelta(address _strategy, uint256 _maxDebtDelta) external;

    /// @notice Retires the Multistrategy. End of Life.
    function retire() external;
}

File 27 of 29 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 28 of 29 : IMultistrategyAdminable.sol
// SPDX-License-Identifier: GNU AGPLv3

pragma solidity ^0.8.27;

/// @title IMultistrategyAdminable
/// @notice Contract module that provides a 3 level access control mechanism, with an owner, a manager
/// and a list of guardians that can be granted exclusive access to specific functions.
/// The inheriting contract must set the initial owner and the initial manager in the constructor.
interface IMultistrategyAdminable {
    /*//////////////////////////////////////////////////////////////////////////
                                       EVENTS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Emitted when a new manager is set.
    /// @param _manager The address of the new manager.
    event ManagerSet(address indexed _manager);

    /// @notice Emitted when a new guardian has been granted access.
    /// @param _guardian The address of the guardian.
    event GuardianEnabled(address indexed _guardian);

    /// @notice Emitted when a the access of a guardian has been revoked.
    /// @param _guardian The address of the guardian.
    event GuardianRevoked(address indexed _guardian);

    /*//////////////////////////////////////////////////////////////////////////
                                 CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice The address of the manager.
    function manager() external view returns (address);

    /// @notice List of addresses enabled as guardian.
    /// @param _guardian The address to check if it is a guardian.
    function guardians(address _guardian) external view returns (bool);
    
    /*//////////////////////////////////////////////////////////////////////////
                               NON-CONSTANT FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /// @notice Sets the manager address.
    /// @dev Doesn't revert if:
    /// - manager address is zero address.
    /// - manager address is the same as previous manager address.
    /// @param _manager Address of the new manager.
    function setManager(address _manager) external;

    /// @notice Enables an address to be a guardian.
    /// @dev Doesn't revert if:
    /// - guardian address is zero address.
    /// - guardian address is already enabled as guardian.
    /// @param _guardian The address of the guardian.
    function enableGuardian(address _guardian) external;

    /// @notice Revokes an address to be a guardian.
    /// @dev Doesn't revert if:
    /// - guardian address is zero address.
    /// - guardian address is already revoked.
    /// @param _guardian The address of the guardian.
    function revokeGuardian(address _guardian) external;

    /// @notice Pauses the smart contract.
    /// @dev Functions that implement the `paused` modifier will revert when called.
    /// Guardians, Manager and Owner can call this function
    function pause() external;

    /// @notice Unpauses the smart contract.
    /// @dev Functions that implement the `paused` won't revert when called.
    /// Guardians, Manager and Owner can call this function
    function unpause() external;
}

File 29 of 29 : DataTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;

/// @notice Namespace for the structs used in `Multistrategy`
library MStrat {
    /// @notice Struct that contains a strategy data
    /// @param activation Block.timestamp of when the strategy was activated. 0 means not active
    /// @param debtRatio Maximum amount the strategy can borrow from the Multistrategy (in BPS of total assets in a Multistrategy)
    /// @param lastReport 
    /// @param minDebtDelta Lower limit on the increase or decrease of debt since last harvest
    /// @param maxDebtDelta Upper limit on the increase or decrease of debt since last harvest
    /// @param totalDebt Total debt that this strategy has
    /// @param totalGain Total gains that this strategy has realized
    /// @param totalLoss Total losses that this strategy has realized
    struct StrategyParams {
        uint256 activation;
        uint256 debtRatio;
        uint256 lastReport;
        uint256 minDebtDelta;
        uint256 maxDebtDelta;
        uint256 totalDebt;
        uint256 totalGain;
        uint256 totalLoss;
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "src/=src/",
    "interfaces/=src/interfaces/",
    "test-utils/=test/utils/",
    "createx/=lib/createx-forge/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
    "@openzeppelin-4/contracts/=lib/openzeppelin-contracts-4/contracts/",
    "@layerzero/=lib/solidity-examples/contracts/",
    "@aave/=lib/aave-v3-origin/src/",
    "@xerc20/=lib/xERC20/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@uniswap/v3-core/=lib/v3-core/",
    "@addressbook/=address-book/",
    "@trust-security/trustlessPermit/=lib/trustlessPermit/",
    "@uniswapV3-periphery/=lib/v3-periphery/contracts/",
    "@prb/math/=lib/prb-math/",
    "@properties/=lib/properties/",
    "@prb/test/=lib/prb-math/node_modules/@prb/test/",
    "ERC4626/=lib/properties/lib/ERC4626/contracts/",
    "aave-v3-core/=lib/aave-v3-origin/src/core/",
    "aave-v3-origin/=lib/aave-v3-origin/",
    "aave-v3-periphery/=lib/aave-v3-origin/src/periphery/",
    "common/=lib/common/",
    "createx-forge/=lib/createx-forge/",
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "erc4626-tests/=lib/erc4626-tests/",
    "openzeppelin-contracts-4/=lib/openzeppelin-contracts-4/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "openzeppelin/=lib/openzeppelin-contracts-4/contracts/",
    "prb-math/=lib/prb-math/src/",
    "properties/=lib/properties/contracts/",
    "solidity-examples/=lib/solidity-examples/contracts/",
    "solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
    "solidity-utils/=lib/aave-v3-origin/lib/solidity-utils/",
    "solmate/=lib/solmate/src/",
    "trustlessPermit/=lib/trustlessPermit/",
    "v3-core/=lib/v3-core/",
    "v3-periphery/=lib/v3-periphery/contracts/",
    "xERC20/=lib/xERC20/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_multistrategy","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_incentivesController","type":"address"},{"components":[{"internalType":"address","name":"swapper","type":"address"},{"internalType":"address","name":"wrappedGas","type":"address"}],"internalType":"struct StrategyAdapterHarvestable.HarvestAddresses","name":"_harvestAddresses","type":"tuple"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_id","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"multAsset","type":"address"},{"internalType":"address","name":"stratAsset","type":"address"}],"name":"AssetMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerNotGuardian","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerNotMultistrategy","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"InvalidRewardToken","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"SlippageCheckFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"slippageLimit","type":"uint256"}],"name":"SlippageLimitExceeded","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountHarvested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAssets","type":"uint256"}],"name":"AdapterHarvested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_guardian","type":"address"}],"name":"GuardianEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_guardian","type":"address"}],"name":"GuardianRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"slippageLimit","type":"uint256"}],"name":"SlippageLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newSwapper","type":"address"}],"name":"SwapperUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"askReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentPnL","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"enableGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guardianAddress","type":"address"}],"name":"guardians","outputs":[{"internalType":"bool","name":"isActive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"id","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incentivesController","outputs":[{"internalType":"contract ISiloIncentivesController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastHarvest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"minimumAmounts","outputs":[{"internalType":"uint256","name":"minimumAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multistrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"panic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestCredit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resetRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"revokeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_repayAmount","type":"uint256"}],"name":"sendReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sendReportPanicked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_minAmount","type":"uint256"}],"name":"setRewardMinimumAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_slippageLimit","type":"uint256"}],"name":"setSlippageLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slippageLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"updateSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IERC4626","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

60c060405234801561000f575f5ffd5b50604051612af8380380612af883398101604081905261002e916106cd565b86868685858585858484848484838333808061006457604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b61006d81610262565b50505f805460ff60a01b19169055604080516338d52e0f60e01b815290516001600160a01b038616916338d52e0f9160048083019260209291908290030181865afa1580156100be573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e291906107b2565b6001600160a01b0316836001600160a01b031614846001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610132573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061015691906107b2565b84909161018957604051634e83a9b960e01b81526001600160a01b0392831660048201529116602482015260440161005b565b50506001600160a01b03808516608052831660a0525f60025560036101ae838261084e565b5060046101bb828261084e565b5060805160a0516101d9916001600160a01b03909116905f196102b1565b50508451600880546001600160a01b03199081166001600160a01b0393841617909155602090960151600780548816918316919091179055600a8054909616908c16179094555061022f94506103759350505050565b5050600b80546001600160a01b0319166001600160a01b0399909916989098179097555061093d98505050505050505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915261030990859083906103b316565b61036f57604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915261036591869161045416565b61036f8482610454565b50505050565b600a5460a051610393916001600160a01b0391821691165f196102b1565b6008546007546103b1916001600160a01b0391821691165f196102b1565b565b5f5f5f846001600160a01b0316846040516103ce9190610908565b5f604051808303815f865af19150503d805f8114610407576040519150601f19603f3d011682016040523d82523d5f602084013e61040c565b606091505b5091509150818015610436575080511580610436575080806020019051810190610436919061091e565b801561044b57505f856001600160a01b03163b115b95945050505050565b5f6104686001600160a01b038416836104ba565b905080515f1415801561048c57508080602001905181019061048a919061091e565b155b156104b557604051635274afe760e01b81526001600160a01b038416600482015260240161005b565b505050565b60606104c783835f6104ce565b9392505050565b6060814710156104f35760405163cd78605960e01b815230600482015260240161005b565b5f5f856001600160a01b0316848660405161050e9190610908565b5f6040518083038185875af1925050503d805f8114610548576040519150601f19603f3d011682016040523d82523d5f602084013e61054d565b606091505b50909250905061055e868383610568565b9695505050505050565b60608261057d57610578826105c4565b6104c7565b815115801561059457506001600160a01b0384163b155b156105bd57604051639996b31560e01b81526001600160a01b038516600482015260240161005b565b50806104c7565b8051156105d45780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80516001600160a01b0381168114610603575f5ffd5b919050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b038111828210171561063e5761063e610608565b60405290565b5f82601f830112610653575f5ffd5b81516001600160401b0381111561066c5761066c610608565b604051601f8201601f19908116603f011681016001600160401b038111828210171561069a5761069a610608565b6040528181528382016020018510156106b1575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f5f5f5f5f5f5f8789036101008112156106e5575f5ffd5b6106ee896105ed565b97506106fc60208a016105ed565b965061070a60408a016105ed565b955061071860608a016105ed565b94506040607f198201121561072b575f5ffd5b5061073461061c565b61074060808a016105ed565b815261074e60a08a016105ed565b602082015260c08901519093506001600160401b0381111561076e575f5ffd5b61077a8a828b01610644565b60e08a015190935090506001600160401b03811115610797575f5ffd5b6107a38a828b01610644565b91505092959891949750929550565b5f602082840312156107c2575f5ffd5b6104c7826105ed565b600181811c908216806107df57607f821691505b6020821081036107fd57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156104b557805f5260205f20601f840160051c810160208510156108285750805b601f840160051c820191505b81811015610847575f8155600101610834565b5050505050565b81516001600160401b0381111561086757610867610608565b61087b8161087584546107cb565b84610803565b6020601f8211600181146108ad575f83156108965750848201515b5f19600385901b1c1916600184901b178455610847565b5f84815260208120601f198516915b828110156108dc57878501518255602094850194600190920191016108bc565b50848210156108f957868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f82518060208501845e5f920191825250919050565b5f6020828403121561092e575f5ffd5b815180151581146104c7575f5ffd5b60805160a05161211c6109dc5f395f81816102900152818161060f0152818161072e015281816108f601528181610ea901528181611043015281816111960152818161137d015281816113c0015281816113fb01526115a501525f8181610412015281816105910152818161063101528181610b1901528181610b89015281816116500152818161187d015281816119650152611cd3015261211c5ff3fe608060405234801561000f575f5ffd5b50600436106101fd575f3560e01c8063a0c9d3fc11610114578063c031d2fb116100a9578063f163d5e811610079578063f163d5e81461040d578063f1a392da14610434578063f2fde38b1461043d578063f301af4214610450578063fbfa77cf14610463575f5ffd5b8063c031d2fb146103cc578063d3033c39146103d4578063e84142ff146103e7578063e98af937146103fa575f5ffd5b8063af640d0f116100e4578063af640d0f146103a1578063af648c3d146103a9578063bbb356d5146103bc578063c0275d5b146103c4575f5ffd5b8063a0c9d3fc1461036b578063ad08b5401461037e578063ad29f5da14610386578063af1df2551461038e575f5ffd5b80634641257d116101955780635c975abb116101655780635c975abb14610327578063715018a6146103385780638456cb59146103405780638da5cb5b146103485780639c9b2e2114610358575f5ffd5b80634641257d146102f15780634700d305146102f957806349185ab8146103015780635b07871a1461031e575f5ffd5b80632e1a7d4d116101d05780632e1a7d4d1461027857806338d52e0f1461028b5780633afca64b146102ca5780633f4ba83a146102e9575f5ffd5b806301e1d114146102015780630633b14a1461021c57806306fdde031461024e5780631fe4ba1714610263575b5f5ffd5b610209610476565b6040519081526020015b60405180910390f35b61023e61022a366004611f73565b60016020525f908152604090205460ff1681565b6040519015158152602001610213565b610256610484565b6040516102139190611f8c565b610276610271366004611fc1565b610510565b005b610209610286366004611fc1565b610585565b6102b27f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610213565b6102096102d8366004611f73565b60096020525f908152604090205481565b61027661065e565b610276610678565b6102766107fd565b610309610867565b60408051928352602083019190915201610213565b61020960025481565b5f54600160a01b900460ff1661023e565b610276610881565b610276610892565b5f546001600160a01b03166102b2565b610276610366366004611f73565b6108ec565b610276610379366004611f73565b6109d9565b610276610a2f565b610276610a47565b600b546102b2906001600160a01b031681565b610256610ab1565b6102766103b7366004611f73565b610abe565b600654610209565b610276610b0e565b610276610b76565b6102766103e2366004611f73565b610c18565b6102766103f5366004611fc1565b610d0b565b610276610408366004611fd8565b610d24565b6102b27f000000000000000000000000000000000000000000000000000000000000000081565b61020960055481565b61027661044b366004611f73565b610d47565b6102b261045e366004611fc1565b610d81565b600a546102b2906001600160a01b031681565b5f61047f610da9565b905090565b6003805461049190612000565b80601f01602080910402602001604051908101604052809291908181526020018280546104bd90612000565b80156105085780601f106104df57610100808354040283529160200191610508565b820191905f5260205f20905b8154815290600101906020018083116104eb57829003601f168201915b505050505081565b610518610f42565b8061271081111561054857604051631920afa360e11b815260040161053f91815260200190565b60405180910390fd5b5060028190556040518181527ff6c3c65ea16f3d6351596faef627640721d4bbe1c42e5f06ae3b40f5a399cfc6906020015b60405180910390a150565b5f336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681146105dc5760405163589614ed60e01b81526001600160a01b03909116600482015260240161053f565b506105e5610f6e565b6105ee82610f98565b5f610600836105fb61102c565b6110b4565b90506106566001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f0000000000000000000000000000000000000000000000000000000000000000836110cd565b90505b919050565b610666610f42565b61066e611131565b610676611185565b565b610680610f6e565b6106886111de565b610690611238565b6007546001600160a01b03165f81815260096020526040908190205490516370a0823160e01b81523060048201529091906370a0823190602401602060405180830381865afa1580156106e5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107099190612038565b111561067657610717611378565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561077b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061079f9190612038565b90506107a96113e4565b426005557f341d28d8d1f85f50ddd0e4e49db6b1172383a69070464a3efa89ab297afa884e33826107d8610da9565b604080516001600160a01b03909416845260208401929092529082015260600161057a565b5f546001600160a01b03163314806108235750335f9081526001602052604090205460ff165b339061084e57604051631f851be960e31b81526001600160a01b03909116600482015260240161053f565b506108576114e4565b61085f611594565b6106766115eb565b5f5f610879610874610da9565b61162d565b915091509091565b610889610f42565b6106765f6116ed565b5f546001600160a01b03163314806108b85750335f9081526001602052604090205460ff165b33906108e357604051631f851be960e31b81526001600160a01b03909116600482015260240161053f565b5061085f611594565b6108f4610f42565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b03161415801561094457506007546001600160a01b03828116911614155b819061096f57604051637926036960e01b81526001600160a01b03909116600482015260240161053f565b506109798161173c565b600680546001810182555f919091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b038381169182179092556008546109d692165f19611778565b50565b6109e1610f42565b6001600160a01b0381165f818152600160208190526040808320805460ff1916909217909155517fd0cd09d977e63f0acaf30ce79e6479c90d7ea56a41a0849ac60f5440ac608a159190a250565b610a37610f42565b610a3f611807565b610676611830565b610a4f610f42565b5f5b600654811015610aa55760085460068054610a9d926001600160a01b0316915f9185908110610a8257610a8261204f565b5f918252602090912001546001600160a01b03169190611778565b600101610a51565b5061067660065f611f2f565b6004805461049190612000565b610ac6610f42565b6001600160a01b0381165f81815260016020526040808220805460ff19169055517f0c92d12d8037dd6d77aed8d12addd54d5eb2a6801541a1bf87c9822e78eea4219190a250565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168114610b645760405163589614ed60e01b81526001600160a01b03909116600482015260240161053f565b50610b6d610f6e565b6106765f6118e2565b610b7e610f42565b610b86610f6e565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c031d2fb6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610be4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c089190612038565b905080156109d6576109d66113e4565b610c20610f42565b5f5b600654811015610c87575f60068281548110610c4057610c4061204f565b5f9182526020822001546008546001600160a01b039182169350610c6992849290911690611778565b610c7e6001600160a01b038216845f19611778565b50600101610c22565b50600854600754610ca5916001600160a01b0391821691165f611778565b600754610cbd906001600160a01b0316825f19611778565b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527fd8489678a8aa0cd1402b92f8985fe21057591f12330e197b86a0a5fa96e1c02a9060200161057a565b610d13610f42565b610d1b610f6e565b6109d6816118e2565b610d2c610f42565b6001600160a01b039091165f90815260096020526040902055565b610d4f610f42565b6001600160a01b038116610d7857604051631e4fbdf760e01b81525f600482015260240161053f565b6109d6816116ed565b60068181548110610d90575f80fd5b5f918252602090912001546001600160a01b0316905081565b600a546040516370a0823160e01b81523060048201525f9182916001600160a01b03909116906370a0823190602401602060405180830381865afa158015610df3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e179190612038565b600a546040516303d1689d60e11b8152600481018390529192505f916001600160a01b03909116906307a2d13a90602401602060405180830381865afa158015610e63573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e879190612038565b6040516370a0823160e01b81523060048201529091505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610eee573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f129190612038565b90505f610f1f8284612077565b90505f8111610f2e5780610f39565b610f3960018261208a565b94505050505090565b5f546001600160a01b031633146106765760405163118cdaa760e01b815233600482015260240161053f565b5f54600160a01b900460ff16156106765760405163d93c066560e01b815260040160405180910390fd5b801580610fac5750610fa861102c565b8111155b15610fb45750565b610fce610fbf61102c565b610fc9908361208a565b6119cc565b5f610fd761102c565b90505f610ff7600254612710610fed919061208a565b8490612710611a0a565b905080828181101561102557604051630a8f6c8d60e41b81526004810192909252602482015260440161053f565b5050505050565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611090573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047f9190612038565b5f8183106110c257816110c4565b825b90505b92915050565b6040516001600160a01b0383811660248301526044820183905261112c91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611aca565b505050565b611139611807565b5f805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600a546111c0906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691165f19611778565b600854600754610676916001600160a01b0391821691165f19611778565b600b54604051633bd73ee360e21b81523060048201526001600160a01b039091169063ef5cfb8c906024015f604051808303815f87803b158015611220575f5ffd5b505af1158015611232573d5f5f3e3d5ffd5b50505050565b5f5b6006548110156109d6575f600682815481106112585761125861204f565b5f9182526020822001546040516370a0823160e01b81523060048201526001600160a01b03909116925082906370a0823190602401602060405180830381865afa1580156112a8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112cc9190612038565b6001600160a01b0383165f9081526009602052604090205490915081111561136e57600854600754604051630df791e560e41b81526001600160a01b03858116600483015291821660248201526044810184905291169063df791e50906064016020604051808303815f875af1158015611348573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061136c9190612038565b505b505060010161123a565b6007547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161461067657600754610676906001600160a01b03167f0000000000000000000000000000000000000000000000000000000000000000611b2b565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611448573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061146c9190612038565b600a54604051636e553f6560e01b8152600481018390523060248201529192506001600160a01b031690636e553f65906044015b6020604051808303815f875af11580156114bc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114e09190612038565b5050565b600a5460405163ce96cb7760e01b81523060048201525f916001600160a01b03169063ce96cb7790602401602060405180830381865afa15801561152a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061154e9190612038565b905080156109d657600a54604051632d182be560e21b815260048101839052306024820181905260448201526001600160a01b039091169063b460af94906064016114a0565b600a546115ce906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691165f611778565b600854600754610676916001600160a01b0391821691165f611778565b6115f3610f6e565b5f805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586111683390565b6040516361f488bf60e01b81523060048201525f90819081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906361f488bf90602401602060405180830381865afa158015611695573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116b99190612038565b90505f808286106116d5576116ce838761208a565b91506116e2565b6116df868461208a565b90505b909590945092505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600a5481906001600160a01b038281169116036114e057604051637926036960e01b81526001600160a01b03909116600482015260240161053f565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526117c98482611c11565b611232576040516001600160a01b0384811660248301525f60448301526117fd91869182169063095ea7b3906064016110fa565b6112328482611aca565b5f54600160a01b900460ff1661067657604051638dfc202b60e01b815260040160405180910390fd5b5f61183961102c565b90505f5f6118468361162d565b90925090505f611856838561208a565b604051633d4f9ac360e11b81526004810182905260248101859052604481018490529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690637a9f3586906064015f604051808303815f87803b1580156118c6575f5ffd5b505af11580156118d8573d5f5f3e3d5ffd5b5050505050505050565b5f6118eb610da9565b90505f5f6118f88361162d565b915091505f6119078584611cb2565b905061191281610f98565b611925838361191f61102c565b84611db5565b90935091505f8361193461102c565b61193e919061208a565b604051633d4f9ac360e11b81526004810182905260248101869052604481018590529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690637a9f3586906064015f604051808303815f87803b1580156119ae575f5ffd5b505af11580156119c0573d5f5f3e3d5ffd5b50505050505050505050565b600a54604051632d182be560e21b815260048101839052306024820181905260448201526001600160a01b039091169063b460af94906064016114a0565b5f838302815f1985870982811083820303915050805f03611a3e57838281611a3457611a3461209d565b0492505050611ac3565b808411611a5e5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150505b9392505050565b5f611ade6001600160a01b03841683611e2d565b905080515f14158015611b02575080806020019051810190611b0091906120b1565b155b1561112c57604051635274afe760e01b81526001600160a01b038416600482015260240161053f565b6040516370a0823160e01b81523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa158015611b6f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b939190612038565b600854604051630df791e560e41b81526001600160a01b03868116600483015285811660248301526044820184905292935091169063df791e50906064016020604051808303815f875af1158015611bed573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112329190612038565b5f5f5f846001600160a01b031684604051611c2c91906120d0565b5f604051808303815f865af19150503d805f8114611c65576040519150601f19603f3d011682016040523d82523d5f602084013e611c6a565b606091505b5091509150818015611c94575080511580611c94575080806020019051810190611c9491906120b1565b8015611ca957505f856001600160a01b03163b115b95945050505050565b6040516374f9479b60e01b81523060048201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906374f9479b90602401602060405180830381865afa158015611d18573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d3c9190612038565b90505f81118015611d4c57505f84115b15611dad5761271060025403611d6e57611d668385612077565b9150506110c7565b5f611d8d612710600254612710611d85919061208a565b849190611a0a565b905083611d9a86836110b4565b611da49190612077565b925050506110c7565b509092915050565b5f5f5f848411611dc5575f611dcf565b611dcf858561208a565b9050805f03611de45786869250925050611e24565b86811115611e0057611df6878261208a565b90505f9650611e10565b611e0a818861208a565b96505f90505b611e1a8187612077565b9550868692509250505b94509492505050565b60606110c483835f845f5f856001600160a01b03168486604051611e5191906120d0565b5f6040518083038185875af1925050503d805f8114611e8b576040519150601f19603f3d011682016040523d82523d5f602084013e611e90565b606091505b5091509150611ea0868383611eaa565b9695505050505050565b606082611ebf57611eba82611f06565b611ac3565b8151158015611ed657506001600160a01b0384163b155b15611eff57604051639996b31560e01b81526001600160a01b038516600482015260240161053f565b5080611ac3565b805115611f165780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5080545f8255905f5260205f20908101906109d691905b80821115611f59575f8155600101611f46565b5090565b80356001600160a01b0381168114610659575f5ffd5b5f60208284031215611f83575f5ffd5b6110c482611f5d565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f60208284031215611fd1575f5ffd5b5035919050565b5f5f60408385031215611fe9575f5ffd5b611ff283611f5d565b946020939093013593505050565b600181811c9082168061201457607f821691505b60208210810361203257634e487b7160e01b5f52602260045260245ffd5b50919050565b5f60208284031215612048575f5ffd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b808201808211156110c7576110c7612063565b818103818111156110c7576110c7612063565b634e487b7160e01b5f52601260045260245ffd5b5f602082840312156120c1575f5ffd5b81518015158114611ac3575f5ffd5b5f82518060208501845e5f92019182525091905056fea2646970667358221220f9ea52c98d9d272e27a0d93397b7546c812b825873a3244fcd51a1e68c42ce2064736f6c634300081b0033000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa600000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388940000000000000000000000004e216c15697c1392fe59e1014b009505e05810df0000000000000000000000000dd368cd6d8869f2b21ba3cb4fd7ba107a2e3752000000000000000000000000108e823a26c5fb096d1f7c493809cce9015507a6000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad3800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000001253696c6f20532f555344432e652049442d380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000753494c4f2d563200000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561000f575f5ffd5b50600436106101fd575f3560e01c8063a0c9d3fc11610114578063c031d2fb116100a9578063f163d5e811610079578063f163d5e81461040d578063f1a392da14610434578063f2fde38b1461043d578063f301af4214610450578063fbfa77cf14610463575f5ffd5b8063c031d2fb146103cc578063d3033c39146103d4578063e84142ff146103e7578063e98af937146103fa575f5ffd5b8063af640d0f116100e4578063af640d0f146103a1578063af648c3d146103a9578063bbb356d5146103bc578063c0275d5b146103c4575f5ffd5b8063a0c9d3fc1461036b578063ad08b5401461037e578063ad29f5da14610386578063af1df2551461038e575f5ffd5b80634641257d116101955780635c975abb116101655780635c975abb14610327578063715018a6146103385780638456cb59146103405780638da5cb5b146103485780639c9b2e2114610358575f5ffd5b80634641257d146102f15780634700d305146102f957806349185ab8146103015780635b07871a1461031e575f5ffd5b80632e1a7d4d116101d05780632e1a7d4d1461027857806338d52e0f1461028b5780633afca64b146102ca5780633f4ba83a146102e9575f5ffd5b806301e1d114146102015780630633b14a1461021c57806306fdde031461024e5780631fe4ba1714610263575b5f5ffd5b610209610476565b6040519081526020015b60405180910390f35b61023e61022a366004611f73565b60016020525f908152604090205460ff1681565b6040519015158152602001610213565b610256610484565b6040516102139190611f8c565b610276610271366004611fc1565b610510565b005b610209610286366004611fc1565b610585565b6102b27f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889481565b6040516001600160a01b039091168152602001610213565b6102096102d8366004611f73565b60096020525f908152604090205481565b61027661065e565b610276610678565b6102766107fd565b610309610867565b60408051928352602083019190915201610213565b61020960025481565b5f54600160a01b900460ff1661023e565b610276610881565b610276610892565b5f546001600160a01b03166102b2565b610276610366366004611f73565b6108ec565b610276610379366004611f73565b6109d9565b610276610a2f565b610276610a47565b600b546102b2906001600160a01b031681565b610256610ab1565b6102766103b7366004611f73565b610abe565b600654610209565b610276610b0e565b610276610b76565b6102766103e2366004611f73565b610c18565b6102766103f5366004611fc1565b610d0b565b610276610408366004611fd8565b610d24565b6102b27f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa681565b61020960055481565b61027661044b366004611f73565b610d47565b6102b261045e366004611fc1565b610d81565b600a546102b2906001600160a01b031681565b5f61047f610da9565b905090565b6003805461049190612000565b80601f01602080910402602001604051908101604052809291908181526020018280546104bd90612000565b80156105085780601f106104df57610100808354040283529160200191610508565b820191905f5260205f20905b8154815290600101906020018083116104eb57829003601f168201915b505050505081565b610518610f42565b8061271081111561054857604051631920afa360e11b815260040161053f91815260200190565b60405180910390fd5b5060028190556040518181527ff6c3c65ea16f3d6351596faef627640721d4bbe1c42e5f06ae3b40f5a399cfc6906020015b60405180910390a150565b5f336001600160a01b037f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa61681146105dc5760405163589614ed60e01b81526001600160a01b03909116600482015260240161053f565b506105e5610f6e565b6105ee82610f98565b5f610600836105fb61102c565b6110b4565b90506106566001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894167f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa6836110cd565b90505b919050565b610666610f42565b61066e611131565b610676611185565b565b610680610f6e565b6106886111de565b610690611238565b6007546001600160a01b03165f81815260096020526040908190205490516370a0823160e01b81523060048201529091906370a0823190602401602060405180830381865afa1580156106e5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107099190612038565b111561067657610717611378565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316906370a0823190602401602060405180830381865afa15801561077b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061079f9190612038565b90506107a96113e4565b426005557f341d28d8d1f85f50ddd0e4e49db6b1172383a69070464a3efa89ab297afa884e33826107d8610da9565b604080516001600160a01b03909416845260208401929092529082015260600161057a565b5f546001600160a01b03163314806108235750335f9081526001602052604090205460ff165b339061084e57604051631f851be960e31b81526001600160a01b03909116600482015260240161053f565b506108576114e4565b61085f611594565b6106766115eb565b5f5f610879610874610da9565b61162d565b915091509091565b610889610f42565b6106765f6116ed565b5f546001600160a01b03163314806108b85750335f9081526001602052604090205460ff165b33906108e357604051631f851be960e31b81526001600160a01b03909116600482015260240161053f565b5061085f611594565b6108f4610f42565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316816001600160a01b03161415801561094457506007546001600160a01b03828116911614155b819061096f57604051637926036960e01b81526001600160a01b03909116600482015260240161053f565b506109798161173c565b600680546001810182555f919091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b038381169182179092556008546109d692165f19611778565b50565b6109e1610f42565b6001600160a01b0381165f818152600160208190526040808320805460ff1916909217909155517fd0cd09d977e63f0acaf30ce79e6479c90d7ea56a41a0849ac60f5440ac608a159190a250565b610a37610f42565b610a3f611807565b610676611830565b610a4f610f42565b5f5b600654811015610aa55760085460068054610a9d926001600160a01b0316915f9185908110610a8257610a8261204f565b5f918252602090912001546001600160a01b03169190611778565b600101610a51565b5061067660065f611f2f565b6004805461049190612000565b610ac6610f42565b6001600160a01b0381165f81815260016020526040808220805460ff19169055517f0c92d12d8037dd6d77aed8d12addd54d5eb2a6801541a1bf87c9822e78eea4219190a250565b336001600160a01b037f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa6168114610b645760405163589614ed60e01b81526001600160a01b03909116600482015260240161053f565b50610b6d610f6e565b6106765f6118e2565b610b7e610f42565b610b86610f6e565b5f7f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa66001600160a01b031663c031d2fb6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610be4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c089190612038565b905080156109d6576109d66113e4565b610c20610f42565b5f5b600654811015610c87575f60068281548110610c4057610c4061204f565b5f9182526020822001546008546001600160a01b039182169350610c6992849290911690611778565b610c7e6001600160a01b038216845f19611778565b50600101610c22565b50600854600754610ca5916001600160a01b0391821691165f611778565b600754610cbd906001600160a01b0316825f19611778565b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527fd8489678a8aa0cd1402b92f8985fe21057591f12330e197b86a0a5fa96e1c02a9060200161057a565b610d13610f42565b610d1b610f6e565b6109d6816118e2565b610d2c610f42565b6001600160a01b039091165f90815260096020526040902055565b610d4f610f42565b6001600160a01b038116610d7857604051631e4fbdf760e01b81525f600482015260240161053f565b6109d6816116ed565b60068181548110610d90575f80fd5b5f918252602090912001546001600160a01b0316905081565b600a546040516370a0823160e01b81523060048201525f9182916001600160a01b03909116906370a0823190602401602060405180830381865afa158015610df3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e179190612038565b600a546040516303d1689d60e11b8152600481018390529192505f916001600160a01b03909116906307a2d13a90602401602060405180830381865afa158015610e63573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e879190612038565b6040516370a0823160e01b81523060048201529091505f906001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889416906370a0823190602401602060405180830381865afa158015610eee573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f129190612038565b90505f610f1f8284612077565b90505f8111610f2e5780610f39565b610f3960018261208a565b94505050505090565b5f546001600160a01b031633146106765760405163118cdaa760e01b815233600482015260240161053f565b5f54600160a01b900460ff16156106765760405163d93c066560e01b815260040160405180910390fd5b801580610fac5750610fa861102c565b8111155b15610fb45750565b610fce610fbf61102c565b610fc9908361208a565b6119cc565b5f610fd761102c565b90505f610ff7600254612710610fed919061208a565b8490612710611a0a565b905080828181101561102557604051630a8f6c8d60e41b81526004810192909252602482015260440161053f565b5050505050565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316906370a0823190602401602060405180830381865afa158015611090573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047f9190612038565b5f8183106110c257816110c4565b825b90505b92915050565b6040516001600160a01b0383811660248301526044820183905261112c91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611aca565b505050565b611139611807565b5f805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600a546111c0906001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894811691165f19611778565b600854600754610676916001600160a01b0391821691165f19611778565b600b54604051633bd73ee360e21b81523060048201526001600160a01b039091169063ef5cfb8c906024015f604051808303815f87803b158015611220575f5ffd5b505af1158015611232573d5f5f3e3d5ffd5b50505050565b5f5b6006548110156109d6575f600682815481106112585761125861204f565b5f9182526020822001546040516370a0823160e01b81523060048201526001600160a01b03909116925082906370a0823190602401602060405180830381865afa1580156112a8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112cc9190612038565b6001600160a01b0383165f9081526009602052604090205490915081111561136e57600854600754604051630df791e560e41b81526001600160a01b03858116600483015291821660248201526044810184905291169063df791e50906064016020604051808303815f875af1158015611348573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061136c9190612038565b505b505060010161123a565b6007547f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0390811691161461067657600754610676906001600160a01b03167f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894611b2b565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316906370a0823190602401602060405180830381865afa158015611448573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061146c9190612038565b600a54604051636e553f6560e01b8152600481018390523060248201529192506001600160a01b031690636e553f65906044015b6020604051808303815f875af11580156114bc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114e09190612038565b5050565b600a5460405163ce96cb7760e01b81523060048201525f916001600160a01b03169063ce96cb7790602401602060405180830381865afa15801561152a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061154e9190612038565b905080156109d657600a54604051632d182be560e21b815260048101839052306024820181905260448201526001600160a01b039091169063b460af94906064016114a0565b600a546115ce906001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894811691165f611778565b600854600754610676916001600160a01b0391821691165f611778565b6115f3610f6e565b5f805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586111683390565b6040516361f488bf60e01b81523060048201525f90819081906001600160a01b037f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa616906361f488bf90602401602060405180830381865afa158015611695573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116b99190612038565b90505f808286106116d5576116ce838761208a565b91506116e2565b6116df868461208a565b90505b909590945092505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600a5481906001600160a01b038281169116036114e057604051637926036960e01b81526001600160a01b03909116600482015260240161053f565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526117c98482611c11565b611232576040516001600160a01b0384811660248301525f60448301526117fd91869182169063095ea7b3906064016110fa565b6112328482611aca565b5f54600160a01b900460ff1661067657604051638dfc202b60e01b815260040160405180910390fd5b5f61183961102c565b90505f5f6118468361162d565b90925090505f611856838561208a565b604051633d4f9ac360e11b81526004810182905260248101859052604481018490529091507f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa66001600160a01b031690637a9f3586906064015f604051808303815f87803b1580156118c6575f5ffd5b505af11580156118d8573d5f5f3e3d5ffd5b5050505050505050565b5f6118eb610da9565b90505f5f6118f88361162d565b915091505f6119078584611cb2565b905061191281610f98565b611925838361191f61102c565b84611db5565b90935091505f8361193461102c565b61193e919061208a565b604051633d4f9ac360e11b81526004810182905260248101869052604481018590529091507f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa66001600160a01b031690637a9f3586906064015f604051808303815f87803b1580156119ae575f5ffd5b505af11580156119c0573d5f5f3e3d5ffd5b50505050505050505050565b600a54604051632d182be560e21b815260048101839052306024820181905260448201526001600160a01b039091169063b460af94906064016114a0565b5f838302815f1985870982811083820303915050805f03611a3e57838281611a3457611a3461209d565b0492505050611ac3565b808411611a5e5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150505b9392505050565b5f611ade6001600160a01b03841683611e2d565b905080515f14158015611b02575080806020019051810190611b0091906120b1565b155b1561112c57604051635274afe760e01b81526001600160a01b038416600482015260240161053f565b6040516370a0823160e01b81523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa158015611b6f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b939190612038565b600854604051630df791e560e41b81526001600160a01b03868116600483015285811660248301526044820184905292935091169063df791e50906064016020604051808303815f875af1158015611bed573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112329190612038565b5f5f5f846001600160a01b031684604051611c2c91906120d0565b5f604051808303815f865af19150503d805f8114611c65576040519150601f19603f3d011682016040523d82523d5f602084013e611c6a565b606091505b5091509150818015611c94575080511580611c94575080806020019051810190611c9491906120b1565b8015611ca957505f856001600160a01b03163b115b95945050505050565b6040516374f9479b60e01b81523060048201525f9081906001600160a01b037f000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa616906374f9479b90602401602060405180830381865afa158015611d18573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d3c9190612038565b90505f81118015611d4c57505f84115b15611dad5761271060025403611d6e57611d668385612077565b9150506110c7565b5f611d8d612710600254612710611d85919061208a565b849190611a0a565b905083611d9a86836110b4565b611da49190612077565b925050506110c7565b509092915050565b5f5f5f848411611dc5575f611dcf565b611dcf858561208a565b9050805f03611de45786869250925050611e24565b86811115611e0057611df6878261208a565b90505f9650611e10565b611e0a818861208a565b96505f90505b611e1a8187612077565b9550868692509250505b94509492505050565b60606110c483835f845f5f856001600160a01b03168486604051611e5191906120d0565b5f6040518083038185875af1925050503d805f8114611e8b576040519150601f19603f3d011682016040523d82523d5f602084013e611e90565b606091505b5091509150611ea0868383611eaa565b9695505050505050565b606082611ebf57611eba82611f06565b611ac3565b8151158015611ed657506001600160a01b0384163b155b15611eff57604051639996b31560e01b81526001600160a01b038516600482015260240161053f565b5080611ac3565b805115611f165780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5080545f8255905f5260205f20908101906109d691905b80821115611f59575f8155600101611f46565b5090565b80356001600160a01b0381168114610659575f5ffd5b5f60208284031215611f83575f5ffd5b6110c482611f5d565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f60208284031215611fd1575f5ffd5b5035919050565b5f5f60408385031215611fe9575f5ffd5b611ff283611f5d565b946020939093013593505050565b600181811c9082168061201457607f821691505b60208210810361203257634e487b7160e01b5f52602260045260245ffd5b50919050565b5f60208284031215612048575f5ffd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b808201808211156110c7576110c7612063565b818103818111156110c7576110c7612063565b634e487b7160e01b5f52601260045260245ffd5b5f602082840312156120c1575f5ffd5b81518015158114611ac3575f5ffd5b5f82518060208501845e5f92019182525091905056fea2646970667358221220f9ea52c98d9d272e27a0d93397b7546c812b825873a3244fcd51a1e68c42ce2064736f6c634300081b0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa600000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388940000000000000000000000004e216c15697c1392fe59e1014b009505e05810df0000000000000000000000000dd368cd6d8869f2b21ba3cb4fd7ba107a2e3752000000000000000000000000108e823a26c5fb096d1f7c493809cce9015507a6000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad3800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000001253696c6f20532f555344432e652049442d380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000753494c4f2d563200000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _multistrategy (address): 0x901e3059Bf118AbC74d917440F0C08FC78eC0Aa6
Arg [1] : _asset (address): 0x29219dd400f2Bf60E5a23d13Be72B486D4038894
Arg [2] : _vault (address): 0x4E216C15697C1392fE59e1014B009505E05810Df
Arg [3] : _incentivesController (address): 0x0dd368Cd6D8869F2b21BA3Cb4fd7bA107a2e3752
Arg [4] : _harvestAddresses (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [5] : _name (string): Silo S/USDC.e ID-8
Arg [6] : _id (string): SILO-V2

-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 000000000000000000000000901e3059bf118abc74d917440f0c08fc78ec0aa6
Arg [1] : 00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894
Arg [2] : 0000000000000000000000004e216c15697c1392fe59e1014b009505e05810df
Arg [3] : 0000000000000000000000000dd368cd6d8869f2b21ba3cb4fd7ba107a2e3752
Arg [4] : 000000000000000000000000108e823a26c5fb096d1f7c493809cce9015507a6
Arg [5] : 000000000000000000000000039e2fb66102314ce7b64ce5ce3e5183bc94ad38
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [9] : 53696c6f20532f555344432e652049442d380000000000000000000000000000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [11] : 53494c4f2d563200000000000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.