FrxUSDCustodianWithReceiver

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/deps/CustodianWithReceiver.sol": {
      "content": "// SPDX-License-Identifier: MIT
// @version 0.2.8
pragma solidity >=0.8.0;

/**
 * ====================================================================
 * |     ______                   _______                             |
 * |    / _____________ __  __   / ____(_____  ____ _____  ________   |
 * |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
 * |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
 * | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
 * |                                                                  |
 * ====================================================================
 * =================== FrxUSDCustodianWithReceiver ====================
 * ====================================================================
 * FrxUSD Custodian contract for the Frax protocol to custody tokens at a 1-1 ratio
 * Frax Finance: https://github.com/FraxFinance
 */

import { FrxUSDCustodian, IERC20 } from "src/deps/FrxUSDCustodian.sol";

contract FrxUSDCustodianWithReceiver is FrxUSDCustodian {
  constructor(address _frxUSD, address _custodianTkn) FrxUSDCustodian(_frxUSD, _custodianTkn) {}

  function onERC721Received(
    address operator,
    address from,
    uint256 tokenId,
    bytes calldata data
  ) external pure returns (bytes4) {
    return FrxUSDCustodianWithReceiver.onERC721Received.selector;
  }

  /// @notice Function to send usdc in this contract to the Wisdom tree on `On Receipt` address
  /// @param amountUsdc The amount of USDC to subscribe
  /// @dev Assumes required USDC is already present in this contract
  /// @dev Assumes WTGXX will be minted to the whitelisted address who sends usdc to the
  ///      `onReceiptPrimaryOrderAddress`
  function shuffleToWtgxx(uint256 amountUsdc) external {
    if (msg.sender != 0x4F95C5bA0C7c69FB2f9340E190cCeE890B3bd87c) revert OnlyUsdcCustodian();
    IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    address onReceiptPrimaryOrderAddress = 0x63a8bb98D70d0aC73460d22b6756528a087ecBf8;
    USDC.transfer(onReceiptPrimaryOrderAddress, amountUsdc);
    emit UsdcShuffledToWtgxxAsync(amountUsdc);
  }

  /// @notice Overriden function not to be used in child
  function shuffleToRwa(
    uint256, 
    uint256, 
    uint8
  ) public override {
    revert NotImplemented();
  }

  /// @notice Overriden function not to be used in child
  function shuffleToRwa(
    uint256,
    uint256
  ) external override {
    revert NotImplemented();
  }

  /// @notice Event emitted when usdc is sucessfully used to subscribe to wtgxx
  event UsdcShuffledToWtgxxAsync(uint256 amountUsdcSent);

  /// @notice Error Emitted when the `shuffleToWtgxx` caller is not approved
  error OnlyUsdcCustodian();

  /// @notice Error Emitted when calling function not intended for this instance
  error NotImplemented();

}
"
    },
    "src/deps/FrxUSDCustodian.sol": {
      "content": "// SPDX-License-Identifier: MIT
// @version 0.2.8
pragma solidity >=0.8.0;

/**
 * ====================================================================
 * |     ______                   _______                             |
 * |    / _____________ __  __   / ____(_____  ____ _____  ________   |
 * |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
 * |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
 * | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
 * |                                                                  |
 * ====================================================================
 * ========================= FrxUSDCustodian ==========================
 * ====================================================================
 * FrxUSD Custodian contract for the Frax protocol to custody tokens at a 1-1 ratio
 * Frax Finance: https://github.com/FraxFinance
 */
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IFrxUSD } from "src/interfaces/IFrxUSD.sol";
import { IRWAIssuer } from "src/interfaces/IRWAIssuer.sol";
import { IWtgxxCustodian } from "src/interfaces/IWtgxxCustodian.sol";

