EthenaWithdrawRequestManager

Description:

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

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/withdraws/Ethena.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import { IsUSDe, sUSDe, USDe } from "../interfaces/IEthena.sol";
import { ClonedCoolDownHolder } from "./ClonedCoolDownHolder.sol";
import { AbstractWithdrawRequestManager } from "./AbstractWithdrawRequestManager.sol";
import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol";
import { ADDRESS_REGISTRY } from "../utils/Constants.sol";

contract EthenaCooldownHolder is ClonedCoolDownHolder {
    uint256 public instantRedeemBalance;
    uint256 public expectedRedeemBalance;

    constructor(address _manager) ClonedCoolDownHolder(_manager) { }

    /// @notice There is no way to stop a cool down
    function _stopCooldown() internal pure override {
        revert();
    }

    function _startCooldown(uint256 cooldownBalance) internal override {
        uint24 duration = sUSDe.cooldownDuration();
        if (duration == 0) {
            // If the cooldown duration is set to zero, can redeem immediately
            instantRedeemBalance = sUSDe.redeem(cooldownBalance, address(this), address(this));
        } else {
            // If we execute a second cooldown while one exists, the cooldown end
            // will be pushed further out. This holder should only ever have one
            // cooldown ever.
            require(sUSDe.cooldowns(address(this)).cooldownEnd == 0);
            expectedRedeemBalance = sUSDe.cooldownShares(cooldownBalance);
        }
    }

    function _finalizeCooldown() internal override returns (uint256 tokensClaimed, bool finalized) {
        uint24 duration = sUSDe.cooldownDuration();
        IsUSDe.UserCooldown memory userCooldown = sUSDe.cooldowns(address(this));

        if (block.timestamp < userCooldown.cooldownEnd && 0 < duration) {
            // Cooldown has not completed, return a false for finalized
            return (0, false);
        }

        uint256 balanceBefore = USDe.balanceOf(address(this));
        // If a cooldown has been initiated, need to call unstake to complete it. If
        // duration was set to zero then the USDe will be on this contract already.
        if (0 < userCooldown.cooldownEnd) sUSDe.unstake(address(this));
        uint256 balanceAfter = USDe.balanceOf(address(this));

        // USDe is immutable. It cannot have a transfer tax and it is ERC20 compliant
        // so we do not need to use the additional protections here.
        tokensClaimed = balanceAfter - balanceBefore + instantRedeemBalance;
        USDe.transfer(manager, tokensClaimed); // forge-lint: disable-line
        finalized = true;
    }
}

contract EthenaWithdrawRequestManager is AbstractWithdrawRequestManager {
    address public HOLDER_IMPLEMENTATION;

    constructor() AbstractWithdrawRequestManager(address(USDe), address(sUSDe), address(USDe)) { }

    function redeployHolder() external {
        require(msg.sender == ADDRESS_REGISTRY.upgradeAdmin());
        HOLDER_IMPLEMENTATION = address(new EthenaCooldownHolder(address(this)));
    }

    function _initialize(bytes calldata /* data */ ) internal override {
        HOLDER_IMPLEMENTATION = address(new EthenaCooldownHolder(address(this)));
    }

    function _stakeTokens(uint256 usdeAmount, bytes memory /* stakeData */ ) internal override {
        USDe.approve(address(sUSDe), usdeAmount);
        sUSDe.deposit(usdeAmount, address(this));
    }

    function _initiateWithdrawImpl(
        address, /* account */
        uint256 balanceToTransfer,
        bytes calldata, /* data */
        address /* forceWithdrawFrom */
    )
        internal
        override
        returns (uint256 requestId)
    {
        EthenaCooldownHolder holder = EthenaCooldownHolder(Clones.clone(HOLDER_IMPLEMENTATION));
        sUSDe.transfer(address(holder), balanceToTransfer); // forge-lint: disable-line
        holder.startCooldown(balanceToTransfer);

        return uint256(uint160(address(holder)));
    }

    function _finalizeWithdrawImpl(
        address, /* account */
        uint256 requestId
    )
        internal
        override
        returns (uint256 tokensClaimed)
    {
        EthenaCooldownHolder holder = EthenaCooldownHolder(address(uint160(requestId)));
        bool finalized;
        (tokensClaimed, finalized) = holder.finalizeCooldown();
        require(finalized);
    }

    function getKnownWithdrawTokenAmount(uint256 requestId)
        public
        view
        override
        returns (bool hasKnownAmount, uint256 amount)
    {
        EthenaCooldownHolder holder = EthenaCooldownHolder(address(uint160(requestId)));
        hasKnownAmount = true;
        amount = holder.expectedRedeemBalance() + holder.instantRedeemBalance();
    }

    function canFinalizeWithdrawRequest(uint256 requestId) public view override returns (bool) {
        uint24 duration = sUSDe.cooldownDuration();
        address holder = address(uint160(requestId));
        // This valuation is the amount of USDe the account will receive at cooldown, once
        // a cooldown is initiated the account is no longer receiving sUSDe yield. This balance
        // of USDe is transferred to a Silo contract and guaranteed to be available once the
        // cooldown has passed.
        IsUSDe.UserCooldown memory userCooldown = sUSDe.cooldowns(holder);
        return (userCooldown.cooldownEnd < block.timestamp || 0 == duration);
    }
}
"
    },
    "src/interfaces/IEthena.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

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

