false
false

Ondo U.S. Dollar Yield

Contract
0x5be26527e817998a7206475496fde1e68957c5a6
Total supply
59,349,003.21 USDY
Holders
Fetching holders...
Transfers
Fetching transfers...
Decimals
18
Token type
ERC-20
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
TokenProxy




Optimization enabled
true
Compiler version
v0.8.16+commit.07a7930e




Optimization runs
100
EVM Version
default




Verified at
2023-11-01T20:10:02.014925Z

Constructor Arguments

0x0000000000000000000000003b355a7a25e75a320f631f9736afb3dcc9f3ef66000000000000000000000000201cdd34310a53915ee55b0a229b5a4eb18d144800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000

Arg [0] (address) : 0x3b355a7a25e75a320f631f9736afb3dcc9f3ef66
Arg [1] (address) : 0x201cdd34310a53915ee55b0a229b5a4eb18d1448
Arg [2] (bytes) : 

                    

contracts/Proxy.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/external/openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";

contract TokenProxy is TransparentUpgradeableProxy {
  constructor(
    address _logic,
    address _admin,
    bytes memory _data
  ) TransparentUpgradeableProxy(_logic, _admin, _data) {}
}
              

contracts/interfaces/IUSDYManager.sol

/**SPDX-License-Identifier: BUSL-1.1
      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐
 */
pragma solidity 0.8.16;

interface IUSDYManager {
  function setClaimableTimestamp(
    uint256 claimDate,
    bytes32[] calldata depositIds
  ) external;

  /**
   * @notice Event emitted when claimable timestamp is set
   *
   * @param claimTimestamp The timestamp at which the mint can be claimed
   * @param depositId      The depositId that can claim at the given 
                           `claimTimestamp`
   */
  event ClaimableTimestampSet(
    uint256 indexed claimTimestamp,
    bytes32 indexed depositId
  );

  /// ERRORS ///
  error MintNotYetClaimable();
  error ClaimableTimestampInPast();
  error ClaimableTimestampNotSet();
}
                

contracts/interfaces/IPricer.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

interface IPricer {
  /**
   * @notice Gets the latest price of the asset
   *
   * @return uint256 The latest price of the asset
   */
  function getLatestPrice() external view returns (uint256);

  /**
   * @notice Gets the price of the asset at a specific priceId
   *
   * @param priceId The priceId at which to get the price
   *
   * @return uint256 The price of the asset with the given priceId
   */
  function getPrice(uint256 priceId) external view returns (uint256);

  /**
   * @notice Adds a price to the pricer
   *
   * @param price     The price to add
   * @param timestamp The timestamp associated with the price
   *
   * @dev Updates the oracle price if price is the latest
   */
  function addPrice(uint256 price, uint256 timestamp) external;

  /**
   * @notice Updates a price in the pricer
   *
   * @param priceId The priceId to update
   * @param price   The price to set
   */
  function updatePrice(uint256 priceId, uint256 price) external;
}
                

contracts/usdy/blocklist/BlocklistClient.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/interfaces/IBlocklist.sol";
import "contracts/interfaces/IBlocklistClient.sol";

/**
 * @title BlocklistClient
 * @author Ondo Finance
 * @notice This abstract contract manages state for blocklist clients
 */
abstract contract BlocklistClient is IBlocklistClient {
  // blocklist address
  IBlocklist public override blocklist;

  /**
   * @notice Constructor
   *
   * @param _blocklist Address of the blocklist contract
   */
  constructor(address _blocklist) {
    _setBlocklist(_blocklist);
  }

  /**
   * @notice Sets the blocklist address for this client
   *
   * @param _blocklist The new blocklist address
   */
  function _setBlocklist(address _blocklist) internal {
    if (_blocklist == address(0)) {
      revert BlocklistZeroAddress();
    }
    address oldBlocklist = address(blocklist);
    blocklist = IBlocklist(_blocklist);
    emit BlocklistSet(oldBlocklist, _blocklist);
  }

  /**
   * @notice Checks whether an address has been blocked
   *
   * @param account The account to check
   */
  function _isBlocked(address account) internal view returns (bool) {
    return blocklist.isBlocked(account);
  }
}
                

contracts/external/openzeppelin/contracts/proxy/ProxyAdmin.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";
import "contracts/external/openzeppelin/contracts/access/Ownable.sol";

/**
 * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
 * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
 */
contract ProxyAdmin is Ownable {
  /**
   * @dev Returns the current implementation of `proxy`.
   *
   * Requirements:
   *
   * - This contract must be the admin of `proxy`.
   */
  function getProxyImplementation(TransparentUpgradeableProxy proxy)
    public
    view
    virtual
    returns (address)
  {
    // We need to manually run the static call since the getter cannot be flagged as view
    // bytes4(keccak256("implementation()")) == 0x5c60da1b
    (bool success, bytes memory returndata) =
      address(proxy).staticcall(hex"5c60da1b");
    require(success);
    return abi.decode(returndata, (address));
  }

  /**
   * @dev Returns the current admin of `proxy`.
   *
   * Requirements:
   *
   * - This contract must be the admin of `proxy`.
   */
  function getProxyAdmin(TransparentUpgradeableProxy proxy)
    public
    view
    virtual
    returns (address)
  {
    // We need to manually run the static call since the getter cannot be flagged as view
    // bytes4(keccak256("admin()")) == 0xf851a440
    (bool success, bytes memory returndata) =
      address(proxy).staticcall(hex"f851a440");
    require(success);
    return abi.decode(returndata, (address));
  }

  /**
   * @dev Changes the admin of `proxy` to `newAdmin`.
   *
   * Requirements:
   *
   * - This contract must be the current admin of `proxy`.
   */
  function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin)
    public
    virtual
    onlyOwner
  {
    proxy.changeAdmin(newAdmin);
  }

  /**
   * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
   *
   * Requirements:
   *
   * - This contract must be the admin of `proxy`.
   */
  function upgrade(TransparentUpgradeableProxy proxy, address implementation)
    public
    virtual
    onlyOwner
  {
    proxy.upgradeTo(implementation);
  }

  /**
   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
   * {TransparentUpgradeableProxy-upgradeToAndCall}.
   *
   * Requirements:
   *
   * - This contract must be the admin of `proxy`.
   */
  function upgradeAndCall(
    TransparentUpgradeableProxy proxy,
    address implementation,
    bytes memory data
  ) public payable virtual onlyOwner {
    proxy.upgradeToAndCall{value: msg.value}(implementation, data);
  }
}
                

contracts/kyc/IKYCRegistry.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

/**
 * @title IKYCRegistry
 * @author Ondo Finance
 * @notice The interface for Ondo's KYC Registry contract
 */
interface IKYCRegistry {
  /**
   * @notice Retrieves KYC status of an account
   *
   * @param kycRequirementGroup The KYC group for which we wish to check
   * @param account             The account we wish to retrieve KYC status for
   *
   * @return bool Whether the `account` is KYC'd
   */
  function getKYCStatus(
    uint256 kycRequirementGroup,
    address account
  ) external view returns (bool);

  /**
   * @notice View function for the public nested mapping of kycState
   *
   * @param kycRequirementGroup The KYC group to view
   * @param account             The account to check if KYC'd
   */
  function kycState(
    uint256 kycRequirementGroup,
    address account
  ) external view returns (bool);
}
                

contracts/interfaces/IRWALike.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐
 */
pragma solidity 0.8.16;

// This interface is not inherited directly by RWA, instead, it is a
// subset of functions provided by all RWA tokens that the RWA Hub
// Client uses.
import "contracts/external/openzeppelin/contracts/token/IERC20.sol";

interface IRWALike is IERC20 {
  function mint(address to, uint256 amount) external;

  function burn(uint256 amount) external;

  function burnFrom(address from, uint256 amount) external;
}
                

contracts/interfaces/IBlocklist.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

interface IBlocklist {
  function addToBlocklist(address[] calldata accounts) external;

  function removeFromBlocklist(address[] calldata accounts) external;

  function isBlocked(address account) external view returns (bool);

  /**
   * @notice Event emitted when addresses are added to the blocklist
   *
   * @param accounts The addresses that were added to the blocklist
   */
  event BlockedAddressesAdded(address[] accounts);

  /**
   * @notice Event emitted when addresses are removed from the blocklist
   *
   * @param accounts The addresses that were removed from the blocklist
   */
  event BlockedAddressesRemoved(address[] accounts);
}
                

contracts/ommf/ommfManager.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */

pragma solidity 0.8.16;

import "contracts/interfaces/IWommf.sol";
import "contracts/kyc/KYCRegistryClient.sol";
import "contracts/RWAHubInstantMints.sol";

contract OMMFManager is RWAHubInstantMints, KYCRegistryClient {
  /// @notice `rwa` variable is OMMF token contract
  IWOMMF public immutable wommf;
  bytes32 public constant REDEMPTION_PROVER_ROLE =
    keccak256("REDEMPTION_PROVER_ROLE");

  constructor(
    address _collateral,
    address _rwa,
    address managerAdmin,
    address pauser,
    address _assetSender,
    address _feeRecipient,
    uint256 _minimumDepositAmount,
    uint256 _minimumRedemptionAmount,
    address _instantMintAssetManager,
    address _kycRegistry,
    uint256 _kycRequirementGroup,
    address _wommf
  )
    RWAHubInstantMints(
      _collateral,
      _rwa,
      managerAdmin,
      pauser,
      _assetSender,
      _feeRecipient,
      _minimumDepositAmount,
      _minimumRedemptionAmount,
      _instantMintAssetManager
    )
  {
    _setKYCRegistry(_kycRegistry);
    _setKYCRequirementGroup(_kycRequirementGroup);
    wommf = IWOMMF(_wommf);
    _setRoleAdmin(REDEMPTION_PROVER_ROLE, MANAGER_ADMIN);
  }

  /**
   * @notice Function to add a redemption proof to the contract
   *
   * @param txHash           The tx hash (redemption Id) of the redemption
   * @param user             The address of the user who made the redemption
   * @param rwaAmountToBurn  The amount of OMMF burned
   * @param timestamp        The timestamp of the redemption request
   */
  function addRedemptionProof(
    bytes32 txHash,
    address user,
    uint256 rwaAmountToBurn,
    uint256 timestamp
  ) external onlyRole(REDEMPTION_PROVER_ROLE) checkRestrictions(user) {
    if (redemptionIdToRedeemer[txHash].user != address(0)) {
      revert RedemptionProofAlreadyExists();
    }
    if (rwaAmountToBurn == 0) {
      revert RedemptionTooSmall();
    }
    if (user == address(0)) {
      revert RedeemerNull();
    }
    rwa.burnFrom(msg.sender, rwaAmountToBurn);
    redemptionIdToRedeemer[txHash] = Redeemer(user, rwaAmountToBurn, 0);

    emit RedemptionProofAdded(txHash, user, rwaAmountToBurn, timestamp);
  }

  /**
   * @notice Function to claim a subscription request in wrapped rwa
   *         token
   *
   * @param depositIds The depositIds to be claimed
   */
  function claimMint_wOMMF(
    bytes32[] memory depositIds
  ) external nonReentrant ifNotPaused(subscriptionPaused) {
    uint256 cacheLength = depositIds.length;
    for (uint256 i; i < cacheLength; ++i) {
      // Get depositor
      Depositor memory depositor = depositIdToDepositor[depositIds[i]];

      // Check if the depositor is valid
      if (depositor.priceId == 0) {
        revert PriceIdNotSet();
      }

      // Get price and rwaOwed based on priceId
      uint256 price = pricer.getPrice(depositor.priceId);
      uint256 rwaOwed = _getMintAmountForPrice(
        depositor.amountDepositedMinusFees,
        price
      );

      // Clean up storage and mint
      delete depositIdToDepositor[depositIds[i]];
      rwa.mint(address(this), rwaOwed);

      // Wrap and transfer wOMMF
      rwa.approve(address(wommf), rwaOwed);
      wommf.wrap(rwaOwed);
      uint256 wRwaOwed = wommf.getwOMMFByOMMF(rwaOwed);
      wommf.transfer(depositor.user, wRwaOwed);

      emit WrappedMintCompleted(
        depositor.user,
        depositIds[i],
        rwaOwed,
        wRwaOwed,
        depositor.amountDepositedMinusFees,
        price
      );
    }
  }

  /**
   * @notice Function to request a redemption in instances when the user would
   *         like to burn the wrapped rwa token
   *
   * @param amount The amount of wrapped rwa that the user would like to burn
   */
  function requestRedemption_wOMMF(
    uint256 amount
  ) external nonReentrant ifNotPaused(redemptionPaused) {
    uint256 ommfAmount = wommf.getOMMFbywOMMF(amount);
    if (ommfAmount < minimumRedemptionAmount) {
      revert RedemptionTooSmall();
    }

    // Transfer and unwrap
    wommf.transferFrom(msg.sender, address(this), amount);
    wommf.unwrap(amount);

    bytes32 redemptionId = bytes32(redemptionRequestCounter++);
    redemptionIdToRedeemer[redemptionId] = Redeemer(msg.sender, amount, 0);

    rwa.burn(ommfAmount);

    emit WrappedRedemptionRequested(
      msg.sender,
      redemptionId,
      ommfAmount,
      amount
    );
  }

  /**
   * @notice Function to enforce KYC/AML requirements that will
   *         be implemented on calls to `requestSubscription` and
   *         `claimRedemption`
   *
   * @param account The account that we would like to check the KYC
   *                status for
   */
  function _checkRestrictions(address account) internal view override {
    // Check Basic KYC requirements for OMMF
    if (!_getKYCStatus(account)) {
      revert KYCCheckFailed();
    }
  }

  /*//////////////////////////////////////////////////////////////
                        KYC Registry Utils
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Update KYC group of the contract for which
   *         accounts are checked against
   *
   * @param _kycRequirementGroup The new KYC requirement group
   */
  function setKYCRequirementGroup(
    uint256 _kycRequirementGroup
  ) external onlyRole(MANAGER_ADMIN) {
    _setKYCRequirementGroup(_kycRequirementGroup);
  }

  /**
   * @notice Update KYC registry address
   *
   * @param _kycRegistry The new KYC registry address
   */
  function setKYCRegistry(
    address _kycRegistry
  ) external onlyRole(MANAGER_ADMIN) {
    _setKYCRegistry(_kycRegistry);
  }

  /**
   * @notice Event emitted when a Mint request is completed
   *
   * @param user                      The address of the user getting the funds
   * @param depositId                 The deposit Id for the subscription request
   * @param rwaAmountOut              The amount of RWA token minted to the
   *                                  user
   * @param wRWAAmountOut             The amount of wrapped RWA token minted
   * @param collateralAmountDeposited The amount of collateral deposited
   * @param price                     The price set for the depositId
   */
  event WrappedMintCompleted(
    address indexed user,
    bytes32 indexed depositId,
    uint256 rwaAmountOut,
    uint256 wRWAAmountOut,
    uint256 collateralAmountDeposited,
    uint256 price
  );

  /**
   * @notice Event emitted when a Redemption request is completed
   *
   * @param user               The address of the user
   * @param redemptionId       The redemption Id for a given redemption
   *                           request
   * @param rwaAmountIn        The amount of rwa being redeemed
   * @param wrappedRwaAmountIn The amount of wrapped rwa to convert to rwa
   */
  event WrappedRedemptionRequested(
    address indexed user,
    bytes32 indexed redemptionId,
    uint256 rwaAmountIn,
    uint256 wrappedRwaAmountIn
  );

  /**
   * @notice Event emitted when redemption proof has been added
   *
   * @param txHash                Tx hash (redemption id) of the redemption transfer
   * @param user                  Address of the user who made the redemption
   * @param rwaAmountBurned       Amount of OMMF burned
   * @param timestamp             Timestamp of the redemption
   */
  event RedemptionProofAdded(
    bytes32 indexed txHash,
    address indexed user,
    uint256 rwaAmountBurned,
    uint256 timestamp
  );

  error KYCCheckFailed();
}
                

contracts/interfaces/IPricerReader.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

interface IPricerReader {
  /**
   * @notice Gets the latest price of the asset
   *
   * @return uint256 The latest price of the asset
   */
  function getLatestPrice() external view returns (uint256);

  /**
   * @notice Gets the price of the asset at a specific priceId
   *
   * @param priceId The priceId at which to get the price
   *
   * @return uint256 The price of the asset with the given priceId
   */
  function getPrice(uint256 priceId) external view returns (uint256);
}
                

contracts/rwaOracles/IRWAOracle.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

interface IRWAOracle {
  /// @notice Retrieve RWA price data
  function getPriceData()
    external
    view
    returns (uint256 price, uint256 timestamp);
}
                

contracts/external/openzeppelin/contracts/security/Pausable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts/utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
  /**
   * @dev Emitted when the pause is triggered by `account`.
   */
  event Paused(address account);

  /**
   * @dev Emitted when the pause is lifted by `account`.
   */
  event Unpaused(address account);

  bool private _paused;

  /**
   * @dev Initializes the contract in unpaused state.
   */
  constructor() {
    _paused = false;
  }

  /**
   * @dev Returns true if the contract is paused, and false otherwise.
   */
  function paused() public view virtual returns (bool) {
    return _paused;
  }

  /**
   * @dev Modifier to make a function callable only when the contract is not paused.
   *
   * Requirements:
   *
   * - The contract must not be paused.
   */
  modifier whenNotPaused() {
    require(!paused(), "Pausable: paused");
    _;
  }

  /**
   * @dev Modifier to make a function callable only when the contract is paused.
   *
   * Requirements:
   *
   * - The contract must be paused.
   */
  modifier whenPaused() {
    require(paused(), "Pausable: not paused");
    _;
  }

  /**
   * @dev Triggers stopped state.
   *
   * Requirements:
   *
   * - The contract must not be paused.
   */
  function _pause() internal virtual whenNotPaused {
    _paused = true;
    emit Paused(_msgSender());
  }

  /**
   * @dev Returns to normal state.
   *
   * Requirements:
   *
   * - The contract must be paused.
   */
  function _unpause() internal virtual whenPaused {
    _paused = false;
    emit Unpaused(_msgSender());
  }
}
                

contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/IERC721EnumerableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721EnumerableUpgradeable is IERC721Upgradeable {
  /**
   * @dev Returns the total amount of tokens stored by the contract.
   */
  function totalSupply() external view returns (uint256);

  /**
   * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
   * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
   */
  function tokenOfOwnerByIndex(address owner, uint256 index)
    external
    view
    returns (uint256);

  /**
   * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
   * Use along with {totalSupply} to enumerate all tokens.
   */
  function tokenByIndex(uint256 index) external view returns (uint256);
}
                

contracts/external/chainlink/AggregatorV3Interface.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

  function getRoundData(
    uint80 _roundId
  )
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}
                

contracts/usdy/blocklist/Blocklist.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;
import "contracts/external/openzeppelin/contracts/access/Ownable2Step.sol";
import "contracts/interfaces/IBlocklist.sol";

/**
 * @title Blocklist
 * @author Ondo Finance
 * @notice This contract manages the blocklist status for accounts.
 */
contract Blocklist is Ownable2Step, IBlocklist {
  constructor() {}

  // {<address> => is account blocked}
  mapping(address => bool) private blockedAddresses;

  /**
   * @notice Returns name of contract
   */
  function name() external pure returns (string memory) {
    return "Ondo Finance Blocklist Oracle";
  }

  /**
   * @notice Function to add a list of accounts to the blocklist
   *
   * @param accounts Array of addresses to block
   */
  function addToBlocklist(address[] calldata accounts) external onlyOwner {
    for (uint256 i; i < accounts.length; ++i) {
      blockedAddresses[accounts[i]] = true;
    }
    emit BlockedAddressesAdded(accounts);
  }

  /**
   * @notice Function to remove a list of accounts from the blocklist
   *
   * @param accounts Array of addresses to unblock
   */
  function removeFromBlocklist(address[] calldata accounts) external onlyOwner {
    for (uint256 i; i < accounts.length; ++i) {
      blockedAddresses[accounts[i]] = false;
    }
    emit BlockedAddressesRemoved(accounts);
  }

  /**
   * @notice Function to check if an account is blocked
   *
   * @param addr Address to check
   *
   * @return True if account is blocked, false otherwise
   */
  function isBlocked(address addr) external view returns (bool) {
    return blockedAddresses[addr];
  }
}
                

contracts/external/openzeppelin/contracts/token/SafeERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts/token/IERC20.sol";
import "contracts/external/openzeppelin/contracts/utils/Address.sol";

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

  function safeTransfer(
    IERC20 token,
    address to,
    uint256 value
  ) internal {
    _callOptionalReturn(
      token,
      abi.encodeWithSelector(token.transfer.selector, to, value)
    );
  }

  function safeTransferFrom(
    IERC20 token,
    address from,
    address to,
    uint256 value
  ) internal {
    _callOptionalReturn(
      token,
      abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
    );
  }

  /**
   * @dev Deprecated. This function has issues similar to the ones found in
   * {IERC20-approve}, and its usage is discouraged.
   *
   * Whenever possible, use {safeIncreaseAllowance} and
   * {safeDecreaseAllowance} instead.
   */
  function safeApprove(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    // safeApprove should only be called when setting an initial allowance,
    // or when resetting it to zero. To increase and decrease it, use
    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
    require(
      (value == 0) || (token.allowance(address(this), spender) == 0),
      "SafeERC20: approve from non-zero to non-zero allowance"
    );
    _callOptionalReturn(
      token,
      abi.encodeWithSelector(token.approve.selector, spender, value)
    );
  }

  function safeIncreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    uint256 newAllowance = token.allowance(address(this), spender) + value;
    _callOptionalReturn(
      token,
      abi.encodeWithSelector(token.approve.selector, spender, newAllowance)
    );
  }

  function safeDecreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    unchecked {
      uint256 oldAllowance = token.allowance(address(this), spender);
      require(
        oldAllowance >= value,
        "SafeERC20: decreased allowance below zero"
      );
      uint256 newAllowance = oldAllowance - value;
      _callOptionalReturn(
        token,
        abi.encodeWithSelector(token.approve.selector, spender, newAllowance)
      );
    }
  }

  /**
   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
   * on the return value: the return value is optional (but if data is returned, it must not be false).
   * @param token The token targeted by the call.
   * @param data The call data (encoded using abi.encode or one of its variants).
   */
  function _callOptionalReturn(IERC20 token, bytes memory data) private {
    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
    // the target address contains contract code and also asserts for success in the low-level call.

    bytes memory returndata =
      address(token).functionCall(data, "SafeERC20: low-level call failed");
    if (returndata.length > 0) {
      // Return data is optional
      require(
        abi.decode(returndata, (bool)),
        "SafeERC20: ERC20 operation did not succeed"
      );
    }
  }
}
                

contracts/usdy/rusdyw/rUSDYW.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/IERC20MetadataUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "contracts/usdy/blocklist/BlocklistClientUpgradeable.sol";
import "contracts/usdy/allowlist/AllowlistClientUpgradeable.sol";
import "contracts/sanctions/SanctionsListClientUpgradeable.sol";
import "contracts/interfaces/IUSDY.sol";
import "contracts/rwaOracles/IRWADynamicOracle.sol";

/**
 * @title Interest-bearing ERC20-like token for rUSDY.
 *
 * rUSDY balances are dynamic and represent the holder's share of the underlying USDY
 * controlled by the protocol. To calculate each account's balance, we do
 *
 *   shares[account] * usdyPrice
 *
 * For example, assume that we have:
 *
 *   usdyPrice = 1.05
 *   sharesOf(user1) -> 100
 *   sharesOf(user2) -> 400
 *
 * Therefore:
 *
 *   balanceOf(user1) -> 105 tokens which corresponds 105 rUSDY
 *   balanceOf(user2) -> 420 tokens which corresponds 420 rUSDY
 *
 * Since balances of all token holders change when the price of USDY changes, this
 * token cannot fully implement ERC20 standard: it only emits `Transfer` events
 * upon explicit transfer between holders. In contrast, when total amount of pooled
 * Cash increases, no `Transfer` events are generated: doing so would require emitting
 * an event for each token holder and thus running an unbounded loop.
 *
 */

