S Price: $0.525445 (-11.98%)

Contract Diff Checker

Contract Name:
LPManagerHelper

Contract Source Code:

// SPDX-License-Identifier: BUSL-1.1
// (c) Long Gamma Labs, 2024.
pragma solidity ^0.8.28;


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


import { ErrorReporter } from "./ErrorReporter.sol";
import { ILPManager } from "./interfaces/ILPManager.sol";
import { IProxyLOB } from "./interfaces/IProxyLOB.sol";
import { TokenValueCalculator } from "./utils/TokenValueCalculator.sol";


contract LPManagerHelper {
    /// @notice Calculates the total USD value of all tokens in the liquidity pool.
    /// @return totalUSDValue The total USD value of the liquidity pool, normalized to 18 decimal places.
    function getTotalValue(address lpManagerAddress) external view returns (uint256 totalUSDValue) {
        ILPManager lpManager = ILPManager(lpManagerAddress);
        IProxyLOB proxyLOB = IProxyLOB(lpManagerAddress);
        uint256 tokensCount = lpManager.getTokensCount();
        
        for (uint8 i = 0; i < tokensCount; ++i) {
            (address tokenAddress,,,,, uint8 decimals,,) = lpManager.tokens(i);
            IERC20 token = IERC20(tokenAddress);

            uint256 currentTokenShares = token.balanceOf(lpManagerAddress) + proxyLOB.lobReservesByTokenId(i);
            (uint256 currentTokenPrice, int32 currentTokenPriceExpo) = proxyLOB.getPriceOf(i);

            uint256 currentTokenValue = TokenValueCalculator.calcNormalizedValue(
                currentTokenShares,
                decimals,
                currentTokenPrice,
                currentTokenPriceExpo
            );
            totalUSDValue += currentTokenValue;
        }
    }

    /// @notice Returns two arrays, one with the shares of each token, and the other with their USD value.
    /// @dev This function is primarily intended for off-chain analysis.
    /// @return shares An array with the shares of each token.
    /// @return usdValues An array with the USD values of each token.
    function getTokenSharesAndValues(address lpManagerAddress) external view returns (uint256[] memory shares, uint256[] memory usdValues) {
        ILPManager lpManager = ILPManager(lpManagerAddress);
        IProxyLOB proxyLOB = IProxyLOB(lpManagerAddress);

        uint256 tokenCount = lpManager.getTokensCount();
        shares = new uint256[](tokenCount);
        usdValues = new uint256[](tokenCount);

        for (uint8 i = 0; i < tokenCount; ++i) {
            (address tokenAddress,,,,, uint8 decimals,,) = lpManager.tokens(i);
            IERC20 token = IERC20(tokenAddress);
            uint256 currentTokenShares = token.balanceOf(lpManagerAddress) + proxyLOB.lobReservesByTokenId(i);
            shares[i] = currentTokenShares;
            (uint256 currentTokenPrice, int32 currentTokenPriceExpo) = proxyLOB.getPriceOf(i);

            usdValues[i] = TokenValueCalculator.calcNormalizedValue(
                currentTokenShares,
                decimals,
                currentTokenPrice,
                currentTokenPriceExpo
            );
        }
    }

    /// @notice Retrieves the total number of token shares, considering the balance on the contract and orders.
    /// @param tokenId The identifier of the token in the tokens array.
    /// @return The total number of token shares.
    function getTotalSharesOf(address lpManagerAddress, uint8 tokenId) external view returns (uint256) {
        ILPManager lpManager = ILPManager(lpManagerAddress);
        IProxyLOB proxyLOB = IProxyLOB(lpManagerAddress);
        require(tokenId < lpManager.getTokensCount(), ErrorReporter.WrongTokenId());

        (address tokenAddress,,,,,,,) = lpManager.tokens(tokenId);
        IERC20 token = IERC20(tokenAddress);
        uint256 tokensInContract = token.balanceOf(lpManagerAddress);
        uint256 tokensInOrders = proxyLOB.lobReservesByTokenId(tokenId);
        return (tokensInContract + tokensInOrders);
    }
}

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