// Mainnet Ethena contract addresses
IsUSDe constant sUSDe = IsUSDe(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497);
ERC20 constant USDe = ERC20(0x4c9EDD5852cd905f086C759E8383e09bff1E68B3);
// Dai and sDAI are required for trading out of sUSDe
ERC20 constant DAI = ERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
IERC4626 constant sDAI = IERC4626(0x83F20F44975D03b1b09e64809B757c47f942BEeA);

interface IsUSDe is IERC4626 {
    struct UserCooldown {
        uint104 cooldownEnd;
        uint152 underlyingAmount;
    }

    function cooldownDuration() external view returns (uint24);
    function cooldowns(address account) external view returns (UserCooldown memory);
    function cooldownShares(uint256 shares) external returns (uint256 assets);
    function unstake(address receiver) external;
    function setCooldownDuration(uint24 duration) external;
    function owner() external view returns (address);
}
"
    },
    "src/withdraws/ClonedCoolDownHolder.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

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

/**
 * @notice Used for withdraws where only one cooldown period can exist per address,
 * this contract will receive the staked token and initiate a cooldown
 */
abstract contract ClonedCoolDownHolder {
    using SafeERC20 for ERC20;

    /// @notice The manager contract that is responsible for managing the cooldown period.
    address public immutable manager;

    constructor(address _manager) {
        manager = _manager;
    }

    modifier onlyManager() {
        require(msg.sender == manager);
        _;
    }

    /// @notice If anything ever goes wrong, allows the manager to recover lost tokens.
    function rescueTokens(ERC20 token, address receiver, uint256 amount) external onlyManager {
        token.safeTransfer(receiver, amount);
    }

    // External methods with authentication
    function startCooldown(uint256 cooldownBalance) external onlyManager {
        _startCooldown(cooldownBalance);
    }

    function stopCooldown() external onlyManager {
        _stopCooldown();
    }

    function finalizeCooldown() external onlyManager returns (uint256 tokensWithdrawn, bool finalized) {
        return _finalizeCooldown();
    }

    // Internal implementation methods
    function _startCooldown(uint256 cooldownBalance) internal virtual;
    function _stopCooldown() internal virtual;
    function _finalizeCooldown() internal virtual returns (uint256 tokensWithdrawn, bool finalized);
}
"
    },
    "src/withdraws/AbstractWithdrawRequestManager.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import {
    IWithdrawRequestManager,
    WithdrawRequest,
    TokenizedWithdrawRequest,
    StakingTradeParams
} from "../interfaces/IWithdrawRequestManager.sol";
import { Initializable } from "../proxy/Initializable.sol";
import { ClonedCoolDownHolder } from "./ClonedCoolDownHolder.sol";
import {
    Unauthorized,
    ExistingWithdrawRequest,
    NoWithdrawRequest,
    InvalidWithdrawRequestTokenization
} from "../interfaces/Errors.sol";
import { TypeConvert } from "../utils/TypeConvert.sol";
import { TokenUtils } from "../utils/TokenUtils.sol";
import { ADDRESS_REGISTRY, DEFAULT_PRECISION } from "../utils/Constants.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Trade, TradeType, TRADING_MODULE, nProxy, ITradingModule } from "../interfaces/ITradingModule.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";