contract rUSDYW is
  Initializable,
  ContextUpgradeable,
  PausableUpgradeable,
  AccessControlEnumerableUpgradeable,
  BlocklistClientUpgradeable,
  IERC20Upgradeable,
  IERC20MetadataUpgradeable
{
  /**
   * @dev rUSDY balances are dynamic and are calculated based on the accounts' shares (USDY)
   * and the the price of USDY. Account shares aren't
   * normalized, so the contract also stores the sum of all shares to calculate
   * each account's token balance which equals to:
   *
   *   shares[account] * usdyPrice
   */
  mapping(address => uint256) private shares;

  /// @dev Allowances are nominated in tokens, not token shares.
  mapping(address => mapping(address => uint256)) private allowances;

  // Total shares in existence
  uint256 private totalShares;

  // Address of the oracle that updates `usdyPrice`
  IRWADynamicOracle public oracle;

  // Address of the USDY token
  IUSDY public usdy;

  // Used to scale up usdy amount -> shares
  uint256 public constant BPS_DENOMINATOR = 10_000;

  // Error when redeeming shares < `BPS_DENOMINATOR`
  error UnwrapTooSmall();

  // Used to hold current terms and condition information
  string public currentTerms;

  /// @dev Role based access control roles
  bytes32 public constant USDY_MANAGER_ROLE = keccak256("ADMIN_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
  bytes32 public constant LIST_CONFIGURER_ROLE =
    keccak256("LIST_CONFIGURER_ROLE");

  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor() {
    _disableInitializers();
  }

  function initialize(
    address blocklist,
    address _usdy,
    address guardian,
    address _oracle
  ) public virtual initializer {
    __rUSDY_init(blocklist, _usdy, guardian, _oracle);
  }

  function __rUSDY_init(
    address blocklist,
    address _usdy,
    address guardian,
    address _oracle
  ) internal onlyInitializing {
    __BlocklistClientInitializable_init(blocklist);
    __rUSDY_init_unchained(_usdy, guardian, _oracle);
  }

  function __rUSDY_init_unchained(
    address _usdy,
    address guardian,
    address _oracle
  ) internal onlyInitializing {
    usdy = IUSDY(_usdy);
    oracle = IRWADynamicOracle(_oracle);
    _grantRole(DEFAULT_ADMIN_ROLE, guardian);
    _grantRole(USDY_MANAGER_ROLE, guardian);
    _grantRole(PAUSER_ROLE, guardian);
    _grantRole(BURNER_ROLE, guardian);
    _grantRole(LIST_CONFIGURER_ROLE, guardian);
  }

  /**
   * @notice An executed shares transfer from `sender` to `recipient`.
   *
   * @dev emitted in pair with an ERC20-defined `Transfer` event.
   */
  event TransferShares(
    address indexed from,
    address indexed to,
    uint256 sharesValue
  );

  /**
   * @notice An executed `burnShares` request
   *
   * @dev Reports simultaneously burnt shares amount
   * and corresponding rUSDY amount.
   * The shares amount is calculated twice: before and after the burning incurred rebase.
   *
   * @param account holder of the burnt shares
   * @param preRebaseTokenAmount amount of rUSDY the burnt shares (USDY) corresponded to before the burn
   * @param postRebaseTokenAmount amount of rUSDY the burnt shares (USDY) corresponded to after the burn
   * @param sharesAmount amount of burnt shares
   */
  event SharesBurnt(
    address indexed account,
    uint256 preRebaseTokenAmount,
    uint256 postRebaseTokenAmount,
    uint256 sharesAmount
  );

  /**
   * @notice An executed `burnShares` request
   *
   * @dev Reports simultaneously burnt shares amount
   * and corresponding rUSDY amount.
   * The rUSDY amount is calculated twice: before and after the burning incurred rebase.
   *
   * @param account holder of the burnt shares
   * @param tokensBurnt amount of burnt tokens
   */
  event TokensBurnt(address indexed account, uint256 tokensBurnt);

  /**
   * @return the name of the token.
   */
  function name() public pure returns (string memory) {
    return "Mantle USD";
  }

  /**
   * @return the symbol of the token, usually a shorter version of the
   * name.
   */
  function symbol() public pure returns (string memory) {
    return "mUSD";
  }

  /**
   * @return the number of decimals for getting user representation of a token amount.
   */
  function decimals() public pure returns (uint8) {
    return 18;
  }

  /**
   * @return the amount of tokens in existence.
   */
  function totalSupply() public view returns (uint256) {
    return (totalShares * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR);
  }

  /**
   * @return the amount of tokens owned by the `_account`.
   *
   * @dev Balances are dynamic and equal the `_account`'s USDY shares multiplied
   *      by the price of USDY
   */
  function balanceOf(address _account) public view returns (uint256) {
    return (_sharesOf(_account) * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR);
  }

  /**
   * @notice Moves `_amount` tokens from the caller's account to the `_recipient` account.
   *
   * @return a boolean value indicating whether the operation succeeded.
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the caller must have a balance of at least `_amount`.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function transfer(address _recipient, uint256 _amount) public returns (bool) {
    _transfer(msg.sender, _recipient, _amount);
    return true;
  }

  /**
   * @return the remaining number of tokens that `_spender` is allowed to spend
   * on behalf of `_owner` through `transferFrom`. This is zero by default.
   *
   * @dev This value changes when `approve` or `transferFrom` is called.
   */
  function allowance(
    address _owner,
    address _spender
  ) public view returns (uint256) {
    return allowances[_owner][_spender];
  }

  /**
   * @notice Sets `_amount` as the allowance of `_spender` over the caller's tokens.
   *
   * @return a boolean value indicating whether the operation succeeded.
   * Emits an `Approval` event.
   *
   * Requirements:
   *
   * - `_spender` cannot be the zero address.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function approve(address _spender, uint256 _amount) public returns (bool) {
    _approve(msg.sender, _spender, _amount);
    return true;
  }

  /**
   * @notice Moves `_amount` tokens from `_sender` to `_recipient` using the
   * allowance mechanism. `_amount` is then deducted from the caller's
   * allowance.
   *
   * @return a boolean value indicating whether the operation succeeded.
   *
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_sender` and `_recipient` cannot be the zero addresses.
   * - `_sender` must have a balance of at least `_amount`.
   * - the caller must have allowance for `_sender`'s tokens of at least `_amount`.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function transferFrom(
    address _sender,
    address _recipient,
    uint256 _amount
  ) public returns (bool) {
    uint256 currentAllowance = allowances[_sender][msg.sender];
    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");

    _transfer(_sender, _recipient, _amount);
    _approve(_sender, msg.sender, currentAllowance - _amount);
    return true;
  }

  /**
   * @notice Atomically increases the allowance granted to `_spender` by the caller by `_addedValue`.
   *
   * This is an alternative to `approve` that can be used as a mitigation for
   * problems described in:
   * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_spender` cannot be the the zero address.
   * - the contract must not be paused.
   */
  function increaseAllowance(
    address _spender,
    uint256 _addedValue
  ) public returns (bool) {
    _approve(
      msg.sender,
      _spender,
      allowances[msg.sender][_spender] + _addedValue
    );
    return true;
  }

  /**
   * @notice Atomically decreases the allowance granted to `_spender` by the caller by `_subtractedValue`.
   *
   * This is an alternative to `approve` that can be used as a mitigation for
   * problems described in:
   * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_spender` cannot be the zero address.
   * - `_spender` must have allowance for the caller of at least `_subtractedValue`.
   * - the contract must not be paused.
   */
  function decreaseAllowance(
    address _spender,
    uint256 _subtractedValue
  ) public returns (bool) {
    uint256 currentAllowance = allowances[msg.sender][_spender];
    require(
      currentAllowance >= _subtractedValue,
      "DECREASED_ALLOWANCE_BELOW_ZERO"
    );
    _approve(msg.sender, _spender, currentAllowance - _subtractedValue);
    return true;
  }

  /**
   * @return the total amount of shares in existence.
   *
   * @dev The sum of all accounts' shares can be an arbitrary number, therefore
   * it is necessary to store it in order to calculate each account's relative share.
   */
  function getTotalShares() public view returns (uint256) {
    return totalShares;
  }

  /**
   * @return the amount of shares owned by `_account`.
   *
   * @dev This is the equivalent to the amount of USDY wrapped by `_account`.
   */
  function sharesOf(address _account) public view returns (uint256) {
    return _sharesOf(_account);
  }

  /**
   * @return the amount of USDY that corresponds to `_rUSDYAmount` of rUSDY
   */
  function getSharesByRUSDY(
    uint256 _rUSDYAmount
  ) public view returns (uint256) {
    return (_rUSDYAmount * 1e18 * BPS_DENOMINATOR) / oracle.getPrice();
  }

  /**
   * @return the amount of rUSDY that corresponds to `_shares` of usdy.
   */
  function getRUSDYByShares(uint256 _shares) public view returns (uint256) {
    return (_shares * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR);
  }

  /**
   * @notice Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account.
   *
   * @return amount of transferred tokens.
   * Emits a `TransferShares` event.
   * Emits a `Transfer` event.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the caller must have at least `_sharesAmount` shares.
   * - the contract must not be paused.
   *
   * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
   */
  function transferShares(
    address _recipient,
    uint256 _sharesAmount
  ) public returns (uint256) {
    _transferShares(msg.sender, _recipient, _sharesAmount);
    emit TransferShares(msg.sender, _recipient, _sharesAmount);
    uint256 tokensAmount = getRUSDYByShares(_sharesAmount);
    emit Transfer(msg.sender, _recipient, tokensAmount);
    return tokensAmount;
  }

  /**
   * @notice Function called by users to wrap their USDY tokens
   *
   * @param _USDYAmount The amount of USDY Tokens to wrap
   *
   * @dev Sanctions, Blocklist, and Allowlist checks implicit in USDY Transfer
   */
  function wrap(uint256 _USDYAmount) external whenNotPaused {
    require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
    uint256 usdySharesAmount = _USDYAmount * BPS_DENOMINATOR;
    _mintShares(msg.sender, usdySharesAmount);
    usdy.transferFrom(msg.sender, address(this), _USDYAmount);
    emit Transfer(address(0), msg.sender, getRUSDYByShares(usdySharesAmount));
    emit TransferShares(address(0), msg.sender, usdySharesAmount);
  }

  /**
   * @notice Function called by users to unwrap their rUSDY tokens
   *
   * @param _rUSDYAmount The amount of rUSDY to unwrap
   *
   * @dev Sanctions, Blocklist, and Allowlist checks implicit in USDY Transfer
   */
  function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
    require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");
    uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
    if (usdyAmount < BPS_DENOMINATOR) revert UnwrapTooSmall();
    _burnShares(msg.sender, usdyAmount);
    usdy.transfer(msg.sender, usdyAmount / BPS_DENOMINATOR);
    emit Transfer(msg.sender, address(0), _rUSDYAmount);
    emit TokensBurnt(msg.sender, _rUSDYAmount);
  }

  /**
   * @notice Moves `_amount` tokens from `_sender` to `_recipient`.
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   */
  function _transfer(
    address _sender,
    address _recipient,
    uint256 _amount
  ) internal {
    uint256 _sharesToTransfer = getSharesByRUSDY(_amount);
    _transferShares(_sender, _recipient, _sharesToTransfer);
    emit Transfer(_sender, _recipient, _amount);
    emit TransferShares(_sender, _recipient, _sharesToTransfer);
  }

  /**
   * @notice Sets `_amount` as the allowance of `_spender` over the `_owner` s tokens.
   *
   * Emits an `Approval` event.
   *
   * Requirements:
   *
   * - `_owner` cannot be the zero address.
   * - `_spender` cannot be the zero address.
   * - the contract must not be paused.
   */
  function _approve(
    address _owner,
    address _spender,
    uint256 _amount
  ) internal whenNotPaused {
    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

    allowances[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
  }

  /**
   * @return the amount of shares owned by `_account`.
   */
  function _sharesOf(address _account) internal view returns (uint256) {
    return shares[_account];
  }

  /**
   * @notice Moves `_sharesAmount` shares from `_sender` to `_recipient`.
   *
   * Requirements:
   *
   * - `_sender` cannot be the zero address.
   * - `_recipient` cannot be the zero address.
   * - `_sender` must hold at least `_sharesAmount` shares.
   * - the contract must not be paused.
   */
  function _transferShares(
    address _sender,
    address _recipient,
    uint256 _sharesAmount
  ) internal whenNotPaused {
    require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
    require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_sender, _recipient, _sharesAmount);

    uint256 currentSenderShares = shares[_sender];
    require(
      _sharesAmount <= currentSenderShares,
      "TRANSFER_AMOUNT_EXCEEDS_BALANCE"
    );

    shares[_sender] = currentSenderShares - _sharesAmount;
    shares[_recipient] = shares[_recipient] + _sharesAmount;
  }

  /**
   * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
   * @dev This doesn't increase the token total supply.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the contract must not be paused.
   */
  function _mintShares(
    address _recipient,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_recipient != address(0), "MINT_TO_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(address(0), _recipient, _sharesAmount);

    totalShares += _sharesAmount;

    shares[_recipient] = shares[_recipient] + _sharesAmount;

    return totalShares;

    // Notice: we're not emitting a Transfer event from the zero address here since shares mint
    // works by taking the amount of tokens corresponding to the minted shares from all other
    // token holders, proportionally to their share. The total supply of the token doesn't change
    // as the result. This is equivalent to performing a send from each other token holder's
    // address to `address`, but we cannot reflect this as it would require sending an unbounded
    // number of events.
  }

  /**
   * @notice Destroys `_sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
   * @dev This doesn't decrease the token total supply.
   *
   * Requirements:
   *
   * - `_account` cannot be the zero address.
   * - `_account` must hold at least `_sharesAmount` shares.
   * - the contract must not be paused.
   */
  function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_account, address(0), _sharesAmount);

    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");

    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    emit SharesBurnt(
      _account,
      preRebaseTokenAmount,
      postRebaseTokenAmount,
      _sharesAmount
    );

    return totalShares;

    // Notice: we're not emitting a Transfer event to the zero address here since shares burn
    // works by redistributing the amount of tokens corresponding to the burned shares between
    // all other token holders. The total supply of the token doesn't change as the result.
    // This is equivalent to performing a send from `address` to each other token holder address,
    // but we cannot reflect this as it would require sending an unbounded number of events.

    // We're emitting `SharesBurnt` event to provide an explicit rebase log record nonetheless.
  }

  /**
   * @dev Hook that is called before any transfer of tokens. This includes
   * minting and burning.
   *
   * Calling conditions:
   *
   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
   * will be transferred to `to`.
   * - when `from` is zero, `amount` tokens will be minted for `to`.
   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
   * - `from` and `to` are never both zero.
   *
   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256
  ) internal view {
    // Check constraints when `transferFrom` is called to facliitate
    // a transfer between two parties that are not `from` or `to`.
    if (from != msg.sender && to != msg.sender) {
      require(!_isBlocked(msg.sender), "rUSDY: 'sender' address blocked");
    }

    if (from != address(0)) {
      // If not minting
      require(!_isBlocked(from), "rUSDY: 'from' address blocked");
    }

    if (to != address(0)) {
      // If not burning
      require(!_isBlocked(to), "rUSDY: 'to' address blocked");
    }
  }

  /**
   * @notice Sets the Oracle address
   * @dev The new oracle must comply with the `IPricerReader` interface
   * @param _oracle Address of the new oracle
   */
  function setOracle(address _oracle) external onlyRole(USDY_MANAGER_ROLE) {
    oracle = IRWADynamicOracle(_oracle);
  }

  /**
   * @notice Admin burn function to burn rUSDY tokens from any account
   * @param _account The account to burn tokens from
   * @param _amount  The amount of rUSDY tokens to burn
   * @dev Transfers burned shares (USDY) to `msg.sender`
   */
  function burn(
    address _account,
    uint256 _amount
  ) external onlyRole(BURNER_ROLE) {
    uint256 sharesAmount = getSharesByRUSDY(_amount);

    _burnShares(_account, sharesAmount);

    usdy.transfer(msg.sender, sharesAmount / BPS_DENOMINATOR);

    emit TokensBurnt(_account, _amount);
  }

  function pause() external onlyRole(PAUSER_ROLE) {
    _pause();
  }

  function unpause() external onlyRole(USDY_MANAGER_ROLE) {
    _unpause();
  }

  /**
   * @notice Sets the blocklist address
   *
   * @param blocklist New blocklist address
   */
  function setBlocklist(
    address blocklist
  ) external override onlyRole(LIST_CONFIGURER_ROLE) {
    _setBlocklist(blocklist);
  }

  /***
   * @notice Sets the current Terms for USDY
   *
   * @param newTerm New Terms to update to
   */
  function updateTerm(
    string calldata newTerm
  ) external onlyRole(LIST_CONFIGURER_ROLE) {
    string memory oldTerms = currentTerms;
    currentTerms = newTerm;
    emit TermsUpdated(oldTerms, currentTerms);
  }

  /**
   * @notice Event emitted when the terms are update
   *
   * @param oldTerms The old terms being updated
   * @param newTerms The new terms we are updating to
   */
  event TermsUpdated(string oldTerms, string newTerms);
}
                

contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721PausableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Pausable.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @dev ERC721 token with pausable token transfers, minting and burning.
 *
 * Useful for scenarios such as preventing trades until the end of an evaluation
 * period, or having an emergency switch for freezing all token transfers in the
 * event of a large bug.
 */
abstract contract ERC721PausableUpgradeable is
  Initializable,
  ERC721Upgradeable,
  PausableUpgradeable
{
  function __ERC721Pausable_init() internal onlyInitializing {
    __Pausable_init_unchained();
  }

  function __ERC721Pausable_init_unchained() internal onlyInitializing {}

  /**
   * @dev See {ERC721-_beforeTokenTransfer}.
   *
   * Requirements:
   *
   * - the contract must not be paused.
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 tokenId
  ) internal virtual override {
    super._beforeTokenTransfer(from, to, tokenId);

    require(!paused(), "ERC721Pausable: token transfer while paused");
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[50] private __gap;
}
                

contracts/usdy/USDYManager.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */

pragma solidity 0.8.16;

import "contracts/RWAHubOffChainRedemptions.sol";
import "contracts/usdy/blocklist/BlocklistClient.sol";
import "contracts/sanctions/SanctionsListClient.sol";
import "contracts/interfaces/IUSDYManager.sol";

contract USDYManager is
  RWAHubOffChainRedemptions,
  BlocklistClient,
  SanctionsListClient,
  IUSDYManager
{
  bytes32 public constant TIMESTAMP_SETTER_ROLE =
    keccak256("TIMESTAMP_SETTER_ROLE");

  mapping(bytes32 => uint256) public depositIdToClaimableTimestamp;

  constructor(
    address _collateral,
    address _rwa,
    address managerAdmin,
    address pauser,
    address _assetSender,
    address _feeRecipient,
    uint256 _minimumDepositAmount,
    uint256 _minimumRedemptionAmount,
    address blocklist,
    address sanctionsList
  )
    RWAHubOffChainRedemptions(
      _collateral,
      _rwa,
      managerAdmin,
      pauser,
      _assetSender,
      _feeRecipient,
      _minimumDepositAmount,
      _minimumRedemptionAmount
    )
    BlocklistClient(blocklist)
    SanctionsListClient(sanctionsList)
  {}

  /**
   * @notice Function to enforce blocklist and sanctionslist restrictions to be
   *         implemented on calls to `requestSubscription` and
   *         `claimRedemption`
   *
   * @param account The account to check blocklist and sanctions list status
   *                for
   */
  function _checkRestrictions(address account) internal view override {
    if (_isBlocked(account)) {
      revert BlockedAccount();
    }
    if (_isSanctioned(account)) {
      revert SanctionedAccount();
    }
  }

  /**
   * @notice Internal hook that is called by `claimMint` to enforce the time
   *         at which a user can claim their USDY
   *
   * @param depositId The depositId to check the claimable timestamp for
   *
   * @dev This function will call the `_claimMint` function in the parent
   *      once USDY-specific checks have been made
   */
  function _claimMint(bytes32 depositId) internal virtual override {
    if (depositIdToClaimableTimestamp[depositId] == 0) {
      revert ClaimableTimestampNotSet();
    }

    if (depositIdToClaimableTimestamp[depositId] > block.timestamp) {
      revert MintNotYetClaimable();
    }

    super._claimMint(depositId);
    delete depositIdToClaimableTimestamp[depositId];
  }

  /**
   * @notice Update blocklist address
   *
   * @param blocklist The new blocklist address
   */
  function setBlocklist(
    address blocklist
  ) external override onlyRole(MANAGER_ADMIN) {
    _setBlocklist(blocklist);
  }

  /**
   * @notice Update sanctions list address
   *
   * @param sanctionsList The new sanctions list address
   */
  function setSanctionsList(
    address sanctionsList
  ) external override onlyRole(MANAGER_ADMIN) {
    _setSanctionsList(sanctionsList);
  }

  /**
   * @notice Set the claimable timestamp for a list of depositIds
   *
   * @param claimTimestamp The timestamp at which the deposit can be claimed
   * @param depositIds The depositIds to set the claimable timestamp for
   */
  function setClaimableTimestamp(
    uint256 claimTimestamp,
    bytes32[] calldata depositIds
  ) external onlyRole(TIMESTAMP_SETTER_ROLE) {
    if (claimTimestamp < block.timestamp) {
      revert ClaimableTimestampInPast();
    }

    uint256 depositsSize = depositIds.length;
    for (uint256 i; i < depositsSize; ++i) {
      depositIdToClaimableTimestamp[depositIds[i]] = claimTimestamp;
      emit ClaimableTimestampSet(claimTimestamp, depositIds[i]);
    }
  }
}
                

contracts/test/powUtils.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

// Library was taken from https://github.com/barakman/solidity-math-utils
// It consists of Math.sol && IntegralMath.sol.
//
// note: FractionalMath.sol L#7: 4 -changed to-> 40 to allow for the desired
//       exponential calculation.

uint256 constant MAX_VAL = type(uint256).max;

// reverts on overflow
function safeAdd(uint256 x, uint256 y) pure returns (uint256) {
  return x + y;
}

// does not revert on overflow
function unsafeAdd(uint256 x, uint256 y) pure returns (uint256) {
  unchecked {
    return x + y;
  }
}

// does not revert on overflow
function unsafeSub(uint256 x, uint256 y) pure returns (uint256) {
  unchecked {
    return x - y;
  }
}

// does not revert on overflow
function unsafeMul(uint256 x, uint256 y) pure returns (uint256) {
  unchecked {
    return x * y;
  }
}

// does not overflow
function mulModMax(uint256 x, uint256 y) pure returns (uint256) {
  unchecked {
    return mulmod(x, y, MAX_VAL);
  }
}

// does not overflow
function mulMod(uint256 x, uint256 y, uint256 z) pure returns (uint256) {
  unchecked {
    return mulmod(x, y, z);
  }
}

library IntegralMath {
  /**
   * @dev Compute the largest integer smaller than or equal to the binary logarithm of `n`
   */
  function floorLog2(uint256 n) internal pure returns (uint8) {
    unchecked {
      uint8 res = 0;

      if (n < 256) {
        // at most 8 iterations
        while (n > 1) {
          n >>= 1;
          res += 1;
        }
      } else {
        // exactly 8 iterations
        for (uint8 s = 128; s > 0; s >>= 1) {
          if (n >= 1 << s) {
            n >>= s;
            res |= s;
          }
        }
      }

      return res;
    }
  }

  /**
   * @dev Compute the largest integer smaller than or equal to the square root of `n`
   */
  function floorSqrt(uint256 n) internal pure returns (uint256) {
    unchecked {
      if (n > 0) {
        uint256 x = n / 2 + 1;
        uint256 y = (x + n / x) / 2;
        while (x > y) {
          x = y;
          y = (x + n / x) / 2;
        }
        return x;
      }
      return 0;
    }
  }

  /**
   * @dev Compute the smallest integer larger than or equal to the square root of `n`
   */
  function ceilSqrt(uint256 n) internal pure returns (uint256) {
    unchecked {
      uint256 x = floorSqrt(n);
      return x ** 2 == n ? x : x + 1;
    }
  }

  /**
   * @dev Compute the largest integer smaller than or equal to the cubic root of `n`
   */
  function floorCbrt(uint256 n) internal pure returns (uint256) {
    unchecked {
      uint256 x = 0;
      for (uint256 y = 1 << 255; y > 0; y >>= 3) {
        x <<= 1;
        uint256 z = 3 * x * (x + 1) + 1;
        if (n / y >= z) {
          n -= y * z;
          x += 1;
        }
      }
      return x;
    }
  }

  /**
   * @dev Compute the smallest integer larger than or equal to the cubic root of `n`
   */
  function ceilCbrt(uint256 n) internal pure returns (uint256) {
    unchecked {
      uint256 x = floorCbrt(n);
      return x ** 3 == n ? x : x + 1;
    }
  }

  /**
   * @dev Compute the nearest integer to the quotient of `n` and `d` (or `n / d`)
   */
  function roundDiv(uint256 n, uint256 d) internal pure returns (uint256) {
    unchecked {
      return n / d + (n % d) / (d - d / 2);
    }
  }

  /**
   * @dev Compute the largest integer smaller than or equal to `x * y / z`
   */
  function mulDivF(
    uint256 x,
    uint256 y,
    uint256 z
  ) internal pure returns (uint256) {
    unchecked {
      (uint256 xyh, uint256 xyl) = mul512(x, y);
      if (xyh == 0) {
        // `x * y < 2 ^ 256`
        return xyl / z;
      }
      if (xyh < z) {
        // `x * y / z < 2 ^ 256`
        uint256 m = mulMod(x, y, z); // `m = x * y % z`
        (uint256 nh, uint256 nl) = sub512(xyh, xyl, m); // `n = x * y - m` hence `n / z = floor(x * y / z)`
        if (nh == 0) {
          // `n < 2 ^ 256`
          return nl / z;
        }
        uint256 p = unsafeSub(0, z) & z; // `p` is the largest power of 2 which `z` is divisible by
        uint256 q = div512(nh, nl, p); // `n` is divisible by `p` because `n` is divisible by `z` and `z` is divisible by `p`
        uint256 r = inv256(z / p); // `z / p = 1 mod 2` hence `inverse(z / p) = 1 mod 2 ^ 256`
        return unsafeMul(q, r); // `q * r = (n / p) * inverse(z / p) = n / z`
      }
      revert(); // `x * y / z >= 2 ^ 256`
    }
  }

  /**
   * @dev Compute the smallest integer larger than or equal to `x * y / z`
   */
  function mulDivC(
    uint256 x,
    uint256 y,
    uint256 z
  ) internal pure returns (uint256) {
    unchecked {
      uint256 w = mulDivF(x, y, z);
      if (mulMod(x, y, z) > 0) return safeAdd(w, 1);
      return w;
    }
  }

  /**
   * @dev Compute the value of `x * y`
   */
  function mul512(
    uint256 x,
    uint256 y
  ) private pure returns (uint256, uint256) {
    unchecked {
      uint256 p = mulModMax(x, y);
      uint256 q = unsafeMul(x, y);
      if (p >= q) return (p - q, q);
      return (unsafeSub(p, q) - 1, q);
    }
  }

  /**
   * @dev Compute the value of `2 ^ 256 * xh + xl - y`, where `2 ^ 256 * xh + xl >= y`
   */
  function sub512(
    uint256 xh,
    uint256 xl,
    uint256 y
  ) private pure returns (uint256, uint256) {
    unchecked {
      if (xl >= y) return (xh, xl - y);
      return (xh - 1, unsafeSub(xl, y));
    }
  }

  /**
   * @dev Compute the value of `(2 ^ 256 * xh + xl) / pow2n`, where `xl` is divisible by `pow2n`
   */
  function div512(
    uint256 xh,
    uint256 xl,
    uint256 pow2n
  ) private pure returns (uint256) {
    unchecked {
      uint256 pow2nInv = unsafeAdd(unsafeSub(0, pow2n) / pow2n, 1); // `1 << (256 - n)`
      return unsafeMul(xh, pow2nInv) | (xl / pow2n); // `(xh << (256 - n)) | (xl >> n)`
    }
  }

  /**
   * @dev Compute the inverse of `d` modulo `2 ^ 256`, where `d` is congruent to `1` modulo `2`
   */
  function inv256(uint256 d) private pure returns (uint256) {
    unchecked {
      // approximate the root of `f(x) = 1 / x - d` using the newton–raphson convergence method
      uint256 x = 1;
      for (uint256 i = 0; i < 8; ++i)
        x = unsafeMul(x, unsafeSub(2, unsafeMul(x, d))); // `x = x * (2 - x * d) mod 2 ^ 256`
      return x;
    }
  }
}

library FractionMath {
  uint256 internal constant MAX_EXP_BIT_LEN = 40;
  uint256 internal constant MAX_EXP = 2 ** MAX_EXP_BIT_LEN - 1;
  uint256 internal constant MAX_UINT128 = type(uint128).max;

  /**
   * @dev Compute the power of a given ratio
   *
   * @param n The ratio numerator
   * @param d The ratio denominator
   * @param exp The exponentiation value
   * @param fast Opt for performance over accuracy
   *
   * @return The powered ratio numerator
   * @return The powered ratio denominator
   */
  function poweredRatio(
    uint256 n,
    uint256 d,
    uint256 exp,
    bool fast
  ) internal pure returns (uint256, uint256) {
    unchecked {
      require(exp <= MAX_EXP, "exp too large");

      function(uint256, uint256, uint256, uint256)
        pure
        returns (uint256, uint256) safeRatio = fast
          ? mulRatio128
          : productRatio;

      uint256[MAX_EXP_BIT_LEN] memory ns;
      uint256[MAX_EXP_BIT_LEN] memory ds;

      (ns[0], ds[0]) = safeRatio(n, 1, d, 1);
      for (uint256 i = 0; (exp >> i) > 1; ++i) {
        (ns[i + 1], ds[i + 1]) = safeRatio(ns[i], ns[i], ds[i], ds[i]);
      }

      n = 1;
      d = 1;

      for (uint256 i = 0; (exp >> i) > 0; ++i) {
        if (((exp >> i) & 1) > 0) {
          (n, d) = safeRatio(n, ns[i], d, ds[i]);
        }
      }

      return (n, d);
    }
  }

  /**
   * @dev Compute the product of two given ratios
   *
   * @param xn The 1st ratio numerator
   * @param yn The 2nd ratio numerator
   * @param xd The 1st ratio denominator
   * @param yd The 2nd ratio denominator
   *
   * @return The product ratio numerator
   * @return The product ratio denominator
   */
  function productRatio(
    uint256 xn,
    uint256 yn,
    uint256 xd,
    uint256 yd
  ) internal pure returns (uint256, uint256) {
    unchecked {
      uint256 n = IntegralMath.mulDivC(xn, yn, MAX_VAL);
      uint256 d = IntegralMath.mulDivC(xd, yd, MAX_VAL);
      uint256 z = n > d ? n : d;
      if (z > 1) {
        return (
          IntegralMath.mulDivC(xn, yn, z),
          IntegralMath.mulDivC(xd, yd, z)
        );
      }
      return (xn * yn, xd * yd);
    }
  }

  /**
   * @dev Reduce the components of a given ratio
   *
   * @param n The ratio numerator
   * @param d The ratio denominator
   * @param max The maximum desired value
   *
   * @return The reduced ratio numerator
   * @return The reduced ratio denominator
   */
  function reducedRatio(
    uint256 n,
    uint256 d,
    uint256 max
  ) internal pure returns (uint256, uint256) {
    unchecked {
      uint256 scale = ((n > d ? n : d) - 1) / max + 1;
      return (n / scale, d / scale);
    }
  }

  /**
   * @dev Compute a normalized ratio as `scale * n / (n + d)` and `scale * d / (n + d)`
   *
   * @param n The ratio numerator
   * @param d The ratio denominator
   * @param scale The desired scale
   *
   * @return The normalized ratio numerator
   * @return The normalized ratio denominator
   */
  function normalizedRatio(
    uint256 n,
    uint256 d,
    uint256 scale
  ) internal pure returns (uint256, uint256) {
    unchecked {
      if (n <= d) {
        return estimatedRatio(n, d, scale);
      }
      (d, n) = estimatedRatio(d, n, scale);
      return (n, d);
    }
  }

  /**
   * @dev Compute an estimated ratio as `scale * n / (n + d)` and `scale * d / (n + d)`, assuming that `n <= d`
   *
   * @param n The ratio numerator
   * @param d The ratio denominator
   * @param scale The desired scale
   *
   * @return The estimated ratio numerator
   * @return The estimated ratio denominator
   */
  function estimatedRatio(
    uint256 n,
    uint256 d,
    uint256 scale
  ) private pure returns (uint256, uint256) {
    unchecked {
      uint256 x = MAX_VAL / scale;
      if (n > x) {
        // `n * scale` will overflow
        uint256 y = (n - 1) / x + 1;
        n /= y;
        d /= y;
        // `n * scale` will not overflow
      }

      if (n < d) {
        uint256 p = n * scale;
        uint256 q = unsafeAdd(n, d); // `n + d` can overflow
        if (q >= n) {
          // `n + d` did not overflow
          uint256 r = IntegralMath.roundDiv(p, q);
          return (r, scale - r); // `r = n * scale / (n + d) < scale`
        }
        if (p < d - (d - n) / 2) {
          return (0, scale); // `n * scale < (n + d) / 2 < MAX_VAL < n + d`
        }
        return (1, scale - 1); // `(n + d) / 2 < n * scale < MAX_VAL < n + d`
      }
      return (scale / 2, scale - scale / 2); // reflect the fact that initially `n <= d`
    }
  }

  /**
   * @dev Compute the product of two ratios and reduce the components of the result to 128 bits,
   * under the implicit assumption that the components of the product are not larger than 256 bits
   *
   * @param xn The 1st ratio numerator
   * @param yn The 2nd ratio numerator
   * @param xd The 1st ratio denominator
   * @param yd The 2nd ratio denominator
   *
   * @return The product ratio numerator
   * @return The product ratio denominator
   */
  function mulRatio128(
    uint256 xn,
    uint256 yn,
    uint256 xd,
    uint256 yd
  ) private pure returns (uint256, uint256) {
    unchecked {
      return reducedRatio(xn * yn, xd * yd, MAX_UINT128);
    }
  }
}
                

contracts/external/openzeppelin/contracts/utils/cryptography/ECDSA.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}
                

contracts/interfaces/IRWAOracle.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

interface IRWAOracle {
  /// @notice Retrieve RWA price data
  function getPriceData()
    external
    view
    returns (uint256 price, uint256 timestamp);
}
                

contracts/usdy/rusdy/rUSDYFactory.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

// Proxy admin contract used in OZ upgrades plugin
import "contracts/external/openzeppelin/contracts/proxy/ProxyAdmin.sol";
import "contracts/Proxy.sol";
import "contracts/usdy/rusdy/rUSDY.sol";
import "contracts/interfaces/IMulticall.sol";

/**
 * @title rUSDYFactory
 * @author Ondo Finance
 * @notice This contract serves as a Factory for the upgradable rUSDY token contract.
 *         Upon calling `deployrUSDY` the `guardian` address (set in constructor) will
 *         deploy the following:
 *         1) rUSDY - The implementation contract, ERC20 contract with the initializer disabled
 *         2) ProxyAdmin - OZ ProxyAdmin contract, used to upgrade the proxy instance.
 *                         @notice Owner is set to `guardian` address.
 *         3) TransparentUpgradeableProxy - OZ, proxy contract. Admin is set to `address(proxyAdmin)`.
 *                                          `_logic' is set to `address(rUSDY)`.
 * @notice `guardian` address in constructor is a msig.
 */