contract FrxUSDCustodian is Ownable2Step, ReentrancyGuard {
  using Math for uint256;

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

  /// @notice frxUSD token = share
  IFrxUSD public immutable frxUSD;

  /// @notice Custodian token = asset
  IERC20 public immutable custodianTkn;

  /// @notice Decimals for the frxUSD
  uint8 public immutable frxUSDDecimals;

  /// @notice Decimals for the custodian token
  uint8 public immutable custodianTknDecimals;

  /// @notice If the contract was initialized
  bool public wasInitialized;

  /// @notice Fee for minting. 18 decimals
  uint256 public mintFee;

  /// @notice Fee for redeeming. 18 decimals
  uint256 public redeemFee;

  /// @notice Mint cap for frxUSD minting
  uint256 public mintCap;

  /// @notice frxUSD minted accounting
  uint256 public frxUSDMinted;

  /// @notice Mapping to indicated which bots are allowed to shuffle assets
  mapping(address => bool) public isApprovedOperator;

  /// @notice The minimum amount of `custodianTkn` that must remain in the contract on `shuffleToRwa`
  uint256 public minAfterShuffle;

  // CONSTRUCTOR & INITIALIZER
  // ===================================================

  /// @notice Contract constructor
  constructor(address _frxUSD, address _custodianTkn) Ownable(msg.sender) {
    // Set the contract as initialized
    wasInitialized = true;

    // Set token addresses
    frxUSD = IFrxUSD(_frxUSD);
    custodianTkn = IERC20(_custodianTkn);

    // Set decimals
    frxUSDDecimals = frxUSD.decimals();
    custodianTknDecimals = ERC20(_custodianTkn).decimals();
  }

  /**
   * @notice Initialize contract
   * @param _owner The owner of this contract
   * @param _mintCap The mint cap for frxUSD minting
   * @param _mintFee The mint fee
   * @param _redeemFee The redeem fee
   */
  function initialize(address _owner, uint256 _mintCap, uint256 _mintFee, uint256 _redeemFee) public {
    // Make sure the contract wasn't already initialized
    if (wasInitialized) revert InitializeFailed();
    if (_owner == address(0)) revert OwnerCannotInitToZeroAddress();

    // Set owner for Ownable
    _transferOwnership(_owner);

    // Set the mint cap
    mintCap = _mintCap;

    // Set the mint/redeem fee
    mintFee = _mintFee;
    redeemFee = _redeemFee;

    // Set the contract as initialized
    wasInitialized = true;
  }

  // ERC4626 PUBLIC/EXTERNAL VIEWS
  // ===================================================

  /// @notice Return the underlying asset
  /// @return _custodianTkn The custodianTkn asset
  function asset() public view returns (address _custodianTkn) {
    _custodianTkn = address(custodianTkn);
  }

  /// @notice Share balance of the supplied address
  /// @param _addr The address to test
  /// @return _balance Total amount of shares
  function balanceOf(address _addr) public view returns (uint256 _balance) {
    return frxUSD.balanceOf(_addr);
  }

  /// @notice Total amount of underlying asset available
  /// @param _assets Amount of underlying tokens
  /// @dev See {IERC4626-totalAssets}
  function totalAssets() public view returns (uint256 _assets) {
    return custodianTkn.balanceOf(address(this));
  }

  /// @notice Total amount of shares
  /// @return _supply Total amount of shares
  function totalSupply() public view returns (uint256 _supply) {
    return frxUSD.totalSupply();
  }

  /// @notice Returns the amount of shares that the contract would exchange for the amount of assets provided
  /// @param _assets Amount of underlying tokens
  /// @return _shares Amount of shares that the underlying _assets represents
  /// @dev See {IERC4626-convertToShares}
  function convertToShares(uint256 _assets) public view returns (uint256 _shares) {
    _shares = _convertToShares(_assets, Math.Rounding.Floor);
  }

  /// @notice Returns the amount of assets that the contract would exchange for the amount of shares provided
  /// @param _shares Amount of shares
  /// @return _assets Amount of underlying asset that _shares represents
  /// @dev See {IERC4626-convertToAssets}
  function convertToAssets(uint256 _shares) public view returns (uint256 _assets) {
    _assets = _convertToAssets(_shares, Math.Rounding.Floor);
  }

  /// @notice Returns the maximum amount of the underlying asset that can be deposited into the contract for the receiver, through a deposit call. Includes fee.
  /// @param _addr The address to test
  /// @return _maxAssetsIn The max amount that can be deposited
  /**
   * @dev See {IERC4626-maxDeposit}
   * Contract frxUSD -> custodianTkn needed
   */
  function maxDeposit(address _addr) public view returns (uint256 _maxAssetsIn) {
    // See how much custodianTkn you would need to exchange for 100% of the frxUSD available under the cap
    if (frxUSDMinted >= mintCap) _maxAssetsIn = 0;
    else _maxAssetsIn = previewMint(mintCap - frxUSDMinted);
  }

  /// @notice Returns the maximum amount of shares that can be minted for the receiver, through a mint call. Includes fee.
  /// @param _addr The address to test
  /// @return _maxSharesOut The max amount that can be minted
  /**
   * @dev See {IERC4626-maxMint}
   * Contract frxUSD balance
   */
  function maxMint(address _addr) public view returns (uint256 _maxSharesOut) {
    // See how much frxUSD is actually available in the contract
    if (frxUSDMinted >= mintCap) _maxSharesOut = 0;
    else _maxSharesOut = mintCap - frxUSDMinted;
  }

  /// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the contract, through a withdraw call. Includes fee.
  /// @param _owner The address to check
  /// @return _maxAssetsOut The maximum amount of underlying asset that can be withdrawn
  /**
   * @dev See {IERC4626-maxWithdraw}
   * Lesser of
   *     a) User frxUSD -> custodianTkn amount
   *     b) Contract custodianTkn balance
   */
  function maxWithdraw(address _owner) public view returns (uint256 _maxAssetsOut) {
    // See how much custodianTkn the user could possibly withdraw with 100% of his frxUSD
    uint256 _maxAssetsUser = previewRedeem(frxUSD.balanceOf(address(_owner)));

    // See how much custodianTkn is actually available in the contract
    uint256 _assetBalanceContract = custodianTkn.balanceOf(address(this));

    // Return the lesser of the two
    _maxAssetsOut = ((_assetBalanceContract > _maxAssetsUser) ? _maxAssetsUser : _assetBalanceContract);
  }

  /// @notice Returns the maximum amount of shares that can be redeemed from the owner balance in the contract, through a redeem call. Includes fee.
  /// @param _owner The address to check
  /// @return _maxSharesIn The maximum amount of shares that can be redeemed
  /**
   * @dev See {IERC4626-maxRedeem}
   * Lesser of
   *     a) User frxUSD
   *     b) Contract custodianTkn -> frxUSD amount
   */
  function maxRedeem(address _owner) public view returns (uint256 _maxSharesIn) {
    // See how much frxUSD the contract could honor if 100% of its custodianTkn was redeemed
    uint256 _maxSharesContract = previewWithdraw(custodianTkn.balanceOf(address(this)));

    // See how much frxUSD the user has
    uint256 _sharesBalanceUser = frxUSD.balanceOf(address(_owner));

    // Return the lesser of the two
    _maxSharesIn = ((_maxSharesContract > _sharesBalanceUser) ? _sharesBalanceUser : _maxSharesContract);
  }

  /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
  /// @param _assetsIn Amount of underlying you want to deposit
  /// @return _sharesOut The amount of output shares expected
  /// @dev See {IERC4626-previewDeposit}
  function previewDeposit(uint256 _assetsIn) public view returns (uint256 _sharesOut) {
    uint256 fee = mintFee;
    if (fee > 0) _assetsIn = Math.mulDiv(_assetsIn, (1e18 - fee), 1e18, Math.Rounding.Floor);
    _sharesOut = _convertToShares(_assetsIn, Math.Rounding.Floor);
  }

  /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
  /// @param _sharesOut Amount of shares you want to mint
  /// @return _assetsIn The amount of input assets needed
  /// @dev See {IERC4626-previewMint}
  function previewMint(uint256 _sharesOut) public view returns (uint256 _assetsIn) {
    uint256 fee = mintFee;
    _assetsIn = _convertToAssets(_sharesOut, Math.Rounding.Ceil);
    if (fee > 0) _assetsIn = Math.mulDiv(_assetsIn, 1e18, (1e18 - fee), Math.Rounding.Ceil);
  }

  /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
  /// @param _assetsOut Amount of underlying tokens you want to get back
  /// @return _sharesIn Amount of shares needed
  /// @dev See {IERC4626-previewWithdraw}
  function previewWithdraw(uint256 _assetsOut) public view returns (uint256 _sharesIn) {
    uint256 fee = redeemFee;
    if (fee > 0) _assetsOut = Math.mulDiv(_assetsOut, 1e18, (1e18 - fee), Math.Rounding.Ceil);
    _sharesIn = _convertToShares(_assetsOut, Math.Rounding.Ceil);
  }

  /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
  /// @param _sharesIn Amount of shares you want to redeem
  /// @return _assetsOut Amount of output asset expected
  /// @dev See {IERC4626-previewRedeem}
  function previewRedeem(uint256 _sharesIn) public view returns (uint256 _assetsOut) {
    uint256 fee = redeemFee;
    _assetsOut = _convertToAssets(_sharesIn, Math.Rounding.Floor);
    if (fee > 0) _assetsOut = Math.mulDiv((1e18 - fee), _assetsOut, 1e18, Math.Rounding.Floor);
  }

  // ERC4626 INTERNAL VIEWS
  // ===================================================

  /// @dev Internal conversion function (from assets to shares) with support for rounding direction.
  /// @param _assets Amount of underlying tokens to convert to shares
  /// @param _rounding Math.Rounding rounding direction
  /// @return _shares Amount of shares represented by the given underlying tokens
  function _convertToShares(uint256 _assets, Math.Rounding _rounding) internal view virtual returns (uint256 _shares) {
    _shares = Math.mulDiv(_assets, uint256(10 ** frxUSDDecimals), uint256(10 ** custodianTknDecimals), _rounding);
  }

  /// @dev Internal conversion function (from shares to assets) with support for rounding direction
  /// @param _shares Amount of shares to convert to underlying tokens
  /// @param _rounding Math.Rounding rounding direction
  /// @return _assets Amount of underlying tokens represented by the given number of shares
  function _convertToAssets(uint256 _shares, Math.Rounding _rounding) internal view virtual returns (uint256 _assets) {
    _assets = Math.mulDiv(_shares, uint256(10 ** custodianTknDecimals), uint256(10 ** frxUSDDecimals), _rounding);
  }

  /// @notice Price of 1E18 shares, in asset tokens
  /// @return _pricePerShare How many underlying asset tokens per 1E18 shares
  function pricePerShare() external view returns (uint256 _pricePerShare) {
    _pricePerShare = _convertToAssets(1e18, Math.Rounding.Floor);
  }

  // ADDITIONAL PUBLIC VIEWS
  // ===================================================

  /// @notice Helper view for max deposit, mint, withdraw, and redeem inputs
  /// @return _maxAssetsDepositable Max amount of underlying asset you can deposit
  /// @return _maxSharesMintable Max number of shares that can be minted
  /// @return _maxAssetsWithdrawable Max amount of underlying asset withdrawable
  /// @return _maxSharesRedeemable Max number of shares redeemable
  function mdwrComboView()
    public
    view
    returns (
      uint256 _maxAssetsDepositable,
      uint256 _maxSharesMintable,
      uint256 _maxAssetsWithdrawable,
      uint256 _maxSharesRedeemable
    )
  {
    uint256 _maxMint = maxMint(address(this));
    return (
      previewMint(_maxMint),
      _maxMint,
      custodianTkn.balanceOf(address(this)),
      previewWithdraw(custodianTkn.balanceOf(address(this)))
    );
  }

  // ERC4626 INTERNAL MUTATORS
  // ===================================================

  /// @notice Deposit/mint common workflow.
  /// @param _caller The caller
  /// @param _receiver Reciever of the shares
  /// @param _assets Amount of assets taken in
  /// @param _shares Amount of shares given out
  function _deposit(address _caller, address _receiver, uint256 _assets, uint256 _shares) internal nonReentrant {
    // If _asset is ERC-777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
    // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
    // calls the vault, which is assumed not malicious.
    //
    // Conclusion: we need to do the transfer beforehand so that any reentrancy would happen before the
    // _assets are transferred and before the _shares are minted, which is a valid state.
    // slither-disable-next-line reentrancy-no-eth

    // Take in the assets
    // User will need to approve _caller -> address(this) first
    SafeERC20.safeTransferFrom(IERC20(address(custodianTkn)), _caller, address(this), _assets);

    // Transfer out the shares / mint frxUSD
    frxUSD.minter_mint(_receiver, _shares);

    // frxUSD minted accounting
    frxUSDMinted += _shares;
    if (frxUSDMinted > mintCap) revert MintCapExceeded(_receiver, _shares, mintCap);

    emit Deposit(_caller, _receiver, _assets, _shares);
  }

  /// @notice Withdraw/redeem common workflow.
  /// @param _caller The caller
  /// @param _receiver Reciever of the assets
  /// @param _owner The owner of the shares
  /// @param _assets Amount of assets given out
  /// @param _shares Amount of shares taken in
  function _withdraw(
    address _caller,
    address _receiver,
    address _owner,
    uint256 _assets,
    uint256 _shares
  ) internal nonReentrant {
    // If _asset is ERC-777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
    // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
    // calls the vault, which is assumed not malicious.
    //
    // Conclusion: we need to do the transfer afterwards so that any reentrancy would happen after the
    // _shares are burned and after the _assets are transferred, which is a valid state.

    // Take in the shares / burn frxUSD
    // User will need to approve owner -> address(this) first
    frxUSD.minter_burn_from(_caller, _shares);

    // frxUSD minted accounting
    if (frxUSDMinted < _shares) frxUSDMinted = 0;
    else frxUSDMinted -= _shares;

    // Transfer out the assets
    SafeERC20.safeTransfer(IERC20(address(custodianTkn)), _receiver, _assets);

    emit Withdraw(_caller, _receiver, _owner, _assets, _shares);
  }

  // ERC4626 PUBLIC/EXTERNAL MUTATIVE
  // ===================================================

  /// @notice Deposit a specified amount of underlying tokens and generate shares. Make sure to approve msg.sender's assets to this contract first.
  /// @param _assetsIn Amount of underlying tokens you are depositing
  /// @param _receiver Recipient of the generated shares
  /// @return _sharesOut Amount of shares generated by the deposit
  /// @dev See {IERC4626-deposit}
  function deposit(uint256 _assetsIn, address _receiver) public virtual returns (uint256 _sharesOut) {
    // See how many asset tokens the user can deposit
    uint256 _maxAssets = maxDeposit(_receiver);

    // Revert if the user is trying to deposit too many asset tokens
    if (_assetsIn > _maxAssets) {
      revert ERC4626ExceededMaxDeposit(_receiver, _assetsIn, _maxAssets);
    }

    // See how many shares would be generated with the specified number of asset tokens
    _sharesOut = previewDeposit(_assetsIn);

    // Do the deposit
    _deposit(msg.sender, _receiver, _assetsIn, _sharesOut);
  }

  /// @notice Mint a specified amount of shares using underlying asset tokens. Make sure to approve msg.sender's assets to this contract first.
  /// @param _sharesOut Amount of shares you want to mint
  /// @param _receiver Recipient of the minted shares
  /// @return _assetsIn Amount of assets used to generate the shares
  /// @dev See {IERC4626-mint}
  function mint(uint256 _sharesOut, address _receiver) public virtual returns (uint256 _assetsIn) {
    // See how many shares the user's can mint
    uint256 _maxShares = maxMint(_receiver);

    // Revert if you are trying to mint too many shares
    if (_sharesOut > _maxShares) {
      revert ERC4626ExceededMaxMint(_receiver, _sharesOut, _maxShares);
    }

    // See how many asset tokens are needed to generate the specified amount of shares
    _assetsIn = previewMint(_sharesOut);

    // Do the minting
    _deposit(msg.sender, _receiver, _assetsIn, _sharesOut);
  }

  /// @notice Withdraw a specified amount of underlying tokens. Make sure to approve _owner's shares to this contract first
  /// @param _assetsOut Amount of asset tokens you want to withdraw
  /// @param _receiver Recipient of the asset tokens
  /// @param _owner Owner of the shares. Must be msg.sender
  /// @return _sharesIn Amount of shares used for the withdrawal
  /// @dev See {IERC4626-withdraw}. Leaving _owner param for ABI compatibility
  function withdraw(uint256 _assetsOut, address _receiver, address _owner) public virtual returns (uint256 _sharesIn) {
    // Make sure _owner is msg.sender
    if (_owner != msg.sender) revert TokenOwnerShouldBeSender();

    // See how much assets the owner can withdraw
    uint256 _maxAssets = maxWithdraw(_owner);

    // Revert if you are trying to withdraw too many asset tokens
    if (_assetsOut > _maxAssets) {
      revert ERC4626ExceededMaxWithdraw(_owner, _assetsOut, _maxAssets);
    }

    // See how many shares are needed
    _sharesIn = previewWithdraw(_assetsOut);

    // Do the withdrawal
    _withdraw(msg.sender, _receiver, _owner, _assetsOut, _sharesIn);
  }

  /// @notice Redeem a specified amount of shares for the underlying tokens. Make sure to approve _owner's shares to this contract first.
  /// @param _sharesIn Number of shares to redeem
  /// @param _receiver Recipient of the underlying asset tokens
  /// @param _owner Owner of the shares being redeemed. Must be msg.sender.
  /// @return _assetsOut Amount of underlying tokens out
  /// @dev See {IERC4626-redeem}. Leaving _owner param for ABI compatibility
  function redeem(uint256 _sharesIn, address _receiver, address _owner) public virtual returns (uint256 _assetsOut) {
    // Make sure _owner is msg.sender
    if (_owner != msg.sender) revert TokenOwnerShouldBeSender();

    // See how many shares the owner can redeem
    uint256 _maxShares = maxRedeem(_owner);

    // Revert if you are trying to redeem too many shares
    if (_sharesIn > _maxShares) {
      revert ERC4626ExceededMaxRedeem(_owner, _sharesIn, _maxShares);
    }

    // See how many asset tokens are expected
    _assetsOut = previewRedeem(_sharesIn);

    // Do the redemption
    _withdraw(msg.sender, _receiver, _owner, _assetsOut, _sharesIn);
  }

  // RESTRICTED FUNCTIONS
  // ===================================================

  /// @notice Set the fee for the contract on mint|deposit/redeem|withdraw flow
  /// @param _mintFee The mint fee
  /// @param _redeemFee The redeem fee
  function setMintRedeemFee(uint256 _mintFee, uint _redeemFee) public onlyOwner {
    require(_mintFee < 1e18 && _redeemFee < 1e18, "Fee must be a fraction of underlying");
    mintFee = _mintFee;
    redeemFee = _redeemFee;
  }

  /// @notice Added to support tokens
  /// @param _tokenAddress The token to recover
  /// @param _tokenAmount The amount to recover
  function recoverERC20(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
    // Only the owner address can ever receive the recovery withdrawal
    SafeERC20.safeTransfer(IERC20(_tokenAddress), owner(), _tokenAmount);
    emit RecoveredERC20(_tokenAddress, _tokenAmount);
  }

  /// @notice Set the mint cap for frxUSD minting
  /// @param _mintCap The new mint cap
  function setMintCap(uint256 _mintCap) public onlyOwner {
    mintCap = _mintCap;
    emit MintCapSet(_mintCap);
  }

  /// @notice Set the status for a custodian operator
  /// @param _operator  The address of the operator whose status is updated
  /// @param _status    The status for the operator
  /// @dev only callable via `owner`
  function setApprovedOperator(address _operator, bool _status) public onlyOwner {
    isApprovedOperator[_operator] = _status;
    emit OperatorStatusSet(_operator, _status);
  }

  /// @notice Set the `minAfterShuffle` variable
  /// @param _minAfterShuffle The minimum amt of `custodianTkn` that must remain in the contract
  ///                         after the call to `shuffleToRwa`
  /// @dev only callable via `owner`
  function setMinAfterShuffle(uint256 _minAfterShuffle) public onlyOwner {
    emit MinAmountAfterShuffleSet(minAfterShuffle, _minAfterShuffle);
    minAfterShuffle = _minAfterShuffle;
  }

  /// @notice Function that will take excess usdc and shuffle it into RWA's earning RFR
  /// @param amount           The amount of `custodianTkn`
  /// @param minAmountRwaOut  The minimum amount of rwa out
  /// @param assetToShuffleTo Enum representing the asset we want to shuffle to
  function shuffleToRwa(uint256 amount, uint256 minAmountRwaOut, uint8 assetToShuffleTo) public virtual {
    IRWAIssuer rwaIssuer = IRWAIssuer(0x43415eB6ff9DB7E26A15b704e7A3eDCe97d31C4e);
    address rwaCustodian = 0x5fbAa3A3B489199338fbD85F7E3D444dc0504F33;
    address wtgxxCustodian = 0x860Cc723935FC9A15fF8b1A94237a711DFeF7857;

    if (!isApprovedOperator[msg.sender]) revert NotOperator();
    if (custodianTkn.balanceOf(address(this)) - amount < minAfterShuffle) revert AmountTooHigh();

    if (assetToShuffleTo == 0) {
      uint256 totalAssetsStart;
      if (minAmountRwaOut > 0) {
        totalAssetsStart = FrxUSDCustodian(rwaCustodian).totalAssets();
      }
      custodianTkn.approve(address(rwaIssuer), amount);
      rwaIssuer.subscribe(rwaCustodian, amount, address(custodianTkn));
      if (minAmountRwaOut > 0) {
        if (FrxUSDCustodian(rwaCustodian).totalAssets() - totalAssetsStart < minAmountRwaOut) {
          revert SlippageTooHigh();
        }
      }
    } else {
      custodianTkn.transfer(wtgxxCustodian, amount);
      IWtgxxCustodian(wtgxxCustodian).shuffleToWtgxx(amount);
    }
  }

  /// @notice Extend the legacy abi of the contract
  function shuffleToRwa(uint256 amount, uint256 minAmountRwaOut) external virtual {
    shuffleToRwa(amount, minAmountRwaOut, 0);
  }

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

  /// @notice When a deposit/mint has occured
  /// @param sender The transaction sender
  /// @param owner The owner of the assets
  /// @param assets Amount of assets taken in
  /// @param shares Amount of shares given out
  event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

  /// @notice When ERC20 tokens were recovered
  /// @param token Token address
  /// @param amount Amount of tokens collected
  event RecoveredERC20(address token, uint256 amount);

  /// @notice When a withdrawal/redemption has occured
  /// @param sender The transaction sender
  /// @param receiver Reciever of the assets
  /// @param owner The owner of the shares
  /// @param assets Amount of assets given out
  /// @param shares Amount of shares taken in
  event Withdraw(
    address indexed sender,
    address indexed receiver,
    address indexed owner,
    uint256 assets,
    uint256 shares
  );

  /// @notice When the mint cap is set
  /// @param mintCap The new mint cap
  event MintCapSet(uint256 mintCap);

  /// @notice Event emitted when an operator is set
  /// @param operator The address of the operator whose status is updated
  /// @param status   The status of the operator: true -> allowed | false -> disallowed
  event OperatorStatusSet(address operator, bool status);

  /// @notice Event emitted when the `minAfterShuffle` variable is set
  /// @param oldValue The old `minAfterShuffle` value
  /// @param newValue The new `minAfterShuffle` value
  event MinAmountAfterShuffleSet(uint256 oldValue, uint256 newValue);

  /// @notice Event emitted when `custodianTkn` is shuffled to RWA
  /// @param custodianTknAmount The amount of `custodianTkn` shuffled
  /// @param targetRwa          The RWA Address we are shuffling to
  event custodianTknShuffledToRwa(uint256 custodianTknAmount, address targetRwa);

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

  /// @notice Attempted to deposit more assets than the max amount for `receiver`
  /// @param receiver The intended recipient of the shares
  /// @param assets The amount of underlying that was attempted to be deposited
  /// @param max Max amount of underlying depositable
  error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);

  /// @notice Attempted to mint more shares than the mint cap
  /// @param receiver The intended recipient of the shares
  /// @param shares The number of shares that was attempted to be minted
  /// @param mintCap The mint cap
  error MintCapExceeded(address receiver, uint256 shares, uint256 mintCap);

  /// @notice Attempted to mint more shares than the max amount for `receiver`
  /// @param receiver The intended recipient of the shares
  /// @param shares The number of shares that was attempted to be minted
  /// @param max Max number of shares mintable
  error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);

  /// @notice Attempted to withdraw more assets than the max amount for `receiver`
  /// @param owner The owner of the shares
  /// @param assets The amount of underlying that was attempted to be withdrawn
  /// @param max Max amount of underlying withdrawable
  error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);

  /// @notice Attempted to redeem more shares than the max amount for `receiver`
  /// @param owner The owner of the shares
  /// @param shares The number of shares that was attempted to be redeemed
  /// @param max Max number of shares redeemable
  error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);

  /// @notice Cannot initialize twice
  error InitializeFailed();

  /// @notice When you are attempting to pull tokens from an owner address that is not msg.sender
  error TokenOwnerShouldBeSender();

  /// @notice When msg.sender does not have operator status
  error NotOperator();

  /// @notice When the amount to convert to RWA's is too high
  error AmountTooHigh();

  /// @notice When rwa Subscription results in too little out
  error SlippageTooHigh();

  /// @notice When owner is set to address(0) in initialize
  error OwnerCannotInitToZeroAddress();
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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/access/Ownable2Step.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This extension of the {Ownable} contract includes a two-step mechanism to transfer
 * ownership, where the new owner must call {acceptOwnership} in order to replace the
 * old one. This can help prevent common mistakes, such as transfers of ownership to
 * incorrect accounts, or to contracts that are unable to interact with the
 * permission system.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     *
     * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/math/Math.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a > b, a, b);
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a < b, a, b);
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
            // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2²⁵⁶ + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
            if (denominator <= prod1) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
            // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv ≡ 1 mod 2⁴.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
            // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    }

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does N

Tags:
ERC20, ERC165, Multisig, Mintable, Burnable, Pausable, Upgradeable, Multi-Signature, Factory|addr:0xd84d585b3588a3c3730ca8b689abf0e19f96d0d3|verified:true|block:23591869|tx:0x9f9a8bac9a0f0f8a690ffca1117d60c07e55030849ee8f4439f1cc6aaf539969|first_check:1760641219

Submitted on: 2025-10-16 21:00:19

Comments

Log in to comment.

No comments yet.