/**
 * Library to handle potentially illiquid withdraw requests of staking tokens where there
 * is some indeterminate lock up time before tokens can be redeemed. Examples would be withdraws
 * of staked or restaked ETH, tokens like sUSDe or stkAave which have cooldown periods before they
 * can be withdrawn.
 *
 * Primarily, this library tracks the withdraw request and an associated identifier for the withdraw
 * request. It also allows for the withdraw request to be "tokenized" so that shares of the withdraw
 * request can be liquidated.
 */
abstract contract AbstractWithdrawRequestManager is IWithdrawRequestManager, Initializable, ReentrancyGuardTransient {
    using SafeERC20 for ERC20;
    using TypeConvert for uint256;

    /// @inheritdoc IWithdrawRequestManager
    address public immutable override YIELD_TOKEN;
    /// @inheritdoc IWithdrawRequestManager
    address public immutable override WITHDRAW_TOKEN;
    /// @inheritdoc IWithdrawRequestManager
    address public immutable override STAKING_TOKEN;

    mapping(address vault => bool isApproved) public override isApprovedVault;
    mapping(address vault => mapping(address account => WithdrawRequest)) private s_accountWithdrawRequest;
    mapping(uint256 requestId => TokenizedWithdrawRequest tokenizedWithdrawRequest) private s_tokenizedWithdrawRequest;

    constructor(address _withdrawToken, address _yieldToken, address _stakingToken) Initializable() {
        WITHDRAW_TOKEN = _withdrawToken;
        YIELD_TOKEN = _yieldToken;
        STAKING_TOKEN = _stakingToken;
    }

    modifier onlyOwner() {
        if (msg.sender != ADDRESS_REGISTRY.upgradeAdmin()) revert Unauthorized(msg.sender);
        _;
    }

    /// @dev Ensures that only approved vaults can initiate withdraw requests.
    modifier onlyApprovedVault() {
        if (!isApprovedVault[msg.sender]) revert Unauthorized(msg.sender);
        _;
    }

    /// @inheritdoc IWithdrawRequestManager
    function getWithdrawRequest(
        address vault,
        address account
    )
        public
        view
        override
        returns (WithdrawRequest memory w, TokenizedWithdrawRequest memory s)
    {
        w = s_accountWithdrawRequest[vault][account];
        s = s_tokenizedWithdrawRequest[w.requestId];
    }

    /// @inheritdoc IWithdrawRequestManager
    function isPendingWithdrawRequest(address vault, address account) public view override returns (bool) {
        return s_accountWithdrawRequest[vault][account].requestId != 0;
    }

    /// @inheritdoc IWithdrawRequestManager
    function setApprovedVault(address vault, bool isApproved) external override onlyOwner {
        isApprovedVault[vault] = isApproved;
        emit ApprovedVault(vault, isApproved);
    }

    /// @inheritdoc IWithdrawRequestManager
    function stakeTokens(
        address depositToken,
        uint256 amount,
        bytes calldata data
    )
        external
        override
        onlyApprovedVault
        nonReentrant
        returns (uint256 yieldTokensMinted)
    {
        uint256 initialYieldTokenBalance = ERC20(YIELD_TOKEN).balanceOf(address(this));
        ERC20(depositToken).safeTransferFrom(msg.sender, address(this), amount);
        (uint256 stakeTokenAmount, bytes memory stakeData) = _preStakingTrade(depositToken, amount, data);
        _stakeTokens(stakeTokenAmount, stakeData);

        yieldTokensMinted = ERC20(YIELD_TOKEN).balanceOf(address(this)) - initialYieldTokenBalance;
        ERC20(YIELD_TOKEN).safeTransfer(msg.sender, yieldTokensMinted);

        // Emits the amount of staking tokens for the yield token at this point in time.
        emit ITradingModule.TradeExecuted(STAKING_TOKEN, YIELD_TOKEN, stakeTokenAmount, yieldTokensMinted);
    }

    /// @inheritdoc IWithdrawRequestManager
    function initiateWithdraw(
        address account,
        uint256 yieldTokenAmount,
        uint256 sharesAmount,
        bytes calldata data,
        address forceWithdrawFrom
    )
        external
        override
        onlyApprovedVault
        nonReentrant
        returns (uint256 requestId)
    {
        WithdrawRequest storage accountWithdraw = s_accountWithdrawRequest[msg.sender][account];
        if (accountWithdraw.requestId != 0) {
            revert ExistingWithdrawRequest(msg.sender, account, accountWithdraw.requestId);
        }

        // Receive the requested amount of yield tokens from the approved vault.
        ERC20(YIELD_TOKEN).safeTransferFrom(msg.sender, address(this), yieldTokenAmount);

        requestId = _initiateWithdrawImpl(account, yieldTokenAmount, data, forceWithdrawFrom);
        accountWithdraw.requestId = requestId;
        accountWithdraw.yieldTokenAmount = yieldTokenAmount.toUint120();
        accountWithdraw.sharesAmount = sharesAmount.toUint120();
        s_tokenizedWithdrawRequest[requestId] = TokenizedWithdrawRequest({
            totalYieldTokenAmount: yieldTokenAmount.toUint120(),
            totalWithdraw: 0,
            finalized: false
        });

        emit InitiateWithdrawRequest(account, msg.sender, yieldTokenAmount, sharesAmount, requestId);
    }

    /// @inheritdoc IWithdrawRequestManager
    function finalizeAndRedeemWithdrawRequest(
        address account,
        uint256 withdrawYieldTokenAmount,
        uint256 sharesToBurn
    )
        external
        override
        onlyApprovedVault
        nonReentrant
        returns (uint256 tokensWithdrawn)
    {
        WithdrawRequest storage s_withdraw = s_accountWithdrawRequest[msg.sender][account];
        if (s_withdraw.requestId == 0) return 0;

        tokensWithdrawn = _finalizeWithdraw(account, msg.sender, s_withdraw);

        // Allows for partial withdrawal of yield tokens
        uint256 requestId = s_withdraw.requestId;
        bool isCleared = false;
        if (withdrawYieldTokenAmount < s_withdraw.yieldTokenAmount) {
            tokensWithdrawn = tokensWithdrawn * withdrawYieldTokenAmount / s_withdraw.yieldTokenAmount;
            s_withdraw.sharesAmount -= sharesToBurn.toUint120();
            s_withdraw.yieldTokenAmount -= withdrawYieldTokenAmount.toUint120();
        } else {
            require(s_withdraw.yieldTokenAmount == withdrawYieldTokenAmount);
            delete s_accountWithdrawRequest[msg.sender][account];
            isCleared = true;
        }

        emit WithdrawRequestRedeemed(msg.sender, account, requestId, withdrawYieldTokenAmount, sharesToBurn, isCleared);

        ERC20(WITHDRAW_TOKEN).safeTransfer(msg.sender, tokensWithdrawn);
    }

    /// @inheritdoc IWithdrawRequestManager
    function finalizeRequestManual(
        address vault,
        address account
    )
        external
        override
        nonReentrant
        returns (uint256 tokensWithdrawn)
    {
        WithdrawRequest storage s_withdraw = s_accountWithdrawRequest[vault][account];
        if (s_withdraw.requestId == 0) revert NoWithdrawRequest(vault, account);

        // Do not transfer any tokens off of this method here. Withdrawn tokens will be held in the
        // tokenized withdraw request until the vault calls this contract to withdraw the tokens.
        tokensWithdrawn = _finalizeWithdraw(account, vault, s_withdraw);
    }

    /// @inheritdoc IWithdrawRequestManager
    function tokenizeWithdrawRequest(
        address _from,
        address _to,
        uint256 sharesAmount
    )
        external
        override
        onlyApprovedVault
        nonReentrant
        returns (bool didTokenize)
    {
        if (_from == _to) revert();

        WithdrawRequest storage s_withdraw = s_accountWithdrawRequest[msg.sender][_from];
        uint256 requestId = s_withdraw.requestId;
        if (requestId == 0 || sharesAmount == 0) return false;

        // Ensure that no withdraw request gets overridden, the _to account always receives their withdraw
        // request in the account withdraw slot. All storage is updated prior to changes to the `w` storage
        // variable below.
        WithdrawRequest storage toWithdraw = s_accountWithdrawRequest[msg.sender][_to];
        if (toWithdraw.requestId != 0 && toWithdraw.requestId != requestId) {
            revert ExistingWithdrawRequest(msg.sender, _to, toWithdraw.requestId);
        }

        toWithdraw.requestId = requestId;

        if (s_withdraw.sharesAmount < sharesAmount) {
            // This should never occur given the checks below.
            revert InvalidWithdrawRequestTokenization();
        } else if (s_withdraw.sharesAmount == sharesAmount) {
            // If the resulting vault shares is zero, then delete the request. The _from account's
            // withdraw request is fully transferred to _to. In this case, the _to account receives
            // the full amount of the _from account's withdraw request.
            toWithdraw.yieldTokenAmount = toWithdraw.yieldTokenAmount + s_withdraw.yieldTokenAmount;
            toWithdraw.sharesAmount = toWithdraw.sharesAmount + s_withdraw.sharesAmount;
            delete s_accountWithdrawRequest[msg.sender][_from];
        } else {
            // In this case, the amount of yield tokens is transferred from one account to the other.
            uint256 yieldTokenAmount = s_withdraw.yieldTokenAmount * sharesAmount / s_withdraw.sharesAmount;
            toWithdraw.yieldTokenAmount = (toWithdraw.yieldTokenAmount + yieldTokenAmount).toUint120();
            toWithdraw.sharesAmount = (toWithdraw.sharesAmount + sharesAmount).toUint120();
            s_withdraw.yieldTokenAmount = (s_withdraw.yieldTokenAmount - yieldTokenAmount).toUint120();
            s_withdraw.sharesAmount = (s_withdraw.sharesAmount - sharesAmount).toUint120();
        }

        emit WithdrawRequestTokenized(_from, _to, msg.sender, requestId, sharesAmount);
        return true;
    }

    /// @inheritdoc IWithdrawRequestManager
    function rescueTokens(
        address cooldownHolder,
        address token,
        address receiver,
        uint256 amount
    )
        external
        override
        onlyOwner
    {
        ClonedCoolDownHolder(cooldownHolder).rescueTokens(ERC20(token), receiver, amount);
    }

    /// @notice Finalizes a withdraw request and updates the account required to determine how many
    /// tokens the account has a claim over.
    function _finalizeWithdraw(
        address account,
        address vault,
        WithdrawRequest memory w
    )
        internal
        returns (uint256 tokensWithdrawn)
    {
        TokenizedWithdrawRequest storage s = s_tokenizedWithdrawRequest[w.requestId];

        // If the tokenized request was already finalized in a different transaction
        // then return the values here and we can short circuit the withdraw impl
        if (s.finalized) {
            return uint256(s.totalWithdraw) * uint256(w.yieldTokenAmount) / uint256(s.totalYieldTokenAmount);
        }

        // These values are the total tokens claimed from the withdraw request, does not
        // account for potential tokenization.
        tokensWithdrawn = _finalizeWithdrawImpl(account, w.requestId);

        s.totalWithdraw = tokensWithdrawn.toUint120();
        // Safety check to ensure that we do not override a finalized tokenized withdraw request
        require(s.finalized == false);
        s.finalized = true;

        tokensWithdrawn = uint256(s.totalWithdraw) * uint256(w.yieldTokenAmount) / uint256(s.totalYieldTokenAmount);
        emit WithdrawRequestFinalized(vault, account, w.requestId, s.totalWithdraw);
    }

    /// @notice Required implementation to begin the withdraw request
    /// @return requestId some identifier of the withdraw request
    function _initiateWithdrawImpl(
        address account,
        uint256 yieldTokenAmount,
        bytes calldata data,
        address forceWithdrawFrom
    )
        internal
        virtual
        returns (uint256 requestId);

    /// @notice Required implementation to finalize the withdraw
    /// @dev Must revert if the withdraw request is not finalized
    /// @return tokensWithdrawn total tokens claimed as a result of the withdraw, does not
    /// necessarily represent the tokens that go to the account if the request has been
    /// tokenized due to liquidation
    function _finalizeWithdrawImpl(
        address account,
        uint256 requestId
    )
        internal
        virtual
        returns (uint256 tokensWithdrawn);

    /// @notice Required implementation to stake the deposit token to the yield token
    function _stakeTokens(uint256 amount, bytes memory stakeData) internal virtual;

    /// @dev Allows for the deposit token to be traded into the staking token prior to staking, i.e.
    /// enables USDC to USDe trades before staking into sUSDe.
    function _preStakingTrade(
        address depositToken,
        uint256 depositAmount,
        bytes calldata data
    )
        internal
        returns (uint256 amountBought, bytes memory stakeData)
    {
        if (depositToken == STAKING_TOKEN) {
            amountBought = depositAmount;
            stakeData = data;
        } else {
            StakingTradeParams memory params = abi.decode(data, (StakingTradeParams));
            stakeData = params.stakeData;

            ( /* */ , amountBought) = _executeTrade(
                Trade({
                    tradeType: TradeType.EXACT_IN_SINGLE,
                    sellToken: depositToken,
                    buyToken: STAKING_TOKEN,
                    amount: depositAmount,
                    exchangeData: params.exchangeData,
                    limit: params.minPurchaseAmount,
                    deadline: block.timestamp
                }),
                params.dexId
            );
        }
    }

    function _executeTrade(
        Trade memory trade,
        uint16 dexId
    )
        internal
        returns (uint256 amountSold, uint256 amountBought)
    {
        (bool success, bytes memory result) = nProxy(payable(address(TRADING_MODULE))).getImplementation().delegatecall(
            abi.encodeWithSelector(TRADING_MODULE.executeTrade.selector, dexId, trade)
        );
        if (!success) {
            assembly {
                // Copy the return data to memory
                returndatacopy(0, 0, returndatasize())
                // Revert with the return data
                revert(0, returndatasize())
            }
        }

        (amountSold, amountBought) = abi.decode(result, (uint256, uint256));
    }

    function getKnownWithdrawTokenAmount(uint256 /* requestId */ )
        public
        view
        virtual
        override
        returns (bool hasKnownAmount, uint256 amount)
    {
        return (false, 0);
    }

    /// @inheritdoc IWithdrawRequestManager
    function getWithdrawRequestValue(
        address vault,
        address account,
        address asset,
        uint256 shares
    )
        external
        view
        override
        returns (bool hasRequest, uint256 valueInAsset)
    {
        WithdrawRequest memory w = s_accountWithdrawRequest[vault][account];
        if (w.requestId == 0) return (false, 0);

        TokenizedWithdrawRequest memory s = s_tokenizedWithdrawRequest[w.requestId];

        (bool hasKnownAmount, uint256 knownAmount) = getKnownWithdrawTokenAmount(w.requestId);
        int256 tokenRate;
        uint256 tokenAmount;
        uint256 tokenDecimals;
        uint256 assetDecimals = TokenUtils.getDecimals(asset);
        if (s.finalized || hasKnownAmount) {
            // If finalized the withdraw request is locked to the tokens withdrawn
            (tokenRate, /* */ ) = TRADING_MODULE.getOraclePrice(WITHDRAW_TOKEN, asset);
            tokenDecimals = TokenUtils.getDecimals(WITHDRAW_TOKEN);
            uint256 totalWithdraw = s.finalized ? uint256(s.totalWithdraw) : knownAmount;
            tokenAmount = (uint256(w.yieldTokenAmount) * totalWithdraw) / uint256(s.totalYieldTokenAmount);
        } else {
            // Otherwise we use the yield token rate
            (tokenRate, /* */ ) = TRADING_MODULE.getOraclePrice(YIELD_TOKEN, asset);
            tokenDecimals = TokenUtils.getDecimals(YIELD_TOKEN);
            tokenAmount = w.yieldTokenAmount;
        }

        // The trading module always returns a positive rate in 18 decimals so we can safely
        // cast to uint256
        uint256 totalValue =
            (uint256(tokenRate) * tokenAmount * (10 ** assetDecimals)) / ((10 ** tokenDecimals) * DEFAULT_PRECISION);
        // NOTE: returns the normalized value given the shares input
        return (true, totalValue * shares / w.sharesAmount);
    }

    /// @inheritdoc IWithdrawRequestManager
    function getExchangeRate() public view virtual override returns (uint256) {
        // Covers the base case where the yield token is an ERC4626 vault and returns
        // the exchange rate of the yield token back to the staking token.
        return IERC4626(YIELD_TOKEN).convertToAssets(10 ** ERC20(YIELD_TOKEN).decimals());
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/proxy/Clones.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/Clones.sol)

pragma solidity ^0.8.20;

import {Create2} from "../utils/Create2.sol";
import {Errors} from "../utils/Errors.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 */
library Clones {
    error CloneArgumentsTooLong();

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        return clone(implementation, 0);
    }

    /**
     * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
     * to the new contract.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function clone(address implementation, uint256 value) internal returns (address instance) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        assembly ("memory-safe") {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(value, 0x09, 0x37)
        }
        if (instance == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple times will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        return cloneDeterministic(implementation, salt, 0);
    }

    /**
     * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
     * a `value` parameter to send native currency to the new contract.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function cloneDeterministic(
        address implementation,
        bytes32 salt,
        uint256 value
    ) internal returns (address instance) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        assembly ("memory-safe") {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(value, 0x09, 0x37, salt)
        }
        if (instance == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
     * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
     * access the arguments within the implementation, use {fetchCloneArgs}.
     *
     * This function uses the create opcode, which should never revert.
     */
    function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) {
        return cloneWithImmutableArgs(implementation, args, 0);
    }

    /**
     * @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
     * parameter to send native currency to the new contract.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function cloneWithImmutableArgs(
        address implementation,
        bytes memory args,
        uint256 value
    ) internal returns (address instance) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
        assembly ("memory-safe") {
            instance := create(value, add(bytecode, 0x20), mload(bytecode))
        }
        if (instance == address(0)) {
            revert Errors.FailedDeployment();
        }
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation` with custom
     * immutable arguments. These are provided through `args` and cannot be changed after deployment. To
     * access the arguments within the implementation, use {fetchCloneArgs}.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
     * `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
     * at the same address.
     */
    function cloneDeterministicWithImmutableArgs(
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal returns (address instance) {
        return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
    }

    /**
     * @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
     * but with a `value` parameter to send native currency to the new contract.
     *
     * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
     * to always have enough balance for new deployments. Consider exposing this function under a payable method.
     */
    function cloneDeterministicWithImmutableArgs(
        address implementation,
        bytes memory args,
        bytes32 salt,
        uint256 value
    ) internal returns (address instance) {
        bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
        return Create2.deploy(value, salt, bytecode);
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
     */
    function predictDeterministicAddressWithImmutableArgs(
        address implementation,
        bytes memory args,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
        return Create2.computeAddress(salt, keccak256(bytecode), deployer);
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
     */
    function predictDeterministicAddressWithImmutableArgs(
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this));
    }

    /**
     * @dev Get the immutable args attached to a clone.
     *
     * - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
     *   function will return an empty array.
     * - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
     *   `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
     *   creation.
     * - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
     *   function should only be used to check addresses that are known to be clones.
     */
    function fetchCloneArgs(address instance) internal view returns (bytes memory) {
        bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
        assembly ("memory-safe") {
            extcodecopy(instance, add(result, 32), 45, mload(result))
        }
        return result;
    }

    /**
     * @dev Helper that prepares the initcode of the proxy with immutable args.
     *
     * An assembly variant of this function requires copying the `args` array, which can be efficiently done using
     * `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
     * abi.encodePacked is more expensive but also more portable and easier to review.
     *
     * NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
     * With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
     */
    function _cloneCodeWithImmutableArgs(
        address implementation,
        bytes memory args
    ) private pure returns (bytes memory) {
        if (args.length > 24531) revert CloneArgumentsTooLong();
        return
            abi.encodePacked(
                hex"61",
                uint16(args.length + 45),
                hex"3d81600a3d39f3363d3d373d3d3d363d73",
                implementation,
                hex"5af43d82803e903d91602b57fd5bf3",
                args
            );
    }
}
"
    },
    "src/utils/Constants.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import { WETH9 } from "../interfaces/IWETH.sol";