// SPDX-License-Identifier: BUSL-1.1
// (c) Long Gamma Labs, 2024.
pragma solidity ^0.8.28;


contract ErrorReporter {
    error CooldownDurationNotYetPassed();
    // 0x5fba365d 

    error EmptyDomainName();
    // 0x2f601761 

    error EmptyTokenName();
    // 0xe2592aed 

    error EmptyTokenSymbol();
    // 0x19c7070a 

    error Expired();
    // 0x203d82d8 

    error FeeBpsExceedsMaximum();
    // 0x132df9c5 

    error Forbidden();
    // 0xee90c468 

    error InsufficientBalance();
    // 0xf4d678b8 

    error InsufficientFeeForPythUpdate();
    // 0xe4764c6f 

    error InsufficientLiquidityValue();
    // 0x5b635d0b 

    error InsufficientMarketMakerLPShare();
    // 0x28f53493 

    error InsufficientMintedLP();
    // 0x212c18d0 

    error InsufficientTokenAmount();
    // 0x2ec48042 

    error InsufficientUSDValue();
    // 0xd6f69157 

    error InvalidFloatingPointRepresentation();
    // 0xa25f85b7 

    error InvalidLob();
    // 0xb9c44f1a 

    error InvalidLobAddress();
    // 0xec09da35 

    error InvalidOracleConfidenceLevel();
    // 0xe6b9bbad 

    error InvalidSignature();
    // 0x8baa579f 

    error InvalidTokenWeights();
    // 0x4b8072c3 

    error InvalidTrader();
    // 0xfb7595a2 

    error InvalidTransfer();
    // 0x2f352531 

    error LobDisabled();
    // 0xa6876da4 

    error MarketMakerLPShareExceedsMaximum();
    // 0x84d3d6cb 

    error MaxLiquidityValueExceeded();
    // 0x9009a2d8 

    error MaxOracleAgeExceedsMaximum();
    // 0x8b7df994

    error NativeGasTokenDisabled();
    // 0x60787531 

    error NonPositivePrice();
    // 0x13caeeae 

    error NotImplementedYet();
    // 0xf88c75b4 

    error OracleConfTooHigh();
    // 0x004f2349 

    error OrderAlreadyUsed();
    // 0x88b39043 

    error PartiallyPaused();
    // 0x1fa6172a

    error PriceTooBig();
    // 0x9bec8e38 

    error PriceTooSmall();
    // 0x8460540d 

    error SlashingUnAvailable();
    // 0xd7b45887 

    error TokenDisabled();
    // 0x1931ea85 

    error TokenWeightExceeded();
    // 0x725ad4f5 

    error TransferFailed();
    // 0x90b8ec18 

    error UnknownLob();
    // 0x0b1066eb 

    error WrongNumber();
    // 0x3546a07e 

    error WrongTokenId();
    // 0x749aeece 

    error ZeroAddress();
    // 0xd92e233d 

    error ZeroAmount();
    // 0x1f2a2005 
}

// SPDX-License-Identifier: BUSL-1.1
// (c) Long Gamma Labs, 2024.
pragma solidity ^0.8.28;

struct FeeConfig {
    /// @notice Enable or disable dynamic fees adjustment based on token weight imbalances.
    bool dynamicFeesEnabled;
    /// @notice Admin fee for minting LP tokens (100 = 1%, maximum 1000 = 10%).
    uint16 adminMintLPFeeBps;
    /// @notice Admin fee for burning LP tokens (100 = 1%, maximum 1000 = 10%).
    uint16 adminBurnLPFeeBps;
    /// @notice Base protocol fee (100 = 1%, maximum 1000 = 10%).
    uint16 feeBasisBps;
    /// @notice Additional tax fee for imbalanced operations (100 = 1%, maximum 1000 = 10%).
    uint16 taxBasisBps;
    /// @notice Performance fee in basis points charged when LP token price reaches new high-water mark (100 = 1%, maximum 3000 = 30%).
    uint16 perfFeeBps;
    /// @notice Admin's share of the performance fee in basis points (100 = 1%, maximum 10000 = 100%).
    uint16 adminPerfFeeBps;
    /// @notice The address that will receive the admin fees.
    address adminFeeRecipient;
}

