EOLPYAPFeed

Description:

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

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/EOLPYAPFeed.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import { MinimalAggregatorV3Interface } from "./interfaces/MinimalAggregatorV3Interface.sol";
import { EOBase } from "./EOBase.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { IEOLPYAPFeed } from "./interfaces/IEOLPYAPFeed.sol";
import { IBoostedPositionBase } from "./interfaces/maverick/IBoostedPositionBase.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IMaverickV2Pool } from "./interfaces/maverick/IMaverickV2Pool.sol";
import { InterfaceVerifier } from "./libraries/InterfaceVerifier.sol";

/**
 * @title EOLPYAPFeed
 * @author EO
 * @notice LPYAP feed oracle.
 */
contract EOLPYAPFeed is IEOLPYAPFeed, EOBase, Initializable {
    using InterfaceVerifier for address;

    // Declare as constant
    uint256 internal constant _ONE = 1e18;
    uint256 internal constant _ONE_D8 = 1e8;

    /**
     * @notice Index of the most significant bit (MSB) in a uint256 (0-based).
     * @dev Is used for shifting the MSB to the right.
     */
    uint256 internal constant _MSB_SHIFT = 255;

    /**
     * @notice The mask for the scale.
     * @dev ((1 << 255) - 1) - is reversed MSB, is used as a mask for the scale
     */
    uint256 internal constant _SCALE_MASK = (1 << _MSB_SHIFT) - 1;

    /**
     * @notice The decimals precision of the price feed.
     */
    // solhint-disable-next-line const-name-snakecase
    uint8 public constant decimals = 18;

    /**
     * @notice The address of the LP YAP
     */
    IBoostedPositionBase public lpYAP;

    /**
     * @notice The address of the pool
     */
    IMaverickV2Pool public pool;

    /**
     * @notice The description of the price feed.
     */
    string public description;

    /**
     * @notice The address of the token A feed  .
     */
    address public tokenAFeed;

    /**
     * @notice The address of the token B feed.
     */
    address public tokenBFeed;

    /**
     * @notice The maximum tick deviation in 8 decimals format
     */
    uint256 public maxTickDeviation;

    /**
     * @notice The decimals of the LP YAP.
     */
    uint8 internal _lpYAPDecimals;

    /**
     * @notice The scale of the token A.
     */
    uint256 internal _tokenAScale;
    /**
     * @notice The scale of the token B.
     */
    uint256 internal _tokenBScale;

    /**
     * @notice The decimals of the token A feed.
     */
    uint256 internal _decimalsFeedA;

    /**
     * @notice The decimals of the token B feed.
     */
    uint256 internal _decimalsFeedB;

    /**
     * @notice The gap for the upgradeable contract.
     */
    uint256[50] private _gap;

    /**
     * @custom:oz-upgrades-unsafe-allow constructor
     */
    constructor() {
        _disableInitializers();
    }

    /**
     * @notice Initialize the feed
     * @param lpYAPToken_ The address of the LP YAP token
     * @param tokenAFeed_ The address of the token A feed
     * @param tokenBFeed_ The address of the token B feed, should be the same denomination as tokenAFeed_
     * @param maxTickDeviation_ The maximum tick deviation in 8 decimals format
     * @param description_ The description of the feed
     */
    function initialize(
        address lpYAPToken_,
        address tokenAFeed_,
        address tokenBFeed_,
        uint256 maxTickDeviation_,
        string memory description_
    )
        external
        initializer
    {
        if (lpYAPToken_ == address(0)) revert LPYAPTokenAddressIsZero();
        if (tokenAFeed_ == address(0)) revert TokenAFeedAddressIsZero();
        if (tokenBFeed_ == address(0)) revert TokenBFeedAddressIsZero();
        if (maxTickDeviation_ == 0) revert MaxTickDeviationIsZero();

        tokenAFeed_.verifyMinimalAggregatorV3Interface();
        tokenBFeed_.verifyMinimalAggregatorV3Interface();

        lpYAP = IBoostedPositionBase(lpYAPToken_);
        pool = IMaverickV2Pool(lpYAP.pool());
        tokenAFeed = tokenAFeed_;
        tokenBFeed = tokenBFeed_;
        maxTickDeviation = maxTickDeviation_;
        description = description_;
        _lpYAPDecimals = lpYAP.decimals();
        _tokenAScale = pool.tokenAScale();
        _tokenBScale = pool.tokenBScale();
        _decimalsFeedA = MinimalAggregatorV3Interface(tokenAFeed_).decimals();
        _decimalsFeedB = MinimalAggregatorV3Interface(tokenBFeed_).decimals();
    }

    /**
     * @notice Returns the minimum updated at among the token A and token B feeds
     * @dev reflects the data freshness
     * @return The minimum updated at among the token A and token B feeds
     */
    function minUpdatedAt() external view override returns (uint256) {
        (,,, uint256 updatedAtA,) = MinimalAggregatorV3Interface(tokenAFeed).latestRoundData();
        (,,, uint256 updatedAtB,) = MinimalAggregatorV3Interface(tokenBFeed).latestRoundData();
        return updatedAtA < updatedAtB ? updatedAtA : updatedAtB;
    }

    /**
     * @notice Returns latest round data
     * @dev Returns zero for roundId, startedAt and answeredInRound.
     *
     * NAV-per-LP is calculated as  sum(external feed rates with the same denomination × current balances) /
     * totalSupply.
     * Before returning, a read-only sanity guard is applied: the pool’s spot tick is
     * compared to the pool’s TWAP tick in the native fractional-tick domain
     * (Maverick returns TWAP as tick * 1e8). If the absolute deviation exceeds a
     * configured threshold of N ticks (expressed as maxTickDeviation with 8 decimals),
     * latestRoundData() reverts.
     *
     * The original issue is single-block inventory manipulation
     * (pushing across bins to distort A/B balances and thus distort NAV).
     * TWAP is slow-moving and cannot be moved materially in one block;
     * therefore, a large spot-vs-TWAP tick gap is a reliable red flag.
     * By reverting during such dislocations, trust in the instantaneous inventory
     * is withheld, preventing publication of a manipulable quote.
     * The feed effectively "pauses" until spot and TWAP realign
     * (TWAP catches up or spot mean-reverts), then resumes normally.
     * This maximizes transparency and avoids masking anomalies with heuristics.
     *
     * @return roundId The round id, returns 0
     * @return answer The answer, returns the rate according to the equation
     *      lpYAPRate = sum(external feed rates × current balances) / totalSupply
     *      adjusted by decimals
     * @return startedAt The started at, returns 0
     * @return updatedAt The updated at, returns current block timestamp
     * @return answeredInRound The answered in round, returns 0
     */
    function latestRoundData() public view override returns (uint80, int256, uint256, uint256, uint80) {
        int256 rateFeedA = _getFeedRate(tokenAFeed);
        if (rateFeedA <= 0) revert RateFeedAIsZero();
        int256 rateFeedB = _getFeedRate(tokenBFeed);
        if (rateFeedB <= 0) revert RateFeedBIsZero();

        (uint256 totalA, uint256 totalB) = lpYAP.totalUnderlying();
        uint256 totalSupply = IERC20(address(lpYAP)).totalSupply();
        if (totalSupply == 0) revert TotalSupplyIsZero();

        uint256 adjA = _applyScale(totalA, _tokenAScale);
        uint256 adjB = _applyScale(totalB, _tokenBScale);

        // Spot NAV per LP (base)
        uint256 spotNavA = adjA * uint256(rateFeedA) / (10 ** _decimalsFeedA);
        uint256 spotNavB = adjB * uint256(rateFeedB) / (10 ** _decimalsFeedB);
        uint256 rate = (spotNavA + spotNavB) * 10 ** _lpYAPDecimals / totalSupply;

        // Guard: Tick deviation
        int256 activeTickD8 = pool.getState().activeTick * int256(_ONE_D8); // 8 decimals
        int256 twaTickD8 = pool.getCurrentTwa(); // 8 decimals

        if (_abs(activeTickD8 - twaTickD8) > maxTickDeviation) {
            revert TickDeviationExceeded();
        }

        return (0, int256(rate), 0, block.timestamp, 0);
    }

    /**
     * @notice Get the feed rate
     * @param feed The address of the feed
     * @return answer The rate of the feed
     */
    function _getFeedRate(address feed) internal view returns (int256) {
        (, int256 answer,,,) = MinimalAggregatorV3Interface(feed).latestRoundData();

        return answer;
    }

    /**
     * @notice Apply the scale to the amount
     * @param amount The amount to apply the scale to
     * @param scaleWithMSB The scale where most significant bit is a flag that indicates whether token has more
     * or less than 18 decimals
     * @return The amount adjusted by the scale
     */
    function _applyScale(uint256 amount, uint256 scaleWithMSB) internal pure returns (uint256) {
        // Extract the lower 255 bits of scaleWithMSB by applying the SCALE_MASK
        // This is the actual numeric scale value
        uint256 scale = scaleWithMSB & _SCALE_MASK;

        // Check if the most significant bit (bit 255) is set by shifting the scaleWithMSB 255 bits to the right
        // If it's 1, the token has MORE than 18 decimals, so we should DIVIDE.
        // If it's 0, the token has FEWER than 18 decimals, so we should MULTIPLY.
        bool hasMoreThan18Decimals = scaleWithMSB >> _MSB_SHIFT == 1;

        // Apply scaling:
        // - If the token has more than 18 decimals - downscale it to 18 decimals.
        // - If it has less - upscale it to 18 decimals.
        return hasMoreThan18Decimals ? amount / scale : amount * scale;
    }

    /**
     * @notice Returns the absolute value of a signed 256-bit integer.
     * @param x The integer to take the absolute value of.
     */
    function _abs(int256 x) internal pure returns (uint256) {
        unchecked {
            return uint256(x < 0 ? -x : x);
        }
    }
}
"
    },
    "src/interfaces/MinimalAggregatorV3Interface.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