contract rUSDYFactory is IMulticall {
  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

  address internal immutable guardian;
  rUSDY public rUSDYImplementation;
  ProxyAdmin public rUSDYProxyAdmin;
  TokenProxy public rUSDYProxy;

  constructor(address _guardian) {
    guardian = _guardian;
  }

  /**
   * @dev This function will deploy an upgradable instance of rUSDY
   *
   * @param blocklist     The address of the blocklist
   * @param allowlist     The address of the allowlist
   * @param sanctionsList The address of the sanctions list
   * @param usdy          The address of USDY
   *
   * @return address The address of the proxy contract.
   * @return address The address of the proxyAdmin contract.
   * @return address The address of the implementation contract.
   *
   * @notice 1) Will automatically revoke all deployer roles granted to
   *            address(this).
   *         2) Will grant DEFAULT_ADMIN & PAUSER_ROLE(S) to `guardian`
   *            address specified in constructor.
   *         3) Will transfer ownership of the proxyAdmin to guardian
   *            address.
   *
   */
  function deployrUSDY(
    address blocklist,
    address allowlist,
    address sanctionsList,
    address usdy,
    address oracle
  ) external onlyGuardian returns (address, address, address) {
    rUSDYImplementation = new rUSDY();
    rUSDYProxyAdmin = new ProxyAdmin();
    rUSDYProxy = new TokenProxy(
      address(rUSDYImplementation),
      address(rUSDYProxyAdmin),
      ""
    );
    rUSDY rUSDYProxied = rUSDY(address(rUSDYProxy));
    rUSDYProxied.initialize(
      blocklist,
      allowlist,
      sanctionsList,
      usdy,
      guardian,
      oracle
    );

    rUSDYProxyAdmin.transferOwnership(guardian);
    assert(rUSDYProxyAdmin.owner() == guardian);
    emit rUSDYDeployed(
      address(rUSDYProxy),
      address(rUSDYProxyAdmin),
      address(rUSDYImplementation),
      "Ondo USD",
      "OUSD"
    );
    return (
      address(rUSDYProxy),
      address(rUSDYProxyAdmin),
      address(rUSDYImplementation)
    );
  }

  /**
   * @notice Allows for arbitrary batched calls
   *
   * @dev All external calls made through this function will
   *      msg.sender == contract address
   *
   * @param exCallData Struct consisting of
   *       1) target - contract to call
   *       2) data - data to call target with
   *       3) value - eth value to call target with
   */
  function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
  }

  /**
   * @dev Event emitted when upgradable rUSDY is deployed
   *
   * @param proxy             The address for the proxy contract
   * @param proxyAdmin        The address for the proxy admin contract
   * @param implementation    The address for the implementation contract
   */
  event rUSDYDeployed(
    address proxy,
    address proxyAdmin,
    address implementation,
    string name,
    string ticker
  );

  modifier onlyGuardian() {
    require(msg.sender == guardian, "rUSDYFactory: You are not the Guardian");
    _;
  }
}
                

contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721EnumerableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/IERC721EnumerableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721EnumerableUpgradeable is
  Initializable,
  ERC721Upgradeable,
  IERC721EnumerableUpgradeable
{
  function __ERC721Enumerable_init() internal onlyInitializing {}

  function __ERC721Enumerable_init_unchained() internal onlyInitializing {}

  // Mapping from owner to list of owned token IDs
  mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

  // Mapping from token ID to index of the owner tokens list
  mapping(uint256 => uint256) private _ownedTokensIndex;

  // Array with all token ids, used for enumeration
  uint256[] private _allTokens;

  // Mapping from token id to position in the allTokens array
  mapping(uint256 => uint256) private _allTokensIndex;

  /**
   * @dev See {IERC165-supportsInterface}.
   */
  function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    override(IERC165Upgradeable, ERC721Upgradeable)
    returns (bool)
  {
    return
      interfaceId == type(IERC721EnumerableUpgradeable).interfaceId ||
      super.supportsInterface(interfaceId);
  }

  /**
   * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
   */
  function tokenOfOwnerByIndex(address owner, uint256 index)
    public
    view
    virtual
    override
    returns (uint256)
  {
    require(
      index < ERC721Upgradeable.balanceOf(owner),
      "ERC721Enumerable: owner index out of bounds"
    );
    return _ownedTokens[owner][index];
  }

  /**
   * @dev See {IERC721Enumerable-totalSupply}.
   */
  function totalSupply() public view virtual override returns (uint256) {
    return _allTokens.length;
  }

  /**
   * @dev See {IERC721Enumerable-tokenByIndex}.
   */
  function tokenByIndex(uint256 index)
    public
    view
    virtual
    override
    returns (uint256)
  {
    require(
      index < ERC721EnumerableUpgradeable.totalSupply(),
      "ERC721Enumerable: global index out of bounds"
    );
    return _allTokens[index];
  }

  /**
   * @dev Hook that is called before any token transfer. This includes minting
   * and burning.
   *
   * Calling conditions:
   *
   * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
   * transferred to `to`.
   * - When `from` is zero, `tokenId` will be minted for `to`.
   * - When `to` is zero, ``from``'s `tokenId` will be burned.
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   *
   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 tokenId
  ) internal virtual override {
    super._beforeTokenTransfer(from, to, tokenId);

    if (from == address(0)) {
      _addTokenToAllTokensEnumeration(tokenId);
    } else if (from != to) {
      _removeTokenFromOwnerEnumeration(from, tokenId);
    }
    if (to == address(0)) {
      _removeTokenFromAllTokensEnumeration(tokenId);
    } else if (to != from) {
      _addTokenToOwnerEnumeration(to, tokenId);
    }
  }

  /**
   * @dev Private function to add a token to this extension's ownership-tracking data structures.
   * @param to address representing the new owner of the given token ID
   * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
   */
  function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
    uint256 length = ERC721Upgradeable.balanceOf(to);
    _ownedTokens[to][length] = tokenId;
    _ownedTokensIndex[tokenId] = length;
  }

  /**
   * @dev Private function to add a token to this extension's token tracking data structures.
   * @param tokenId uint256 ID of the token to be added to the tokens list
   */
  function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
    _allTokensIndex[tokenId] = _allTokens.length;
    _allTokens.push(tokenId);
  }

  /**
   * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
   * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
   * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
   * This has O(1) time complexity, but alters the order of the _ownedTokens array.
   * @param from address representing the previous owner of the given token ID
   * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
   */
  function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId)
    private
  {
    // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
    // then delete the last slot (swap and pop).

    uint256 lastTokenIndex = ERC721Upgradeable.balanceOf(from) - 1;
    uint256 tokenIndex = _ownedTokensIndex[tokenId];

    // When the token to delete is the last token, the swap operation is unnecessary
    if (tokenIndex != lastTokenIndex) {
      uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

      _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
      _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
    }

    // This also deletes the contents at the last position of the array
    delete _ownedTokensIndex[tokenId];
    delete _ownedTokens[from][lastTokenIndex];
  }

  /**
   * @dev Private function to remove a token from this extension's token tracking data structures.
   * This has O(1) time complexity, but alters the order of the _allTokens array.
   * @param tokenId uint256 ID of the token to be removed from the tokens list
   */
  function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
    // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
    // then delete the last slot (swap and pop).

    uint256 lastTokenIndex = _allTokens.length - 1;
    uint256 tokenIndex = _allTokensIndex[tokenId];

    // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
    // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
    // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
    uint256 lastTokenId = _allTokens[lastTokenIndex];

    _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
    _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

    // This also deletes the contents at the last position of the array
    delete _allTokensIndex[tokenId];
    _allTokens.pop();
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[46] private __gap;
}
                

contracts/interfaces/IAllowlist.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

interface IAllowlist {
  function addTerm(string calldata term) external;

  function setCurrentTermIndex(uint256 _currentTermIndex) external;

  function setValidTermIndexes(uint256[] calldata indexes) external;

  function isAllowed(address account) external view returns (bool);

  function getCurrentTerm() external view returns (string memory);

  function currentTermIndex() external view returns (uint256);

  function getValidTermIndexes() external view returns (uint256[] memory);

  function addAccountToAllowlist(
    uint256 _currentTermIndex,
    address account,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  function addSelfToAllowlist(uint256 termIndex) external;

  function setAccountStatus(
    address account,
    uint256 termIndex,
    bool status
  ) external;

  /**
   * @notice Event emitted when a term is added
   *
   * @param hashedMessage The hash of the terms string that was added
   * @param termIndex     The index of the term that was added
   */
  event TermAdded(bytes32 hashedMessage, uint256 termIndex);

  /**
   * @notice Event emitted when the current term index is set
   *
   * @param oldIndex The old current term index
   * @param newIndex The new current term index
   */
  event CurrentTermIndexSet(uint256 oldIndex, uint256 newIndex);

  /**
   * @notice Event emitted when the valid term indexes are set
   *
   * @param oldIndexes The old valid term indexes
   * @param newIndexes The new valid term indexes
   */
  event ValidTermIndexesSet(uint256[] oldIndexes, uint256[] newIndexes);

  /**
   * @notice Event emitted when an accoun status is set by an admin
   *
   * @param account   The account whose status was set
   * @param termIndex The term index of the account whose status that was set
   * @param status    The new status of the account
   */
  event AccountStatusSetByAdmin(
    address indexed account,
    uint256 indexed termIndex,
    bool status
  );

  /**
   * @notice Event emitted when an account adds itself added to the allowlist
   *
   * @param account   The account that was added
   * @param termIndex The term index for which the account was added
   */
  event AccountAddedSelf(address indexed account, uint256 indexed termIndex);

  /**
   * @notice Event emitted when an account is added to the allowlist by a signature
   *
   * @param account   The account that was added
   * @param termIndex The term index for which the account was added
   * @param v         The v value of the signature
   * @param r         The r value of the signature
   * @param s         The s value of the signature
   */
  event AccountAddedFromSignature(
    address indexed account,
    uint256 indexed termIndex,
    uint8 v,
    bytes32 r,
    bytes32 s
  );

  /**
   * @notice Event emitted when an account status is set
   *
   * @param account   The account whose status was set
   * @param termIndex The term index of the account whose status was set
   * @param status    The new status of the account
   */
  event AccountStatusSet(
    address indexed account,
    uint256 indexed termIndex,
    bool status
  );

  /// ERRORS ///
  error InvalidTermIndex();
  error InvalidVSignature();
  error AlreadyVerified();
  error InvalidSigner();
}
                

contracts/bridge/DestinationBridge.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/interfaces/IAxelarGateway.sol";
import "contracts/external/axelar/AxelarExecutable.sol";
import "contracts/interfaces/IRWALike.sol";
import "contracts/interfaces/IAllowlist.sol";
import "contracts/external/openzeppelin/contracts/access/Ownable.sol";
import "contracts/external/openzeppelin/contracts/security/Pausable.sol";
import "contracts/bridge/MintRateLimiter.sol";
import "contracts/external/openzeppelin/contracts/token/SafeERC20.sol";

contract DestinationBridge is
  AxelarExecutable,
  MintTimeBasedRateLimiter,
  Ownable,
  Pausable
{
  using SafeERC20 for IERC20;
  /// @notice Token contract bridged by this contract
  IRWALike public immutable TOKEN;

  /// @notice Pointer to AxelarGateway contract
  IAxelarGateway public immutable AXELAR_GATEWAY;

  /// @notice Pointer to USDY allowlist
  IAllowlist public immutable ALLOWLIST;

  // Mapping used to track approvers, approved msg src's and spent nonces
  mapping(address => bool) public approvers;
  mapping(string => bytes32) public chainToApprovedSender;
  mapping(bytes32 => mapping(uint256 => bool)) public isSpentNonce;

  /// @notice Versioning for payload, must match SourceBridge version
  bytes32 public constant VERSION = "1.0";

  /// @notice Mappings used to track transaction and thresholds
  mapping(bytes32 => TxnThreshold) public txnToThresholdSet;
  mapping(string => Threshold[]) public chainToThresholds;
  mapping(bytes32 => Transaction) public txnHashToTransaction;

  constructor(
    address _token,
    address _axelarGateway,
    address _allowlist,
    address _ondoApprover,
    address _owner,
    uint256 _mintLimit,
    uint256 _mintDuration
  )
    AxelarExecutable(_axelarGateway)
    MintTimeBasedRateLimiter(_mintDuration, _mintLimit)
  {
    TOKEN = IRWALike(_token);
    AXELAR_GATEWAY = IAxelarGateway(_axelarGateway);
    ALLOWLIST = IAllowlist(_allowlist);
    approvers[_ondoApprover] = true;
    _transferOwnership(_owner);
  }

  /*//////////////////////////////////////////////////////////////
                         Axelar Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Internal overriden function that is executed when contract is called by Axelar Relayer
   *
   * @param srcChain The string of the source chain eg: arbitrum
   * @param srcAddr  The string of the address of the source contract
   * @param payload  The payload to pass cross chain
   */
  function _execute(
    string calldata srcChain,
    string calldata srcAddr,
    bytes calldata payload
  ) internal override whenNotPaused {
    (bytes32 version, , address srcSender, uint256 amt, uint256 nonce) = abi
      .decode(payload, (bytes32, uint256, address, uint256, uint256));

    if (version != VERSION) {
      revert InvalidVersion();
    }
    if (chainToApprovedSender[srcChain] == bytes32(0)) {
      revert ChainNotSupported();
    }
    if (chainToApprovedSender[srcChain] != keccak256(abi.encode(srcAddr))) {
      revert SourceNotSupported();
    }
    if (isSpentNonce[chainToApprovedSender[srcChain]][nonce]) {
      revert NonceSpent();
    }

    isSpentNonce[chainToApprovedSender[srcChain]][nonce] = true;

    bytes32 txnHash = keccak256(payload);
    txnHashToTransaction[txnHash] = Transaction(srcSender, amt);
    _attachThreshold(amt, txnHash, srcChain);
    _approve(txnHash);
    _mintIfThresholdMet(txnHash);
    emit MessageReceived(txnHash, srcChain, srcSender, amt, nonce);
  }

  /*//////////////////////////////////////////////////////////////
                        Internal Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Internal function used to attach a specific threshold to a given
   *         `txnHash`.
   *
   * @param amount   The amount of the token being bridged
   * @param txnHash  The transaction hash to associate the threshold with
   * @param srcChain The chain corresponding to the chain that the token
   *                 being bridged originated from.
   */
  function _attachThreshold(
    uint256 amount,
    bytes32 txnHash,
    string memory srcChain
  ) internal {
    Threshold[] memory thresholds = chainToThresholds[srcChain];
    for (uint256 i = 0; i < thresholds.length; ++i) {
      Threshold memory t = thresholds[i];
      if (amount <= t.amount) {
        txnToThresholdSet[txnHash] = TxnThreshold(
          t.numberOfApprovalsNeeded,
          new address[](0)
        );
        break;
      }
    }
    if (txnToThresholdSet[txnHash].numberOfApprovalsNeeded == 0) {
      revert NoThresholdMatch();
    }
  }

  /**
   * @notice Internal function used to approve and conditionally mint for a
   *         given txn. Approval is conditional on this approver having not
   *         previously approved the txn
   *
   * @param txnHash The txnHash to approve and conditionally mint to
   */
  function _approve(bytes32 txnHash) internal {
    // Check that the approver has not already approved
    TxnThreshold storage t = txnToThresholdSet[txnHash];
    uint256 approversLength = t.approvers.length;
    if (approversLength > 0) {
      for (uint256 i = 0; i < approversLength; ++i) {
        if (t.approvers[i] == msg.sender) {
          revert AlreadyApproved();
        }
      }
    }
    t.approvers.push(msg.sender);
    emit TransactionApproved(
      txnHash,
      msg.sender,
      approversLength + 1,
      t.numberOfApprovalsNeeded
    );
  }

  /**
   * @notice Internal function to mint tokens for a user if the transaction has
   *         passed the threshold for number of approvers
   *
   * @param txnHash The hash of the payload we wish to mint
   */
  function _mintIfThresholdMet(bytes32 txnHash) internal {
    bool thresholdMet = _checkThresholdMet(txnHash);
    if (thresholdMet) {
      Transaction memory txn = txnHashToTransaction[txnHash];
      _checkAndUpdateInstantMintLimit(txn.amount);
      if (
        address(ALLOWLIST) != address(0) && !ALLOWLIST.isAllowed(txn.sender)
      ) {
        ALLOWLIST.setAccountStatus(
          txn.sender,
          ALLOWLIST.getValidTermIndexes()[0],
          true
        );
      }
      TOKEN.mint(txn.sender, txn.amount);
      // Clear the approval for this bridge payload
      delete txnHashToTransaction[txnHash];
      emit BridgeCompleted(txn.sender, txn.amount);
    }
  }

  /**
   * @notice Internal function used to check if the approval threshold has been
   *         met for a given transaction.
   *
   * @param txnHash The txnHash to check
   *
   * @dev If an approver has been removed, any previous approvals are still valid
   */
  function _checkThresholdMet(bytes32 txnHash) internal view returns (bool) {
    TxnThreshold storage t = txnToThresholdSet[txnHash];
    return t.approvers.length >= t.numberOfApprovalsNeeded;
  }

  /*//////////////////////////////////////////////////////////////
                        Protected Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Protected Function used to approve messages passed to the
   *         Receiver contract. This function is able to be called by any
   *         approver that is added and associated with Ondo.
   *
   * @param txnHash The keccak256 hash of the payload
   */
  function approve(bytes32 txnHash) external {
    if (!approvers[msg.sender]) {
      revert NotApprover();
    }
    _approve(txnHash);
    _mintIfThresholdMet(txnHash);
  }

  /**
   * @notice Admin function to add an ondo Signer or Axelar Relayer
   *
   * @param approver  The address we would like to add
   */
  function addApprover(address approver) external onlyOwner {
    approvers[approver] = true;
    emit ApproverAdded(approver);
  }

  /**
   * @notice Admin function to remove an approver
   *
   * @param approver The address of the approver that we would like to remove
   */
  function removeApprover(address approver) external onlyOwner {
    delete approvers[approver];
    emit ApproverRemoved(approver);
  }

  /**
   * @notice Admin function that will allow bridge calls originating from a given address
   *         on a given chain.
   * @notice This will initialize a nested mapping in which spent nonces from this `srcAddress`
   *         are logged and prevented from being reused
   *
   * @param srcChain            The chain to support
   * @param srcContractAddress  The address of the Ondo Bridge on the source chain
   *
   * @dev srcContractAddress: Is case sensitive and must be the checksum address
   * of the srcBridge contract which is allowed to call into this contract.
   */
  function addChainSupport(
    string calldata srcChain,
    string calldata srcContractAddress
  ) external onlyOwner {
    chainToApprovedSender[srcChain] = keccak256(abi.encode(srcContractAddress));
    emit ChainIdSupported(srcChain, srcContractAddress);
  }

  /**
   * @notice Admin function that will remove support for previously supported chains
   *
   * @param srcChain The source chain whose support is being removed
   */
  function removeChainSupport(string calldata srcChain) external onlyOwner {
    delete chainToApprovedSender[srcChain];
    emit ChainSupportRemoved(srcChain);
  }

  /**
   * @notice Admin function used to clear and set thresholds corresponding to a chain
   *
   * @param srcChain       The chain to set the threshold for
   * @param amounts        The ordered array of values corresponding to
   *                       the amount for a given threshold
   * @param numOfApprovers The ordered array of the number of approvals needed
   *                       for a given threshold
   *
   * @dev This function will remove all previously set thresholds for a given chain
   *      and will thresholds corresponding to the params of this function. Passing
   *      in empty arrays will remove all thresholds for a given chain
   */
  function setThresholds(
    string calldata srcChain,
    uint256[] calldata amounts,
    uint256[] calldata numOfApprovers
  ) external onlyOwner {
    if (amounts.length != numOfApprovers.length) {
      revert ArrayLengthMismatch();
    }
    delete chainToThresholds[srcChain];
    for (uint256 i = 0; i < amounts.length; ++i) {
      if (numOfApprovers[i] == 0) {
        revert NumOfApproversCannotBeZero();
      }
      if (i == 0) {
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      } else {
        if (chainToThresholds[srcChain][i - 1].amount > amounts[i]) {
          revert ThresholdsNotInAscendingOrder();
        }
        chainToThresholds[srcChain].push(
          Threshold(amounts[i], numOfApprovers[i])
        );
      }
    }
    emit ThresholdSet(srcChain, amounts, numOfApprovers);
  }

  /**
   * @notice Admin function used to set the mint limit
   *
   * @param mintLimit The new mint limit
   */
  function setMintLimit(uint256 mintLimit) external onlyOwner {
    _setMintLimit(mintLimit);
  }

  /**
   * @notice Admin function used to set the mint duration
   *
   * @param mintDuration The new mint duration
   */
  function setMintLimitDuration(uint256 mintDuration) external onlyOwner {
    _setMintLimitDuration(mintDuration);
  }

  /**
   * @notice Admin function used to pause the contract
   *
   * @dev Only used for bridge functions
   */
  function pause() external onlyOwner {
    _pause();
  }

  /**
   * @notice Admin function used to unpause the contract
   *
   * @dev Only used for bridge functions
   */
  function unpause() external onlyOwner {
    _unpause();
  }

  /**
   * @notice Admin function used to rescue ERC20 Tokens sent to the contract
   *
   * @param _token The address of the token to rescue
   */
  function rescueTokens(address _token) external onlyOwner {
    uint256 balance = IERC20(_token).balanceOf(address(this));
    IERC20(_token).safeTransfer(owner(), balance);
  }

  /*//////////////////////////////////////////////////////////////
                       Helper Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice External view function used to get the number of approvers for a
   *         given txnHash
   *
   * @param txnHash The hash to get the number of approvers for
   */
  function getNumApproved(bytes32 txnHash) external view returns (uint256) {
    return txnToThresholdSet[txnHash].approvers.length;
  }

  /*//////////////////////////////////////////////////////////////
                      Structs, Events, Errors
  //////////////////////////////////////////////////////////////*/

  struct Threshold {
    uint256 amount;
    uint256 numberOfApprovalsNeeded;
  }

  struct TxnThreshold {
    uint256 numberOfApprovalsNeeded;
    address[] approvers;
  }

  struct Transaction {
    address sender;
    uint256 amount;
  }

  /**
   * @notice event emitted when an address is removed as an approver
   *
   * @param approver The address being removed
   */
  event ApproverRemoved(address approver);

  /**
   * @notice event emitted when an address is added as an approver
   *
   * @param approver  The address to add
   */
  event ApproverAdded(address approver);

  /**
   * @notice event emitted when a new contract is whitelisted as an approved
   *         message passer.
   *
   * @param srcChain        The chain for the approved address
   * @param approvedSource  The address corresponding to the source bridge contract
   */
  event ChainIdSupported(string indexed srcChain, string approvedSource);

  /**
   * @notice event emitted when a threshold has been set
   *
   * @param chain           The chain for which the threshold was set
   * @param amounts         The amount of tokens to reach this threshold
   * @param numOfApprovers  The number of approvals needed
   */
  event ThresholdSet(
    string indexed chain,
    uint256[] amounts,
    uint256[] numOfApprovers
  );

  /**
   * @notice event emitted when the user has been minted their tokens on the dst chain
   *
   * @param user    The recipient address of the newly minted tokens
   * @param amount  The amount of tokens that have been minted
   */
  event BridgeCompleted(address indexed user, uint256 amount);

  /**
   * @notice event emitted when this bridge contract receives a cross chain message
   *
   * @param txnHash   The hash of the payload that has been bridged
   * @param srcChain  The chain from which the message is originating
   * @param srcSender The address of the msg.sender on the source chain
   * @param amt       The amount of tokens being bridged
   * @param nonce     The nonce corresponding to the contract which originated the msg
   */
  event MessageReceived(
    bytes32 indexed txnHash,
    string indexed srcChain,
    address indexed srcSender,
    uint256 amt,
    uint256 nonce
  );

  /**
   * @notice event emitted when a transaction has been approved
   *
   * @param txnHash              The hash of the payload that has been approved
   * @param approver             The address of the approver
   * @param numApprovers         The number of approvers for this transaction
   * @param thresholdRequirement The number of approvals needed for this transaction
   */
  event TransactionApproved(
    bytes32 indexed txnHash,
    address approver,
    uint256 numApprovers,
    uint256 thresholdRequirement
  );

  /**
   * @notice event emitted when support for a chain is removed
   *
   * @param srcChain The chain whose support is being removed
   */
  event ChainSupportRemoved(string indexed srcChain);

  error NotApprover();
  error NoThresholdMatch();
  error ThresholdsNotInAscendingOrder();

  error ChainNotSupported();
  error SourceNotSupported();
  error NonceSpent();
  error AlreadyApproved();
  error InvalidVersion();
  error ArrayLengthMismatch();
  error NumOfApproversCannotBeZero();
}
                

contracts/usdy/usdy/USDYFactory.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

// Proxy admin contract used in OZ upgrades plugin
import "contracts/external/openzeppelin/contracts/proxy/ProxyAdmin.sol";
import "contracts/Proxy.sol";
import "contracts/usdy/usdy/USDY.sol";
import "contracts/interfaces/IMulticall.sol";

/**
 * @title USDYFactory
 * @author Ondo Finance
 * @notice This contract serves as a Factory for the upgradable USDY token contract.
 *         Upon calling `deployUSDY` the `guardian` address (set in constructor) will
 *         deploy the following:
 *         1) USDY - The implementation contract, ERC20 contract with the initializer disabled
 *         2) ProxyAdmin - OZ ProxyAdmin contract, used to upgrade the proxy instance.
 *                         @notice Owner is set to `guardian` address.
 *         3) TransparentUpgradeableProxy - OZ, proxy contract. Admin is set to `address(proxyAdmin)`.
 *                                          `_logic' is set to `address(cash)`.
 *
 *         Following the above mentioned deployment, the address of the CashFactory contract will:
 *         i) Grant the `DEFAULT_ADMIN_ROLE` & PAUSER_ROLE to the `guardian` address
 *         ii) Revoke the `MINTER_ROLE`, `PAUSER_ROLE` & `DEFAULT_ADMIN_ROLE` from address(this).
 *         iii) Transfer ownership of the ProxyAdmin to that of the `guardian` address.
 *
 * @notice `guardian` address in constructor is a msig.
 */
contract USDYFactory is IMulticall {
  struct USDYListData {
    address blocklist;
    address allowlist;
    address sanctionsList;
  }

  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

  address internal immutable guardian;
  USDY public usdyImplementation;
  ProxyAdmin public usdyProxyAdmin;
  TokenProxy public usdyProxy;

  constructor(address _guardian) {
    guardian = _guardian;
  }

  /**
   * @dev This function will deploy an upgradable instance of USDY
   *
   * @param name   The name of the token we want to deploy.
   * @param ticker The ticker for the token we want to deploy.
   *
   * @return address The address of the proxy contract.
   * @return address The address of the proxyAdmin contract.
   * @return address The address of the implementation contract.
   *
   * @notice 1) Will automatically revoke all deployer roles granted to
   *            address(this).
   *         2) Will grant DEFAULT_ADMIN & PAUSER_ROLE(S) to `guardian`
   *            address specified in constructor.
   *         3) Will transfer ownership of the proxyAdmin to guardian
   *            address.
   *
   */
  function deployUSDY(
    string calldata name,
    string calldata ticker,
    USDYListData calldata listData
  ) external onlyGuardian returns (address, address, address) {
    usdyImplementation = new USDY();
    usdyProxyAdmin = new ProxyAdmin();
    usdyProxy = new TokenProxy(
      address(usdyImplementation),
      address(usdyProxyAdmin),
      ""
    );
    USDY usdyProxied = USDY(address(usdyProxy));
    usdyProxied.initialize(
      name,
      ticker,
      listData.blocklist,
      listData.allowlist,
      listData.sanctionsList
    );

    usdyProxied.grantRole(DEFAULT_ADMIN_ROLE, guardian);
    usdyProxied.grantRole(PAUSER_ROLE, guardian);

    usdyProxied.revokeRole(MINTER_ROLE, address(this));
    usdyProxied.revokeRole(PAUSER_ROLE, address(this));
    usdyProxied.revokeRole(DEFAULT_ADMIN_ROLE, address(this));

    usdyProxyAdmin.transferOwnership(guardian);
    assert(usdyProxyAdmin.owner() == guardian);
    emit USDYDeployed(
      address(usdyProxied),
      address(usdyProxyAdmin),
      address(usdyImplementation),
      name,
      ticker,
      listData
    );

    return (
      address(usdyProxied),
      address(usdyProxyAdmin),
      address(usdyImplementation)
    );
  }

  /**
   * @notice Allows for arbitrary batched calls
   *
   * @dev All external calls made through this function will
   *      msg.sender == contract address
   *
   * @param exCallData Struct consisting of
   *       1) target - contract to call
   *       2) data - data to call target with
   *       3) value - eth value to call target with
   */
  function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
  }

  /**
   * @dev Event emitted when upgradable USDY is deployed
   *
   * @param proxy             The address for the proxy contract
   * @param proxyAdmin        The address for the proxy admin contract
   * @param implementation    The address for the implementation contract
   */
  event USDYDeployed(
    address proxy,
    address proxyAdmin,
    address implementation,
    string name,
    string ticker,
    USDYListData listData
  );

  modifier onlyGuardian() {
    require(msg.sender == guardian, "USDYFactory: You are not the Guardian");
    _;
  }
}
                

contracts/external/openzeppelin/contracts/utils/Context.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
  function _msgSender() internal view virtual returns (address) {
    return msg.sender;
  }

  function _msgData() internal view virtual returns (bytes calldata) {
    return msg.data;
  }
}
                

contracts/rwaOracles/IRWAOracleSetter.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