struct LiquidityConfig {
    /// @notice Lockup period after adding liquidity in seconds
    ///         during which removeLiquidity and LP token transfers are blocked
    ///         (maximum 16 777 215 seconds ≈ 194 days)
    uint24 cooldownDuration;
    /// @notice Minimum liquidity value in USD (scaled by 1e18).
    uint128 minLiquidityValueUsd;
    /// @notice Maximum liquidity value in USD (scaled by 1e18).
    uint128 maxLiquidityValueUsd;
}

struct MarketMakerConfig {
    /// @notice Enable or disable minimum market maker share in LP tokens.
    bool marketMakerLPShareEnabled;
    /// @notice Minimum market maker share in LP tokens (100 = 1%, maximum 10000 = 100%).
    uint16 marketMakerLPShareBps;
}

struct NativeTokenConfig {
    /// @notice Flag to enable or disable the use of the native GAS token for transactions.
    bool enabled;
    /// @notice The index of the token in the tokens array that is used as the native GAS token, if enabled.
    uint8 tokenId;
}

struct PriceConfig {
    /// @notice Maximum allowable age of oracle price data in seconds.
    uint16 maxOracleAge;
    /// @notice The period in seconds for which the LP token price is considered valid.
    uint24 priceValidityPeriod;
    /// @notice Base multiplier for calculating the allowed LP token price deviation (scaled by 1e18).
    uint64 baseMultiplier;
    /// @notice The maximum allowed deviation of the LP token price from the previous valid price.
    uint64 maxAllowedPriceDeviation;
}

interface ILPManager {
    // governance
    function setConfig(
        FeeConfig calldata feeConfig,
        LiquidityConfig calldata liquidityConfig,
        MarketMakerConfig calldata mmConfig,
        NativeTokenConfig calldata nativeConfig,
        PriceConfig calldata priceConfig
    ) external;

    function getConfig() external view returns (
        FeeConfig memory feeConfig,
        LiquidityConfig memory liquidityConfig,
        MarketMakerConfig memory mmConfig,
        NativeTokenConfig memory nativeConfig,
        PriceConfig memory priceConfig,
        bool slashingStatus
    );

    function changeToken(
        address tokenAddress,
        bool isActive,
        uint16 targetWeight,
        uint16 lowerBoundWeight,
        uint16 upperBoundWeight,
        uint8 decimals,
        uint24 oracleConfRel,
        bytes32 oraclePriceId
    ) external;

    function changeLob(
        address lobAddress,
        bool isActive,
        uint8 tokenIdX,
        uint8 tokenIdY,
        uint16 maxOrderDistanceBps
    ) external;

    function slashMakersShares(uint256 amount) external;
    function disableSlashingStatus() external;
    function pause() external;
    function validateLPPriceAndDistributeFees(bytes[] calldata priceUpdateData) payable external;

    // client entries
    function addLiquidity(
        uint8 tokenID,
        uint256 amount,
        uint256 minUsdValue,
        uint256 minLPMinted,
        uint256 expires,
        bytes[] calldata priceUpdateData
    ) external payable returns (uint256);

    function removeLiquidity(
        uint8 tokenID,
        uint256 burnLP,
        uint256 minUsdValue,
        uint256 minTokenGet,
        uint256 expires,
        bytes[] calldata priceUpdateData
    ) external payable returns (uint256);

    function collectFees() external;