/**
 * @title MinimalAggregatorV3Interface
 * @author EO
 * @notice Interface for price feed oracle
 */
interface MinimalAggregatorV3Interface {
    /**
     * @notice Returns feed decimals
     * @return The decimals of the feed.
     */
    function decimals() external view returns (uint8);

    /**
     * @notice Returns the latest round data
     * @return roundId The latest round ID, optional
     * @return answer The latest answer
     * @return startedAt The timestamp when the round started, optional
     * @return updatedAt The timestamp of the latest update
     * @return answeredInRound The round ID in which the answer was computed, optional
     */
    function latestRoundData()
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
"
    },
    "src/EOBase.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

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

/**
 * @title EOBase
 * @author EO
 * @notice Base contract for EO contracts
 */
abstract contract EOBase is AggregatorV2V3Interface {
    /**
     * @notice Returns the latest answer
     * @return The latest answer
     */
    function latestAnswer() external view returns (int256) {
        (, int256 answer,,,) = latestRoundData();
        return answer;
    }

    /**
     * @notice Returns the latest update timestamp
     * @return The latest update timestamp
     */
    function latestTimestamp() external view returns (uint256) {
        (,,, uint256 updatedAt,) = latestRoundData();
        return updatedAt;
    }

    /**
     * @notice Returns the latest round ID
     * @return The latest round ID
     */
    function latestRound() external view returns (uint256) {
        (uint80 roundId,,,,) = latestRoundData();
        return uint256(roundId);
    }

    /**
     * @notice Returns the answer of the latest round
     * @param roundId The round ID (ignored)
     * @return The answer of the latest round
     */
    function getAnswer(uint256 roundId) external view returns (int256) {
        (, int256 answer,,,) = getRoundData(uint80(roundId));
        return answer;
    }

    /**
     * @notice Returns the update timestamp of the latest round
     * @param roundId The round ID (ignored)
     * @return The update timestamp of the latest round
     */
    function getTimestamp(uint256 roundId) external view returns (uint256) {
        (,,, uint256 updatedAt,) = getRoundData(uint80(roundId));
        return updatedAt;
    }

    /**
     * @notice Returns the minimum updated at, default to updatedAt in latestRoundData
     * @dev This method is valuable for complex feeds, where other feeds rates are involved into computation
     * and since updatedAt in latestRoundData is the latest updatedAt among the feeds, it may not reflect the data
     * freshness, so this method shows the earliest updatedAt among the feeds, and reflects the data freshness
     * @return The minimum updated at among the feeds used to compute the final rate
     */
    function minUpdatedAt() external view virtual returns (uint256) {
        (,,, uint256 updatedAt,) = latestRoundData();
        return updatedAt;
    }

    /**
     * @notice Returns the latest round data
     * @return roundId The latest round ID, optional
     * @return answer The latest answer
     * @return startedAt The timestamp when the round started, optional
     * @return updatedAt The timestamp of the latest update
     * @return answeredInRound The round ID in which the answer was computed, optional
     */
    function latestRoundData()
        public
        view
        virtual
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    /**
     * @notice Returns the latest round data
     * @param roundId The round ID, is ignored
     * @return roundId The round ID of   the latest round data
     * @return answer The answer of the latest round data
     * @return startedAt The started at of the latest round data
     * @return updatedAt The updated at of the latest round data
     * @return answeredInRound The round ID in which the answer was computed of the latest round data
     */
    function getRoundData(uint80)
        public
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
    {
        return latestRoundData();
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reinitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
     *
     * NOTE: Consider following the ERC-7201 formula to derive storage locations.
     */
    function _initializableStorageSlot() internal pure virtual returns (bytes32) {
        return INITIALIZABLE_STORAGE;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        bytes32 slot = _initializableStorageSlot();
        assembly {
            $.slot := slot
        }
    }
}
"
    },
    "src/interfaces/IEOLPYAPFeed.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