interface IRWAOracleSetter {
  /// @notice Retrieve RWA price data
  function getPriceData()
    external
    view
    returns (uint256 price, uint256 timestamp);

  /// @notice Set the RWA price
  function setPrice(int256 price) external;
}
                

contracts/usdy/rusdy/rUSDY.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/IERC20MetadataUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "contracts/usdy/blocklist/BlocklistClientUpgradeable.sol";
import "contracts/usdy/allowlist/AllowlistClientUpgradeable.sol";
import "contracts/sanctions/SanctionsListClientUpgradeable.sol";
import "contracts/interfaces/IUSDY.sol";
import "contracts/rwaOracles/IRWADynamicOracle.sol";

/**
 * @title Interest-bearing ERC20-like token for rUSDY.
 *
 * rUSDY balances are dynamic and represent the holder's share of the underlying USDY
 * controlled by the protocol. To calculate each account's balance, we do
 *
 *   shares[account] * usdyPrice
 *
 * For example, assume that we have:
 *
 *   usdyPrice = 1.05
 *   sharesOf(user1) -> 100
 *   sharesOf(user2) -> 400
 *
 * Therefore:
 *
 *   balanceOf(user1) -> 105 tokens which corresponds 105 rUSDY
 *   balanceOf(user2) -> 420 tokens which corresponds 420 rUSDY
 *
 * Since balances of all token holders change when the price of USDY changes, this
 * token cannot fully implement ERC20 standard: it only emits `Transfer` events
 * upon explicit transfer between holders. In contrast, when total amount of pooled
 * Cash increases, no `Transfer` events are generated: doing so would require emitting
 * an event for each token holder and thus running an unbounded loop.
 *
 */

contract rUSDY is
  Initializable,
  ContextUpgradeable,
  PausableUpgradeable,
  AccessControlEnumerableUpgradeable,
  BlocklistClientUpgradeable,
  AllowlistClientUpgradeable,
  SanctionsListClientUpgradeable,
  IERC20Upgradeable,
  IERC20MetadataUpgradeable
{
  /**
   * @dev rUSDY balances are dynamic and are calculated based on the accounts' shares (USDY)
   * and the the price of USDY. Account shares aren't
   * normalized, so the contract also stores the sum of all shares to calculate
   * each account's token balance which equals to:
   *
   *   shares[account] * usdyPrice
   */
  mapping(address => uint256) private shares;

  /// @dev Allowances are nominated in tokens, not token shares.
  mapping(address => mapping(address => uint256)) private allowances;

  // Total shares in existence
  uint256 private totalShares;

  // Address of the oracle that updates `usdyPrice`
  IRWADynamicOracle public oracle;

  // Address of the USDY token
  IUSDY public usdy;

  // Used to scale up usdy amount -> shares
  uint256 public constant BPS_DENOMINATOR = 10_000;

  // Error when redeeming shares < `BPS_DENOMINATOR`
  error UnwrapTooSmall();

  /// @dev Role based access control roles
  bytes32 public constant USDY_MANAGER_ROLE = keccak256("ADMIN_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
  bytes32 public constant LIST_CONFIGURER_ROLE =
    keccak256("LIST_CONFIGURER_ROLE");

  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor() {
    _disableInitializers();
  }

  function initialize(
    address blocklist,
    address allowlist,
    address sanctionsList,
    address _usdy,
    address guardian,
    address _oracle
  ) public virtual initializer {
    __rUSDY_init(blocklist, allowlist, sanctionsList, _usdy, guardian, _oracle);
  }

  function __rUSDY_init(
    address blocklist,
    address allowlist,
    address sanctionsList,
    address _usdy,
    address guardian,
    address _oracle
  ) internal onlyInitializing {
    __BlocklistClientInitializable_init(blocklist);
    __AllowlistClientInitializable_init(allowlist);
    __SanctionsListClientInitializable_init(sanctionsList);
    __rUSDY_init_unchained(_usdy, guardian, _oracle);
  }

  function __rUSDY_init_unchained(
    address _usdy,
    address guardian,
    address _oracle
  ) internal onlyInitializing {
    usdy = IUSDY(_usdy);
    oracle = IRWADynamicOracle(_oracle);
    _grantRole(DEFAULT_ADMIN_ROLE, guardian);
    _grantRole(USDY_MANAGER_ROLE, guardian);
    _grantRole(PAUSER_ROLE, guardian);
    _grantRole(BURNER_ROLE, guardian);
    _grantRole(LIST_CONFIGURER_ROLE, guardian);
  }

  /**
   * @notice An executed shares transfer from `sender` to `recipient`.
   *
   * @dev emitted in pair with an ERC20-defined `Transfer` event.
   */
  event TransferShares(
    address indexed from,
    address indexed to,
    uint256 sharesValue
  );

  /**
   * @notice An executed `burnShares` request
   *
   * @dev Reports simultaneously burnt shares amount
   * and corresponding rUSDY amount.
   * The shares amount is calculated twice: before and after the burning incurred rebase.
   *
   * @param account holder of the burnt shares
   * @param preRebaseTokenAmount amount of rUSDY the burnt shares (USDY) corresponded to before the burn
   * @param postRebaseTokenAmount amount of rUSDY the burnt shares (USDY) corresponded to after the burn
   * @param sharesAmount amount of burnt shares
   */
  event SharesBurnt(
    address indexed account,
    uint256 preRebaseTokenAmount,
    uint256 postRebaseTokenAmount,
    uint256 sharesAmount
  );

  /**
   * @notice An executed `burnShares` request
   *
   * @dev Reports simultaneously burnt shares amount
   * and corresponding rUSDY amount.
   * The rUSDY amount is calculated twice: before and after the burning incurred rebase.
   *
   * @param account holder of the burnt shares
   * @param tokensBurnt amount of burnt tokens
   */
  event TokensBurnt(address indexed account, uint256 tokensBurnt);

  /**
   * @return the name of the token.
   */
  function name() public pure returns (string memory) {
    return "Ondo USD";
  }

  /**
   * @return the symbol of the token, usually a shorter version of the
   * name.
   */
  function symbol() public pure returns (string memory) {
    return "OUSD";
  }

  /**
   * @return the number of decimals for getting user representation of a token amount.
   */
  function decimals() public pure returns (uint8) {
    return 18;
  }

  /**
   * @return the amount of tokens in existence.
   */
  function totalSupply() public view returns (uint256) {
    return (totalShares * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR);
  }

  /**
   * @return the amount of tokens owned by the `_account`.
   *
   * @dev Balances are dynamic and equal the `_account`'s USDY shares multiplied
   *      by the price of USDY
   */
  function balanceOf(address _account) public view returns (uint256) {
    return (_sharesOf(_account) * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR);
  }

  /**
   * @notice Moves `_amount` tokens from the caller's account to the `_recipient` account.
   *
   * @return a boolean value indicating whether the operation succeeded.
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the caller must have a balance of at least `_amount`.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function transfer(address _recipient, uint256 _amount) public returns (bool) {
    _transfer(msg.sender, _recipient, _amount);
    return true;
  }

  /**
   * @return the remaining number of tokens that `_spender` is allowed to spend
   * on behalf of `_owner` through `transferFrom`. This is zero by default.
   *
   * @dev This value changes when `approve` or `transferFrom` is called.
   */
  function allowance(
    address _owner,
    address _spender
  ) public view returns (uint256) {
    return allowances[_owner][_spender];
  }

  /**
   * @notice Sets `_amount` as the allowance of `_spender` over the caller's tokens.
   *
   * @return a boolean value indicating whether the operation succeeded.
   * Emits an `Approval` event.
   *
   * Requirements:
   *
   * - `_spender` cannot be the zero address.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function approve(address _spender, uint256 _amount) public returns (bool) {
    _approve(msg.sender, _spender, _amount);
    return true;
  }

  /**
   * @notice Moves `_amount` tokens from `_sender` to `_recipient` using the
   * allowance mechanism. `_amount` is then deducted from the caller's
   * allowance.
   *
   * @return a boolean value indicating whether the operation succeeded.
   *
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_sender` and `_recipient` cannot be the zero addresses.
   * - `_sender` must have a balance of at least `_amount`.
   * - the caller must have allowance for `_sender`'s tokens of at least `_amount`.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function transferFrom(
    address _sender,
    address _recipient,
    uint256 _amount
  ) public returns (bool) {
    uint256 currentAllowance = allowances[_sender][msg.sender];
    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");

    _transfer(_sender, _recipient, _amount);
    _approve(_sender, msg.sender, currentAllowance - _amount);
    return true;
  }

  /**
   * @notice Atomically increases the allowance granted to `_spender` by the caller by `_addedValue`.
   *
   * This is an alternative to `approve` that can be used as a mitigation for
   * problems described in:
   * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_spender` cannot be the the zero address.
   * - the contract must not be paused.
   */
  function increaseAllowance(
    address _spender,
    uint256 _addedValue
  ) public returns (bool) {
    _approve(
      msg.sender,
      _spender,
      allowances[msg.sender][_spender] + _addedValue
    );
    return true;
  }

  /**
   * @notice Atomically decreases the allowance granted to `_spender` by the caller by `_subtractedValue`.
   *
   * This is an alternative to `approve` that can be used as a mitigation for
   * problems described in:
   * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_spender` cannot be the zero address.
   * - `_spender` must have allowance for the caller of at least `_subtractedValue`.
   * - the contract must not be paused.
   */
  function decreaseAllowance(
    address _spender,
    uint256 _subtractedValue
  ) public returns (bool) {
    uint256 currentAllowance = allowances[msg.sender][_spender];
    require(
      currentAllowance >= _subtractedValue,
      "DECREASED_ALLOWANCE_BELOW_ZERO"
    );
    _approve(msg.sender, _spender, currentAllowance - _subtractedValue);
    return true;
  }

  /**
   * @return the total amount of shares in existence.
   *
   * @dev The sum of all accounts' shares can be an arbitrary number, therefore
   * it is necessary to store it in order to calculate each account's relative share.
   */
  function getTotalShares() public view returns (uint256) {
    return totalShares;
  }

  /**
   * @return the amount of shares owned by `_account`.
   *
   * @dev This is the equivalent to the amount of USDY wrapped by `_account`.
   */
  function sharesOf(address _account) public view returns (uint256) {
    return _sharesOf(_account);
  }

  /**
   * @return the amount of USDY that corresponds to `_rUSDYAmount` of rUSDY
   */
  function getSharesByRUSDY(
    uint256 _rUSDYAmount
  ) public view returns (uint256) {
    return (_rUSDYAmount * 1e18 * BPS_DENOMINATOR) / oracle.getPrice();
  }

  /**
   * @return the amount of rUSDY that corresponds to `_shares` of usdy.
   */
  function getRUSDYByShares(uint256 _shares) public view returns (uint256) {
    return (_shares * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR);
  }

  /**
   * @notice Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account.
   *
   * @return amount of transferred tokens.
   * Emits a `TransferShares` event.
   * Emits a `Transfer` event.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the caller must have at least `_sharesAmount` shares.
   * - the contract must not be paused.
   *
   * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
   */
  function transferShares(
    address _recipient,
    uint256 _sharesAmount
  ) public returns (uint256) {
    _transferShares(msg.sender, _recipient, _sharesAmount);
    emit TransferShares(msg.sender, _recipient, _sharesAmount);
    uint256 tokensAmount = getRUSDYByShares(_sharesAmount);
    emit Transfer(msg.sender, _recipient, tokensAmount);
    return tokensAmount;
  }

  /**
   * @notice Function called by users to wrap their USDY tokens
   *
   * @param _USDYAmount The amount of USDY Tokens to wrap
   *
   * @dev Sanctions, Blocklist, and Allowlist checks implicit in USDY Transfer
   */
  function wrap(uint256 _USDYAmount) external whenNotPaused {
    require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
    uint256 usdySharesAmount = _USDYAmount * BPS_DENOMINATOR;
    _mintShares(msg.sender, usdySharesAmount);
    usdy.transferFrom(msg.sender, address(this), _USDYAmount);
    emit Transfer(address(0), msg.sender, getRUSDYByShares(usdySharesAmount));
    emit TransferShares(address(0), msg.sender, usdySharesAmount);
  }

  /**
   * @notice Function called by users to unwrap their rUSDY tokens
   *
   * @param _rUSDYAmount The amount of rUSDY to unwrap
   *
   * @dev Sanctions, Blocklist, and Allowlist checks implicit in USDY Transfer
   */
  function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
    require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");
    uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
    if (usdyAmount < BPS_DENOMINATOR) revert UnwrapTooSmall();
    _burnShares(msg.sender, usdyAmount);
    usdy.transfer(msg.sender, usdyAmount / BPS_DENOMINATOR);
    emit Transfer(msg.sender, address(0), _rUSDYAmount);
    emit TokensBurnt(msg.sender, _rUSDYAmount);
  }

  /**
   * @notice Moves `_amount` tokens from `_sender` to `_recipient`.
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   */
  function _transfer(
    address _sender,
    address _recipient,
    uint256 _amount
  ) internal {
    uint256 _sharesToTransfer = getSharesByRUSDY(_amount);
    _transferShares(_sender, _recipient, _sharesToTransfer);
    emit Transfer(_sender, _recipient, _amount);
    emit TransferShares(_sender, _recipient, _sharesToTransfer);
  }

  /**
   * @notice Sets `_amount` as the allowance of `_spender` over the `_owner` s tokens.
   *
   * Emits an `Approval` event.
   *
   * Requirements:
   *
   * - `_owner` cannot be the zero address.
   * - `_spender` cannot be the zero address.
   * - the contract must not be paused.
   */
  function _approve(
    address _owner,
    address _spender,
    uint256 _amount
  ) internal whenNotPaused {
    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

    allowances[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
  }

  /**
   * @return the amount of shares owned by `_account`.
   */
  function _sharesOf(address _account) internal view returns (uint256) {
    return shares[_account];
  }

  /**
   * @notice Moves `_sharesAmount` shares from `_sender` to `_recipient`.
   *
   * Requirements:
   *
   * - `_sender` cannot be the zero address.
   * - `_recipient` cannot be the zero address.
   * - `_sender` must hold at least `_sharesAmount` shares.
   * - the contract must not be paused.
   */
  function _transferShares(
    address _sender,
    address _recipient,
    uint256 _sharesAmount
  ) internal whenNotPaused {
    require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
    require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_sender, _recipient, _sharesAmount);

    uint256 currentSenderShares = shares[_sender];
    require(
      _sharesAmount <= currentSenderShares,
      "TRANSFER_AMOUNT_EXCEEDS_BALANCE"
    );

    shares[_sender] = currentSenderShares - _sharesAmount;
    shares[_recipient] = shares[_recipient] + _sharesAmount;
  }

  /**
   * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
   * @dev This doesn't increase the token total supply.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the contract must not be paused.
   */
  function _mintShares(
    address _recipient,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_recipient != address(0), "MINT_TO_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(address(0), _recipient, _sharesAmount);

    totalShares += _sharesAmount;

    shares[_recipient] = shares[_recipient] + _sharesAmount;

    return totalShares;

    // Notice: we're not emitting a Transfer event from the zero address here since shares mint
    // works by taking the amount of tokens corresponding to the minted shares from all other
    // token holders, proportionally to their share. The total supply of the token doesn't change
    // as the result. This is equivalent to performing a send from each other token holder's
    // address to `address`, but we cannot reflect this as it would require sending an unbounded
    // number of events.
  }

  /**
   * @notice Destroys `_sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
   * @dev This doesn't decrease the token total supply.
   *
   * Requirements:
   *
   * - `_account` cannot be the zero address.
   * - `_account` must hold at least `_sharesAmount` shares.
   * - the contract must not be paused.
   */
  function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_account, address(0), _sharesAmount);

    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");

    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

    emit SharesBurnt(
      _account,
      preRebaseTokenAmount,
      postRebaseTokenAmount,
      _sharesAmount
    );

    return totalShares;

    // Notice: we're not emitting a Transfer event to the zero address here since shares burn
    // works by redistributing the amount of tokens corresponding to the burned shares between
    // all other token holders. The total supply of the token doesn't change as the result.
    // This is equivalent to performing a send from `address` to each other token holder address,
    // but we cannot reflect this as it would require sending an unbounded number of events.

    // We're emitting `SharesBurnt` event to provide an explicit rebase log record nonetheless.
  }

  /**
   * @dev Hook that is called before any transfer of tokens. This includes
   * minting and burning.
   *
   * Calling conditions:
   *
   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
   * will be transferred to `to`.
   * - when `from` is zero, `amount` tokens will be minted for `to`.
   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
   * - `from` and `to` are never both zero.
   *
   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256
  ) internal view {
    // Check constraints when `transferFrom` is called to facliitate
    // a transfer between two parties that are not `from` or `to`.
    if (from != msg.sender && to != msg.sender) {
      require(!_isBlocked(msg.sender), "rUSDY: 'sender' address blocked");
      require(!_isSanctioned(msg.sender), "rUSDY: 'sender' address sanctioned");
      require(
        _isAllowed(msg.sender),
        "rUSDY: 'sender' address not on allowlist"
      );
    }

    if (from != address(0)) {
      // If not minting
      require(!_isBlocked(from), "rUSDY: 'from' address blocked");
      require(!_isSanctioned(from), "rUSDY: 'from' address sanctioned");
      require(_isAllowed(from), "rUSDY: 'from' address not on allowlist");
    }

    if (to != address(0)) {
      // If not burning
      require(!_isBlocked(to), "rUSDY: 'to' address blocked");
      require(!_isSanctioned(to), "rUSDY: 'to' address sanctioned");
      require(_isAllowed(to), "rUSDY: 'to' address not on allowlist");
    }
  }

  /**
   * @notice Sets the Oracle address
   * @dev The new oracle must comply with the `IPricerReader` interface
   * @param _oracle Address of the new oracle
   */
  function setOracle(address _oracle) external onlyRole(USDY_MANAGER_ROLE) {
    oracle = IRWADynamicOracle(_oracle);
  }

  /**
   * @notice Admin burn function to burn rUSDY tokens from any account
   * @param _account The account to burn tokens from
   * @param _amount  The amount of rUSDY tokens to burn
   * @dev Transfers burned shares (USDY) to `msg.sender`
   */
  function burn(
    address _account,
    uint256 _amount
  ) external onlyRole(BURNER_ROLE) {
    uint256 sharesAmount = getSharesByRUSDY(_amount);

    _burnShares(_account, sharesAmount);

    usdy.transfer(msg.sender, sharesAmount / BPS_DENOMINATOR);

    emit TokensBurnt(_account, _amount);
  }

  function pause() external onlyRole(PAUSER_ROLE) {
    _pause();
  }

  function unpause() external onlyRole(USDY_MANAGER_ROLE) {
    _unpause();
  }

  /**
   * @notice Sets the blocklist address
   *
   * @param blocklist New blocklist address
   */
  function setBlocklist(
    address blocklist
  ) external override onlyRole(LIST_CONFIGURER_ROLE) {
    _setBlocklist(blocklist);
  }

  /**
   * @notice Sets the allowlist address
   *
   * @param allowlist New allowlist address
   */
  function setAllowlist(
    address allowlist
  ) external override onlyRole(LIST_CONFIGURER_ROLE) {
    _setAllowlist(allowlist);
  }

  /**
   * @notice Sets the sanctions list address
   *
   * @param sanctionsList New sanctions list address
   */
  function setSanctionsList(
    address sanctionsList
  ) external override onlyRole(LIST_CONFIGURER_ROLE) {
    _setSanctionsList(sanctionsList);
  }
}
                

contracts/interfaces/IAxelarGasService.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// This should be owned by the microservice that is paying for gas.
interface IAxelarGasService {
  // This is called on the source chain before calling the gateway to execute a remote contract.
  function payNativeGasForContractCall(
    address sender,
    string calldata destinationChain,
    string calldata destinationAddress,
    bytes calldata payload,
    address refundAddress
  ) external payable;
}
                

contracts/external/openzeppelin/contracts/utils/Counters.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
  struct Counter {
    // This variable should never be directly accessed by users of the library: interactions must be restricted to
    // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
    // this feature: see https://github.com/ethereum/solidity/issues/4637
    uint256 _value; // default: 0
  }

  function current(Counter storage counter) internal view returns (uint256) {
    return counter._value;
  }

  function increment(Counter storage counter) internal {
    unchecked {
      counter._value += 1;
    }
  }

  function decrement(Counter storage counter) internal {
    uint256 value = counter._value;
    require(value > 0, "Counter: decrement overflow");
    unchecked {
      counter._value = value - 1;
    }
  }

  function reset(Counter storage counter) internal {
    counter._value = 0;
  }
}
                

contracts/interfaces/IAllowlistClient.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/interfaces/IAllowlist.sol";

/**
 * @title IAllowlistClient
 * @author Ondo Finance
 * @notice The client interface for Ondo's Allowlist contract.
 */
interface IAllowlistClient {
  /// @notice Returns reference to the allowlist that this client queries
  function allowlist() external view returns (IAllowlist);

  /// @notice Sets the allowlist contract reference
  function setAllowlist(address allowlist) external;

  /**
   * @dev Event for when the allowlist reference is set
   *
   * @param oldAllowlist The old allowlist
   * @param newAllowlist The new allowlist
   */
  event AllowlistSet(address oldAllowlist, address newAllowlist);

  /// @notice Error for when caller attempts to set the allowlist reference
  ///         to the zero address.
  error AllowlistZeroAddress();
}
                

contracts/external/openzeppelin/contracts/access/Ownable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts/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.
 *
 * By default, the owner account will be the one that deploys the contract. 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;

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

  /**
   * @dev Initializes the contract setting the deployer as the initial owner.
   */
  constructor() {
    _transferOwnership(_msgSender());
  }

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

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(owner() == _msgSender(), "Ownable: caller is not the owner");
    _;
  }

  /**
   * @dev Leaves the contract without owner. It will not be possible to call
   * `onlyOwner` functions anymore. Can only be called by the current owner.
   *
   * NOTE: Renouncing ownership will leave the contract without an owner,
   * thereby removing 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 {
    require(newOwner != address(0), "Ownable: new owner is the zero address");
    _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);
  }
}
                

contracts/usdy/allowlist/AllowlistFactory.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

// Proxy admin contract used in OZ upgrades plugin
import "contracts/external/openzeppelin/contracts/proxy/ProxyAdmin.sol";
import "contracts/usdy/allowlist/AllowlistProxy.sol";
import "contracts/usdy/allowlist/AllowlistUpgradeable.sol";
import "contracts/interfaces/IMulticall.sol";

/**
 * @title AllowlistFactory
 * @author Ondo Finance
 * @notice This contract serves as a Factory for the upgradable AllowlistUpgradeable contract.
 *         Upon calling `deployAllowlist` the `guardian` address (set in constructor) will
 *         deploy the following:
 *         1) AllowlistUpgradeable - The implementation contract.
 *         2) ProxyAdmin - OZ ProxyAdmin contract, used to upgrade the proxy instance.
 *                         @notice Owner is set to `guardian` address.
 *         3) TransparentUpgradeableProxy - OZ, proxy contract. Admin is set to `address(proxyAdmin)`.
 *                                          `_logic' is set to `address(cash)`.
 *
 * @notice `guardian` address in constructor is a msig.
 */
contract AllowlistFactory is IMulticall {
  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

  address internal immutable guardian;
  AllowlistUpgradeable public allowlistImplementation;
  ProxyAdmin public allowlistProxyAdmin;
  AllowlistProxy public allowlistProxy;

  constructor(address _guardian) {
    guardian = _guardian;
  }

  /**
   * @dev This function will deploy an upgradable instance of AllowlistUpgradeable
   *
   * @param admin  The admin account for the AllowlistUpgradeable contract.
   * @param setter The setter account for the AllowlistUpgradeable contract.
   *
   * @return address The address of the proxy contract.
   * @return address The address of the proxyAdmin contract.
   * @return address The address of the implementation contract.
   *
   */
  function deployAllowlist(
    address admin,
    address setter
  ) external onlyGuardian returns (address, address, address) {
    allowlistImplementation = new AllowlistUpgradeable();

    allowlistProxyAdmin = new ProxyAdmin();
    allowlistProxy = new AllowlistProxy(
      address(allowlistImplementation),
      address(allowlistProxyAdmin),
      ""
    );
    AllowlistUpgradeable allowlistProxied = AllowlistUpgradeable(
      address(allowlistProxy)
    );

    allowlistProxied.initialize(admin, setter);

    allowlistProxyAdmin.transferOwnership(guardian);
    assert(allowlistProxyAdmin.owner() == guardian);
    emit AllowlistDeployed(
      address(allowlistProxied),
      address(allowlistProxyAdmin),
      address(allowlistImplementation)
    );
    return (
      address(allowlistProxied),
      address(allowlistProxyAdmin),
      address(allowlistImplementation)
    );
  }

  /**
   * @notice Allows for arbitrary batched calls
   *
   * @dev All external calls made through this function will
   *      msg.sender == contract address
   *
   * @param exCallData Struct consisting of
   *       1) target - contract to call
   *       2) data - data to call target with
   *       3) value - eth value to call target with
   */
  function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
  }

  /**
   * @dev Event emitted when upgradable AllowlistUpgradeable is deployed
   *
   * @param proxy             The address for the proxy contract
   * @param proxyAdmin        The address for the proxy admin contract
   * @param implementation    The address for the implementation contract
   */
  event AllowlistDeployed(
    address proxy,
    address proxyAdmin,
    address implementation
  );

  modifier onlyGuardian() {
    require(
      msg.sender == guardian,
      "AllowlistFactory: You are not the Guardian"
    );
    _;
  }
}
                

contracts/external/openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts/proxy/ERC1967Proxy.sol";

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 *
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 *
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 *
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 *
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 */
contract TransparentUpgradeableProxy is ERC1967Proxy {
  /**
   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
   */
  constructor(
    address _logic,
    address admin_,
    bytes memory _data
  ) payable ERC1967Proxy(_logic, _data) {
    assert(
      _ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)
    );
    _changeAdmin(admin_);
  }

  /**
   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
   */
  modifier ifAdmin() {
    if (msg.sender == _getAdmin()) {
      _;
    } else {
      _fallback();
    }
  }

  /**
   * @dev Returns the current admin.
   *
   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
   *
   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
   */
  function admin() external ifAdmin returns (address admin_) {
    admin_ = _getAdmin();
  }

  /**
   * @dev Returns the current implementation.
   *
   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
   *
   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
   */
  function implementation() external ifAdmin returns (address implementation_) {
    implementation_ = _implementation();
  }

  /**
   * @dev Changes the admin of the proxy.
   *
   * Emits an {AdminChanged} event.
   *
   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
   */
  function changeAdmin(address newAdmin) external virtual ifAdmin {
    _changeAdmin(newAdmin);
  }

  /**
   * @dev Upgrade the implementation of the proxy.
   *
   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
   */
  function upgradeTo(address newImplementation) external ifAdmin {
    _upgradeToAndCall(newImplementation, bytes(""), false);
  }

  /**
   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
   * proxied contract.
   *
   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
   */
  function upgradeToAndCall(address newImplementation, bytes calldata data)
    external
    payable
    ifAdmin
  {
    _upgradeToAndCall(newImplementation, data, true);
  }

  /**
   * @dev Returns the current admin.
   */
  function _admin() internal view virtual returns (address) {
    return _getAdmin();
  }

  /**
   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
   */
  function _beforeFallback() internal virtual override {
    require(
      msg.sender != _getAdmin(),
      "TransparentUpgradeableProxy: admin cannot fallback to proxy target"
    );
    super._beforeFallback();
  }
}
                

contracts/bridge/SourceBridge.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/interfaces/IAxelarGateway.sol";
import "contracts/interfaces/IAxelarGasService.sol";
import "contracts/interfaces/IMulticall.sol";
import "contracts/interfaces/IRWALike.sol";
import {AddressToString} from "contracts/external/axelar/StringAddressUtils.sol";
import "contracts/external/openzeppelin/contracts/access/Ownable.sol";
import "contracts/external/openzeppelin/contracts/security/Pausable.sol";