    // views
    function getFeeBasisPoints(
        uint256 totalValue,
        uint256 initialTokenValue,
        uint256 nextTokenValue,
        uint16 targetTokenWeight
    ) external view returns (uint256);
    function tokens(uint256 index) external view returns (
        address tokenAddress,
        bool isActive,
        uint16 targetWeight,
        uint16 lowerBoundWeight,
        uint16 upperBoundWeight,
        uint8 decimals,
        uint24 oracleConfRel,
        bytes32 oraclePriceId
    );
    function lastAddedAt(address account) external view returns (uint256);
    function totalWeight() external view returns (uint24);
    function checkCooldown(address account) external view;
    function getTokensCount() external view returns (uint256);
    function marketMakers(address account) external view returns (bool);
    function primaryMarketMaker() external view returns (address);
    function validateMarketMakerLPShare() external view;
    function ensureNotPartiallyPaused() external view;
    function lobs(uint256 index) external view returns (
        address lobAddress,
        uint8 tokenIdX,
        uint8 tokenIdY,
        bool isActive,
        uint16 maxOrderDistanceBps
    );
}

// SPDX-License-Identifier: BUSL-1.1
// (c) Long Gamma Labs, 2024.
pragma solidity ^0.8.28;


interface IProxyLOB {
    function lobReservesByTokenId(uint8 tokenId) external view returns (uint256);
    function getPriceOf(uint8 tokenId) external view returns (uint256, int32);
    function placeOrder(
        uint8 lobId,
        bool isAsk,
        uint128 quantity,
        uint72 price,
        uint128 maxCommission,
        bool marketOnly,
        bool postOnly,
        uint256 expires,
        bytes[] calldata priceUpdateData
    ) external payable returns (uint64 orderId);
    function claimOrder(uint8 lobId, uint64 orderId, bool onlyClaim, uint256 expires) external;
}

// SPDX-License-Identifier: BUSL-1.1
// (c) Long Gamma Labs, 2024.
pragma solidity ^0.8.28;


library TokenValueCalculator {

    /// @notice Calculates normalized USD value of tokens with 18 decimals precision.
    /// @param tokenShares The amount of token shares.
    /// @param tokenDecimals The number of decimals the token uses.
    /// @param price The price of one token in USD, scaled by 10^priceExpo.
    /// @param priceExpo The exponent that the price is scaled by.
    /// @return The normalized USD value of the token shares, normalized to 18 decimal places.
    function calcNormalizedValue(
        uint256 tokenShares,
        uint8 tokenDecimals,
        uint256 price,
        int32 priceExpo
    ) internal pure returns (uint256) {
        int256 decimalAdjustment;
        unchecked {
            decimalAdjustment = int256(uint256(tokenDecimals)) - priceExpo - 18;
        }
        
        uint256 normalizedValue = tokenShares * price;
        if (decimalAdjustment > 0) {
            normalizedValue /= 10 ** uint256(decimalAdjustment);
        } else {
            normalizedValue *= 10 ** uint256(-decimalAdjustment);
        }

        return normalizedValue;
    }

    /// @notice Calculates the amount of token shares corresponding to a given USD value, taking into account 
    /// token decimals and price normalization. This is the inverse operation of calcNormalizedValue.
    /// @param usdValue The value in USD for which to calculate the corresponding token shares.
    /// @param tokenDecimals The number of decimals of the token for which shares are being calculated.
    /// @param price The price of the token in USD, adjusted by the price exponent.
    /// @param priceExpo The exponent to which the price is normalized (e.g., -18 for wei normalization).
    /// @return tokenAmount The calculated amount of token shares.
    function calcTokenShares(
        uint256 usdValue,
        uint8 tokenDecimals,
        uint256 price,
        int32 priceExpo
    ) internal pure returns (uint256) {
        int256 decimalAdjustment;
        unchecked {
            decimalAdjustment = int256(uint256(tokenDecimals)) - priceExpo - 18;
        }

        uint256 tokenAmount;
        if (decimalAdjustment > 0) {
            // There is no practical benefit to using mulDiv with real data here.
            tokenAmount = usdValue * 10 ** uint256(decimalAdjustment) / price;
        } else {
            tokenAmount = usdValue / (price * 10 ** uint256(-decimalAdjustment));
        }
        
        return tokenAmount;
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):