/**
 * @title IEOLPYAPFeed
 * @author EO
 * @notice Interface for the EOLPYAPFeed contract.
 */
interface IEOLPYAPFeed {
    /**
     * @notice The error for the LP YAP token address is zero.
     */
    error LPYAPTokenAddressIsZero();

    /**
     * @notice The error for the token A feed address is zero.
     */
    error TokenAFeedAddressIsZero();

    /**
     * @notice The error for the token B feed address is zero.
     */
    error TokenBFeedAddressIsZero();

    /**
     * @notice The error for the maximum tick deviation is zero.
     */
    error MaxTickDeviationIsZero();

    /**
     * @notice The error for the total supply is zero.
     */
    error TotalSupplyIsZero();

    /**
     * @notice The error for the rate feed A is zero.
     */
    error RateFeedAIsZero();

    /**
     * @notice The error for the rate feed B is zero.
     */
    error RateFeedBIsZero();

    /**
     * @notice The error for the tick deviation exceeded.
     */
    error TickDeviationExceeded();

    function initialize(
        address lpYAPToken_,
        address tokenAFeed_,
        address tokenBFeed_,
        uint256 maxTickDeviation_,
        string memory description_
    )
        external;
}
"
    },
    "src/interfaces/maverick/IBoostedPositionBase.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface IBoostedPositionBase {
    /**
     * @notice The address of the pool.
     */
    function pool() external view returns (address);

    /**
     * @notice Total underlying pool.tokenA() and pool.tokenB() assets in the BP.
     */
    function totalUnderlying() external view returns (uint256 tokenAOut, uint256 tokenBOut);

    /**
     * @notice Decimal of the BP.
     */
    function decimals() external view returns (uint8);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "src/interfaces/maverick/IMaverickV2Pool.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// As the copyright holder of this work, Ubiquity Labs retains
