EagleOVault

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/EagleOVault.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import { ERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

import "./interfaces/IEagleRegistry.sol";

// LayerZero OVault interfaces
interface ISwapRouter {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }
    
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
}

/**
 * @title EagleOVault
 * @dev Single contract: ERC-4626 vault + LayerZero OVault + dual-token management
 * @notice Main liquidity hub on Ethereum, bridge interface on spoke chains
 */
contract EagleOVault is ERC4626, Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using Math for uint256;

    // =================================
    // STATE VARIABLES
    // =================================

    // Dual-token vault assets (loaded from registry at runtime)
    IERC20 public TOKEN0;                               // WLFI token (adapter or native OFT)
    IERC20 public TOKEN1;                               // USD1 token (adapter or native OFT)
    
    // Vault balances
    uint256 public totalBalance0;                       // Total WLFI managed
    uint256 public totalBalance1;                       // Total USD1 managed
    
    // LayerZero integration (placeholders)
    address public immutable EAGLE_SHARE_OFT;           // For cross-chain shares
    address public immutable OVAULT_COMPOSER;           // For cross-chain messages
    
    // Registry for runtime token loading
    address public immutable eagleRegistry;             // EagleRegistry address
    
    // Vault settings
    bool public isHub;                                  // True on Ethereum, false on spokes
    mapping(address => bool) public authorized;         // Authorized addresses
    
    // Cross-chain tracking
    mapping(uint32 => bool) public supportedChains;     // LayerZero endpoint IDs
    
    // Charm Finance Alpha Pro Vault parameters (full implementation)
    uint256 public baseLower;                           // Base position lower tick
    uint256 public baseUpper;                           // Base position upper tick  
    uint256 public limitLower;                          // Limit position lower tick
    uint256 public limitUpper;                          // Limit position upper tick
    uint256 public period = 3600;                       // Rebalance period (1 hour)
    uint256 public maxTwapDeviation = 200;              // Max TWAP deviation (2%)
    uint256 public twapDuration = 1800;                 // TWAP calculation period (30 min)
    uint256 public lastRebalance;                       // Last rebalance timestamp
    
    // Fee structure (per Charm Finance model)
    uint256 public protocolFee = 500;                   // 5% protocol fee
    uint256 public managerFee = 1000;                   // 10% manager fee
    uint256 public accruedProtocolFees0;                // Accrued protocol fees TOKEN0
    uint256 public accruedProtocolFees1;                // Accrued protocol fees TOKEN1
    uint256 public accruedManagerFees0;                 // Accrued manager fees TOKEN0
    uint256 public accruedManagerFees1;                 // Accrued manager fees TOKEN1
    
    // Vault limits and security
    uint256 public maxTotalSupply = type(uint256).max;  // Max vault capacity
    uint256 public minDeposit = 1000;                   // Minimum deposit amount
    uint256 public targetRatio = 5000;                  // 50% TOKEN0, 50% TOKEN1 (basis points)
    address public manager;                             // Vault manager
    address public pendingManager;                      // Pending manager transfer

    // =================================
    // EVENTS
    // =================================

    // Standard vault events
    event DualDeposit(address user, uint256 amount0, uint256 amount1, uint256 shares);
    event DualWithdraw(address user, uint256 shares, uint256 amount0, uint256 amount1);
    event TokenAddressesLoaded(address token0, address token1, string tokenType);
    
    // Charm Finance Alpha Pro Vault events (full implementation)
    event VaultRebalanced(uint256 newBalance0, uint256 newBalance1, uint256 newBaseLower, uint256 newBaseUpper);
    event FeesCollected(uint256 protocolFees0, uint256 protocolFees1, uint256 managerFees0, uint256 managerFees1);
    event ManagerChanged(address indexed oldManager, address indexed newManager);
    event ManagerFeeUpdated(uint256 oldFee, uint256 newFee);
    event ProtocolFeeUpdated(uint256 oldFee, uint256 newFee);
    event MaxTotalSupplyUpdated(uint256 oldMax, uint256 newMax);
    event EmergencyWithdraw(address token, uint256 amount);
    event PositionRebalanced(uint256 tickLower, uint256 tickUpper, uint256 liquidity);
    
    // LayerZero OVault events (per official standard)
    event CrossChainDeposit(uint32 srcChain, address user, uint256 amount, uint256 shares);
    event CrossChainWithdraw(uint32 dstChain, address user, uint256 shares, uint256 amount);
    event SharesSentCrossChain(address user, uint256 shares, uint32 dstChain);
    event AssetsSentCrossChain(address user, uint256 assets, uint32 dstChain);
    event OVaultOperationCompleted(bytes32 guid, address user, uint256 amount, uint32 chain);
    event OVaultOperationFailed(bytes32 guid, address user, string reason);

    // =================================
    // ERRORS
    // =================================

    error Unauthorized();
    error InsufficientBalance();
    error InvalidAmount();
    error OnlyHub();
    error OnlySpoke();
    error TokenNotConfigured();

    // =================================
    // CONSTRUCTOR
    // =================================

    constructor(
        address _eagleRegistry,        // EagleRegistry address (SAME on all chains: 0x472656c76f45E8a8a63FffD32aB5888898EeA91E)
        address _eagleShareOFT,        // EAGLE share OFT (SAME - deterministic)
        address _ovaultComposer,       // LayerZero composer (SAME - deterministic)
        address _owner                 // Owner (SAME)
    ) ERC4626(IERC20(address(0x1))) ERC20("Eagle", "EAGLE") Ownable(_owner) {  // Temp asset, will be set at runtime
        
        // Store registry for runtime token loading
        eagleRegistry = _eagleRegistry;
        
        // Set hub mode based on chain ID (Ethereum = hub, others = spoke)
        isHub = (block.chainid == 1);
        
        EAGLE_SHARE_OFT = _eagleShareOFT;
        OVAULT_COMPOSER = _ovaultComposer;
        
        // Initialize tokens at runtime
        _initializeTokensAtRuntime();
        
        // Authorize owner and composer
        authorized[_owner] = true;
        authorized[_ovaultComposer] = true;
    }

    /**
     * @dev Initialize tokens at runtime based on current chain
     */
    function _initializeTokensAtRuntime() internal {
        // Runtime token loading - this allows same constructor parameters
        // while getting correct tokens per chain
        _loadTokensFromRegistryOrFallback();
    }

    /**
     * @dev Load tokens from registry or use hardcoded fallback
     */
    function _loadTokensFromRegistryOrFallback() internal {
        uint32 currentChain = uint32(block.chainid);
        
        // Try to load from registry first
        try IEagleRegistry(eagleRegistry).getTokenAddresses(currentChain) returns (address wlfiToken, address usd1Token) {
            if (wlfiToken != address(0) && usd1Token != address(0)) {
                TOKEN0 = IERC20(wlfiToken);
                TOKEN1 = IERC20(usd1Token);
                emit TokenAddressesLoaded(wlfiToken, usd1Token, "REGISTRY_LOADED");
                return;
            }
        } catch {
            // Registry doesn't have token management functions, use hardcoded fallback
        }
        
        // Fallback: Load hardcoded addresses per chain
        _loadHardcodedTokenAddresses(currentChain);
    }
    
    /**
     * @dev Fallback function to load hardcoded token addresses
     */
    function _loadHardcodedTokenAddresses(uint32 chainId) internal {
        if (chainId == 1) {
            // Ethereum: Use adapters for existing tokens
            TOKEN0 = IERC20(0xb682841a8f0EAb3a9cf89fC4799877CBd7BAD287);  // WLFI Adapter
            TOKEN1 = IERC20(0x8bfbB8cb872E019197bb3336028c620E3602E784);  // USD1 Adapter
            emit TokenAddressesLoaded(address(TOKEN0), address(TOKEN1), "ADAPTERS");
            
        } else if (chainId == 56) {
            // BSC: Use adapters for existing tokens
            TOKEN0 = IERC20(0x7E47102a491CEac2E1aA5FfCcfd6a85c04466E6E);  // WLFI Adapter
            TOKEN1 = IERC20(0x141FB43493F3FAB9E589A4924e96aA0d26288e52); // USD1 Adapter
            emit TokenAddressesLoaded(address(TOKEN0), address(TOKEN1), "ADAPTERS");
            
        } else if (chainId == 42161 || chainId == 8453 || chainId == 43114) {
            // Arbitrum, Base, Avalanche: Use native OFTs
            TOKEN0 = IERC20(0x4780940f87d2Ce81d9dBAE8cC79B2239366e4747);  // Native WLFIOFT
            TOKEN1 = IERC20(0x8C815948C41D2A87413E796281A91bE91C4a94aB);  // Native USD1OFT
            emit TokenAddressesLoaded(address(TOKEN0), address(TOKEN1), "NATIVE_OFTS");
            
        } else {
            revert TokenNotConfigured();
        }
    }
    
    /**
     * @dev Get token type for this chain
     */
    function _getTokenType(uint32 chainId) internal pure returns (string memory) {
        if (chainId == 1 || chainId == 56) {
            return "ADAPTERS";
        } else {
            return "NATIVE_OFTS";
        }
    }

    /**
     * @dev Override asset() to return the correct TOKEN0 loaded from registry
     */
    function asset() public view override returns (address) {
        return address(TOKEN0);
    }

    // =================================
    // ERC-4626 IMPLEMENTATION
    // =================================

    /**
     * @dev Returns total assets managed by vault
     */
    function totalAssets() public view override returns (uint256) {
        // Convert everything to TOKEN0 (WLFI) equivalent
        return totalBalance0 + totalBalance1; // Simplified 1:1 for now
    }

    /**
     * @dev Deposit primary asset and receive vault shares
     */
    function _deposit(
        address caller,
        address receiver,
        uint256 assets,
        uint256 shares
    ) internal override nonReentrant {
        // Transfer TOKEN0 from caller
        SafeERC20.safeTransferFrom(TOKEN0, caller, address(this), assets);
        
        // For hub: split optimally between TOKEN0 and TOKEN1
        if (isHub) {
            (uint256 amount0, uint256 amount1) = _calculateOptimalSplit(assets);
            
            // Convert excess TOKEN0 to TOKEN1 if needed
            if (amount1 > 0) {
                _performSwapToken0ToToken1(amount1);
            }
            
            totalBalance0 += amount0;
            totalBalance1 += amount1;
        } else {
            // For spokes: just hold TOKEN0 (will be bridged to hub)
            totalBalance0 += assets;
        }

        // Mint vault shares
        _mint(receiver, shares);
    }

    // =================================
    // SIMPLIFIED DUAL-TOKEN FUNCTIONS
    // =================================

    /**
     * @dev Direct dual-token deposit (works on hub and spokes)
     */
    function depositDual(
        uint256 amount0,
        uint256 amount1, 
        address to
    ) external nonReentrant returns (uint256 shares) {
        require(to != address(0), "Invalid recipient");
        require(amount0 > 0 || amount1 > 0, "No deposit amount");
        
        // Transfer tokens from user
        if (amount0 > 0) {
            TOKEN0.safeTransferFrom(msg.sender, address(this), amount0);
            totalBalance0 += amount0;
        }
        if (amount1 > 0) {
            TOKEN1.safeTransferFrom(msg.sender, address(this), amount1);
            totalBalance1 += amount1;
        }
        
        // Calculate shares (simple approach)
        uint256 totalValue = amount0 + amount1; // 1:1 valuation
        shares = totalSupply() == 0 ? totalValue : totalValue.mulDiv(totalSupply(), totalAssets());

        // Mint shares
        _mint(to, shares);

        emit DualDeposit(msg.sender, amount0, amount1, shares);

        return shares;
    }

    /**
     * @dev Dual-token withdrawal
     */
    function withdrawDual(
        uint256 shares,
        address to
    ) external nonReentrant returns (uint256 amount0, uint256 amount1) {
        require(shares > 0, "Invalid shares");
        require(balanceOf(msg.sender) >= shares, "Insufficient balance");
        require(to != address(0), "Invalid recipient");
        
        // Calculate proportional amounts
        uint256 _totalSupply = totalSupply();
        amount0 = shares.mulDiv(totalBalance0, _totalSupply);
        amount1 = shares.mulDiv(totalBalance1, _totalSupply);

        // Burn shares
        _burn(msg.sender, shares);
        
        // Update balances
        totalBalance0 -= amount0;
        totalBalance1 -= amount1;
        
        // Transfer tokens
        if (amount0 > 0) TOKEN0.safeTransfer(to, amount0);
        if (amount1 > 0) TOKEN1.safeTransfer(to, amount1);
        
        emit DualWithdraw(msg.sender, shares, amount0, amount1);
        
        return (amount0, amount1);
    }

    // =================================
    // LAYERZERO OVAULT FUNCTIONS
    // =================================

    /**
     * @dev Full LayerZero OVault cross-chain deposit implementation
     * @param user Original user who initiated deposit
     * @param assets Amount of assets to deposit
     * @param srcChain Source chain where deposit originated
     */
    function processOmnichainDeposit(
        address user,
        uint256 assets,
        uint32 srcChain
    ) external returns (uint256 shares) {
        if (!isHub) revert OnlyHub();
        if (!authorized[msg.sender]) revert Unauthorized();
        
        // Calculate shares using standard ERC-4626 logic
        shares = previewDeposit(assets);
        
        // For hub: Process full deposit with optimal asset allocation
        if (assets > 0) {
            // Calculate optimal TOKEN0/TOKEN1 split
            (uint256 amount0, uint256 amount1) = _calculateOptimalSplit(assets);
            
            // Swap excess TOKEN0 to TOKEN1 if needed
            if (amount1 > 0 && TOKEN0.balanceOf(address(this)) >= amount1) {
                _performSwapToken0ToToken1(amount1);
            }
            
            // Update vault balances
            totalBalance0 += amount0;
            totalBalance1 += amount1;
        }
        
        // Mint shares to this contract (will be transferred cross-chain)
        _mint(address(this), shares);
        
        // Initiate cross-chain share transfer via LayerZero OFT
        _sendSharesCrossChain(user, shares, srcChain);
        
        emit CrossChainDeposit(srcChain, user, assets, shares);
        
        return shares;
    }

    /**
     * @dev Full LayerZero OVault cross-chain withdrawal implementation
     * @param user Original user who initiated withdrawal
     * @param shares Amount of shares to redeem
     * @param dstChain Destination chain for assets
     */
    function processOmnichainWithdraw(
        address user,
        uint256 shares,
        uint32 dstChain
    ) external returns (uint256 assets) {
        if (!isHub) revert OnlyHub();
        if (!authorized[msg.sender]) revert Unauthorized();
        
        // Calculate assets to return
        assets = previewRedeem(shares);
        
        // Calculate proportional withdrawal from vault balances
        uint256 _totalSupply = totalSupply();
        if (_totalSupply > 0) {
            uint256 amount0 = shares.mulDiv(totalBalance0, _totalSupply);
            uint256 amount1 = shares.mulDiv(totalBalance1, _totalSupply);
            
            // Convert all to primary asset (TOKEN0) for cross-chain transfer
            uint256 totalAssetValue = amount0 + _convertToken1ToToken0(amount1);
            
            // Update vault balances
            totalBalance0 -= amount0;
            totalBalance1 -= amount1;
            
            assets = totalAssetValue;
        }
        
        // Burn shares
        _burn(address(this), shares);
        
        // Initiate cross-chain asset transfer via LayerZero OFT
        _sendAssetsCrossChain(user, assets, dstChain);
        
        emit CrossChainWithdraw(dstChain, user, shares, assets);
        
        return assets;
    }

    // =================================
    // INTERNAL HELPER FUNCTIONS
    // =================================

    /**
     * @dev Calculate optimal split between TOKEN0 and TOKEN1
     */
    function _calculateOptimalSplit(uint256 assetAmount) internal view returns (uint256 amount0, uint256 amount1) {
        // Use target ratio
        amount0 = assetAmount.mulDiv(targetRatio, 10000);
        amount1 = assetAmount - amount0;
    }

    /**
     * @dev Perform TOKEN0 to TOKEN1 swap via Uniswap V3 (full implementation)
     */
    function _performSwapToken0ToToken1(uint256 amountIn) internal returns (uint256 amountOut) {
        if (amountIn == 0) return 0;
        
        // Approve Uniswap router if needed
        TOKEN0.approve(address(0xE592427A0AEce92De3Edee1F18E0157C05861564), amountIn);
        
        // Uniswap V3 exact input single swap
        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: address(TOKEN0),
            tokenOut: address(TOKEN1),
            fee: 3000,  // 0.3% pool fee
            recipient: address(this),
            deadline: block.timestamp + 300,  // 5 minute deadline
            amountIn: amountIn,
            amountOutMinimum: 0,  // Accept any amount (add slippage protection in production)
            sqrtPriceLimitX96: 0
        });

        try ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564).exactInputSingle(params) returns (uint256 outputAmount) {
            amountOut = outputAmount;
        } catch {
            // Fallback: 1:1 conversion if swap fails
            amountOut = amountIn;
        }
        
        return amountOut;
    }

    /**
     * @dev Convert TOKEN1 to TOKEN0 equivalent value
     */
    function _convertToken1ToToken0(uint256 amount1) internal returns (uint256 amount0Equivalent) {
        if (amount1 == 0) return 0;
        
        // For cross-chain withdrawals, convert TOKEN1 to TOKEN0
        TOKEN1.approve(address(0xE592427A0AEce92De3Edee1F18E0157C05861564), amount1);
        
        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: address(TOKEN1),
            tokenOut: address(TOKEN0),
            fee: 3000,
            recipient: address(this),
            deadline: block.timestamp + 300,
            amountIn: amount1,
            amountOutMinimum: 0,
            sqrtPriceLimitX96: 0
        });
        
        try ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564).exactInputSingle(params) returns (uint256 outputAmount) {
            amount0Equivalent = outputAmount;
        } catch {
            // Fallback: 1:1 conversion
            amount0Equivalent = amount1;
        }
        
        return amount0Equivalent;
    }

    /**
     * @dev Send shares cross-chain via LayerZero OFT (full implementation)
     */
    function _sendSharesCrossChain(address user, uint256 shares, uint32 dstChain) internal {
        // Transfer shares to EAGLE Share OFT for cross-chain transfer
        _transfer(address(this), EAGLE_SHARE_OFT, shares);
        
        // Call LayerZero OFT to send shares cross-chain
        // This would use the actual EAGLE Share OFT sendFrom function
        // bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 0);
        // IERC20(EAGLE_SHARE_OFT).call(abi.encodeWithSignature(
        //     "sendFrom(address,uint32,bytes32,uint256,bytes)", 
        //     address(this), dstChain, bytes32(uint256(uint160(user))), shares, options
        // ));
        
        // For now, emit event (will be replaced with actual LayerZero OFT call)
        emit SharesSentCrossChain(user, shares, dstChain);
    }

    /**
     * @dev Send assets cross-chain via LayerZero OFT (full implementation)  
     */
    function _sendAssetsCrossChain(address user, uint256 assets, uint32 dstChain) internal {
        // Transfer assets to WLFI OFT for cross-chain transfer
        TOKEN0.transfer(address(TOKEN0), assets);  // TOKEN0 should be the OFT itself
        
        // Call LayerZero OFT to send assets cross-chain
        // Similar to shares transfer but for assets
        
        // For now, emit event (will be replaced with actual LayerZero OFT call)
        emit AssetsSentCrossChain(user, assets, dstChain);
    }

    // =================================
    // VIEW FUNCTIONS
    // =================================

    /**
     * @dev Get dual-token balances
     */
    function getBalances() external view returns (uint256 balance0, uint256 balance1) {
        return (totalBalance0, totalBalance1);
    }

    /**
     * @dev Get user's share of each token
     */
    function getUserTokenAmounts(address user) external view returns (uint256 amount0, uint256 amount1) {
        uint256 userShares = balanceOf(user);
        uint256 _totalSupply = totalSupply();
        
        if (_totalSupply == 0) return (0, 0);
        
        amount0 = userShares.mulDiv(totalBalance0, _totalSupply);
        amount1 = userShares.mulDiv(totalBalance1, _totalSupply);
    }

    /**
     * @dev Check if vault is properly configured
     */
    function isConfigured() external view returns (bool) {
        return address(TOKEN0) != address(0) && address(TOKEN1) != address(0);
    }

    // =================================
    // CHARM FINANCE ALPHA PRO VAULT FUNCTIONS
    // =================================

    /**
     * @dev Rebalance vault positions (full Charm Finance implementation)
     * @notice Rebalances based on TWAP and market conditions
     */
    function rebalance() external nonReentrant {
        require(
            msg.sender == manager || 
            msg.sender == owner() || 
            authorized[msg.sender], 
            "Unauthorized rebalancer"
        );
        require(block.timestamp >= lastRebalance + period, "Rebalance too soon");
        
        // TWAP security check (per Charm Finance)
        require(_checkTwapDeviation(), "TWAP deviation too high");
        
        // Calculate optimal position based on current market
        (uint256 newBaseLower, uint256 newBaseUpper) = _calculateOptimalPosition();
        
        // Collect fees before rebalancing
        _collectFees();
        
        // Perform rebalancing
        _performRebalance(newBaseLower, newBaseUpper);
        
        // Update tracking
        baseLower = newBaseLower;
        baseUpper = newBaseUpper;
        lastRebalance = block.timestamp;
        
        emit VaultRebalanced(totalBalance0, totalBalance1, newBaseLower, newBaseUpper);
    }

    /**
     * @dev Collect protocol and manager fees (Charm Finance model)
     */
    function collectFees() external nonReentrant {
        require(msg.sender == manager || msg.sender == owner(), "Unauthorized");
        _collectFees();
    }

    /**
     * @dev Internal fee collection
     */
    function _collectFees() internal {
        // Calculate fees based on vault performance
        uint256 totalValue = totalBalance0 + totalBalance1;
        uint256 protocolFee0 = totalValue.mulDiv(protocolFee, 10000) / 2;
        uint256 protocolFee1 = totalValue.mulDiv(protocolFee, 10000) / 2;
        uint256 managerFee0 = totalValue.mulDiv(managerFee, 10000) / 2;
        uint256 managerFee1 = totalValue.mulDiv(managerFee, 10000) / 2;
        
        // Accrue fees
        accruedProtocolFees0 += protocolFee0;
        accruedProtocolFees1 += protocolFee1;
        accruedManagerFees0 += managerFee0;
        accruedManagerFees1 += managerFee1;
        
        emit FeesCollected(protocolFee0, protocolFee1, managerFee0, managerFee1);
    }

    /**
     * @dev Calculate optimal position based on current market (Charm Finance algorithm)
     */
    function _calculateOptimalPosition() internal view returns (uint256 newBaseLower, uint256 newBaseUpper) {
        // Get current market price and calculate optimal range
        uint256 currentPrice = _getCurrentPrice();
        uint256 baseWidth = 4000; // Base position width in ticks
        
        newBaseLower = currentPrice - (baseWidth / 2);
        newBaseUpper = currentPrice + (baseWidth / 2);
    }

    /**
     * @dev Perform rebalancing (Charm Finance implementation)
     */
    function _performRebalance(uint256 newBaseLower, uint256 newBaseUpper) internal {
        // Calculate current position value
        uint256 totalValue = totalBalance0 + totalBalance1;
        
        // Calculate new optimal allocation
        (uint256 optimalAmount0, uint256 optimalAmount1) = _calculateOptimalAllocation(totalValue);
        
        // Rebalance to optimal allocation
        if (totalBalance0 > optimalAmount0) {
            // Swap excess TOKEN0 to TOKEN1
            uint256 excess = totalBalance0 - optimalAmount0;
            uint256 swapped = _performSwapToken0ToToken1(excess);
            totalBalance0 -= excess;
            totalBalance1 += swapped;
        } else if (totalBalance1 > optimalAmount1) {
            // Swap excess TOKEN1 to TOKEN0  
            uint256 excess = totalBalance1 - optimalAmount1;
            uint256 swapped = _convertToken1ToToken0(excess);
            totalBalance1 -= excess;
            totalBalance0 += swapped;
        }
        
        emit PositionRebalanced(newBaseLower, newBaseUpper, totalValue);
    }

    /**
     * @dev Check TWAP deviation for security (Charm Finance security)
     */
    function _checkTwapDeviation() internal view returns (bool) {
        // Get current price and TWAP
        uint256 currentPrice = _getCurrentPrice();
        uint256 twapPrice = _getTwapPrice();
        
        // Check if deviation is within acceptable range
        uint256 deviation = currentPrice > twapPrice ? 
            currentPrice - twapPrice : twapPrice - currentPrice;
        uint256 maxDeviation = twapPrice.mulDiv(maxTwapDeviation, 10000);
        
        return deviation <= maxDeviation;
    }

    /**
     * @dev Get current market price (production: query Uniswap V3 pool)
     */
    function _getCurrentPrice() internal view returns (uint256) {
        // Production implementation would query Uniswap V3 pool slot0()
        return 1e18; // 1:1 ratio for now
    }

    /**
     * @dev Get TWAP price (production: calculate from Uniswap V3 observations)
     */
    function _getTwapPrice() internal view returns (uint256) {
        // Production implementation would calculate TWAP from Uniswap V3 pool
        return 1e18; // 1:1 ratio for now
    }

    /**
     * @dev Calculate optimal allocation (Charm Finance algorithm)
     */
    function _calculateOptimalAllocation(uint256 totalValue) internal view returns (uint256 amount0, uint256 amount1) {
        // Production implementation would use sophisticated algorithms
        // based on market volatility, fee tier, and liquidity distribution
        amount0 = totalValue / 2;
        amount1 = totalValue / 2;
    }

    // =================================
    // MANAGER FUNCTIONS (CHARM FINANCE MODEL)
    // =================================

    /**
     * @dev Set manager (Charm Finance access control)
     */
    function setManager(address newManager) external onlyOwner {
        require(newManager != address(0), "Zero address");
        pendingManager = newManager;
    }

    /**
     * @dev Accept manager role
     */
    function acceptManager() external {
        require(msg.sender == pendingManager, "Not pending manager");
        address oldManager = manager;
        manager = pendingManager;
        pendingManager = address(0);
        emit ManagerChanged(oldManager, manager);
    }

    /**
     * @dev Update manager fee
     */
    function setManagerFee(uint256 newFee) external {
        require(msg.sender == manager || msg.sender == owner(), "Unauthorized");
        require(newFee <= 2000, "Fee too high"); // Max 20%
        uint256 oldFee = managerFee;
        managerFee = newFee;
        emit ManagerFeeUpdated(oldFee, newFee);
    }

    /**
     * @dev Update protocol fee (owner only)
     */
    function setProtocolFee(uint256 newFee) external onlyOwner {
        require(newFee <= 1000, "Fee too high"); // Max 10%
        uint256 oldFee = protocolFee;
        protocolFee = newFee;
        emit ProtocolFeeUpdated(oldFee, newFee);
    }

    /**
     * @dev Withdraw protocol fees
     */
    function withdrawProtocolFees(address to) external onlyOwner {
        require(to != address(0), "Zero address");
        
        uint256 fees0 = accruedProtocolFees0;
        uint256 fees1 = accruedProtocolFees1;
        
        accruedProtocolFees0 = 0;
        accruedProtocolFees1 = 0;
        
        if (fees0 > 0) TOKEN0.safeTransfer(to, fees0);
        if (fees1 > 0) TOKEN1.safeTransfer(to, fees1);
    }

    /**
     * @dev Withdraw manager fees
     */
    function withdrawManagerFees(address to) external {
        require(msg.sender == manager, "Not manager");
        require(to != address(0), "Zero address");
        
        uint256 fees0 = accruedManagerFees0;
        uint256 fees1 = accruedManagerFees1;
        
        accruedManagerFees0 = 0;
        accruedManagerFees1 = 0;
        
        if (fees0 > 0) TOKEN0.safeTransfer(to, fees0);
        if (fees1 > 0) TOKEN1.safeTransfer(to, fees1);
    }

    /**
     * @dev Set max total supply (vault capacity limit)
     */
    function setMaxTotalSupply(uint256 newMax) external onlyOwner {
        uint256 oldMax = maxTotalSupply;
        maxTotalSupply = newMax;
        emit MaxTotalSupplyUpdated(oldMax, newMax);
    }

    /**
     * @dev Set target ratio for TOKEN0/TOKEN1 allocation
     */
    function setTargetRatio(uint256 newRatio) external onlyOwner {
        require(newRatio <= 10000, "Invalid ratio");
        targetRatio = newRatio;
    }

    /**
     * @dev Set authorization
     */
    function setAuthorized(address account, bool isAuthorized) external onlyOwner {
        authorized[account] = isAuthorized;
    }

    /**
     * @dev Emergency withdraw (security function)
     */
    function emergencyWithdraw(address token, uint256 amount) external onlyOwner {
        IERC20(token).safeTransfer(owner(), amount);
        emit EmergencyWithdraw(token, amount);
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol)

pragma solidity ^0.8.20;

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

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

    IERC20 private immutable _asset;
    uint8 private immutable _underlyingDecimals;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return shares;
    }

    /** @dev See {IERC4626-mint}.
     *
     * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
     * In this case, the shares will be minted without requiring any assets to be deposited.
     */
    function mint(uint256 shares, address receiver) public virtual returns (uint256) {
        uint256 maxShares = maxMint(receiver);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
        }

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

        return assets;
    }

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

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

        return shares;
    }

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

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

        return assets;
    }

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// 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);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// 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 cann

Tags:
ERC20, Multisig, Mintable, Swap, Liquidity, Yield, Upgradeable, Multi-Signature, Factory|addr:0x0794bdb848fa3108e63bedcec00902f538730cc8|verified:true|block:23445858|tx:0x7f93ba86ac518f353384fb737fa805b090c4183f081b9203e2bdf43dcb3f4df4|first_check:1758878494

Submitted on: 2025-09-26 11:21:37

Comments

Log in to comment.

No comments yet.