import { AddressRegistry } from "../proxy/AddressRegistry.sol";

address constant ETH_ADDRESS = address(0);
address constant ALT_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint256 constant DEFAULT_PRECISION = 1e18;
uint256 constant DEFAULT_DECIMALS = 18;
uint256 constant SHARE_PRECISION = 1e24;
uint256 constant VIRTUAL_SHARES = 1e6;

uint256 constant COOLDOWN_PERIOD = 5 minutes;
uint256 constant YEAR = 365 days;

// Will move these to a deployment file when we go to multiple chains
uint256 constant CHAIN_ID_MAINNET = 1;
WETH9 constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
AddressRegistry constant ADDRESS_REGISTRY = AddressRegistry(0xe335d314BD4eF7DD44F103dC124FEFb7Ce63eC95);
"
    },
    "node_modules/@openzeppelin/contracts/interfaces/IERC4626.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

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

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

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

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

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

        emit Transfer(from, to, value);
    }

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

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

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

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

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

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

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successfu

Tags:
ERC20, ERC165, Multisig, Mintable, Swap, Staking, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x7aa5dee9ec5f94d42596e1b475ce9700fff11222|verified:true|block:23420205|tx:0xafb6a48625d50680b087fed57f5eab8200c22caf94dc6408c105dd8ba894907e|first_check:1758567523

Submitted on: 2025-09-22 20:58:44

Comments

Log in to comment.

No comments yet.