contract SourceBridge is Ownable, Pausable, IMulticall {
  /// @notice Mapping from destination chain to bridge address on that chain
  /// @dev Axelar uses the string representation of addresses, hence we store
  ///      the address as a string
  mapping(string => string) public destChainToContractAddr;

  /// @notice Token contract bridged by this contract
  IRWALike public immutable TOKEN;

  /// @notice Pointer  to AxelarGateway contract
  IAxelarGateway public immutable AXELAR_GATEWAY;

  /// @notice Pointer to AxelarGasService contract
  IAxelarGasService public immutable GAS_RECEIVER;

  /// @notice Versioning for payload
  bytes32 public constant VERSION = "1.0";

  /// @notice Monotonically increasing nonce for each transaction
  uint256 public nonce;

  /// @notice src Chain Id, used to salt txnHash on dst Chain
  uint256 public immutable CHAIN_ID;

  /**
   * @notice Constructor
   *
   * @param _token The address of the token bridged
   * @param _axelarGateway The address of the AxelarGateway contract
   * @param _gasService The address of the AxelarGasService contract
   * @param owner The owner of the contract
   */
  constructor(
    address _token,
    address _axelarGateway,
    address _gasService,
    address owner
  ) {
    TOKEN = IRWALike(_token);
    AXELAR_GATEWAY = IAxelarGateway(_axelarGateway);
    GAS_RECEIVER = IAxelarGasService(_gasService);
    _transferOwnership(owner);
    CHAIN_ID = block.chainid;
  }

  /**
   * @notice Burns tokens on the source Chain and calls AxelarGateway contract
   *         to mint tokens on the destination chain
   *
   * @param amount The amount of tokens to burn
   * @param destinationChain The destination chain to mint tokens on
   *
   * @dev The amount of tokens to mint is the same as the amount burned
   * @dev User must approve `amount` tokens to this contract before bridging
   */
  function burnAndCallAxelar(
    uint256 amount,
    string calldata destinationChain
  ) external payable whenNotPaused {
    // Ensure that some gas is sent
    if (msg.value == 0) {
      revert GasFeeTooLow();
    }

    // Ensure that amount bridged is not zero
    if (amount == 0) {
      revert CannotBridgeZero();
    }

    // Check destinationChain is correct
    string memory destContract = destChainToContractAddr[destinationChain];

    if (bytes(destContract).length == 0) {
      revert DestinationNotSupported();
    }

    // burn amount
    TOKEN.burnFrom(msg.sender, amount);

    bytes memory payload = abi.encode(
      VERSION,
      CHAIN_ID,
      msg.sender,
      amount,
      nonce++
    );

    _payGasAndCallContract(destinationChain, destContract, payload);
    emit BridgeInitiated(msg.sender, nonce - 1, CHAIN_ID, VERSION, amount);
  }

  /**
   * @notice Helper function that pays gas and calls the AxelarGateway contract
   *
   * @param destinationChain The destination chain to mint tokens on
   * @param destContract The contract address on the destination chain
   * @param payload The payload to send to the AxelarGateway contract
   */
  function _payGasAndCallContract(
    string calldata destinationChain,
    string memory destContract,
    bytes memory payload
  ) private {
    GAS_RECEIVER.payNativeGasForContractCall{value: msg.value}(
      address(this),
      destinationChain,
      destContract,
      payload,
      msg.sender
    );

    // Send all information to AxelarGateway contract.
    AXELAR_GATEWAY.callContract(destinationChain, destContract, payload);
  }

  /*//////////////////////////////////////////////////////////////
                          Admin Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Sets the destination chain to contract address mapping
   *
   * @param destinationChain The destination chain
   * @param contractAddress The contract address on the destination chain
   *
   * @dev Chain name must come from list of supported chains
   *      at https://docs.axelar.dev/dev/reference/mainnet-chain-names
   */
  function setDestinationChainContractAddress(
    string calldata destinationChain,
    address contractAddress
  ) external onlyOwner {
    destChainToContractAddr[destinationChain] = AddressToString.toString(
      contractAddress
    );
    emit DestinationChainContractAddressSet(destinationChain, contractAddress);
  }

  /**
   * @notice Deletes an entry w/n the destination chain to contract address
   *         mapping
   *
   * @param destinationChain The destination chain to disallow
   */
  function removeDestinationChainContractAddress(
    string calldata destinationChain
  ) external onlyOwner {
    delete destChainToContractAddr[destinationChain];
    emit ChainSupportRemoved(destinationChain);
  }

  /**
   * @notice Admin function to pause the contract
   *
   * @dev Only used for bridge functions
   */
  function pause() external onlyOwner {
    _pause();
  }

  /**
   * @notice Admin function to unpause the contract
   *
   * @dev Only used for bridge functions
   */
  function unpause() external onlyOwner {
    _unpause();
  }

  /**
   * @notice Allows for arbitrary batched calls
   *
   * @dev All external calls made through this function will
   *      msg.sender == contract address
   *
   * @param exCallData Struct consisting of
   *       1) target - contract to call
   *       2) data - data to call target with
   *       3) value - eth value to call target with
   */
  function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyOwner returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
  }

  /*//////////////////////////////////////////////////////////////
                           Events & Errors
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Event emitted when the destination chain to contract address mapping is set
   *
   * @param destinationChain The destination chain
   * @param contractAddress The contract address on the destination chain
   */
  event DestinationChainContractAddressSet(
    string indexed destinationChain,
    address contractAddress
  );

  /**
   * @notice Event emitted when a message is passed to an Axelar gateway
   *
   * @param user    The account initiating the msg pass
   * @param nonce   The nonce of the src bridge for this msg pass
   * @param chainId The chainId of the chain which the src bridge is deployed to
   * @param version The payload version
   * @param amount  The amount field in the msg being passed
   */
  event BridgeInitiated(
    address indexed user,
    uint256 indexed nonce,
    uint256 indexed chainId,
    bytes32 version,
    uint256 amount
  );
  /**
   * @notice Event emitted when the destination chain to contract address mapping is
   *         deleted
   *
   * @param destinationChain The destination chain to be removed;
   */
  event ChainSupportRemoved(string indexed destinationChain);

  // Errors
  error DestinationNotSupported();
  error GasFeeTooLow();
  error CannotBridgeZero();
}
                

contracts/sanctions/SanctionsListClientUpgradeable.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "contracts/external/chainalysis/ISanctionsList.sol";
import "contracts/sanctions/ISanctionsListClient.sol";

/**
 * @title SanctionsListClient
 * @author Ondo Finance
 * @notice This abstract contract enables inheritors to query whether accounts
 *         are sanctioned or not
 */
abstract contract SanctionsListClientUpgradeable is
  Initializable,
  ISanctionsListClient
{
  // Sanctions list address
  ISanctionsList public override sanctionsList;

  /**
   * @notice Initialize the contract by setting blocklist variable
   *
   * @param _sanctionsList Address of the sanctionsList contract
   *
   * @dev Function should be called by the inheriting contract on
   *      initialization
   */
  function __SanctionsListClientInitializable_init(
    address _sanctionsList
  ) internal onlyInitializing {
    __SanctionsListClientInitializable_init_unchained(_sanctionsList);
  }

  /**
   * @dev Internal function to future-proof parent linearization. Matches OZ
   *      upgradeable suggestions
   */
  function __SanctionsListClientInitializable_init_unchained(
    address _sanctionsList
  ) internal onlyInitializing {
    _setSanctionsList(_sanctionsList);
  }

  /**
   * @notice Sets the sanctions list address for this client
   *
   * @param _sanctionsList The new sanctions list address
   */
  function _setSanctionsList(address _sanctionsList) internal {
    if (_sanctionsList == address(0)) {
      revert SanctionsListZeroAddress();
    }
    address oldSanctionsList = address(sanctionsList);
    sanctionsList = ISanctionsList(_sanctionsList);
    emit SanctionsListSet(oldSanctionsList, _sanctionsList);
  }

  /**
   * @notice Checks whether an address has been sanctioned
   *
   * @param account The account to check
   */
  function _isSanctioned(address account) internal view returns (bool) {
    return sanctionsList.isSanctioned(account);
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[50] private __gap;
}
                

contracts/RWAHub.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/interfaces/IPricerReader.sol";
import "contracts/interfaces/IRWALike.sol";
import "contracts/external/openzeppelin/contracts/token/IERC20.sol";
import "contracts/external/openzeppelin/contracts/token/SafeERC20.sol";
import "contracts/interfaces/IRWAHub.sol";

// Additional Dependencies
import "contracts/external/openzeppelin/contracts/token/IERC20Metadata.sol";
import "contracts/external/openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "contracts/external/openzeppelin/contracts/security/ReentrancyGuard.sol";

abstract contract RWAHub is IRWAHub, ReentrancyGuard, AccessControlEnumerable {
  using SafeERC20 for IERC20;
  // RWA Token contract
  IRWALike public immutable rwa;
  // Pointer to Pricer
  IPricerReader public pricer;
  // Address to receive deposits
  address public constant assetRecipient =
    0x599C5276F94153Aed4Abfe267eFc719b524F3aAA; // OMMF - CB Deposit Address
  // Address to send redemptions
  address public assetSender;
  // Address fee recipient
  address public feeRecipient;
  // Mapping from deposit Id -> Depositor
  mapping(bytes32 => Depositor) public depositIdToDepositor;
  // Mapping from redemptionId -> Redeemer
  mapping(bytes32 => Redeemer) public redemptionIdToRedeemer;

  /// @dev Mint/Redeem Parameters
  // Minimum amount that must be deposited to mint the RWA token
  // Denoted in decimals of `collateral`
  uint256 public minimumDepositAmount;

  // Minimum amount that must be redeemed for a withdraw request
  uint256 public minimumRedemptionAmount;

  // Minting fee specified in basis points
  uint256 public mintFee = 0;

  // Redemption fee specified in basis points
  uint256 public redemptionFee = 0;

  // The asset accepted by the RWAHub
  IERC20 public immutable collateral;

  // Decimal multiplier representing the difference between `rwa` decimals
  // In `collateral` token decimals
  uint256 public immutable decimalsMultiplier;

  // Deposit counter to map subscription requests to
  uint256 public subscriptionRequestCounter = 1;

  // Redemption Id to map from
  uint256 public redemptionRequestCounter = 1;

  // Helper constant that allows us to specify basis points in calculations
  uint256 public constant BPS_DENOMINATOR = 10_000;

  // Pause variables
  bool public redemptionPaused;
  bool public subscriptionPaused;

  /// @dev Role based access control roles
  bytes32 public constant MANAGER_ADMIN = keccak256("MANAGER_ADMIN");
  bytes32 public constant PAUSER_ADMIN = keccak256("PAUSER_ADMIN");
  bytes32 public constant PRICE_ID_SETTER_ROLE =
    keccak256("PRICE_ID_SETTER_ROLE");
  bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");

  /// @notice constructor
  constructor(
    address _collateral,
    address _rwa,
    address managerAdmin,
    address pauser,
    address _assetSender,
    address _feeRecipient,
    uint256 _minimumDepositAmount,
    uint256 _minimumRedemptionAmount
  ) {
    if (_collateral == address(0)) {
      revert CollateralCannotBeZero();
    }
    if (_rwa == address(0)) {
      revert RWACannotBeZero();
    }
    if (_assetSender == address(0)) {
      revert AssetSenderCannotBeZero();
    }
    if (_feeRecipient == address(0)) {
      revert FeeRecipientCannotBeZero();
    }

    _grantRole(DEFAULT_ADMIN_ROLE, managerAdmin);
    _grantRole(MANAGER_ADMIN, managerAdmin);
    _grantRole(PAUSER_ADMIN, pauser);
    _setRoleAdmin(PAUSER_ADMIN, MANAGER_ADMIN);
    _setRoleAdmin(PRICE_ID_SETTER_ROLE, MANAGER_ADMIN);
    _setRoleAdmin(RELAYER_ROLE, MANAGER_ADMIN);

    collateral = IERC20(_collateral);
    rwa = IRWALike(_rwa);
    feeRecipient = _feeRecipient;
    assetSender = _assetSender;
    minimumDepositAmount = _minimumDepositAmount;
    minimumRedemptionAmount = _minimumRedemptionAmount;

    decimalsMultiplier =
      10 **
        (IERC20Metadata(_rwa).decimals() -
          IERC20Metadata(_collateral).decimals());
  }

  /*//////////////////////////////////////////////////////////////
                  Subscription/Redemption Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Function used by users to request subscription to the fund
   *
   * @param amount The amount of collateral one wished to deposit
   */
  function requestSubscription(
    uint256 amount
  )
    external
    virtual
    nonReentrant
    ifNotPaused(subscriptionPaused)
    checkRestrictions(msg.sender)
  {
    if (amount < minimumDepositAmount) {
      revert DepositTooSmall();
    }

    uint256 feesInCollateral = _getMintFees(amount);
    uint256 depositAmountAfterFee = amount - feesInCollateral;

    // Link the depositor to their deposit ID
    bytes32 depositId = bytes32(subscriptionRequestCounter++);
    depositIdToDepositor[depositId] = Depositor(
      msg.sender,
      depositAmountAfterFee,
      0
    );

    if (feesInCollateral > 0) {
      collateral.safeTransferFrom(msg.sender, feeRecipient, feesInCollateral);
    }

    collateral.safeTransferFrom(
      msg.sender,
      assetRecipient,
      depositAmountAfterFee
    );

    emit MintRequested(
      msg.sender,
      depositId,
      amount,
      depositAmountAfterFee,
      feesInCollateral
    );
  }

  /**
   * @notice Function used to claim tokens corresponding to a deposit request
   *
   * @param depositIds An array containing the deposit Ids one wishes to claim
   *
   * @dev Implicitly does all transfer checks present in underlying `rwa`
   * @dev The priceId corresponding to a given depositId must be set prior to
   *      claiming a mint
   */
  function claimMint(
    bytes32[] calldata depositIds
  ) external virtual nonReentrant ifNotPaused(subscriptionPaused) {
    uint256 depositsSize = depositIds.length;
    for (uint256 i = 0; i < depositsSize; ++i) {
      _claimMint(depositIds[i]);
    }
  }

  /**
   * @notice Internal claim mint helper
   *
   * @dev This function can be overriden to implement custom claiming logic
   */
  function _claimMint(bytes32 depositId) internal virtual {
    Depositor memory depositor = depositIdToDepositor[depositId];
    // Revert if priceId is not set
    if (depositor.priceId == 0) {
      revert PriceIdNotSet();
    }

    uint256 price = pricer.getPrice(depositor.priceId);
    uint256 rwaOwed = _getMintAmountForPrice(
      depositor.amountDepositedMinusFees,
      price
    );

    delete depositIdToDepositor[depositId];
    rwa.mint(depositor.user, rwaOwed);

    emit MintCompleted(
      depositor.user,
      depositId,
      rwaOwed,
      depositor.amountDepositedMinusFees,
      price,
      depositor.priceId
    );
  }

  /**
   * @notice Function used by users to request a redemption from the fund
   *
   * @param amount The amount (in units of `rwa`) that a user wishes to redeem
   *               from the fund
   */
  function requestRedemption(
    uint256 amount
  ) external virtual nonReentrant ifNotPaused(redemptionPaused) {
    if (amount < minimumRedemptionAmount) {
      revert RedemptionTooSmall();
    }
    bytes32 redemptionId = bytes32(redemptionRequestCounter++);
    redemptionIdToRedeemer[redemptionId] = Redeemer(msg.sender, amount, 0);

    rwa.burnFrom(msg.sender, amount);

    emit RedemptionRequested(msg.sender, redemptionId, amount);
  }

  /**
   * @notice Function to claim collateral corresponding to a redemption request
   *
   * @param redemptionIds an Array of redemption Id's which ought to fulfilled
   *
   * @dev Implicitly does all checks present in underlying `rwa`
   * @dev The price Id corresponding to a redemptionId must be set prior to
   *      claiming a redemption
   */
  function claimRedemption(
    bytes32[] calldata redemptionIds
  ) external virtual nonReentrant ifNotPaused(redemptionPaused) {
    uint256 fees;
    uint256 redemptionsSize = redemptionIds.length;
    for (uint256 i = 0; i < redemptionsSize; ++i) {
      Redeemer memory member = redemptionIdToRedeemer[redemptionIds[i]];
      _checkRestrictions(member.user);
      if (member.priceId == 0) {
        // Then the price for this redemption has not been set
        revert PriceIdNotSet();
      }

      // Calculate collateral due and fees
      uint256 price = pricer.getPrice(member.priceId);
      uint256 collateralDue = _getRedemptionAmountForRwa(
        member.amountRwaTokenBurned,
        price
      );
      uint256 fee = _getRedemptionFees(collateralDue);
      uint256 collateralDuePostFees = collateralDue - fee;
      fees += fee;

      delete redemptionIdToRedeemer[redemptionIds[i]];

      collateral.safeTransferFrom(
        assetSender,
        member.user,
        collateralDuePostFees
      );

      emit RedemptionCompleted(
        member.user,
        redemptionIds[i],
        member.amountRwaTokenBurned,
        collateralDuePostFees,
        fee,
        price,
        member.priceId
      );
    }
    if (fees > 0) {
      collateral.safeTransferFrom(assetSender, feeRecipient, fees);
    }
  }

  /*//////////////////////////////////////////////////////////////
                         Relayer Functions
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Adds a deposit proof to the contract
   *
   * @param txHash                The transaction hash of the deposit
   * @param user                  The address of the user who made the deposit
   * @param depositAmountAfterFee The amount of the deposit after fees
   * @param feeAmount             The amount of the fees taken
   * @param timestamp             The timestamp of the deposit
   *
   * @dev txHash is used as the depositId in storage
   * @dev All amounts are in decimals of `collateral`
   */
  function addProof(
    bytes32 txHash,
    address user,
    uint256 depositAmountAfterFee,
    uint256 feeAmount,
    uint256 timestamp
  ) external override onlyRole(RELAYER_ROLE) checkRestrictions(user) {
    if (depositIdToDepositor[txHash].user != address(0)) {
      revert DepositProofAlreadyExists();
    }
    depositIdToDepositor[txHash] = Depositor(user, depositAmountAfterFee, 0);
    emit DepositProofAdded(
      txHash,
      user,
      depositAmountAfterFee,
      feeAmount,
      timestamp
    );
  }

  /*//////////////////////////////////////////////////////////////
                           PriceId Setters
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Admin function to associate a depositId with a given Price Id
   *
   * @param depositIds an Array of deposit Ids to be associated
   * @param priceIds   an Array of price Ids to be associated
   *
   * @dev Array size must match
   */
  function setPriceIdForDeposits(
    bytes32[] calldata depositIds,
    uint256[] calldata priceIds
  ) external virtual onlyRole(PRICE_ID_SETTER_ROLE) {
    uint256 depositsSize = depositIds.length;
    if (depositsSize != priceIds.length) {
      revert ArraySizeMismatch();
    }
    for (uint256 i = 0; i < depositsSize; ++i) {
      if (depositIdToDepositor[depositIds[i]].user == address(0)) {
        revert DepositorNull();
      }
      if (depositIdToDepositor[depositIds[i]].priceId != 0) {
        revert PriceIdAlreadySet();
      }
      depositIdToDepositor[depositIds[i]].priceId = priceIds[i];
      emit PriceIdSetForDeposit(depositIds[i], priceIds[i]);
    }
  }

  /**
   * @notice Admin function to associate redemptionId with a given priceId
   *
   * @param redemptionIds an Array of redemptionIds to associate
   * @param priceIds  an Array of priceIds to associate
   */
  function setPriceIdForRedemptions(
    bytes32[] calldata redemptionIds,
    uint256[] calldata priceIds
  ) external virtual onlyRole(PRICE_ID_SETTER_ROLE) {
    uint256 redemptionsSize = redemptionIds.length;
    if (redemptionsSize != priceIds.length) {
      revert ArraySizeMismatch();
    }
    for (uint256 i = 0; i < redemptionsSize; ++i) {
      if (redemptionIdToRedeemer[redemptionIds[i]].priceId != 0) {
        revert PriceIdAlreadySet();
      }
      redemptionIdToRedeemer[redemptionIds[i]].priceId = priceIds[i];
      emit PriceIdSetForRedemption(redemptionIds[i], priceIds[i]);
    }
  }

  /*//////////////////////////////////////////////////////////////
                           Admin Setters
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Admin function to overwrite entries in the depoitIdToDepositor
   *         mapping
   *
   * @param depositIdToOverwrite  The depositId of the entry we wish to
   *                              overwrite
   * @param user                  The user for the new entry
   * @param depositAmountAfterFee The deposit value for the new entry
   * @param priceId               The priceId to be associated with the new
   *                              entry
   */
  function overwriteDepositor(
    bytes32 depositIdToOverwrite,
    address user,
    uint256 depositAmountAfterFee,
    uint256 priceId
  ) external onlyRole(MANAGER_ADMIN) checkRestrictions(user) {
    Depositor memory oldDepositor = depositIdToDepositor[depositIdToOverwrite];

    depositIdToDepositor[depositIdToOverwrite] = Depositor(
      user,
      depositAmountAfterFee,
      priceId
    );

    emit DepositorOverwritten(
      depositIdToOverwrite,
      oldDepositor.user,
      user,
      oldDepositor.priceId,
      priceId,
      oldDepositor.amountDepositedMinusFees,
      depositAmountAfterFee
    );
  }

  /**
   * @notice Admin function to overwrite entries in the redemptionIdToRedeemer
   *         mapping
   *
   * @param redemptionIdToOverwrite The redemptionId of the entry we wish to
   *                                overwrite
   * @param user                    The user for the new entry
   * @param rwaTokenAmountBurned    The burn amount for the new entry
   * @param priceId                 The priceID to be associated with the new
   *                                entry
   */
  function overwriteRedeemer(
    bytes32 redemptionIdToOverwrite,
    address user,
    uint256 rwaTokenAmountBurned,
    uint256 priceId
  ) external onlyRole(MANAGER_ADMIN) checkRestrictions(user) {
    Redeemer memory oldRedeemer = redemptionIdToRedeemer[
      redemptionIdToOverwrite
    ];
    redemptionIdToRedeemer[redemptionIdToOverwrite] = Redeemer(
      user,
      rwaTokenAmountBurned,
      priceId
    );
    emit RedeemerOverwritten(
      redemptionIdToOverwrite,
      oldRedeemer.user,
      user,
      oldRedeemer.priceId,
      priceId,
      oldRedeemer.amountRwaTokenBurned,
      rwaTokenAmountBurned
    );
  }

  /**
   * @notice Admin function to set the minimum amount to redeem
   *
   * @param _minimumRedemptionAmount The minimum amount required to submit a
   *                                 redemption request
   */
  function setMinimumRedemptionAmount(
    uint256 _minimumRedemptionAmount
  ) external onlyRole(MANAGER_ADMIN) {
    if (_minimumRedemptionAmount < BPS_DENOMINATOR) {
      revert AmountTooSmall();
    }
    uint256 oldRedeemMinimum = minimumRedemptionAmount;
    minimumRedemptionAmount = _minimumRedemptionAmount;
    emit MinimumRedemptionAmountSet(oldRedeemMinimum, _minimumRedemptionAmount);
  }

  /**
   * @notice Admin function to set the minimum amount required for a deposit
   *
   * @param minDepositAmount The minimum amount required to submit a deposit
   *                         request
   */
  function setMinimumDepositAmount(
    uint256 minDepositAmount
  ) external onlyRole(MANAGER_ADMIN) {
    if (minDepositAmount < BPS_DENOMINATOR) {
      revert AmountTooSmall();
    }
    uint256 oldMinimumDepositAmount = minimumDepositAmount;
    minimumDepositAmount = minDepositAmount;
    emit MinimumDepositAmountSet(oldMinimumDepositAmount, minDepositAmount);
  }

  /**
   * @notice Admin function to set the mint fee
   *
   * @param _mintFee The new mint fee specified in basis points
   *
   * @dev The maximum fee that can be set is 10_000 bps, or 100%
   */
  function setMintFee(uint256 _mintFee) external onlyRole(MANAGER_ADMIN) {
    if (_mintFee > BPS_DENOMINATOR) {
      revert FeeTooLarge();
    }
    uint256 oldMintFee = mintFee;
    mintFee = _mintFee;
    emit MintFeeSet(oldMintFee, _mintFee);
  }

  /**
   * @notice Admin function to set the redeem fee
   *
   * @param _redemptionFee The new redeem fee specified in basis points
   *
   * @dev The maximum fee that can be set is 10_000 bps, or 100%
   */
  function setRedemptionFee(
    uint256 _redemptionFee
  ) external onlyRole(MANAGER_ADMIN) {
    if (_redemptionFee > BPS_DENOMINATOR) {
      revert FeeTooLarge();
    }
    uint256 oldRedeemFee = redemptionFee;
    redemptionFee = _redemptionFee;
    emit RedemptionFeeSet(oldRedeemFee, _redemptionFee);
  }

  /**
   * @notice Admin function to set the address of the Pricer contract
   *
   * @param newPricer The address of the new pricer contract
   */
  function setPricer(address newPricer) external onlyRole(MANAGER_ADMIN) {
    address oldPricer = address(pricer);
    pricer = IPricerReader(newPricer);
    emit NewPricerSet(oldPricer, newPricer);
  }

  /**
   * @notice Admin function to set the address of `feeRecipient`
   *
   * @param newFeeRecipient The address of the new `feeRecipient`
   */
  function setFeeRecipient(
    address newFeeRecipient
  ) external onlyRole(MANAGER_ADMIN) {
    address oldFeeRecipient = feeRecipient;
    feeRecipient = newFeeRecipient;
    emit FeeRecipientSet(oldFeeRecipient, feeRecipient);
  }

  /**
   * @notice Admin function to set the address of `assetSender`
   *
   * @param newAssetSender The address of the new `assetSender`
   */
  function setAssetSender(
    address newAssetSender
  ) external onlyRole(MANAGER_ADMIN) {
    address oldAssetSender = assetSender;
    assetSender = newAssetSender;
    emit AssetSenderSet(oldAssetSender, newAssetSender);
  }

  /*//////////////////////////////////////////////////////////////
                            Pause Utils
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Modifier to check if a feature is paused
   *
   * @param feature The feature to check if paused
   */
  modifier ifNotPaused(bool feature) {
    if (feature) {
      revert FeaturePaused();
    }
    _;
  }

  /**
   * @notice Function to pause subscription to RWAHub
   */
  function pauseSubscription() external onlyRole(PAUSER_ADMIN) {
    subscriptionPaused = true;
    emit SubscriptionPaused(msg.sender);
  }

  /**
   * @notice Function to pause redemptions to RWAHub
   */
  function pauseRedemption() external onlyRole(PAUSER_ADMIN) {
    redemptionPaused = true;
    emit RedemptionPaused(msg.sender);
  }

  /**
   * @notice Function to unpause subscriptions to RWAHub
   */
  function unpauseSubscription() external onlyRole(MANAGER_ADMIN) {
    subscriptionPaused = false;
    emit SubscriptionUnpaused(msg.sender);
  }

  /**
   * @notice Function to unpause redemptions to RWAHub
   */
  function unpauseRedemption() external onlyRole(MANAGER_ADMIN) {
    redemptionPaused = false;
    emit RedemptionUnpaused(msg.sender);
  }

  /*//////////////////////////////////////////////////////////////
                      Check Restriction Utils
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Modifier to check restrictions status of an account
   *
   * @param account The account to check
   */
  modifier checkRestrictions(address account) {
    _checkRestrictions(account);
    _;
  }

  /**
   * @notice internal function to check restriction status
   *         of an address
   *
   * @param account The account to check restriction status for
   *
   * @dev This function is virtual to be overridden by child contract
   *      to check restrictions on a more granular level
   */
  function _checkRestrictions(address account) internal view virtual;

  /*//////////////////////////////////////////////////////////////
                           Math Utils
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Given amount of `collateral`, returns how much in fees
   *         are owed
   *
   *
   * @param collateralAmount Amount `collateral` to calculate fees
   *                         (in decimals of `collateral`)
   */
  function _getMintFees(
    uint256 collateralAmount
  ) internal view returns (uint256) {
    return (collateralAmount * mintFee) / BPS_DENOMINATOR;
  }

  /**
   * @notice Given amount of `collateral`, returns how much in fees
   *         are owed
   *
   * @param collateralAmount Amount of `collateral` to calculate fees
   *                         (in decimals of `collateral`)
   */
  function _getRedemptionFees(
    uint256 collateralAmount
  ) internal view returns (uint256) {
    return (collateralAmount * redemptionFee) / BPS_DENOMINATOR;
  }

  /**
   * @notice Given a deposit amount and priceId, returns the amount
   *         of `rwa` due
   *
   * @param depositAmt The amount deposited in units of `collateral`
   * @param price      The price associated with this deposit
   */
  function _getMintAmountForPrice(
    uint256 depositAmt,
    uint256 price
  ) internal view returns (uint256 rwaAmountOut) {
    uint256 amountE36 = _scaleUp(depositAmt) * 1e18;
    // Will revert with div by 0 if price not defined for a priceId
    rwaAmountOut = amountE36 / price;
  }

  /**
   * @notice Given a redemption amount and a priceId, returns the amount
   *         of `collateral` due
   *
   * @param rwaTokenAmountBurned The amount of `rwa` burned for a redemption
   * @param price                The price associated with this redemption
   */
  function _getRedemptionAmountForRwa(
    uint256 rwaTokenAmountBurned,
    uint256 price
  ) internal view returns (uint256 collateralOwed) {
    uint256 amountE36 = rwaTokenAmountBurned * price;
    collateralOwed = _scaleDown(amountE36 / 1e18);
  }

  /**
   * @notice Scale provided amount up by `decimalsMultiplier`
   *
   * @dev This helper is used for converting the collateral's decimals
   *      representation to the RWA amount decimals representation.
   */
  function _scaleUp(uint256 amount) internal view returns (uint256) {
    return amount * decimalsMultiplier;
  }

  /**
   * @notice Scale provided amount down by `decimalsMultiplier`
   *
   * @dev This helper is used for converting `rwa`'s decimal
   *      representation to the `collateral`'s decimal representation
   */
  function _scaleDown(uint256 amount) internal view returns (uint256) {
    return amount / decimalsMultiplier;
  }
}
                

contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721PresetMinterPauserAutoIdUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721EnumerableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721BurnableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721PausableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/utils/CounterUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @dev {ERC721} token, including:
 *
 *  - ability for holders to burn (destroy) their tokens
 *  - a minter role that allows for token minting (creation)
 *  - a pauser role that allows to stop all token transfers
 *  - token ID and URI autogeneration
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 *
 * The account that deploys the contract will be granted the minter and pauser
 * roles, as well as the default admin role, which will let it grant both minter
 * and pauser roles to other accounts.
 *
 * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._
 */
contract ERC721PresetMinterPauserAutoIdUpgradeable is
  Initializable,
  ContextUpgradeable,
  AccessControlEnumerableUpgradeable,
  ERC721EnumerableUpgradeable,
  ERC721BurnableUpgradeable,
  ERC721PausableUpgradeable
{
  function initialize(
    string memory name,
    string memory symbol,
    string memory baseTokenURI
  ) public virtual initializer {
    __ERC721PresetMinterPauserAutoId_init(name, symbol, baseTokenURI);
  }

  using CountersUpgradeable for CountersUpgradeable.Counter;

  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

  CountersUpgradeable.Counter private _tokenIdTracker;

  string private _baseTokenURI;

  /**
   * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
   * account that deploys the contract.
   *
   * Token URIs will be autogenerated based on `baseURI` and their token IDs.
   * See {ERC721-tokenURI}.
   */
  function __ERC721PresetMinterPauserAutoId_init(
    string memory name,
    string memory symbol,
    string memory baseTokenURI
  ) internal onlyInitializing {
    __ERC721_init_unchained(name, symbol);
    __Pausable_init_unchained();
    __ERC721PresetMinterPauserAutoId_init_unchained(name, symbol, baseTokenURI);
  }

  function __ERC721PresetMinterPauserAutoId_init_unchained(
    string memory,
    string memory,
    string memory baseTokenURI
  ) internal onlyInitializing {
    _baseTokenURI = baseTokenURI;

    _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

    _setupRole(MINTER_ROLE, _msgSender());
    _setupRole(PAUSER_ROLE, _msgSender());
  }

  function _baseURI() internal view virtual override returns (string memory) {
    return _baseTokenURI;
  }

  /**
   * @dev Creates a new token for `to`. Its token ID will be automatically
   * assigned (and available on the emitted {IERC721-Transfer} event), and the token
   * URI autogenerated based on the base URI passed at construction.
   *
   * See {ERC721-_mint}.
   *
   * Requirements:
   *
   * - the caller must have the `MINTER_ROLE`.
   */
  function mint(address to) public virtual {
    require(
      hasRole(MINTER_ROLE, _msgSender()),
      "ERC721PresetMinterPauserAutoId: must have minter role to mint"
    );

    // We cannot just use balanceOf to create the new tokenId because tokens
    // can be burned (destroyed), so we need a separate counter.
    _mint(to, _tokenIdTracker.current());
    _tokenIdTracker.increment();
  }

  /**
   * @dev Pauses all token transfers.
   *
   * See {ERC721Pausable} and {Pausable-_pause}.
   *
   * Requirements:
   *
   * - the caller must have the `PAUSER_ROLE`.
   */
  function pause() public virtual {
    require(
      hasRole(PAUSER_ROLE, _msgSender()),
      "ERC721PresetMinterPauserAutoId: must have pauser role to pause"
    );
    _pause();
  }

  /**
   * @dev Unpauses all token transfers.
   *
   * See {ERC721Pausable} and {Pausable-_unpause}.
   *
   * Requirements:
   *
   * - the caller must have the `PAUSER_ROLE`.
   */
  function unpause() public virtual {
    require(
      hasRole(PAUSER_ROLE, _msgSender()),
      "ERC721PresetMinterPauserAutoId: must have pauser role to unpause"
    );
    _unpause();
  }

  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 tokenId
  )
    internal
    virtual
    override(
      ERC721Upgradeable,
      ERC721EnumerableUpgradeable,
      ERC721PausableUpgradeable
    )
  {
    super._beforeTokenTransfer(from, to, tokenId);
  }

  /**
   * @dev See {IERC165-supportsInterface}.
   */
  function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    override(
      AccessControlEnumerableUpgradeable,
      ERC721Upgradeable,
      ERC721EnumerableUpgradeable
    )
    returns (bool)
  {
    return super.supportsInterface(interfaceId);
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[48] private __gap;
}
                

contracts/ommf/ommf_token/ommf.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/IERC20MetadataUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "contracts/kyc/KYCRegistryClientUpgradeable.sol";

/**
 * @title Interest-bearing ERC20-like token for OMMF.
 *
 * OMMF balances are dynamic and represent the holder's share in the total amount
 * of Cash controlled by the protocol. Account shares aren't normalized, so the
 * contract also stores the sum of all shares to calculate each account's token balance
 * which equals to:
 *
 *   shares[account] * depositedCash() / _getTotalShares()
 *
 * For example, assume that we have:
 *
 *   depositedCash() -> 10 USDC underlying OMMF
 *   sharesOf(user1) -> 100
 *   sharesOf(user2) -> 400
 *
 * Therefore:
 *
 *   balanceOf(user1) -> 2 tokens which corresponds 2 OMMF
 *   balanceOf(user2) -> 8 tokens which corresponds 8 OMMF
 *
 * Since balances of all token holders change when the amount of total pooled Cash
 * changes, this token cannot fully implement ERC20 standard: it only emits `Transfer`
 * events upon explicit transfer between holders. In contrast, when total amount of
 * pooled Cash increases, no `Transfer` events are generated: doing so would require
 * emitting an event for each token holder and thus running an unbounded loop.
 *
 */

contract OMMF is
  Initializable,
  ContextUpgradeable,
  PausableUpgradeable,
  AccessControlEnumerableUpgradeable,
  KYCRegistryClientUpgradeable,
  IERC20Upgradeable,
  IERC20MetadataUpgradeable
{
  /**
   * @dev OMMF balances are dynamic and are calculated based on the accounts' shares
   * and the total amount of Cash controlled by the protocol. Account shares aren't
   * normalized, so the contract also stores the sum of all shares to calculate
   * each account's token balance which equals to:
   *
   *   shares[account] * depositedCash() / _getTotalShares()
   */
  mapping(address => uint256) private shares;

  /// @dev Allowances are nominated in tokens, not token shares.
  mapping(address => mapping(address => uint256)) private allowances;

  // Total shares in existence
  uint256 private totalShares;

  // Total cash in fund
  uint256 public depositedCash;

  // Address of the oracle that updates `depositedCash`
  address public oracle;

  /// @dev Role based access control roles
  bytes32 public constant OMMF_MANAGER_ROLE = keccak256("ADMIN_ROLE");
  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
  bytes32 public constant KYC_CONFIGURER_ROLE =
    keccak256("KYC_CONFIGURER_ROLE");

  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor() {
    _disableInitializers();
  }

  function initialize(
    address admin,
    address kycRegistry,
    uint256 requirementGroup
  ) public virtual initializer {
    __OMMF_init(admin, kycRegistry, requirementGroup);
  }

  function __OMMF_init(
    address admin,
    address kycRegistry,
    uint256 requirementGroup
  ) internal onlyInitializing {
    __Pausable_init_unchained();
    __KYCRegistryClientInitializable_init_unchained(
      kycRegistry,
      requirementGroup
    );
    __OMMF_init_unchained(admin);
  }

  function __OMMF_init_unchained(address admin) internal onlyInitializing {
    _grantRole(DEFAULT_ADMIN_ROLE, admin);
    _grantRole(OMMF_MANAGER_ROLE, admin);
    _grantRole(PAUSER_ROLE, admin);
    _grantRole(BURNER_ROLE, admin);
    _grantRole(KYC_CONFIGURER_ROLE, admin);
  }

  /**
   * @notice An executed shares transfer from `sender` to `recipient`.
   *
   * @dev emitted in pair with an ERC20-defined `Transfer` event.
   */
  event TransferShares(
    address indexed from,
    address indexed to,
    uint256 sharesValue
  );

  /**
   * @notice Emitted when an oracle report (rebase) is executed
   *
   * @param oldDepositedCash The old NAV value.
   * @param newDepositedCash The new NAV value.
   */
  event OracleReportHandled(uint256 oldDepositedCash, uint256 newDepositedCash);

  /**
   * @return the name of the token.
   */
  function name() public pure returns (string memory) {
    return "Ondo US Money Markets";
  }

  /**
   * @return the symbol of the token, usually a shorter version of the
   * name.
   */
  function symbol() public pure returns (string memory) {
    return "OMMF";
  }

  /**
   * @return the number of decimals for getting user representation of a token amount.
   */
  function decimals() public pure returns (uint8) {
    return 18;
  }

  /**
   * @return the amount of tokens in existence.
   *
   * @dev Always equals to `depositedCash()` since token amount
   * is pegged to the total amount of OMMF controlled by the protocol.
   */
  function totalSupply() public view returns (uint256) {
    return depositedCash;
  }

  /**
   * @return the amount of tokens owned by the `_account`.
   *
   * @dev Balances are dynamic and equal the `_account`'s share in the amount of the
   * total Cash controlled by the protocol. See `sharesOf`.
   */
  function balanceOf(address _account) public view returns (uint256) {
    return getBalanceOfByShares(_sharesOf(_account));
  }

  /**
   * @notice Moves `_amount` tokens from the caller's account to the `_recipient` account.
   *
   * @return a boolean value indicating whether the operation succeeded.
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the caller must have a balance of at least `_amount`.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function transfer(address _recipient, uint256 _amount) public returns (bool) {
    _transfer(msg.sender, _recipient, _amount);
    return true;
  }

  /**
   * @return the remaining number of tokens that `_spender` is allowed to spend
   * on behalf of `_owner` through `transferFrom`. This is zero by default.
   *
   * @dev This value changes when `approve` or `transferFrom` is called.
   */
  function allowance(
    address _owner,
    address _spender
  ) public view returns (uint256) {
    return allowances[_owner][_spender];
  }

  /**
   * @notice Sets `_amount` as the allowance of `_spender` over the caller's tokens.
   *
   * @return a boolean value indicating whether the operation succeeded.
   * Emits an `Approval` event.
   *
   * Requirements:
   *
   * - `_spender` cannot be the zero address.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function approve(address _spender, uint256 _amount) public returns (bool) {
    _approve(msg.sender, _spender, _amount);
    return true;
  }

  /**
   * @notice Moves `_amount` tokens from `_sender` to `_recipient` using the
   * allowance mechanism. `_amount` is then deducted from the caller's
   * allowance.
   *
   * @return a boolean value indicating whether the operation succeeded.
   *
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_sender` and `_recipient` cannot be the zero addresses.
   * - `_sender` must have a balance of at least `_amount`.
   * - the caller must have allowance for `_sender`'s tokens of at least `_amount`.
   * - the contract must not be paused.
   *
   * @dev The `_amount` argument is the amount of tokens, not shares.
   */
  function transferFrom(
    address _sender,
    address _recipient,
    uint256 _amount
  ) public returns (bool) {
    uint256 currentAllowance = allowances[_sender][msg.sender];
    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");

    _transfer(_sender, _recipient, _amount);
    _approve(_sender, msg.sender, currentAllowance - _amount);
    return true;
  }

  /**
   * @notice Atomically increases the allowance granted to `_spender` by the caller by `_addedValue`.
   *
   * This is an alternative to `approve` that can be used as a mitigation for
   * problems described in:
   * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_spender` cannot be the the zero address.
   * - the contract must not be paused.
   */
  function increaseAllowance(
    address _spender,
    uint256 _addedValue
  ) public returns (bool) {
    _approve(
      msg.sender,
      _spender,
      allowances[msg.sender][_spender] + _addedValue
    );
    return true;
  }

  /**
   * @notice Atomically decreases the allowance granted to `_spender` by the caller by `_subtractedValue`.
   *
   * This is an alternative to `approve` that can be used as a mitigation for
   * problems described in:
   * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
   * Emits an `Approval` event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `_spender` cannot be the zero address.
   * - `_spender` must have allowance for the caller of at least `_subtractedValue`.
   * - the contract must not be paused.
   */
  function decreaseAllowance(
    address _spender,
    uint256 _subtractedValue
  ) public returns (bool) {
    uint256 currentAllowance = allowances[msg.sender][_spender];
    require(
      currentAllowance >= _subtractedValue,
      "DECREASED_ALLOWANCE_BELOW_ZERO"
    );
    _approve(msg.sender, _spender, currentAllowance - _subtractedValue);
    return true;
  }

  /**
   * @return the total amount of shares in existence.
   *
   * @dev The sum of all accounts' shares can be an arbitrary number, therefore
   * it is necessary to store it in order to calculate each account's relative share.
   */
  function getTotalShares() public view returns (uint256) {
    return totalShares;
  }

  /**
   * @return the amount of shares owned by `_account`.
   */
  function sharesOf(address _account) public view returns (uint256) {
    return _sharesOf(_account);
  }

  /**
   * @return the amount of shares that corresponds to `cashAmount` protocol-controlled Cash.
   */
  function getSharesByPooledCash(
    uint256 _cashAmount
  ) public view returns (uint256) {
    uint256 totalPooledCash = depositedCash;
    if (totalPooledCash == 0) {
      return 0;
    } else {
      return (_cashAmount * totalShares) / totalPooledCash;
    }
  }

  /**
   * @return the amount of OMMF that corresponds to `_sharesAmount` token shares.
   */
  function getBalanceOfByShares(
    uint256 _sharesAmount
  ) public view returns (uint256) {
    if (totalShares == 0) {
      return 0;
    } else {
      return (_sharesAmount * depositedCash) / totalShares;
    }
  }

  /**
   * @notice Moves `_amount` tokens from `_sender` to `_recipient`.
   * Emits a `Transfer` event.
   * Emits a `TransferShares` event.
   */
  function _transfer(
    address _sender,
    address _recipient,
    uint256 _amount
  ) internal {
    uint256 _sharesToTransfer = getSharesByPooledCash(_amount);
    _transferShares(_sender, _recipient, _sharesToTransfer);
    emit Transfer(_sender, _recipient, _amount);
  }

  /**
   * @notice Sets `_amount` as the allowance of `_spender` over the `_owner` s tokens.
   *
   * Emits an `Approval` event.
   *
   * Requirements:
   *
   * - `_owner` cannot be the zero address.
   * - `_spender` cannot be the zero address.
   * - the contract must not be paused.
   */
  function _approve(
    address _owner,
    address _spender,
    uint256 _amount
  ) internal whenNotPaused {
    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");

    allowances[_owner][_spender] = _amount;
    emit Approval(_owner, _spender, _amount);
  }

  /**
   * @return the amount of shares owned by `_account`.
   */
  function _sharesOf(address _account) internal view returns (uint256) {
    return shares[_account];
  }

  /**
   * @notice Moves `_sharesAmount` shares from `_sender` to `_recipient`.
   *
   * Requirements:
   *
   * - `_sender` cannot be the zero address.
   * - `_recipient` cannot be the zero address.
   * - `_sender` must hold at least `_sharesAmount` shares.
   * - the contract must not be paused.
   */
  function _transferShares(
    address _sender,
    address _recipient,
    uint256 _sharesAmount
  ) internal whenNotPaused {
    require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
    require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_sender, _recipient, _sharesAmount);

    uint256 currentSenderShares = shares[_sender];
    require(
      _sharesAmount <= currentSenderShares,
      "TRANSFER_AMOUNT_EXCEEDS_BALANCE"
    );

    shares[_sender] = currentSenderShares - _sharesAmount;
    shares[_recipient] = shares[_recipient] + _sharesAmount;
    emit TransferShares(_sender, _recipient, _sharesAmount);
  }

  /**
   * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
   * @dev This doesn't increase the token total supply.
   *
   * Requirements:
   *
   * - `_recipient` cannot be the zero address.
   * - the contract must not be paused.
   */
  function _mintShares(
    address _recipient,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_recipient != address(0), "MINT_TO_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(address(0), _recipient, _sharesAmount);

    totalShares += _sharesAmount;

    shares[_recipient] = shares[_recipient] + _sharesAmount;

    return totalShares;
  }

  /**
   * @notice Destroys `_sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
   * @dev This doesn't decrease the token total supply.
   *
   * Requirements:
   *
   * - `_account` cannot be the zero address.
   * - `_account` must hold at least `_sharesAmount` shares.
   * - the contract must not be paused.
   */
  function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_account, address(0), _sharesAmount);

    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");

    totalShares -= _sharesAmount;

    shares[_account] = accountShares - _sharesAmount;

    return totalShares;
  }

  /**
   * @dev Hook that is called before any transfer of tokens. This includes
   * minting and burning.
   *
   * Calling conditions:
   *
   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
   * will be transferred to `to`.
   * - when `from` is zero, `amount` tokens will be minted for `to`.
   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
   * - `from` and `to` are never both zero.
   *
   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256
  ) internal virtual {
    if (from != _msgSender() && to != _msgSender()) {
      require(
        _getKYCStatus(_msgSender()),
        "OMMF: must be KYC'd to initiate transfer"
      );
    }

    if (from != address(0)) {
      // Only check KYC if not minting
      require(
        _getKYCStatus(from),
        "OMMF: `from` address must be KYC'd to send tokens"
      );
    }

    if (to != address(0)) {
      // Only check KYC if not burning
      require(
        _getKYCStatus(to),
        "OMMF: `to` address must be KYC'd to receive tokens"
      );
    }
  }

  /**
   * @notice Updates underlying cash in fund
   * @dev periodically called by the Oracle contract
   * @param _depositedCash Total cash in fund
   */
  function handleOracleReport(uint256 _depositedCash) external whenNotPaused {
    require(msg.sender == oracle, "OMMF: not oracle");
    uint256 oldDepositedCash = depositedCash;
    depositedCash = _depositedCash;
    emit OracleReportHandled(oldDepositedCash, _depositedCash);
  }

  /**
   * @notice Sets the Oracle address
   * @dev The new oracle can call `handleOracleReport` for rebases
   * @param _oracle Address of the new oracle
   */
  function setOracle(address _oracle) external onlyRole(OMMF_MANAGER_ROLE) {
    oracle = _oracle;
  }

  /**
   * @notice Send funds to the pool
   * @return Amount of OMMF shares generated
   */
  function mint(
    address user,
    uint256 depositAmount
  ) external onlyRole(MINTER_ROLE) returns (uint256) {
    require(depositAmount > 0, "OMMF: zero deposit amount");

    uint256 sharesAmount = getSharesByPooledCash(depositAmount);
    if (sharesAmount == 0) {
      // totalControlledCash is 0: first-ever deposit
      // assume that shares correspond to Cash 1-to-1
      sharesAmount = depositAmount;
    }

    _mintShares(user, sharesAmount);

    depositedCash += depositAmount;

    emit TransferShares(address(0), user, sharesAmount);
    emit Transfer(address(0), user, getBalanceOfByShares(sharesAmount));
    return sharesAmount;
  }

  /**
   * @notice Admin burn function to burn OMMF tokens from any account
   * @param _account The account to burn tokens from
   * @param _amount  The amount of OMMF tokens to burn
   */
  function adminBurn(
    address _account,
    uint256 _amount
  ) external onlyRole(BURNER_ROLE) {
    uint256 sharesAmount = getSharesByPooledCash(_amount);

    _burnShares(_account, sharesAmount);
    depositedCash -= _amount; // OMMF corresponds to deposited collateral 1:1
    emit TransferShares(_account, address(0), sharesAmount);
    emit Transfer(_account, address(0), _amount);
  }

  /**
   * @notice Burns `_amount` of OMMF tokens from msg.sender's holdings
   * @param _amount The amount of OMMF tokens to burn
   */
  function burn(uint256 _amount) external {
    require(
      getBalanceOfByShares(_sharesOf(msg.sender)) >= _amount,
      "BURN_AMOUNT_EXCEEDS_BALANCE"
    );
    uint256 sharesAmount = getSharesByPooledCash(_amount);
    _burnShares(msg.sender, sharesAmount);
    depositedCash -= _amount;

    emit TransferShares(msg.sender, address(0), sharesAmount);
    emit Transfer(msg.sender, address(0), _amount);
  }

  /**
   * @notice Burns `_amount` of OMMF tokens from `_account`'s holdings,
   *         deducting from the caller's allowance
   * @param _account Account to burn tokens from
   * @param _amount  Amount of tokens to burn
   * @dev Decrements shares as well as `depositedAmount`
   */
  function burnFrom(address _account, uint256 _amount) external {
    uint256 currentAllowance = allowances[_account][msg.sender];
    require(currentAllowance >= _amount, "BURN_AMOUNT_EXCEEDS_ALLOWANCE");

    uint256 sharesAmount = getSharesByPooledCash(_amount);

    _burnShares(_account, sharesAmount);
    depositedCash -= _amount; // OMMF corresponds to deposited collateral 1:1

    _approve(_account, msg.sender, currentAllowance - _amount);

    emit TransferShares(_account, address(0), sharesAmount);
    emit Transfer(_account, address(0), _amount);
  }

  function pause() external onlyRole(PAUSER_ROLE) {
    _pause();
  }

  function unpause() external onlyRole(OMMF_MANAGER_ROLE) {
    _unpause();
  }

  function setKYCRequirementGroup(
    uint256 group
  ) external override onlyRole(KYC_CONFIGURER_ROLE) {
    _setKYCRequirementGroup(group);
  }

  function setKYCRegistry(
    address registry
  ) external override onlyRole(KYC_CONFIGURER_ROLE) {
    _setKYCRegistry(registry);
  }
}
                