// the right to distribute, use, and modify this code under any license of
// their choosing, in addition to the terms of the GPL-v2 or later.
pragma solidity 0.8.25;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IMaverickV2Pool {
    /**
     * @notice State of the pool.
     * @param reserveA Pool tokenA balanceOf at end of last operation
     * @param reserveB Pool tokenB balanceOf at end of last operation
     * @param lastTwaD8 Value of log time weighted average price at last block.
     * Value is 8-decimal scale and is in the fractional tick domain.  E.g. a
     * value of 12.3e8 indicates the TWAP was 3/10ths of the way into the 12th
     * tick.
     * @param lastLogPriceD8 Value of log price at last block. Value is
     * 8-decimal scale and is in the fractional tick domain.  E.g. a value of
     * 12.3e8 indicates the price was 3/10ths of the way into the 12th tick.
     * @param lastTimestamp Last block.timestamp value in seconds for latest
     * swap transaction.
     * @param activeTick Current tick position that contains the active bins.
     * @param isLocked Pool isLocked, E.g., locked or unlocked; isLocked values
     * defined in Pool.sol.
     * @param binCounter Index of the last bin created.
     * @param protocolFeeRatioD3 Ratio of the swap fee that is kept for the
     * protocol.
     */
    struct State {
        uint128 reserveA;
        uint128 reserveB;
        int64 lastTwaD8;
        int64 lastLogPriceD8;
        uint40 lastTimestamp;
        int32 activeTick;
        bool isLocked;
        uint32 binCounter;
        uint8 protocolFeeRatioD3;
    }

    /**
     * @notice Pool tokenA.  Address of tokenA is such that tokenA < tokenB.
     */
    function tokenA() external view returns (IERC20);

    /**
     * @notice Pool tokenB.
     */
    function tokenB() external view returns (IERC20);

    /**
     * @notice Most significant bit of scale value is a flag to indicate whether
     * tokenA has more or less than 18 decimals.  Scale is used in conjunction
     * with Math.toScale/Math.fromScale functions to convert from token amounts
     * to D18 scale internal pool accounting.
     */
    function tokenAScale() external view returns (uint256);

    /**
     * @notice Most significant bit of scale value is a flag to indicate whether
     * tokenA has more or less than 18 decimals.  Scale is used in conjunction
     * with Math.toScale/Math.fromScale functions to convert from token amounts
     * to D18 scale internal pool accounting.
     */
    function tokenBScale() external view returns (uint256);

    /**
     * @notice External function to get the state of the pool.
     */
    function getState() external view returns (State memory);

    /**
     * @notice External function to get the current time-weighted average price.
     */
    function getCurrentTwa() external view returns (int256);
}
"
    },
    "src/libraries/InterfaceVerifier.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import { MinimalAggregatorV3Interface } from "../interfaces/MinimalAggregatorV3Interface.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { IDecimals } from "../interfaces/IDecimals.sol";
