{"file_path":"src/AlchemistV3.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nimport \"./interfaces/IAlchemistV3.sol\";\nimport {ITransmuter} from \"./interfaces/ITransmuter.sol\";\nimport {IAlchemistV3Position} from \"./interfaces/IAlchemistV3Position.sol\";\nimport {IFeeVault} from \"./interfaces/IFeeVault.sol\";\nimport {TokenUtils} from \"./libraries/TokenUtils.sol\";\nimport {Initializable} from \"../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol\";\nimport {IERC721} from \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport {Unauthorized, IllegalArgument, IllegalState, MissingInputData} from \"./base/Errors.sol\";\nimport {IERC20} from \"../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\";\nimport {IVaultV2} from \"../lib/vault-v2/src/interfaces/IVaultV2.sol\";\nimport {FixedPointMath} from \"./libraries/FixedPointMath.sol\";\n\n/// @title  AlchemistV3\n/// @author Alchemix Finance\n///\n/// For Juris, Graham, and Marcus\ncontract AlchemistV3 is IAlchemistV3, Initializable {\n    uint256 public constant BPS = 10_000;\n    uint256 public constant FIXED_POINT_SCALAR = 1e18;\n\n    uint256 public constant ONE_Q128 = uint256(1) << 128;\n\n    /// @inheritdoc IAlchemistV3Immutables\n    string public constant version = \"3.0.0\";\n\n    /// @inheritdoc IAlchemistV3State\n    address public admin;\n\n    /// @inheritdoc IAlchemistV3State\n    address public alchemistFeeVault;\n\n    /// @inheritdoc IAlchemistV3Immutables\n    address public debtToken;\n\n    /// @inheritdoc IAlchemistV3State\n    address public myt;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public underlyingConversionFactor;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public cumulativeEarmarked;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public depositCap;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public lastEarmarkBlock;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public lastRedemptionBlock;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public lastTransmuterTokenBalance;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public minimumCollateralization;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public collateralizationLowerBound;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public globalMinimumCollateralization;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public liquidationTargetCollateralization;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public totalDebt;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public totalSyntheticsIssued;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public protocolFee;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public liquidatorFee;\n\n    /// @inheritdoc IAlchemistV3State\n    uint256 public repaymentFee;\n\n    /// @inheritdoc IAlchemistV3State\n    address public alchemistPositionNFT;\n\n    /// @inheritdoc IAlchemistV3State\n    address public protocolFeeReceiver;\n\n    /// @inheritdoc IAlchemistV3State\n    address public underlyingToken;\n\n    /// @inheritdoc IAlchemistV3State\n    address public tokenAdapter;\n\n    /// @inheritdoc IAlchemistV3State\n    address public transmuter;\n\n    /// @inheritdoc IAlchemistV3State\n    address public pendingAdmin;\n\n    /// @inheritdoc IAlchemistV3State\n    bool public depositsPaused;\n\n    /// @inheritdoc IAlchemistV3State\n    bool public loansPaused;\n\n    /// @inheritdoc IAlchemistV3State\n    mapping(address => bool) public guardians;\n\n    /// @dev Total debt retired through Transmuter-driven redemptions.\n    uint256 private _totalRedeemedDebt;\n\n    /// @dev Total MYT shares removed from global accounting by redemptions and redemption fees.\n    uint256 private _totalRedeemedSharesOut;\n\n    /// @dev Packed earmark weight used to track how much live unearmarked debt survives each earmark step.\n    uint256 private _earmarkWeight;\n\n    /// @dev Packed redemption weight used to track survival of earmarked debt across redemptions.\n    uint256 private _redemptionWeight;\n\n    /// @dev Accumulator used to reconstruct earmarked debt survival across redemption windows.\n    uint256 private _survivalAccumulator;\n\n    /// @dev Total MYT shares currently assigned to open positions.\n    /// This intentionally excludes any unrelated MYT balance the contract may temporarily hold.\n    uint256 private _mytSharesDeposited;\n\n    /// @dev Transmuter MYT balance increases not yet applied as cover in `_earmark()`.\n    uint256 private _pendingCoverShares;\n\n    /// @dev User accounts\n    mapping(uint256 => Account) private _accounts;\n\n    /// @dev Redemption weight snapshot at the start of each earmark epoch.\n    mapping(uint256 => uint256) private _earmarkEpochStartRedemptionWeight;\n\n    /// @dev Survival accumulator snapshot at the start of each earmark epoch.\n    mapping(uint256 => uint256) private _earmarkEpochStartSurvivalAccumulator;\n    \n    uint256 private constant _REDEMPTION_INDEX_BITS = 129;\n    uint256 private constant _REDEMPTION_INDEX_MASK = (uint256(1) << _REDEMPTION_INDEX_BITS) - 1;\n\n    uint256 private constant _EARMARK_INDEX_BITS = 129;\n    uint256 private constant _EARMARK_INDEX_MASK = (uint256(1) << _EARMARK_INDEX_BITS) - 1;\n\n    modifier onlyAdmin() {\n        _onlyAdmin();\n        _;\n    }\n\n    modifier onlyAdminOrGuardian() {\n        _onlyAdminOrGuardian();\n        _;\n    }\n\n    modifier onlyTransmuter() {\n        _onlyTransmuter();\n        _;\n    }\n\n    constructor() initializer {}\n\n    function initialize(AlchemistInitializationParams calldata params) external initializer {\n        _checkArgument(params.protocolFee <= BPS);\n        _checkArgument(params.liquidatorFee <= BPS);\n        _checkArgument(params.repaymentFee <= BPS);\n        _checkArgument(params.liquidationTargetCollateralization >= params.minimumCollateralization);\n        \n        debtToken = params.debtToken;\n        underlyingToken = params.underlyingToken;\n        underlyingConversionFactor = 10 ** (TokenUtils.expectDecimals(params.debtToken) - TokenUtils.expectDecimals(params.underlyingToken));\n        depositCap = params.depositCap;\n        minimumCollateralization = params.minimumCollateralization;\n        globalMinimumCollateralization = params.globalMinimumCollateralization;\n        collateralizationLowerBound = params.collateralizationLowerBound;\n        liquidationTargetCollateralization = params.liquidationTargetCollateralization;\n        admin = params.admin;\n        transmuter = params.transmuter;\n        protocolFee = params.protocolFee;\n        protocolFeeReceiver = params.protocolFeeReceiver;\n        liquidatorFee = params.liquidatorFee;\n        repaymentFee = params.repaymentFee;\n        lastEarmarkBlock = block.number;\n        lastRedemptionBlock = block.number;\n        myt = params.myt;\n\n        // Initialize packed weights at full survival.\n        _redemptionWeight = ONE_Q128;\n        _earmarkWeight = ONE_Q128;\n\n        // Initialize epoch checkpoints.\n        _earmarkEpochStartRedemptionWeight[0] = _redemptionWeight;\n        _earmarkEpochStartSurvivalAccumulator[0] = _survivalAccumulator;\n    }\n\n    /// @notice Sets the NFT position token, callable by admin.\n    function setAlchemistPositionNFT(address nft) external onlyAdmin {\n        if (nft == address(0)) {\n            revert AlchemistV3NFTZeroAddressError();\n        }\n\n        if (alchemistPositionNFT != address(0)) {\n            revert AlchemistV3NFTAlreadySetError();\n        }\n\n        alchemistPositionNFT = nft;\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setAlchemistFeeVault(address value) external onlyAdmin {\n        if (IFeeVault(value).token() != underlyingToken) {\n            revert AlchemistVaultTokenMismatchError();\n        }\n        alchemistFeeVault = value;\n        emit AlchemistFeeVaultUpdated(value);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setPendingAdmin(address value) external onlyAdmin {\n        pendingAdmin = value;\n\n        emit PendingAdminUpdated(value);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function acceptAdmin() external {\n        _checkState(pendingAdmin != address(0));\n\n        if (msg.sender != pendingAdmin) {\n            revert Unauthorized();\n        }\n\n        admin = pendingAdmin;\n        pendingAdmin = address(0);\n\n        emit AdminUpdated(admin);\n        emit PendingAdminUpdated(address(0));\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setDepositCap(uint256 value) external onlyAdmin {\n        _checkArgument(value >= IERC20(myt).balanceOf(address(this)));\n\n        depositCap = value;\n        emit DepositCapUpdated(value);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setProtocolFeeReceiver(address value) external onlyAdmin {\n        _checkArgument(value != address(0));\n\n        protocolFeeReceiver = value;\n        emit ProtocolFeeReceiverUpdated(value);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setProtocolFee(uint256 fee) external onlyAdmin {\n        _checkArgument(fee <= BPS);\n\n        protocolFee = fee;\n        emit ProtocolFeeUpdated(fee);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setLiquidatorFee(uint256 fee) external onlyAdmin {\n        _checkArgument(fee <= BPS);\n\n        liquidatorFee = fee;\n        emit LiquidatorFeeUpdated(fee);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setRepaymentFee(uint256 fee) external onlyAdmin {\n        _checkArgument(fee <= BPS);\n\n        repaymentFee = fee;\n        emit RepaymentFeeUpdated(fee);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setTokenAdapter(address value) external onlyAdmin {\n        _checkArgument(value != address(0));\n\n        tokenAdapter = value;\n        emit TokenAdapterUpdated(value);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setGuardian(address guardian, bool isActive) external onlyAdmin {\n        _checkArgument(guardian != address(0));\n\n        guardians[guardian] = isActive;\n        emit GuardianSet(guardian, isActive);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setMinimumCollateralization(uint256 value) external onlyAdmin {\n        _checkArgument(value >= FIXED_POINT_SCALAR);\n\n        // cannot exceed global minimum\n        minimumCollateralization = value > globalMinimumCollateralization ? globalMinimumCollateralization : value;\n\n        // cannot exceed liquidation target\n        if (minimumCollateralization > liquidationTargetCollateralization) {\n            minimumCollateralization = liquidationTargetCollateralization;\n        }\n        emit MinimumCollateralizationUpdated(minimumCollateralization);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setGlobalMinimumCollateralization(uint256 value) external onlyAdmin {\n        _checkArgument(value >= minimumCollateralization);\n        globalMinimumCollateralization = value;\n        emit GlobalMinimumCollateralizationUpdated(value);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setCollateralizationLowerBound(uint256 value) external onlyAdmin {\n        _checkArgument(value < minimumCollateralization);\n        _checkArgument(value >= FIXED_POINT_SCALAR);\n        collateralizationLowerBound = value;\n        emit CollateralizationLowerBoundUpdated(value);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function setLiquidationTargetCollateralization(uint256 value) external onlyAdmin {\n        _checkArgument(value > FIXED_POINT_SCALAR);\n        _checkArgument(value >= minimumCollateralization);\n        _checkArgument(value > collateralizationLowerBound);\n        _checkArgument(value <= 2 * FIXED_POINT_SCALAR);\n        liquidationTargetCollateralization = value;\n        emit LiquidationTargetCollateralizationUpdated(value);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function pauseDeposits(bool isPaused) external onlyAdminOrGuardian {\n        depositsPaused = isPaused;\n        emit DepositsPaused(isPaused);\n    }\n\n    /// @inheritdoc IAlchemistV3AdminActions\n    function pauseLoans(bool isPaused) external onlyAdminOrGuardian {\n        loansPaused = isPaused;\n        emit LoansPaused(isPaused);\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function getCDP(uint256 tokenId) external view returns (uint256, uint256, uint256) {\n        (uint256 debt, uint256 earmarked, uint256 collateral) = _calculateUnrealizedDebt(tokenId);\n        return (collateral, debt, earmarked);\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function getTotalDeposited() external view returns (uint256) {\n        return _mytSharesDeposited;\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function getMaxBorrowable(uint256 tokenId) external view returns (uint256) {\n        (uint256 debt,, uint256 collateral) = _calculateUnrealizedDebt(tokenId);\n        uint256 debtValueOfCollateral = convertYieldTokensToDebt(collateral);\n        uint256 capacity = (debtValueOfCollateral * FIXED_POINT_SCALAR / minimumCollateralization);\n        return debt > capacity  ? 0 : capacity - debt;\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function getMaxWithdrawable(uint256 tokenId) external view returns (uint256) {\n        (uint256 debt,, uint256 collateral) = _calculateUnrealizedDebt(tokenId);\n\n        uint256 lockedCollateral = 0;\n        if (debt != 0) {\n            uint256 debtShares = convertDebtTokensToYield(debt);\n            lockedCollateral = FixedPointMath.mulDivUp(debtShares, minimumCollateralization, FIXED_POINT_SCALAR);\n        }\n\n        uint256 positionFree = collateral > lockedCollateral ? collateral - lockedCollateral : 0;\n        uint256 required = _requiredLockedShares();\n        uint256 globalFree = _mytSharesDeposited > required ? _mytSharesDeposited - required : 0;\n\n        return positionFree < globalFree ? positionFree : globalFree;\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function mintAllowance(uint256 ownerTokenId, address spender) external view returns (uint256) {\n        Account storage account = _accounts[ownerTokenId];\n        return account.mintAllowances[account.allowancesVersion][spender];\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function getTotalUnderlyingValue() external view returns (uint256) {\n        return _getTotalUnderlyingValue();\n    }\n\n    \n    /// @inheritdoc IAlchemistV3State\n    function getTotalLockedUnderlyingValue() external view returns (uint256) {\n        return _getTotalLockedUnderlyingValue();\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function totalValue(uint256 tokenId) public view returns (uint256) {\n        return _totalCollateralValue(tokenId, true);\n    }\n\n    /// @notice Returns cumulative earmarked debt including one simulated pending earmark window.\n    function getUnrealizedCumulativeEarmarked() external view returns (uint256) {\n        if (totalDebt == 0) return 0;\n        (, uint256 effectiveEarmarked) = _simulateUnrealizedEarmark();\n        return cumulativeEarmarked + effectiveEarmarked;\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function deposit(uint256 amount, address recipient, uint256 tokenId) external returns (uint256, uint256) {\n        _checkArgument(recipient != address(0));\n        _checkArgument(amount > 0);\n        _checkState(!depositsPaused);\n        _checkState(!_isProtocolInBadDebt());\n        _checkState(_mytSharesDeposited + amount <= depositCap);\n\n        // Only mint a new position if the id is 0\n        if (tokenId == 0) {\n            tokenId = IAlchemistV3Position(alchemistPositionNFT).mint(recipient);\n            emit AlchemistV3PositionNFTMinted(recipient, tokenId);\n        } else {\n            _checkForValidAccountId(tokenId);\n            _earmark();\n            _sync(tokenId);\n        }\n\n        _accounts[tokenId].collateralBalance += amount;\n\n        // Transfer tokens from msg.sender now that the internal storage updates have been committed.\n        TokenUtils.safeTransferFrom(myt, msg.sender, address(this), amount);\n        _mytSharesDeposited += amount;\n\n        emit Deposit(amount, tokenId);\n\n        return (tokenId, convertYieldTokensToDebt(amount));\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function withdraw(uint256 amount, address recipient, uint256 tokenId) external returns (uint256) {\n        _checkArgument(recipient != address(0));\n        _checkForValidAccountId(tokenId);\n        _checkArgument(amount > 0);\n        _checkAccountOwnership(IAlchemistV3Position(alchemistPositionNFT).ownerOf(tokenId), msg.sender);\n        _earmark();\n        _sync(tokenId);\n\n        if (_accounts[tokenId].collateralBalance > _mytSharesDeposited) {\n            _accounts[tokenId].collateralBalance = _mytSharesDeposited;\n        }\n\n        uint256 debtShares = convertDebtTokensToYield(_accounts[tokenId].debt);\n        uint256 lockedCollateral = FixedPointMath.mulDivUp(debtShares, minimumCollateralization, FIXED_POINT_SCALAR);\n        _checkArgument(_accounts[tokenId].collateralBalance - lockedCollateral >= amount);\n        uint256 transferred = _subCollateralBalance(amount, tokenId);\n\n        // Assure that the collateralization invariant is still held.\n        _validate(tokenId);\n\n        // Transfer the yield tokens to msg.sender\n        TokenUtils.safeTransfer(myt, recipient, transferred);\n\n        emit Withdraw(transferred, tokenId, recipient);\n\n        return transferred;\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function mint(uint256 tokenId, uint256 amount, address recipient) external {\n        _checkArgument(recipient != address(0));\n        _checkForValidAccountId(tokenId);\n        _checkArgument(amount > 0);\n        _checkState(!loansPaused);\n        _checkAccountOwnership(IAlchemistV3Position(alchemistPositionNFT).ownerOf(tokenId), msg.sender);\n\n        // Query transmuter and earmark global debt\n        _earmark();\n        _checkState(!_isProtocolInBadDebt());\n        // Sync current user debt before more is taken\n        _sync(tokenId);\n\n        // Mint tokens to recipient\n        _mint(tokenId, amount, recipient);\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function mintFrom(uint256 tokenId, uint256 amount, address recipient) external {\n        _checkArgument(amount > 0);\n        _checkForValidAccountId(tokenId);\n        _checkArgument(recipient != address(0));\n        _checkState(!loansPaused);\n        // Preemptively try and decrease the minting allowance. This will save gas when the allowance is not sufficient.\n        _decreaseMintAllowance(tokenId, msg.sender, amount);\n\n        // Query transmuter and earmark global debt\n        _earmark();\n        _checkState(!_isProtocolInBadDebt());\n        // Sync current user debt before more is taken\n        _sync(tokenId);\n\n        // Mint tokens from the tokenId's account to the recipient.\n        _mint(tokenId, amount, recipient);\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function burn(uint256 amount, uint256 recipientId) external returns (uint256) {\n        _checkArgument(amount > 0);\n        _checkForValidAccountId(recipientId);\n        // Block same-block mint -> burn round trips.\n        if (block.number == _accounts[recipientId].lastMintBlock) revert CannotRepayOnMintBlock();\n\n        // Query transmuter and earmark global debt\n        _earmark();\n\n        // Sync current user debt before more is taken\n        _sync(recipientId);\n\n        uint256 debt;\n        // Debt-token burns can only repay the unearmarked portion.\n        _checkState((debt = _accounts[recipientId].debt - _accounts[recipientId].earmarked) > 0);\n\n        uint256 credit = _capDebtCredit(amount, debt);\n        if (credit == 0) return 0;\n\n        // Must only burn enough tokens that the transmuter positions can still be fulfilled\n        if (credit > totalSyntheticsIssued - ITransmuter(transmuter).totalLocked()) {\n            revert BurnLimitExceeded(credit, totalSyntheticsIssued - ITransmuter(transmuter).totalLocked());\n        }\n\n        // Burn the debt tokens from the caller.\n        TokenUtils.safeBurnFrom(debtToken, msg.sender, credit);\n\n        // Apply the repayment to the target account.\n        _subDebt(recipientId, credit);\n        _accounts[recipientId].lastRepayBlock = block.number;\n\n        totalSyntheticsIssued -= credit;\n\n        // Assure that the collateralization invariant is still held.\n        _validate(recipientId);\n\n        emit Burn(msg.sender, credit, recipientId);\n\n        return credit;\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function repay(uint256 amount, uint256 recipientTokenId) external returns (uint256) {\n        _checkArgument(amount > 0);\n        _checkForValidAccountId(recipientTokenId);\n        Account storage account = _accounts[recipientTokenId];\n        // Block same-block mint -> repay round trips.\n        if (block.number == account.lastMintBlock) revert CannotRepayOnMintBlock();\n\n        // Query transmuter and earmark global debt\n        _earmark();\n\n        // Sync current user debt before deciding how much is available to be repaid\n        _sync(recipientTokenId);\n\n        uint256 debt;\n\n        // MYT repayments can extinguish both earmarked and unearmarked debt.\n        _checkState((debt = account.debt) > 0);\n\n        uint256 yieldToDebt = convertYieldTokensToDebt(amount);\n        uint256 credit = _capDebtCredit(yieldToDebt, debt);\n        if (credit == 0) return 0;\n\n        // Repay earmarked debt first so protocol fees are charged on the earmarked portion only.\n        uint256 earmarkedRepaid = _subEarmarkedDebt(credit, recipientTokenId);\n\n\n        uint256 creditToYield = convertDebtTokensToYield(credit);\n        uint256 earmarkedRepaidToYield = convertDebtTokensToYield(earmarkedRepaid);\n\n\n        // Protocol fee only applies to the earmarked portion of the repayment.\n        uint256 feeAmount = earmarkedRepaidToYield * protocolFee / BPS;\n        if (feeAmount > account.collateralBalance) {\n            revert IllegalState();\n        } else {\n            _subCollateralBalance(feeAmount, recipientTokenId);\n        }\n\n        _subDebt(recipientTokenId, credit);\n        account.lastRepayBlock = block.number;\n\n        // Forward the repaid MYT to the transmuter.\n        TokenUtils.safeTransferFrom(myt, msg.sender, transmuter, creditToYield);\n        _syncEarmarkedTransmuterTransfer(creditToYield, earmarkedRepaidToYield);\n        if (feeAmount > 0) {\n            TokenUtils.safeTransfer(myt, protocolFeeReceiver, feeAmount);\n        }\n        emit Repay(msg.sender, amount, recipientTokenId, creditToYield);\n\n        return creditToYield;\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function liquidate(uint256 accountId) external override returns (uint256 yieldAmount, uint256 feeInYield, uint256 feeInUnderlying) {\n        _checkForValidAccountId(accountId);\n        uint256 debtBefore = _accounts[accountId].debt;\n        (yieldAmount, feeInYield, feeInUnderlying) = _liquidate(accountId);\n        if (yieldAmount > 0 || feeInYield > 0 || feeInUnderlying > 0 || _accounts[accountId].debt < debtBefore) {\n            return (yieldAmount, feeInYield, feeInUnderlying);\n        } else {\n            // No collateral or fee movement occurred and debt did not decrease.\n            revert LiquidationError();\n        }\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function batchLiquidate(uint256[] calldata accountIds)\n        external\n        returns (uint256 totalAmountLiquidated, uint256 totalFeesInYield, uint256 totalFeesInUnderlying)\n    {\n        if (accountIds.length == 0) {\n            revert MissingInputData();\n        }\n\n        bool anyProgress = false;\n        for (uint256 i = 0; i < accountIds.length; i++) {\n            uint256 accountId = accountIds[i];\n            if (accountId == 0 || !_tokenExists(alchemistPositionNFT, accountId)) {\n                continue;\n            }\n            uint256 debtBefore = _accounts[accountId].debt;\n            (uint256 underlyingAmount, uint256 feeInYield, uint256 feeInUnderlying) = _liquidate(accountId);\n            totalAmountLiquidated += underlyingAmount;\n            totalFeesInYield += feeInYield;\n            totalFeesInUnderlying += feeInUnderlying;\n            if (\n                underlyingAmount > 0 || feeInYield > 0 || feeInUnderlying > 0 || _accounts[accountId].debt < debtBefore\n            ) {\n                anyProgress = true;\n            }\n        }\n\n        if (anyProgress) {\n            return (totalAmountLiquidated, totalFeesInYield, totalFeesInUnderlying);\n        } else {\n            // None of the requested accounts made progress.\n            revert LiquidationError();\n        }\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function redeem(uint256 amount) external onlyTransmuter returns (uint256 sharesSent) {\n        _earmark();\n\n        uint256 liveEarmarked = cumulativeEarmarked;\n        if (amount > liveEarmarked) amount = liveEarmarked;\n\n        uint256 effectiveRedeemed = 0;\n\n        if (liveEarmarked != 0 && amount != 0) {\n            // ratioWanted = (liveEarmarked - amount) / liveEarmarked in Q128.128\n            uint256 remaining = liveEarmarked - amount;\n            uint256 ratioWanted = (remaining <= 5_256_001) ? 0 : FixedPointMath.divQ128(remaining, liveEarmarked);\n\n            // Snapshot old packed\n            uint256 packedOld = _redemptionWeight;\n            uint256 oldEpoch  = _redEpoch(packedOld);\n            uint256 oldIndex  = _redIndex(packedOld);\n\n            // Normalize uninitialized / zero index\n            if (packedOld == 0) {\n                oldIndex = ONE_Q128;\n            }\n            if (oldIndex == 0) {\n                oldEpoch += 1;\n                oldIndex = ONE_Q128;\n            }\n\n            // Compute new packed\n            uint256 newEpoch = oldEpoch;\n            uint256 newIndex;\n\n            if (ratioWanted == 0) {\n                newEpoch += 1;\n                newIndex = ONE_Q128;\n            } else {\n                newIndex = FixedPointMath.mulQ128(oldIndex, ratioWanted);\n            }\n\n            _redemptionWeight = _packRed(newEpoch, newIndex);\n\n            // ratioApplied is what accounts will actually see via _redemptionSurvivalRatio()\n            // epoch advance => full wipe => 0 survival\n            uint256 ratioApplied = (newEpoch > oldEpoch) ? 0 : FixedPointMath.divQ128(newIndex, oldIndex);\n\n            // Apply survival using the APPLIED ratio\n            _survivalAccumulator = FixedPointMath.mulQ128(_survivalAccumulator, ratioApplied);\n\n            // Derive effective redeemed amount using the SAME applied ratio\n            uint256 remainingEarmarked = FixedPointMath.mulQ128(liveEarmarked, ratioApplied);\n            effectiveRedeemed = liveEarmarked - remainingEarmarked;\n            if (amount > effectiveRedeemed + 5_256_001) revert IllegalState();\n\n            cumulativeEarmarked = remainingEarmarked;\n            totalDebt -= effectiveRedeemed;\n        }\n\n        lastRedemptionBlock = block.number;\n\n        // Use the effective redeemed amount everywhere downstream\n        uint256 collRedeemed  = convertDebtTokensToYield(effectiveRedeemed);\n        uint256 feeCollateral = collRedeemed * protocolFee / BPS;\n\n        _totalRedeemedDebt += effectiveRedeemed;\n        _totalRedeemedSharesOut += collRedeemed;\n\n        TokenUtils.safeTransfer(myt, transmuter, collRedeemed);\n        _mytSharesDeposited -= collRedeemed;\n\n        // If the remaining tracked MYT cannot fully cover the protocol fee, skip the fee entirely.\n        if (feeCollateral <= _mytSharesDeposited) {\n            TokenUtils.safeTransfer(myt, protocolFeeReceiver, feeCollateral);\n            _mytSharesDeposited -= feeCollateral;\n            _totalRedeemedSharesOut += feeCollateral;\n        }\n\n        emit Redemption(effectiveRedeemed);\n        return collRedeemed;\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function selfLiquidate(uint256 accountId, address recipient) external returns (uint256 amountLiquidated) {\n        _checkArgument(recipient != address(0));\n        _checkForValidAccountId(accountId);\n        _checkAccountOwnership(IAlchemistV3Position(alchemistPositionNFT).ownerOf(accountId), msg.sender);\n        _poke(accountId);\n        _checkState(_accounts[accountId].debt > 0);\n        if (!_isAccountHealthy(accountId, false)) {\n            // Unhealthy accounts must go through the regular liquidation path.\n           revert AccountNotHealthy();\n        }\n        Account storage account = _accounts[accountId];\n\n        // First clear any earmarked debt from the account's own collateral.\n        uint256 repaidEarmarkedDebtInYield = _forceRepay(accountId, account.earmarked, true);\n    \n        uint256 debt = account.debt;\n\n        // Then clear whatever debt remains.\n        _subDebt(accountId, debt);\n\n        // Remove the collateral that backed the remaining debt.\n        uint256 repaidDebtInYield = _subCollateralBalance(convertDebtTokensToYield(debt), accountId);\n\n        // Sweep any residual collateral out of the account.\n        uint256 remainingCollateral = _subCollateralBalance(account.collateralBalance, accountId);\n\n        if(repaidDebtInYield > 0) {\n            // Forward repaid collateral to the transmuter.\n            _syncEarmarkedTransmuterTransfer(repaidDebtInYield, 0);\n            TokenUtils.safeTransfer(myt, transmuter, repaidDebtInYield);\n        }\n\n        if(remainingCollateral > 0) {\n            // Return leftover collateral to the recipient.\n            TokenUtils.safeTransfer(myt, recipient, remainingCollateral);\n        }   \n        emit SelfLiquidated(accountId, repaidEarmarkedDebtInYield + repaidDebtInYield);\n        return repaidEarmarkedDebtInYield + repaidDebtInYield;\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function calculateLiquidation(\n        uint256 collateral,\n        uint256 debt,\n        uint256 targetCollateralization,\n        uint256 alchemistCurrentCollateralization,\n        uint256 alchemistMinimumCollateralization,\n        uint256 feeBps\n    ) public pure returns (uint256 grossCollateralToSeize, uint256 debtToBurn, uint256 fee, uint256 outsourcedFee) {\n        if (debt >= collateral) {\n            outsourcedFee = (debt * feeBps) / BPS;\n            // fully liquidate debt if debt is greater than collateral\n            return (collateral, debt, 0, outsourcedFee);\n        }\n\n        if (alchemistCurrentCollateralization < alchemistMinimumCollateralization) {\n            outsourcedFee = (debt * feeBps) / BPS;\n            // fully liquidate debt in high ltv global environment\n            return (debt, debt, 0, outsourcedFee);\n        }\n\n        // fee is taken from surplus = collateral - debt\n        uint256 surplus = collateral - debt;\n\n        fee = (surplus * feeBps) / BPS;\n\n        // collateral remaining for margin‐restore calc\n        uint256 adjCollat = collateral - fee;\n        // compute m*d  (both plain units)\n        uint256 md = (targetCollateralization * debt) / FIXED_POINT_SCALAR;\n        // if md <= adjCollat, nothing to liquidate\n        if (md <= adjCollat) {\n            return (0, 0, 0, 0);\n        }\n\n        // numerator = md - adjCollat\n        uint256 num = md - adjCollat;\n\n        // denom = m - 1  =>  (targetCollateralization - FIXED_POINT_SCALAR)/FIXED_POINT_SCALAR\n        uint256 denom = targetCollateralization - FIXED_POINT_SCALAR;\n\n        debtToBurn = (num * FIXED_POINT_SCALAR) / denom;\n\n        // gross collateral seize = net + fee\n        grossCollateralToSeize = debtToBurn + fee;\n    }\n\n    ///@inheritdoc IAlchemistV3Actions\n    function reduceSyntheticsIssued(uint256 amount) external onlyTransmuter {\n        totalSyntheticsIssued -= amount;\n    }\n\n    ///@inheritdoc IAlchemistV3Actions\n    function setTransmuterTokenBalance(uint256 amount) external onlyTransmuter {\n        if (amount > lastTransmuterTokenBalance) {\n            _pendingCoverShares += amount - lastTransmuterTokenBalance;\n        }\n\n        lastTransmuterTokenBalance = amount;\n    }\n\n    /// @dev Tracks transmuter inflows so earmarked shares bypass cover and the remainder becomes live cover.\n    function _syncEarmarkedTransmuterTransfer(uint256 sharesSent, uint256 earmarkedShares) internal {\n        if (sharesSent > earmarkedShares) {\n            _pendingCoverShares += sharesSent - earmarkedShares;\n        }\n        lastTransmuterTokenBalance += sharesSent;\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function poke(uint256 tokenId) external {\n        _checkForValidAccountId(tokenId);\n        _poke(tokenId);\n    }\n\n\n    /// @dev Pokes the account owned by `tokenId` to sync the state.\n    /// @param tokenId The tokenId of the account to poke.\n    function _poke(uint256 tokenId) internal {\n        _earmark();\n        _sync(tokenId);\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function approveMint(uint256 tokenId, address spender, uint256 amount) external {\n        _checkAccountOwnership(IAlchemistV3Position(alchemistPositionNFT).ownerOf(tokenId), msg.sender);\n        _approveMint(tokenId, spender, amount);\n    }\n\n    /// @inheritdoc IAlchemistV3Actions\n    function resetMintAllowances(uint256 tokenId) external {\n        // Allow calls from either the token owner or the NFT contract\n        if (msg.sender != address(alchemistPositionNFT)) {\n            // Direct call - verify caller is current owner\n            address tokenOwner = IERC721(alchemistPositionNFT).ownerOf(tokenId);\n            if (msg.sender != tokenOwner) {\n                revert Unauthorized();\n            }\n        }\n        // increment version to start the mapping from a fresh state\n        _accounts[tokenId].allowancesVersion += 1;\n        // Emit event to notify allowance clearing\n        emit MintAllowancesReset(tokenId);\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function convertYieldTokensToDebt(uint256 amount) public view returns (uint256) {\n        return normalizeUnderlyingTokensToDebt(convertYieldTokensToUnderlying(amount));\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function convertDebtTokensToYield(uint256 amount) public view returns (uint256) {\n        return convertUnderlyingTokensToYield(normalizeDebtTokensToUnderlying(amount));\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function convertYieldTokensToUnderlying(uint256 amount) public view returns (uint256) {\n        return IVaultV2(myt).convertToAssets(amount);\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function convertUnderlyingTokensToYield(uint256 amount) public view returns (uint256) {\n        return IVaultV2(myt).convertToShares(amount);\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function normalizeUnderlyingTokensToDebt(uint256 amount) public view returns (uint256) {\n        return amount * underlyingConversionFactor;\n    }\n\n    /// @inheritdoc IAlchemistV3State\n    function normalizeDebtTokensToUnderlying(uint256 amount) public view returns (uint256) {\n        return amount / underlyingConversionFactor;\n    }\n\n    /// @dev Mints debt tokens to `recipient` using the account owned by `tokenId`.\n    /// @param tokenId     The tokenId of the account to mint from.\n    /// @param amount    The amount to mint.\n    /// @param recipient The recipient of the minted debt tokens.\n    function _mint(uint256 tokenId, uint256 amount, address recipient) internal {\n        if (block.number == _accounts[tokenId].lastRepayBlock) revert CannotMintOnRepayBlock();\n        _addDebt(tokenId, amount);\n\n        totalSyntheticsIssued += amount;\n\n        // Validate the tokenId's account to assure that the collateralization invariant is still held.\n        _validate(tokenId);\n\n        _accounts[tokenId].lastMintBlock = block.number;\n\n        // Mint the debt tokens to the recipient.\n        TokenUtils.safeMint(debtToken, recipient, amount);\n\n        emit Mint(tokenId, amount, recipient);\n    }\n\n    /**\n     * @notice Uses the account's own collateral to retire debt and the associated protocol fee.\n     * @param accountId The token id of the account to repay from.\n     * @param amount The requested debt repayment amount, denominated in debt tokens.\n     * @param skipPoke Whether to skip syncing the account before repayment.\n     * @return creditToYield The MYT shares routed to the transmuter for the retired debt.\n     */\n     function _forceRepay(uint256 accountId, uint256 amount, bool skipPoke) internal returns (uint256) {\n        if (amount == 0) {\n            return 0;\n        }\n        _checkForValidAccountId(accountId);\n        if (!skipPoke) {\n            _poke(accountId);\n        }\n        Account storage account = _accounts[accountId];\n        uint256 debt;\n\n        // Retiring debt from collateral can cover any debt bucket.\n        _checkState((debt = account.debt) > 0);\n\n        // Clamp requested repayment against live account/global debt.\n        uint256 credit = _capDebtCredit(amount, debt);\n        if (credit == 0) return 0;\n        // Reduce earmarked debt first, then total debt.\n        uint256 earmarkedRepaid = _subEarmarkedDebt(credit, accountId);\n        _subDebt(accountId, credit);\n        \n        // Remove the collateral that backs the retired debt.\n        uint256 creditToYield = _subCollateralBalance(convertDebtTokensToYield(credit), accountId);\n\n        // Collect as much protocol fee as the remaining collateral can support.\n        uint256 targetProtocolFee = creditToYield * protocolFee / BPS;\n        uint256 protocolFeeTotal = _subCollateralBalance(targetProtocolFee, accountId);\n\n\n        emit ForceRepay(accountId, amount, creditToYield, protocolFeeTotal);\n\n        if (creditToYield > 0) {\n            // Route the retired collateral to the transmuter.\n            TokenUtils.safeTransfer(myt, address(transmuter), creditToYield);\n            _syncEarmarkedTransmuterTransfer(creditToYield, convertDebtTokensToYield(earmarkedRepaid));\n        }\n\n        if (protocolFeeTotal > 0) {\n            // Forward the realized fee to the protocol fee receiver.\n            TokenUtils.safeTransfer(myt, protocolFeeReceiver, protocolFeeTotal);\n        }\n\n        return creditToYield;\n    }\n\n    /// @dev Subtracts the earmarked debt by `amount` for the account owned by `accountId`.\n    /// @param amountInDebtTokens The amount of debt tokens to subtract from the earmarked debt.\n    /// @param accountId The tokenId of the account to subtract the earmarked debt from.\n    /// @return The amount of debt tokens subtracted from the earmarked debt.\n    function _subEarmarkedDebt(uint256 amountInDebtTokens, uint256 accountId) internal returns (uint256) {\n        Account storage account = _accounts[accountId];\n\n        uint256 debt = account.debt;\n        uint256 earmarkedDebt = account.earmarked;\n\n        uint256 credit = amountInDebtTokens > debt ? debt : amountInDebtTokens;\n        uint256 earmarkToRemove = credit > earmarkedDebt ? earmarkedDebt : credit;\n\n        // Always reduce local earmark by the full local repay amount.\n        account.earmarked = earmarkedDebt - earmarkToRemove;\n\n        // Global can lag local by rounding; clamp only the global subtraction.\n        uint256 remove = earmarkToRemove > cumulativeEarmarked ? cumulativeEarmarked : earmarkToRemove;\n        cumulativeEarmarked -= remove;\n\n        return earmarkToRemove;\n    }\n\n\n    /// @dev Subtracts the collateral balance by `amount` for the account owned by `accountId`.\n    /// @param amountInYieldTokens The amount of yield tokens to subtract from the collateral balance.\n    /// @param accountId The tokenId of the account to subtract the collateral balance from.\n    /// @return The amount of yield tokens subtracted from the collateral balance.\n    function _subCollateralBalance(uint256 amountInYieldTokens, uint256 accountId) internal returns (uint256) {\n        Account storage account = _accounts[accountId];\n        uint256 collateralBalance = account.collateralBalance;\n\n        // Reconcile local collateral against global tracked shares before subtraction.\n        // This prevents underflow if rounding/drift made local storage exceed global storage.\n        if (collateralBalance > _mytSharesDeposited) {\n            collateralBalance = _mytSharesDeposited;\n            account.collateralBalance = collateralBalance;\n        }\n\n        uint256 amountToRemove = amountInYieldTokens > collateralBalance ? collateralBalance : amountInYieldTokens;\n        account.collateralBalance = collateralBalance - amountToRemove;\n        _mytSharesDeposited -= amountToRemove;\n        return amountToRemove;\n    }\n\n    /// @dev Syncs the account, attempts earmarked debt repayment, and liquidates collateral if still unhealthy.\n    /// @param accountId The token id of the account to liquidate.\n    /// @return amountLiquidated The amount of MYT seized from the account.\n    /// @return feeInYield The MYT fee paid to the liquidator.\n    /// @return feeInUnderlying The underlying-denominated fee paid from the fee vault, if any.\n    function _liquidate(uint256 accountId) internal returns (uint256 amountLiquidated, uint256 feeInYield, uint256 feeInUnderlying) {\n        // Query transmuter and earmark global debt\n        _earmark();\n        // Sync current user debt before deciding how much needs to be liquidated\n        _sync(accountId);\n\n        Account storage account = _accounts[accountId];\n\n        // If one share is worth zero underlying, liquidation math is undefined and should noop.\n        if (IVaultV2(myt).convertToAssets(1e18) == 0) {\n            return (0, 0, 0);\n        }\n       \n        if (_isAccountHealthy(accountId, false)) {\n            return (0, 0, 0);\n        }\n\n        // First try to clear earmarked debt from the account's own collateral.\n        uint256 repaidAmountInYield = 0;\n        if (account.earmarked > 0) {\n            repaidAmountInYield = _forceRepay(accountId, account.earmarked, false);\n            feeInYield = _calculateRepaymentFee(repaidAmountInYield);\n            // Final safety check after all deductions\n            if (account.collateralBalance == 0 && account.debt > 0) {\n                uint256 debtToClear = _clearableDebt(account.debt);\n                if (debtToClear > 0) {\n                    _subDebt(accountId, debtToClear);\n                }\n            }\n        }\n\n        // Recalculate ratio after any repayment to determine if further liquidation is needed\n        if (_isAccountHealthy(accountId, false)) {\n            if (feeInYield > 0) {\n                uint256 targetFeeInYield = feeInYield;\n                uint256 maxSafeFeeInYield = _maxRepaymentFeeInYield(accountId);\n                // Use a single fee source: either the account can safely cover the full fee in MYT,\n                // or the entire fee is outsourced to the fee vault.\n                if (maxSafeFeeInYield < targetFeeInYield) {\n                    feeInYield = 0;\n                    feeInUnderlying = convertYieldTokensToUnderlying(targetFeeInYield);\n                }\n            }\n\n            if (feeInYield > 0) {\n                feeInYield = _subCollateralBalance(feeInYield, accountId); // clamps to available balance\n                TokenUtils.safeTransfer(myt, msg.sender, feeInYield);\n            } else if (feeInUnderlying > 0) {\n                feeInUnderlying = _payWithFeeVault(feeInUnderlying);\n            }\n            emit RepaymentFee(accountId, msg.sender, feeInYield, feeInUnderlying);\n            return (repaidAmountInYield, feeInYield, feeInUnderlying);\n        } else {\n            // Do actual liquidation\n            return _doLiquidation(accountId);\n        }\n\n    }\n\n    /// @dev Pays the fee to msg.sender in underlying tokens using the fee vault\n    /// @param amountInUnderlying The amount of underlying tokens to pay\n    /// @return actual amount paid based on the vault balance\n    function _payWithFeeVault(uint256 amountInUnderlying) internal returns (uint256) {\n        if (amountInUnderlying == 0) return 0;\n        if (alchemistFeeVault == address(0)) {\n            emit FeeShortfall(msg.sender, amountInUnderlying, 0);\n            return 0;\n        }\n        uint256 vaultBalance = IFeeVault(alchemistFeeVault).totalDeposits();\n        if (vaultBalance > 0) {\n            uint256 adjustedAmount = amountInUnderlying > vaultBalance ? vaultBalance : amountInUnderlying;\n            IFeeVault(alchemistFeeVault).withdraw(msg.sender, adjustedAmount);\n            if (adjustedAmount < amountInUnderlying) {\n                emit FeeShortfall(msg.sender, amountInUnderlying, adjustedAmount);\n            }\n            return adjustedAmount;\n        }\n        emit FeeShortfall(msg.sender, amountInUnderlying, 0);\n        return 0;\n    }\n\n    /// @dev Checks if the account is healthy\n    /// @dev An account is healthy if its collateralization ratio is greater than the collateralization lower bound\n    /// @dev An account is healthy if it has no debt\n    /// @param accountId The tokenId of the account to check.\n    /// @param refresh Whether to refresh the account's collateral value by including unrealized debt.\n    /// @return true if the account is healthy, false otherwise.\n    function _isAccountHealthy(uint256 accountId, bool refresh) internal view returns (bool) {\n        if (_accounts[accountId].debt == 0) {\n            return true;\n        }\n        uint256 collateralInUnderlying = _totalCollateralValue(accountId, refresh);\n        uint256 collateralizationRatio = collateralInUnderlying * FIXED_POINT_SCALAR / _accounts[accountId].debt;\n        return collateralizationRatio > collateralizationLowerBound;\n    }\n\n    /// @dev Performs the collateral-seizure phase of liquidation once the account must be liquidated.\n    /// @param accountId The token id of the account to liquidate.\n    /// @return amountLiquidated The amount of MYT seized from the account.\n    /// @return feeInYield The MYT fee paid to the liquidator.\n    /// @return feeInUnderlying The underlying-denominated fee paid from the fee vault.\n    function _doLiquidation(uint256 accountId)\n        internal\n        returns (uint256 amountLiquidated, uint256 feeInYield, uint256 feeInUnderlying)\n    {\n        Account storage account = _accounts[accountId];\n        uint256 debt = account.debt;\n        uint256 collateralInUnderlying = totalValue(accountId);\n        (uint256 liquidationAmount, uint256 debtToBurn, uint256 baseFee, uint256 outsourcedFee) = calculateLiquidation(\n            collateralInUnderlying,\n            debt,\n            liquidationTargetCollateralization,\n            normalizeUnderlyingTokensToDebt(_getTotalUnderlyingValue()) * FIXED_POINT_SCALAR / totalDebt,\n            globalMinimumCollateralization,\n            liquidatorFee\n        );\n\n        if (liquidationAmount == 0) {\n            // Debt-only closeout path: account can be insolvent with no remaining collateral to seize.\n            if (debtToBurn > 0) {\n                uint256 burnableDebt = _capDebtCredit(debtToBurn, account.debt);\n                if (burnableDebt > 0) {\n                    _subDebt(accountId, burnableDebt);\n                }\n            }\n\n            uint256 feeRequestInUnderlying = normalizeDebtTokensToUnderlying(outsourcedFee);\n            if (feeRequestInUnderlying > 0) {\n                feeInUnderlying = _payWithFeeVault(feeRequestInUnderlying);\n            }\n\n            if (account.debt < debt || feeInUnderlying > 0) {\n                emit Liquidated(accountId, msg.sender, 0, 0, feeInUnderlying);\n                return (0, 0, feeInUnderlying);\n            }\n\n            return (0, 0, 0);\n        }\n\n        uint256 requestedLiquidationInYield = convertDebtTokensToYield(liquidationAmount);\n        amountLiquidated = _subCollateralBalance(requestedLiquidationInYield, accountId);\n        if (amountLiquidated == 0) return (0, 0, 0);\n\n        // Fee and debt burn are derived from idealized liquidation math, so clamp them to what was\n        // actually realized after collateral capping to avoid underflow and over-burning debt.\n        uint256 requestedFeeInYield = convertDebtTokensToYield(baseFee);\n        feeInYield = requestedFeeInYield > amountLiquidated ? amountLiquidated : requestedFeeInYield;\n\n        uint256 netToTransmuter = amountLiquidated - feeInYield;\n        uint256 maxDebtByRealized = convertYieldTokensToDebt(netToTransmuter);\n        uint256 maxDebtByStorage = account.debt < totalDebt ? account.debt : totalDebt;\n\n        if (debtToBurn > maxDebtByRealized) debtToBurn = maxDebtByRealized;\n        if (debtToBurn > maxDebtByStorage) debtToBurn = maxDebtByStorage;\n\n        // Apply the realized debt reduction.\n        if (debtToBurn > 0) {\n            _subDebt(accountId, debtToBurn);\n        }\n\n        // If liquidation still leaves the account unhealthy, force-close the residual:\n        // sweep all remaining collateral and clear any debt that cannot be backed anymore.\n        if (account.debt > 0 && !_isAccountHealthy(accountId, false)) {\n            uint256 remainingShares = account.collateralBalance;\n            if (remainingShares > 0) {\n                uint256 removedShares = _subCollateralBalance(remainingShares, accountId);\n                netToTransmuter += removedShares;\n\n                uint256 extraDebtBurn = _capDebtCredit(convertYieldTokensToDebt(removedShares), account.debt);\n                if (extraDebtBurn > 0) {\n                    _subDebt(accountId, extraDebtBurn);\n                }\n            }\n\n            if (account.collateralBalance == 0 && account.debt > 0) {\n                uint256 debtToClear = _clearableDebt(account.debt);\n                if (debtToClear > 0) {\n                    _subDebt(accountId, debtToClear);\n                }\n            }\n        }\n\n        // Route seized collateral net of liquidator fee to the transmuter.\n        if (netToTransmuter > 0) {\n            _syncEarmarkedTransmuterTransfer(netToTransmuter, 0);\n            TokenUtils.safeTransfer(myt, transmuter, netToTransmuter);\n        }\n\n        // Pay the liquidator from MYT if available, otherwise fall back to the fee vault.\n        if (feeInYield > 0) {\n            TokenUtils.safeTransfer(myt, msg.sender, feeInYield);\n        } else if (normalizeDebtTokensToUnderlying(outsourcedFee) > 0) {\n            feeInUnderlying = _payWithFeeVault(normalizeDebtTokensToUnderlying(outsourcedFee));\n        }\n        emit Liquidated(accountId, msg.sender, amountLiquidated, feeInYield, feeInUnderlying);\n        return (amountLiquidated, feeInYield, feeInUnderlying);\n    }\n\n \n    /// @dev Handles repayment fee calculation.\n    /// @param repaidAmountInYield The amount of debt repaid in yield tokens.\n    /// @return feeInYield The fee in yield tokens to be sent to the liquidator.\n    function _calculateRepaymentFee(uint256 repaidAmountInYield) internal view returns (uint256 feeInYield) {\n        return repaidAmountInYield * repaymentFee / BPS;\n    }\n\n    /// @dev Returns max yield-fee removable while remaining strictly healthy (> lower bound).\n    /// @param accountId The tokenId of the account to compute the max repayment fee for.\n    /// @return The max repayment fee in yield tokens.\n    function _maxRepaymentFeeInYield(uint256 accountId) internal view returns (uint256) {\n        Account storage account = _accounts[accountId];\n        uint256 debt = account.debt;\n        if (debt == 0) {\n            return account.collateralBalance;\n        }\n\n        uint256 collateralInDebt = convertYieldTokensToDebt(account.collateralBalance);\n        uint256 minimumByLowerBound = FixedPointMath.mulDivUp(debt, collateralizationLowerBound, FIXED_POINT_SCALAR);\n        if (minimumByLowerBound == type(uint256).max) {\n            return 0;\n        }\n\n        // _isAccountHealthy uses a strict \">\" check, so retain one debt-unit of margin.\n        uint256 minRequiredPostFee = minimumByLowerBound + 1;\n        if (collateralInDebt <= minRequiredPostFee) {\n            return 0;\n        }\n\n        uint256 removableInDebt = collateralInDebt - minRequiredPostFee;\n        return convertDebtTokensToYield(removableInDebt);\n    }\n\n    /// @dev Increases the debt by `amount` for the account owned by `tokenId`.\n    ///\n    /// @param tokenId   The account owned by tokenId.\n    /// @param amount  The amount to increase the debt by.\n    function _addDebt(uint256 tokenId, uint256 amount) internal {\n        Account storage account = _accounts[tokenId];\n\n        uint256 newDebt = account.debt + amount;\n\n        // After _sync(tokenId), you can use the current collateralBalance (no simulation needed)\n        uint256 collateralValue = _totalCollateralValue(tokenId, false);\n\n        uint256 required = FixedPointMath.mulDivUp(newDebt, minimumCollateralization, FIXED_POINT_SCALAR);\n        if (collateralValue < required) revert Undercollateralized();\n\n        account.debt = newDebt;\n        totalDebt += amount;\n    }\n\n    /// @dev Subtracts the debt by `amount` for the account owned by `tokenId`.\n    ///\n    /// @param tokenId   The account owned by tokenId.\n    /// @param amount  The amount to decrease the debt by.\n    function _subDebt(uint256 tokenId, uint256 amount) internal {\n        Account storage account = _accounts[tokenId];\n\n        account.debt -= amount;\n        totalDebt -= amount;\n\n        if (cumulativeEarmarked > totalDebt) {\n            cumulativeEarmarked = totalDebt;\n        }\n    }\n\n    /// @dev Caps a debt-denominated credit against account debt and global debt.\n    function _capDebtCredit(uint256 requested, uint256 accountDebt) internal view returns (uint256) {\n        uint256 credit = requested > accountDebt ? accountDebt : requested;\n        if (credit > totalDebt) credit = totalDebt;\n        return credit;\n    }\n\n    /// @dev Returns debt that can be safely cleared against global debt accounting.\n    function _clearableDebt(uint256 accountDebt) internal view returns (uint256) {\n        return accountDebt > totalDebt ? totalDebt : accountDebt;\n    }\n\n    /// @dev Set the mint allowance for `spender` to `amount` for the account owned by `tokenId`.\n    ///\n    /// @param ownerTokenId   The id of the account granting approval.\n    /// @param spender The address of the spender.\n    /// @param amount  The amount of debt tokens to set the mint allowance to.\n    function _approveMint(uint256 ownerTokenId, address spender, uint256 amount) internal {\n        Account storage account = _accounts[ownerTokenId];\n        account.mintAllowances[account.allowancesVersion][spender] = amount;\n        emit ApproveMint(ownerTokenId, spender, amount);\n    }\n\n    /// @dev Decrease the mint allowance for `spender` by `amount` for the account owned by `ownerTokenId`.\n    ///\n    /// @param ownerTokenId The id of the account owner.\n    /// @param spender The address of the spender.\n    /// @param amount  The amount of debt tokens to decrease the mint allowance by.\n    function _decreaseMintAllowance(uint256 ownerTokenId, address spender, uint256 amount) internal {\n        Account storage account = _accounts[ownerTokenId];\n        account.mintAllowances[account.allowancesVersion][spender] -= amount;\n    }\n\n    /// @dev Checks an expression and reverts with an {IllegalArgument} error if the expression is {false}.\n    ///\n    /// @param expression The expression to check.\n    function _checkArgument(bool expression) internal pure {\n        if (!expression) {\n            revert IllegalArgument();\n        }\n    }\n\n    /// @dev Checks if owner == sender and reverts with an {UnauthorizedAccountAccessError} error if the result is {false}.\n    ///\n    /// @param owner The address of the owner of an account.\n    /// @param user The address of the user attempting to access an account.\n    function _checkAccountOwnership(address owner, address user) internal pure {\n        if (owner != user) {\n            revert UnauthorizedAccountAccessError();\n        }\n    }\n\n    /// @dev reverts {UnknownAccountOwnerIDError} error by if no owner exists.\n    ///\n    /// @param tokenId The id of an account.\n    function _checkForValidAccountId(uint256 tokenId) internal view {\n        if (!_tokenExists(alchemistPositionNFT, tokenId)) {\n            revert UnknownAccountOwnerIDError();\n        }\n    }\n\n    function _onlyAdmin() internal view {\n        if (msg.sender != admin) {\n            revert Unauthorized();\n        }\n    }\n\n    function _onlyAdminOrGuardian() internal view {\n        if (msg.sender != admin && !guardians[msg.sender]) {\n            revert Unauthorized();\n        }\n    }\n\n    function _onlyTransmuter() internal view {\n        if (msg.sender != transmuter) {\n            revert Unauthorized();\n        }\n    }\n\n    /**\n     * @notice Checks whether a token id is linked to an owner. Non blocking / no reverts.\n     * @param nft The address of the ERC721 based contract.\n     * @param tokenId The token id to check.\n     * @return exists A boolean that is true if the token exists.\n     */\n    function _tokenExists(address nft, uint256 tokenId) internal view returns (bool exists) {\n        if (tokenId == 0) {\n            // token ids start from 1\n            return false;\n        }\n        try IERC721(nft).ownerOf(tokenId) {\n            // If the call succeeds, the token exists.\n            exists = true;\n        } catch {\n            // If the call fails, then the token does not exist.\n            exists = false;\n        }\n    }\n\n    /// @dev Checks an expression and reverts with an {IllegalState} error if the expression is {false}.\n    ///\n    /// @param expression The expression to check.\n    function _checkState(bool expression) internal pure {\n        if (!expression) {\n            revert IllegalState();\n        }\n    }\n\n    /// @dev Checks that the account owned by `tokenId` is properly collateralized.\n    /// @dev If the account is undercollateralized then this will revert with an {Undercollateralized} error.\n    ///\n    /// @param tokenId The id of the account owner.\n    function _validate(uint256 tokenId) internal view {\n        if (_isUnderCollateralized(tokenId)) revert Undercollateralized();\n    }\n\n    /// @dev Calculate the total collateral value of the account in debt tokens.\n    /// @param tokenId The id of the account owner.\n    /// @return The total collateral value of the account in debt tokens.\n    function _totalCollateralValue(uint256 tokenId, bool includeUnrealizedDebt) internal view returns (uint256) {\n        uint256 totalUnderlying;\n        if (includeUnrealizedDebt) {\n            (,, uint256 collateral) = _calculateUnrealizedDebt(tokenId);\n            if (collateral > 0) totalUnderlying += convertYieldTokensToUnderlying(collateral);\n        } else {\n            totalUnderlying = convertYieldTokensToUnderlying(_accounts[tokenId].collateralBalance);\n        }\n        return normalizeUnderlyingTokensToDebt(totalUnderlying);\n    }\n\n    /// @dev Update the user's earmarked and redeemed debt amounts.\n    function _sync(uint256 tokenId) internal {\n        Account storage account = _accounts[tokenId];\n        (uint256 newDebt, uint256 newEarmarked, uint256 redeemedTotal) =\n            _computeUnrealizedAccount(account, _earmarkWeight, _redemptionWeight, _survivalAccumulator);\n\n        // Calculate collateral to remove\n        uint256 globalDebtDelta = _totalRedeemedDebt - account.lastTotalRedeemedDebt;\n        if (globalDebtDelta != 0 && redeemedTotal != 0) {\n            uint256 globalSharesDelta = _totalRedeemedSharesOut - account.lastTotalRedeemedSharesOut;\n\n            // sharesToDebit = redeemedTotal * globalSharesDelta / globalDebtDelta\n            uint256 sharesToDebit = FixedPointMath.mulDivUp(redeemedTotal, globalSharesDelta, globalDebtDelta);\n\n            if (sharesToDebit > account.collateralBalance) sharesToDebit = account.collateralBalance;\n            account.collateralBalance -= sharesToDebit;\n        }\n\n        // advance checkpoints even if redeemedTotal==0\n        account.lastTotalRedeemedDebt = _totalRedeemedDebt;\n        account.lastTotalRedeemedSharesOut = _totalRedeemedSharesOut;\n\n        account.earmarked = newEarmarked;\n        account.debt = newDebt;\n\n        // Advance account checkpoint\n        account.lastAccruedEarmarkWeight = _earmarkWeight;\n        account.lastAccruedRedemptionWeight = _redemptionWeight;\n\n        // Snapshot G for this account\n        account.lastSurvivalAccumulator = _survivalAccumulator;\n    }\n\n    /// @dev Computes the account debt/earmark state at a given global weight snapshot.\n    /// @return newDebt The debt after applying earmark + redemption.\n    /// @return newEarmarked The earmarked portion after applying survival and new earmarks.\n    /// @return redeemedDebt Realized redeemed debt for this step.\n    function _computeUnrealizedAccount(\n        Account storage account,\n        uint256 earmarkWeightCurrent,\n        uint256 redemptionWeightCurrent,\n        uint256 survivalAccumulatorCurrent\n    ) internal view returns (uint256 newDebt, uint256 newEarmarked, uint256 redeemedDebt) {\n        // Survival during current sync window\n        uint256 survivalRatio = \n            _redemptionSurvivalRatio(account.lastAccruedRedemptionWeight, redemptionWeightCurrent);\n\n        // User exposure at last sync used to calculate newly earmarked debt pre redemption\n        uint256 userExposure = account.debt > account.earmarked ? account.debt - account.earmarked : 0;\n        uint256 unearmarkSurvivalRatio = \n            _earmarkSurvivalRatio(account.lastAccruedEarmarkWeight, earmarkWeightCurrent);\n\n        // amount that stayed unearmarked from userExposure\n        uint256 unearmarkedRemaining = FixedPointMath.mulQ128(userExposure, unearmarkSurvivalRatio);\n\n        // amount newly earmarked since last sync\n        uint256 earmarkRaw = userExposure - unearmarkedRemaining;\n\n        // No redemption in this sync window -> debt cannot decrease.\n        if (survivalRatio == ONE_Q128) {\n            newDebt = account.debt;\n            newEarmarked = account.earmarked + earmarkRaw;\n            if (newEarmarked > newDebt) newEarmarked = newDebt;\n            redeemedDebt = 0;\n            return (newDebt, newEarmarked, redeemedDebt);\n        }\n\n        // Unwind via the survival accumulator.\n        uint256 earmarkSurvival = _redIndex(account.lastAccruedEarmarkWeight);\n        if (earmarkSurvival == 0) earmarkSurvival = ONE_Q128;\n\n        // Default path for accounts that stayed inside the same earmark epoch.\n        uint256 decayedRedeemed = FixedPointMath.mulQ128(account.lastSurvivalAccumulator, survivalRatio);\n        uint256 survivalDiff = survivalAccumulatorCurrent > decayedRedeemed ? survivalAccumulatorCurrent - decayedRedeemed : 0;\n        if (survivalDiff > earmarkSurvival) survivalDiff = earmarkSurvival;\n        uint256 unredeemedRatio = FixedPointMath.divQ128(survivalDiff, earmarkSurvival);\n        uint256 earmarkedUnredeemed = FixedPointMath.mulQ128(userExposure, unredeemedRatio);\n\n        uint256 oldEarEpoch = account.lastAccruedEarmarkWeight >> _EARMARK_INDEX_BITS;\n        uint256 newEarEpoch = earmarkWeightCurrent >> _EARMARK_INDEX_BITS;\n        if (newEarEpoch == oldEarEpoch) {\n            uint256 currentEarmarkIndex = earmarkWeightCurrent & _EARMARK_INDEX_MASK;\n            uint256 telescopedEarmarkDrop = earmarkSurvival > currentEarmarkIndex ? earmarkSurvival - currentEarmarkIndex : 0;\n\n            // When the current sync window stayed inside one earmark epoch and the accumulator\n            // collapses to the telescoped earmark drop, there were no interleaved pre-claim\n            // redemptions that require per-step weighting. Using the user-sized formula here\n            // avoids amplifying index-scale ceil rounding by exposure / earmarkSurvival.\n            if (survivalDiff == FixedPointMath.mulQ128(telescopedEarmarkDrop, survivalRatio)) {\n                earmarkedUnredeemed = FixedPointMath.mulQ128(earmarkRaw, survivalRatio);\n            }\n        }\n\n        // If the account crossed an earmark epoch, split math at the first boundary:\n        // - pre-boundary via accumulator diff at epoch boundary,\n        // - post-boundary via redemption survival only.\n        // This avoids both over-redemption (pre-boundary redemptions applied twice)\n        // and under-redemption (post-boundary accumulator contamination).\n        if (newEarEpoch > oldEarEpoch) {\n            uint256 boundaryEpoch = oldEarEpoch + 1;\n            uint256 boundaryRedemptionWeight = _earmarkEpochStartRedemptionWeight[boundaryEpoch];\n            uint256 boundarySurvivalAccumulator = _earmarkEpochStartSurvivalAccumulator[boundaryEpoch];\n\n            if (boundaryRedemptionWeight != 0) {\n                uint256 preBoundarySurvival =\n                    _redemptionSurvivalRatio(account.lastAccruedRedemptionWeight, boundaryRedemptionWeight);\n                uint256 decayedAtBoundary = FixedPointMath.mulQ128(account.lastSurvivalAccumulator, preBoundarySurvival);\n\n                uint256 boundaryDiff =\n                    boundarySurvivalAccumulator > decayedAtBoundary ? boundarySurvivalAccumulator - decayedAtBoundary : 0;\n                if (boundaryDiff > earmarkSurvival) boundaryDiff = earmarkSurvival;\n\n                uint256 unredeemedAtBoundaryRatio = FixedPointMath.divQ128(boundaryDiff, earmarkSurvival);\n                uint256 unredeemedAtBoundary = FixedPointMath.mulQ128(userExposure, unredeemedAtBoundaryRatio);\n\n                uint256 postBoundarySurvival =\n                    _redemptionSurvivalRatio(boundaryRedemptionWeight, redemptionWeightCurrent);\n\n                earmarkedUnredeemed = FixedPointMath.mulQ128(unredeemedAtBoundary, postBoundarySurvival);\n            } else {\n                // Backward-compatibility fallback for old state without boundary checkpoints.\n                earmarkedUnredeemed = FixedPointMath.mulQ128(earmarkRaw, survivalRatio);\n            }\n        }\n\n        if (earmarkedUnredeemed > earmarkRaw) earmarkedUnredeemed = earmarkRaw;\n\n        // Old earmarks that survived redemptions in the current sync window\n        uint256 exposureSurvival = FixedPointMath.mulQ128(account.earmarked, survivalRatio);\n        // What was redeemed from the newly earmark between last sync and now\n        uint256 redeemedFromEarmarked = earmarkRaw - earmarkedUnredeemed;\n        // Total overall earmarked to adjust user debt\n        uint256 redeemedTotal = (account.earmarked - exposureSurvival) + redeemedFromEarmarked;\n\n        newDebt = account.debt >= redeemedTotal ? account.debt - redeemedTotal : 0;\n        redeemedDebt = account.debt - newDebt;\n        newEarmarked = exposureSurvival + earmarkedUnredeemed;\n        if (newEarmarked > newDebt) newEarmarked = newDebt;\n    }\n\n    /// @dev Earmarks the debt for redemption.\n    function _earmark() internal {\n        if (totalDebt == 0) return;\n        if (block.number <= lastEarmarkBlock) return;\n\n        // update pending cover shares based on transmuter balance delta\n        uint256 transmuterBalance = TokenUtils.safeBalanceOf(myt, address(transmuter));\n\n        if (transmuterBalance > lastTransmuterTokenBalance) {\n            _pendingCoverShares += (transmuterBalance - lastTransmuterTokenBalance);\n        }\n\n        lastTransmuterTokenBalance = transmuterBalance;\n\n        // how to earmark this window\n        uint256 amount = ITransmuter(transmuter).queryGraph(lastEarmarkBlock + 1, block.number);\n\n        // apply cover\n        uint256 coverInDebt = convertYieldTokensToDebt(_pendingCoverShares);\n        if (amount != 0 && coverInDebt != 0) {\n            uint256 usedDebt = amount > coverInDebt ? coverInDebt : amount;\n            amount -= usedDebt;\n\n            // consume the corresponding portion of pending cover shares so we can't reuse it\n            uint256 sharesUsed = FixedPointMath.mulDivUp(_pendingCoverShares, usedDebt, coverInDebt);\n            if (sharesUsed > _pendingCoverShares) sharesUsed = _pendingCoverShares;\n            _pendingCoverShares -= sharesUsed;\n        }\n\n        uint256 liveUnearmarked = totalDebt - cumulativeEarmarked;\n        if (amount > liveUnearmarked) amount = liveUnearmarked;\n\n        if (amount > 0 && liveUnearmarked != 0) {\n            // ratioWanted = (liveUnearmarked - amount) / liveUnearmarked\n            uint256 ratioWanted =\n                (amount == liveUnearmarked) ? 0 : FixedPointMath.divQ128(liveUnearmarked - amount, liveUnearmarked);\n\n            uint256 packedOld = _earmarkWeight;\n            (uint256 packedNew, uint256 ratioApplied, uint256 oldIndex, uint256 newEpoch, bool epochAdvanced) =\n                _simulateEarmarkPackedUpdate(packedOld, ratioWanted);\n\n            _earmarkWeight = packedNew;\n\n            // Survival increment uses the APPLIED earmark fraction\n            uint256 earmarkedFraction = ONE_Q128 - ratioApplied;\n            _survivalAccumulator += FixedPointMath.mulQ128(oldIndex, earmarkedFraction);\n\n            if (epochAdvanced) {\n                _earmarkEpochStartRedemptionWeight[newEpoch] = _redemptionWeight;\n                _earmarkEpochStartSurvivalAccumulator[newEpoch] = _survivalAccumulator;\n            }\n\n            // Bump cumulativeEarmarked by the effective amount implied by ratioApplied\n            uint256 newUnearmarked = FixedPointMath.mulQ128(liveUnearmarked, ratioApplied);\n            uint256 effectiveEarmarked = liveUnearmarked - newUnearmarked;\n\n            cumulativeEarmarked += effectiveEarmarked;\n        }\n\n        lastEarmarkBlock = block.number;\n    }\n\n    /// @dev Projects an account through one sync, including one simulated pending earmark window.\n    ///\n    /// @param tokenId The account id to project.\n    ///\n    /// @return The projected debt after sync.\n    /// @return The projected earmarked debt after sync.\n    /// @return The projected collateral remaining after realized redemptions are applied.\n    function _calculateUnrealizedDebt(uint256 tokenId)\n        internal\n        view\n        returns (uint256, uint256, uint256)\n    {\n        Account storage account = _accounts[tokenId];\n\n        // Simulate one uncommitted earmark window and use its simulated weight.\n        (uint256 earmarkWeightCopy,) = _simulateUnrealizedEarmark();\n\n        // First, compute account state against committed globals only.\n        (uint256 newDebt, uint256 newEarmarked, uint256 redeemedTotalSim) =\n            _computeUnrealizedAccount(account, _earmarkWeight, _redemptionWeight, _survivalAccumulator);\n\n        // Then, apply the simulated earmark-only delta.\n        // Important: this prospective earmark happens \"now\", so historical redemptions must not\n        // reduce debt again through this simulated step.\n        if (earmarkWeightCopy != _earmarkWeight) {\n            uint256 exposure = newDebt > newEarmarked ? newDebt - newEarmarked : 0;\n            if (exposure != 0) {\n                uint256 unearmarkedRatio = _earmarkSurvivalRatio(_earmarkWeight, earmarkWeightCopy);\n                uint256 unearmarkedRemaining = FixedPointMath.mulQ128(exposure, unearmarkedRatio);\n                uint256 newlyEarmarked = exposure - unearmarkedRemaining;\n                newEarmarked += newlyEarmarked;\n                if (newEarmarked > newDebt) newEarmarked = newDebt;\n            }\n        }\n\n        // Calculate collateral to remove from fees and redemptions\n        uint256 collateralBalanceCopy = account.collateralBalance;\n        uint256 globalDebtDelta = _totalRedeemedDebt - account.lastTotalRedeemedDebt;\n        if (globalDebtDelta != 0 && redeemedTotalSim != 0) {\n            uint256 globalSharesDelta = _totalRedeemedSharesOut - account.lastTotalRedeemedSharesOut;\n            uint256 sharesToDebit = FixedPointMath.mulDivUp(redeemedTotalSim, globalSharesDelta, globalDebtDelta);\n            if (sharesToDebit > collateralBalanceCopy) sharesToDebit = collateralBalanceCopy;\n            collateralBalanceCopy -= sharesToDebit;\n        }\n\n        return (newDebt, newEarmarked, collateralBalanceCopy);\n    }\n\n    /// @dev Checks that the account owned by `tokenId` is properly collateralized.\n    /// @dev Returns true only if the account is undercollateralized\n    ///\n    /// @param tokenId The id of the account owner.\n    function _isUnderCollateralized(uint256 tokenId) internal view returns (bool) {\n        uint256 debt = _accounts[tokenId].debt;\n        if (debt == 0) return false;\n\n        // totalValue(tokenId) is already denominated in debt-token units\n        uint256 collateralValue = totalValue(tokenId);\n\n        // Required collateral value = ceil(debt * minCollat / 1e18)\n        uint256 required = FixedPointMath.mulDivUp(debt, minimumCollateralization, FIXED_POINT_SCALAR);\n\n        return collateralValue < required;\n    }\n\n    /// @dev Converts tracked MYT shares into underlying units.\n    /// @return totalUnderlyingValue The underlying value represented by `_mytSharesDeposited`.\n    function _getTotalUnderlyingValue() internal view returns (uint256 totalUnderlyingValue) {\n        uint256 yieldTokenTVLInUnderlying = convertYieldTokensToUnderlying(_mytSharesDeposited);\n        totalUnderlyingValue = yieldTokenTVLInUnderlying;\n    }\n\n    /// @dev Returns the underlying value that must remain locked to support live debt, capped by tracked MYT.\n    function _getTotalLockedUnderlyingValue() internal view returns (uint256) {\n        uint256 required = _requiredLockedShares();\n\n        // Cap by actual shares held in the Alchemist\n        uint256 held = _mytSharesDeposited;\n\n        uint256 lockedShares = required > held ? held : required;\n        return convertYieldTokensToUnderlying(lockedShares);\n    }\n    /// @dev Returns true if issued synthetics exceed global backing.\n    ///      Backing mirrors Transmuter claim math:\n    ///      locked collateral in Alchemist + MYT shares currently held by Transmuter.\n    function _isProtocolInBadDebt() internal view returns (bool) {\n        if (totalSyntheticsIssued == 0) return false;\n\n        uint256 transmuterShares = TokenUtils.safeBalanceOf(myt, address(transmuter));\n        uint256 backingUnderlying = _getTotalLockedUnderlyingValue() + convertYieldTokensToUnderlying(transmuterShares);\n        uint256 backingDebt = normalizeUnderlyingTokensToDebt(backingUnderlying);\n\n        return totalSyntheticsIssued > backingDebt;\n    }\n\n    /// @dev Calculates locked collateral based on share price\n    function _requiredLockedShares() internal view returns (uint256) {\n        if (totalDebt == 0) return 0;\n\n        uint256 debtShares = convertDebtTokensToYield(totalDebt);\n        return FixedPointMath.mulDivUp(debtShares, minimumCollateralization, FIXED_POINT_SCALAR);\n    }\n\n    // Survival ratio of *unearmarked* exposure between two packed earmark states.\n    function _earmarkSurvivalRatio(uint256 oldPacked, uint256 newPacked) internal pure returns (uint256) {\n        if (newPacked == oldPacked) return ONE_Q128;\n        if (oldPacked == 0) return ONE_Q128; // uninitialized snapshot => assume \"no change\"\n\n        uint256 oldEpoch = oldPacked >> _EARMARK_INDEX_BITS;\n        uint256 newEpoch = newPacked >> _EARMARK_INDEX_BITS;\n\n        // Epoch advanced => old unearmarked was fully earmarked at some point.\n        if (newEpoch > oldEpoch) return 0;\n\n        uint256 oldIdx = oldPacked & _EARMARK_INDEX_MASK;\n        uint256 newIdx = newPacked & _EARMARK_INDEX_MASK;\n\n        if (oldIdx == 0) return 0;\n\n        return FixedPointMath.divQ128(newIdx, oldIdx);\n    }\n\n    /// @dev Computes redemption survival ratio between two redemption weights.\n    ///      Uses division when survivals are representable, and falls back to delta-weight\n    ///      when SurvivalFromWeight() underflows to 0 for both endpoints.\n    function _redemptionSurvivalRatio(uint256 oldPacked, uint256 newPacked) internal pure returns (uint256) {\n        if (newPacked == oldPacked) return ONE_Q128;\n        if (oldPacked == 0) return ONE_Q128;\n\n        uint256 oldEpoch = oldPacked >> _REDEMPTION_INDEX_BITS;\n        uint256 newEpoch = newPacked >> _REDEMPTION_INDEX_BITS;\n\n        // If epoch advances, there was a full wipe at some point\n        if (newEpoch > oldEpoch) return 0;\n\n        uint256 oldIndex = oldPacked & _REDEMPTION_INDEX_MASK;\n        uint256 newIndex = newPacked & _REDEMPTION_INDEX_MASK;\n\n        // If oldIndex is 0, treat as fully redeemed.\n        if (oldIndex == 0) return 0;\n\n        // ratio = newIndex / oldIndex\n        return FixedPointMath.divQ128(newIndex, oldIndex);\n    }\n\n    /// @dev Simulates one uncommitted earmark window using current on-chain state.\n    /// @return earmarkWeightCopy Simulated earmark packed weight after the window.\n    /// @return effectiveEarmarked The additional earmarked debt from this simulated window.\n    function _simulateUnrealizedEarmark() internal view returns (uint256 earmarkWeightCopy, uint256 effectiveEarmarked) {\n        earmarkWeightCopy = _earmarkWeight;\n        if (block.number <= lastEarmarkBlock || totalDebt == 0) return (earmarkWeightCopy, 0);\n\n        uint256 transmuterBalance = TokenUtils.safeBalanceOf(myt, address(transmuter));\n\n        // simulate pending cover\n        uint256 pendingCover = _pendingCoverShares;\n        if (transmuterBalance > lastTransmuterTokenBalance) {\n            pendingCover += (transmuterBalance - lastTransmuterTokenBalance);\n        }\n\n        // simulate earmark amount for this window\n        uint256 amount = ITransmuter(transmuter).queryGraph(lastEarmarkBlock + 1, block.number);\n\n        // apply cover the same way\n        uint256 coverInDebt = convertYieldTokensToDebt(pendingCover);\n        if (amount != 0 && coverInDebt != 0) {\n            uint256 usedDebt = amount > coverInDebt ? coverInDebt : amount;\n            amount -= usedDebt;\n        }\n\n        uint256 liveUnearmarked = totalDebt - cumulativeEarmarked;\n        if (amount > liveUnearmarked) amount = liveUnearmarked;\n        if (amount == 0 || liveUnearmarked == 0) return (earmarkWeightCopy, 0);\n\n        // ratioWanted = (liveUnearmarked - amount) / liveUnearmarked\n        uint256 ratioWanted =\n            (amount == liveUnearmarked) ? 0 : FixedPointMath.divQ128(liveUnearmarked - amount, liveUnearmarked);\n\n        (uint256 packedNew, uint256 ratioApplied,,,) = _simulateEarmarkPackedUpdate(earmarkWeightCopy, ratioWanted);\n        earmarkWeightCopy = packedNew;\n\n        uint256 newUnearmarked = FixedPointMath.mulQ128(liveUnearmarked, ratioApplied);\n        effectiveEarmarked = liveUnearmarked - newUnearmarked;\n    }\n\n    /// @dev Simulates the packed earmark update and returns the applied survival ratio.\n    /// @param packedOld Existing packed earmark weight.\n    /// @param ratioWanted Wanted survival ratio for unearmarked debt in this step.\n    /// @return packedNew The new packed earmark weight.\n    /// @return ratioApplied The effective survival ratio that will be observed by accounts.\n    /// @return oldIndex The normalized previous index.\n    /// @return newEpoch The resulting epoch after this step.\n    /// @return epochAdvanced Whether the update crossed an epoch boundary.\n    function _simulateEarmarkPackedUpdate(uint256 packedOld, uint256 ratioWanted)\n        internal\n        pure\n        returns (uint256 packedNew, uint256 ratioApplied, uint256 oldIndex, uint256 newEpoch, bool epochAdvanced)\n    {\n        uint256 oldEpoch = packedOld >> _EARMARK_INDEX_BITS;\n        oldIndex = packedOld & _EARMARK_INDEX_MASK;\n\n        if (packedOld == 0) {\n            oldIndex = ONE_Q128;\n        }\n        if (oldIndex == 0) {\n            oldEpoch += 1;\n            oldIndex = ONE_Q128;\n        }\n\n        newEpoch = oldEpoch;\n        uint256 newIndex;\n\n        if (ratioWanted == 0) {\n            newEpoch += 1;\n            newIndex = ONE_Q128;\n        } else {\n            newIndex = FixedPointMath.mulQ128(oldIndex, ratioWanted);\n        }\n\n        epochAdvanced = newEpoch > oldEpoch;\n        packedNew = _packRed(newEpoch, newIndex);\n        ratioApplied = epochAdvanced ? 0 : FixedPointMath.divQ128(newIndex, oldIndex);\n    }\n\n    // Bitwise helpers\n    function _redEpoch(uint256 packed) private pure returns (uint256) {\n        return packed >> _REDEMPTION_INDEX_BITS;\n    }\n\n    function _redIndex(uint256 packed) private pure returns (uint256) {\n        return packed & _REDEMPTION_INDEX_MASK;\n    }\n\n    function _packRed(uint256 epoch, uint256 index) private pure returns (uint256) {\n        return (epoch << _REDEMPTION_INDEX_BITS) | index;\n    }\n\n}\n\n\n\n\n","deployed_bytecode":"0x60806040526004361015610011575f80fd5b5f3560e01c8062f5271c1461051f5780630633b14a1461051a5780630c199dd5146105155780630e18b6811461051057806313bf77e61461050b57806314e29b0d1461050657806316c140e91461050157806317931901146104fc57806319815a32146104f75780632495a599146104f2578063249d39e9146104ed57806326782247146104e857806328214f44146104e357806329935820146104de5780632b8a1c5a146104d95780632eb5d4f4146104d457806332145f90146104cf57806333196d92146104ca5780633585c955146104c557806336214144146104c057806339a51be5146104bb578063415f1240146104b657806342a39ec3146104b157806345b01c1a146104ac57806346877b1a146104a75780634dd18bf5146104a25780634fd52c6e1461049d57806353e5bce61461049857806354fd4d5014610493578063579f07b01461048e5780635b972a51146104895780635c0ce2cc1461048457806360da3e831461047f5780636528ea011461047a5780637239d2b914610475578063738b62e514610470578063787dce3d1461046b5780637ecc0002146104665780637fd7f0f114610461578063807ad1141461045c5780638091e4081461045757806386651203146104525780638c46f1761461044d57806392b6630d1461044857806394cb6e7e146104435780639bf6bf471461043e578063a287fbbe14610439578063a487f67714610434578063a623fda51461042f578063a64a256f1461042a578063b0e21e8a14610425578063b390c0ab14610420578063b60b4ed61461041b578063b68ef55914610416578063ba2f55e014610411578063bc157ac11461040c578063c2da6f6e14610407578063c886391d14610402578063ca0d66b2146103fd578063cc3c1d54146103f8578063cf64add8146103f3578063d8aed145146103ee578063d97b6b15146103e9578063db006a75146103e4578063dbd5edc7146103df578063de3bea57146103da578063deb3d8a7146103d5578063e1d5201e146103d0578063e21c90ab146103cb578063e63697c8146103c6578063e71c531b146103c1578063e7d3fe6b146103bc578063e8b6571b146103b7578063eaa968e5146103b2578063f2744a48146103ad578063f3803f7f146103a8578063f431116e146103a3578063f851a4401461039e578063f8d8989814610399578063faf2bf73146103945763fc7b9c180361052e57611423565b6113fd565b6113e2565b6113bc565b611397565b611371565b61134e565b611335565b61131a565b6112f6565b6112ab565b611284565b611268565b611250565b611238565b611220565b611205565b6111df565b6111c3565b61114d565b611132565b611117565b6110f1565b6110cb565b6110a5565b611076565b611048565b61102d565b611015565b610ff9565b610fbb565b610f95565b610f2b565b610f04565b610eec565b610ed4565b610ebc565b610ea4565b610e89565b610e71565b610e56565b610e30565b610e0a565b610de6565b610d9e565b610d86565b610d57565b610d30565b610cf2565b610cae565b610c0d565b610bf2565b610bcb565b610a8e565b610a68565b610a50565b610a38565b610a1d565b6109fa565b6109cf565b610992565b61096c565b610946565b61092e565b6108f2565b6108d7565b6108be565b61086e565b610856565b61083b565b610815565b6107da565b610786565b61076b565b61072a565b610704565b6106e9565b6106c1565b6106a6565b610651565b61055c565b5f91031261052e57565b5f80fd5b61053d916008021c81565b90565b9061053d9154610532565b61053d5f6012610540565b9052565b565b3461052e5761056c366004610524565b61058861057761054b565b6040515b9182918290815260200190565b0390f35b73ffffffffffffffffffffffffffffffffffffffff1690565b6105ae8161058c565b0361052e57565b9050359061055a826105a5565b9060208282031261052e5761053d916105b5565b61058c61053d61053d9273ffffffffffffffffffffffffffffffffffffffff1690565b61053d906105d6565b61053d906105f9565b9061061590610602565b5f5260205260405f2090565b61053d916008021c5b60ff1690565b9061053d9154610621565b61053d9061064c6019915f9261060b565b610630565b3461052e5761058861066c6106673660046105c2565b61063b565b6040515b91829182901515815260200190565b806105ae565b9050359061055a8261067f565b9060208282031261052e5761053d91610685565b3461052e576105886105776106bc366004610692565b611486565b3461052e576106d1366004610524565b6106d9611500565b604051005b61053d5f600d610540565b3461052e576106f9366004610524565b6105886105776106de565b3461052e5761058861057761071a366004610692565b61161f565b61053d5f600f610540565b3461052e5761073a366004610524565b61058861057761071f565b61053d61053d61053d9290565b61053d670de0b6b3a7640000610745565b61053d610752565b3461052e5761077b366004610524565b610588610577610763565b3461052e576106d9610799366004610692565b611799565b61053d916008021c61058c565b9061053d915461079e565b61053d5f60156107ab565b6105569061058c565b60208101929161055a91906107c1565b3461052e576107ea366004610524565b6105886107f56107b6565b6040515b918291826107ca565b61053d612710610745565b61053d610802565b3461052e57610825366004610524565b61058861057761080d565b61053d5f60186107ab565b3461052e5761084b366004610524565b6105886107f5610830565b3461052e576106d9610869366004610692565b6117eb565b3461052e576106d9610881366004610692565b611852565b8015156105ae565b9050359061055a82610886565b919060408382031261052e5761053d906108b581856105b5565b9360200161088e565b3461052e576106d96108d136600461089b565b9061191a565b3461052e576105886105776108ed366004610692565b611937565b3461052e576106d9610905366004610692565b61194b565b90816101c091031261052e5790565b906101c08282031261052e5761053d9161090a565b3461052e576106d9610941366004610919565b611dbe565b3461052e57610956366004610524565b610588610577611dc7565b61053d5f6008610540565b3461052e5761097c366004610524565b610588610577610961565b61053d5f60146107ab565b3461052e576109a2366004610524565b6105886107f5610987565b90815260608101939261055a9290916040916109cb905b6020830152565b0152565b3461052e576105886109ea6109e5366004610692565b611dcf565b6040519193915b938493846109ad565b3461052e576106d9610a0d3660046105c2565b611f85565b61053d5f60036107ab565b3461052e57610a2d366004610524565b6105886107f5610a12565b3461052e576106d9610a4b3660046105c2565b611fe6565b3461052e576106d9610a633660046105c2565b612006565b3461052e57610a78366004610524565b61058861057761201c565b61053d5f600b610540565b3461052e57610a9e366004610524565b610588610577610a83565b634e487b7160e01b5f52604160045260245ffd5b90601f01601f1916810190811067ffffffffffffffff821117610adf57604052565b610aa9565b9061055a610af160405190565b9283610abd565b67ffffffffffffffff8111610adf57602090601f01601f19160190565b0190565b90610b2b610b2683610af8565b610ae4565b918252565b610b3a6005610b19565b7f332e302e30000000000000000000000000000000000000000000000000000000602082015290565b61053d610b30565b61053d610b63565b61053d610b6b565b90825f9392825e0152565b610ba7610bb0602093610b1593610b9b815190565b80835293849260200190565b95869101610b7b565b601f01601f191690565b602080825261053d92910190610b86565b3461052e57610bdb366004610524565b610588610be6610b73565b60405191829182610bba565b3461052e57610588610577610c08366004610692565b61205c565b3461052e57610588610577610c23366004610692565b612068565b909160c08284031261052e57610c3e8383610685565b92610c4c8160208501610685565b92610c5a8260408301610685565b9261053d610c6b8460608501610685565b93610c798160808601610685565b9360a001610685565b6109cb61055a94610ca7606094989795610ca0608086019a5f870152565b6020850152565b6040830152565b3461052e57610588610cd0610cc4366004610c28565b9493909392919261207c565b90610cdd94929460405190565b94859485610c82565b61053d60146018610630565b3461052e57610d02366004610524565b61058861066c610ce6565b919060408382031261052e5761053d90610d278185610685565b936020016105b5565b3461052e57610588610577610d46366004610d0d565b90612190565b61053d5f60176107ab565b3461052e57610d67366004610524565b6105886107f5610d4c565b9060208282031261052e5761053d9161088e565b3461052e576106d9610d99366004610d72565b612251565b3461052e576106d9610db1366004610692565b6122b1565b909160608284031261052e5761053d610dcf8484610685565b93610ddd81602086016105b5565b93604001610685565b3461052e576106d9610df9366004610db6565b916122ba565b61053d5f600c610540565b3461052e57610e1a366004610524565b610588610577610dff565b61053d5f60166107ab565b3461052e57610e40366004610524565b6105886107f5610e25565b61053d5f6009610540565b3461052e57610e66366004610524565b610588610577610e4b565b3461052e576106d9610e84366004610692565b6123e8565b3461052e57610588610577610e9f366004610692565b6123f1565b3461052e576106d9610eb73660046105c2565b6124b0565b3461052e576106d9610ecf366004610d72565b61253b565b3461052e576106d9610ee7366004610692565b61259d565b3461052e576106d9610eff366004610692565b6125a6565b3461052e57610588610577610f1a366004610692565b6126a3565b61053d60156018610630565b3461052e57610f3b366004610524565b61058861066c610f1f565b61062a61053d61053d9290565b61053d90610f6b610f6761053d9460ff1690565b9190565b901b90565b61053d610f7d6001610745565b610f876080610f46565b90610f53565b61053d610f70565b3461052e57610fa5366004610524565b610588610577610f8d565b61053d5f6010610540565b3461052e57610fcb366004610524565b610588610577610fb0565b919060408382031261052e5761053d90610ff08185610685565b93602001610685565b3461052e5761058861057761100f366004610fd6565b906126b7565b3461052e576106d96110283660046105c2565b612953565b3461052e5761103d366004610524565b61058861057761295c565b3461052e5761058861057761105e366004610692565b612966565b90815260408101929161055a9160200152565b3461052e5761108f611089366004610db6565b916129ab565b9061058861109c60405190565b92839283611063565b3461052e576110b5366004610524565b610588610577612b92565b61053d5f60136107ab565b3461052e576110db366004610524565b6105886107f56110c0565b61053d5f6005610540565b3461052e57611101366004610524565b6105886105776110e6565b61053d5f6004610540565b3461052e57611127366004610524565b61058861057761110c565b3461052e57610588610577611148366004610692565b612b9a565b3461052e57610588610577611163366004610fd6565b90612c59565b909182601f8301121561052e5781359167ffffffffffffffff831161052e57602001926020830284011161052e57565b9060208282031261052e57813567ffffffffffffffff811161052e576111bf9201611169565b9091565b3461052e576105886109ea6111d9366004611199565b90612e3a565b3461052e576105886105776111f5366004610692565b6132f1565b61053d5f6006610540565b3461052e57611215366004610524565b6105886105776111fa565b3461052e576106d9611233366004610692565b613352565b3461052e576106d961124b366004610692565b6133cc565b3461052e576106d9611263366004610692565b61342d565b3461052e5761058861057761127e366004610d0d565b90613436565b3461052e5761058861057761129a366004610db6565b91613602565b61053d5f6007610540565b3461052e576112bb366004610524565b6105886105776112a0565b909160608284031261052e5761053d6112df8484610685565b936112ed8160208601610685565b936040016105b5565b3461052e576106d96113093660046112c6565b91613787565b61053d5f600a610540565b3461052e5761132a366004610524565b61058861057761130f565b3461052e576106d96113483660046112c6565b91613822565b3461052e576106d9611361366004610692565b613907565b61053d5f6011610540565b3461052e57611381366004610524565b610588610577611366565b61053d5f60016107ab565b3461052e576113a7366004610524565b6105886107f561138c565b61053d5f806107ab565b3461052e576113cc366004610524565b6105886107f56113b2565b61053d5f60026107ab565b3461052e576113f2366004610524565b6105886107f56113d7565b3461052e576105886109ea611413366004610692565b613910565b61053d5f600e610540565b3461052e57611433366004610524565b610588610577611418565b61053d9081565b61053d905461143e565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b8115611481570490565b61144f565b61053d906114946004611445565b90611477565b61053d9061058c565b61053d905461149a565b61058c61053d61053d9290565b61053d906114ad565b9073ffffffffffffffffffffffffffffffffffffffff905b9181191691161790565b906114f561053d6114fc92610602565b82546114c3565b9055565b61152e61150d60186114a3565b61152761152161151c5f6114ba565b61058c565b9161058c565b141561391d565b3361153f61152161151c60186114a3565b036115e05761155761155160186114a3565b5f6114e5565b61156a6115635f6114ba565b60186114e5565b6115735f6114a3565b6115a17f54e4612788f90384e6843298d7854436f3a585b2c3831ab66abf1de63bfa6c2d916107f960405190565b0390a16115ad5f6114ba565b6115db7fa728e84b447788a55ff664fbfb5c3983925f88b80b672a1b0b8271c94b22df35916107f960405190565b0390a1565b6282b42960e81b5f9081528060045b0390fd5b9050519061055a8261067f565b9060208282031261052e5761053d916115f3565b6040513d5f823e3d90fd5b602061166e9161162c5f90565b5061164261163d61163d60036114a3565b610602565b6116606307a2d13a61165360405190565b9586948593849360e01b90565b835260048301526024820190565b03915afa9081156116ab575f91611683575090565b61053d915060203d81116116a4575b61169c8183610abd565b810190611600565b503d611692565b611614565b61055a906116bc613924565b6116e1565b905f19906114db565b906116da61053d6114fc92610745565b82546116c1565b6116fa816116f3610f6761053d610752565b101561393b565b8061170b610f6761053d600c611445565b1115611790575061172661171f600c611445565b600a6116ca565b611730600a611445565b611740610f6761053d600d611445565b1161177e575b611750600a611445565b6115db7f6f428e543b1146a0996303f0250c6fe0682874c690989e63ae53bf75bed8ef6f9161057b60405190565b61178b61171f600d611445565b611746565b6117269061171f565b61055a906116b0565b61055a906117ae613953565b6117ce565b91906117be565b9290565b82039182116117c957565b611463565b6117e461055a916117df600f611445565b6117b3565b600f6116ca565b61055a906117a2565b61055a90611800613924565b61181981611812610f6761053d610802565b111561393b565b6118248160126116ca565b6115db7f696a162ca55c8b2fc0ae1a749323963295a866dcc8be8808f85c2b0858a782eb9161057b60405190565b61055a906117f4565b9061055a91611868613924565b6118b4565b9060ff906114db565b151590565b9061188b61053d6114fc92611876565b825461186d565b91602061055a9294936118ac60408201965f8301906107c1565b019015159052565b6118ce816118c761152161151c5f6114ba565b141561393b565b6118e2826118dd83601961060b565b61187b565b907fc4459d655e1efe034a75446f1f4db087f75d23bdfabccffafa6384cdb23a1544916115db61191160405190565b92839283611892565b9061055a9161185b565b818102929181159184041417156117c957565b61053d906119456004611445565b90611924565b61055a9061195881613964565b613991565b61053d9060401c61062a565b61053d905461195d565b61053d905b67ffffffffffffffff1690565b61053d9054611973565b61197861053d61053d9290565b9067ffffffffffffffff906114db565b61197861053d61053d9267ffffffffffffffff1690565b906119d361053d6114fc926119ac565b825461199c565b9068ff00000000000000009060401b6114db565b906119fe61053d6114fc92611876565b82546119da565b6105569061198f565b60208101929161055a9190611a05565b611a266139a2565b90611a37611a3383611969565b1590565b90611a4183611985565b80611a5c611a4e5f61198f565b9167ffffffffffffffff1690565b1480611b3f575b90611a71611a4e600161198f565b1480611b1d575b90155b9081611b14575b50611b0357611aa890611a9e611a98600161198f565b856119c3565b82611af357611b89565b611aaf5750565b611aba905f906119ee565b60016115db7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d291611aea60405190565b91829182611a0e565b611afe6001856119ee565b611b89565b63f92ee8a960e01b5f908152600490fd5b1590505f611a82565b50611a7b611b2a30610602565b3b611b37610f675f610745565b149050611a78565b5082611a63565b3561053d8161067f565b3561053d816105a5565b60ff908116911690039060ff82116117c957565b60ff16604d81116117c957600a0a90565b9061061590610745565b611d5e6101a0611d6592611bb1611ba36101208301611b46565b611812610f6761053d610802565b611bc1611ba36101608301611b46565b611bd1611ba36101808301611b46565b611bf2611be060e08301611b46565b6116f3610f6761053d60808601611b46565b611c08611c0160208301611b50565b60026114e5565b611c1e611c1760408301611b50565b60156114e5565b611c5c611c55611c50611c3b611c3660208601611b50565b613a22565b611c4a611c3660408701611b50565b90611b5a565b611b6e565b60046116ca565b611c72611c6b60608301611b46565b60066116ca565b611c8161171f60808301611b46565b611c97611c9060a08301611b46565b600c6116ca565b611cad611ca660c08301611b46565b600b6116ca565b611cc3611cbc60e08301611b46565b600d6116ca565b611ccf61155182611b50565b611ce6611cdf6101008301611b50565b60176114e5565b611cfd611cf66101208301611b46565b60106116ca565b611d14611d0d6101408301611b50565b60146114e5565b611d2b611d246101608301611b46565b60116116ca565b611d42611d3b6101808301611b46565b60126116ca565b611d4d4360076116ca565b611d584360086116ca565b01611b50565b60036114e5565b611d77611d70610f70565b601d6116ca565b611d89611d82610f70565b601c6116ca565b611da6611d96601d611445565b611da15f6022611b7f565b6116ca565b61055a611db3601e611445565b611da15f6023611b7f565b61055a90611a1e565b61053d613aee565b611dd881613964565b611dee6001611de8836021611b7f565b01611445565b91611df882613b51565b9490939082611e09610f675f610745565b118015611e71575b8015611e5d575b918215611e3c575b505015611e2b579290565b63f478bcad60e01b5f908152600490fd5b611e559192506117ba6001611de8610f67936021611b7f565b105f80611e20565b5085611e6b610f675f610745565b11611e18565b5084611e7f610f675f610745565b11611e11565b61055a90611e91613924565b611eb7565b9050519061055a826105a5565b9060208282031261052e5761053d91611e96565b611ee06020611ec861163d84610602565b63fc0c546a90611ed760405190565b93849260e01b90565b825260049082905afa9081156116ab575f91611f57575b50611f0861152161151c60156114a3565b03611f4657611f188160016114e5565b6115db7ff4259e02769887f4a91cc0918b184c4624757024889632563e716c7dc7953db8916107f960405190565b632b632fa960e01b5f908152600490fd5b611f78915060203d8111611f7e575b611f708183610abd565b810190611ea3565b5f611ef7565b503d611f66565b61055a90611e85565b61055a90611f9a613924565b611fad816118c761152161151c5f6114ba565b611fb88160146114e5565b6115db7f76203fab169061f503a4139455a7dfc20cc2d5a59c97d5bb810e431f4059aad7916107f960405190565b61055a90611f8e565b61055a90611ffb613924565b6115ad8160186114e5565b61055a90611fef565b919082018092116117c957565b612026600e611445565b612032610f675f610745565b146120535761053d612042613dea565b905061204e6005611445565b61200f565b61053d5f610745565b61053d90600190614010565b6108ed61053d916120765f90565b5061161f565b93949092946120885f90565b505f958585101561216e571061214d576120cf6120c86120c06120b86120d7946120b38989906117b3565b611924565b611494610802565b9586906117b3565b9383611924565b611494610752565b8281111561211d576121016120f261210f9461210a936117b3565b926120fb610752565b906117b3565b91611945610752565b611477565b9161211a828461200f565b93565b50505050505f905f915f9161053d61214761214161213b5f95610745565b96610745565b94610745565b92610745565b9050816120b89294506121609350611924565b9091829190610f675f610745565b505082612182939495506120b89250611924565b9192909190610f675f610745565b61053d916121c66121b061053d6121cb946121a85f90565b506021611b7f565b6121c0600d600c83019201611445565b90611b7f565b61060b565b611445565b61055a906121dc61407b565b612218565b9074ff00000000000000000000000000000000000000009060a01b6114db565b9061221161053d6114fc92611876565b82546121e1565b612223816018612201565b6115db7f5ef34f53269a04b5b817e5be24a51ba6b477118b399ec9dd5b2baf5b299563dc9161067060405190565b61055a906121d0565b61055a90612266613924565b61227881611812610f6761053d610802565b6122838160106116ca565b6115db7fd10d75876659a287a59a6ccfa2e3fff42f84d94b542837acd30bc184d562de409161057b60405190565b61055a9061225a565b916122f29260206122d161163d61163d60136114a3565b636352211e9061166084926122e560405190565b9889948593849360e01b90565b03915afa9384156116ab5761055a94612314915f9161231c575b5033906140b5565b9190916140da565b612334915060203d8111611f7e57611f708183610abd565b5f61230c565b61055a90612346613924565b61239081602061235c61163d61163d60036114a3565b6370a082319061238561236e30610602565b9261237860405190565b9687948593849360e01b90565b8352600483016107ca565b03915afa80156116ab57610f676116f3916123af945f91611683575090565b6123ba8160066116ca565b6115db7f333b26cca69716ad4680ddb07663f5bfb4f06045671f336af9a83690a3ae00f99161057b60405190565b61055a9061233a565b61242761241d61240b612415936124055f90565b5061414b565b9491905093612068565b611945610752565b611494600a611445565b908181111561243b57505061053d5f610745565b61053d916117b3565b61055a90612450613924565b8061246061152161151c5f6114ba565b1461249f5761246f60136114a3565b61247e61152161151c5f6114ba565b0361248e5761055a9060136114e5565b633e7b157360e21b5f908152600490fd5b636d5ba3c360e11b5f908152600490fd5b61055a90612444565b61055a906124c561407b565b612502565b9075ff0000000000000000000000000000000000000000009060a81b6114db565b906124fb61053d6114fc92611876565b82546124ca565b61250d8160186124eb565b6115db7f3dd464fe54464bc196eadd576785c44f071a8179dc0d9758e836eed88daaf3bd9161067060405190565b61055a906124b9565b61055a90612550613924565b612564816116f3610f6761053d600a611445565b61256f81600c6116ca565b6115db7fc11eea583cef3e01cdb6c3d7a8d3c1f1304208651290ca8ecb9b3eec265082d09161057b60405190565b61055a90612544565b336125b761152161151c60136114a3565b03612627575b6125eb6125ca6001610745565b6125e5600d6125da856021611b7f565b019161204e83611445565b906116ca565b6126157fa4cd6dfb26d61208c2461ddd06b17e0168e102fde3c1988ed117d3ecfe77f73f91610745565b9061261f60405190565b80805b0390a2565b612650602061263c61163d61163d60136114a3565b636352211e90611660859261165360405190565b03915afa9081156116ab575f91612685575b50612670611521339261058c565b146125bd576282b42960e81b5f908152600490fd5b61269d915060203d8111611f7e57611f708183610abd565b5f612662565b61105e61053d916126b15f90565b50611486565b6126cd816126c7610f675f610745565b1161393b565b6126d682613964565b436126ed610f6761053d6007611de8876021611b7f565b146128ea57612746906126fe6142c3565b6127078361458f565b61272a61271a6001611de8866021611b7f565b6120fb6002611de8876021611b7f565b906127418261273b610f675f610745565b1161391d565b6146e0565b9081612754610f675f610745565b146128df5781612791612767600f611445565b602061277961163d61163d60176114a3565b63568914129061278860405190565b94859260e01b90565b825260049082905afa9182156116ab576127ba92610f679261053d925f926128bf575b506117b3565b11612857576127d46127cc60026114a3565b338491614750565b6127de82826147fc565b6127f54360086127ef846021611b7f565b016116ca565b6128066117e4836117df600f611445565b61280f8161485c565b81907f49995e5dd6158cf69ad3e9777c46755a1a826a446c6416992167462dad033b2a61285161284761284133610602565b93610745565b9361057b60405190565b0390a390565b50612865612767600f611445565b825260049082905afa9081156116ab57612885925f9261289f57506117b3565b636a16435b60e11b5f9081529182916115ef916004611063565b6128b891925060203d81116116a45761169c8183610abd565b90846127b4565b6128d891925060203d81116116a45761169c8183610abd565b905f6127b4565b505061053d5f610745565b6372812ba560e01b5f908152600490fd5b61055a90612907613924565b61291a816118c761152161151c5f6114ba565b6129258160166114e5565b6115db7f3ef741755291c333a73ae7a92a463d54beeeae3e3d5dd55ebd822fda4d2176b3916107f960405190565b61055a906128fb565b61053d601f611445565b602061166e916129735f90565b5061298461163d61163d60036114a3565b61166063c6e6f59261165360405190565b61053d9060a01c61062a565b61053d9054612995565b92916129c0826118c761152161151c5f6114ba565b6129d0846126c7610f675f610745565b6129e56129e0611a3360186129a1565b61391d565b6129f36129e0611a3361487c565b612a21612a0a612a03601f611445565b869061200f565b612a1a610f6761053d6006611445565b111561391d565b80612a2e610f675f610745565b03612b6e5750612a6c906020612a4a61163d61163d60136114a3565b636a627842906123855f8593612a5f60405190565b9788958694859360e01b90565b03925af180156116ab5761053d925f91612b50575b509081612ab7612ab17f80f4d65d99e602916903d8040ce9e4bdfb03957f3c2fd761375e6c21c7ace03693610602565b91610745565b91612ac160405190565b5f90a35b612ad8846125e55f6125da856021611b7f565b612af7612ae560036114a3565b33612aef30610602565b9087926148fc565b612b0f612b088561204e601f611445565b601f6116ca565b837fa3af609bf46297028ce551832669030f9effef2b02606d02cbbcc40fe6b47c55612b47612b3d84610745565b9261057b60405190565b0390a292612068565b612b68915060203d81116116a45761169c8183610abd565b5f612a81565b61053d9150612b7c81613964565b612b846142c3565b612b8d8161458f565b612ac5565b61053d61492a565b612ba4905f612405565b919050612bb05f610745565b9080612bbe610f675f610745565b03612c2d575b5080821115612c1d57612bd6916117b3565b612bde61498a565b612be8601f611445565b811015612c0e57612bfd906117df601f611445565b80821015612c09575090565b905090565b50612c185f610745565b612bfd565b5050612c285f610745565b612bd6565b612c539150612c3b906126a3565b612c45600a611445565b612c4d610752565b91614937565b5f612bc4565b90612c6a826126c7610f675f610745565b612c7381613964565b612c8161053d826021611b7f565b9143612c95610f6761053d60078701611445565b146128ea57612ca26142c3565b612cab8261458f565b612cd3612cba60018501611445565b612cca8161273b610f675f610745565b61274183612068565b80612ce0610f675f610745565b14612dfe57612cef83826149b5565b93612d02612cfc836126a3565b956126a3565b612d136120b8826119456010611445565b9283612d24610f6761053d86611445565b1115612d3b57634a613c4160e01b5f908152600490fd5b612d8892612d58612d6292612d51878a90614a51565b50886147fc565b60084391016116ca565b612d82612d6f60036114a3565b33612d7a60176114a3565b9089926148fc565b85614ae0565b80612d95610f675f610745565b11612dd8575b50907f2fe77b1c99aca6b022b8efc6e3e8dd1b48b30748709339b65c50ef3263443e0983612dcb61284133610602565b9361285161109c60405190565b612df890612de660036114a3565b90612df160146114a3565b9091614b1a565b5f612d9b565b5050505061053d5f610745565b60010190565b634e487b7160e01b5f52603260045260245ffd5b9190811015612e35576020020190565b612e11565b91905f5f915f9481612e4e610f675f610745565b14612fa0575f9593929594612e625f610745565b83811015612f8e57612e7d612e78828686612e25565b611b46565b80612e8a610f675f610745565b148015612f71575b612f6357612ea66001611de8836021611b7f565b612ec6612ec6612ece612eb885613b51565b919e91949093918f9061200f565b9a839061200f565b9b612edb610f675f610745565b11908115612f4e575b50908115612f39575b50918215612f18575b5050612f0f575b612f0690612e0b565b96949396612e62565b60019650612efd565b612f319192506117ba6001611de8610f67936021611b7f565b105f80612ef6565b9050612f47610f675f610745565b115f612eed565b9050612f5c610f675f610745565b115f612ee4565b50612f069097949597612e0b565b50612f89611a33612f8260136114a3565b8390614b3c565b612e92565b50939592949150505f14611e2b579290565b63ee6a148160e01b5f908152600490fd5b9061053d91612fbe613953565b50612fc76142c3565b612fd16005611445565b908181116132ea575b612fe35f610745565b9180612ff1610f675f610745565b1415806132d5575b613118575b505061300b4360086116ca565b613014816126a3565b906130266120b8836119456010611445565b61303e6130378361204e601a611445565b601a6116ca565b61305661304f8461204e601b611445565b601b6116ca565b61307461306360036114a3565b61306d60176114a3565b8591614b1a565b613085612b08846117df601f611445565b80613096610f6761053d601f611445565b11156130d2575b506130cc7fe27d271547ea98e824607f6d87e0be0fbedc1fa82418a245c86146cb4c9e0ae39161057b60405190565b0390a190565b61304f613112916130f76130e660036114a3565b6130f060146114a3565b8391614b1a565b613108612b08826117df601f611445565b61204e601b611445565b5f61309d565b90915061312582826117b3565b80613135610f6762503341610745565b116132c557506131445f610745565b61314e601d611445565b61315781614c78565b9161316182614cc3565b9161316e610f675f610745565b146132b5575b81613181610f675f610745565b14613296575b829080613196610f675f610745565b0361327d57506131b0906131aa6001610745565b9061200f565b916131ce610f676131bf610f70565b945b6117ba611d708783614d6a565b11156132685750506132116132096131e55f610745565b6132036131fc6131f5601e611445565b8390614ccf565b601e6116ca565b83614ccf565b9182906117b3565b91613228610f6761053d856131aa62503341610745565b11613257576132389060056116ca565b613250613249826117df600e611445565b600e6116ca565b5f80612ffe565b634a613c4160e01b5f908152600490fd5b6132786132099161321193614bd4565b6131e5565b610f676132906131ce9285969496614ccf565b946131c1565b916132a691506131aa6001610745565b906132af610f70565b90613187565b90506132bf610f70565b90613174565b6132d0908290614bd4565b613144565b50816132e3610f675f610745565b1415612ff9565b5080612fda565b61053d905f612fb1565b61055a90613307613924565b61331981611812610f6761053d610802565b6133248160116116ca565b6115db7f10dfca9c141b166a72d390e49a47e5202fad85c411de1306464d6edce482aecd9161057b60405190565b61055a906132fb565b61055a90613367613924565b6133818161337b610f6761053d600a611445565b1061393b565b613393816116f3610f6761053d610752565b61339e81600b6116ca565b6115db7f18c3125cf6a37e325bab627140219ddcbb229488d0df8b117a28ae3837736bfe9161057b60405190565b61055a9061335b565b61055a906133e1613953565b61055a90806133f6610f6761053d6009611445565b11613403575b60096116ca565b613428613421613417836120fb6009611445565b61204e6020611445565b60206116ca565b6133fc565b61055a906133d5565b613449826118c761152161151c5f6114ba565b61345281613964565b613467602061263c61163d61163d60136114a3565b03915afa80156116ab57613483915f9161231c575033906140b5565b61348c81613991565b6134ab61349f6001611de8846021611b7f565b61273b610f675f610745565b6134b8611a335f83614d7a565b6135db5761053d916134cc83836021611b7f565b916134e5816134dd60028601611445565b600191614dd2565b9161351e6135175f6135106135096134ff60018a01611445565b610f1a81896147fc565b8690614a51565b9601611445565b8390614a51565b908461352c610f675f610745565b116135a5575b8161353f610f675f610745565b11613588575b505061355282849061200f565b90613580612b3d7ffea4a7a7031887f4888d278f82f383a7efcc5d6d5e86922fd5089325fe8ae36d92610745565b0390a261200f565b61359e9161359660036114a3565b919091614b1a565b5f80613545565b6135b8856135b25f610745565b90614ae0565b6135d66135c560036114a3565b6135cf60176114a3565b8791614b1a565b613532565b63f69df64d60e01b5f908152600490fd5b90815260408101929161055a91602001906107c1565b90919061365b9061361c846118c761152161151c5f6114ba565b61362583613964565b613635816126c7610f675f610745565b602061364761163d61163d60136114a3565b636352211e90611660869261237860405190565b03915afa9182156116ab576136eb9261367c915f9161231c575033906140b5565b6136846142c3565b61368d8361458f565b61369c5f611de8856021611b7f565b6136ac610f6761053d601f611445565b11613753575b6136e46136dd6136ce612c3b610f1a6001611de8896021611b7f565b6117df5f611de8886021611b7f565b82906116f3565b8290614a51565b916136f58261485c565b61370a61370260036114a3565b828591614b1a565b8291906137377f71ef96c43343734b1d843bb85d52ef329f5e9143e4d35827771e3b0dd90c5f8492610745565b9261374d61374460405190565b928392836135ec565b0390a290565b61376c613760601f611445565b5f6127ef866021611b7f565b6136b2565b61053d9060a81c61062a565b61053d9054613771565b916137da9261379f826118c761152161151c5f6114ba565b6137a881613964565b6137b8836126c7610f675f610745565b6137c86129e0611a33601861377d565b60206122d161163d61163d60136114a3565b03915afa9384156116ab5761055a946137fb915f9161231c575033906140b5565b6138036142c3565b6138116129e0611a3361487c565b61381a8161458f565b919091614f2f565b9161055a92613837836126c7610f675f610745565b61384081613964565b613853826118c761152161151c5f6114ba565b6138636129e0611a33601861377d565b6137fb833383614fdf565b61055a9061387a613924565b61388c816126c7610f6761053d610752565b6138a0816116f3610f6761053d600a611445565b6138b4816126c7610f6761053d600b611445565b6138ce81611812610f6761053d60026120b3612ab1610752565b6138d981600d6116ca565b6115db7f03802a2ea67750df04454dc6298d86ea86fc78dbae9d4f342789c495b20ae79b9161057b60405190565b61055a9061386e565b61391a905f612405565b92565b1561325757565b3361393461152161151c5f6114a3565b036115e057565b1561394257565b630134249960e71b5f908152600490fd5b3361393461152161151c60176114a3565b611a3361397a9161397560136114a3565b614b3c565b61398057565b631bdf1dcd60e01b5f908152600490fd5b61055a9061399d6142c3565b61458f565b61053d61502e565b3d156139c3576139b93d610b19565b903d5f602084013e565b606090565b6139eb61053d9492936139e260608401955f8501906107c1565b15156020830152565b6040818403910152610b86565b60ff81166105ae565b9050519061055a826139f8565b9060208282031261052e5761053d91613a01565b5f80826004613a5d63313ce56760e01b613a4e613a3e60405190565b93849260208401908152015f0190565b60208201810382520382610abd565b602081019051915afa613a6e6139aa565b91803b613a7d610f675f610745565b148015613ae6575b8015613ad0575b613aab57505061053d906020613aa0825190565b818301019101613a0e565b6115ef909192613aba60405190565b63e7e40b5b60e01b8152938493600485016139c8565b508251613ae0610f676020610745565b10613a8c565b508115613a85565b61053d613af961498a565b613b03601f611445565b9081811115613b12575061161f565b905061161f565b61055690610745565b60208101929161055a9190613b19565b6040906109cb61055a94969593966109c460608401985f8501906107c1565b5f9182613b5c6142c3565b613b658361458f565b613b7361053d846021611b7f565b90613bb06020613b8961163d61163d60036114a3565b6307a2d13a90613ba5670de0b6b3a76400009261165360405190565b835260048301613b22565b03915afa9081156116ab575f91613dcc575b50613bcf610f675f610745565b14613db457613bde5f85614d7a565b613db457613beb5f610745565b91613bf860028201611445565b613c04610f675f610745565b11613d18575b50613c155f85614d7a565b15613d045780613c27610f675f610745565b11613cd1575b80613c3a610f675f610745565b1115613cad57613c4b908490614a51565b92613c61613c5960036114a3565b338691614b1a565b3390848692613ca6613c937f14ecf0ab88d32e49a8a2a6527ee7b8bb4ec899b29e8ef98ced171e4a411f343794610745565b94613c9d60405190565b93849384613b32565b0390a29290565b9284613cbb610f675f610745565b1115613c615793613ccb906155f8565b93613c61565b80613cdb85615563565b8111613ce8575b50613c2d565b945050613cfd613cf75f610745565b9461161f565b935f613ce2565b50509150613d11906150c7565b9192909190565b915050613d3283613d2b60028401611445565b5f91614dd2565b90613d3c82615036565b90613d4681611445565b613d52610f675f610745565b1480613d95575b15613c0a57613d6d6001613d729201611445565b61504f565b80613d7f610f675f610745565b1115613c0a57613d8f90856147fc565b5f613c0a565b50613da260018201611445565b613dae610f675f610745565b11613d59565b50509150505f5f9161053d6121476121415f94610745565b613de4915060203d81116116a45761169c8183610abd565b5f613bc2565b613df4601c611445565b9043613e06610f6761053d6007611445565b11158015613ff3575b61205357613e30613e2060036114a3565b613e2a60176114a3565b906157e1565b613e3a6020611445565b9080613e4c610f6761053d6009611445565b11613fdb575b50613e6361163d61163d60176114a3565b602063b5024ce891613e82613e786007611445565b6131aa6001610745565b90613ea14394613eac613e9460405190565b9687958694859460e01b90565b845260048401611063565b03915afa80156116ab57613ec8915f91613fbd575b5091612068565b81613ed5610f675f610745565b141580613fa8575b613f88575b50613efa613ef0600e611445565b6120fb6005611445565b90818111613f81575b80613f10610f675f610745565b148015613f6d575b6128df5790613f486120fb948361053d94613f33610f678690565b03613f545750613f425f610745565b90615885565b50505094909482614ccf565b613f61613f6891856117b3565b8490614bd4565b613f42565b5081613f7b610f675f610745565b14613f18565b5080613f03565b613f9b919080821115613fa157906117b3565b5f613ee2565b50806120fb565b5080613fb6610f675f610745565b1415613edd565b613fd5915060203d81116116a45761169c8183610abd565b5f613ec1565b906131aa613fed926120fb6009611445565b5f613e52565b50613ffe600e611445565b61400a610f675f610745565b14613e0f565b61053d91905f9115614054576140259061414b565b91505080614035610f675f610745565b11614041575b50611937565b906131aa61404e9261161f565b5f61403b565b6108ed91505f611de861071a926021611b7f565b61053d9061062a565b61053d9054614068565b3361408b61152161151c5f6114a3565b141580614099575b6115e057565b506140b0611a336140ab33601961060b565b614071565b614093565b6115216140c2919261058c565b036140c957565b633055827b60e11b5f908152600490fd5b90919061410d6140ee61053d836021611b7f565b611da161410685926121c0600d600c83019201611445565b869061060b565b9190916141466128476141407f4ebe02d1fe6bac44c7072ea0e71bb30db417ea99ad6b6462c64af036a9e521a093610745565b93610602565b0390a3565b61053d614160916141595f90565b505f6121a8565b90614169613dea565b509061419483614179601c611445565b614183601d611445565b9061418e601e611445565b92615963565b9093806141a7610f6761053d601c611445565b0361424b575b506141b785611445565b946141d16141c5601a611445565b6120fb600a8401611445565b90816141df610f675f610745565b141580614236575b6141f5575b50919492915050565b916142209161421961422d9598946120fb600b614212601b611445565b9201611445565b9091614937565b818111613fa157906117b3565b925f80806141ec565b5082614244610f675f610745565b14156141e7565b848311156142b55761425d85846117b3565b908161426b610f675f610745565b03614277575b506141ad565b61429c9295916120fb6142966131aa93614291601c611445565b615cf5565b82614ccf565b928184116142ac575b5f80614271565b925080926142a5565b6142be5f610745565b61425d565b6142cd600e611445565b6142d9610f675f610745565b1461055a57436142ef610f6761053d6007611445565b111561055a57614316614305613e2060036114a3565b806133f6610f6761053d6009611445565b61432661163d61163d60176114a3565b602063b5024ce89161433b613e786007611445565b90613ea1439461434d613e9460405190565b03915afa9081156116ab575f91614571575b5061436d610c236020611445565b8161437a610f675f610745565b14158061455c575b6144e0575b50614395613ef0600e611445565b8082116144d8575b816143aa610f675f610745565b11806144c3575b6143c3575b505061055a4360076116ca565b614449614453918361445a946143d9610f678490565b0361449857506120fb6144336131fc61441b61443d6144096143fa5f610745565b614404601c611445565b615885565b9297909692959293909290601c6116ca565b61442d614426610f70565b88906117b3565b90614ccf565b61204e601e611445565b614461575b5082614ccf565b61204e6005611445565b60056116ca565b5f806143b6565b6144929061447d614472601d611445565b611da1836022611b7f565b611da161448a601e611445565b916023611b7f565b5f614442565b6144336131fc61441b61443d6144096144be6144b76120fb97896117b3565b8890614bd4565b6143fa565b50806144d1610f675f610745565b14156143b1565b90508061439d565b9061450c9082811115614553576144f9839182906117b3565b926145046020611445565b919091614937565b8061451d610f6761053d6020611445565b1161453a575b613421614534916117df6020611445565b5f614387565b5061453461342161454b6020611445565b915050614523565b6144f981613209565b508061456a610f675f610745565b1415614382565b614589915060203d81116116a45761169c8183610abd565b5f61435f565b6145a061053d61055a926021611b7f565b61462b6146226145b483614179601c611445565b9192916145d06145c4601a611445565b6120fb600a8901611445565b90816145de610f675f610745565b1415806146cb575b61466a575b50506146036145fa601a611445565b600a87016116ca565b614619614610601b611445565b600b87016116ca565b600285016116ca565b600183016116ca565b614641614638601c611445565b600483016116ca565b61465761464e601d611445565b600583016116ca565b6009614663601e611445565b91016116ca565b6146879161421961467b601b611445565b6120fb600b8b01611445565b80614697610f6761053d89611445565b116146b7575b6146b09086906125e5906117df83611445565b5f806145eb565b506146b06146c486611445565b905061469d565b50806146d9610f675f610745565b14156145e6565b8181111561470e57505b806146fb610f6761053d600e611445565b116147035790565b5061053d600e611445565b90506146ea565b91602061055a9294936109cb60408201965f8301906107c1565b9050519061055a82610886565b9060208282031261052e5761053d9161472f565b915f919060046147828493613a4e879463079cc67960e41b9261477260405190565b9586946020860190815201614715565b82602082019151925af16147946139aa565b91803b6147a3610f675f610745565b1480156147f4575b80156147bb575b613aab57505050565b5082516147ca610f675f610745565b141580156147b257506147ef611a338460206147e4825190565b81830101910161473c565b6147b2565b5081156147ab565b61482e9161482461481461053d613249946021611b7f565b6001016125e5836117df83611445565b6117df600e611445565b6148386005611445565b614848610f6761053d600e611445565b1161484f57565b61055a614453600e611445565b61486590615d6a565b61486b57565b63fddafdf560e01b5f908152600490fd5b614886600f611445565b614892610f675f610745565b146148cf576148bb6108ed6148aa613e2060036114a3565b6131aa6148b5613aee565b9161161f565b6148cb610f676117ba600f611445565b1190565b5f90565b6040906109cb61055a94969593966148f260608401985f8501906107c1565b60208301906107c1565b6040516323b872dd60e01b602082015290935f938493869392614782928492613a4e929190602485016148d3565b61053d61071a601f611445565b9291614944818386615db4565b939190918215611481570961495b610f675f610745565b0361496257565b905f1981146149795761053d90610b156001610745565b6349248f7760e01b5f908152600490fd5b614994600e611445565b6149a0610f675f610745565b146120535761053d612c3b610f1a600e611445565b906149c761053d614a02926121a85f90565b6149d360018201611445565b6149df60028301611445565b9381811115614a4a57505b83811115614a3f57506002614663845b9485906117b3565b80614a13610f6761053d6005611445565b1115614a335761053d614453614a296005611445565b6117df6005611445565b61053d61445382614a29565b6146636002916149fa565b90506149ea565b9061053d614a60916121a85f90565b614a6981611445565b9081614a7b610f6761053d601f611445565b11614abe575b82614aa39293614a91610f678690565b1115614ab457505f61466384806117b3565b61053d612b08826117df601f611445565b6146635f916149fa565b614aa39150614acd601f611445565b92614ad884836116ca565b929150614a81565b6133fc9061055a9281614af3610f678390565b11614b04575b5061204e6009611445565b613421613417614b1492846117b3565b5f614af9565b915f919060046147828493613a4e879463a9059cbb60e01b9261477260405190565b91905f9281614b4d610f675f610745565b14614bac57614b7591614b6461163d602093610602565b611660636352211e61165360405190565b03915afa9081614b90575b50614b8a575f9150565b60019150565b614ba79060203d8111611f7e57611f708183610abd565b614b80565b505f925050565b61053d90614bc7610f6761053d9460ff1690565b901c90565b0290565b0390565b9081614be2610f675f610745565b146128df5781614c03610f6761053d5f19614bfd6080610f46565b90614bb3565b1115614c4c5790610b1561053d9261210a614c41614c36614c25868590611477565b95614c308588614bcc565b90614bd0565b94610f876080610f46565b93610f876080610f46565b61210a61053d92610f876080610f46565b61053d6081610745565b61053d90614bc7610f6761053d9490565b61053d90614c84614c5d565b90614c67565b61053d90610f6b610f6761053d9490565b61053d614cb9614cab6001610745565b614cb3614c5d565b90614c8a565b6120fb6001610745565b614ccb614c9b565b1690565b919082614cde610f675f610745565b148015614d56575b614d4a57614d07905f1981850993029283808210910303610f876080610f46565b614d1583614bfd6080610f46565b1791614d27614cb9610f7d6001610745565b16614d34610f675f610745565b03614d3b57565b9061053d90610b156001610745565b50905061053d5f610745565b5080614d64610f675f610745565b14614ce6565b614d7690614cb3614c5d565b1790565b614d8a6001611de8836021611b7f565b614d96610f675f610745565b14614dcb576114946001611de8614db3612415614dbb9686614010565b936021611b7f565b6148cb610f6761053d600b611445565b5050600190565b909182614de1610f675f610745565b14614f1557614df390611a3383613964565b614f21575b614e24614e0e6001611de861053d856021611b7f565b614e1e8161273b610f675f610745565b836146e0565b80614e31610f675f610745565b14614f1557614e51613517614e4684846149b5565b92610f1a81866147fc565b92614e6d614e666120b8866119456010611445565b8490614a51565b92848492614ea8614e9e7f85e4a966dae695c747415c155708ec4e77481b3161efaef4593bdb0a4e79ce5994610745565b946109f160405190565b0390a282614eb8610f675f610745565b11614ee3575b5080614ecc610f675f610745565b11614ed5575090565b61053d90612de660036114a3565b614f0f90614f05614ef460036114a3565b614efe60176114a3565b8691614b1a565b6135b284916126a3565b5f614ebe565b50505061053d5f610745565b614f2a81613991565b614df8565b43614f46610f6761053d6008611de8866021611b7f565b14614fce57614f558282615efd565b614f666117e48361204e600f611445565b614f6f8161485c565b614f804360076127ef846021611b7f565b614f95614f8d60026114a3565b848491615f65565b9091614fc17f8334f87aeaf76e52b061d93ee968e51fdd3ad53ca04e80271249227997aab3a092610745565b9261262261374460405190565b63848288c360e01b5f908152600490fd5b614ffb61055a93926121c66121b061053d6125e5956021611b7f565b916117df83611445565b61053d7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610745565b61053d615005565b6120b861053d916150445f90565b506119456012611445565b80615060610f6761053d600e611445565b111561053d575061053d600e611445565b6109cb61055a9461509c606094989795615092608086019a5f8701906107c1565b6020850190613b19565b6040830190613b19565b6109cb61055a94610ca7606094989795610ca0608086019a5f8701906107c1565b5f916150d761053d836021611b7f565b916150e460018401611445565b906151336150f18261205c565b836150fc600d611445565b61511761510d6124156108ed61492a565b611494600e611445565b90615122600c611445565b9261512d6011611445565b9461207c565b909490929081615145610f675f610745565b146154385750614e66615157916126a3565b9384615165610f675f610745565b1461541d57615173906126a3565b848111156154185750835b9561518987866117b3565b9161519383612068565b61519f60018401611445565b6151af610f6761053d600e611445565b1015615409576151c160018401611445565b90808311615401575b508082116153f9575b50806151e1610f675f610745565b116153e9575b506151f460018201611445565b615200610f675f610745565b11806153d6575b6152f0575b508061521a610f675f610745565b116152c4575b508461522e610f675f610745565b11156152885750615242613c5960036114a3565b3390828592613ca6886152757f969067f3911888d4f34a9dba2e904ea5e0dcd3323ccf9692ef8f091c6a4326a595610745565b9561527f60405190565b948594856150a6565b61529181611486565b61529d610f675f610745565b116152a9575b50615242565b6152bd9195506152b890611486565b6155f8565b935f6152a3565b806152d56152ea926135b25f610745565b6152df60036114a3565b90612df160176114a3565b5f615220565b6152f981611445565b80615306610f675f610745565b1161537f575b5061531681611445565b615322610f675f610745565b1480615360575b1561520c57613d6d600161533d9201611445565b8061534a610f675f610745565b111561520c5761535a90846147fc565b5f61520c565b5061536d60018201611445565b615379610f675f610745565b11615329565b6153a161539b6153936153b3938890614a51565b94859061200f565b93612068565b6153ad60018401611445565b906146e0565b806153c0610f675f610745565b111561530c576153d090856147fc565b5f61530c565b506153e4611a335f86614d7a565b615207565b6153f390856147fc565b5f6151e7565b90505f6151d3565b91505f6151ca565b615413600e611445565b6151c1565b61517e565b50505050509150505f5f9161053d6121476121415f94610745565b945050908161545c9296939596615451610f675f610745565b116155305750611486565b80615469610f675f610745565b1161550f575b50610f676117ba60016154829301611445565b1080156154fb575b6154a357505f9150819061053d61214761214184610745565b33905f5f926154e9866154d67f969067f3911888d4f34a9dba2e904ea5e0dcd3323ccf9692ef8f091c6a4326a595610745565b956154e060405190565b94859485615071565b0390a25f9190610f6761284184610745565b5082615509610f675f610745565b1161548a565b6154829195506117ba6001615526610f67936155f8565b979350505061546f565b615540906153ad60018601611445565b8061554d610f675f610745565b11156126b15761555d90866147fc565b5f6126b1565b61053d615571916121a85f90565b61557d60018201611445565b908161558b610f675f610745565b146155d8576155a1610c235f6155ac9301611445565b91612c45600b611445565b5f1981146128df576155c2906131aa6001610745565b90818111156128df5761053d91610f1a916117b3565b61053d9150611445565b90815260408101929161055a9160200190613b19565b80615605610f675f610745565b146157d75761561460016114a3565b61562361152161151c5f6114ba565b146157ac5761564c602061563d61163d61163d60016114a3565b637d88209790611ed760405190565b825260049082905afa9081156116ab575f9161578e575b5080615671610f675f610745565b116156c457507f7781e9a51c92f9403c996e7eeb66bf0eba96c64d2272768d3e52e7cd137c672b5f6156a233610602565b926156b86156af60405190565b928392836155e2565b0390a261053d5f610745565b80821115615787575b906156de61163d61163d60016114a3565b63f3fef3a3338492803b1561052e5761570c5f80946157176156ff60405190565b9788968795869460e01b90565b845260048401614715565b03925af180156116ab5761576a575b50808210615732575090565b7f7781e9a51c92f9403c996e7eeb66bf0eba96c64d2272768d3e52e7cd137c672b8261575d33610602565b9261374d61109c60405190565b615781905f6157798183610abd565b810190610524565b5f615726565b50806156cd565b6157a6915060203d81116116a45761169c8183610abd565b5f615663565b7f7781e9a51c92f9403c996e7eeb66bf0eba96c64d2272768d3e52e7cd137c672b5f6156a233610602565b5061053d5f610745565b905f80916157ec5f90565b5060046158198592613a4e6370a0823160e01b9161580960405190565b94859360208501908152016107ca565b602081019051915afa61582a6139aa565b91803b615839610f675f610745565b14801561587d575b8015615867575b613aab57505061053d90602061585c825190565b818301019101611600565b508251615877610f676020610745565b10615848565b508115615841565b90919061589481614c84614c5d565b928161589e614c9b565b16916158ac610f675f610745565b14615953575b816158bf610f675f610745565b14615934575b8390806158d4610f675f610745565b0361592457506158e8906131aa6001610745565b6158f0610f70565b935b6158fd610f67839290565b116159088583614d6a565b948115615919575061211a5f610745565b61211a908490614bd4565b61592e9083614ccf565b936158f2565b9261594491506131aa6001610745565b9161594d610f70565b906158c5565b905061595d610f70565b906158b2565b93929061597c61597560058701611445565b8490615cf5565b61598860018701611445565b61599a610f6761053d60028a01611445565b1115615ce657856159bc6159b060018301611445565b6120fb60028401611445565b6159de6159d86132036159d160048601611445565b8790615cf5565b826117b3565b94836159ee610f6761053d610f70565b14615caa57615b279596615a0c615a0760048601611445565b614cc3565b9182615a1a610f675f610745565b14615c9a575b615a36615a2f60098701611445565b8790614ccf565b80821115615c8a57615a47916117b3565b828111615c83575b615a62615a5c8483614bd4565b85614ccf565b96615a7a615a7260048801611445565b614c84614c5d565b91615a8782614c84614c5d565b91838314615c21575b5050615a9c610f678390565b11615b55575b505050505081615ab2610f678690565b11615b4d575b615adb615ad3615af192615ace60028a01611445565b614ccf565b9483906117b3565b61204e615aea60028901611445565b86906117b3565b615afd60018701611445565b8111615b3657615aea6001615b1a615b21936117df838b01611445565b9701611445565b9261200f565b91838311615b3157565b839250565b50615b21615aea6001615b485f610745565b615b1a565b839150615ab8565b615b69919293949596506131aa6001610745565b92615b846121cb615b7c81876022611b7f565b956023611b7f565b9084615b92610f675f610745565b14615c0a5761442d61442d94615be193615bc489615ace6009614212615bbd6005615be79f01611445565b8d90615cf5565b80821115615bfa57615bd5916117b3565b818111615bf357614bd4565b92615cf5565b905b855f808080615aa2565b5080614bd4565b5050615c055f610745565b615bd5565b505050505050615c1b838290614ccf565b90615be9565b615c29614c9b565b1680861115615c6b57610f6761053d615c45615c4c93896117b3565b8b90614ccf565b14615c59575b5f80615a90565b9650615c65868a614ccf565b96615c52565b50615c4c610f6761053d615c7e5f610745565b615c45565b5081615a4f565b5050615c955f610745565b615a47565b9150615ca4610f70565b91615a20565b5050615ccd945061204e9250600291506001615cc69101611445565b9501611445565b90828211615cde5761053d5f610745565b829150612053565b85615cf05f610745565b6159bc565b808214615d605780615d09610f675f610745565b14615d6057615d1a81614c84614c5d565b615d2c610f676117ba85614c84614c5d565b116128df57615d39614c9b565b1690615d43614c9b565b169080615d52610f675f610745565b146128df5761053d91614bd4565b505061053d610f70565b615d7a6001611de8836021611b7f565b9081615d88610f675f610745565b14615dae57610f6761053d615d9f615daa9361205c565b93612c45600a611445565b1090565b50505f90565b90919081615dc4610f675f610745565b14615eec575f198382099183820292838082109103039384615de8610f675f610745565b14615ede57848211156149795761053d9482615e3692615e7f95615e095f90565b500980860395109003600183615e238119610b1584610745565b1680809504960493805f03040190614bcc565b1791615e7f615ecd615ebb615ea9615e97615e85615e5d87615e586003610745565b614bcc565b615e676002610745565b18615e7f6002615e7a612ab1848c614bcc565b614bd0565b90614bcc565b615e7f6002615e7a612ab1848b614bcc565b615e7f6002615e7a612ab1848a614bcc565b615e7f6002615e7a612ab18489614bcc565b615e7f6002615e7a612ab18488614bcc565b91615e7a612ab16002928590614bcc565b5091905061053d9250611477565b632144a53360e01b5f908152600490fd5b90615f0c61053d836021611b7f565b90615f2e615f26615f1f60018501611445565b839061200f565b935f90614010565b615f42610f6761053d86612c45600a611445565b1061486b57615f5b61324992600161055a9591016116ca565b61204e600e611445565b915f919060046147828493613a4e87946340c10f1960e01b926147726040519056fea2646970667358221220604864101ed74282ba0b35db2cdb57859cb52a0bdf98c00c512ae333378c066064736f6c634300081c0033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"cancun","metadata":{"appendCBOR":true,"bytecodeHash":"ipfs","useLiteralContent":false},"optimizer":{"details":{"yul":false},"enabled":true,"runs":800},"remappings":["\"ds-test/=\"../lib/ds-test/src/","\"forge-std/=\"lib/forge-std/src/","\"@permit2\"/=\"lib/permit2/src/\"/","@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","chainlink-brownie-contracts/=lib/chainlink-brownie-contracts/","ds-test/=lib/solmate/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-gas-snapshot/=lib/permit2/lib/forge-gas-snapshot/src/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/","permit2/=lib/permit2/","solmate/=lib/solmate/src/","v2-foundry/=lib/v2-foundry/src/","vault-v2/=lib/vault-v2/src/"],"viaIR":true},"optimization_runs":800,"sourcify_repo_url":null,"decoded_constructor_args":null,"compiler_version":"0.8.28+commit.7893614a","is_verified_via_verifier_alliance":true,"verified_at":"2026-07-28T10:35:23.761902Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x6080604052346100225761001161025b565b604051615fbd61029d8239615fbd90f35b5f80fd5b6100339060401c60ff1690565b90565b6100339054610026565b610033905b6001600160401b031690565b6100339054610040565b6100456100336100339290565b6100339061007c906001600160a01b031682565b6001600160a01b031690565b61003390610068565b61003390610088565b6100336100336100339290565b906001600160401b03905b9181191691161790565b61003390610045906001600160401b031682565b906100e06100336100e7926100bc565b82546100a7565b9055565b9068ff00000000000000009060401b6100b2565b9061010f6100336100e792151590565b82546100eb565b61011f9061005b565b9052565b6020810192916101339190610116565b565b61013d610263565b61014d61014982610036565b1590565b61015682610051565b806101706101635f61005b565b916001600160401b031690565b1480610254575b90610185610163600161005b565b148061022e575b90155b9081610225575b50610214576101ae6101a8600161005b565b836100d0565b80610204575b6101bb5750565b6101c6905f906100ff565b60016101ff7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2916101f660405190565b91829182610123565b0390a1565b61020f6001836100ff565b6101b4565b63f92ee8a960e01b5f908152600490fd5b1590505f610196565b5061018f61023b30610091565b3b61024c6102485f61009a565b9190565b14905061018c565b5081610177565b610133610135565b610033610294565b6100337ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061009a565b61003361026b56fe60806040526004361015610011575f80fd5b5f3560e01c8062f5271c1461051f5780630633b14a1461051a5780630c199dd5146105155780630e18b6811461051057806313bf77e61461050b57806314e29b0d1461050657806316c140e91461050157806317931901146104fc57806319815a32146104f75780632495a599146104f2578063249d39e9146104ed57806326782247146104e857806328214f44146104e357806329935820146104de5780632b8a1c5a146104d95780632eb5d4f4146104d457806332145f90146104cf57806333196d92146104ca5780633585c955146104c557806336214144146104c057806339a51be5146104bb578063415f1240146104b657806342a39ec3146104b157806345b01c1a146104ac57806346877b1a146104a75780634dd18bf5146104a25780634fd52c6e1461049d57806353e5bce61461049857806354fd4d5014610493578063579f07b01461048e5780635b972a51146104895780635c0ce2cc1461048457806360da3e831461047f5780636528ea011461047a5780637239d2b914610475578063738b62e514610470578063787dce3d1461046b5780637ecc0002146104665780637fd7f0f114610461578063807ad1141461045c5780638091e4081461045757806386651203146104525780638c46f1761461044d57806392b6630d1461044857806394cb6e7e146104435780639bf6bf471461043e578063a287fbbe14610439578063a487f67714610434578063a623fda51461042f578063a64a256f1461042a578063b0e21e8a14610425578063b390c0ab14610420578063b60b4ed61461041b578063b68ef55914610416578063ba2f55e014610411578063bc157ac11461040c578063c2da6f6e14610407578063c886391d14610402578063ca0d66b2146103fd578063cc3c1d54146103f8578063cf64add8146103f3578063d8aed145146103ee578063d97b6b15146103e9578063db006a75146103e4578063dbd5edc7146103df578063de3bea57146103da578063deb3d8a7146103d5578063e1d5201e146103d0578063e21c90ab146103cb578063e63697c8146103c6578063e71c531b146103c1578063e7d3fe6b146103bc578063e8b6571b146103b7578063eaa968e5146103b2578063f2744a48146103ad578063f3803f7f146103a8578063f431116e146103a3578063f851a4401461039e578063f8d8989814610399578063faf2bf73146103945763fc7b9c180361052e57611423565b6113fd565b6113e2565b6113bc565b611397565b611371565b61134e565b611335565b61131a565b6112f6565b6112ab565b611284565b611268565b611250565b611238565b611220565b611205565b6111df565b6111c3565b61114d565b611132565b611117565b6110f1565b6110cb565b6110a5565b611076565b611048565b61102d565b611015565b610ff9565b610fbb565b610f95565b610f2b565b610f04565b610eec565b610ed4565b610ebc565b610ea4565b610e89565b610e71565b610e56565b610e30565b610e0a565b610de6565b610d9e565b610d86565b610d57565b610d30565b610cf2565b610cae565b610c0d565b610bf2565b610bcb565b610a8e565b610a68565b610a50565b610a38565b610a1d565b6109fa565b6109cf565b610992565b61096c565b610946565b61092e565b6108f2565b6108d7565b6108be565b61086e565b610856565b61083b565b610815565b6107da565b610786565b61076b565b61072a565b610704565b6106e9565b6106c1565b6106a6565b610651565b61055c565b5f91031261052e57565b5f80fd5b61053d916008021c81565b90565b9061053d9154610532565b61053d5f6012610540565b9052565b565b3461052e5761056c366004610524565b61058861057761054b565b6040515b9182918290815260200190565b0390f35b73ffffffffffffffffffffffffffffffffffffffff1690565b6105ae8161058c565b0361052e57565b9050359061055a826105a5565b9060208282031261052e5761053d916105b5565b61058c61053d61053d9273ffffffffffffffffffffffffffffffffffffffff1690565b61053d906105d6565b61053d906105f9565b9061061590610602565b5f5260205260405f2090565b61053d916008021c5b60ff1690565b9061053d9154610621565b61053d9061064c6019915f9261060b565b610630565b3461052e5761058861066c6106673660046105c2565b61063b565b6040515b91829182901515815260200190565b806105ae565b9050359061055a8261067f565b9060208282031261052e5761053d91610685565b3461052e576105886105776106bc366004610692565b611486565b3461052e576106d1366004610524565b6106d9611500565b604051005b61053d5f600d610540565b3461052e576106f9366004610524565b6105886105776106de565b3461052e5761058861057761071a366004610692565b61161f565b61053d5f600f610540565b3461052e5761073a366004610524565b61058861057761071f565b61053d61053d61053d9290565b61053d670de0b6b3a7640000610745565b61053d610752565b3461052e5761077b366004610524565b610588610577610763565b3461052e576106d9610799366004610692565b611799565b61053d916008021c61058c565b9061053d915461079e565b61053d5f60156107ab565b6105569061058c565b60208101929161055a91906107c1565b3461052e576107ea366004610524565b6105886107f56107b6565b6040515b918291826107ca565b61053d612710610745565b61053d610802565b3461052e57610825366004610524565b61058861057761080d565b61053d5f60186107ab565b3461052e5761084b366004610524565b6105886107f5610830565b3461052e576106d9610869366004610692565b6117eb565b3461052e576106d9610881366004610692565b611852565b8015156105ae565b9050359061055a82610886565b919060408382031261052e5761053d906108b581856105b5565b9360200161088e565b3461052e576106d96108d136600461089b565b9061191a565b3461052e576105886105776108ed366004610692565b611937565b3461052e576106d9610905366004610692565b61194b565b90816101c091031261052e5790565b906101c08282031261052e5761053d9161090a565b3461052e576106d9610941366004610919565b611dbe565b3461052e57610956366004610524565b610588610577611dc7565b61053d5f6008610540565b3461052e5761097c366004610524565b610588610577610961565b61053d5f60146107ab565b3461052e576109a2366004610524565b6105886107f5610987565b90815260608101939261055a9290916040916109cb905b6020830152565b0152565b3461052e576105886109ea6109e5366004610692565b611dcf565b6040519193915b938493846109ad565b3461052e576106d9610a0d3660046105c2565b611f85565b61053d5f60036107ab565b3461052e57610a2d366004610524565b6105886107f5610a12565b3461052e576106d9610a4b3660046105c2565b611fe6565b3461052e576106d9610a633660046105c2565b612006565b3461052e57610a78366004610524565b61058861057761201c565b61053d5f600b610540565b3461052e57610a9e366004610524565b610588610577610a83565b634e487b7160e01b5f52604160045260245ffd5b90601f01601f1916810190811067ffffffffffffffff821117610adf57604052565b610aa9565b9061055a610af160405190565b9283610abd565b67ffffffffffffffff8111610adf57602090601f01601f19160190565b0190565b90610b2b610b2683610af8565b610ae4565b918252565b610b3a6005610b19565b7f332e302e30000000000000000000000000000000000000000000000000000000602082015290565b61053d610b30565b61053d610b63565b61053d610b6b565b90825f9392825e0152565b610ba7610bb0602093610b1593610b9b815190565b80835293849260200190565b95869101610b7b565b601f01601f191690565b602080825261053d92910190610b86565b3461052e57610bdb366004610524565b610588610be6610b73565b60405191829182610bba565b3461052e57610588610577610c08366004610692565b61205c565b3461052e57610588610577610c23366004610692565b612068565b909160c08284031261052e57610c3e8383610685565b92610c4c8160208501610685565b92610c5a8260408301610685565b9261053d610c6b8460608501610685565b93610c798160808601610685565b9360a001610685565b6109cb61055a94610ca7606094989795610ca0608086019a5f870152565b6020850152565b6040830152565b3461052e57610588610cd0610cc4366004610c28565b9493909392919261207c565b90610cdd94929460405190565b94859485610c82565b61053d60146018610630565b3461052e57610d02366004610524565b61058861066c610ce6565b919060408382031261052e5761053d90610d278185610685565b936020016105b5565b3461052e57610588610577610d46366004610d0d565b90612190565b61053d5f60176107ab565b3461052e57610d67366004610524565b6105886107f5610d4c565b9060208282031261052e5761053d9161088e565b3461052e576106d9610d99366004610d72565b612251565b3461052e576106d9610db1366004610692565b6122b1565b909160608284031261052e5761053d610dcf8484610685565b93610ddd81602086016105b5565b93604001610685565b3461052e576106d9610df9366004610db6565b916122ba565b61053d5f600c610540565b3461052e57610e1a366004610524565b610588610577610dff565b61053d5f60166107ab565b3461052e57610e40366004610524565b6105886107f5610e25565b61053d5f6009610540565b3461052e57610e66366004610524565b610588610577610e4b565b3461052e576106d9610e84366004610692565b6123e8565b3461052e57610588610577610e9f366004610692565b6123f1565b3461052e576106d9610eb73660046105c2565b6124b0565b3461052e576106d9610ecf366004610d72565b61253b565b3461052e576106d9610ee7366004610692565b61259d565b3461052e576106d9610eff366004610692565b6125a6565b3461052e57610588610577610f1a366004610692565b6126a3565b61053d60156018610630565b3461052e57610f3b366004610524565b61058861066c610f1f565b61062a61053d61053d9290565b61053d90610f6b610f6761053d9460ff1690565b9190565b901b90565b61053d610f7d6001610745565b610f876080610f46565b90610f53565b61053d610f70565b3461052e57610fa5366004610524565b610588610577610f8d565b61053d5f6010610540565b3461052e57610fcb366004610524565b610588610577610fb0565b919060408382031261052e5761053d90610ff08185610685565b93602001610685565b3461052e5761058861057761100f366004610fd6565b906126b7565b3461052e576106d96110283660046105c2565b612953565b3461052e5761103d366004610524565b61058861057761295c565b3461052e5761058861057761105e366004610692565b612966565b90815260408101929161055a9160200152565b3461052e5761108f611089366004610db6565b916129ab565b9061058861109c60405190565b92839283611063565b3461052e576110b5366004610524565b610588610577612b92565b61053d5f60136107ab565b3461052e576110db366004610524565b6105886107f56110c0565b61053d5f6005610540565b3461052e57611101366004610524565b6105886105776110e6565b61053d5f6004610540565b3461052e57611127366004610524565b61058861057761110c565b3461052e57610588610577611148366004610692565b612b9a565b3461052e57610588610577611163366004610fd6565b90612c59565b909182601f8301121561052e5781359167ffffffffffffffff831161052e57602001926020830284011161052e57565b9060208282031261052e57813567ffffffffffffffff811161052e576111bf9201611169565b9091565b3461052e576105886109ea6111d9366004611199565b90612e3a565b3461052e576105886105776111f5366004610692565b6132f1565b61053d5f6006610540565b3461052e57611215366004610524565b6105886105776111fa565b3461052e576106d9611233366004610692565b613352565b3461052e576106d961124b366004610692565b6133cc565b3461052e576106d9611263366004610692565b61342d565b3461052e5761058861057761127e366004610d0d565b90613436565b3461052e5761058861057761129a366004610db6565b91613602565b61053d5f6007610540565b3461052e576112bb366004610524565b6105886105776112a0565b909160608284031261052e5761053d6112df8484610685565b936112ed8160208601610685565b936040016105b5565b3461052e576106d96113093660046112c6565b91613787565b61053d5f600a610540565b3461052e5761132a366004610524565b61058861057761130f565b3461052e576106d96113483660046112c6565b91613822565b3461052e576106d9611361366004610692565b613907565b61053d5f6011610540565b3461052e57611381366004610524565b610588610577611366565b61053d5f60016107ab565b3461052e576113a7366004610524565b6105886107f561138c565b61053d5f806107ab565b3461052e576113cc366004610524565b6105886107f56113b2565b61053d5f60026107ab565b3461052e576113f2366004610524565b6105886107f56113d7565b3461052e576105886109ea611413366004610692565b613910565b61053d5f600e610540565b3461052e57611433366004610524565b610588610577611418565b61053d9081565b61053d905461143e565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b8115611481570490565b61144f565b61053d906114946004611445565b90611477565b61053d9061058c565b61053d905461149a565b61058c61053d61053d9290565b61053d906114ad565b9073ffffffffffffffffffffffffffffffffffffffff905b9181191691161790565b906114f561053d6114fc92610602565b82546114c3565b9055565b61152e61150d60186114a3565b61152761152161151c5f6114ba565b61058c565b9161058c565b141561391d565b3361153f61152161151c60186114a3565b036115e05761155761155160186114a3565b5f6114e5565b61156a6115635f6114ba565b60186114e5565b6115735f6114a3565b6115a17f54e4612788f90384e6843298d7854436f3a585b2c3831ab66abf1de63bfa6c2d916107f960405190565b0390a16115ad5f6114ba565b6115db7fa728e84b447788a55ff664fbfb5c3983925f88b80b672a1b0b8271c94b22df35916107f960405190565b0390a1565b6282b42960e81b5f9081528060045b0390fd5b9050519061055a8261067f565b9060208282031261052e5761053d916115f3565b6040513d5f823e3d90fd5b602061166e9161162c5f90565b5061164261163d61163d60036114a3565b610602565b6116606307a2d13a61165360405190565b9586948593849360e01b90565b835260048301526024820190565b03915afa9081156116ab575f91611683575090565b61053d915060203d81116116a4575b61169c8183610abd565b810190611600565b503d611692565b611614565b61055a906116bc613924565b6116e1565b905f19906114db565b906116da61053d6114fc92610745565b82546116c1565b6116fa816116f3610f6761053d610752565b101561393b565b8061170b610f6761053d600c611445565b1115611790575061172661171f600c611445565b600a6116ca565b611730600a611445565b611740610f6761053d600d611445565b1161177e575b611750600a611445565b6115db7f6f428e543b1146a0996303f0250c6fe0682874c690989e63ae53bf75bed8ef6f9161057b60405190565b61178b61171f600d611445565b611746565b6117269061171f565b61055a906116b0565b61055a906117ae613953565b6117ce565b91906117be565b9290565b82039182116117c957565b611463565b6117e461055a916117df600f611445565b6117b3565b600f6116ca565b61055a906117a2565b61055a90611800613924565b61181981611812610f6761053d610802565b111561393b565b6118248160126116ca565b6115db7f696a162ca55c8b2fc0ae1a749323963295a866dcc8be8808f85c2b0858a782eb9161057b60405190565b61055a906117f4565b9061055a91611868613924565b6118b4565b9060ff906114db565b151590565b9061188b61053d6114fc92611876565b825461186d565b91602061055a9294936118ac60408201965f8301906107c1565b019015159052565b6118ce816118c761152161151c5f6114ba565b141561393b565b6118e2826118dd83601961060b565b61187b565b907fc4459d655e1efe034a75446f1f4db087f75d23bdfabccffafa6384cdb23a1544916115db61191160405190565b92839283611892565b9061055a9161185b565b818102929181159184041417156117c957565b61053d906119456004611445565b90611924565b61055a9061195881613964565b613991565b61053d9060401c61062a565b61053d905461195d565b61053d905b67ffffffffffffffff1690565b61053d9054611973565b61197861053d61053d9290565b9067ffffffffffffffff906114db565b61197861053d61053d9267ffffffffffffffff1690565b906119d361053d6114fc926119ac565b825461199c565b9068ff00000000000000009060401b6114db565b906119fe61053d6114fc92611876565b82546119da565b6105569061198f565b60208101929161055a9190611a05565b611a266139a2565b90611a37611a3383611969565b1590565b90611a4183611985565b80611a5c611a4e5f61198f565b9167ffffffffffffffff1690565b1480611b3f575b90611a71611a4e600161198f565b1480611b1d575b90155b9081611b14575b50611b0357611aa890611a9e611a98600161198f565b856119c3565b82611af357611b89565b611aaf5750565b611aba905f906119ee565b60016115db7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d291611aea60405190565b91829182611a0e565b611afe6001856119ee565b611b89565b63f92ee8a960e01b5f908152600490fd5b1590505f611a82565b50611a7b611b2a30610602565b3b611b37610f675f610745565b149050611a78565b5082611a63565b3561053d8161067f565b3561053d816105a5565b60ff908116911690039060ff82116117c957565b60ff16604d81116117c957600a0a90565b9061061590610745565b611d5e6101a0611d6592611bb1611ba36101208301611b46565b611812610f6761053d610802565b611bc1611ba36101608301611b46565b611bd1611ba36101808301611b46565b611bf2611be060e08301611b46565b6116f3610f6761053d60808601611b46565b611c08611c0160208301611b50565b60026114e5565b611c1e611c1760408301611b50565b60156114e5565b611c5c611c55611c50611c3b611c3660208601611b50565b613a22565b611c4a611c3660408701611b50565b90611b5a565b611b6e565b60046116ca565b611c72611c6b60608301611b46565b60066116ca565b611c8161171f60808301611b46565b611c97611c9060a08301611b46565b600c6116ca565b611cad611ca660c08301611b46565b600b6116ca565b611cc3611cbc60e08301611b46565b600d6116ca565b611ccf61155182611b50565b611ce6611cdf6101008301611b50565b60176114e5565b611cfd611cf66101208301611b46565b60106116ca565b611d14611d0d6101408301611b50565b60146114e5565b611d2b611d246101608301611b46565b60116116ca565b611d42611d3b6101808301611b46565b60126116ca565b611d4d4360076116ca565b611d584360086116ca565b01611b50565b60036114e5565b611d77611d70610f70565b601d6116ca565b611d89611d82610f70565b601c6116ca565b611da6611d96601d611445565b611da15f6022611b7f565b6116ca565b61055a611db3601e611445565b611da15f6023611b7f565b61055a90611a1e565b61053d613aee565b611dd881613964565b611dee6001611de8836021611b7f565b01611445565b91611df882613b51565b9490939082611e09610f675f610745565b118015611e71575b8015611e5d575b918215611e3c575b505015611e2b579290565b63f478bcad60e01b5f908152600490fd5b611e559192506117ba6001611de8610f67936021611b7f565b105f80611e20565b5085611e6b610f675f610745565b11611e18565b5084611e7f610f675f610745565b11611e11565b61055a90611e91613924565b611eb7565b9050519061055a826105a5565b9060208282031261052e5761053d91611e96565b611ee06020611ec861163d84610602565b63fc0c546a90611ed760405190565b93849260e01b90565b825260049082905afa9081156116ab575f91611f57575b50611f0861152161151c60156114a3565b03611f4657611f188160016114e5565b6115db7ff4259e02769887f4a91cc0918b184c4624757024889632563e716c7dc7953db8916107f960405190565b632b632fa960e01b5f908152600490fd5b611f78915060203d8111611f7e575b611f708183610abd565b810190611ea3565b5f611ef7565b503d611f66565b61055a90611e85565b61055a90611f9a613924565b611fad816118c761152161151c5f6114ba565b611fb88160146114e5565b6115db7f76203fab169061f503a4139455a7dfc20cc2d5a59c97d5bb810e431f4059aad7916107f960405190565b61055a90611f8e565b61055a90611ffb613924565b6115ad8160186114e5565b61055a90611fef565b919082018092116117c957565b612026600e611445565b612032610f675f610745565b146120535761053d612042613dea565b905061204e6005611445565b61200f565b61053d5f610745565b61053d90600190614010565b6108ed61053d916120765f90565b5061161f565b93949092946120885f90565b505f958585101561216e571061214d576120cf6120c86120c06120b86120d7946120b38989906117b3565b611924565b611494610802565b9586906117b3565b9383611924565b611494610752565b8281111561211d576121016120f261210f9461210a936117b3565b926120fb610752565b906117b3565b91611945610752565b611477565b9161211a828461200f565b93565b50505050505f905f915f9161053d61214761214161213b5f95610745565b96610745565b94610745565b92610745565b9050816120b89294506121609350611924565b9091829190610f675f610745565b505082612182939495506120b89250611924565b9192909190610f675f610745565b61053d916121c66121b061053d6121cb946121a85f90565b506021611b7f565b6121c0600d600c83019201611445565b90611b7f565b61060b565b611445565b61055a906121dc61407b565b612218565b9074ff00000000000000000000000000000000000000009060a01b6114db565b9061221161053d6114fc92611876565b82546121e1565b612223816018612201565b6115db7f5ef34f53269a04b5b817e5be24a51ba6b477118b399ec9dd5b2baf5b299563dc9161067060405190565b61055a906121d0565b61055a90612266613924565b61227881611812610f6761053d610802565b6122838160106116ca565b6115db7fd10d75876659a287a59a6ccfa2e3fff42f84d94b542837acd30bc184d562de409161057b60405190565b61055a9061225a565b916122f29260206122d161163d61163d60136114a3565b636352211e9061166084926122e560405190565b9889948593849360e01b90565b03915afa9384156116ab5761055a94612314915f9161231c575b5033906140b5565b9190916140da565b612334915060203d8111611f7e57611f708183610abd565b5f61230c565b61055a90612346613924565b61239081602061235c61163d61163d60036114a3565b6370a082319061238561236e30610602565b9261237860405190565b9687948593849360e01b90565b8352600483016107ca565b03915afa80156116ab57610f676116f3916123af945f91611683575090565b6123ba8160066116ca565b6115db7f333b26cca69716ad4680ddb07663f5bfb4f06045671f336af9a83690a3ae00f99161057b60405190565b61055a9061233a565b61242761241d61240b612415936124055f90565b5061414b565b9491905093612068565b611945610752565b611494600a611445565b908181111561243b57505061053d5f610745565b61053d916117b3565b61055a90612450613924565b8061246061152161151c5f6114ba565b1461249f5761246f60136114a3565b61247e61152161151c5f6114ba565b0361248e5761055a9060136114e5565b633e7b157360e21b5f908152600490fd5b636d5ba3c360e11b5f908152600490fd5b61055a90612444565b61055a906124c561407b565b612502565b9075ff0000000000000000000000000000000000000000009060a81b6114db565b906124fb61053d6114fc92611876565b82546124ca565b61250d8160186124eb565b6115db7f3dd464fe54464bc196eadd576785c44f071a8179dc0d9758e836eed88daaf3bd9161067060405190565b61055a906124b9565b61055a90612550613924565b612564816116f3610f6761053d600a611445565b61256f81600c6116ca565b6115db7fc11eea583cef3e01cdb6c3d7a8d3c1f1304208651290ca8ecb9b3eec265082d09161057b60405190565b61055a90612544565b336125b761152161151c60136114a3565b03612627575b6125eb6125ca6001610745565b6125e5600d6125da856021611b7f565b019161204e83611445565b906116ca565b6126157fa4cd6dfb26d61208c2461ddd06b17e0168e102fde3c1988ed117d3ecfe77f73f91610745565b9061261f60405190565b80805b0390a2565b612650602061263c61163d61163d60136114a3565b636352211e90611660859261165360405190565b03915afa9081156116ab575f91612685575b50612670611521339261058c565b146125bd576282b42960e81b5f908152600490fd5b61269d915060203d8111611f7e57611f708183610abd565b5f612662565b61105e61053d916126b15f90565b50611486565b6126cd816126c7610f675f610745565b1161393b565b6126d682613964565b436126ed610f6761053d6007611de8876021611b7f565b146128ea57612746906126fe6142c3565b6127078361458f565b61272a61271a6001611de8866021611b7f565b6120fb6002611de8876021611b7f565b906127418261273b610f675f610745565b1161391d565b6146e0565b9081612754610f675f610745565b146128df5781612791612767600f611445565b602061277961163d61163d60176114a3565b63568914129061278860405190565b94859260e01b90565b825260049082905afa9182156116ab576127ba92610f679261053d925f926128bf575b506117b3565b11612857576127d46127cc60026114a3565b338491614750565b6127de82826147fc565b6127f54360086127ef846021611b7f565b016116ca565b6128066117e4836117df600f611445565b61280f8161485c565b81907f49995e5dd6158cf69ad3e9777c46755a1a826a446c6416992167462dad033b2a61285161284761284133610602565b93610745565b9361057b60405190565b0390a390565b50612865612767600f611445565b825260049082905afa9081156116ab57612885925f9261289f57506117b3565b636a16435b60e11b5f9081529182916115ef916004611063565b6128b891925060203d81116116a45761169c8183610abd565b90846127b4565b6128d891925060203d81116116a45761169c8183610abd565b905f6127b4565b505061053d5f610745565b6372812ba560e01b5f908152600490fd5b61055a90612907613924565b61291a816118c761152161151c5f6114ba565b6129258160166114e5565b6115db7f3ef741755291c333a73ae7a92a463d54beeeae3e3d5dd55ebd822fda4d2176b3916107f960405190565b61055a906128fb565b61053d601f611445565b602061166e916129735f90565b5061298461163d61163d60036114a3565b61166063c6e6f59261165360405190565b61053d9060a01c61062a565b61053d9054612995565b92916129c0826118c761152161151c5f6114ba565b6129d0846126c7610f675f610745565b6129e56129e0611a3360186129a1565b61391d565b6129f36129e0611a3361487c565b612a21612a0a612a03601f611445565b869061200f565b612a1a610f6761053d6006611445565b111561391d565b80612a2e610f675f610745565b03612b6e5750612a6c906020612a4a61163d61163d60136114a3565b636a627842906123855f8593612a5f60405190565b9788958694859360e01b90565b03925af180156116ab5761053d925f91612b50575b509081612ab7612ab17f80f4d65d99e602916903d8040ce9e4bdfb03957f3c2fd761375e6c21c7ace03693610602565b91610745565b91612ac160405190565b5f90a35b612ad8846125e55f6125da856021611b7f565b612af7612ae560036114a3565b33612aef30610602565b9087926148fc565b612b0f612b088561204e601f611445565b601f6116ca565b837fa3af609bf46297028ce551832669030f9effef2b02606d02cbbcc40fe6b47c55612b47612b3d84610745565b9261057b60405190565b0390a292612068565b612b68915060203d81116116a45761169c8183610abd565b5f612a81565b61053d9150612b7c81613964565b612b846142c3565b612b8d8161458f565b612ac5565b61053d61492a565b612ba4905f612405565b919050612bb05f610745565b9080612bbe610f675f610745565b03612c2d575b5080821115612c1d57612bd6916117b3565b612bde61498a565b612be8601f611445565b811015612c0e57612bfd906117df601f611445565b80821015612c09575090565b905090565b50612c185f610745565b612bfd565b5050612c285f610745565b612bd6565b612c539150612c3b906126a3565b612c45600a611445565b612c4d610752565b91614937565b5f612bc4565b90612c6a826126c7610f675f610745565b612c7381613964565b612c8161053d826021611b7f565b9143612c95610f6761053d60078701611445565b146128ea57612ca26142c3565b612cab8261458f565b612cd3612cba60018501611445565b612cca8161273b610f675f610745565b61274183612068565b80612ce0610f675f610745565b14612dfe57612cef83826149b5565b93612d02612cfc836126a3565b956126a3565b612d136120b8826119456010611445565b9283612d24610f6761053d86611445565b1115612d3b57634a613c4160e01b5f908152600490fd5b612d8892612d58612d6292612d51878a90614a51565b50886147fc565b60084391016116ca565b612d82612d6f60036114a3565b33612d7a60176114a3565b9089926148fc565b85614ae0565b80612d95610f675f610745565b11612dd8575b50907f2fe77b1c99aca6b022b8efc6e3e8dd1b48b30748709339b65c50ef3263443e0983612dcb61284133610602565b9361285161109c60405190565b612df890612de660036114a3565b90612df160146114a3565b9091614b1a565b5f612d9b565b5050505061053d5f610745565b60010190565b634e487b7160e01b5f52603260045260245ffd5b9190811015612e35576020020190565b612e11565b91905f5f915f9481612e4e610f675f610745565b14612fa0575f9593929594612e625f610745565b83811015612f8e57612e7d612e78828686612e25565b611b46565b80612e8a610f675f610745565b148015612f71575b612f6357612ea66001611de8836021611b7f565b612ec6612ec6612ece612eb885613b51565b919e91949093918f9061200f565b9a839061200f565b9b612edb610f675f610745565b11908115612f4e575b50908115612f39575b50918215612f18575b5050612f0f575b612f0690612e0b565b96949396612e62565b60019650612efd565b612f319192506117ba6001611de8610f67936021611b7f565b105f80612ef6565b9050612f47610f675f610745565b115f612eed565b9050612f5c610f675f610745565b115f612ee4565b50612f069097949597612e0b565b50612f89611a33612f8260136114a3565b8390614b3c565b612e92565b50939592949150505f14611e2b579290565b63ee6a148160e01b5f908152600490fd5b9061053d91612fbe613953565b50612fc76142c3565b612fd16005611445565b908181116132ea575b612fe35f610745565b9180612ff1610f675f610745565b1415806132d5575b613118575b505061300b4360086116ca565b613014816126a3565b906130266120b8836119456010611445565b61303e6130378361204e601a611445565b601a6116ca565b61305661304f8461204e601b611445565b601b6116ca565b61307461306360036114a3565b61306d60176114a3565b8591614b1a565b613085612b08846117df601f611445565b80613096610f6761053d601f611445565b11156130d2575b506130cc7fe27d271547ea98e824607f6d87e0be0fbedc1fa82418a245c86146cb4c9e0ae39161057b60405190565b0390a190565b61304f613112916130f76130e660036114a3565b6130f060146114a3565b8391614b1a565b613108612b08826117df601f611445565b61204e601b611445565b5f61309d565b90915061312582826117b3565b80613135610f6762503341610745565b116132c557506131445f610745565b61314e601d611445565b61315781614c78565b9161316182614cc3565b9161316e610f675f610745565b146132b5575b81613181610f675f610745565b14613296575b829080613196610f675f610745565b0361327d57506131b0906131aa6001610745565b9061200f565b916131ce610f676131bf610f70565b945b6117ba611d708783614d6a565b11156132685750506132116132096131e55f610745565b6132036131fc6131f5601e611445565b8390614ccf565b601e6116ca565b83614ccf565b9182906117b3565b91613228610f6761053d856131aa62503341610745565b11613257576132389060056116ca565b613250613249826117df600e611445565b600e6116ca565b5f80612ffe565b634a613c4160e01b5f908152600490fd5b6132786132099161321193614bd4565b6131e5565b610f676132906131ce9285969496614ccf565b946131c1565b916132a691506131aa6001610745565b906132af610f70565b90613187565b90506132bf610f70565b90613174565b6132d0908290614bd4565b613144565b50816132e3610f675f610745565b1415612ff9565b5080612fda565b61053d905f612fb1565b61055a90613307613924565b61331981611812610f6761053d610802565b6133248160116116ca565b6115db7f10dfca9c141b166a72d390e49a47e5202fad85c411de1306464d6edce482aecd9161057b60405190565b61055a906132fb565b61055a90613367613924565b6133818161337b610f6761053d600a611445565b1061393b565b613393816116f3610f6761053d610752565b61339e81600b6116ca565b6115db7f18c3125cf6a37e325bab627140219ddcbb229488d0df8b117a28ae3837736bfe9161057b60405190565b61055a9061335b565b61055a906133e1613953565b61055a90806133f6610f6761053d6009611445565b11613403575b60096116ca565b613428613421613417836120fb6009611445565b61204e6020611445565b60206116ca565b6133fc565b61055a906133d5565b613449826118c761152161151c5f6114ba565b61345281613964565b613467602061263c61163d61163d60136114a3565b03915afa80156116ab57613483915f9161231c575033906140b5565b61348c81613991565b6134ab61349f6001611de8846021611b7f565b61273b610f675f610745565b6134b8611a335f83614d7a565b6135db5761053d916134cc83836021611b7f565b916134e5816134dd60028601611445565b600191614dd2565b9161351e6135175f6135106135096134ff60018a01611445565b610f1a81896147fc565b8690614a51565b9601611445565b8390614a51565b908461352c610f675f610745565b116135a5575b8161353f610f675f610745565b11613588575b505061355282849061200f565b90613580612b3d7ffea4a7a7031887f4888d278f82f383a7efcc5d6d5e86922fd5089325fe8ae36d92610745565b0390a261200f565b61359e9161359660036114a3565b919091614b1a565b5f80613545565b6135b8856135b25f610745565b90614ae0565b6135d66135c560036114a3565b6135cf60176114a3565b8791614b1a565b613532565b63f69df64d60e01b5f908152600490fd5b90815260408101929161055a91602001906107c1565b90919061365b9061361c846118c761152161151c5f6114ba565b61362583613964565b613635816126c7610f675f610745565b602061364761163d61163d60136114a3565b636352211e90611660869261237860405190565b03915afa9182156116ab576136eb9261367c915f9161231c575033906140b5565b6136846142c3565b61368d8361458f565b61369c5f611de8856021611b7f565b6136ac610f6761053d601f611445565b11613753575b6136e46136dd6136ce612c3b610f1a6001611de8896021611b7f565b6117df5f611de8886021611b7f565b82906116f3565b8290614a51565b916136f58261485c565b61370a61370260036114a3565b828591614b1a565b8291906137377f71ef96c43343734b1d843bb85d52ef329f5e9143e4d35827771e3b0dd90c5f8492610745565b9261374d61374460405190565b928392836135ec565b0390a290565b61376c613760601f611445565b5f6127ef866021611b7f565b6136b2565b61053d9060a81c61062a565b61053d9054613771565b916137da9261379f826118c761152161151c5f6114ba565b6137a881613964565b6137b8836126c7610f675f610745565b6137c86129e0611a33601861377d565b60206122d161163d61163d60136114a3565b03915afa9384156116ab5761055a946137fb915f9161231c575033906140b5565b6138036142c3565b6138116129e0611a3361487c565b61381a8161458f565b919091614f2f565b9161055a92613837836126c7610f675f610745565b61384081613964565b613853826118c761152161151c5f6114ba565b6138636129e0611a33601861377d565b6137fb833383614fdf565b61055a9061387a613924565b61388c816126c7610f6761053d610752565b6138a0816116f3610f6761053d600a611445565b6138b4816126c7610f6761053d600b611445565b6138ce81611812610f6761053d60026120b3612ab1610752565b6138d981600d6116ca565b6115db7f03802a2ea67750df04454dc6298d86ea86fc78dbae9d4f342789c495b20ae79b9161057b60405190565b61055a9061386e565b61391a905f612405565b92565b1561325757565b3361393461152161151c5f6114a3565b036115e057565b1561394257565b630134249960e71b5f908152600490fd5b3361393461152161151c60176114a3565b611a3361397a9161397560136114a3565b614b3c565b61398057565b631bdf1dcd60e01b5f908152600490fd5b61055a9061399d6142c3565b61458f565b61053d61502e565b3d156139c3576139b93d610b19565b903d5f602084013e565b606090565b6139eb61053d9492936139e260608401955f8501906107c1565b15156020830152565b6040818403910152610b86565b60ff81166105ae565b9050519061055a826139f8565b9060208282031261052e5761053d91613a01565b5f80826004613a5d63313ce56760e01b613a4e613a3e60405190565b93849260208401908152015f0190565b60208201810382520382610abd565b602081019051915afa613a6e6139aa565b91803b613a7d610f675f610745565b148015613ae6575b8015613ad0575b613aab57505061053d906020613aa0825190565b818301019101613a0e565b6115ef909192613aba60405190565b63e7e40b5b60e01b8152938493600485016139c8565b508251613ae0610f676020610745565b10613a8c565b508115613a85565b61053d613af961498a565b613b03601f611445565b9081811115613b12575061161f565b905061161f565b61055690610745565b60208101929161055a9190613b19565b6040906109cb61055a94969593966109c460608401985f8501906107c1565b5f9182613b5c6142c3565b613b658361458f565b613b7361053d846021611b7f565b90613bb06020613b8961163d61163d60036114a3565b6307a2d13a90613ba5670de0b6b3a76400009261165360405190565b835260048301613b22565b03915afa9081156116ab575f91613dcc575b50613bcf610f675f610745565b14613db457613bde5f85614d7a565b613db457613beb5f610745565b91613bf860028201611445565b613c04610f675f610745565b11613d18575b50613c155f85614d7a565b15613d045780613c27610f675f610745565b11613cd1575b80613c3a610f675f610745565b1115613cad57613c4b908490614a51565b92613c61613c5960036114a3565b338691614b1a565b3390848692613ca6613c937f14ecf0ab88d32e49a8a2a6527ee7b8bb4ec899b29e8ef98ced171e4a411f343794610745565b94613c9d60405190565b93849384613b32565b0390a29290565b9284613cbb610f675f610745565b1115613c615793613ccb906155f8565b93613c61565b80613cdb85615563565b8111613ce8575b50613c2d565b945050613cfd613cf75f610745565b9461161f565b935f613ce2565b50509150613d11906150c7565b9192909190565b915050613d3283613d2b60028401611445565b5f91614dd2565b90613d3c82615036565b90613d4681611445565b613d52610f675f610745565b1480613d95575b15613c0a57613d6d6001613d729201611445565b61504f565b80613d7f610f675f610745565b1115613c0a57613d8f90856147fc565b5f613c0a565b50613da260018201611445565b613dae610f675f610745565b11613d59565b50509150505f5f9161053d6121476121415f94610745565b613de4915060203d81116116a45761169c8183610abd565b5f613bc2565b613df4601c611445565b9043613e06610f6761053d6007611445565b11158015613ff3575b61205357613e30613e2060036114a3565b613e2a60176114a3565b906157e1565b613e3a6020611445565b9080613e4c610f6761053d6009611445565b11613fdb575b50613e6361163d61163d60176114a3565b602063b5024ce891613e82613e786007611445565b6131aa6001610745565b90613ea14394613eac613e9460405190565b9687958694859460e01b90565b845260048401611063565b03915afa80156116ab57613ec8915f91613fbd575b5091612068565b81613ed5610f675f610745565b141580613fa8575b613f88575b50613efa613ef0600e611445565b6120fb6005611445565b90818111613f81575b80613f10610f675f610745565b148015613f6d575b6128df5790613f486120fb948361053d94613f33610f678690565b03613f545750613f425f610745565b90615885565b50505094909482614ccf565b613f61613f6891856117b3565b8490614bd4565b613f42565b5081613f7b610f675f610745565b14613f18565b5080613f03565b613f9b919080821115613fa157906117b3565b5f613ee2565b50806120fb565b5080613fb6610f675f610745565b1415613edd565b613fd5915060203d81116116a45761169c8183610abd565b5f613ec1565b906131aa613fed926120fb6009611445565b5f613e52565b50613ffe600e611445565b61400a610f675f610745565b14613e0f565b61053d91905f9115614054576140259061414b565b91505080614035610f675f610745565b11614041575b50611937565b906131aa61404e9261161f565b5f61403b565b6108ed91505f611de861071a926021611b7f565b61053d9061062a565b61053d9054614068565b3361408b61152161151c5f6114a3565b141580614099575b6115e057565b506140b0611a336140ab33601961060b565b614071565b614093565b6115216140c2919261058c565b036140c957565b633055827b60e11b5f908152600490fd5b90919061410d6140ee61053d836021611b7f565b611da161410685926121c0600d600c83019201611445565b869061060b565b9190916141466128476141407f4ebe02d1fe6bac44c7072ea0e71bb30db417ea99ad6b6462c64af036a9e521a093610745565b93610602565b0390a3565b61053d614160916141595f90565b505f6121a8565b90614169613dea565b509061419483614179601c611445565b614183601d611445565b9061418e601e611445565b92615963565b9093806141a7610f6761053d601c611445565b0361424b575b506141b785611445565b946141d16141c5601a611445565b6120fb600a8401611445565b90816141df610f675f610745565b141580614236575b6141f5575b50919492915050565b916142209161421961422d9598946120fb600b614212601b611445565b9201611445565b9091614937565b818111613fa157906117b3565b925f80806141ec565b5082614244610f675f610745565b14156141e7565b848311156142b55761425d85846117b3565b908161426b610f675f610745565b03614277575b506141ad565b61429c9295916120fb6142966131aa93614291601c611445565b615cf5565b82614ccf565b928184116142ac575b5f80614271565b925080926142a5565b6142be5f610745565b61425d565b6142cd600e611445565b6142d9610f675f610745565b1461055a57436142ef610f6761053d6007611445565b111561055a57614316614305613e2060036114a3565b806133f6610f6761053d6009611445565b61432661163d61163d60176114a3565b602063b5024ce89161433b613e786007611445565b90613ea1439461434d613e9460405190565b03915afa9081156116ab575f91614571575b5061436d610c236020611445565b8161437a610f675f610745565b14158061455c575b6144e0575b50614395613ef0600e611445565b8082116144d8575b816143aa610f675f610745565b11806144c3575b6143c3575b505061055a4360076116ca565b614449614453918361445a946143d9610f678490565b0361449857506120fb6144336131fc61441b61443d6144096143fa5f610745565b614404601c611445565b615885565b9297909692959293909290601c6116ca565b61442d614426610f70565b88906117b3565b90614ccf565b61204e601e611445565b614461575b5082614ccf565b61204e6005611445565b60056116ca565b5f806143b6565b6144929061447d614472601d611445565b611da1836022611b7f565b611da161448a601e611445565b916023611b7f565b5f614442565b6144336131fc61441b61443d6144096144be6144b76120fb97896117b3565b8890614bd4565b6143fa565b50806144d1610f675f610745565b14156143b1565b90508061439d565b9061450c9082811115614553576144f9839182906117b3565b926145046020611445565b919091614937565b8061451d610f6761053d6020611445565b1161453a575b613421614534916117df6020611445565b5f614387565b5061453461342161454b6020611445565b915050614523565b6144f981613209565b508061456a610f675f610745565b1415614382565b614589915060203d81116116a45761169c8183610abd565b5f61435f565b6145a061053d61055a926021611b7f565b61462b6146226145b483614179601c611445565b9192916145d06145c4601a611445565b6120fb600a8901611445565b90816145de610f675f610745565b1415806146cb575b61466a575b50506146036145fa601a611445565b600a87016116ca565b614619614610601b611445565b600b87016116ca565b600285016116ca565b600183016116ca565b614641614638601c611445565b600483016116ca565b61465761464e601d611445565b600583016116ca565b6009614663601e611445565b91016116ca565b6146879161421961467b601b611445565b6120fb600b8b01611445565b80614697610f6761053d89611445565b116146b7575b6146b09086906125e5906117df83611445565b5f806145eb565b506146b06146c486611445565b905061469d565b50806146d9610f675f610745565b14156145e6565b8181111561470e57505b806146fb610f6761053d600e611445565b116147035790565b5061053d600e611445565b90506146ea565b91602061055a9294936109cb60408201965f8301906107c1565b9050519061055a82610886565b9060208282031261052e5761053d9161472f565b915f919060046147828493613a4e879463079cc67960e41b9261477260405190565b9586946020860190815201614715565b82602082019151925af16147946139aa565b91803b6147a3610f675f610745565b1480156147f4575b80156147bb575b613aab57505050565b5082516147ca610f675f610745565b141580156147b257506147ef611a338460206147e4825190565b81830101910161473c565b6147b2565b5081156147ab565b61482e9161482461481461053d613249946021611b7f565b6001016125e5836117df83611445565b6117df600e611445565b6148386005611445565b614848610f6761053d600e611445565b1161484f57565b61055a614453600e611445565b61486590615d6a565b61486b57565b63fddafdf560e01b5f908152600490fd5b614886600f611445565b614892610f675f610745565b146148cf576148bb6108ed6148aa613e2060036114a3565b6131aa6148b5613aee565b9161161f565b6148cb610f676117ba600f611445565b1190565b5f90565b6040906109cb61055a94969593966148f260608401985f8501906107c1565b60208301906107c1565b6040516323b872dd60e01b602082015290935f938493869392614782928492613a4e929190602485016148d3565b61053d61071a601f611445565b9291614944818386615db4565b939190918215611481570961495b610f675f610745565b0361496257565b905f1981146149795761053d90610b156001610745565b6349248f7760e01b5f908152600490fd5b614994600e611445565b6149a0610f675f610745565b146120535761053d612c3b610f1a600e611445565b906149c761053d614a02926121a85f90565b6149d360018201611445565b6149df60028301611445565b9381811115614a4a57505b83811115614a3f57506002614663845b9485906117b3565b80614a13610f6761053d6005611445565b1115614a335761053d614453614a296005611445565b6117df6005611445565b61053d61445382614a29565b6146636002916149fa565b90506149ea565b9061053d614a60916121a85f90565b614a6981611445565b9081614a7b610f6761053d601f611445565b11614abe575b82614aa39293614a91610f678690565b1115614ab457505f61466384806117b3565b61053d612b08826117df601f611445565b6146635f916149fa565b614aa39150614acd601f611445565b92614ad884836116ca565b929150614a81565b6133fc9061055a9281614af3610f678390565b11614b04575b5061204e6009611445565b613421613417614b1492846117b3565b5f614af9565b915f919060046147828493613a4e879463a9059cbb60e01b9261477260405190565b91905f9281614b4d610f675f610745565b14614bac57614b7591614b6461163d602093610602565b611660636352211e61165360405190565b03915afa9081614b90575b50614b8a575f9150565b60019150565b614ba79060203d8111611f7e57611f708183610abd565b614b80565b505f925050565b61053d90614bc7610f6761053d9460ff1690565b901c90565b0290565b0390565b9081614be2610f675f610745565b146128df5781614c03610f6761053d5f19614bfd6080610f46565b90614bb3565b1115614c4c5790610b1561053d9261210a614c41614c36614c25868590611477565b95614c308588614bcc565b90614bd0565b94610f876080610f46565b93610f876080610f46565b61210a61053d92610f876080610f46565b61053d6081610745565b61053d90614bc7610f6761053d9490565b61053d90614c84614c5d565b90614c67565b61053d90610f6b610f6761053d9490565b61053d614cb9614cab6001610745565b614cb3614c5d565b90614c8a565b6120fb6001610745565b614ccb614c9b565b1690565b919082614cde610f675f610745565b148015614d56575b614d4a57614d07905f1981850993029283808210910303610f876080610f46565b614d1583614bfd6080610f46565b1791614d27614cb9610f7d6001610745565b16614d34610f675f610745565b03614d3b57565b9061053d90610b156001610745565b50905061053d5f610745565b5080614d64610f675f610745565b14614ce6565b614d7690614cb3614c5d565b1790565b614d8a6001611de8836021611b7f565b614d96610f675f610745565b14614dcb576114946001611de8614db3612415614dbb9686614010565b936021611b7f565b6148cb610f6761053d600b611445565b5050600190565b909182614de1610f675f610745565b14614f1557614df390611a3383613964565b614f21575b614e24614e0e6001611de861053d856021611b7f565b614e1e8161273b610f675f610745565b836146e0565b80614e31610f675f610745565b14614f1557614e51613517614e4684846149b5565b92610f1a81866147fc565b92614e6d614e666120b8866119456010611445565b8490614a51565b92848492614ea8614e9e7f85e4a966dae695c747415c155708ec4e77481b3161efaef4593bdb0a4e79ce5994610745565b946109f160405190565b0390a282614eb8610f675f610745565b11614ee3575b5080614ecc610f675f610745565b11614ed5575090565b61053d90612de660036114a3565b614f0f90614f05614ef460036114a3565b614efe60176114a3565b8691614b1a565b6135b284916126a3565b5f614ebe565b50505061053d5f610745565b614f2a81613991565b614df8565b43614f46610f6761053d6008611de8866021611b7f565b14614fce57614f558282615efd565b614f666117e48361204e600f611445565b614f6f8161485c565b614f804360076127ef846021611b7f565b614f95614f8d60026114a3565b848491615f65565b9091614fc17f8334f87aeaf76e52b061d93ee968e51fdd3ad53ca04e80271249227997aab3a092610745565b9261262261374460405190565b63848288c360e01b5f908152600490fd5b614ffb61055a93926121c66121b061053d6125e5956021611b7f565b916117df83611445565b61053d7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610745565b61053d615005565b6120b861053d916150445f90565b506119456012611445565b80615060610f6761053d600e611445565b111561053d575061053d600e611445565b6109cb61055a9461509c606094989795615092608086019a5f8701906107c1565b6020850190613b19565b6040830190613b19565b6109cb61055a94610ca7606094989795610ca0608086019a5f8701906107c1565b5f916150d761053d836021611b7f565b916150e460018401611445565b906151336150f18261205c565b836150fc600d611445565b61511761510d6124156108ed61492a565b611494600e611445565b90615122600c611445565b9261512d6011611445565b9461207c565b909490929081615145610f675f610745565b146154385750614e66615157916126a3565b9384615165610f675f610745565b1461541d57615173906126a3565b848111156154185750835b9561518987866117b3565b9161519383612068565b61519f60018401611445565b6151af610f6761053d600e611445565b1015615409576151c160018401611445565b90808311615401575b508082116153f9575b50806151e1610f675f610745565b116153e9575b506151f460018201611445565b615200610f675f610745565b11806153d6575b6152f0575b508061521a610f675f610745565b116152c4575b508461522e610f675f610745565b11156152885750615242613c5960036114a3565b3390828592613ca6886152757f969067f3911888d4f34a9dba2e904ea5e0dcd3323ccf9692ef8f091c6a4326a595610745565b9561527f60405190565b948594856150a6565b61529181611486565b61529d610f675f610745565b116152a9575b50615242565b6152bd9195506152b890611486565b6155f8565b935f6152a3565b806152d56152ea926135b25f610745565b6152df60036114a3565b90612df160176114a3565b5f615220565b6152f981611445565b80615306610f675f610745565b1161537f575b5061531681611445565b615322610f675f610745565b1480615360575b1561520c57613d6d600161533d9201611445565b8061534a610f675f610745565b111561520c5761535a90846147fc565b5f61520c565b5061536d60018201611445565b615379610f675f610745565b11615329565b6153a161539b6153936153b3938890614a51565b94859061200f565b93612068565b6153ad60018401611445565b906146e0565b806153c0610f675f610745565b111561530c576153d090856147fc565b5f61530c565b506153e4611a335f86614d7a565b615207565b6153f390856147fc565b5f6151e7565b90505f6151d3565b91505f6151ca565b615413600e611445565b6151c1565b61517e565b50505050509150505f5f9161053d6121476121415f94610745565b945050908161545c9296939596615451610f675f610745565b116155305750611486565b80615469610f675f610745565b1161550f575b50610f676117ba60016154829301611445565b1080156154fb575b6154a357505f9150819061053d61214761214184610745565b33905f5f926154e9866154d67f969067f3911888d4f34a9dba2e904ea5e0dcd3323ccf9692ef8f091c6a4326a595610745565b956154e060405190565b94859485615071565b0390a25f9190610f6761284184610745565b5082615509610f675f610745565b1161548a565b6154829195506117ba6001615526610f67936155f8565b979350505061546f565b615540906153ad60018601611445565b8061554d610f675f610745565b11156126b15761555d90866147fc565b5f6126b1565b61053d615571916121a85f90565b61557d60018201611445565b908161558b610f675f610745565b146155d8576155a1610c235f6155ac9301611445565b91612c45600b611445565b5f1981146128df576155c2906131aa6001610745565b90818111156128df5761053d91610f1a916117b3565b61053d9150611445565b90815260408101929161055a9160200190613b19565b80615605610f675f610745565b146157d75761561460016114a3565b61562361152161151c5f6114ba565b146157ac5761564c602061563d61163d61163d60016114a3565b637d88209790611ed760405190565b825260049082905afa9081156116ab575f9161578e575b5080615671610f675f610745565b116156c457507f7781e9a51c92f9403c996e7eeb66bf0eba96c64d2272768d3e52e7cd137c672b5f6156a233610602565b926156b86156af60405190565b928392836155e2565b0390a261053d5f610745565b80821115615787575b906156de61163d61163d60016114a3565b63f3fef3a3338492803b1561052e5761570c5f80946157176156ff60405190565b9788968795869460e01b90565b845260048401614715565b03925af180156116ab5761576a575b50808210615732575090565b7f7781e9a51c92f9403c996e7eeb66bf0eba96c64d2272768d3e52e7cd137c672b8261575d33610602565b9261374d61109c60405190565b615781905f6157798183610abd565b810190610524565b5f615726565b50806156cd565b6157a6915060203d81116116a45761169c8183610abd565b5f615663565b7f7781e9a51c92f9403c996e7eeb66bf0eba96c64d2272768d3e52e7cd137c672b5f6156a233610602565b5061053d5f610745565b905f80916157ec5f90565b5060046158198592613a4e6370a0823160e01b9161580960405190565b94859360208501908152016107ca565b602081019051915afa61582a6139aa565b91803b615839610f675f610745565b14801561587d575b8015615867575b613aab57505061053d90602061585c825190565b818301019101611600565b508251615877610f676020610745565b10615848565b508115615841565b90919061589481614c84614c5d565b928161589e614c9b565b16916158ac610f675f610745565b14615953575b816158bf610f675f610745565b14615934575b8390806158d4610f675f610745565b0361592457506158e8906131aa6001610745565b6158f0610f70565b935b6158fd610f67839290565b116159088583614d6a565b948115615919575061211a5f610745565b61211a908490614bd4565b61592e9083614ccf565b936158f2565b9261594491506131aa6001610745565b9161594d610f70565b906158c5565b905061595d610f70565b906158b2565b93929061597c61597560058701611445565b8490615cf5565b61598860018701611445565b61599a610f6761053d60028a01611445565b1115615ce657856159bc6159b060018301611445565b6120fb60028401611445565b6159de6159d86132036159d160048601611445565b8790615cf5565b826117b3565b94836159ee610f6761053d610f70565b14615caa57615b279596615a0c615a0760048601611445565b614cc3565b9182615a1a610f675f610745565b14615c9a575b615a36615a2f60098701611445565b8790614ccf565b80821115615c8a57615a47916117b3565b828111615c83575b615a62615a5c8483614bd4565b85614ccf565b96615a7a615a7260048801611445565b614c84614c5d565b91615a8782614c84614c5d565b91838314615c21575b5050615a9c610f678390565b11615b55575b505050505081615ab2610f678690565b11615b4d575b615adb615ad3615af192615ace60028a01611445565b614ccf565b9483906117b3565b61204e615aea60028901611445565b86906117b3565b615afd60018701611445565b8111615b3657615aea6001615b1a615b21936117df838b01611445565b9701611445565b9261200f565b91838311615b3157565b839250565b50615b21615aea6001615b485f610745565b615b1a565b839150615ab8565b615b69919293949596506131aa6001610745565b92615b846121cb615b7c81876022611b7f565b956023611b7f565b9084615b92610f675f610745565b14615c0a5761442d61442d94615be193615bc489615ace6009614212615bbd6005615be79f01611445565b8d90615cf5565b80821115615bfa57615bd5916117b3565b818111615bf357614bd4565b92615cf5565b905b855f808080615aa2565b5080614bd4565b5050615c055f610745565b615bd5565b505050505050615c1b838290614ccf565b90615be9565b615c29614c9b565b1680861115615c6b57610f6761053d615c45615c4c93896117b3565b8b90614ccf565b14615c59575b5f80615a90565b9650615c65868a614ccf565b96615c52565b50615c4c610f6761053d615c7e5f610745565b615c45565b5081615a4f565b5050615c955f610745565b615a47565b9150615ca4610f70565b91615a20565b5050615ccd945061204e9250600291506001615cc69101611445565b9501611445565b90828211615cde5761053d5f610745565b829150612053565b85615cf05f610745565b6159bc565b808214615d605780615d09610f675f610745565b14615d6057615d1a81614c84614c5d565b615d2c610f676117ba85614c84614c5d565b116128df57615d39614c9b565b1690615d43614c9b565b169080615d52610f675f610745565b146128df5761053d91614bd4565b505061053d610f70565b615d7a6001611de8836021611b7f565b9081615d88610f675f610745565b14615dae57610f6761053d615d9f615daa9361205c565b93612c45600a611445565b1090565b50505f90565b90919081615dc4610f675f610745565b14615eec575f198382099183820292838082109103039384615de8610f675f610745565b14615ede57848211156149795761053d9482615e3692615e7f95615e095f90565b500980860395109003600183615e238119610b1584610745565b1680809504960493805f03040190614bcc565b1791615e7f615ecd615ebb615ea9615e97615e85615e5d87615e586003610745565b614bcc565b615e676002610745565b18615e7f6002615e7a612ab1848c614bcc565b614bd0565b90614bcc565b615e7f6002615e7a612ab1848b614bcc565b615e7f6002615e7a612ab1848a614bcc565b615e7f6002615e7a612ab18489614bcc565b615e7f6002615e7a612ab18488614bcc565b91615e7a612ab16002928590614bcc565b5091905061053d9250611477565b632144a53360e01b5f908152600490fd5b90615f0c61053d836021611b7f565b90615f2e615f26615f1f60018501611445565b839061200f565b935f90614010565b615f42610f6761053d86612c45600a611445565b1061486b57615f5b61324992600161055a9591016116ca565b61204e600e611445565b915f919060046147828493613a4e87946340c10f1960e01b926147726040519056fea2646970667358221220604864101ed74282ba0b35db2cdb57859cb52a0bdf98c00c512ae333378c066064736f6c634300081c0033","name":"AlchemistV3","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"cancun","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":false,"additional_sources":[{"file_path":"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Storage of the initializable contract.\n     *\n     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions\n     * when using with upgradeable contracts.\n     *\n     * @custom:storage-location erc7201:openzeppelin.storage.Initializable\n     */\n    struct InitializableStorage {\n        /**\n         * @dev Indicates that the contract has been initialized.\n         */\n        uint64 _initialized;\n        /**\n         * @dev Indicates that the contract is in the process of being initialized.\n         */\n        bool _initializing;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Initializable\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;\n\n    /**\n     * @dev The contract is already initialized.\n     */\n    error InvalidInitialization();\n\n    /**\n     * @dev The contract is not initializing.\n     */\n    error NotInitializing();\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint64 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any\n     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in\n     * production.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        // Cache values to avoid duplicated sloads\n        bool isTopLevelCall = !$._initializing;\n        uint64 initialized = $._initialized;\n\n        // Allowed calls:\n        // - initialSetup: the contract is not in the initializing state and no previous version was\n        //                 initialized\n        // - construction: the contract is initialized at version 1 (no reinitialization) and the\n        //                 current contract is just being deployed\n        bool initialSetup = initialized == 0 && isTopLevelCall;\n        bool construction = initialized == 1 && address(this).code.length == 0;\n\n        if (!initialSetup && !construction) {\n            revert InvalidInitialization();\n        }\n        $._initialized = 1;\n        if (isTopLevelCall) {\n            $._initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            $._initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint64 version) {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        if ($._initializing || $._initialized >= version) {\n            revert InvalidInitialization();\n        }\n        $._initialized = version;\n        $._initializing = true;\n        _;\n        $._initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        _checkInitializing();\n        _;\n    }\n\n    /**\n     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.\n     */\n    function _checkInitializing() internal view virtual {\n        if (!_isInitializing()) {\n            revert NotInitializing();\n        }\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        if ($._initializing) {\n            revert InvalidInitialization();\n        }\n        if ($._initialized != type(uint64).max) {\n            $._initialized = type(uint64).max;\n            emit Initialized(type(uint64).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint64) {\n        return _getInitializableStorage()._initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _getInitializableStorage()._initializing;\n    }\n\n    /**\n     * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.\n     *\n     * NOTE: Consider following the ERC-7201 formula to derive storage locations.\n     */\n    function _initializableStorageSlot() internal pure virtual returns (bytes32) {\n        return INITIALIZABLE_STORAGE;\n    }\n\n    /**\n     * @dev Returns a pointer to the storage namespace.\n     */\n    // solhint-disable-next-line var-name-mixedcase\n    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {\n        bytes32 slot = _initializableStorageSlot();\n        assembly {\n            $.slot := slot\n        }\n    }\n}\n"},{"file_path":"src/interfaces/IFeeVault.sol","source_code":"pragma solidity >=0.5.0;\n\n/// @title  IFeeVault\n/// @author Alchemix Finance\ninterface IFeeVault {\n    /**\n     * @notice Get the ERC20 token managed by this vault\n     * @return The ERC20 token address\n     */\n    function token() external view returns (address);\n\n    /**\n     * @notice Get the total deposits in the vault\n     * @return Total deposits\n     */\n    function totalDeposits() external view returns (uint256);\n\n    /**\n     * @notice Withdraw funds from the vault to a target address\n     * @param recipient Address to receive the funds\n     * @param amount Amount to withdraw\n     */\n    function withdraw(address recipient, uint256 amount) external;\n}\n"},{"file_path":"src/interfaces/ITransmuter.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.28;\n\nimport \"./IAlchemistV3.sol\";\n\ninterface ITransmuter {\n    struct StakingPosition {\n        // Amount staked.\n        uint256 amount;\n        // Block when the position was opened\n        uint256 startBlock;\n        // Time when the transmutation will be complete/claimable.\n        uint256 maturationBlock;\n    }\n\n    struct TransmuterInitializationParams {\n        address syntheticToken;\n        address feeReceiver;\n        uint256 timeToTransmute;\n        uint256 transmutationFee;\n        uint256 exitFee;\n        uint256 graphSize;\n    }\n\n    /// @notice Gets the address of the alchemist.\n    ///\n    /// @return alchemist The alchemist address.\n    function alchemist() external view returns (IAlchemistV3 alchemist);\n\n    /// @notice Gets the address of the admin.\n    ///\n    /// @return admin The admin address.\n    function admin() external view returns (address admin);\n\n    /// @notice Gets the address of the pending admin.\n    ///\n    /// @return pendingAdmin The pending admin address.\n    function pendingAdmin() external view returns (address pendingAdmin);\n\n    /// @notice Returns the version of the alchemist.\n    function version() external view returns (string memory version);\n\n    /// @notice Returns the address of the synthetic token.\n    function syntheticToken() external view returns (address token);\n\n    /// @notice Returns the current transmuter deposit cap.\n    function depositCap() external view returns (uint256 cap);\n\n    /// @notice Returns the transmutation early exit fee.\n    /// @notice This is for users who choose to pull from the transmuter before their position has fully matured.\n    function exitFee() external view returns (uint256 fee);\n\n    /// @notice Returns the transmutation fee.\n    /// @notice This fee affects all claims.\n    function transmutationFee() external view returns (uint256 fee);\n\n    /// @notice Returns the current time to transmute (in blocks).\n    function timeToTransmute() external view returns (uint256 transmutationTime);\n\n    /// @notice Returns the total locked debt tokens in the transmuter adjusted for poked accounts.\n    function totalActiveLocked() external view returns (uint256 totalLocked);\n\n    /// @notice Returns the total locked debt tokens in the transmuter.\n    function totalLocked() external view returns (uint256 totalLocked);\n\n    function protocolFeeReceiver() external view returns (address receiver);\n\n    /// @notice Sets the pending administrator.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {PendingAdminUpdated} event.\n    ///\n    /// @dev This is the first step in the two-step process of setting a new administrator. After this function is called, the pending administrator will then need to call {acceptAdmin} to complete the process.\n    ///\n    /// @param value The address to set the pending admin to.\n    function setPendingAdmin(address value) external;\n\n    /// @notice Allows for `msg.sender` to accepts the role of administrator.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    /// @notice The current pending administrator must be non-zero or this call will revert with an {IllegalState} error.\n    ///\n    /// @dev This is the second step in the two-step process of setting a new administrator. After this function is successfully called, this pending administrator will be reset and the new administrator will be set.\n    ///\n    /// @notice Emits a {AdminUpdated} event.\n    /// @notice Emits a {PendingAdminUpdated} event.\n    function acceptAdmin() external;\n\n    /// @notice Set a new alchemist for redemptions.\n    ///\n    /// @param alchemist The address of the new alchemist.\n    function setAlchemist(address alchemist) external;\n\n    /// @notice Updates transmuter deposit limit to `cap`.\n    ///\n    /// @notice `cap` must be greater or equal to current synths locked in the transmuter.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    ///\n    /// @param cap    The new deposit cap.\n    function setDepositCap(uint256 cap) external;\n\n    /// @notice Sets time to transmute to `time`.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @param time    The new transmutation time.\n    function setTransmutationTime(uint256 time) external;\n\n    /// @notice Sets the transmutation fee to `fee`.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @param fee    The new transmutation fee.\n    function setTransmutationFee(uint256 fee) external;\n\n    /// @notice Sets the early exit fee to `fee`.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @param fee    The new exit fee.\n    function setExitFee(uint256 fee) external;\n\n    /// @notice Set a new protocol fee receiver.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {ProtocolFeeReceiverUpdated} event.\n    ///\n    /// @param receiver The address of the new fee receiver.\n    function setProtocolFeeReceiver(address receiver) external;\n\n    /// @notice Gets position info for `id`.\n    ///\n    /// @param id      NFT ID,\n    ///\n    /// @return position   Position data.\n    function getPosition(uint256 id) external view returns (StakingPosition memory position);\n\n    /// @notice Creates a new staking position in the transmuter.\n    ///\n    /// @notice `depositAmount` must be non-zero or this call will revert with a {DepositZeroAmount} error.\n    ///\n    /// @notice Emits a {PositionCreated} event.\n    ///\n    /// @param depositAmount    Amount of debt tokens to deposit.\n    /// @param recipient        Address which will own the minted redemption position NFT.\n    function createRedemption(uint256 depositAmount, address recipient) external;\n\n    /// @notice Claims a staking position from the transmuter.\n    ///\n    /// @notice `id` must return a valid position or this call will revert with a {PositionNotFound} error.\n    /// @notice End block of position must be <= to current block or this call will revert with a {PrematureClaim} error.\n    ///\n    /// @notice Emits a {PositionClaimed} event.\n    ///\n    /// @param id   Id of the nft representing the position.\n    function claimRedemption(\n        uint256 id\n    ) external returns (uint256 claimYield, uint256 feeYield, uint256 syntheticReturned, uint256 syntheticFee);\n\n    /// @notice Queries the staking graph from `startBlock` to `endBlock`.\n    ///\n    /// @param startBlock   The block to start query from.\n    /// @param endBlock     The last block to query up to.\n    ///\n    /// @return totalValue  Total value of tokens needed to fulfill redemptions between `startBlock` and `endBlock`.\n    function queryGraph(uint256 startBlock, uint256 endBlock) external view returns (uint256 totalValue);\n\n    /// @notice Poke matured account to lower deposit cap\n    ///\n    /// @param id   The position ID.\n    function pokeMatured(uint256 id) external;\n\n    /// @notice Emitted when the admin address is updated.\n    ///\n    /// @param admin The new admin address.\n    event AdminUpdated(address admin);\n\n    /// @notice Emitted when the pending admin is updated.\n    ///\n    /// @param pendingAdmin The address of the pending admin.\n    event PendingAdminUpdated(address pendingAdmin);\n\n    /// @notice Emitted when the associated alchemist is updated.\n    ///\n    /// @param alchemist The address of the new alchemist.\n    event AlchemistUpdated(address alchemist);\n\n    /// @dev Emitted when a position is created.\n    ///\n    /// @param creator          The address that created the position.\n    /// @param amountStaked     The amount of tokens staked.\n    /// @param nftId            The id of the newly minted NFT.\n    event PositionCreated(address indexed creator, uint256 amountStaked, uint256 nftId);\n\n    /// @dev Emitted when a position is claimed.\n    ///\n    /// @param claimer          The address that claimed the position.\n    /// @param amountClaimed    The amount of tokens claimed.\n    /// @param amountUnclaimed  The amount of tokens that were not transmuted.\n    event PositionClaimed(address indexed claimer, uint256 amountClaimed, uint256 amountUnclaimed);\n\n    /// @dev Emitted when the graph size is extended.\n    ///\n    /// @param size  The new length of the graph.\n    event GraphSizeUpdated(uint256 size);\n\n    /// @dev Emitted when the deposit cap is updated.\n    ///\n    /// @param cap  The new transmuter deposit cap.\n    event DepositCapUpdated(uint256 cap);\n\n    /// @dev Emitted when the transmutaiton time is updated.\n    ///\n    /// @param time  The new transmutation time in blocks.\n    event TransmutationTimeUpdated(uint256 time);\n\n    /// @dev Emitted when the transmutaiton fee is updated.\n    ///\n    /// @param fee  The new transmutation fee.\n    event TransmutationFeeUpdated(uint256 fee);\n\n    /// @dev Emitted when the early exit fee is updated.\n    ///\n    /// @param fee  The new exit fee.\n    event ExitFeeUpdated(uint256 fee);\n\n    /// @dev Emitted when the fee receiver is updates.\n    ///\n    /// @param recevier  The new receiver.\n    event ProtocolFeeReceiverUpdated(address recevier);\n\n    event PositionPoked(uint256 indexed id, uint256 amountRemovedFromCap);\n}\n"},{"file_path":"src/libraries/FixedPointMath.sol","source_code":"// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity ^0.8.23;\n\n/**\n * @notice A library which implements fixed point decimal math.\n */\nlibrary FixedPointMath {\n    /**\n     * @dev This will give approximately 60 bits of precision\n     */\n    uint256 public constant DECIMALS = 18;\n    uint256 public constant ONE = 10 ** DECIMALS;\n\n    error MulDivZeroDenominator();\n    error MulDivOverflow();\n\n    /**\n     * @notice A struct representing a fixed point decimal.\n     */\n    struct Number {\n        uint256 n;\n    }\n\n    /**\n     * @notice Encodes a unsigned 256-bit integer into a fixed point decimal.\n     *\n     * @param value The value to encode.\n     * @return      The fixed point decimal representation.\n     */\n    function encode(uint256 value) internal pure returns (Number memory) {\n        return Number(FixedPointMath.encodeRaw(value));\n    }\n\n    /**\n     * @notice Encodes a unsigned 256-bit integer into a uint256 representation of a\n     *         fixed point decimal.\n     *\n     * @param value The value to encode.\n     * @return      The fixed point decimal representation.\n     */\n    function encodeRaw(uint256 value) internal pure returns (uint256) {\n        return value * ONE;\n    }\n\n    /**\n     * @notice Encodes a uint256 MAX VALUE into a uint256 representation of a\n     *         fixed point decimal.\n     *\n     * @return      The uint256 MAX VALUE fixed point decimal representation.\n     */\n    function max() internal pure returns (Number memory) {\n        return Number(type(uint256).max);\n    }\n\n    /**\n     * @notice Creates a rational fraction as a Number from 2 uint256 values\n     *\n     * @param n The numerator.\n     * @param d The denominator.\n     * @return  The fixed point decimal representation.\n     */\n    function rational(uint256 n, uint256 d) internal pure returns (Number memory) {\n        Number memory numerator = encode(n);\n        return FixedPointMath.div(numerator, d);\n    }\n\n    /**\n     * @notice Adds two fixed point decimal numbers together.\n     *\n     * @param self  The left hand operand.\n     * @param value The right hand operand.\n     * @return      The result.\n     */\n    function add(Number memory self, Number memory value) internal pure returns (Number memory) {\n        return Number(self.n + value.n);\n    }\n\n    /**\n     * @notice Adds a fixed point number to a unsigned 256-bit integer.\n     *\n     * @param self  The left hand operand.\n     * @param value The right hand operand. This will be converted to a fixed point decimal.\n     * @return      The result.\n     */\n    function add(Number memory self, uint256 value) internal pure returns (Number memory) {\n        return add(self, FixedPointMath.encode(value));\n    }\n\n    /**\n     * @notice Subtract a fixed point decimal from another.\n     *\n     * @param self  The left hand operand.\n     * @param value The right hand operand.\n     * @return      The result.\n     */\n    function sub(Number memory self, Number memory value) internal pure returns (Number memory) {\n        return Number(self.n - value.n);\n    }\n\n    /**\n     * @notice Subtract a unsigned 256-bit integer from a fixed point decimal.\n     *\n     * @param self  The left hand operand.\n     * @param value The right hand operand. This will be converted to a fixed point decimal.\n     * @return      The result.\n     */\n    function sub(Number memory self, uint256 value) internal pure returns (Number memory) {\n        return sub(self, FixedPointMath.encode(value));\n    }\n\n    /**\n     * @notice Multiplies a fixed point decimal by another fixed point decimal.\n     *\n     * @param self  The fixed point decimal to multiply.\n     * @param number The fixed point decimal to multiply by.\n     * @return      The result.\n     */\n    function mul(Number memory self, Number memory number) internal pure returns (Number memory) {\n        return Number((self.n * number.n) / ONE);\n    }\n\n    /**\n     * @notice Multiplies a fixed point decimal by an unsigned 256-bit integer.\n     *\n     * @param self  The fixed point decimal to multiply.\n     * @param value The unsigned 256-bit integer to multiply by.\n     * @return      The result.\n     */\n    function mul(Number memory self, uint256 value) internal pure returns (Number memory) {\n        return Number(self.n * value);\n    }\n\n    /**\n     * @notice Divides a fixed point decimal by an unsigned 256-bit integer.\n     *\n     * @param self  The fixed point decimal to multiply by.\n     * @param value The unsigned 256-bit integer to divide by.\n     * @return      The result.\n     */\n    function div(Number memory self, uint256 value) internal pure returns (Number memory) {\n        return Number(self.n / value);\n    }\n\n    /// @notice floor(x * y / denominator) with full precision.\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            if (denominator == 0) revert MulDivZeroDenominator();\n\n            // 512-bit multiply: [prod1 prod0] = x * y\n            uint256 prod0;\n            uint256 prod1;\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow case (prod1 == 0)\n            if (prod1 == 0) {\n                return prod0 / denominator;\n            }\n\n            // Ensure result fits in 256 bits and denominator != 0\n            if (denominator <= prod1) revert MulDivOverflow();\n\n            // Make division exact by subtracting remainder from [prod1 prod0]\n            uint256 remainder;\n            assembly {\n                remainder := mulmod(x, y, denominator)\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                denominator := div(denominator, twos)\n                prod0 := div(prod0, twos)\n                // twos = 2^256 / twos\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift high bits into prod0\n            prod0 |= prod1 * twos;\n\n            // Compute modular inverse of denominator mod 2^256 (denominator is now odd)\n            uint256 inv = (3 * denominator) ^ 2;\n            inv *= 2 - denominator * inv; // 8 bits\n            inv *= 2 - denominator * inv; // 16\n            inv *= 2 - denominator * inv; // 32\n            inv *= 2 - denominator * inv; // 64\n            inv *= 2 - denominator * inv; // 128\n            inv *= 2 - denominator * inv; // 256\n\n            // Multiply by inverse to get the quotient\n            result = prod0 * inv;\n        }\n    }\n\n    /// @notice ceil(x * y / denominator) with full precision.\n    function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        result = mulDiv(x, y, denominator);\n        unchecked {\n            if (mulmod(x, y, denominator) != 0) {\n                if (result == type(uint256).max) revert MulDivOverflow();\n                result += 1;\n            }\n        }\n    }\n    \n    /**\n     * @notice Compares two fixed point decimals.\n     *\n     * @param self  The left hand number to compare.\n     * @param value The right hand number to compare.\n     * @return      When the left hand number is less than the right hand number this returns -1,\n     *              when the left hand number is greater than the right hand number this returns 1,\n     *              when they are equal this returns 0.\n     */\n    function cmp(Number memory self, Number memory value) internal pure returns (int256) {\n        if (self.n < value.n) {\n            return -1;\n        }\n\n        if (self.n > value.n) {\n            return 1;\n        }\n\n        return 0;\n    }\n\n    /**\n     * @notice Gets if two fixed point numbers are equal.\n     *\n     * @param self  the first fixed point number.\n     * @param value the second fixed point number.\n     *\n     * @return if they are equal.\n     */\n    function equals(Number memory self, Number memory value) internal pure returns (bool) {\n        return self.n == value.n;\n    }\n\n    /**\n     * @notice Truncates a fixed point decimal into an unsigned 256-bit integer.\n     *\n     * @return The integer portion of the fixed point decimal.\n     */\n    function truncate(Number memory self) internal pure returns (uint256) {\n        return self.n / ONE;\n    }\n\n    // Math helpers for Q128.128\n    function mulQ128(uint256 aQ, uint256 bQ) internal pure returns (uint256 z) {\n        if (aQ == 0 || bQ == 0) return 0;\n        uint256 lo;\n        uint256 hi;\n        assembly {\n            // 512-bit product [hi lo] = aQ * bQ\n            let mm := mulmod(aQ, bQ, not(0))\n            lo := mul(aQ, bQ)\n            hi := sub(sub(mm, lo), lt(mm, lo))\n        }\n        // floor((a*b) / 2^128)\n        z = (hi << 128) | (lo >> 128);\n        // if there are non-zero low bits, round up\n        if (lo & ((uint256(1) << 128) - 1) != 0) {\n            unchecked {\n                z += 1;\n            }\n        }\n    }\n\n    function divQ128(uint256 numerQ128, uint256 denomQ128) internal pure returns (uint256) {\n        if (numerQ128 == 0) return 0;\n        unchecked {\n            // Fast path: shifting is safe if numerQ128 < 2^128\n            if (numerQ128 <= type(uint256).max >> 128) {\n                return (numerQ128 << 128) / denomQ128;\n            }\n            // Slow path: numerQ128 can only be 2^128 here.\n            uint256 q = numerQ128 / denomQ128; // 0 or 1 in our domain\n            uint256 r = numerQ128 - q * denomQ128; // remainder\n            return (q << 128) + ((r << 128) / denomQ128);\n        }\n    }\n}"},{"file_path":"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev Interface of the ERC-20 standard as defined in the ERC.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity >=0.6.2;\n\nimport {IERC20} from \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC-20 standard.\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721.sol)\n\npragma solidity >=0.6.2;\n\nimport {IERC165} from \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC-721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n    /**\n     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n     */\n    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n     */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n     * @dev Returns the number of tokens in ``owner``'s account.\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon\n     *   a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or\n     *   {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon\n     *   a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Transfers `tokenId` token from `from` to `to`.\n     *\n     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721\n     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n     * understand this adds an external call which potentially creates a reentrancy vulnerability.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n     * The approval is cleared when the token is transferred.\n     *\n     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n     *\n     * Requirements:\n     *\n     * - The caller must own the token or be an approved operator.\n     * - `tokenId` must exist.\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address to, uint256 tokenId) external;\n\n    /**\n     * @dev Approve or remove `operator` as an operator for the caller.\n     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n     *\n     * Requirements:\n     *\n     * - The `operator` cannot be the address zero.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function setApprovalForAll(address operator, bool approved) external;\n\n    /**\n     * @dev Returns the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\n\n    /**\n     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n     *\n     * See {setApprovalForAll}\n     */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev Interface of the ERC-165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[ERC].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"},{"file_path":"lib/vault-v2/src/interfaces/IERC20.sol","source_code":"// SPDX-License-Identifier: GPL-2.0-or-later\n// Copyright (c) 2025 Morpho Association\npragma solidity >=0.5.0;\n\ninterface IERC20 {\n    function decimals() external view returns (uint8);\n    function name() external view returns (string memory);\n    function symbol() external view returns (string memory);\n    function totalSupply() external view returns (uint256);\n    function balanceOf(address account) external view returns (uint256);\n    function transfer(address to, uint256 shares) external returns (bool success);\n    function transferFrom(address from, address to, uint256 shares) external returns (bool success);\n    function approve(address spender, uint256 shares) external returns (bool success);\n    function allowance(address owner, address spender) external view returns (uint256);\n}\n"},{"file_path":"lib/vault-v2/src/interfaces/IERC2612.sol","source_code":"// SPDX-License-Identifier: GPL-2.0-or-later\n// Copyright (c) 2025 Morpho Association\npragma solidity >=0.5.0;\n\ninterface IERC2612 {\n    function permit(address owner, address spender, uint256 shares, uint256 deadline, uint8 v, bytes32 r, bytes32 s)\n        external;\n    function nonces(address owner) external view returns (uint256);\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"},{"file_path":"src/libraries/TokenUtils.sol","source_code":"pragma solidity ^0.8.23;\n\nimport \"../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\";\nimport \"../../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"../interfaces/IERC20Burnable.sol\";\nimport \"../interfaces/IERC20Mintable.sol\";\n\n/// @title  TokenUtils\n/// @author Alchemix Finance\nlibrary TokenUtils {\n    /// @notice An error used to indicate that a call to an ERC20 contract failed.\n    ///\n    /// @param target  The target address.\n    /// @param success If the call to the token was a success.\n    /// @param data    The resulting data from the call. This is error data when the call was not a success. Otherwise,\n    ///                this is malformed data when the call was a success.\n    error ERC20CallFailed(address target, bool success, bytes data);\n\n    /// @dev A safe function to get the decimals of an ERC20 token.\n    ///\n    /// @dev Reverts with a {CallFailed} error if execution of the query fails or returns an unexpected value.\n    ///\n    /// @param token The target token.\n    ///\n    /// @return The amount of decimals of the token.\n    function expectDecimals(address token) internal view returns (uint8) {\n        (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(IERC20Metadata.decimals.selector));\n\n        if (token.code.length == 0 || !success || data.length < 32) {\n            revert ERC20CallFailed(token, success, data);\n        }\n\n        return abi.decode(data, (uint8));\n    }\n\n    /// @dev Gets the balance of tokens held by an account.\n    ///\n    /// @dev Reverts with a {CallFailed} error if execution of the query fails or returns an unexpected value.\n    ///\n    /// @param token   The token to check the balance of.\n    /// @param account The address of the token holder.\n    ///\n    /// @return The balance of the tokens held by an account.\n    function safeBalanceOf(address token, address account) internal view returns (uint256) {\n        (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(IERC20.balanceOf.selector, account));\n\n        if (token.code.length == 0 || !success || data.length < 32) {\n            revert ERC20CallFailed(token, success, data);\n        }\n\n        return abi.decode(data, (uint256));\n    }\n\n    /// @dev Transfers tokens to another address.\n    ///\n    /// @dev Reverts with a {CallFailed} error if execution of the transfer failed or returns an unexpected value.\n    ///\n    /// @param token     The token to transfer.\n    /// @param recipient The address of the recipient.\n    /// @param amount    The amount of tokens to transfer.\n    function safeTransfer(address token, address recipient, uint256 amount) internal {\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, recipient, amount));\n\n        if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) {\n            revert ERC20CallFailed(token, success, data);\n        }\n    }\n\n    /// @dev Approves tokens for the smart contract.\n    ///\n    /// @dev Reverts with a {CallFailed} error if execution of the approval fails or returns an unexpected value.\n    ///\n    /// @param token   The token to approve.\n    /// @param spender The contract to spend the tokens.\n    /// @param value   The amount of tokens to approve.\n    function safeApprove(address token, address spender, uint256 value) internal {\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, spender, value));\n\n        if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) {\n            revert ERC20CallFailed(token, success, data);\n        }\n    }\n\n    /// @dev Transfer tokens from one address to another address.\n    ///\n    /// @dev Reverts with a {CallFailed} error if execution of the transfer fails or returns an unexpected value.\n    ///\n    /// @param token     The token to transfer.\n    /// @param owner     The address of the owner.\n    /// @param recipient The address of the recipient.\n    /// @param amount    The amount of tokens to transfer.\n    function safeTransferFrom(address token, address owner, address recipient, uint256 amount) internal {\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, owner, recipient, amount));\n\n        if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) {\n            revert ERC20CallFailed(token, success, data);\n        }\n    }\n\n    /// @dev Mints tokens to an address.\n    ///\n    /// @dev Reverts with a {CallFailed} error if execution of the mint fails or returns an unexpected value.\n    ///\n    /// @param token     The token to mint.\n    /// @param recipient The address of the recipient.\n    /// @param amount    The amount of tokens to mint.\n    function safeMint(address token, address recipient, uint256 amount) internal {\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20Mintable.mint.selector, recipient, amount));\n\n        if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) {\n            revert ERC20CallFailed(token, success, data);\n        }\n    }\n\n    /// @dev Burns tokens.\n    ///\n    /// Reverts with a `CallFailed` error if execution of the burn fails or returns an unexpected value.\n    ///\n    /// @param token  The token to burn.\n    /// @param amount The amount of tokens to burn.\n    function safeBurn(address token, uint256 amount) internal {\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20Burnable.burn.selector, amount));\n\n        if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) {\n            revert ERC20CallFailed(token, success, data);\n        }\n    }\n\n    /// @dev Burns tokens from its total supply.\n    ///\n    /// @dev Reverts with a {CallFailed} error if execution of the burn fails or returns an unexpected value.\n    ///\n    /// @param token  The token to burn.\n    /// @param owner  The owner of the tokens.\n    /// @param amount The amount of tokens to burn.\n    function safeBurnFrom(address token, address owner, uint256 amount) internal {\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20Burnable.burnFrom.selector, owner, amount));\n\n        if (token.code.length == 0 || !success || (data.length != 0 && !abi.decode(data, (bool)))) {\n            revert ERC20CallFailed(token, success, data);\n        }\n    }\n}"},{"file_path":"lib/vault-v2/src/interfaces/IERC4626.sol","source_code":"// SPDX-License-Identifier: GPL-2.0-or-later\n// Copyright (c) 2025 Morpho Association\npragma solidity >=0.5.0;\n\nimport {IERC20} from \"./IERC20.sol\";\n\ninterface IERC4626 is IERC20 {\n    function asset() external view returns (address);\n    function totalAssets() external view returns (uint256);\n    function convertToAssets(uint256 shares) external view returns (uint256 assets);\n    function convertToShares(uint256 assets) external view returns (uint256 shares);\n    function deposit(uint256 assets, address onBehalf) external returns (uint256 shares);\n    function mint(uint256 shares, address onBehalf) external returns (uint256 assets);\n    function withdraw(uint256 assets, address onBehalf, address receiver) external returns (uint256 shares);\n    function redeem(uint256 shares, address onBehalf, address receiver) external returns (uint256 assets);\n    function previewDeposit(uint256 assets) external view returns (uint256 shares);\n    function previewMint(uint256 shares) external view returns (uint256 assets);\n    function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n    function previewRedeem(uint256 shares) external view returns (uint256 assets);\n    function maxDeposit(address onBehalf) external view returns (uint256 assets);\n    function maxMint(address onBehalf) external view returns (uint256 shares);\n    function maxWithdraw(address onBehalf) external view returns (uint256 assets);\n    function maxRedeem(address onBehalf) external view returns (uint256 shares);\n}\n"},{"file_path":"lib/vault-v2/src/interfaces/IVaultV2.sol","source_code":"// SPDX-License-Identifier: GPL-2.0-or-later\n// Copyright (c) 2025 Morpho Association\npragma solidity >=0.5.0;\n\nimport {IERC20} from \"./IERC20.sol\";\nimport {IERC4626} from \"./IERC4626.sol\";\nimport {IERC2612} from \"./IERC2612.sol\";\n\nstruct Caps {\n    uint256 allocation;\n    uint128 absoluteCap;\n    uint128 relativeCap;\n}\n\ninterface IVaultV2 is IERC4626, IERC2612 {\n    // State variables\n    function virtualShares() external view returns (uint256);\n    function owner() external view returns (address);\n    function curator() external view returns (address);\n    function receiveSharesGate() external view returns (address);\n    function sendSharesGate() external view returns (address);\n    function receiveAssetsGate() external view returns (address);\n    function sendAssetsGate() external view returns (address);\n    function adapterRegistry() external view returns (address);\n    function isSentinel(address account) external view returns (bool);\n    function isAllocator(address account) external view returns (bool);\n    function firstTotalAssets() external view returns (uint256);\n    function _totalAssets() external view returns (uint128);\n    function lastUpdate() external view returns (uint64);\n    function maxRate() external view returns (uint64);\n    function adapters(uint256 index) external view returns (address);\n    function adaptersLength() external view returns (uint256);\n    function isAdapter(address account) external view returns (bool);\n    function allocation(bytes32 id) external view returns (uint256);\n    function absoluteCap(bytes32 id) external view returns (uint256);\n    function relativeCap(bytes32 id) external view returns (uint256);\n    function forceDeallocatePenalty(address adapter) external view returns (uint256);\n    function liquidityAdapter() external view returns (address);\n    function liquidityData() external view returns (bytes memory);\n    function timelock(bytes4 selector) external view returns (uint256);\n    function abdicated(bytes4 selector) external view returns (bool);\n    function executableAt(bytes memory data) external view returns (uint256);\n    function performanceFee() external view returns (uint96);\n    function performanceFeeRecipient() external view returns (address);\n    function managementFee() external view returns (uint96);\n    function managementFeeRecipient() external view returns (address);\n\n    // Gating\n    function canSendShares(address account) external view returns (bool);\n    function canReceiveShares(address account) external view returns (bool);\n    function canSendAssets(address account) external view returns (bool);\n    function canReceiveAssets(address account) external view returns (bool);\n\n    // Multicall\n    function multicall(bytes[] memory data) external;\n\n    // Owner functions\n    function setOwner(address newOwner) external;\n    function setCurator(address newCurator) external;\n    function setIsSentinel(address account, bool isSentinel) external;\n    function setName(string memory newName) external;\n    function setSymbol(string memory newSymbol) external;\n\n    // Timelocks for curator functions\n    function submit(bytes memory data) external;\n    function revoke(bytes memory data) external;\n\n    // Curator functions\n    function setIsAllocator(address account, bool newIsAllocator) external;\n    function setReceiveSharesGate(address newReceiveSharesGate) external;\n    function setSendSharesGate(address newSendSharesGate) external;\n    function setReceiveAssetsGate(address newReceiveAssetsGate) external;\n    function setSendAssetsGate(address newSendAssetsGate) external;\n    function setAdapterRegistry(address newAdapterRegistry) external;\n    function addAdapter(address account) external;\n    function removeAdapter(address account) external;\n    function increaseTimelock(bytes4 selector, uint256 newDuration) external;\n    function decreaseTimelock(bytes4 selector, uint256 newDuration) external;\n    function abdicate(bytes4 selector) external;\n    function setPerformanceFee(uint256 newPerformanceFee) external;\n    function setManagementFee(uint256 newManagementFee) external;\n    function setPerformanceFeeRecipient(address newPerformanceFeeRecipient) external;\n    function setManagementFeeRecipient(address newManagementFeeRecipient) external;\n    function increaseAbsoluteCap(bytes memory idData, uint256 newAbsoluteCap) external;\n    function decreaseAbsoluteCap(bytes memory idData, uint256 newAbsoluteCap) external;\n    function increaseRelativeCap(bytes memory idData, uint256 newRelativeCap) external;\n    function decreaseRelativeCap(bytes memory idData, uint256 newRelativeCap) external;\n    function setMaxRate(uint256 newMaxRate) external;\n    function setForceDeallocatePenalty(address adapter, uint256 newForceDeallocatePenalty) external;\n\n    // Allocator functions\n    function allocate(address adapter, bytes memory data, uint256 assets) external;\n    function deallocate(address adapter, bytes memory data, uint256 assets) external;\n    function setLiquidityAdapterAndData(address newLiquidityAdapter, bytes memory newLiquidityData) external;\n\n    // Exchange rate\n    function accrueInterest() external;\n    function accrueInterestView()\n        external\n        view\n        returns (uint256 newTotalAssets, uint256 performanceFeeShares, uint256 managementFeeShares);\n\n    // Force deallocate\n    function forceDeallocate(address adapter, bytes memory data, uint256 assets, address onBehalf)\n        external\n        returns (uint256 penaltyShares);\n}\n"},{"file_path":"src/base/Errors.sol","source_code":"pragma solidity ^0.8.23;\n\n/// @notice An error used to indicate that an action could not be completed because either the `msg.sender` or\n///         `msg.origin` is not authorized.\nerror Unauthorized();\n\n/// @notice An error used to indicate that an action could not be completed because the contract either already existed\n///         or entered an illegal condition which is not recoverable from.\nerror IllegalState();\n\n/// @notice An error used to indicate that an action could not be completed because of an illegal argument was passed\n///         to the function.\nerror IllegalArgument();\n\n/// @notice An error used to indicate that an action could not be completed because the required amount of allowance has not\n///         been approved.\nerror InsufficientAllowance();\n\n/// @notice An error used to indicate that the function input data is missing\nerror MissingInputData();\n\n/// @notice An error used to indicate that the function input data is missing\nerror ZeroAmount();\n\n/// @notice An error used to indicate that the function input data is missing\nerror ZeroAddress();\n"},{"file_path":"src/interfaces/IAlchemistV3.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.28;\n\n/// @notice Contract initialization parameters.\nstruct AlchemistInitializationParams {\n    // The initial admin account.\n    address admin;\n    // The ERC20 token used to represent debt. i.e. the alAsset.\n    address debtToken;\n    // The ERC20 token used to represent the underlying token of the yield token.\n    address underlyingToken;\n    // The global maximum amount of deposited collateral.\n    uint256 depositCap;\n    // The minimum collateralization between 0 and 1 exclusive\n    uint256 minimumCollateralization;\n    // The global minimum collateralization, >= minimumCollateralization.\n    uint256 globalMinimumCollateralization;\n    // The minimum collateralization for liquidation eligibility. between 1 and minimumCollateralization inclusive.\n    uint256 collateralizationLowerBound;\n    // The target collateralization ratio to restore accounts to after liquidation. Must be >= minimumCollateralization.\n    uint256 liquidationTargetCollateralization;\n    // The initial transmuter or transmuter buffer.\n    address transmuter;\n    // The fee on user debt paid to the protocol.\n    uint256 protocolFee;\n    // The address that receives protocol fees.\n    address protocolFeeReceiver;\n    // Fee paid to liquidators.\n    uint256 liquidatorFee;\n    // Fee paid to liquidators forcing an account earmarked debt repayment.\n    uint256 repaymentFee;\n    // The address of the morpho v2 vault.\n    address myt;\n}\n\n/// @notice A user account.\n/// @notice This account struct is included in the main contract, AlchemistV3.sol, to aid readability.\nstruct Account {\n    /// @notice User's collateral.\n    uint256 collateralBalance;\n    /// @notice User's debt.\n    uint256 debt;\n    /// @notice User debt earmarked for redemption.\n    uint256 earmarked;\n    /// @notice The amount of unlocked collateral.\n    uint256 freeCollateral;\n    /// @notice Last weight of earmark from most recent account sync.\n    uint256 lastAccruedEarmarkWeight;\n    /// @notice Last weight of redemption from most recent account sync.\n    uint256 lastAccruedRedemptionWeight;\n    /// @notice Last weight of collateral from most recent account sync.\n    uint256 lastCollateralWeight;\n    /// @notice Block of the most recent mint\n    uint256 lastMintBlock;\n    /// @notice Block of the most recent repay\n    uint256 lastRepayBlock;\n    /// @notice Last stored survival accumulator\n    uint256 lastSurvivalAccumulator;\n    /// @notice Total debt redeemed at last sync\n    uint256 lastTotalRedeemedDebt;\n    /// @notice Total debt paid out from redemptions at last sync\n    uint256 lastTotalRedeemedSharesOut;\n    /// @notice allowances for minting alAssets, per version.\n    mapping(uint256 => mapping(address => uint256)) mintAllowances;\n    /// @notice id used in the mintAllowances map which is incremented on reset.\n    uint256 allowancesVersion;\n}\n\n/// @notice Information associated with a redemption.\n/// @notice This redemption struct is included in the main contract, AlchemistV3.sol, to aid in calculating user debt from historic redemptions.\nstruct RedemptionInfo {\n    uint256 earmarked;\n    uint256 debt;\n    uint256 earmarkWeight;\n}\n\ninterface IAlchemistV3Actions {\n    /// @notice Approve `spender` to mint `amount` debt tokens.\n    ///\n    /// @param tokenId The tokenId of account granting approval.\n    /// @param spender The address that will be approved to mint.\n    /// @param amount  The amount of tokens that `spender` will be allowed to mint.\n\n    function approveMint(uint256 tokenId, address spender, uint256 amount) external;\n\n    /// @notice Synchronizes the state of the account owned by `owner`.\n    ///\n    /// @param tokenId   The tokenId of account\n    function poke(uint256 tokenId) external;\n\n    /// @notice Deposit a yield token into a user's account.\n    /// @notice Create a new position by using zero (0) for the `recipientId`.\n    /// @notice Users may create as many positions as they want.\n    ///\n    /// @notice An approval must be set for `yieldToken` which is greater than `amount`.\n    ///\n    /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error.\n    /// @notice `amount` must be greater than zero or the call will revert with an {IllegalArgument} error.\n    ///\n    /// @notice Emits a {Deposit} event.\n    ///\n    /// @notice **_NOTE:_** When depositing, the `AlchemistV3` contract must have **allowance()** to spend funds on behalf of **msg.sender** for at least **amount** of the **yieldToken** being deposited.  This can be done via the standard `ERC20.approve()` method.\n    ///\n    /// @notice **Example:**\n    /// @notice ```\n    /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95;\n    /// @notice uint256 amount = 50000;\n    /// @notice IERC20(ydai).approve(alchemistAddress, amount);\n    /// @notice (uint256 tokenId, uint256 debtValue) = AlchemistV3(alchemistAddress).deposit(amount, msg.sender, 0);\n    /// @notice ```\n    ///\n    /// @param amount     The amount of yield tokens to deposit.\n    /// @param recipient  The owner of the account that will receive the resulting shares.\n    /// @param recipientId The id of account.\n    /// @return tokenId The id of the account.\n    /// @return debtValue The value of deposited tokens normalized to debt token value.\n    function deposit(uint256 amount, address recipient, uint256 recipientId) external returns (uint256 tokenId, uint256 debtValue);\n\n    /// @notice Withdraw `amount` yield tokens to `recipient`.\n    ///\n    /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error.\n    ///\n    /// @notice Emits a {Withdraw} event.\n    ///\n    /// @notice **_NOTE:_** When withdrawing, th amount withdrawn must not put user over allowed LTV ratio.\n    ///\n    /// @notice **Example:**\n    /// @notice ```\n    /// @notice address ydai = 0xdA816459F1AB5631232FE5e97a05BBBb94970c95;\n    /// @notice (uint256 LTV, ) = AlchemistV3(alchemistAddress).getLoanTerms(msg.sender);\n    /// @notice (uint256 yieldTokens, ) = AlchemistV3(alchemistAddress).getCDP(tokenId);\n    /// @notice uint256 maxWithdrawableTokens = (AlchemistV3(alchemistAddress).LTV() - LTV) * yieldTokens / LTV;\n    /// @notice AlchemistV3(alchemistAddress).withdraw(maxWithdrawableTokens, msg.sender);\n    /// @notice ```\n    ///\n    /// @param amount     The number of tokens to withdraw.\n    /// @param recipient  The address of the recipient.\n    /// @param tokenId The tokenId of account.\n    ///\n    /// @return amountWithdrawn The number of yield tokens that were withdrawn to `recipient`.\n    function withdraw(uint256 amount, address recipient, uint256 tokenId) external returns (uint256 amountWithdrawn);\n\n    /// @notice Mint `amount` debt tokens.\n    ///\n    /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error.\n    /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error.\n    ///\n    /// @notice Emits a {Mint} event.\n    ///\n    /// @notice **Example:**\n    /// @notice ```\n    /// @notice uint256 amtDebt = 5000;\n    /// @notice AlchemistV3(alchemistAddress).mint(amtDebt, msg.sender);\n    /// @notice ```\n    ///\n    /// @param tokenId The tokenId of account.\n    /// @param amount    The amount of tokens to mint.\n    /// @param recipient The address of the recipient.\n    function mint(uint256 tokenId, uint256 amount, address recipient) external;\n\n    /// @notice Mint `amount` debt tokens from the account owned by `owner` to `recipient`.\n    ///\n    /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error.\n    /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error.\n    ///\n    /// @notice Emits a {Mint} event.\n    ///\n    /// @notice **_NOTE:_** The caller of `mintFrom()` must have **mintAllowance()** to mint debt from the `Account` controlled by **owner** for at least the amount of **yieldTokens** that **shares** will be converted to.  This can be done via the `approveMint()` or `permitMint()` methods.\n    ///\n    /// @notice **Example:**\n    /// @notice ```\n    /// @notice uint256 amtDebt = 5000;\n    /// @notice AlchemistV3(alchemistAddress).mintFrom(msg.sender, amtDebt, msg.sender);\n    /// @notice ```\n    ///\n    /// @param tokenId   The tokenId of account.\n    /// @param amount    The amount of tokens to mint.\n    /// @param recipient The address of the recipient.\n    function mintFrom(uint256 tokenId, uint256 amount, address recipient) external;\n\n    /// @notice Burn `amount` debt tokens to credit the account owned by `recipientId`.\n    ///\n    /// @notice `amount` will be limited up to the amount of unearmarked debt that `recipient` currently holds.\n    ///\n    /// @notice `recipientId` must be non-zero or this call will revert with an {IllegalArgument} error.\n    /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error.\n    /// @notice account for `recipientId` must have non-zero debt or this call will revert with an {IllegalState} error.\n    ///\n    /// @notice Emits a {Burn} event.\n    ///\n    /// @notice **Example:**\n    /// @notice ```\n    /// @notice uint256 amtBurn = 5000;\n    /// @notice AlchemistV3(alchemistAddress).burn(amtBurn, 420);\n    /// @notice ```\n    ///\n    /// @param amount    The amount of tokens to burn.\n    /// @param recipientId   The tokenId of account to being credited.\n    ///\n    /// @return amountBurned The amount of tokens that were burned.\n    function burn(uint256 amount, uint256 recipientId) external returns (uint256 amountBurned);\n\n    /// @notice Repay `amount` debt using yield tokens to credit the account owned by `recipientId`.\n    ///\n    /// @notice `amount` will be limited up to the amount of debt that `recipient` currently holds.\n    ///\n    /// @notice `amount` must be greater than zero or this call will revert with a {IllegalArgument} error.\n    /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error.\n    ///\n    /// @notice Emits a {Repay} event.\n    ///\n    /// @notice **Example:**\n    /// @notice ```\n    /// @notice uint256 amtRepay = 5000;\n    /// @notice AlchemistV3(alchemistAddress).repay(amtRepay, msg.sender);\n    /// @notice ```\n    ///\n    /// @param amount          The amount of the yield tokens to repay with.\n    /// @param recipientTokenId   The tokenId of account to be repaid\n    ///\n    /// @return amountRepaid The amount of tokens that were repaid.\n    function repay(uint256 amount, uint256 recipientTokenId) external returns (uint256 amountRepaid);\n\n    /**\n     * @notice Liquidates `owner` if the debt for account `owner` is greater than the underlying value of their collateral * LTV.\n     *\n     * @notice `owner` must be non-zero or this call will revert with an {IllegalArgument} error.\n     *\n     * @notice Emits a {Liquidate} event.\n     *\n     * @notice **Example:**\n     * @notice ```\n     * @notice AlchemistV2(alchemistAddress).liquidate(id4);\n     * @notice ```\n     *\n     * @param accountId   The tokenId of account\n     *\n     * @return yieldAmount         Yield tokens sent to the transmuter.\n     * @return feeInYield          Fee paid to liquidator in yield tokens.\n     * @return feeInUnderlying     Fee paid to liquidator in underlying token.\n     */\n    function liquidate(uint256 accountId) external returns (uint256 yieldAmount, uint256 feeInYield, uint256 feeInUnderlying);\n\n\n    /// @notice Self liquidates the account owned by `accountId`.\n    ///\n    /// @notice `accountId` must be non-zero or this call will revert with an {IllegalArgument} error.\n    /// @notice `recipient` must be non-zero or this call will revert with an {IllegalArgument} error.\n    /// @notice `accountId` must be owned by `msg.sender` or this call will revert with an {Unauthorized} error.\n    /// @notice `accountId` must be healthy (overcollateralized) or this call will revert with an {AccountNotHealthy} error.\n    /// @notice in the event that account is not healthy, the account can only be liquidated using the regular liquidation path. (i.e. liquidate(accountId))\n    ///\n    /// @notice Emits a {SelfLiquidated} event.\n    ///\n    /// @param accountId The tokenId of account to be self liquidated.\n    /// @param recipient The address of the recipient.\n    function selfLiquidate(uint256 accountId, address recipient) external returns (uint256 amountLiquidated);\n\n    /// @notice Liquidates `owners` if the debt for account `owner` is greater than the underlying value of their collateral * LTV.\n    ///\n    /// @notice `owner` must be non-zero or this call will revert with an {IllegalArgument} error.\n    ///\n    ///\n    /// @notice **Example:**\n    /// @notice ```\n    /// @notice AlchemistV3(alchemistAddress).batchLiquidate([id1, id35]);\n    /// @notice ```\n    ///\n    /// @param accountIds   The tokenId of each account\n    ///\n    /// @return totalAmountLiquidated   Amount in yield tokens sent to the transmuter.\n    /// @return totalFeesInYield        Amount sent to liquidator in yield tokens.\n    /// @return totalFeesInUnderlying   Amount sent to liquidator in underlying token.\n    function batchLiquidate(uint256[] memory accountIds)\n        external\n        returns (uint256 totalAmountLiquidated, uint256 totalFeesInYield, uint256 totalFeesInUnderlying);\n\n    /// @notice Redeems `amount` debt from the alchemist in exchange for yield tokens sent to the transmuter.\n    ///\n    /// @notice This function is only callable by the transmuter.\n    ///\n    /// @notice Emits a {Redeem} event.\n    ///\n    /// @param amount The amount of tokens to redeem.\n    function redeem(uint256 amount) external returns (uint256 sharesSent);\n\n    /// @notice Reduces syntheticTokensIssued by `amount`.\n    ///\n    /// @notice This function is only callable by the transmuter.\n    ///\n    /// @param amount The amount of tokens burned during redemption.\n    function reduceSyntheticsIssued(uint256 amount) external;\n\n    /// @notice Sets lastTransmuterTokenBalance to `amount`.\n    ///\n    /// @notice This function is only callable by the transmuter.\n    ///\n    /// @param amount The balance of the transmuter.\n    function setTransmuterTokenBalance(uint256 amount) external;\n\n    /// @notice Resets all mint allowances by account managed by `tokenId`.\n    ///\n    /// @notice This function is only callable by the owner of the token id or the AlchemistV3Position contract.\n    ///\n    /// @notice Emits a {MintAllowancesReset} event.\n    ///\n    /// @param tokenId The token id of the account.\n    function resetMintAllowances(uint256 tokenId) external;\n}\n\ninterface IAlchemistV3AdminActions {\n    /// @notice Sets the pending administrator.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {PendingAdminUpdated} event.\n    ///\n    /// @dev This is the first step in the two-step process of setting a new administrator. After this function is called, the pending administrator will then need to call {acceptAdmin} to complete the process.\n    ///\n    /// @param value The address to set the pending admin to.\n    function setPendingAdmin(address value) external;\n\n    /// @notice Sets the active state of a guardian.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {GuardianSet} event.\n    ///\n    /// @param guardian The address of the target guardian.\n    /// @param isActive The active state to set for the guardian.\n    function setGuardian(address guardian, bool isActive) external;\n\n    /// @notice Allows for `msg.sender` to accepts the role of administrator.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    /// @notice The current pending administrator must be non-zero or this call will revert with an {IllegalState} error.\n    ///\n    /// @dev This is the second step in the two-step process of setting a new administrator. After this function is successfully called, this pending administrator will be reset and the new administrator will be set.\n    ///\n    /// @notice Emits a {AdminUpdated} event.\n    /// @notice Emits a {PendingAdminUpdated} event.\n    function acceptAdmin() external;\n\n    /// @notice Set a new alchemist deposit cap.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {DepositCapUpdated} event.\n    ///\n    /// @param value The value of the new deposit cap.\n    function setDepositCap(uint256 value) external;\n\n    /// @notice Sets the token adapter for the yield token.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {TokenAdapterSet} event.\n    ///\n    /// @param value The address of token adapter.\n    function setTokenAdapter(address value) external;\n\n    /// @notice Set the minimum collateralization ratio.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {MinimumCollateralizationUpdated} event.\n    ///\n    /// @param value The new minimum collateralization ratio.\n    function setMinimumCollateralization(uint256 value) external;\n\n    /// @notice Set a new protocol fee receiver.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {ProtocolFeeReceiverUpdated} event.\n    ///\n    /// @param receiver The address of the new fee receiver.\n    function setProtocolFeeReceiver(address receiver) external;\n\n    /// @notice Set a new protocol debt fee.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {ProtocolFeeUpdated} event.\n    ///\n    /// @param fee The new protocol debt fee.\n    function setProtocolFee(uint256 fee) external;\n\n    /// @notice Set a new liquidator fee.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {LiquidatorFeeUpdated} event.\n    ///\n    /// @param fee The new liquidator fee.\n    function setLiquidatorFee(uint256 fee) external;\n\n    /// @notice Set a new repayment fee.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {RepaymentFeeUpdated} event.\n    ///\n    /// @param fee The new repayment fee.\n    function setRepaymentFee(uint256 fee) external;\n\n    /// @notice Set the global minimum collateralization ratio.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {GlobalMinimumCollateralizationUpdated} event.\n    ///\n    /// @param value The new global minimum collateralization ratio.\n    function setGlobalMinimumCollateralization(uint256 value) external;\n\n    /// @notice Set the collateralization lower bound ratio.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {CollateralizationLowerBoundUpdated} event.\n    ///\n    /// @param value The new collateralization lower bound ratio.\n    function setCollateralizationLowerBound(uint256 value) external;\n\n    /// @notice Set the liquidation target collateralization ratio.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {LiquidationTargetCollateralizationUpdated} event.\n    ///\n    /// @param value The new liquidation target collateralization ratio.\n    function setLiquidationTargetCollateralization(uint256 value) external;\n\n    /// @notice Pause all future deposits in the Alchemist.\n    ///\n    /// @notice `msg.sender` must be the admin or guardian or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {DepositsPaused} event.\n    ///\n    /// @param isPaused The new pause state for deposits in the alchemist.\n    function pauseDeposits(bool isPaused) external;\n\n    /// @notice Pause all future loans in the Alchemist.\n    ///\n    /// @notice `msg.sender` must be the admin or guardian or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {LoansPaused} event.\n    ///\n    /// @param isPaused The new pause state for loans in the alchemist.\n    function pauseLoans(bool isPaused) external;\n\n    /// @notice Set the alchemist Fee vault.\n    ///\n    /// @notice `msg.sender` must be the admin or this call will revert with an {Unauthorized} error.\n    ///\n    /// @notice Emits a {AlchemistFeeVaultUpdated} event.\n    ///\n    /// @param value The address of the new alchemist Fee vault.\n    function setAlchemistFeeVault(address value) external;\n}\n\ninterface IAlchemistV3Events {\n    /// @notice Emitted when the pending admin is updated.\n    ///\n    /// @param pendingAdmin The address of the pending admin.\n    event PendingAdminUpdated(address pendingAdmin);\n\n    /// @notice Emitted when the alchemist Fee vault is updated.\n    ///\n    /// @param alchemistFeeVault The address of the alchemist Fee vault.\n    event AlchemistFeeVaultUpdated(address alchemistFeeVault);\n\n    /// @notice Emitted when the administrator is updated.\n    ///\n    /// @param admin The address of the administrator.\n    event AdminUpdated(address admin);\n\n    /// @notice Emitted when the deposit cap is updated.\n    ///\n    /// @param value The value of the new deposit cap.\n    event DepositCapUpdated(uint256 value);\n\n    /// @notice Emitted when a guardian is added or removed from the alchemist.\n    ///\n    /// @param guardian The addres of the new guardian.\n    /// @param state    The active state of the guardian.\n    event GuardianSet(address guardian, bool state);\n\n    /// @notice Emitted when a new token adapter is set in the alchemist.\n    ///\n    /// @param adapter The addres of the new adapter.\n    event TokenAdapterUpdated(address adapter);\n\n    /// @notice Emitted when the transmuter is updated.\n    ///\n    /// @param transmuter The updated address of the transmuter.\n    event TransmuterUpdated(address transmuter);\n\n    /// @notice Emitted when the minimum collateralization is updated.\n    ///\n    /// @param minimumCollateralization The updated minimum collateralization.\n    event MinimumCollateralizationUpdated(uint256 minimumCollateralization);\n\n    /// @notice Emitted when the global minimum collateralization is updated.\n    ///\n    /// @param globalMinimumCollateralization The updated global minimum collateralization.\n    event GlobalMinimumCollateralizationUpdated(uint256 globalMinimumCollateralization);\n\n    /// @notice Emitted when the collateralization lower bound (for a liquidation) is updated.\n    ///\n    /// @param collateralizationLowerBound The updated collateralization lower bound.\n    event CollateralizationLowerBoundUpdated(uint256 collateralizationLowerBound);\n\n    /// @notice Emitted when the liquidation target collateralization ratio is updated.\n    ///\n    /// @param liquidationTargetCollateralization The updated liquidation target collateralization.\n    event LiquidationTargetCollateralizationUpdated(uint256 liquidationTargetCollateralization);\n\n    /// @notice Emitted when deposits are paused or unpaused in the alchemist.\n    ///\n    /// @param isPaused The current pause state of deposits in the alchemist.\n    event DepositsPaused(bool isPaused);\n\n    /// @notice Emitted when loans are paused or unpaused in the alchemist.\n    ///\n    /// @param isPaused The current pause state of loans in the alchemist.\n    event LoansPaused(bool isPaused);\n\n    /// @notice Emitted when `owner` grants `spender` the ability to mint debt tokens on its behalf.\n    ///\n    /// @param ownerTokenId   The id of the account authorized to grant approval\n    /// @param spender The address which is being permitted to mint tokens on the behalf of `owner`.\n    /// @param amount  The amount of debt tokens that `spender` is allowed to mint.\n    event ApproveMint(uint256 indexed ownerTokenId, address indexed spender, uint256 amount);\n\n    /// @notice Emitted when a user deposits `amount of yieldToken to `recipient`.\n    ///\n    /// @notice This event does not imply that `sender` directly deposited yield tokens. It is possible that the\n    ///         underlying tokens were wrapped.\n    ///\n    /// @param amount       The amount of yield tokens that were deposited.\n    /// @param recipientId    The id of the account that received the deposited funds.\n    event Deposit(uint256 amount, uint256 indexed recipientId);\n\n    /// @notice Emitted when yieldToken is withdrawn from the account owned.\n    ///         by `owner` to `recipient`.\n    ///\n    /// @notice This event does not imply that `recipient` received yield tokens. It is possible that the yield tokens\n    ///         were unwrapped.\n    ///\n    /// @param amount     Amount of tokens withdrawn.\n    /// @param tokenId The id of the account that the funds are withdrawn from.\n    /// @param recipient  The address that received the withdrawn funds.\n    event Withdraw(uint256 amount, uint256 indexed tokenId, address recipient);\n\n    /// @notice Emitted when `amount` debt tokens are minted to `recipient` using the account owned by `owner`.\n    ///\n    /// @param tokenId     The tokenId of the account owner.\n    /// @param amount    The amount of tokens that were minted.\n    /// @param recipient The recipient of the minted tokens.\n    event Mint(uint256 indexed tokenId, uint256 amount, address recipient);\n\n    /// @notice Emitted when `sender` burns `amount` debt tokens to grant credit to  account owner `recipientId`.\n    ///\n    /// @param amount    The amount of tokens that were burned.\n    /// @param recipientId The token id of account owned by recipientId that received credit for the burned tokens.\n    event Burn(address indexed sender, uint256 amount, uint256 indexed recipientId);\n\n    /// @notice Emitted when `amount` of `underlyingToken` are repaid to grant credit to account owned by `recipientId`.\n    ///\n    /// @param sender          The address which is repaying tokens.\n    /// @param amount          The amount of the underlying token that was used to repay debt.\n    /// @param recipientId     The id of account that received credit for the repaid tokens.\n    /// @param credit          The amount of debt that was paid-off to the account owned by owner.\n    event Repay(address indexed sender, uint256 amount, uint256 indexed recipientId, uint256 credit);\n\n    /// @notice Emitted when the transmuter triggers a redemption.\n    ///\n    /// @param amount   The amount of debt to redeem.\n    event Redemption(uint256 amount);\n\n    /// @notice Emitted when the protocol debt fee is updated.\n    ///\n    /// @param fee  The new protocol fee.\n    event ProtocolFeeUpdated(uint256 fee);\n\n    /// @notice Emitted when the liquidator fee is updated.\n    ///\n    /// @param fee  The new liquidator fee.\n    event LiquidatorFeeUpdated(uint256 fee);\n\n    /// @notice Emitted when the repayment fee is updated.\n    ///\n    /// @param fee  The new repayment fee.\n    event RepaymentFeeUpdated(uint256 fee);\n\n    /// @notice Emitted when the fee receiver is updated.\n    ///\n    /// @param receiver   The address of the new receiver.\n    event ProtocolFeeReceiverUpdated(address receiver);\n\n    /// @notice Emitted when account owned by 'accountId' has been liquidated.\n    ///\n    /// @param accountId        The token id of the account liquidated\n    /// @param liquidator   The address of the liquidator\n    /// @param amount       The amount liquidated in yield tokens\n    /// @param feeInYield          The liquidation fee sent to 'liquidator' in yield tokens.\n    /// @param feeInUnderlying            The liquidation fee sent to 'liquidator' in ETH (if needed i.e. if there isn't enough remaining collateral to cover the fee).\n    event Liquidated(uint256 indexed accountId, address liquidator, uint256 amount, uint256 feeInYield, uint256 feeInUnderlying);\n\n    /// @notice Emitted when account owned by 'accountId' has been self liquidated.\n    ///\n    /// @param accountId        The token id of the account self liquidated\n    /// @param amountLiquidated The amount liquidated in yield tokens\n    event SelfLiquidated(uint256 indexed accountId, uint256 amountLiquidated);\n\n    /// @notice Emitted when account for 'owner' has been liquidated.\n    ///\n    /// @param accounts       The address of the accounts liquidated\n    /// @param liquidator   The address of the liquidator\n    /// @param amount       The amount liquidated\n    /// @param feeInYield          The liquidation fee sent to 'liquidator' in yield tokens.\n    /// @param feeInETH            The liquidation fee sent to 'liquidator' in ETH (if needed i.e. if there isn't enough remaining collateral to cover the fee).\n    event BatchLiquidated(uint256[] indexed accounts, address liquidator, uint256 amount, uint256 feeInYield, uint256 feeInETH);\n\n    /// @notice Emitted when all mint allowances for account managed by `tokenId` are reset.\n    ///\n    /// @param tokenId       The tokenId of the account.\n    event MintAllowancesReset(uint256 indexed tokenId);\n\n    /// @notice Emitted when `amount` of debt is force repaid from `accountId`.\n    ///\n    /// @param accountId       The tokenId of the account.\n    /// @param amount          The amount of debt repaid.\n    /// @param creditToYield   The amount of collateral used to repay the debt in yield tokens.\n    /// @param protocolFeeTotal The amount of protocol fee paid.\n    event ForceRepay(uint256 indexed accountId, uint256 amount, uint256 creditToYield, uint256 protocolFeeTotal);\n\n    /// @notice Emitted when `amount` of debt is repaid from `accountId`.\n    ///\n    /// @param accountId       The tokenId of the account.\n    /// @param feeReciever     The address of the fee receiver.\n    /// @param feeInYield      The amount of fee paid in yield tokens.\n    /// @param feeInUnderlying The amount of fee paid in underlying tokens.\n    event RepaymentFee(uint256 indexed accountId, address feeReciever, uint256 feeInYield, uint256 feeInUnderlying);\n\n    /// @notice Emitted when a fee-vault payout is short of the requested amount.\n    ///\n    /// @param liquidator The liquidator receiving the payout.\n    /// @param requested  The requested fee amount in underlying tokens.\n    /// @param paid       The amount actually paid from the fee vault.\n    event FeeShortfall(address indexed liquidator, uint256 requested, uint256 paid);\n\n\n    /// @notice Emitted when a new Position NFT is minted.\n    ///\n    /// @param to The address of the account that minted the Position NFT.\n    /// @param tokenId The tokenId of the Position NFT.\n    event AlchemistV3PositionNFTMinted(address indexed to, uint256 indexed tokenId);\n}\n\ninterface IAlchemistV3Immutables {\n    /// @notice Returns the version of the alchemist.\n    ///\n    /// @return The version.\n    function version() external view returns (string memory);\n\n    /// @notice Returns the address of the debt token used by the system.\n    ///\n    /// @return The address of the debt token.\n    function debtToken() external view returns (address);\n}\n\ninterface IAlchemistV3State {\n    /// @notice Gets the address of the admin.\n    ///\n    /// @return admin The admin address.\n    function admin() external view returns (address admin);\n\n    function depositCap() external view returns (uint256 cap);\n\n    function guardians(address guardian) external view returns (bool isActive);\n\n    function cumulativeEarmarked() external view returns (uint256 earmarked);\n\n    function lastEarmarkBlock() external view returns (uint256 block);\n\n    function lastRedemptionBlock() external view returns (uint256 block);\n\n    function lastTransmuterTokenBalance() external view returns (uint256 balance);\n\n    function totalDebt() external view returns (uint256 debt);\n\n    function totalSyntheticsIssued() external view returns (uint256 syntheticAmount);\n\n    function protocolFee() external view returns (uint256 fee);\n\n    function liquidatorFee() external view returns (uint256 fee);\n\n    function repaymentFee() external view returns (uint256 fee);\n\n    function underlyingConversionFactor() external view returns (uint256 factor);\n\n    function protocolFeeReceiver() external view returns (address receiver);\n\n    function underlyingToken() external view returns (address token);\n\n    function myt() external view returns (address token);\n\n    function depositsPaused() external view returns (bool isPaused);\n\n    function loansPaused() external view returns (bool isPaused);\n\n    function alchemistPositionNFT() external view returns (address nftContract);\n\n    /// @notice Gets the address of the pending administrator.\n    ///\n    /// @return pendingAdmin The pending administrator address.\n    function pendingAdmin() external view returns (address pendingAdmin);\n\n    /// @notice Gets the address of the current yield token adapter.\n    ///\n    /// @return adapter The token adapter address.\n    function tokenAdapter() external returns (address adapter);\n\n    /// @notice Gets the address of the alchemist fee vault.\n    ///\n    /// @return vault The alchemist fee vault address.\n    function alchemistFeeVault() external view returns (address vault);\n\n    /// @notice Gets the address of the transmuter.\n    ///\n    /// @return transmuter The transmuter address.\n    function transmuter() external view returns (address transmuter);\n\n    /// @notice Gets the minimum collateralization.\n    ///\n    /// @notice Collateralization is determined by taking the total value of collateral that a user has deposited into their account and dividing it their debt.\n    ///\n    /// @dev The value returned is a 18 decimal fixed point integer.\n    ///\n    /// @return minimumCollateralization The minimum collateralization.\n    function minimumCollateralization() external view returns (uint256 minimumCollateralization);\n\n    /// @notice Gets the global minimum collateralization.\n    ///\n    /// @notice Collateralization is determined by taking the total value of collateral deposited in the alchemist and dividing it by the total debt.\n    ///\n    /// @dev The value returned is a 18 decimal fixed point integer.\n    ///\n    /// @return globalMinimumCollateralization The global minimum collateralization.\n    function globalMinimumCollateralization() external view returns (uint256 globalMinimumCollateralization);\n\n    ///  @notice Gets collaterlization level that will result in an account being eligible for partial liquidation\n    function collateralizationLowerBound() external view returns (uint256 ratio);\n\n    /// @notice Gets the liquidation target collateralization ratio.\n    ///\n    /// @notice This is the collateralization ratio that accounts are restored to after liquidation.\n    ///\n    /// @dev The value returned is a 18 decimal fixed point integer.\n    ///\n    /// @return liquidationTargetCollateralization The liquidation target collateralization.\n    function liquidationTargetCollateralization() external view returns (uint256 liquidationTargetCollateralization);\n\n    /// @dev Returns the debt value of `amount` yield tokens.\n    ///\n    /// @param amount   The amount to convert.\n    function convertYieldTokensToDebt(uint256 amount) external view returns (uint256);\n\n    /// @dev Returns the underlying value of `amount` yield tokens.\n    ///\n    /// @param amount   The amount to convert.\n    function convertYieldTokensToUnderlying(uint256 amount) external view returns (uint256);\n\n    /// @dev Returns the yield token value of `amount` debt tokens.\n    ///\n    /// @param amount   The amount to convert.\n    function convertDebtTokensToYield(uint256 amount) external view returns (uint256);\n\n    /// @dev Returns the yield token value of `amount` underlying tokens.\n    ///\n    /// @param amount   The amount to convert.\n    function convertUnderlyingTokensToYield(uint256 amount) external view returns (uint256);\n\n    /// @notice Calculates fee, net debt burn, and gross collateral seize,\n    ///         using a single minCollateralization factor (FIXED_POINT_SCALAR scaled).\n    /// @param collateral               Current collateral value\n    /// @param debt                     Current debt value\n    /// @param targetCollateralization  Target collateralization ratio, (e.g. 100/90 =  1.1111e18 for 111.11%)\n    /// @param alchemistCurrentCollateralization Current collateralization ratio of the alchemist\n    /// @param alchemistMinimumCollateralization Minimum collateralization ratio of the alchemist to trigger full liquidation\n    /// @param feeBps                   Fee in basis points on the surplus (0–10000)\n    /// @return grossCollateralToSeize  Total collateral to take (fee + net)\n    /// @return debtToBurn              Amount of debt to erase (sent to protocol)\n    /// @return fee                     Amount of collateral paid to liquidator\n    /// @return outsourcedFee           Amount of fee paid to liquidator in underlying tokens in the event that account funds are insufficient to cover the fee\n    function calculateLiquidation(\n        uint256 collateral,\n        uint256 debt,\n        uint256 targetCollateralization,\n        uint256 alchemistCurrentCollateralization,\n        uint256 alchemistMinimumCollateralization,\n        uint256 feeBps\n    ) external view returns (uint256 grossCollateralToSeize, uint256 debtToBurn, uint256 fee, uint256 outsourcedFee);\n\n    /// @dev Normalizes underlying tokens to debt tokens.\n    /// @notice This is to handle decimal conversion in the case where underlying tokens have < 18 decimals.\n    ///\n    /// @param amount   The amount to convert.\n    function normalizeUnderlyingTokensToDebt(uint256 amount) external view returns (uint256);\n\n    /// @dev Normalizes debt tokens to underlying tokens.\n    /// @notice This is to handle decimal conversion in the case where underlying tokens have < 18 decimals.\n    ///\n    /// @param amount   The amount to convert.\n    function normalizeDebtTokensToUnderlying(uint256 amount) external view returns (uint256);\n\n    /// @dev Get information about CDP of tokenId\n    ///\n    /// @param  tokenId   The token Id of the account.\n    ///\n    /// @return collateral  Collateral balance.\n    /// @return debt        Current debt.\n    /// @return earmarked   Current debt that is earmarked for redemption.\n    function getCDP(uint256 tokenId) external view returns (uint256 collateral, uint256 debt, uint256 earmarked);\n\n    /// @dev Gets total value of account managed by `tokenId` in units of underlying tokens.\n    ///\n    /// @param tokenId    tokenId of the account to query.\n    ///\n    /// @return value   Underlying value of the account.\n    function totalValue(uint256 tokenId) external view returns (uint256 value);\n\n    /// @dev Gets total value deposited in the alchemist\n    ///\n    /// @return amount   Total deposite amount.\n    function getTotalDeposited() external view returns (uint256 amount);\n\n    /// @dev Gets maximum debt that `user` can borrow from their CDP.\n    ///\n    /// @param tokenId    tokenId of the account to query.\n    ///\n    /// @return maxDebt   Maximum debt that can be taken.\n    function getMaxBorrowable(uint256 tokenId) external view returns (uint256 maxDebt);\n\n    \n    /// @notice Returns the maximum MYT shares that could be withdrawn from a position\n    ///\n    /// @param tokenId    tokenId of the account to query.\n    ///\n    /// @return maxWithraw   Maximum yield tokens that can be withdrawn.\n    function getMaxWithdrawable(uint256 tokenId) external view returns (uint256 maxWithraw);\n\n    /// @dev Gets total underlying value deposited in the alchemist.\n    ///\n    /// @return TVL   Total value deposited.\n    function getTotalUnderlyingValue() external view returns (uint256 TVL);\n\n    /// @dev Gets total underlying value locked in the alchemist.\n    ///\n    /// @return TVL   Total value locked.\n    function getTotalLockedUnderlyingValue() external view returns (uint256);\n\n    /// @notice Gets the amount of debt tokens `spender` is allowed to mint on behalf of `owner`.\n    ///\n    /// @param ownerTokenId    tokenId of the account to query.\n    /// @param spender The address which is allowed to mint on behalf of `owner`.\n    ///\n    /// @return allowance The amount of debt tokens that `spender` can mint on behalf of `owner`.\n    function mintAllowance(uint256 ownerTokenId, address spender) external view returns (uint256 allowance);\n}\n\ninterface IAlchemistV3Errors {\n    /// @notice An error which is used to indicate that an operation failed because an account became undercollateralized.\n    error Undercollateralized();\n\n    /// @notice An error which is used to indicate that a liquidate operation failed because an account is sufficiaenly collateralized.\n    error LiquidationError();\n\n    /// @notice An error which is used to indicate that a user is performing an action on an account that requires account ownership\n    error UnauthorizedAccountAccessError();\n\n    /// @notice An error which is used to indicate that a burn operation failed because the transmuter requires more debt in the system.\n    ///\n    /// @param amount    The amount of debt tokens that were requested to be burned.\n    /// @param available The amount of debt tokens which can be burned;\n    error BurnLimitExceeded(uint256 amount, uint256 available);\n\n    /// @notice An error which is used to indicate that the account id used is not linked to any owner\n    error UnknownAccountOwnerIDError();\n\n    /// @notice An error which is used to indicate that the NFT address being set is the zero address\n    error AlchemistV3NFTZeroAddressError();\n\n    /// @notice An error which is used to indicate that the NFT address for the Alchemist has already been set\n    error AlchemistV3NFTAlreadySetError();\n\n    /// @notice An error which is used to indicate that the token address for the AlchemistTokenVault does not match the underlyingToken\n    error AlchemistVaultTokenMismatchError();\n\n    /// @notice An error which is used to indicate that a user is trying to repay on the same block they are minting\n    error CannotRepayOnMintBlock();\n    error CannotMintOnRepayBlock();\n\n    error GlobalCollateralizationTooLow(uint256, uint256);\n\n    /// @notice An error which is used to indicate that an account is not healthy\n    error AccountNotHealthy();\n}\n\n/// @title  IAlchemistV3\n/// @author Alchemix Finance\ninterface IAlchemistV3 is IAlchemistV3Actions, IAlchemistV3AdminActions, IAlchemistV3Errors, IAlchemistV3Immutables, IAlchemistV3Events, IAlchemistV3State {}\n"},{"file_path":"src/interfaces/IAlchemistV3Position.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nimport {IERC721Enumerable} from \"../interfaces/IERC721Enumerable.sol\";\n\n/**\n * @title IAlchemistV3Position\n * @notice Interface for the AlchemistV3Position ERC721 token.\n */\ninterface IAlchemistV3Position is IERC721Enumerable {\n    /**\n     * @notice Mints a new position NFT to the specified address.\n     * @param to The recipient address for the new position.\n     * @return tokenId The unique token ID minted.\n     */\n    function mint(address to) external returns (uint256);\n\n    /**\n     * @notice Burns the NFT with the specified token ID.\n     * @param tokenId The ID of the token to burn.\n     */\n    function burn(uint256 tokenId) external;\n\n    /**\n     * @notice Returns the address of the AlchemistV3 contract which is allowed to mint and burn tokens.\n     */\n    function alchemist() external view returns (address);\n\n    /**\n     * @notice Returns the address of the admin allowed to update the metadata renderer.\n     */\n    function admin() external view returns (address);\n\n    /**\n     * @notice Returns the address of the current metadata renderer contract.\n     */\n    function metadataRenderer() external view returns (address);\n\n    /**\n     * @notice Sets or updates the metadata renderer contract. Only callable by the admin.\n     * @param renderer The address of the new metadata renderer.\n     */\n    function setMetadataRenderer(address renderer) external;\n\n    /**\n     * @notice Transfers admin rights to a new address. Only callable by the current admin.\n     * @param newAdmin The address of the new admin.\n     */\n    function setAdmin(address newAdmin) external;\n\n    /**\n     * @dev Returns the total amount of tokens stored by the contract.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.\n     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.\n     */\n    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);\n\n    /**\n     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.\n     * Use along with {totalSupply} to enumerate all tokens.\n     */\n    function tokenByIndex(uint256 index) external view returns (uint256);\n}\n"},{"file_path":"src/interfaces/IERC20Burnable.sol","source_code":"pragma solidity >=0.5.0;\n\nimport \"../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\";\n\n/// @title  IERC20Burnable\n/// @author Alchemix Finance\ninterface IERC20Burnable is IERC20 {\n    /// @notice Burns `amount` tokens from the balance of `msg.sender`.\n    ///\n    /// @param amount The amount of tokens to burn.\n    ///\n    /// @return If burning the tokens was successful.\n    function burn(uint256 amount) external returns (bool);\n\n    /// @notice Burns `amount` tokens from `owner`'s balance.\n    ///\n    /// @param owner  The address to burn tokens from.\n    /// @param amount The amount of tokens to burn.\n    ///\n    /// @return If burning the tokens was successful.\n    function burnFrom(address owner, uint256 amount) external returns (bool);\n}"},{"file_path":"src/interfaces/IERC20Mintable.sol","source_code":"pragma solidity >=0.5.0;\n\nimport \"../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\";\n\n/// @title  IERC20Mintable\n/// @author Alchemix Finance\ninterface IERC20Mintable is IERC20 {\n    /// @notice Mints `amount` tokens to `recipient`.\n    ///\n    /// @param recipient The address which will receive the minted tokens.\n    /// @param amount    The amount of tokens to mint.\n    function mint(address recipient, uint256 amount) external;\n}"},{"file_path":"src/interfaces/IERC721Enumerable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC721} from \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721Enumerable is IERC721 {\n    /**\n     * @dev Returns the total amount of tokens stored by the contract.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.\n     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.\n     */\n    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);\n\n    /**\n     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.\n     * Use along with {totalSupply} to enumerate all tokens.\n     */\n    function tokenByIndex(uint256 index) external view returns (uint256);\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountNotHealthy","type":"error"},{"inputs":[],"name":"AlchemistV3NFTAlreadySetError","type":"error"},{"inputs":[],"name":"AlchemistV3NFTZeroAddressError","type":"error"},{"inputs":[],"name":"AlchemistVaultTokenMismatchError","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"BurnLimitExceeded","type":"error"},{"inputs":[],"name":"CannotMintOnRepayBlock","type":"error"},{"inputs":[],"name":"CannotRepayOnMintBlock","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"ERC20CallFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"GlobalCollateralizationTooLow","type":"error"},{"inputs":[],"name":"IllegalArgument","type":"error"},{"inputs":[],"name":"IllegalState","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"LiquidationError","type":"error"},{"inputs":[],"name":"MissingInputData","type":"error"},{"inputs":[],"name":"MulDivOverflow","type":"error"},{"inputs":[],"name":"MulDivZeroDenominator","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnauthorizedAccountAccessError","type":"error"},{"inputs":[],"name":"Undercollateralized","type":"error"},{"inputs":[],"name":"UnknownAccountOwnerIDError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"AdminUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"alchemistFeeVault","type":"address"}],"name":"AlchemistFeeVaultUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AlchemistV3PositionNFTMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"ownerTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ApproveMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256[]","name":"accounts","type":"uint256[]"},{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeInYield","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeInETH","type":"uint256"}],"name":"BatchLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"recipientId","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"collateralizationLowerBound","type":"uint256"}],"name":"CollateralizationLowerBoundUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"recipientId","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"DepositCapUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"DepositsPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"requested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid","type":"uint256"}],"name":"FeeShortfall","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creditToYield","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFeeTotal","type":"uint256"}],"name":"ForceRepay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"globalMinimumCollateralization","type":"uint256"}],"name":"GlobalMinimumCollateralizationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"guardian","type":"address"},{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"GuardianSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountId","type":"uint256"},{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeInYield","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeInUnderlying","type":"uint256"}],"name":"Liquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"liquidationTargetCollateralization","type":"uint256"}],"name":"LiquidationTargetCollateralizationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LiquidatorFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"LoansPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minimumCollateralization","type":"uint256"}],"name":"MinimumCollateralizationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"MintAllowancesReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingAdmin","type":"address"}],"name":"PendingAdminUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"ProtocolFeeReceiverUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"ProtocolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redemption","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"recipientId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"credit","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountId","type":"uint256"},{"indexed":false,"internalType":"address","name":"feeReciever","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeInYield","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeInUnderlying","type":"uint256"}],"name":"RepaymentFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"RepaymentFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountLiquidated","type":"uint256"}],"name":"SelfLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"adapter","type":"address"}],"name":"TokenAdapterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"transmuter","type":"address"}],"name":"TransmuterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FIXED_POINT_SCALAR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_Q128","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"alchemistFeeVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"alchemistPositionNFT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"accountIds","type":"uint256[]"}],"name":"batchLiquidate","outputs":[{"internalType":"uint256","name":"totalAmountLiquidated","type":"uint256"},{"internalType":"uint256","name":"totalFeesInYield","type":"uint256"},{"internalType":"uint256","name":"totalFeesInUnderlying","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"recipientId","type":"uint256"}],"name":"burn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"targetCollateralization","type":"uint256"},{"internalType":"uint256","name":"alchemistCurrentCollateralization","type":"uint256"},{"internalType":"uint256","name":"alchemistMinimumCollateralization","type":"uint256"},{"internalType":"uint256","name":"feeBps","type":"uint256"}],"name":"calculateLiquidation","outputs":[{"internalType":"uint256","name":"grossCollateralToSeize","type":"uint256"},{"internalType":"uint256","name":"debtToBurn","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"outsourcedFee","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"collateralizationLowerBound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertDebtTokensToYield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertUnderlyingTokensToYield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertYieldTokensToDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertYieldTokensToUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cumulativeEarmarked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getCDP","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getMaxBorrowable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getMaxWithdrawable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalDeposited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalLockedUnderlyingValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalUnderlyingValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnrealizedCumulativeEarmarked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalMinimumCollateralization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"guardians","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"debtToken","type":"address"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint256","name":"depositCap","type":"uint256"},{"internalType":"uint256","name":"minimumCollateralization","type":"uint256"},{"internalType":"uint256","name":"globalMinimumCollateralization","type":"uint256"},{"internalType":"uint256","name":"collateralizationLowerBound","type":"uint256"},{"internalType":"uint256","name":"liquidationTargetCollateralization","type":"uint256"},{"internalType":"address","name":"transmuter","type":"address"},{"internalType":"uint256","name":"protocolFee","type":"uint256"},{"internalType":"address","name":"protocolFeeReceiver","type":"address"},{"internalType":"uint256","name":"liquidatorFee","type":"uint256"},{"internalType":"uint256","name":"repaymentFee","type":"uint256"},{"internalType":"address","name":"myt","type":"address"}],"internalType":"struct AlchemistInitializationParams","name":"params","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastEarmarkBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRedemptionBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTransmuterTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"accountId","type":"uint256"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"yieldAmount","type":"uint256"},{"internalType":"uint256","name":"feeInYield","type":"uint256"},{"internalType":"uint256","name":"feeInUnderlying","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidationTargetCollateralization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidatorFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loansPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumCollateralization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"ownerTokenId","type":"uint256"},{"internalType":"address","name":"spender","type":"address"}],"name":"mintAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"mintFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"myt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"normalizeDebtTokensToUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"normalizeUnderlyingTokensToDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"isPaused","type":"bool"}],"name":"pauseDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isPaused","type":"bool"}],"name":"pauseLoans","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"poke","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"sharesSent","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"reduceSyntheticsIssued","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"recipientTokenId","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repaymentFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"resetMintAllowances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"accountId","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"selfLiquidate","outputs":[{"internalType":"uint256","name":"amountLiquidated","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"value","type":"address"}],"name":"setAlchemistFeeVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nft","type":"address"}],"name":"setAlchemistPositionNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setCollateralizationLowerBound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setDepositCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setGlobalMinimumCollateralization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"},{"internalType":"bool","name":"isActive","type":"bool"}],"name":"setGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setLiquidationTargetCollateralization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setLiquidatorFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setMinimumCollateralization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"value","type":"address"}],"name":"setPendingAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"value","type":"address"}],"name":"setProtocolFeeReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setRepaymentFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"value","type":"address"}],"name":"setTokenAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setTransmuterTokenBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenAdapter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSyntheticsIssued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"totalValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transmuter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlyingConversionFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlyingToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":null}