contracts/interfaces/IAxelarExecutable.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

interface IAxelarExecutable {
  error InvalidAddress();
  error NotApprovedByGateway();

  function gateway() external view returns (IAxelarGateway);

  function execute(
    bytes32 commandId,
    string calldata sourceChain,
    string calldata sourceAddress,
    bytes calldata payload
  ) external;

  function executeWithToken(
    bytes32 commandId,
    string calldata sourceChain,
    string calldata sourceAddress,
    bytes calldata payload,
    string calldata tokenSymbol,
    uint256 amount
  ) external;
}
                

contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721BurnableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721Burnable.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @title ERC721 Burnable Token
 * @dev ERC721 Token that can be burned (destroyed).
 */
abstract contract ERC721BurnableUpgradeable is
  Initializable,
  ContextUpgradeable,
  ERC721Upgradeable
{
  function __ERC721Burnable_init() internal onlyInitializing {}

  function __ERC721Burnable_init_unchained() internal onlyInitializing {}

  /**
   * @dev Burns `tokenId`. See {ERC721-_burn}.
   *
   * Requirements:
   *
   * - The caller must own `tokenId` or be an approved operator.
   */
  function burn(uint256 tokenId) public virtual {
    //solhint-disable-next-line max-line-length
    require(
      _isApprovedOrOwner(_msgSender(), tokenId),
      "ERC721: caller is not token owner nor approved"
    );
    _burn(tokenId);
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[50] private __gap;
}
                

contracts/usdy/usdy/USDY.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20PresetMinterPauserUpgradeable.sol";
import "contracts/usdy/blocklist/BlocklistClientUpgradeable.sol";
import "contracts/usdy/allowlist/AllowlistClientUpgradeable.sol";
import "contracts/sanctions/SanctionsListClientUpgradeable.sol";

contract USDY is
  ERC20PresetMinterPauserUpgradeable,
  BlocklistClientUpgradeable,
  AllowlistClientUpgradeable,
  SanctionsListClientUpgradeable
{
  bytes32 public constant LIST_CONFIGURER_ROLE =
    keccak256("LIST_CONFIGURER_ROLE");
  bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor() {
    _disableInitializers();
  }

  function initialize(
    string memory name,
    string memory symbol,
    address blocklist,
    address allowlist,
    address sanctionsList
  ) public initializer {
    __ERC20PresetMinterPauser_init(name, symbol);
    __BlocklistClientInitializable_init(blocklist);
    __AllowlistClientInitializable_init(allowlist);
    __SanctionsListClientInitializable_init(sanctionsList);
  }

  /**
   * @notice Sets the blocklist address
   *
   * @param blocklist New blocklist address
   */
  function setBlocklist(
    address blocklist
  ) external override onlyRole(LIST_CONFIGURER_ROLE) {
    _setBlocklist(blocklist);
  }

  /**
   * @notice Sets the allowlist address
   *
   * @param allowlist New allowlist address
   */
  function setAllowlist(
    address allowlist
  ) external override onlyRole(LIST_CONFIGURER_ROLE) {
    _setAllowlist(allowlist);
  }

  /**
   * @notice Sets the sanctions list address
   *
   * @param sanctionsList New sanctions list address
   */
  function setSanctionsList(
    address sanctionsList
  ) external override onlyRole(LIST_CONFIGURER_ROLE) {
    _setSanctionsList(sanctionsList);
  }

  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 amount
  ) internal override {
    super._beforeTokenTransfer(from, to, amount);

    // Check constraints when `transferFrom` is called to facliitate
    // a transfer between two parties that are not `from` or `to`.
    if (from != msg.sender && to != msg.sender) {
      require(!_isBlocked(msg.sender), "USDY: 'sender' address blocked");
      require(!_isSanctioned(msg.sender), "USDY: 'sender' address sanctioned");
      require(
        _isAllowed(msg.sender),
        "USDY: 'sender' address not on allowlist"
      );
    }

    if (from != address(0)) {
      // If not minting
      require(!_isBlocked(from), "USDY: 'from' address blocked");
      require(!_isSanctioned(from), "USDY: 'from' address sanctioned");
      require(_isAllowed(from), "USDY: 'from' address not on allowlist");
    }

    if (to != address(0)) {
      // If not burning
      require(!_isBlocked(to), "USDY: 'to' address blocked");
      require(!_isSanctioned(to), "USDY: 'to' address sanctioned");
      require(_isAllowed(to), "USDY: 'to' address not on allowlist");
    }
  }

  /**
   * @notice Burns a specific amount of tokens
   *
   * @param from The account whose tokens will be burned
   * @param amount The amount of token to be burned
   *
   * @dev This function can be considered an admin-burn and is only callable
   *      by an address with the `BURNER_ROLE`
   */
  function burn(address from, uint256 amount) external onlyRole(BURNER_ROLE) {
    _burn(from, amount);
  }
}
                

contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20BurnableUpgradeable is
  Initializable,
  ContextUpgradeable,
  ERC20Upgradeable
{
  function __ERC20Burnable_init() internal onlyInitializing {}

  function __ERC20Burnable_init_unchained() internal onlyInitializing {}

  /**
   * @dev Destroys `amount` tokens from the caller.
   *
   * See {ERC20-_burn}.
   */
  function burn(uint256 amount) public virtual {
    _burn(_msgSender(), amount);
  }

  /**
   * @dev Destroys `amount` tokens from `account`, deducting from the caller's
   * allowance.
   *
   * See {ERC20-_burn} and {ERC20-allowance}.
   *
   * Requirements:
   *
   * - the caller must have allowance for ``accounts``'s tokens of at least
   * `amount`.
   */
  function burnFrom(address account, uint256 amount) public virtual {
    _spendAllowance(account, _msgSender(), amount);
    _burn(account, amount);
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[50] private __gap;
}
                

contracts/external/openzeppelin/contracts/utils/EnumerableSet.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
  // To implement this library for multiple types with as little code
  // repetition as possible, we write it in terms of a generic Set type with
  // bytes32 values.
  // The Set implementation uses private functions, and user-facing
  // implementations (such as AddressSet) are just wrappers around the
  // underlying Set.
  // This means that we can only create new EnumerableSets for types that fit
  // in bytes32.

  struct Set {
    // Storage of set values
    bytes32[] _values;
    // Position of the value in the `values` array, plus 1 because index 0
    // means a value is not in the set.
    mapping(bytes32 => uint256) _indexes;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function _add(Set storage set, bytes32 value) private returns (bool) {
    if (!_contains(set, value)) {
      set._values.push(value);
      // The value is stored at length-1, but we add 1 to all indexes
      // and use 0 as a sentinel value
      set._indexes[value] = set._values.length;
      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function _remove(Set storage set, bytes32 value) private returns (bool) {
    // We read and store the value's index to prevent multiple reads from the same storage slot
    uint256 valueIndex = set._indexes[value];

    if (valueIndex != 0) {
      // Equivalent to contains(set, value)
      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
      // the array, and then remove the last element (sometimes called as 'swap and pop').
      // This modifies the order of the array, as noted in {at}.

      uint256 toDeleteIndex = valueIndex - 1;
      uint256 lastIndex = set._values.length - 1;

      if (lastIndex != toDeleteIndex) {
        bytes32 lastvalue = set._values[lastIndex];

        // Move the last value to the index where the value to delete is
        set._values[toDeleteIndex] = lastvalue;
        // Update the index for the moved value
        set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
      }

      // Delete the slot where the moved value was stored
      set._values.pop();

      // Delete the index for the deleted slot
      delete set._indexes[value];

      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function _contains(Set storage set, bytes32 value)
    private
    view
    returns (bool)
  {
    return set._indexes[value] != 0;
  }

  /**
   * @dev Returns the number of values on the set. O(1).
   */
  function _length(Set storage set) private view returns (uint256) {
    return set._values.length;
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function _at(Set storage set, uint256 index) private view returns (bytes32) {
    return set._values[index];
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function _values(Set storage set) private view returns (bytes32[] memory) {
    return set._values;
  }

  // Bytes32Set

  struct Bytes32Set {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    return _add(set._inner, value);
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(Bytes32Set storage set, bytes32 value)
    internal
    returns (bool)
  {
    return _remove(set._inner, value);
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(Bytes32Set storage set, bytes32 value)
    internal
    view
    returns (bool)
  {
    return _contains(set._inner, value);
  }

  /**
   * @dev Returns the number of values in the set. O(1).
   */
  function length(Bytes32Set storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(Bytes32Set storage set, uint256 index)
    internal
    view
    returns (bytes32)
  {
    return _at(set._inner, index);
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(Bytes32Set storage set)
    internal
    view
    returns (bytes32[] memory)
  {
    return _values(set._inner);
  }

  // AddressSet

  struct AddressSet {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(AddressSet storage set, address value) internal returns (bool) {
    return _add(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(AddressSet storage set, address value)
    internal
    returns (bool)
  {
    return _remove(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(AddressSet storage set, address value)
    internal
    view
    returns (bool)
  {
    return _contains(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Returns the number of values in the set. O(1).
   */
  function length(AddressSet storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(AddressSet storage set, uint256 index)
    internal
    view
    returns (address)
  {
    return address(uint160(uint256(_at(set._inner, index))));
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(AddressSet storage set)
    internal
    view
    returns (address[] memory)
  {
    bytes32[] memory store = _values(set._inner);
    address[] memory result;

    assembly {
      result := store
    }

    return result;
  }

  // UintSet

  struct UintSet {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(UintSet storage set, uint256 value) internal returns (bool) {
    return _add(set._inner, bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(UintSet storage set, uint256 value) internal returns (bool) {
    return _remove(set._inner, bytes32(value));
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(UintSet storage set, uint256 value)
    internal
    view
    returns (bool)
  {
    return _contains(set._inner, bytes32(value));
  }

  /**
   * @dev Returns the number of values on the set. O(1).
   */
  function length(UintSet storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(UintSet storage set, uint256 index)
    internal
    view
    returns (uint256)
  {
    return uint256(_at(set._inner, index));
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(UintSet storage set)
    internal
    view
    returns (uint256[] memory)
  {
    bytes32[] memory store = _values(set._inner);
    uint256[] memory result;

    assembly {
      result := store
    }

    return result;
  }
}
                

contracts/sanctions/ISanctionsListClient.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/external/chainalysis/ISanctionsList.sol";

/**
 * @title ISanctionsListClient
 * @author Ondo Finance
 * @notice The client interface for sanctions contract.
 */
interface ISanctionsListClient {
  /// @notice Returns reference to the sanctions list that this client queries
  function sanctionsList() external view returns (ISanctionsList);

  /// @notice Sets the sanctions list reference
  function setSanctionsList(address sanctionsList) external;

  /// @notice Error for when caller attempts to set the `sanctionsList`
  ///         reference to the zero address
  error SanctionsListZeroAddress();

  /// @notice Error for when caller attempts to perform an action on a
  ///         sanctioned account
  error SanctionedAccount();

  /**
   * @dev Event for when the sanctions list reference is set
   *
   * @param oldSanctionsList The old list
   * @param newSanctionsList The new list
   */
  event SanctionsListSet(address oldSanctionsList, address newSanctionsList);
}
                

contracts/InstantMintTimeBasedRateLimiter.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

/**
 * @title InstantMintTimeBasedRateLimiter
 *
 * @notice This abstract contract implements two rate limiters: one for minting
 *         and one for redeeming. Each limit is completely independent: mints
 *         and redemption don't offset each other. Each limit is associated
 *         with a duration, after which the tracked amount is reset. The
 *         amounts tracked are agnostic to a specific token; the usage is
 *         determined by the inheriting contracts.
 *
 * @dev Although this contract has all of its functions implemented, this
 *      contract is marked abstract to prevent an accidental deployment and to
 *      signify that we would never deploy this contract standalone.
 *
 */
abstract contract InstantMintTimeBasedRateLimiter {
  // `currentInstantMintAmount` resets after this interval (in seconds)
  uint256 public resetInstantMintDuration;
  // timestamp when `currentInstantMintAmount` was last reset
  uint256 public lastResetInstantMintTime;

  // maximum amount that can be minted during a `resetInstantMintDuration` window
  uint256 public instantMintLimit;
  // amount already minted during the current `resetInstantMintDuration` window
  uint256 public currentInstantMintAmount;

  // `currentInstantRedemptionAmount` resets after this interval (in seconds)
  uint256 public resetInstantRedemptionDuration;
  // timestamp when the `currentInstantRedemptionAmount` was last reset
  uint256 public lastResetInstantRedemptionTime;

  // maximum amount that can be redeemed during a `resetInstantRedemptionDuration` window
  uint256 public instantRedemptionLimit;
  // amount already redeemed during the current `resetInstantRedemptionDuration` window
  uint256 public currentInstantRedemptionAmount;

  /**
   * @notice In the constructor, we initialize the variables for the mint and
   *         redemption rate limiters.
   *
   * @param _instantMintResetDuration   `currentInstantMintAmount` resets after this interval
   *                                    (in seconds)
   * @param _instantRedemptionResetDuration `currentInstantRedemptionAmount` resets after this
   *                                    interval (in seconds)
   * @param _instantMintLimit           maximum amount that can be minted during a
   *                                    `resetInstantMintDuration` window
   * @param _instantRedemptionLimit     maximum amount that can be redeemed during a
   *                                    `resetInstantRedemptionDuration` window
   *
   * @dev If a duration is zero, the limit resets before each mint/redemption.
   * @dev If a limit is zero, the relevant check always fails.
   */
  constructor(
    uint256 _instantMintResetDuration,
    uint256 _instantRedemptionResetDuration,
    uint256 _instantMintLimit,
    uint256 _instantRedemptionLimit
  ) {
    resetInstantMintDuration = _instantMintResetDuration; // can be zero for per-block limit
    resetInstantRedemptionDuration = _instantRedemptionResetDuration; // can be zero for per-block limit
    instantMintLimit = _instantMintLimit; // can be zero to disable minting
    instantRedemptionLimit = _instantRedemptionLimit; // can be zero to disable redemptions

    lastResetInstantMintTime = block.timestamp;
    lastResetInstantRedemptionTime = block.timestamp;
  }

  /**
   * @notice Checks the requested mint amount against the rate limiter (and
   *         updates the remaining amount)
   *
   * @param amount The requested mint amount
   *
   * @dev Reverts if the requested mint amount exceeds the current limit
   */
  function _checkAndUpdateInstantMintLimit(uint256 amount) internal {
    require(amount > 0, "RateLimit: mint amount can't be zero");

    if (
      block.timestamp >= lastResetInstantMintTime + resetInstantMintDuration
    ) {
      // time has passed, reset
      currentInstantMintAmount = 0;
      lastResetInstantMintTime = block.timestamp;
    }
    require(
      amount <= instantMintLimit - currentInstantMintAmount,
      "RateLimit: Mint exceeds rate limit"
    );

    currentInstantMintAmount += amount;
  }

  /**
   * @notice Checks the requested redemption amount against the rate limiter
   *         (and updates the remaining amount)
   *
   * @param amount The requested redemption amount
   *
   * @dev Reverts if the requested redemption amount exceeds the current
   *      limit
   */
  function _checkAndUpdateInstantRedemptionLimit(uint256 amount) internal {
    require(amount > 0, "RateLimit: redemption amount can't be zero");

    if (
      block.timestamp >=
      lastResetInstantRedemptionTime + resetInstantRedemptionDuration
    ) {
      // time has passed, reset
      currentInstantRedemptionAmount = 0;
      lastResetInstantRedemptionTime = block.timestamp;
    }
    require(
      amount <= instantRedemptionLimit - currentInstantRedemptionAmount,
      "RateLimit: Redemption exceeds rate limit"
    );
    currentInstantRedemptionAmount += amount;
  }

  /**
   * @notice Update the amount of token that can be minted during one duration
   *
   * @param _instantMintLimit The token amount
   *
   * @dev If a limit is zero, the relevant check always fails.
   */
  function _setInstantMintLimit(uint256 _instantMintLimit) internal {
    instantMintLimit = _instantMintLimit;
    emit InstantMintLimitSet(_instantMintLimit);
  }

  /**
   * @notice Update the amount of token that can be redeemed during one duration
   *
   * @param _redemptionLimit The token amount
   *
   * @dev If a limit is zero, the relevant check always fails.
   */
  function _setInstantRedemptionLimit(uint256 _redemptionLimit) internal {
    instantRedemptionLimit = _redemptionLimit;
    emit InstantRedemptionLimitSet(_redemptionLimit);
  }

  /**
   * @notice Update the duration for the mint rate limiter
   *
   * @param _instantMintResetDuration The duration in seconds
   *
   * @dev If a duration is zero, the limit resets before each mint/redemption
   */
  function _setInstantMintLimitDuration(
    uint256 _instantMintResetDuration
  ) internal {
    resetInstantMintDuration = _instantMintResetDuration;
    emit InstantMintLimitDurationSet(_instantMintResetDuration);
  }

  /**
   * @notice Update the duration for the redemption rate limiter
   *
   * @param _instantRedemptionResetDuration The duration in seconds
   *
   * @dev If a duration is zero, the limit resets before each mint/redemption
   */
  function _setInstantRedemptionLimitDuration(
    uint256 _instantRedemptionResetDuration
  ) internal {
    resetInstantRedemptionDuration = _instantRedemptionResetDuration;
    emit InstantRedemptionLimitDurationSet(_instantRedemptionResetDuration);
  }

  /**
   * @notice Event emitted when instant mint limit is set
   *
   * @param instantMintLimit How much of some token can be minted within
   *                  an interval of length `resetInstantMintDuration`
   *
   * @dev See inheriting contract for representation
   */
  event InstantMintLimitSet(uint256 instantMintLimit);

  /**
   * @notice Event emitted when instant redemption limit is set
   *
   * @param instantRedemptionLimit How much of some token can be redeemed within
   *                    an interval of length `resetInstantRedemptionDuration`
   *
   * @dev See inheriting contract for representation
   */
  event InstantRedemptionLimitSet(uint256 instantRedemptionLimit);

  /**
   * @notice Event emitted when mint limit duration is set
   *
   * @param instantMintLimitDuration The time window in which `instantMintLimit`
   *                          of some token can be minted
   *
   * @dev instantMintLimitDuration is specified in seconds
   */
  event InstantMintLimitDurationSet(uint256 instantMintLimitDuration);

  /**
   * @notice Event emitted when redemption limit duration is set
   *
   * @param redemptionLimitDuration The time window in which `instantRedemptionLimit`
   *                            of some token can be redeemed
   *
   * @dev redemptionLimitDuration is specified in seconds.
   */
  event InstantRedemptionLimitDurationSet(uint256 redemptionLimitDuration);
}
                

contracts/interfaces/IRWAHubOffChainRedemptions.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐
 */
pragma solidity 0.8.16;

interface IRWAHubOffChainRedemptions {
  function requestRedemptionServicedOffchain(
    uint256 amountRWATokenToRedeem,
    bytes32 offChainDestination
  ) external;

  function pauseOffChainRedemption() external;

  function unpauseOffChainRedemption() external;

  function setOffChainRedemptionMinimum(uint256 minimumAmount) external;

  /**
   * @notice Event emitted when redemption request is submitted
   *
   * @param user                The user submitting the offchain redemption request
   * @param redemptionId        The id corresponding to a given offchain redemption request
   * @param rwaTokenAmountIn    The amount of cash being burned
   * @param offChainDestination Hash of destination to which the request
   *                            should be serviced to
   */
  event RedemptionRequestedServicedOffChain(
    address indexed user,
    bytes32 indexed redemptionId,
    uint256 rwaTokenAmountIn,
    bytes32 offChainDestination
  );

  /**
   * @notice Event emitted when the off chain redemption feature is
   *         paused
   *
   * @param caller Address which initiated the pause
   */
  event OffChainRedemptionPaused(address caller);

  /**
   * @notice Event emitted when the off chain redemption feature is
   *         unpaused
   *
   * @param caller Address which initiated the unpause
   */
  event OffChainRedemptionUnpaused(address caller);

  /**
   * @notice Event emitted when the off chain redemption minimum is
   *         updated
   *
   * @param oldMinimum the old minimum redemption amount
   * @param newMinimum the new minimum redemption amount
   */
  event OffChainRedemptionMinimumSet(uint256 oldMinimum, uint256 newMinimum);
}
                

contracts/external/openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSetUpgradeable {
  // To implement this library for multiple types with as little code
  // repetition as possible, we write it in terms of a generic Set type with
  // bytes32 values.
  // The Set implementation uses private functions, and user-facing
  // implementations (such as AddressSet) are just wrappers around the
  // underlying Set.
  // This means that we can only create new EnumerableSets for types that fit
  // in bytes32.

  struct Set {
    // Storage of set values
    bytes32[] _values;
    // Position of the value in the `values` array, plus 1 because index 0
    // means a value is not in the set.
    mapping(bytes32 => uint256) _indexes;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function _add(Set storage set, bytes32 value) private returns (bool) {
    if (!_contains(set, value)) {
      set._values.push(value);
      // The value is stored at length-1, but we add 1 to all indexes
      // and use 0 as a sentinel value
      set._indexes[value] = set._values.length;
      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function _remove(Set storage set, bytes32 value) private returns (bool) {
    // We read and store the value's index to prevent multiple reads from the same storage slot
    uint256 valueIndex = set._indexes[value];

    if (valueIndex != 0) {
      // Equivalent to contains(set, value)
      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
      // the array, and then remove the last element (sometimes called as 'swap and pop').
      // This modifies the order of the array, as noted in {at}.

      uint256 toDeleteIndex = valueIndex - 1;
      uint256 lastIndex = set._values.length - 1;

      if (lastIndex != toDeleteIndex) {
        bytes32 lastValue = set._values[lastIndex];

        // Move the last value to the index where the value to delete is
        set._values[toDeleteIndex] = lastValue;
        // Update the index for the moved value
        set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
      }

      // Delete the slot where the moved value was stored
      set._values.pop();

      // Delete the index for the deleted slot
      delete set._indexes[value];

      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function _contains(Set storage set, bytes32 value)
    private
    view
    returns (bool)
  {
    return set._indexes[value] != 0;
  }

  /**
   * @dev Returns the number of values on the set. O(1).
   */
  function _length(Set storage set) private view returns (uint256) {
    return set._values.length;
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function _at(Set storage set, uint256 index) private view returns (bytes32) {
    return set._values[index];
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function _values(Set storage set) private view returns (bytes32[] memory) {
    return set._values;
  }

  // Bytes32Set

  struct Bytes32Set {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    return _add(set._inner, value);
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(Bytes32Set storage set, bytes32 value)
    internal
    returns (bool)
  {
    return _remove(set._inner, value);
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(Bytes32Set storage set, bytes32 value)
    internal
    view
    returns (bool)
  {
    return _contains(set._inner, value);
  }

  /**
   * @dev Returns the number of values in the set. O(1).
   */
  function length(Bytes32Set storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(Bytes32Set storage set, uint256 index)
    internal
    view
    returns (bytes32)
  {
    return _at(set._inner, index);
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(Bytes32Set storage set)
    internal
    view
    returns (bytes32[] memory)
  {
    return _values(set._inner);
  }

  // AddressSet

  struct AddressSet {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(AddressSet storage set, address value) internal returns (bool) {
    return _add(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(AddressSet storage set, address value)
    internal
    returns (bool)
  {
    return _remove(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(AddressSet storage set, address value)
    internal
    view
    returns (bool)
  {
    return _contains(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Returns the number of values in the set. O(1).
   */
  function length(AddressSet storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(AddressSet storage set, uint256 index)
    internal
    view
    returns (address)
  {
    return address(uint160(uint256(_at(set._inner, index))));
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(AddressSet storage set)
    internal
    view
    returns (address[] memory)
  {
    bytes32[] memory store = _values(set._inner);
    address[] memory result;

    /// @solidity memory-safe-assembly
    assembly {
      result := store
    }

    return result;
  }

  // UintSet

  struct UintSet {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(UintSet storage set, uint256 value) internal returns (bool) {
    return _add(set._inner, bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(UintSet storage set, uint256 value) internal returns (bool) {
    return _remove(set._inner, bytes32(value));
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(UintSet storage set, uint256 value)
    internal
    view
    returns (bool)
  {
    return _contains(set._inner, bytes32(value));
  }

  /**
   * @dev Returns the number of values on the set. O(1).
   */
  function length(UintSet storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(UintSet storage set, uint256 index)
    internal
    view
    returns (uint256)
  {
    return uint256(_at(set._inner, index));
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(UintSet storage set)
    internal
    view
    returns (uint256[] memory)
  {
    bytes32[] memory store = _values(set._inner);
    uint256[] memory result;

    /// @solidity memory-safe-assembly
    assembly {
      result := store
    }

    return result;
  }
}
                

contracts/external/openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
  /**
   * @dev Emitted when the pause is triggered by `account`.
   */
  event Paused(address account);

  /**
   * @dev Emitted when the pause is lifted by `account`.
   */
  event Unpaused(address account);

  bool private _paused;

  /**
   * @dev Initializes the contract in unpaused state.
   */
  function __Pausable_init() internal onlyInitializing {
    __Pausable_init_unchained();
  }

  function __Pausable_init_unchained() internal onlyInitializing {
    _paused = false;
  }

  /**
   * @dev Modifier to make a function callable only when the contract is not paused.
   *
   * Requirements:
   *
   * - The contract must not be paused.
   */
  modifier whenNotPaused() {
    _requireNotPaused();
    _;
  }

  /**
   * @dev Modifier to make a function callable only when the contract is paused.
   *
   * Requirements:
   *
   * - The contract must be paused.
   */
  modifier whenPaused() {
    _requirePaused();
    _;
  }

  /**
   * @dev Returns true if the contract is paused, and false otherwise.
   */
  function paused() public view virtual returns (bool) {
    return _paused;
  }

  /**
   * @dev Throws if the contract is paused.
   */
  function _requireNotPaused() internal view virtual {
    require(!paused(), "Pausable: paused");
  }

  /**
   * @dev Throws if the contract is not paused.
   */
  function _requirePaused() internal view virtual {
    require(paused(), "Pausable: not paused");
  }

  /**
   * @dev Triggers stopped state.
   *
   * Requirements:
   *
   * - The contract must not be paused.
   */
  function _pause() internal virtual whenNotPaused {
    _paused = true;
    emit Paused(_msgSender());
  }

  /**
   * @dev Returns to normal state.
   *
   * Requirements:
   *
   * - The contract must be paused.
   */
  function _unpause() internal virtual whenPaused {
    _paused = false;
    emit Unpaused(_msgSender());
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[49] private __gap;
}
                

contracts/interfaces/IRWAHub.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐
 */
pragma solidity 0.8.16;

interface IRWAHub {
  // Struct to contain the deposit information for a given depositId
  struct Depositor {
    address user;
    uint256 amountDepositedMinusFees;
    uint256 priceId;
  }

  // Struc to contain withdrawal infromation for a given redemptionId
  struct Redeemer {
    address user;
    uint256 amountRwaTokenBurned;
    uint256 priceId;
  }

  function requestSubscription(uint256 amount) external;

  function claimMint(bytes32[] calldata depositIds) external;

  function requestRedemption(uint256 amount) external;

  function claimRedemption(bytes32[] calldata redemptionIds) external;

  function addProof(
    bytes32 txHash,
    address user,
    uint256 depositAmountAfterFee,
    uint256 feeAmount,
    uint256 timestamp
  ) external;

  function setPriceIdForDeposits(
    bytes32[] calldata depositIds,
    uint256[] calldata priceIds
  ) external;

  function setPriceIdForRedemptions(
    bytes32[] calldata redemptionIds,
    uint256[] calldata priceIds
  ) external;

  function setPricer(address newPricer) external;

  function overwriteDepositor(
    bytes32 depositIdToOverride,
    address user,
    uint256 depositAmountAfterFee,
    uint256 priceId
  ) external;

  function overwriteRedeemer(
    bytes32 redemptionIdToOverride,
    address user,
    uint256 rwaTokenAmountBurned,
    uint256 priceId
  ) external;

  /**
   * @notice Event emitted when fee recipient is set
   *
   * @param oldFeeRecipient Old fee recipient
   * @param newFeeRecipient New fee recipient
   */
  event FeeRecipientSet(address oldFeeRecipient, address newFeeRecipient);

  /**
   * @notice Event emitted when the assetSender is changed
   *
   * @param oldAssetSender The address of the old assetSender
   * @param newAssetSender The address of the new assetSender
   */
  event AssetSenderSet(address oldAssetSender, address newAssetSender);

  /**
   * @notice Event emitted when minimum deposit amount is set
   *
   * @param oldMinimum Old minimum
   * @param newMinimum New minimum
   *
   * @dev See inheriting contract for decimals representation
   */
  event MinimumDepositAmountSet(uint256 oldMinimum, uint256 newMinimum);

  /**
   * @notice Event emitted when a new redeem minimum is set.
   *         All units are in 1e18
   *
   * @param oldRedemptionMin The old redeem minimum value
   * @param newRedemptionMin The new redeem minimum value
   */
  event MinimumRedemptionAmountSet(
    uint256 oldRedemptionMin,
    uint256 newRedemptionMin
  );

  /**
   * @notice Event emitted when mint fee is set
   *
   * @param oldFee Old fee
   * @param newFee New fee
   *
   * @dev See inheriting contract for decimals representation
   */
  event MintFeeSet(uint256 oldFee, uint256 newFee);

  /**
   * @notice Event emitted when redeem fee is set
   *
   * @param oldFee Old fee
   * @param newFee New fee
   *
   * @dev see inheriting contract for decimal representation
   */
  event RedemptionFeeSet(uint256 oldFee, uint256 newFee);

  /**
   * @notice Event emitted when redemption request is submitted
   *
   * @param user         The user submitting the redemption request
   * @param redemptionId The id corresponding to a given redemption
   * @param rwaAmountIn  The amount of cash being burned
   */
  event RedemptionRequested(
    address indexed user,
    bytes32 indexed redemptionId,
    uint256 rwaAmountIn
  );

  /**
   * @notice Event emitted when a mint request is submitted
   *
   * @param user                      The user requesting to mint
   * @param depositId                 The depositId of the request
   * @param collateralAmountDeposited The total amount deposited
   * @param depositAmountAfterFee     The value deposited - fee
   * @param feeAmount                 The fee amount taken
   *                                  (units of collateral)
   */
  event MintRequested(
    address indexed user,
    bytes32 indexed depositId,
    uint256 collateralAmountDeposited,
    uint256 depositAmountAfterFee,
    uint256 feeAmount
  );

  /**
   * @notice Event emitted when a redemption request is completed
   *
   * @param user                     The address of the user getting the funds
   * @param redemptionId             The id corresponding to a given redemption
   *                                 requested
   * @param rwaAmountRequested       Amount of RWA originally requested by the user
   * @param collateralAmountReturned Amount of collateral received by the user
   * @param feeAmount                Amount of fees taken
   * @param price                    The price at which the redemption was
   *                                 serviced at
   * @param priceId                  The priceId of the used redemption price
   */
  event RedemptionCompleted(
    address indexed user,
    bytes32 indexed redemptionId,
    uint256 rwaAmountRequested,
    uint256 collateralAmountReturned,
    uint256 feeAmount,
    uint256 price,
    uint256 priceId
  );

  /**
   * @notice Event emitted when a Mint request is completed
   *
   * @param user                      The address of the user getting the funds
   * @param depositId                 The depositId of the mint request
   * @param rwaAmountOut              The amount of RWA token minted to the
   *                                  user
   * @param collateralAmountDeposited The amount of collateral deposited
   * @param price                     The price set for the given
   *                                  deposit id
   * @param priceId                   The priceId used to determine price
   */
  event MintCompleted(
    address indexed user,
    bytes32 indexed depositId,
    uint256 rwaAmountOut,
    uint256 collateralAmountDeposited,
    uint256 price,
    uint256 priceId
  );

  /**
   * @notice Event emitted when a deposit has its corresponding priceId set
   *
   * @param depositIdSet The Deposit Id for which the price Id is being set
   * @param priceIdSet   The price Id being associate with a deposit Id
   */
  event PriceIdSetForDeposit(
    bytes32 indexed depositIdSet,
    uint256 indexed priceIdSet
  );

  /**
   * @notice Event Emitted when a redemption has its corresponding priceId set
   *
   * @param redemptionIdSet The Redemption Id for which the price Id is being
   *                        set
   * @param priceIdSet      The Price Id being associated with a redemption Id
   */
  event PriceIdSetForRedemption(
    bytes32 indexed redemptionIdSet,
    uint256 indexed priceIdSet
  );

  /**
   * @notice Event emitted when a new Pricer contract is set
   *
   * @param oldPricer The address of the old pricer contract
   * @param newPricer The address of the new pricer contract
   */
  event NewPricerSet(address oldPricer, address newPricer);

  /**
   * @notice Event emitted when deposit proof has been added
   *
   * @param txHash                Tx hash of the deposit
   * @param user                  Address of the user who made the deposit
   * @param depositAmountAfterFee Amount of the deposit after fees
   * @param feeAmount             Amount of fees taken
   * @param timestamp             Timestamp of the deposit
   */
  event DepositProofAdded(
    bytes32 indexed txHash,
    address indexed user,
    uint256 depositAmountAfterFee,
    uint256 feeAmount,
    uint256 timestamp
  );

  /**
   * @notice Event emitted when subscriptions are paused
   *
   * @param caller Address which initiated the pause
   */
  event SubscriptionPaused(address caller);

  /**
   * @notice Event emitted when redemptions are paused
   *
   * @param caller Address which initiated the pause
   */
  event RedemptionPaused(address caller);

  /**
   * @notice Event emitted when subscriptions are unpaused
   *
   * @param caller Address which initiated the unpause
   */
  event SubscriptionUnpaused(address caller);

  /**
   * @notice Event emitted when redemptions are unpaused
   *
   * @param caller Address which initiated the unpause
   */
  event RedemptionUnpaused(address caller);

  event DepositorOverwritten(
    bytes32 indexed depositId,
    address oldDepositor,
    address newDepositor,
    uint256 oldPriceId,
    uint256 newPriceId,
    uint256 oldDepositAmount,
    uint256 newDepositAmount
  );

  event RedeemerOverwritten(
    bytes32 indexed redemptionId,
    address oldRedeemer,
    address newRedeemer,
    uint256 oldPriceId,
    uint256 newPriceId,
    uint256 oldRWATokenAmountBurned,
    uint256 newRWATokenAmountBurned
  );

  /// ERRORS ///
  error PriceIdNotSet();
  error ArraySizeMismatch();
  error DepositTooSmall();
  error RedemptionTooSmall();
  error TxnAlreadyValidated();
  error CollateralCannotBeZero();
  error RWACannotBeZero();
  error AssetSenderCannotBeZero();
  error FeeRecipientCannotBeZero();
  error FeeTooLarge();
  error AmountTooSmall();
  error DepositorNull();
  error RedeemerNull();
  error DepositProofAlreadyExists();
  error RedemptionProofAlreadyExists();
  error FeaturePaused();
  error PriceIdAlreadySet();
}
                

contracts/ommf/wrappedOMMF/wOMMF_factory.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

// Proxy admin contract used in OZ upgrades plugin
import "contracts/external/openzeppelin/contracts/proxy/ProxyAdmin.sol";
import "contracts/Proxy.sol";
import "contracts/ommf/wrappedOMMF/wOMMF.sol";
import "contracts/interfaces/IMulticall.sol";

/**
 * @title wommfFactory
 * @author Ondo Finance
 * @notice This contract serves as a Factory for the upgradable wOMMF token contract.
 *         Upon calling `deployWOMMF` the `guardian` address (set in constructor) will
 *         deploy the following:
 *         1) wOMMF - The implementation contract, ERC20 contract with the initializer disabled
 *         2) ProxyAdmin - OZ ProxyAdmin contract, used to upgrade the proxy instance.
 *                         @notice Owner is set to `guardian` address.
 *         3) TransparentUpgradeableProxy - OZ, proxy contract. Admin is set to `address(proxyAdmin)`.
 *                                          `_logic' is set to `address(rwa)`.
 *
 *         Following the above mentioned deployment, the address of the RWAFactory contract will:
 *         i) Transfer ownership of the ProxyAdmin to that of the `guardian` address.
 *         ii) Emit an event detailing the addresses on the upgradable contract array.
 *
 * @notice `guardian` address in constructor is a msig.
 */
contract WOMMFFactory is IMulticall {
  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

  address internal immutable guardian;
  WOMMF public wommfImplementation;
  ProxyAdmin public wommfProxyAdmin;
  TokenProxy public wommfProxy;

  constructor(address _guardian) {
    guardian = _guardian;
  }

  /**
   * @dev This function will deploy an upgradable instance of RWA
   *
   * @param name             The name of the token we want to deploy.
   * @param ticker           The ticker for the token we want to deploy.
   * @param ommfAddress      The address of the OMMF token to wrap
   * @param registry         The address of the KYC Registry
   * @param requirementGroup The KYC requirement group for this token
   *
   * @return address The address of the proxy contract.
   * @return address The address of the proxyAdmin contract.
   * @return address The address of the implementation contract.
   *
   * @notice 1) Will automatically revoke all deployer roles granted to
   *            address(this).
   *         2) Will grant DEFAULT_ADMIN & PAUSER_ROLE(S) to `guardian`
   *            address specified in constructor.
   *         3) Will transfer ownership of the proxyAdmin to guardian
   *            address.
   *
   */
  function deployWOMMF(
    string calldata name,
    string calldata ticker,
    address ommfAddress,
    address registry,
    uint256 requirementGroup
  ) external onlyGuardian returns (address, address, address) {
    wommfImplementation = new WOMMF();
    wommfProxyAdmin = new ProxyAdmin();
    wommfProxy = new TokenProxy(
      address(wommfImplementation),
      address(wommfProxyAdmin),
      ""
    );
    WOMMF wOMMFProxied = WOMMF(address(wommfProxy));
    wOMMFProxied.initialize(
      guardian,
      name,
      ticker,
      ommfAddress,
      registry,
      requirementGroup
    );
    wOMMFProxied.revokeRole(MINTER_ROLE, address(this));
    wOMMFProxied.revokeRole(PAUSER_ROLE, address(this));
    wOMMFProxied.revokeRole(DEFAULT_ADMIN_ROLE, address(this));

    wommfProxyAdmin.transferOwnership(guardian);
    assert(wommfProxyAdmin.owner() == guardian);
    emit WOMMFDeployed(
      address(wOMMFProxied),
      address(wommfProxyAdmin),
      address(wommfImplementation),
      name,
      ticker
    );
    return (
      address(wOMMFProxied),
      address(wommfProxyAdmin),
      address(wommfImplementation)
    );
  }

  /**
   * @notice Allows for arbitrary batched calls
   *
   * @dev All external calls made through this function will
   *      msg.sender == contract address
   *
   * @param exCallData Struct consisting of
   *       1) target - contract to call
   *       2) data - data to call target with
   *       3) value - eth value to call target with
   */
  function multiexcall(
    ExCallData[] calldata exCallData
  ) external payable override onlyGuardian returns (bytes[] memory results) {
    results = new bytes[](exCallData.length);
    for (uint256 i = 0; i < exCallData.length; ++i) {
      (bool success, bytes memory ret) = address(exCallData[i].target).call{
        value: exCallData[i].value
      }(exCallData[i].data);
      require(success, "Call Failed");
      results[i] = ret;
    }
  }

  /**
   * @dev Event emitted when upgradable RWA token is deployed
   *
   * @param proxy             The address for the proxy contract
   * @param proxyAdmin        The address for the proxy admin contract
   * @param implementation    The address for the implementation contract
   */
  event WOMMFDeployed(
    address proxy,
    address proxyAdmin,
    address implementation,
    string name,
    string ticker
  );

  modifier onlyGuardian() {
    require(msg.sender == guardian, "WOMMFFactory: You are not the Guardian");
    _;
  }
}
                

contracts/usdy/blocklist/BlocklistClientUpgradeable.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/interfaces/IBlocklist.sol";
import "contracts/interfaces/IBlocklistClient.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @title BlocklistClient
 * @author Ondo Finance
 * @notice This abstract contract manages state for upgradeable blocklist
 *         clients
 */
abstract contract BlocklistClientUpgradeable is
  Initializable,
  IBlocklistClient
{
  // Blocklist contract
  IBlocklist public override blocklist;

  /**
   * @notice Initialize the contract by setting blocklist variable
   *
   * @param _blocklist Address of the blocklist contract
   *
   * @dev Function should be called by the inheriting contract on
   *      initialization
   */
  function __BlocklistClientInitializable_init(
    address _blocklist
  ) internal onlyInitializing {
    __BlocklistClientInitializable_init_unchained(_blocklist);
  }

  /**
   * @dev Internal function to future-proof parent linearization. Matches OZ
   *      upgradeable suggestions
   */
  function __BlocklistClientInitializable_init_unchained(
    address _blocklist
  ) internal onlyInitializing {
    _setBlocklist(_blocklist);
  }

  /**
   * @notice Sets the blocklist address for this client
   *
   * @param _blocklist The new blocklist address
   */
  function _setBlocklist(address _blocklist) internal {
    if (_blocklist == address(0)) {
      revert BlocklistZeroAddress();
    }
    address oldBlocklist = address(blocklist);
    blocklist = IBlocklist(_blocklist);
    emit BlocklistSet(oldBlocklist, _blocklist);
  }

  /**
   * @notice Checks whether an address has been blocked
   *
   * @param account The account to check
   */
  function _isBlocked(address account) internal view returns (bool) {
    return blocklist.isBlocked(account);
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[50] private __gap;
}
                

contracts/interfaces/IOmmf.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */

import "contracts/external/openzeppelin/contracts/token/IERC20.sol";

pragma solidity 0.8.16; // latest available for using OZ

interface IOMMF is IERC20 {
  function getBalanceOfByShares(uint256) external view returns (uint256);

  function getSharesByPooledCash(uint256) external view returns (uint256);
}
                

contracts/external/openzeppelin/contracts/utils/ERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts/utils/IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
  /**
   * @dev See {IERC165-supportsInterface}.
   */
  function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    override
    returns (bool)
  {
    return interfaceId == type(IERC165).interfaceId;
  }
}
                

contracts/external/openzeppelin/contracts/utils/Address.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
  /**
   * @dev Returns true if `account` is a contract.
   *
   * [IMPORTANT]
   * ====
   * It is unsafe to assume that an address for which this function returns
   * false is an externally-owned account (EOA) and not a contract.
   *
   * Among others, `isContract` will return false for the following
   * types of addresses:
   *
   *  - an externally-owned account
   *  - a contract in construction
   *  - an address where a contract will be created
   *  - an address where a contract lived, but was destroyed
   * ====
   *
   * [IMPORTANT]
   * ====
   * You shouldn't rely on `isContract` to protect against flash loan attacks!
   *
   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
   * constructor.
   * ====
   */
  function isContract(address account) internal view returns (bool) {
    // This method relies on extcodesize/address.code.length, which returns 0
    // for contracts in construction, since the code is only stored at the end
    // of the constructor execution.

    return account.code.length > 0;
  }

  /**
   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
   * `recipient`, forwarding all available gas and reverting on errors.
   *
   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
   * of certain opcodes, possibly making contracts go over the 2300 gas limit
   * imposed by `transfer`, making them unable to receive funds via
   * `transfer`. {sendValue} removes this limitation.
   *
   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
   *
   * IMPORTANT: because control is transferred to `recipient`, care must be
   * taken to not create reentrancy vulnerabilities. Consider using
   * {ReentrancyGuard} or the
   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
   */
  function sendValue(address payable recipient, uint256 amount) internal {
    require(address(this).balance >= amount, "Address: insufficient balance");

    (bool success, ) = recipient.call{value: amount}("");
    require(
      success,
      "Address: unable to send value, recipient may have reverted"
    );
  }

  /**
   * @dev Performs a Solidity function call using a low level `call`. A
   * plain `call` is an unsafe replacement for a function call: use this
   * function instead.
   *
   * If `target` reverts with a revert reason, it is bubbled up by this
   * function (like regular Solidity function calls).
   *
   * Returns the raw returned data. To convert to the expected return value,
   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
   *
   * Requirements:
   *
   * - `target` must be a contract.
   * - calling `target` with `data` must not revert.
   *
   * _Available since v3.1._
   */
  function functionCall(address target, bytes memory data)
    internal
    returns (bytes memory)
  {
    return functionCall(target, data, "Address: low-level call failed");
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
   * `errorMessage` as a fallback revert reason when `target` reverts.
   *
   * _Available since v3.1._
   */
  function functionCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    return functionCallWithValue(target, data, 0, errorMessage);
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
   * but also transferring `value` wei to `target`.
   *
   * Requirements:
   *
   * - the calling contract must have an ETH balance of at least `value`.
   * - the called Solidity function must be `payable`.
   *
   * _Available since v3.1._
   */
  function functionCallWithValue(
    address target,
    bytes memory data,
    uint256 value
  ) internal returns (bytes memory) {
    return
      functionCallWithValue(
        target,
        data,
        value,
        "Address: low-level call with value failed"
      );
  }

  /**
   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
   * with `errorMessage` as a fallback revert reason when `target` reverts.
   *
   * _Available since v3.1._
   */
  function functionCallWithValue(
    address target,
    bytes memory data,
    uint256 value,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(
      address(this).balance >= value,
      "Address: insufficient balance for call"
    );
    require(isContract(target), "Address: call to non-contract");

    (bool success, bytes memory returndata) = target.call{value: value}(data);
    return verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
   * but performing a static call.
   *
   * _Available since v3.3._
   */
  function functionStaticCall(address target, bytes memory data)
    internal
    view
    returns (bytes memory)
  {
    return
      functionStaticCall(target, data, "Address: low-level static call failed");
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
   * but performing a static call.
   *
   * _Available since v3.3._
   */
  function functionStaticCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal view returns (bytes memory) {
    require(isContract(target), "Address: static call to non-contract");

    (bool success, bytes memory returndata) = target.staticcall(data);
    return verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
   * but performing a delegate call.
   *
   * _Available since v3.4._
   */
  function functionDelegateCall(address target, bytes memory data)
    internal
    returns (bytes memory)
  {
    return
      functionDelegateCall(
        target,
        data,
        "Address: low-level delegate call failed"
      );
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
   * but performing a delegate call.
   *
   * _Available since v3.4._
   */
  function functionDelegateCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(isContract(target), "Address: delegate call to non-contract");

    (bool success, bytes memory returndata) = target.delegatecall(data);
    return verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
   * revert reason using the provided one.
   *
   * _Available since v4.3._
   */
  function verifyCallResult(
    bool success,
    bytes memory returndata,
    string memory errorMessage
  ) internal pure returns (bytes memory) {
    if (success) {
      return returndata;
    } else {
      // Look for revert reason and bubble it up if present
      if (returndata.length > 0) {
        // The easiest way to bubble the revert reason is using memory via assembly

        assembly {
          let returndata_size := mload(returndata)
          revert(add(32, returndata), returndata_size)
        }
      } else {
        revert(errorMessage);
      }
    }
  }
}
                

contracts/external/openzeppelin/contracts/utils/IERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
  /**
   * @dev Returns true if this contract implements the interface defined by
   * `interfaceId`. See the corresponding
   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
   * to learn more about how these ids are created.
   *
   * This function call must use less than 30 000 gas.
   */
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
                

contracts/external/openzeppelin/contracts-upgradeable/utils/CounterUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library CountersUpgradeable {
  struct Counter {
    // This variable should never be directly accessed by users of the library: interactions must be restricted to
    // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
    // this feature: see https://github.com/ethereum/solidity/issues/4637
    uint256 _value; // default: 0
  }

  function current(Counter storage counter) internal view returns (uint256) {
    return counter._value;
  }

  function increment(Counter storage counter) internal {
    unchecked {
      counter._value += 1;
    }
  }

  function decrement(Counter storage counter) internal {
    uint256 value = counter._value;
    require(value > 0, "Counter: decrement overflow");
    unchecked {
      counter._value = value - 1;
    }
  }

  function reset(Counter storage counter) internal {
    counter._value = 0;
  }
}
                

contracts/external/openzeppelin/contracts/proxy/ERC1967Proxy.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts/proxy/Proxy.sol";
import "contracts/external/openzeppelin/contracts/proxy/ERC1967Upgrade.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 */
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
  /**
   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
   *
   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
   */
  constructor(address _logic, bytes memory _data) payable {
    assert(
      _IMPLEMENTATION_SLOT ==
        bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
    );
    _upgradeToAndCall(_logic, _data, false);
  }

  /**
   * @dev Returns the current implementation address.
   */
  function _implementation()
    internal
    view
    virtual
    override
    returns (address impl)
  {
    return ERC1967Upgrade._getImplementation();
  }
}
                

contracts/external/openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
  /**
   * @dev Returns true if `account` is a contract.
   *
   * [IMPORTANT]
   * ====
   * It is unsafe to assume that an address for which this function returns
   * false is an externally-owned account (EOA) and not a contract.
   *
   * Among others, `isContract` will return false for the following
   * types of addresses:
   *
   *  - an externally-owned account
   *  - a contract in construction
   *  - an address where a contract will be created
   *  - an address where a contract lived, but was destroyed
   * ====
   *
   * [IMPORTANT]
   * ====
   * You shouldn't rely on `isContract` to protect against flash loan attacks!
   *
   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
   * constructor.
   * ====
   */
  function isContract(address account) internal view returns (bool) {
    // This method relies on extcodesize/address.code.length, which returns 0
    // for contracts in construction, since the code is only stored at the end
    // of the constructor execution.

    return account.code.length > 0;
  }

  /**
   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
   * `recipient`, forwarding all available gas and reverting on errors.
   *
   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
   * of certain opcodes, possibly making contracts go over the 2300 gas limit
   * imposed by `transfer`, making them unable to receive funds via
   * `transfer`. {sendValue} removes this limitation.
   *
   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
   *
   * IMPORTANT: because control is transferred to `recipient`, care must be
   * taken to not create reentrancy vulnerabilities. Consider using
   * {ReentrancyGuard} or the
   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
   */
  function sendValue(address payable recipient, uint256 amount) internal {
    require(address(this).balance >= amount, "Address: insufficient balance");

    (bool success, ) = recipient.call{value: amount}("");
    require(
      success,
      "Address: unable to send value, recipient may have reverted"
    );
  }

  /**
   * @dev Performs a Solidity function call using a low level `call`. A
   * plain `call` is an unsafe replacement for a function call: use this
   * function instead.
   *
   * If `target` reverts with a revert reason, it is bubbled up by this
   * function (like regular Solidity function calls).
   *
   * Returns the raw returned data. To convert to the expected return value,
   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
   *
   * Requirements:
   *
   * - `target` must be a contract.
   * - calling `target` with `data` must not revert.
   *
   * _Available since v3.1._
   */
  function functionCall(address target, bytes memory data)
    internal
    returns (bytes memory)
  {
    return functionCall(target, data, "Address: low-level call failed");
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
   * `errorMessage` as a fallback revert reason when `target` reverts.
   *
   * _Available since v3.1._
   */
  function functionCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    return functionCallWithValue(target, data, 0, errorMessage);
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
   * but also transferring `value` wei to `target`.
   *
   * Requirements:
   *
   * - the calling contract must have an ETH balance of at least `value`.
   * - the called Solidity function must be `payable`.
   *
   * _Available since v3.1._
   */
  function functionCallWithValue(
    address target,
    bytes memory data,
    uint256 value
  ) internal returns (bytes memory) {
    return
      functionCallWithValue(
        target,
        data,
        value,
        "Address: low-level call with value failed"
      );
  }

  /**
   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
   * with `errorMessage` as a fallback revert reason when `target` reverts.
   *
   * _Available since v3.1._
   */
  function functionCallWithValue(
    address target,
    bytes memory data,
    uint256 value,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(
      address(this).balance >= value,
      "Address: insufficient balance for call"
    );
    require(isContract(target), "Address: call to non-contract");

    (bool success, bytes memory returndata) = target.call{value: value}(data);
    return verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
   * but performing a static call.
   *
   * _Available since v3.3._
   */
  function functionStaticCall(address target, bytes memory data)
    internal
    view
    returns (bytes memory)
  {
    return
      functionStaticCall(target, data, "Address: low-level static call failed");
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
   * but performing a static call.
   *
   * _Available since v3.3._
   */
  function functionStaticCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal view returns (bytes memory) {
    require(isContract(target), "Address: static call to non-contract");

    (bool success, bytes memory returndata) = target.staticcall(data);
    return verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
   * revert reason using the provided one.
   *
   * _Available since v4.3._
   */
  function verifyCallResult(
    bool success,
    bytes memory returndata,
    string memory errorMessage
  ) internal pure returns (bytes memory) {
    if (success) {
      return returndata;
    } else {
      // Look for revert reason and bubble it up if present
      if (returndata.length > 0) {
        // The easiest way to bubble the revert reason is using memory via assembly
        /// @solidity memory-safe-assembly
        assembly {
          let returndata_size := mload(returndata)
          revert(add(32, returndata), returndata_size)
        }
      } else {
        revert(errorMessage);
      }
    }
  }
}
                

contracts/interfaces/IAxelarGateway.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IAxelarGateway {
  function callContract(
    string calldata destinationChain,
    string calldata contractAddress,
    bytes calldata payload
  ) external;

  function validateContractCall(
    bytes32 commandId,
    string calldata sourceChain,
    string calldata sourceAddress,
    bytes32 payloadHash
  ) external returns (bool);

  function validateContractCallAndMint(
    bytes32 commandId,
    string calldata sourceChain,
    string calldata sourceAddress,
    bytes32 payloadHash,
    string calldata symbol,
    uint256 amount
  ) external returns (bool);
}
                

contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
  function __Context_init() internal onlyInitializing {}

  function __Context_init_unchained() internal onlyInitializing {}

  function _msgSender() internal view virtual returns (address) {
    return msg.sender;
  }

  function _msgData() internal view virtual returns (bytes calldata) {
    return msg.data;
  }

  /**
   * @dev This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[50] private __gap;
}
                

contracts/external/openzeppelin/contracts/proxy/Proxy.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol)

pragma solidity ^0.8.0;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
  /**
   * @dev Delegates the current call to `implementation`.
   *
   * This function does not return to its internal call site, it will return directly to the external caller.
   */
  function _delegate(address implementation) internal virtual {
    assembly {
      // Copy msg.data. We take full control of memory in this inline assembly
      // block because it will not return to Solidity code. We overwrite the
      // Solidity scratch pad at memory position 0.
      calldatacopy(0, 0, calldatasize())

      // Call the implementation.
      // out and outsize are 0 because we don't know the size yet.
      let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

      // Copy the returned data.
      returndatacopy(0, 0, returndatasize())

      switch result
        // delegatecall returns 0 on error.
        case 0 {
          revert(0, returndatasize())
        }
        default {
          return(0, returndatasize())
        }
    }
  }

  /**
   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
   * and {_fallback} should delegate.
   */
  function _implementation() internal view virtual returns (address);

  /**
   * @dev Delegates the current call to the address returned by `_implementation()`.
   *
   * This function does not return to its internall call site, it will return directly to the external caller.
   */
  function _fallback() internal virtual {
    _beforeFallback();
    _delegate(_implementation());
  }

  /**
   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
   * function in the contract matches the call data.
   */
  fallback() external payable {
    _fallback();
  }

  /**
   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
   * is empty.
   */
  receive() external payable {
    _fallback();
  }

  /**
   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
   * call, or as part of the Solidity `fallback` or `receive` functions.
   *
   * If overriden should call `super._beforeFallback()`.
   */
  function _beforeFallback() internal virtual {}
}
                

contracts/rwaOracles/RWAOracleExternalComparisonCheck.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;
import "contracts/external/chainlink/AggregatorV3Interface.sol";
import "contracts/rwaOracles/IRWAOracleExternalComparisonCheck.sol";
import "contracts/external/openzeppelin/contracts/access/AccessControlEnumerable.sol";

contract RWAOracleExternalComparisonCheck is
  IRWAOracleExternalComparisonCheck,
  AccessControlEnumerable
{
  /// @notice Helper struct for storing SHV data from Chainlink
  struct ChainlinkRoundData {
    uint80 roundId;
    int256 answer;
    uint256 startedAt;
    uint256 updatedAt;
    uint80 answeredInRound;
  }

  // Price of RWA token (OUSG, OSTB, OHYG, etc.)
  int256 public rwaPrice;

  // Timestamp in which the RWA token price was last set
  uint256 public priceTimestamp;

  // The associated Chainlink price update associated with the stored
  // `rwaPrice`
  ChainlinkRoundData public lastSetRound;

  // Chainlink oracle whose tracked instrument is used to constrain price updates.
  AggregatorV3Interface public immutable chainlinkOracle;

  // Description of oracle set in Constructor
  string public description;

  // How recent a Chainlink update needs to be in order to be associated to
  // a RWA price change.
  uint256 public constant MAX_CL_WINDOW = 25 hours;

  // Helper constant that allows us to specify basis points in calculations
  int256 public constant BPS_DENOMINATOR = 10_000;

  // Amount of bps that the RWA price can differ from the SHV change. For
  // example, if SHV changes by 1% in between RWA price updates,
  // RWA token can change between .26% and 1.74%
  uint256 public constant MAX_CHANGE_DIFF_BPS = 74;

  // Max amount of bps that RWA price in a single price update.
  uint256 public constant MAX_ABSOLUTE_DIFF_BPS = 200;

  // Minimum time between price updates
  uint256 public constant MIN_PRICE_UPDATE_WINDOW = 23 hours;

  /// @notice How many decimals `rwaPrice` is represented in
  /// @dev UNUSED AND UNENFORCED - This is present only for operational
  ///      clarity.
  uint256 public constant decimals = 18;

  // Role that can set RWA price
  bytes32 public constant SETTER_ROLE = keccak256("SETTER_ROLE");

  /**
   * @notice constructor
   *
   * @param _initialPrice     The initial RWA price
   * @param _chainlinkOracle Chainlink oracle to compare differences with
   * @param _description     Human readable description
   * @param _admin           admin which holds the DEFAULT_ADMIN_ROLE
   * @param _setter          setter address which holds the role to set rwa price
   */
  constructor(
    int256 _initialPrice,
    address _chainlinkOracle,
    string memory _description,
    address _admin,
    address _setter
  ) {
    if (_admin == address(0) || _setter == address(0)) {
      revert InvalidAddress();
    }
    chainlinkOracle = AggregatorV3Interface(_chainlinkOracle);
    // Revert if Chainlink oracle is not reporting 8 decimals
    if (chainlinkOracle.decimals() != 8) {
      revert CorruptedChainlinkResponse();
    }

    ChainlinkRoundData memory round = _getLatestChainlinkRoundData();
    if (block.timestamp > round.updatedAt + MAX_CL_WINDOW) {
      revert ChainlinkOraclePriceStale();
    }

    description = _description;
    rwaPrice = _initialPrice;
    priceTimestamp = block.timestamp;
    lastSetRound = round;

    _grantRole(DEFAULT_ADMIN_ROLE, _admin);
    _grantRole(SETTER_ROLE, _setter);
  }

  /**
   * @notice Retrieve the last set RWA price data
   *
   * @dev `price` is in 18 decimals, `timestamp` is unix seconds since epoch
   */
  function getPriceData()
    external
    view
    override
    returns (uint256 price, uint256 timestamp)
  {
    price = uint256(rwaPrice);
    timestamp = priceTimestamp;
  }

  /**
   * @notice Sets the price of RWA if all the following criteria are met:
   *  - It is able to pull a consistent and recent Chainlink price that is
   *    different than the last used Chainlink round
   *  - The price wasn't updated too recently (`MIN_PRICE_UPDATE_WINDOW`
   *    seconds)
   *  - The change in RWA price is < MAX_ABSOLUTE_DIFF_BPS
   *  - The change in RWA price has not deviated `MAX_CHANGE_DIFF_BPS` more
   *    than the change in the Chainlink price.
   * If the change in Chainlink price is larger than `MAX_ABSOLUTE_DIFF_BPS +
   * MAX_CHANGE_DIFF_BPS` it is deemed malfunctioning and ignored.
   *
   * @param newPrice The new price of some RWA token (In `decimals` decimals)
   *
   * @dev The decimal representation is not enforced yet must be respected by
   *      the caller of this function and deployer of this contract
   */
  function setPrice(int256 newPrice) external override onlyRole(SETTER_ROLE) {
    // RWA price must be greater than zero
    if (newPrice <= 0) {
      revert InvalidRWAPrice();
    }

    ChainlinkRoundData memory round = _getLatestChainlinkRoundData();
    // Chainlink price update must be recent
    if (block.timestamp > round.updatedAt + MAX_CL_WINDOW) {
      revert ChainlinkOraclePriceStale();
    }

    // Chainlink price update must not be comparing the same rounds against
    // eachother
    if (round.roundId == lastSetRound.roundId) {
      revert ChainlinkRoundNotUpdated();
    }

    // Ensure at least `MIN_PRICE_UPDATE_WINDOW` seconds have passed since
    // last RWA price update
    if (block.timestamp < priceTimestamp + MIN_PRICE_UPDATE_WINDOW) {
      revert PriceUpdateWindowViolation();
    }

    int256 rwaPriceChangeBps = _getPriceChangeBps(rwaPrice, newPrice);
    // Never allow a price change that violates the max absolute change
    // threshold.
    if (_abs_unsigned(rwaPriceChangeBps) > MAX_ABSOLUTE_DIFF_BPS) {
      revert AbsoluteDifferenceConstraintViolated();
    }

    int256 chainlinkPriceChangeBps = _getPriceChangeBps(
      lastSetRound.answer,
      round.answer
    );

    if (
      _abs_unsigned(chainlinkPriceChangeBps) <=
      MAX_ABSOLUTE_DIFF_BPS + MAX_CHANGE_DIFF_BPS
    ) {
      // Chainlink price change is sane, so we compare rwa price changes
      // against the Chainlink price changes.
      uint256 changeDifferenceBps = _abs_unsigned(
        rwaPriceChangeBps - chainlinkPriceChangeBps
      );

      if (changeDifferenceBps > MAX_CHANGE_DIFF_BPS) {
        revert DeltaDifferenceConstraintViolation();
      }
    } else {
      emit ChainlinkPriceIgnored(
        lastSetRound.answer,
        lastSetRound.roundId,
        round.answer,
        round.roundId
      );
    }

    emit RWAExternalComparisonCheckPriceSet(
      lastSetRound.answer,
      lastSetRound.roundId,
      round.answer,
      round.roundId,
      rwaPrice,
      newPrice
    );

    rwaPrice = newPrice;
    priceTimestamp = block.timestamp;
    lastSetRound = round;
  }

  /**
   * @notice Retrieve latest Chainlink data
   *
   * @dev Reverts if any corruption is detected in Chainlink response
   */
  function _getLatestChainlinkRoundData()
    private
    view
    returns (ChainlinkRoundData memory round)
  {
    (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    ) = chainlinkOracle.latestRoundData();
    if (
      answer < 0 ||
      roundId != answeredInRound ||
      roundId == 0 ||
      updatedAt == 0 ||
      updatedAt > block.timestamp
    ) {
      revert CorruptedChainlinkResponse();
    }
    round = ChainlinkRoundData(
      roundId,
      answer,
      startedAt,
      updatedAt,
      answeredInRound
    );
  }

  /**
   * @notice Compute the price change in basis point
   *
   * @param previousPrice Previous price
   * @param newPrice      New price
   *
   * @dev The price change can be negative.
   */
  function _getPriceChangeBps(
    int256 previousPrice,
    int256 newPrice
  ) private pure returns (int256 changeBps) {
    int256 change = newPrice - previousPrice;
    changeBps = (change * BPS_DENOMINATOR) / previousPrice;
  }

  /**
   * @notice returns the absolute value of the input.
   *
   * @param x the number to return absolute value of.
   */
  function _abs_unsigned(int256 x) private pure returns (uint256) {
    return x >= 0 ? uint256(x) : uint256(-x);
  }
}
                

contracts/interfaces/IRWAHubNonStableInstantMint.sol

/**SPDX-License-Identifier: BUSL-1.1

      ▄▄█████████▄
   ╓██▀└ ,╓▄▄▄, '▀██▄
  ██▀ ▄██▀▀╙╙▀▀██▄ └██µ           ,,       ,,      ,     ,,,            ,,,
 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`
  ██▄ ╙▀██▄▄▄▄▄,,,                ¬─                                    '─¬
   ╙▀██▄ '╙╙╙▀▀▀▀▀▀▀▀
      ╙▀▀██████R⌐

 */
pragma solidity 0.8.16;

import "contracts/interfaces/IRWAHub.sol";

interface IRWAHubNonStableInstantMint {
  /**
   * @notice Event emitted when an instant mint is completed
   *
   * @param user                      The address of the user
   * @param collateralAmountDeposited The amount of collateral deposited
   * @param collateralAmountAfterFees The amount of collateral after fees
   * @param feesInCollateral          The amount of fees in collateral
   * @param rwaGiven                  The amount of rwa minted to the user
   * @param priceMinted               The price at which the rwa was minted at
   * @param depositId                 The id of the deposit in RWAHub
   */
  event InstantMint(
    address indexed user,
    uint256 collateralAmountDeposited,
    uint256 collateralAmountAfterFees,
    uint256 feesInCollateral,
    uint256 rwaGiven,
    uint256 priceMinted,
    bytes32 depositId
  );

  /**
   * @notice Event emitted when the excess of the instant mint is claimed
   *
   * @param user           The address of the user
   * @param totalRWAOwed   The total amount of rwa owed (instant mint + excess)
   * @param rwaExcessDue   The amount of excess rwa owed
   * @param rwaExcessGiven The amount of rwa excess given in this claim
   * @param priceClaimed   The price at which the excess rwa was claimed at
   * @param depositId      The id of the deposit in RWAHub
   */
  event ExcessMintClaimed(
    address indexed user,
    uint256 totalRWAOwed,
    uint256 rwaExcessDue,
    uint256 rwaExcessGiven,
    uint256 priceClaimed,
    bytes32 depositId
  );

  /**
   * @notice Event emitted when instant mint given is overriden
   *
   * @param depositId   The id of the deposit in RWAHub
   * @param oldGivenAmt The old amount of rwa given
   * @param newGivenAmt The new amount of rwa given
   */
  event InstantMintGivenOverriden(
    bytes32 indexed depositId,
    uint256 oldGivenAmt,
    uint256 newGivenAmt
  );

  /**
   * @notice Event emitted when instant mint asset manager is set
   *
   * @param oldInstantMintAssetManager Old instant mint asset manager
   * @param newInstantMintAssetManager New instant mint asset manager
   */
  event InstantMintAssetManagerSet(
    address oldInstantMintAssetManager,
    address newInstantMintAssetManager
  );

  /**
   * @notice Event emitted when instant mint amount % (in bps) is set
   *
   * @param oldInstantMintBps Old instant mint amount in bps
   * @param newInstantMintBps New instant mint amount in bps
   */
  event InstantMintAmountSet(
    uint256 oldInstantMintBps,
    uint256 newInstantMintBps
  );

  /**
   * @notice Event emitted when instant mints are paused
   *
   * @param caller Address which initiated the pause
   */
  event InstantMintPaused(address caller);

  /**
   * @notice Event emitted when instant mints are unpaused
   *
   * @param caller Address which initiated the unpause
   */
  event InstantMintUnpaused(address caller);

  /**
   * @notice Event emitted when claiming excess is paused
   *
   * @param caller Address which initiated the pause
   */
  event ClaimExcessPaused(address caller);

  /**
   * @notice Event emitted when claiming excess is unpaused
   *
   * @param caller Address which initiated the unpause
   */
  event ClaimExcessUnpaused(address caller);

  /**
   * @notice Event emitted when instant mint fee is set
   *
   * @param oldFee Old fee
   * @param newFee New fee
   *
   * @dev See inheriting contract for decimals representation
   */
  event InstantMintFeeSet(uint256 oldFee, uint256 newFee);

  // Errors
  error CannotClaimExcess();
  error CannotClaimMint();
}
                

contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/token/ERC20/IERC20MetadataUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "contracts/external/openzeppelin/contracts-upgradeable/proxy/Initializable.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}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is
  Initializable,
  ContextUpgradeable,
  IERC20Upgradeable,
  IERC20MetadataUpgradeable
{
  mapping(address => uint256) private _balances;

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

  uint256 private _totalSupply;

  string private _name;
  string private _symbol;

  /**
   * @dev Sets the values for {name} and {symbol}.
   *
   * The default value of {decimals} is 18. To select a different value for
   * {decimals} you should overload it.
   *
   * All two of these values are immutable: they can only be set once during
   * construction.
   */
  function __ERC20_init(string memory name_, string memory symbol_)
    internal
    onlyInitializing
  {
    __ERC20_init_unchained(name_, symbol_);
  }

  function __ERC20_init_unchained(string memory name_, string memory symbol_)
    internal
    onlyInitializing
  {
    _name = name_;
    _symbol = symbol_;
  }

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

  /**
   * @dev Returns the symbol of the token, usually a shorter version of the
   * name.
   */
  function symbol() public view virtual override 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 value {ERC20} uses, unless this function is
   * 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 override returns (uint8) {
    return 18;
  }

  /**
   * @