import { ICustomAdapter } from "../interfaces/ICustomAdapter.sol";

/**
 * @title InterfaceVerifier
 * @author EO
 * @notice A library for verifying the interface of the feed.
 */
library InterfaceVerifier {
    /**
     * @notice The error emitted when the MinimalAggregatorV3Interface is not implemented.
     * @param feed The address of the feed.
     */
    error MinimalAggregatorV3InterfaceNotImplemented(address feed);

    /**
     * @notice The error emitted when the IERC4626Vault is not implemented.
     * @param feed The address of the feed.
     */
    error IERC4626VaultNotImplemented(address feed);

    /**
     * @notice The error emitted when the CustomAdapter interface is not implemented.
     * @param feed The address of the feed.
     * @param adapter The address of the adapter.
     */
    error CustomAdapterInterfaceNotImplemented(address feed, address adapter);

    /**
     * @notice The error emitted when the Decimals interface is not implemented.
     * @param addr The address of the feed.
     */
    error DecimalsNotImplemented(address addr);

    /**
     * @notice Verifies the MinimalAggregatorV3Interface.
     * @dev feed latestRoundData() AND feed decimals() must be implemented, reverts otherwise.
     * @param feed The address of the feed.
     */
    function verifyMinimalAggregatorV3Interface(address feed) internal view {
        try MinimalAggregatorV3Interface(feed).latestRoundData() returns (uint80, int256, uint256, uint256, uint80) {
            _verifyDecimals(feed);
        } catch {
            revert MinimalAggregatorV3InterfaceNotImplemented(feed);
        }
    }

    /**
     * @notice Verifies the IERC4626Vault.
     * @dev feed convertToAssets() AND feed decimals() AND feed asset decimals must be implemented, reverts otherwise.
     * @param feed The address of the feed.
     */
    function verifyIERC4626(address feed) internal view {
        try IERC4626(feed).convertToAssets(10 ** IERC4626(feed).decimals()) returns (uint256) {
            _verifyDecimals(IERC4626(feed).asset());
        } catch {
            revert IERC4626VaultNotImplemented(feed);
        }
    }

    /**
     * @notice Verifies the ICustomAdapter.
     * @dev feed getCustomRate() AND (plugin getDecimals() OR feed decimals()) must be implemented.
     * @param feed The address of the feed.
     * @param adapter The address of the adapter.
     */
    function verifyICustomAdapter(address feed, address adapter) internal view {
        try ICustomAdapter(adapter).getCustomRate(feed) returns (int256) {
            try ICustomAdapter(adapter).getDecimals(feed) returns (uint8) {
                return;
            } catch {
                _verifyDecimals(feed);
            }
        } catch {
            revert CustomAdapterInterfaceNotImplemented(feed, adapter);
        }
    }

    /**
     * @notice Verifies the IDecimals.
     * @dev decimals() must be implemented, reverts otherwise.
     * @param addr The address of the contract to verify the decimals of.
     */
    function verifyDecimals(address addr) internal view {
        _verifyDecimals(addr);
    }

    /**
     * @notice Internal function to verify the IDecimals.
     * @dev decimals() must be implemented, reverts otherwise.
     * @param addr The address of the contract to verify the decimals of.
     */
    function _verifyDecimals(address addr) private view {
        try IDecimals(addr).decimals() returns (uint8) {
            return;
        } catch {
            revert DecimalsNotImplemented(addr);
        }
    }
}
"
    },
    "src/interfaces/AggregatorV2V3Interface.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

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

/**
 * @title AggregatorV2V3Interface
 * @author EO
 * @notice Interface for the AggregatorV2V3 contract
 */
interface AggregatorV2V3Interface is MinimalAggregatorV3Interface {
    /**
     * @notice Returns the latest answer
     * @return The latest answer
     */
    function latestAnswer() external view returns (int256);

    /**
     * @notice Returns the latest update timestamp
     * @return The latest update timestamp
     */
    function latestTimestamp() external view returns (uint256);

    /**
     * @notice Returns the latest round ID
     * @return The latest round ID
     */
    function latestRound() external view returns (uint256);

    /**
     * @notice Returns the answer of the latest round
     * @param roundId The round ID (ignored)
     * @return The answer of the latest round
     */
    function getAnswer(uint256 roundId) external view returns (int256);

    /**
     * @notice Returns the update timestamp of the latest round
     * @param roundId The round ID (ignored)
     * @return The update timestamp of the latest round
     */
    function getTimestamp(uint256 roundId) external view returns (uint256);

    /**
     * @notice Returns the latest round data
     * @return roundId The latest round ID, optional
     * @return answer The latest answer
     * @return startedAt The timestamp when the round started, optional
     * @return updatedAt The timestamp of the latest update
     * @return answeredInRound The round ID in which the answer was computed, optional
     */
    function latestRoundData()
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    /**
     * @notice Returns the latest round data
     * @param roundId The round ID, is ignored
     * @return roundId The round ID of   the latest round data
     * @return answer The answer of the latest round data
     * @return startedAt The started at of the latest round data
     * @return updatedAt The updated at of the latest round data
     * @return answeredInRound The round ID in which the answer was computed of the latest round data
     */
    function getRoundData(uint80)
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
"
    },
    "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 ERC-4626 "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 redemption 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);
}
"
    },
    "src/interfaces/IDecimals.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

/**
 * @title IDecimals
 * @author EO
 * @notice Interface for the contract with decimals method.
 */
interface IDecimals {
    /**
     * @notice The function to get the decimals.
     * @return The decimals.
     */
    function decimals() external view returns (uint8);
}
"
    },
    "src/interfaces/ICustomAdapter.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

/**
 * @title ICustomAdapter
 * @author EO
 * @notice This interface is used to define the custom adapter.
 */
interface ICustomAdapter {
    /**
     * @notice This function returns the custom rate.
     * @param feedAddress The address of the asset/feed.
     * @return The custom rate.
     */
    function getCustomRate(address feedAddress) external view returns (int256);

    /**
     * @notice This function returns the decimals of the custom rate.
     * @param feedAddress The address of the asset/feed
     * @return The decimals of the custom rate
     */
    function getDecimals(address feedAddress) external view returns (uint8);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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 ERC-20 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);
}
"
    }
  },
  "settings": {
    "remappings": [
      "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
      "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
      "@openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
      "@maverick/v2-common/=lib/maverickprotocol-v2-common/",
      "forge-std/=lib/forge-std/src/",
      "@eoracle/=lib/target-contracts/src/",
      "@spectra-core/=lib/spectra-core/",
      "lib/spectra-core/:openzeppelin-contracts-upgradeable/=lib/spectra-core/lib/openzeppelin-contracts-upgradeable/contracts/",
      "lib/spectra-core/:openzeppelin-contracts/=lib/spectra-core/lib/openzeppelin-contracts/contracts/",
      "ds-test/=lib/spectra-core/lib/forge-std/lib/ds-test/src/",
      "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
      "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
      "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts/",
      "openzeppelin-erc20-basic/=lib/spectra-core/lib/openzeppelin-contracts/contracts/token/ERC20/",
      "openzeppelin-erc20-extensions/=lib/spectra-core/lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/",
      "openzeppelin-erc20/=lib/spectra-core/lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/",
      "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
      "openzeppelin-math/=lib/spectra-core/lib/openzeppelin-contracts/contracts/utils/math/",
      "openzeppelin-proxy/=lib/spectra-core/lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/",
      "openzeppelin-utils/=lib/spectra-core/lib/openzeppelin-contracts/contracts/utils/",
      "spectra-core/=lib/spectra-core/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 100
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "none",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "cancun",
    "viaIR": true
  }
}}

Tags:
ERC20, Proxy, Mintable, Swap, Yield, Upgradeable, Factory, Oracle|addr:0x7535f1715c1f91a1d3b961533192a75c029c4e2f|verified:true|block:23675954|tx:0xa09deac0d2b16ca02efcc3f8fd4e13fe9204b3503e47dce16fd05db16ea7409a|first_check:1761658511

Submitted on: 2025-10-28 14:35:13

Comments

Log in to comment.

No comments yet.