ETH Price: $2,080.76 (+2.61%)

Contract

0x1B3F4ED11cb5f4c3F08c91C5593cbbcD63Fa8b23
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Approve241435132026-01-02 0:58:3567 days ago1767315515IN
0x1B3F4ED1...D63Fa8b23
0 ETH0.000001310.02834415
Approve241158062025-12-29 4:06:5971 days ago1766981219IN
0x1B3F4ED1...D63Fa8b23
0 ETH0.000003410.07311929
Join With Token239944222025-12-12 5:16:1188 days ago1765516571IN
0x1B3F4ED1...D63Fa8b23
0 ETH0.000925781.10035046
Join With Token239868102025-12-11 3:38:4789 days ago1765424327IN
0x1B3F4ED1...D63Fa8b23
0 ETH0.000173460.27173992
Seed239370062025-12-04 2:25:3596 days ago1764815135IN
0x1B3F4ED1...D63Fa8b23
0 ETH0.000026050.03844298
Propose New Owne...239363492025-12-04 0:09:2396 days ago1764806963IN
0x1B3F4ED1...D63Fa8b23
0 ETH0.000001340.02787828
Set Manager239361712025-12-03 23:31:4796 days ago1764804707IN
0x1B3F4ED1...D63Fa8b23
0 ETH0.000002030.02503953

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x6D74D646...EEc5897A6
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
OpalVault

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 9999 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (investments/opal/OpalVault.sol)

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

import { ITokenizedBalanceSheetVault } from "contracts/interfaces/external/tokenizedBalanceSheetVault/ITokenizedBalanceSheetVault.sol";
import { IOrigamiTokenizedBalanceSheetVault } from "contracts/interfaces/common/IOrigamiTokenizedBalanceSheetVault.sol";
import { IOpalManager } from "contracts/interfaces/investments/opal/IOpalManager.sol";
import { IOpalVault } from "contracts/interfaces/investments/opal/IOpalVault.sol";
import { OrigamiTokenizedBalanceSheetVault } from "contracts/common/OrigamiTokenizedBalanceSheetVault.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol";
import { OrigamiTBSLib as TBSLib, TBSState } from "contracts/libraries/OrigamiTBSLib.sol";

/**
 * @title Origami Portfolio of Assets and Liabilities (OPAL) - Tokenized Balance Sheet (TBS) Vault 
 * @notice The logic to aggregate the adapter balance sheets and allocate out to the adapters is delegated to a manager.
 */
contract OpalVault is
    OrigamiTokenizedBalanceSheetVault,
    IOpalVault
{
    using SafeERC20 for IERC20;
    using OrigamiMath for uint256;
    using TBSLib for TBSState;
    
    /// @dev The internal manager
    IOpalManager private _manager;

    /// @inheritdoc IOpalVault
    address public override feeCollector;

    /// @inheritdoc IOpalVault
    uint48 public override annualPerformanceFeeBps;

    /// @inheritdoc IOpalVault
    uint48 public override lastPerformanceFeeTime;

    /// @inheritdoc IOpalVault
    uint16 public constant override MAX_PERFORMANCE_FEE_BPS = 1_000; // 10%

    constructor(
        address initialOwner_,
        string memory name_,
        string memory symbol_,
        uint48 _annualPerformanceFeeBps,
        address _feeCollector,
        address tokenPrices_
    )
        OrigamiTokenizedBalanceSheetVault(initialOwner_, name_, symbol_, tokenPrices_)
    {
        if (_annualPerformanceFeeBps > MAX_PERFORMANCE_FEE_BPS) revert CommonEventsAndErrors.InvalidParam();
        annualPerformanceFeeBps = _annualPerformanceFeeBps;
        lastPerformanceFeeTime = uint48(block.timestamp);
        feeCollector = _feeCollector;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function setManager(address newManager) external override onlyElevatedAccess {
        if (newManager == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));

        if (newManager != address(_manager)) {
            _manager = IOpalManager(newManager);
            emit ManagerSet(newManager);
        }

        // Update the tokens hash in case it's changed
        updateCurrentTokensHash();
    }

    /// @inheritdoc IOpalVault
    function setAnnualPerformanceFee(uint48 _annualPerformanceFeeBps) external override onlyElevatedAccess {
        if (_annualPerformanceFeeBps > MAX_PERFORMANCE_FEE_BPS) revert CommonEventsAndErrors.InvalidParam();

        // Harvest on the old rate prior to updating the fee
        _collectPerformanceFees();

        emit PerformanceFeeSet(_annualPerformanceFeeBps);
        annualPerformanceFeeBps = _annualPerformanceFeeBps;
    }

    /// @inheritdoc IOpalVault
    function setFeeCollector(address _feeCollector) external override onlyElevatedAccess {
        if (_feeCollector == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
        emit FeeCollectorSet(_feeCollector);
        feeCollector = _feeCollector;
    }

    /// @inheritdoc IOpalVault
    function collectPerformanceFees() external override onlyElevatedAccess returns (uint256 amount) {
        return _collectPerformanceFees();
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function areJoinsPaused() public override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        bool
    ) {
        return _manager.areJoinsPaused();
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function areExitsPaused() public override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        bool
    ) {
        return _manager.areExitsPaused();
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function manager() external override view returns (address) {
        return address(_manager);
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function joinFeeBps() public override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        uint256
    ) {
        return _manager.joinFeeBps();
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function exitFeeBps() public override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        uint256
    ) {
        return _manager.exitFeeBps();
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function assetTokens() public override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        address[] memory assets
    ) {
        assets = _manager.assetTokens();
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function liabilityTokens() public override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (
        address[] memory liabilities
    ) {
        liabilities = _manager.liabilityTokens();
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    /// @dev It is not always guaranteed that the amount returned here will succeed when joinWithToken() is called, 
    /// for example underlying adapter reverts if the protocol is paused (which may not be able to be captured adequately
    ///  here). It may also be slightly more conservative than what may actually be possible. This is a best efforts.
    function maxJoinWithToken(
        address tokenAddress, 
        address /*receiver*/
    ) public view override(ITokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) virtual returns (uint256 maxToken) {
        // NOTE joinWithToken() will only use the base OrigamiTokenizedBalanceSheetVault::_maxJoinWithToken() implementation
        // as a precondition check rather than this version which also checks each underlying adapter.
        // This calc is gas intensive, and the join will revert anyway in the case that the underlying adapter cannot join
        // with the requested tokens.

        // Calculate the minimum amount of shares which can be used to join the vault
        TBSState memory $ = _stateForInputToken(tokenAddress, 0);
        uint256 maxShares = _leastOfMaxJoinWithShares($);

        // Match _previewJoinWithShares to calc the amount of shares prior to fees being taken
        if (maxShares < type(uint256).max) maxShares = maxShares.inverseSubtractBps(joinFeeBps(), OrigamiMath.Rounding.ROUND_UP);

        // Finally convert that max amount of shares into the requested token
        return _maxSharesToInputToken($, maxShares);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    /// @dev It is not always guaranteed that the amount returned here will succeed when joinWithShares() is called, 
    /// for example underlying adapter reverts if the protocol is paused (which may not be able to be captured adequately
    ///  here). It may also be slightly more conservative than what may actually be possible. This is a best efforts.
    function maxJoinWithShares(
        address /*receiver*/
    ) public view override(ITokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) virtual returns (uint256 maxShares) {
        return _leastOfMaxJoinWithShares(_state());
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    /// @dev It is not always guaranteed that the amount returned here will succeed when exitWithToken() is called, 
    /// for example underlying adapter reverts if the protocol is paused (which may not be able to be captured adequately
    ///  here). It may also be slightly more conservative than what may actually be possible. This is a best efforts.
    function maxExitWithToken(
        address tokenAddress,
        address sharesOwner
    ) public view override(ITokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) virtual returns (uint256 maxToken) {
        // NOTE exitWithToken() will only use the base OrigamiTokenizedBalanceSheetVault::_maxExitWithToken() implementation
        // as a precondition check rather than this version which also checks each underlying adapter.
        // This calc is gas intensive, and the exit will revert anyway in the case that the underlying adapter cannot exit
        // with the requested tokens.

        // Calculate the minimum amount of shares which can be used to exit the vault
        TBSState memory $ = _stateForInputToken(tokenAddress, 0);
        uint256 maxShares = _leastOfMaxExitWithShares($, sharesOwner);

        // Match _previewExitWithShares to calc the amount of shares after fees are taken
        if (maxShares < type(uint256).max) maxShares = maxShares.subtractBps(exitFeeBps(), OrigamiMath.Rounding.ROUND_DOWN);

        // Finally convert that max amount of shares into the requested token
        return _maxSharesToInputToken($, maxShares);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    /// @dev It is not always guaranteed that the amount returned here will succeed when exitWithShares() is called, 
    /// for example underlying adapter reverts if the protocol is paused (which may not be able to be captured adequately
    ///  here). It may also be slightly more conservative than what may actually be possible. This is a best efforts.
    function maxExitWithShares(
        address sharesOwner
    ) public view override(ITokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) virtual returns (uint256 maxShares) {
        return _leastOfMaxExitWithShares(_state(), sharesOwner);
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function matchToken(address tokenAddress) public virtual override(IOrigamiTokenizedBalanceSheetVault, OrigamiTokenizedBalanceSheetVault) view returns (AssetOrLiability kind, uint256 index) {
        return _manager.matchToken(tokenAddress);
    }

    /// @inheritdoc OrigamiTokenizedBalanceSheetVault
    function _balanceSheet() internal override view returns (
        uint256[] memory totalAssets,
        uint256[] memory totalLiabilities,
        bytes memory balanceSheetData
    ) {
        return _manager.balanceSheet();
    }

    /// @inheritdoc IERC165
    function supportsInterface(
        bytes4 interfaceId
    ) public override(IERC165, OrigamiTokenizedBalanceSheetVault) pure returns (bool) {
        return OrigamiTokenizedBalanceSheetVault.supportsInterface(interfaceId)
            || interfaceId == type(IOpalVault).interfaceId;
    }

    /// @inheritdoc IOpalVault
    function accruedPerformanceFee() public override view returns (uint256) {
        // totalSupply * feeBps * timeDelta / 365 days / 10_000
        // Round down (protocol takes less of a fee)
        uint256 _timeDelta = block.timestamp - lastPerformanceFeeTime;
        return OrigamiMath.mulDiv(
            totalSupply(), 
            annualPerformanceFeeBps * _timeDelta, 
            OrigamiMath.BASIS_POINTS_DIVISOR * 365 days, 
            OrigamiMath.Rounding.ROUND_DOWN
        );
    }

    /// @dev collect the accrued performance fees by minting new shares.
    function _collectPerformanceFees() internal returns (uint256 amount) {
        amount = accruedPerformanceFee();
        if (amount != 0) {
            address _feeCollector = feeCollector;
            emit PerformanceFeesCollected(_feeCollector, amount);

            // Do not need to check vs maxTotalSupply here as it is
            // only for new user investments
            _mint(_feeCollector, amount);
        }

        lastPerformanceFeeTime = uint48(block.timestamp);
    }

    /// @dev A hook for joins - it must pull assets from caller and send liabilities to receiver,
    /// along with any other interactions required.
    function _joinPreMintHook(
        TBSState memory $,
        address caller,
        address receiver,
        uint256 /*shares*/,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal override {
        // Transfer assets from the caller to the manager then join
        for (uint256 i; i < assets.length; ++i) {
            IERC20($.assetAddresses[i]).safeTransferFrom(caller, address(_manager), assets[i]);
        }

        // The aggregated and underlying 'per adapter' balance sheet data is passed through as calldata
        // to save potentially expensive calls duplicating the effort in the manager.
        // It also ensures consistency
        IOpalManager.BalanceSheetData memory bsData = IOpalManager.BalanceSheetData({
            aggregatedBalanceSheet: IOpalManager.AssetsAndLiabilities($.assetBalances, $.liabilityBalances),
            perAdapterBS: $.balanceSheetData
        });

        _manager.join(assets, liabilities, receiver, bsData);
    }

    /// @dev A hook for exits - it must send assets to receiver and pull liabilities from caller,
    /// along with any other interactions required.
    function _exitPreBurnHook(
        TBSState memory $,
        address caller,
        address /*sharesOwner*/,
        address receiver,
        uint256 /*shares*/,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal override {
        // Transfer liabilities from the caller to the manager then exit
        for (uint256 i; i < liabilities.length; ++i) {
            IERC20($.liabilityAddresses[i]).safeTransferFrom(caller, address(_manager), liabilities[i]);
        }
        
        // The aggregated and underlying 'per adapter' balance sheet data is passed through as calldata
        // to save potentially expensive calls duplicating the effort in the manager.
        // It also ensures consistency
        IOpalManager.BalanceSheetData memory bsData = IOpalManager.BalanceSheetData({
            aggregatedBalanceSheet: IOpalManager.AssetsAndLiabilities($.assetBalances, $.liabilityBalances),
            perAdapterBS: $.balanceSheetData
        });

        _manager.exit(assets, liabilities, receiver, bsData);
    }

    /// @dev Calculate the maximum shares possible by converting each of the max join/exit amounts of underlying tokens into shares
    /// and returning the minimum.
    function _maxSharesFromTokens(
        uint256[] memory tokenBalances,
        uint256[] memory managerMaxTokens,
        uint256 _totalSupply,
        uint256 prevMaxShares
    ) private pure returns (uint256 maxShares) {
        uint256 _shares;
        maxShares = prevMaxShares;
        uint256 totalTokenBalance;
        uint256 maxTokens;
        for (uint256 i; i < managerMaxTokens.length; ++i) {
            // If the token balance of this asset is zero then it wont contribute to what 
            // can be joined into the vault, so can be skipped
            totalTokenBalance = tokenBalances[i];
            if (totalTokenBalance == 0) continue;

            // Also skip if it is uncapped
            maxTokens = managerMaxTokens[i];
            if (maxTokens == type(uint256).max) continue;

            // Convert the amount to shares, rounding down to ensure joins/exits will work for all valid tokens or shares
            // given the max constraints
            _shares = TBSLib.tokenToShares(maxTokens, totalTokenBalance, _totalSupply, OrigamiMath.Rounding.ROUND_DOWN);

            // Update the minimum value if necessary
            if (_shares < maxShares) maxShares = _shares;
        }
    }

    /// @dev The least amount of shares that joinWithShares can be called, given the two constraints:
    ///   1/ The maxTotalSupply based free shares capacity
    ///   2/ The underyling manager maxJoin amounts for each token in the balance sheet
    function _leastOfMaxJoinWithShares(
        TBSState memory $
    ) private view returns (uint256 maxShares) {
        // Given the maximum underlying asset/liability amounts which can be joined into the underlying adapters in the manager
        // calculate the maximum shares for each token and take the overall minimum.
        {
            (uint256[] memory maxAssets, uint256[] memory maxLiabilities) = _manager.maxJoin();

            // Starts at type(uint256).max then takes the minimum shares allowed for each token
            // Round down to ensure joinWithShares() will succeed for both assets and liabilities
            maxShares = _maxSharesFromTokens($.assetBalances, maxAssets, $.totalSupply, type(uint256).max);
            maxShares = _maxSharesFromTokens($.liabilityBalances, maxLiabilities, $.totalSupply, maxShares);

            // Calculate the shares before the exit fee is taken on the shares.
            // Inverse of the fees being taken in _previewJoinWithShares();
            if (maxShares < type(uint256).max) maxShares = maxShares.subtractBps(joinFeeBps(), OrigamiMath.Rounding.ROUND_DOWN);
        }

        // Use the min of what the total supply capacity based max is (calculated in the base implementation), 
        // and the maximum allowed by the manager assets above.
        // NOTE joinWithShares() will only use this base implementation as a precondition check rather than checking each underlying adapter
        // as above. This is because this calc is gas intensive, and will revert anyway in the case that the underlying adapter cannot join
        // with the requested tokens.
        uint256 maxSharesGivenTotalSupplyCap = _maxJoinWithShares($.totalSupply);
        if (maxSharesGivenTotalSupplyCap < maxShares) maxShares = maxSharesGivenTotalSupplyCap;
    }

    /// @dev The least amount of shares that exitWithShares can be called, given the two constraints:
    ///   1/ The balance of shares `sharesOwner` holds
    ///   2/ The underyling manager maxExit amounts for each token in the balance sheet
    function _leastOfMaxExitWithShares(
        TBSState memory $,
        address sharesOwner
    ) private view returns (uint256 maxShares) {
        // Given the maximum underlying asset/liability amounts which can be exited from the underlying adapters in the manager
        // calculate the maximum shares for each token and take the overall minimum.
        {
            (uint256[] memory maxAssets, uint256[] memory maxLiabilities) = _manager.maxExit();

            // Starts at type(uint256).max then takes the minimum shares allowed for each token
            // Round down to ensure exitWithShares() will succeed for both assets and liabilities
            maxShares = _maxSharesFromTokens($.assetBalances, maxAssets, $.totalSupply, type(uint256).max);
            maxShares = _maxSharesFromTokens($.liabilityBalances, maxLiabilities, $.totalSupply, maxShares);

            // Calculate the shares before the exit fee is taken on the shares.
            // Inverse of the fees being taken in _previewExitWithShares();
            if (maxShares < type(uint256).max) maxShares = maxShares.inverseSubtractBps(exitFeeBps(), OrigamiMath.Rounding.ROUND_UP);
        }

        // Use the min of what the total supply capacity based max is (calculated in the base implementation), 
        // and the maximum allowed by the manager assets above.
        // NOTE exitWithShares() will only use this base implementation as a precondition check rather than checking each underlying adapter
        // as above. This is because this calc is gas intensive, and will revert anyway in the case that the underlying adapter cannot exit
        // with the requested tokens.
        uint256 maxSharesGivenTotalSupplyCap = _maxExitWithShares(sharesOwner);
        if (maxSharesGivenTotalSupplyCap < maxShares) maxShares = maxSharesGivenTotalSupplyCap;
    }

    /// @dev Convert the maxShares into the max amount of tokens possible
    function _maxSharesToInputToken(
        TBSState memory $,
        uint256 maxShares
    ) private pure returns (uint256) {
        // Find the current balance of the `tokenAddress` given the balance sheet.
        uint256 tokenBalance = $.inputTokenBalance();
        if (tokenBalance == 0) return 0;

        if (maxShares == type(uint256).max) return maxShares;

        // This always needs to round down to ensure joinWithToken / exitWithToken is always less than the
        // underlying adapter caps
        return TBSLib.sharesToToken(
            maxShares,
            tokenBalance,
            $.totalSupply,
            OrigamiMath.Rounding.ROUND_DOWN
        );
    }
}

File 2 of 48 : OwnableOFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

abstract contract OwnableOFT {
    modifier onlyOFTOwner() virtual;
}

// @dev Example to use OpenZeppelin's Ownable:
/**
    import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
    contract OZOwnableOFT is OwnableOFT, Ownable {
        modifier onlyOFTOwner() override {
            _checkOwner();
            _;
        }
    }
*/

// @dev Example to use Origami's OnlyElevatedAccess:
/**
    import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";

    contract OrigamiOwnableOFT is OwnableOFT, OrigamiElevatedAccess {
        modifier onlyOFTOwner() override {
            if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
            _;
        }
    }
*/

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";

/**
 * @title IOAppCore
 */
interface IOAppCore {
    // Custom error messages
    error OnlyPeer(uint32 eid, bytes32 sender);
    error NoPeer(uint32 eid);
    error InvalidEndpointCall();
    error InvalidDelegate();

    // Event emitted when a peer (OApp) is set for a corresponding endpoint
    event PeerSet(uint32 eid, bytes32 peer);

    /**
     * @notice Retrieves the OApp version information.
     * @return senderVersion The version of the OAppSender.sol contract.
     * @return receiverVersion The version of the OAppReceiver.sol contract.
     */
    function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);

    /**
     * @notice Retrieves the LayerZero endpoint associated with the OApp.
     * @return iEndpoint The LayerZero endpoint as an interface.
     */
    function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);

    /**
     * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @return peer The peer address (OApp instance) associated with the corresponding endpoint.
     */
    function peers(uint32 _eid) external view returns (bytes32 peer);

    /**
     * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @param _peer The address of the peer to be associated with the corresponding endpoint.
     */
    function setPeer(uint32 _eid, bytes32 _peer) external;

    /**
     * @notice Sets the delegate address for the OApp Core.
     * @param _delegate The address of the delegate to be set.
     */
    function setDelegate(address _delegate) external;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { OwnableOFT } from "../access/OwnableOFT.sol";
import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol";

/**
 * @title OAppCore
 * @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
 */
abstract contract OAppCore is IOAppCore, OwnableOFT {
    // The LayerZero endpoint associated with the given OApp
    ILayerZeroEndpointV2 public immutable endpoint;

    // Mapping to store peers associated with corresponding endpoints
    mapping(uint32 eid => bytes32 peer) public peers;

    /**
     * @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
     * @param _endpoint The address of the LOCAL Layer Zero endpoint.
     * @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
     *
     * @dev The delegate typically should be set as the owner of the contract.
     */
    constructor(address _endpoint, address _delegate) {
        endpoint = ILayerZeroEndpointV2(_endpoint);

        if (_delegate == address(0)) revert InvalidDelegate();
        endpoint.setDelegate(_delegate);
    }

    /**
     * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @param _peer The address of the peer to be associated with the corresponding endpoint.
     *
     * @dev Only the owner/admin of the OApp can call this function.
     * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
     * @dev Set this to bytes32(0) to remove the peer address.
     * @dev Peer is a bytes32 to accommodate non-evm chains.
     */
    function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOFTOwner {
        _setPeer(_eid, _peer);
    }

    /**
     * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
     * @param _eid The endpoint ID.
     * @param _peer The address of the peer to be associated with the corresponding endpoint.
     *
     * @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
     * @dev Set this to bytes32(0) to remove the peer address.
     * @dev Peer is a bytes32 to accommodate non-evm chains.
     */
    function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {
        peers[_eid] = _peer;
        emit PeerSet(_eid, _peer);
    }

    /**
     * @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
     * ie. the peer is set to bytes32(0).
     * @param _eid The endpoint ID.
     * @return peer The address of the peer associated with the specified endpoint.
     */
    function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
        bytes32 peer = peers[_eid];
        if (peer == bytes32(0)) revert NoPeer(_eid);
        return peer;
    }

    /**
     * @notice Sets the delegate address for the OApp.
     * @param _delegate The address of the delegate to be set.
     *
     * @dev Only the owner/admin of the OApp can call this function.
     * @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
     */
    function setDelegate(address _delegate) public onlyOFTOwner {
        endpoint.setDelegate(_delegate);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OAppCore } from "./OAppCore.sol";

/**
 * @title OAppSender
 * @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
 */
abstract contract OAppSender is OAppCore {
    using SafeERC20 for IERC20;

    // Custom error messages
    error NotEnoughNative(uint256 msgValue);
    error LzTokenUnavailable();

    // @dev The version of the OAppSender implementation.
    // @dev Version is bumped when changes are made to this contract.
    uint64 internal constant SENDER_VERSION = 1;

    /**
     * @notice Retrieves the OApp version information.
     * @return senderVersion The version of the OAppSender.sol contract.
     * @return receiverVersion The version of the OAppReceiver.sol contract.
     *
     * @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
     * ie. this is a SEND only OApp.
     * @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
     */
    function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
        return (SENDER_VERSION, 0);
    }

    /**
     * @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
     * @param _dstEid The destination endpoint ID.
     * @param _message The message payload.
     * @param _options Additional options for the message.
     * @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
     * @return fee The calculated MessagingFee for the message.
     *      - nativeFee: The native fee for the message.
     *      - lzTokenFee: The LZ token fee for the message.
     */
    function _quote(
        uint32 _dstEid,
        bytes memory _message,
        bytes memory _options,
        bool _payInLzToken
    ) internal view virtual returns (MessagingFee memory fee) {
        return
            endpoint.quote(
                MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
                address(this)
            );
    }

    /**
     * @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
     * @param _dstEid The destination endpoint ID.
     * @param _message The message payload.
     * @param _options Additional options for the message.
     * @param _fee The calculated LayerZero fee for the message.
     *      - nativeFee: The native fee.
     *      - lzTokenFee: The lzToken fee.
     * @param _refundAddress The address to receive any excess fee values sent to the endpoint.
     * @return receipt The receipt for the sent message.
     *      - guid: The unique identifier for the sent message.
     *      - nonce: The nonce of the sent message.
     *      - fee: The LayerZero fee incurred for the message.
     */
    function _lzSend(
        uint32 _dstEid,
        bytes memory _message,
        bytes memory _options,
        MessagingFee memory _fee,
        address _refundAddress
    ) internal virtual returns (MessagingReceipt memory receipt) {
        // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
        uint256 messageValue = _payNative(_fee.nativeFee);
        if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);

        return
            // solhint-disable-next-line check-send-result
            endpoint.send{ value: messageValue }(
                MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),
                _refundAddress
            );
    }

    /**
     * @dev Internal function to pay the native fee associated with the message.
     * @param _nativeFee The native fee to be paid.
     * @return nativeFee The amount of native currency paid.
     *
     * @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
     * this will need to be overridden because msg.value would contain multiple lzFees.
     * @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
     * @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
     * @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
     */
    function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
        if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);
        return _nativeFee;
    }

    /**
     * @dev Internal function to pay the LZ token fee associated with the message.
     * @param _lzTokenFee The LZ token fee to be paid.
     *
     * @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
     * @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
     */
    function _payLzToken(uint256 _lzTokenFee) internal virtual {
        // @dev Cannot cache the token because it is not immutable in the endpoint.
        address lzToken = endpoint.lzToken();
        if (lzToken == address(0)) revert LzTokenUnavailable();

        // Pay LZ token fee by sending tokens to the endpoint.
        IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import { MessagingReceipt, MessagingFee } from "../../oapp/OAppSender.sol";

/**
 * @dev Struct representing token parameters for the OFT send() operation.
 */
struct SendParam {
    uint32 dstEid; // Destination endpoint ID.
    bytes32 to; // Recipient address.
    uint256 amountLD; // Amount to send in local decimals.
    uint256 minAmountLD; // Minimum amount to send in local decimals.
    bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
    bytes composeMsg; // The composed message for the send() operation.
    bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}

/**
 * @dev Struct representing OFT limit information.
 * @dev These amounts can change dynamically and are up the the specific oft implementation.
 */
struct OFTLimit {
    uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.
    uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.
}

/**
 * @dev Struct representing OFT receipt information.
 */
struct OFTReceipt {
    uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
    // @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
    uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}

/**
 * @dev Struct representing OFT fee details.
 * @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
 */
struct OFTFeeDetail {
    int256 feeAmountLD; // Amount of the fee in local decimals.
    string description; // Description of the fee.
}

/**
 * @title IOFT
 * @dev Interface for the OftChain (OFT) token.
 * @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.
 * @dev This specific interface ID is '0x02e49c2c'.
 */
interface IOFT {
    // Custom error messages
    error InvalidLocalDecimals();
    error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);

    // Events
    event OFTSent(
        bytes32 indexed guid, // GUID of the OFT message.
        uint32 dstEid, // Destination Endpoint ID.
        address indexed fromAddress, // Address of the sender on the src chain.
        uint256 amountSentLD, // Amount of tokens sent in local decimals.
        uint256 amountReceivedLD // Amount of tokens received in local decimals.
    );
    event OFTReceived(
        bytes32 indexed guid, // GUID of the OFT message.
        uint32 srcEid, // Source Endpoint ID.
        address indexed toAddress, // Address of the recipient on the dst chain.
        uint256 amountReceivedLD // Amount of tokens received in local decimals.
    );

    /**
     * @notice Retrieves interfaceID and the version of the OFT.
     * @return interfaceId The interface ID.
     * @return version The version.
     *
     * @dev interfaceId: This specific interface ID is '0x02e49c2c'.
     * @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.
     * @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.
     * ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)
     */
    function oftVersion() external view returns (bytes4 interfaceId, uint64 version);

    /**
     * @notice Retrieves the address of the token associated with the OFT.
     * @return token The address of the ERC20 token implementation.
     */
    function token() external view returns (address);

    /**
     * @notice Indicates whether the OFT contract requires approval of the 'token()' to send.
     * @return requiresApproval Needs approval of the underlying token implementation.
     *
     * @dev Allows things like wallet implementers to determine integration requirements,
     * without understanding the underlying token implementation.
     */
    function approvalRequired() external view returns (bool);

    /**
     * @notice Retrieves the shared decimals of the OFT.
     * @return sharedDecimals The shared decimals of the OFT.
     */
    function sharedDecimals() external view returns (uint8);

    /**
     * @notice Provides a quote for OFT-related operations.
     * @param _sendParam The parameters for the send operation.
     * @return limit The OFT limit information.
     * @return oftFeeDetails The details of OFT fees.
     * @return receipt The OFT receipt information.
     */
    function quoteOFT(
        SendParam calldata _sendParam
    ) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);

    /**
     * @notice Provides a quote for the send() operation.
     * @param _sendParam The parameters for the send() operation.
     * @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
     * @return fee The calculated LayerZero messaging fee from the send() operation.
     *
     * @dev MessagingFee: LayerZero msg fee
     *  - nativeFee: The native fee.
     *  - lzTokenFee: The lzToken fee.
     */
    function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);

    /**
     * @notice Executes the send() operation.
     * @param _sendParam The parameters for the send operation.
     * @param _fee The fee information supplied by the caller.
     *      - nativeFee: The native fee.
     *      - lzTokenFee: The lzToken fee.
     * @param _refundAddress The address to receive any excess funds from fees etc. on the src.
     * @return receipt The LayerZero messaging receipt from the send() operation.
     * @return oftReceipt The OFT receipt information.
     *
     * @dev MessagingReceipt: LayerZero msg receipt
     *  - guid: The unique identifier for the sent message.
     *  - nonce: The nonce of the sent message.
     *  - fee: The LayerZero fee incurred for the message.
     */
    function send(
        SendParam calldata _sendParam,
        MessagingFee calldata _fee,
        address _refundAddress
    ) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

import { IMessageLibManager } from "./IMessageLibManager.sol";
import { IMessagingComposer } from "./IMessagingComposer.sol";
import { IMessagingChannel } from "./IMessagingChannel.sol";
import { IMessagingContext } from "./IMessagingContext.sol";

struct MessagingParams {
    uint32 dstEid;
    bytes32 receiver;
    bytes message;
    bytes options;
    bool payInLzToken;
}

struct MessagingReceipt {
    bytes32 guid;
    uint64 nonce;
    MessagingFee fee;
}

struct MessagingFee {
    uint256 nativeFee;
    uint256 lzTokenFee;
}

struct Origin {
    uint32 srcEid;
    bytes32 sender;
    uint64 nonce;
}

interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
    event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);

    event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);

    event PacketDelivered(Origin origin, address receiver);

    event LzReceiveAlert(
        address indexed receiver,
        address indexed executor,
        Origin origin,
        bytes32 guid,
        uint256 gas,
        uint256 value,
        bytes message,
        bytes extraData,
        bytes reason
    );

    event LzTokenSet(address token);

    event DelegateSet(address sender, address delegate);

    function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);

    function send(
        MessagingParams calldata _params,
        address _refundAddress
    ) external payable returns (MessagingReceipt memory);

    function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;

    function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);

    function initializable(Origin calldata _origin, address _receiver) external view returns (bool);

    function lzReceive(
        Origin calldata _origin,
        address _receiver,
        bytes32 _guid,
        bytes calldata _message,
        bytes calldata _extraData
    ) external payable;

    // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
    function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;

    function setLzToken(address _lzToken) external;

    function lzToken() external view returns (address);

    function nativeToken() external view returns (address);

    function setDelegate(address _delegate) external;
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

struct SetConfigParam {
    uint32 eid;
    uint32 configType;
    bytes config;
}

interface IMessageLibManager {
    struct Timeout {
        address lib;
        uint256 expiry;
    }

    event LibraryRegistered(address newLib);
    event DefaultSendLibrarySet(uint32 eid, address newLib);
    event DefaultReceiveLibrarySet(uint32 eid, address newLib);
    event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
    event SendLibrarySet(address sender, uint32 eid, address newLib);
    event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
    event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);

    function registerLibrary(address _lib) external;

    function isRegisteredLibrary(address _lib) external view returns (bool);

    function getRegisteredLibraries() external view returns (address[] memory);

    function setDefaultSendLibrary(uint32 _eid, address _newLib) external;

    function defaultSendLibrary(uint32 _eid) external view returns (address);

    function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _timeout) external;

    function defaultReceiveLibrary(uint32 _eid) external view returns (address);

    function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;

    function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);

    function isSupportedEid(uint32 _eid) external view returns (bool);

    function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);

    /// ------------------- OApp interfaces -------------------
    function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;

    function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);

    function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);

    function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;

    function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);

    function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _gracePeriod) external;

    function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);

    function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;

    function getConfig(
        address _oapp,
        address _lib,
        uint32 _eid,
        uint32 _configType
    ) external view returns (bytes memory config);
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

interface IMessagingChannel {
    event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
    event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
    event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);

    function eid() external view returns (uint32);

    // this is an emergency function if a message cannot be verified for some reasons
    // required to provide _nextNonce to avoid race condition
    function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;

    function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;

    function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;

    function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);

    function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);

    function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);

    function inboundPayloadHash(
        address _receiver,
        uint32 _srcEid,
        bytes32 _sender,
        uint64 _nonce
    ) external view returns (bytes32);

    function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

interface IMessagingComposer {
    event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
    event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
    event LzComposeAlert(
        address indexed from,
        address indexed to,
        address indexed executor,
        bytes32 guid,
        uint16 index,
        uint256 gas,
        uint256 value,
        bytes message,
        bytes extraData,
        bytes reason
    );

    function composeQueue(
        address _from,
        address _to,
        bytes32 _guid,
        uint16 _index
    ) external view returns (bytes32 messageHash);

    function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;

    function lzCompose(
        address _from,
        address _to,
        bytes32 _guid,
        uint16 _index,
        bytes calldata _message,
        bytes calldata _extraData
    ) external payable;
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

interface IMessagingContext {
    function isSendingMessage() external view returns (bool);

    function getSendContext() external view returns (uint32 dstEid, address sender);
}

File 12 of 48 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";

File 13 of 48 : IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.0;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

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

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.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.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    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}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual 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 default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

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

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

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

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

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

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

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * 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`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @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 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

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

pragma solidity ^0.8.0;

import "./IERC20Permit.sol";
import "../ERC20.sol";
import "../../../utils/cryptography/ECDSA.sol";
import "../../../utils/cryptography/EIP712.sol";
import "../../../utils/Counters.sol";

/**
 * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * _Available since v3.4._
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
    using Counters for Counters.Counter;

    mapping(address => Counters.Counter) private _nonces;

    // solhint-disable-next-line var-name-mixedcase
    bytes32 private constant _PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    /**
     * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
     * However, to ensure consistency with the upgradeable transpiler, we will continue
     * to reserve a slot.
     * @custom:oz-renamed-from _PERMIT_TYPEHASH
     */
    // solhint-disable-next-line var-name-mixedcase
    bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @dev See {IERC20Permit-permit}.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");

        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        _approve(owner, spender, value);
    }

    /**
     * @dev See {IERC20Permit-nonces}.
     */
    function nonces(address owner) public view virtual override returns (uint256) {
        return _nonces[owner].current();
    }

    /**
     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @dev "Consume a nonce": return the current value and increment.
     *
     * _Available since v4.1._
     */
    function _useNonce(address owner) internal virtual returns (uint256 current) {
        Counters.Counter storage nonce = _nonces[owner];
        current = nonce.current();
        nonce.increment();
    }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.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));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    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");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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);
        }
    }
}

// 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;
    }
}

// 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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @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 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

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

pragma solidity ^0.8.8;

import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {EIP-5267}.
     *
     * _Available since v4.9._
     */
    function eip712Domain()
        public
        view
        virtual
        override
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _name.toStringWithFallback(_nameFallback),
            _version.toStringWithFallback(_versionFallback),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }
}

// 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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

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

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

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

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

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

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

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)

pragma solidity ^0.8.8;

import "./StorageSlot.sol";

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(_FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 32 of 48 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

// Common.sol
//
// Common mathematical functions needed by both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);

/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);

/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();

/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;

/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;

/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;

/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;

/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;

/*//////////////////////////////////////////////////////////////////////////
                                    FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
    unchecked {
        // Start from 0.5 in the 192.64-bit fixed-point format.
        result = 0x800000000000000000000000000000000000000000000000;

        // The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
        //
        // 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
        // 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
        // a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
        // we know that `x & 0xFF` is also 1.
        if (x & 0xFF00000000000000 > 0) {
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
        }

        if (x & 0xFF000000000000 > 0) {
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
        }

        if (x & 0xFF0000000000 > 0) {
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
        }

        if (x & 0xFF000000 > 0) {
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
        }

        if (x & 0xFF0000 > 0) {
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
        }

        if (x & 0xFF00 > 0) {
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
        }

        if (x & 0xFF > 0) {
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
        }

        // In the code snippet below, two operations are executed simultaneously:
        //
        // 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
        // accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
        // 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
        //
        // The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
        // integer part, $2^n$.
        result *= UNIT;
        result >>= (191 - (x >> 64));
    }
}

/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
///     x >>= 128;
///     result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
    // 2^128
    assembly ("memory-safe") {
        let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^64
    assembly ("memory-safe") {
        let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^32
    assembly ("memory-safe") {
        let factor := shl(5, gt(x, 0xFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^16
    assembly ("memory-safe") {
        let factor := shl(4, gt(x, 0xFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^8
    assembly ("memory-safe") {
        let factor := shl(3, gt(x, 0xFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^4
    assembly ("memory-safe") {
        let factor := shl(2, gt(x, 0xF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^2
    assembly ("memory-safe") {
        let factor := shl(1, gt(x, 0x3))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^1
    // No need to shift x any more.
    assembly ("memory-safe") {
        let factor := gt(x, 0x1)
        result := or(result, factor)
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
    // use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
    // variables such that product = prod1 * 2^256 + prod0.
    uint256 prod0; // Least significant 256 bits of the product
    uint256 prod1; // Most significant 256 bits of the product
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    // Handle non-overflow cases, 256 by 256 division.
    if (prod1 == 0) {
        unchecked {
            return prod0 / denominator;
        }
    }

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
    if (prod1 >= denominator) {
        revert PRBMath_MulDiv_Overflow(x, y, denominator);
    }

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

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

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

    unchecked {
        // Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
        // because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
        // For more detail, see https://cs.stackexchange.com/q/138556/92363.
        uint256 lpotdod = denominator & (~denominator + 1);
        uint256 flippedLpotdod;

        assembly ("memory-safe") {
            // Factor powers of two out of denominator.
            denominator := div(denominator, lpotdod)

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

            // Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
            // `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
            // However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
            flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
        }

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

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

        // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
        // in modular arithmetic, doubling the correct bits in each step.
        inverse *= 2 - denominator * inverse; // inverse mod 2^8
        inverse *= 2 - denominator * inverse; // inverse mod 2^16
        inverse *= 2 - denominator * inverse; // inverse mod 2^32
        inverse *= 2 - denominator * inverse; // inverse mod 2^64
        inverse *= 2 - denominator * inverse; // inverse mod 2^128
        inverse *= 2 - denominator * inverse; // inverse mod 2^256

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

/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
///     x * y = MAX\_UINT256 * UNIT \\
///     (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
    uint256 prod0;
    uint256 prod1;
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    if (prod1 == 0) {
        unchecked {
            return prod0 / UNIT;
        }
    }

    if (prod1 >= UNIT) {
        revert PRBMath_MulDiv18_Overflow(x, y);
    }

    uint256 remainder;
    assembly ("memory-safe") {
        remainder := mulmod(x, y, UNIT)
        result :=
            mul(
                or(
                    div(sub(prod0, remainder), UNIT_LPOTD),
                    mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
                ),
                UNIT_INVERSE
            )
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
        revert PRBMath_MulDivSigned_InputTooSmall();
    }

    // Get hold of the absolute values of x, y and the denominator.
    uint256 xAbs;
    uint256 yAbs;
    uint256 dAbs;
    unchecked {
        xAbs = x < 0 ? uint256(-x) : uint256(x);
        yAbs = y < 0 ? uint256(-y) : uint256(y);
        dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
    }

    // Compute the absolute value of x*y÷denominator. The result must fit in int256.
    uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
    if (resultAbs > uint256(type(int256).max)) {
        revert PRBMath_MulDivSigned_Overflow(x, y);
    }

    // Get the signs of x, y and the denominator.
    uint256 sx;
    uint256 sy;
    uint256 sd;
    assembly ("memory-safe") {
        // "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
        sx := sgt(x, sub(0, 1))
        sy := sgt(y, sub(0, 1))
        sd := sgt(denominator, sub(0, 1))
    }

    // XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
    // If there are, the result should be negative. Otherwise, it should be positive.
    unchecked {
        result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
    if (x == 0) {
        return 0;
    }

    // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
    //
    // We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
    //
    // $$
    // msb(x) <= x <= 2*msb(x)$
    // $$
    //
    // We write $msb(x)$ as $2^k$, and we get:
    //
    // $$
    // k = log_2(x)
    // $$
    //
    // Thus, we can write the initial inequality as:
    //
    // $$
    // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
    // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
    // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
    // $$
    //
    // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
    uint256 xAux = uint256(x);
    result = 1;
    if (xAux >= 2 ** 128) {
        xAux >>= 128;
        result <<= 64;
    }
    if (xAux >= 2 ** 64) {
        xAux >>= 64;
        result <<= 32;
    }
    if (xAux >= 2 ** 32) {
        xAux >>= 32;
        result <<= 16;
    }
    if (xAux >= 2 ** 16) {
        xAux >>= 16;
        result <<= 8;
    }
    if (xAux >= 2 ** 8) {
        xAux >>= 8;
        result <<= 4;
    }
    if (xAux >= 2 ** 4) {
        xAux >>= 4;
        result <<= 2;
    }
    if (xAux >= 2 ** 2) {
        result <<= 1;
    }

    // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
    // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision
    // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of
    // precision into the expected uint128 result.
    unchecked {
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;

        // If x is not a perfect square, round the result toward zero.
        uint256 roundedResult = x / result;
        if (result >= roundedResult) {
            result = roundedResult;
        }
    }
}

File 33 of 48 : OrigamiElevatedAccess.sol
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)

import { OrigamiElevatedAccessBase } from "contracts/common/access/OrigamiElevatedAccessBase.sol";

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
abstract contract OrigamiElevatedAccess is OrigamiElevatedAccessBase {
    constructor(address initialOwner) {
        _init(initialOwner);
    }
}

pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)

import { IOrigamiElevatedAccess } from "contracts/interfaces/common/access/IOrigamiElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
abstract contract OrigamiElevatedAccessBase is IOrigamiElevatedAccess {
    /**
     * @notice The address of the current owner.
     */ 
    address public override owner;

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    mapping(address => mapping(bytes4 => bool)) public override explicitFunctionAccess;

    /// @dev Track proposed owner
    address private _proposedNewOwner;

    /// @dev propose this as the new owner before revoking, for 2 step approval
    address private constant PROPOSED_DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;

    function _init(address initialOwner) internal {
        if (owner != address(0)) revert CommonEventsAndErrors.InvalidAccess();
        if (initialOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
        owner = initialOwner;
    }

    /**
     * @notice Revoke ownership. 
     * @dev To enforce a two-step revoke, it must first propose to 0x000...dEaD prior to calling.
     * This cannot be undone.
     */
    function revokeOwnership() external override onlyElevatedAccess {
        if (_proposedNewOwner != PROPOSED_DEAD_ADDRESS) revert CommonEventsAndErrors.InvalidAddress(_proposedNewOwner);

        emit NewOwnerAccepted(owner, address(0));
        owner = address(0);
    }

    /**
     * @notice Proposes a new Owner.
     * Can only be called by the current owner
     */
    function proposeNewOwner(address account) external override onlyElevatedAccess {
        if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress(account);
        emit NewOwnerProposed(owner, _proposedNewOwner, account);
        _proposedNewOwner = account;
    }

    /**
     * @notice Caller accepts the role as new Owner.
     * Can only be called by the proposed owner
     */
    function acceptOwner() public virtual override {
        if (msg.sender != _proposedNewOwner) revert CommonEventsAndErrors.InvalidAccess();

        emit NewOwnerAccepted(owner, msg.sender);
        owner = msg.sender;
        delete _proposedNewOwner;
    }

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external override onlyElevatedAccess {
        if (allowedCaller == address(0)) revert CommonEventsAndErrors.InvalidAddress(allowedCaller);
        ExplicitAccess memory _access;
        for (uint256 i; i < access.length; ++i) {
            _access = access[i];
            emit ExplicitAccessSet(allowedCaller, _access.fnSelector, _access.allowed);
            explicitFunctionAccess[allowedCaller][_access.fnSelector] = _access.allowed;
        }
    }

    function isElevatedAccess(address caller, bytes4 fnSelector) internal view returns (bool) {
        return (
            caller == owner || 
            explicitFunctionAccess[caller][fnSelector]
        );
    }

    /**
     * @notice The owner is allowed to call, or if explicit access has been given to the caller.
     * @dev Important: Only for use when called from an *external* contract. 
     * If a function with this modifier is called internally then the `msg.sig` 
     * will still refer to the top level externally called function.
     */
    modifier onlyElevatedAccess() {
        _onlyElevatedAccess();
        _;
    }

    function _onlyElevatedAccess() private view {
        if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
    }
}

pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/omnichain/OrigamiTeleportableToken.sol)

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";

import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { IOrigamiTeleportableToken } from "contracts/interfaces/common/omnichain/IOrigamiTeleportableToken.sol";

import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { SendParam, OFTReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/standards/oft-evm/interfaces/IOFT.sol";
import { IOFT } from "@layerzerolabs/lz-evm-oapp-v2/contracts/standards/oft-evm/interfaces/IOFT.sol";

/// @title Origami Teleportable Token
/// @notice An ERC20 token (supporting Permit) which does not require token approval to be spent
///     by the trusted teleporter. 
/// @dev There are intentionally no external mint/burn functions on this token, 
///     the teleporter is expected to be a 'locker', ie escrow the tokens.
contract OrigamiTeleportableToken is 
    ERC20Permit,
    OrigamiElevatedAccess,
    IOrigamiTeleportableToken
{
    using SafeERC20 for IERC20;
    /// @inheritdoc IOrigamiTeleportableToken
    IOFT public override teleporter;

    constructor(
        string memory name_,
        string memory symbol_,
        address initialOwner_
    )
        ERC20(name_, symbol_) 
        ERC20Permit(name_) 
        OrigamiElevatedAccess(initialOwner_)
    {}

    /// @inheritdoc IOrigamiTeleportableToken
    function setTeleporter(address newTeleporter) external override onlyElevatedAccess {
        if (newTeleporter == address(0)) revert CommonEventsAndErrors.InvalidAddress(newTeleporter);
        teleporter = IOFT(newTeleporter);
        emit TeleporterSet(newTeleporter);
    }

    /// @inheritdoc IERC20
    function allowance(address tokenOwner, address spender) public view virtual override(ERC20, IERC20) returns (uint256) {
        // If the spender is the trusted teleporter, then no approval is required.
        return spender == address(teleporter)
            ? type(uint256).max
            : super.allowance(tokenOwner, spender);
    }
    
    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public virtual override pure returns (bool) {
        return interfaceId == type(IOrigamiTeleportableToken).interfaceId 
            || interfaceId == type(IERC20Metadata).interfaceId
            || interfaceId == type(IERC20).interfaceId
            || interfaceId == type(IERC20Permit).interfaceId
            || interfaceId == type(EIP712).interfaceId
            || interfaceId == type(IERC165).interfaceId;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       LAYER ZERO SEND                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IOrigamiTeleportableToken
    function send(
        SendParam calldata sendParam,
        MessagingFee calldata fee,
        address refundAddress
    ) external payable override returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {
        // pull tokens to vault first. no allowance needed
        uint256 amount = sendParam.amountLD;
        _transfer(msg.sender, address(this), amount);

        // no approval, teleporter is trusted as spender
        (msgReceipt, oftReceipt) = teleporter.send{value: msg.value}(sendParam, fee, refundAddress);

        // There may be a dust refund as LZ truncates to 6 decimals by default.
        uint256 refundAmount = amount - oftReceipt.amountSentLD;
        if (refundAmount > 0) {
            _transfer(address(this), msg.sender, refundAmount);
        }
    }

    /// @inheritdoc IOrigamiTeleportableToken
    function quoteSend(
        SendParam calldata sendParam,
        bool payInLzToken
    ) external view override returns (MessagingFee memory fee) {
        fee = teleporter.quoteSend(sendParam, payInLzToken);
    }
}

File 36 of 48 : OrigamiTokenizedBalanceSheetVault.sol
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later

import { ITokenizedBalanceSheetVault } from "contracts/interfaces/external/tokenizedBalanceSheetVault/ITokenizedBalanceSheetVault.sol";
import { IOrigamiTokenizedBalanceSheetVault } from "contracts/interfaces/common/IOrigamiTokenizedBalanceSheetVault.sol";

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { OrigamiTeleportableToken } from "contracts/common/omnichain/OrigamiTeleportableToken.sol";
import { OrigamiTBSLib as TBSLib, TBSState } from "contracts/libraries/OrigamiTBSLib.sol";
import { ITokenPrices } from "contracts/interfaces/common/ITokenPrices.sol";

/**
 * @title Tokenized 'Balance Sheet' Vault
 * @notice See `ITokenizedBalanceSheetVault` for more details on what a Tokenized Balance Sheet vault represents
 *
 * This Origami implementation is a more opinionated implementation:
 *  - Fees can be taken (from the shares minted/burned) on joins/exits
 *  - The vault needs to be initially seeded by elevated access prior to trading (and as such are protected from inflation style attacks)
 *  - The vault decimals are kept as 18 decimals regardless of the vault assets
 *  - Donations are allowed and will change the share price of the assets/liabilities
 *  - Assets and liabilities need to be in a tokenized form. If that's not possible then a wrapper must be used to represent the balances.
 *  - maxExitWithToken and maxExitWithShares for the sharesOwner=address(0) returns uint256.max. 
 *    This is to reflect that is no explicit exit limits other than the totalSupply. Useful for non-connected users as they browse the dapp
 */
abstract contract OrigamiTokenizedBalanceSheetVault is
    OrigamiTeleportableToken,
    ReentrancyGuard,
    IOrigamiTokenizedBalanceSheetVault
{
    using SafeERC20 for IERC20;
    using OrigamiMath for uint256;
    using TBSLib for TBSState;

    /// @dev The maximum total supply allowed for this vault.
    /// It is initially set to zero, and first set within seed()
    uint256 private _maxTotalSupply;

    /// @inheritdoc ITokenizedBalanceSheetVault
    bytes32 public override currentTokensHash;

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    ITokenPrices public override tokenPrices;

    constructor(
        address initialOwner_,
        string memory name_,
        string memory symbol_,
        address tokenPrices_
    )
        OrigamiTeleportableToken(name_, symbol_, initialOwner_)
    {
        tokenPrices = ITokenPrices(tokenPrices_);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ADMIN                            */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function setMaxTotalSupply(uint256 maxTotalSupply_) external override onlyElevatedAccess {
        // Cannot set if the totalSupply is zero - seed should be used first
        if (totalSupply() == 0) revert CommonEventsAndErrors.ExpectedNonZero();

        _maxTotalSupply = maxTotalSupply_;
        emit MaxTotalSupplySet(maxTotalSupply_);
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function updateCurrentTokensHash() public virtual {
        bytes32 newHash = keccak256(abi.encode(assetTokens(), liabilityTokens()));
        if (currentTokensHash != newHash) {
            currentTokensHash = newHash;
            emit CurrentTokensHashSet(newHash);
        }
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function seed(
        uint256[] calldata assetAmounts,
        uint256[] calldata liabilityAmounts,
        uint256 sharesToMint,
        address receiver,
        uint256 newMaxTotalSupply,
        bytes calldata balanceSheetData
    ) external override virtual onlyElevatedAccess {
        // Only to be used for the first deposit
        if (totalSupply() != 0) revert CommonEventsAndErrors.InvalidAccess();

        TBSState memory $;
        {
            $.assetAddresses = assetTokens();
            $.liabilityAddresses = liabilityTokens();

            // Existing balances in the local state are set exactly to the new balance sheet amounts
            // such that any proportional based calculations are using this seed amount
            // rather than zero's for an empty vault (or if someone skews by depositing funds into the unseeded
            // contracts)
            $.assetBalances = assetAmounts;
            $.liabilityBalances = liabilityAmounts;

            // Implementation specific balance sheet data is encoded by the caller
            // For example, this could be the split across multiple positions (OPAL)
            // This may also be empty - it is unused in hOHM
            $.balanceSheetData = balanceSheetData;
        }

        // Double check the amount arrays length match the addresses length
        if (assetAmounts.length != $.assetAddresses.length) revert CommonEventsAndErrors.InvalidParam();
        if (liabilityAmounts.length != $.liabilityAddresses.length) revert CommonEventsAndErrors.InvalidParam();

        if (sharesToMint > newMaxTotalSupply) revert ExceededMaxJoinWithShares(receiver, sharesToMint, newMaxTotalSupply);
        _maxTotalSupply = newMaxTotalSupply;
        emit MaxTotalSupplySet(newMaxTotalSupply);

        _join($, msg.sender, receiver, sharesToMint, assetAmounts, liabilityAmounts);
    }

    /// @dev Elevated access can recover any token.
    /// Note: If the asset/liability balances are kept directly in this contract
    /// (rather than delegated to a manager/3rd party upon a join), then the asset/liability tokens
    /// should not be recoverable. In that case the implementation should override this function.
    function recoverToken(address token, address to, uint256 amount) virtual external onlyElevatedAccess {
        emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
        IERC20(token).safeTransfer(to, amount);
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function setTokenPrices(address _tokenPrices) external override onlyElevatedAccess {
        if (_tokenPrices == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
        emit TokenPricesSet(_tokenPrices);
        tokenPrices = ITokenPrices(_tokenPrices);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     ACCOUNTING LOGIC                       */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @inheritdoc ITokenizedBalanceSheetVault
    function tokens() public virtual override view returns (address[] memory assets, address[] memory liabilities) {
        assets = assetTokens();
        liabilities = liabilityTokens();
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function balanceSheet() public override view returns (
        uint256[] memory totalAssets, 
        uint256[] memory totalLiabilities
    ) {
        (totalAssets, totalLiabilities, ) = _balanceSheet();
    }

    /// @dev Implementations are required to implement.
    /// @return totalAssets The per token amount of assets in the balance sheet
    /// @return totalLiabilities The per token amount of liabilities in the balance sheet
    /// @return balanceSheetData Optional encoded data which can be used to cache balance sheet
    ///         information, to be used in joins/exits later
    function _balanceSheet() internal virtual view returns (
        uint256[] memory totalAssets, 
        uint256[] memory totalLiabilities,
        bytes memory balanceSheetData
    );

    /// @inheritdoc ITokenizedBalanceSheetVault
    function convertFromToken(
        address tokenAddress,
        uint256 tokenAmount
    ) public view override returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // Following ERC4626 convention, round down for the two convert functions.
        (shares, assets, liabilities) = TBSLib.convertInputTokenAmountToSharesAndTokens({
            $: _stateForInputToken(tokenAddress, tokenAmount),
            sharesRounding: OrigamiMath.Rounding.ROUND_DOWN,
            assetsRounding: OrigamiMath.Rounding.ROUND_DOWN,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_DOWN
        });
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function convertFromShares(
        uint256 shares
    ) public view override returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // Following ERC4626 convention, round down for the two convert functions.
        return TBSLib.proportionalTokenAmountsFromShares({
                $: _state(),
                shares: shares,
                assetsRounding: OrigamiMath.Rounding.ROUND_DOWN,
                liabilitiesRounding: OrigamiMath.Rounding.ROUND_DOWN
            });
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*         IMPLEMENTATIONS TO OVERRIDE (EXTERNAL FNS)         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function assetTokens() public virtual override view returns (address[] memory);

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function liabilityTokens() public virtual override view returns (address[] memory);

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function joinFeeBps() public virtual override view returns (uint256) {
        return 0;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function exitFeeBps() public virtual override view returns (uint256) {
        return 0;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function maxTotalSupply() public override view returns (uint256) {
        return _maxTotalSupply;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function areJoinsPaused() public virtual override view returns (bool) {
        return false;
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function areExitsPaused() public virtual override view returns (bool) {
        return false;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                            JOINS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc ITokenizedBalanceSheetVault
    function maxJoinWithToken(
        address tokenAddress, 
        address /*receiver*/
    ) public view override virtual returns (uint256 maxToken) {
        return _maxJoinWithToken(
            _stateForInputToken(tokenAddress, 0),
            joinFeeBps()
        );
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function previewJoinWithToken(
        address tokenAddress,
        uint256 tokenAmount
    ) public view override returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (shares, /*shareFeesTaken*/, assets, liabilities) = _previewJoinWithToken(
            _stateForInputToken(tokenAddress, tokenAmount),
            joinFeeBps()
        );
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function joinWithToken(
        address tokenAddress,
        uint256 tokenAmount,
        address receiver,
        bytes32 tokensHash
    ) public override nonReentrant returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        if (tokensHash != currentTokensHash) revert IncorrectTokensHash();
        TBSState memory $ = _stateForInputToken(tokenAddress, tokenAmount);

        uint256 feeBps = joinFeeBps();
        uint256 maxTokens = _maxJoinWithToken($, feeBps);
        if (tokenAmount > maxTokens) {
            revert ExceededMaxJoinWithToken(receiver, tokenAddress, tokenAmount, maxTokens);
        }

        uint256 shareFeesTaken;
        (shares, shareFeesTaken, assets, liabilities) = _previewJoinWithToken($, feeBps);
        if (shareFeesTaken > 0) {
            emit InKindFees(FeeType.JOIN_FEE, feeBps, shareFeesTaken);
        }

        _join($, msg.sender, receiver, shares, assets, liabilities);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function maxJoinWithShares(address /*receiver*/) public view override virtual returns (uint256 maxShares) {
        return _maxJoinWithShares(totalSupply());
    }

    function _maxJoinWithShares(uint256 _totalSupply) internal view returns (uint256 maxShares) {
        if (areJoinsPaused()) return 0;
        return TBSLib.availableSharesCapacity(_totalSupply, maxTotalSupply());
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function previewJoinWithShares(
        uint256 shares
    ) public view override returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (assets, liabilities, /*shareFeesTaken*/) = _previewJoinWithShares(_state(), shares, joinFeeBps());
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function joinWithShares(
        uint256 shares,
        address receiver,
        bytes32 tokensHash
    ) public override nonReentrant returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        if (tokensHash != currentTokensHash) revert IncorrectTokensHash();
        TBSState memory $ = _state();
        uint256 maxShares = _maxJoinWithShares($.totalSupply);
        if (shares > maxShares) {
            revert ExceededMaxJoinWithShares(receiver, shares, maxShares);
        }

        uint256 feeBps = joinFeeBps();
        uint256 shareFeesTaken;
        (assets, liabilities, shareFeesTaken) = _previewJoinWithShares($, shares, feeBps);
        if (shareFeesTaken > 0) {
            emit InKindFees(FeeType.JOIN_FEE, feeBps, shareFeesTaken);
        }

        _join($, msg.sender, receiver, shares, assets, liabilities);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                            EXITS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc ITokenizedBalanceSheetVault
    function maxExitWithToken(
        address tokenAddress,
        address sharesOwner
    ) public view override virtual returns (uint256 maxToken) {
        return _maxExitWithToken(
            _stateForInputToken(tokenAddress, 0),
            sharesOwner,
            exitFeeBps()
        );
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function previewExitWithToken(
        address tokenAddress,
        uint256 tokenAmount
    ) public view override returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (shares, /*shareFeesTaken*/, assets, liabilities) = _previewExitWithToken(
            _stateForInputToken(tokenAddress, tokenAmount),
            exitFeeBps()
        );
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function exitWithToken(
        address tokenAddress,
        uint256 tokenAmount,
        address receiver,
        address sharesOwner,
        bytes32 tokensHash
    ) public override nonReentrant returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        if (tokensHash != currentTokensHash) revert IncorrectTokensHash();
        TBSState memory $ = _stateForInputToken(tokenAddress, tokenAmount);

        uint256 feeBps = exitFeeBps();
        uint256 maxTokens = _maxExitWithToken($, sharesOwner, feeBps);
        if (tokenAmount > maxTokens) {
            revert ExceededMaxExitWithToken(sharesOwner, tokenAddress, tokenAmount, maxTokens);
        }

        uint256 shareFeesTaken;
        (shares, shareFeesTaken, assets, liabilities) = _previewExitWithToken($, feeBps);
        if (shareFeesTaken > 0) {
            emit InKindFees(FeeType.EXIT_FEE, feeBps, shareFeesTaken);
        }

        _exit($, msg.sender, receiver, sharesOwner, shares, assets, liabilities);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function maxExitWithShares(
        address sharesOwner
    ) public view override virtual returns (uint256 maxShares) {
        return _maxExitWithShares(sharesOwner);
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function previewExitWithShares(
        uint256 shares
    ) public view override returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        (/*shareFeesTaken*/, assets, liabilities) = _previewExitWithShares(
            _state(),
            shares, 
            exitFeeBps()
        );
    }

    /// @inheritdoc ITokenizedBalanceSheetVault
    function exitWithShares(
        uint256 shares,
        address receiver,
        address sharesOwner,
        bytes32 tokensHash
    ) public override nonReentrant returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        if (tokensHash != currentTokensHash) revert IncorrectTokensHash();
        uint256 maxShares = _maxExitWithShares(sharesOwner);
        if (shares > maxShares) {
            revert ExceededMaxExitWithShares(sharesOwner, shares, maxShares);
        }

        TBSState memory $ = _state();
        uint256 feeBps = exitFeeBps();
        uint256 shareFeesTaken;
        (shareFeesTaken, assets, liabilities) = _previewExitWithShares($, shares, feeBps);
        if (shareFeesTaken > 0) {
            emit InKindFees(FeeType.EXIT_FEE, feeBps, shareFeesTaken);
        }

        _exit($, msg.sender, receiver, sharesOwner, shares, assets, liabilities);        
    }
    
    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function burn(uint256 amount) external virtual override {
        _burn(msg.sender, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       EXTERNAL VIEWS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function availableSharesCapacity() public override view returns (uint256 shares) {
        return TBSLib.availableSharesCapacity(totalSupply(), maxTotalSupply());
    }

    /// @inheritdoc IOrigamiTokenizedBalanceSheetVault
    function matchToken(address tokenAddress) public virtual override view returns (AssetOrLiability kind, uint256 index) {
        return TBSLib.matchToken(tokenAddress, assetTokens(), liabilityTokens());
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      EXTERNAL ERC165                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public virtual override(IERC165, OrigamiTeleportableToken) pure returns (bool) {
        return OrigamiTeleportableToken.supportsInterface(interfaceId)
            || interfaceId == type(IOrigamiTokenizedBalanceSheetVault).interfaceId 
            || interfaceId == type(ITokenizedBalanceSheetVault).interfaceId;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*         IMPLEMENTATIONS TO OVERRIDE (INTERNAL FNS)         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev A hook for joins - it must pull assets from caller and send liabilities to receiver,
    /// along with any other interactions required.
    function _joinPreMintHook(
        TBSState memory $,
        address caller,
        address receiver,
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal virtual;

    /// @dev A hook for exits - it must send assets to receiver and pull liabilities from caller,
    /// along with any other interactions required.
    function _exitPreBurnHook(
        TBSState memory $,
        address caller,
        address sharesOwner,
        address receiver,
        uint256 sharesPreBurn,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal virtual;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*        INTERNAL TOKENIZED BALANCE SHEET VAULT - JOINS      */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @dev What's the max amount that can be joined into the vault
    /// given a cached inputTokenBalance
    function _maxJoinWithToken(
        TBSState memory $,
        uint256 feeBps
    ) internal virtual view returns (uint256 maxTokens) {
        if (areJoinsPaused()) return 0;

        // If the token balance of the requested token is zero then cannot join
        if ($.inputTokenBalance() == 0) return 0;

        uint256 availableShares = TBSLib.availableSharesCapacity($.totalSupply, maxTotalSupply());
        if (availableShares == type(uint256).max) return availableShares;

        return TBSLib.sharesToToken({
            // Calculating the number of shares before the fee can be rounded up
            shares: availableShares.inverseSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_UP),
            tokenBalance: $.inputTokenBalance(),
            totalSupply: $.totalSupply,
            // The final max number of tokens is always rounded down
            rounding: OrigamiMath.Rounding.ROUND_DOWN
        });
    }

    /// @dev Preview a join for an amount of one of the cached inputToken
    function _previewJoinWithToken(
        TBSState memory $,
        uint256 feeBps
    ) internal virtual pure returns (
        uint256 shares,
        uint256 shareFeesTaken,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // When calculating the number of shares/assets/liabilities when JOINING 
        // given an input token amount:
        //   - shares: 
        //       - if input token is an asset: ROUND_DOWN
        //       - if input token is a liability: ROUND_UP
        //   - assets: ROUND_UP (the assets which are pulled from the caller)
        //   - liabilities: ROUND_DOWN (liabilities sent to recipient)
        (shares, assets, liabilities) = $.convertInputTokenAmountToSharesAndTokens({
            sharesRounding: $.inputTokenKind == AssetOrLiability.ASSET ? OrigamiMath.Rounding.ROUND_DOWN : OrigamiMath.Rounding.ROUND_UP,
            assetsRounding: OrigamiMath.Rounding.ROUND_UP,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_DOWN
        });

        // Deposit fees are taken from the shares in kind
        // The `shares` the recipient gets are rounded down (ie fees rounded up), in favour of the vault
        (shares, shareFeesTaken) = shares.splitSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_DOWN);
    }

    /// @dev Preview a join for an amount of shares
    function _previewJoinWithShares(TBSState memory $, uint256 shares, uint256 feeBps) internal virtual pure returns (
        uint256[] memory assets,
        uint256[] memory liabilities,
        uint256 shareFeesTaken
    ) {
        // Deposit fees are taken from the shares the user would otherwise receive
        // so calculate the amount of shares required before fees are taken.
        // Round up the fees taken, so it's in favour of the vault
        uint256 sharesBeforeFees = shares.inverseSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_UP);
        unchecked {
            shareFeesTaken = sharesBeforeFees - shares;
        }

        // When calculating the number of assets/liabilities when JOINING
        // given a number of shares:
        //   - assets: ROUND_UP (the assets which are pulled from the caller)
        //   - liabilities: ROUND_DOWN (liabilities sent to recipient)
        (assets, liabilities) = $.proportionalTokenAmountsFromShares({
            shares: sharesBeforeFees,
            assetsRounding: OrigamiMath.Rounding.ROUND_UP,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_DOWN
        });
    }

    /// @dev join for specific assets and liabilities and mint exact shares to the receiver
    /// Implementations must implement the hook which pulls assets from caller and sends 
    /// liabilities to receiver
    function _join(
        TBSState memory $,
        address caller,
        address receiver,
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal virtual {
        if (shares == 0) revert CommonEventsAndErrors.ExpectedNonZero();
        _joinPreMintHook($, caller, receiver, shares, assets, liabilities);

        _mint(receiver, shares);

        emit Join(caller, receiver, assets, liabilities, shares);
    }
    
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*        INTERNAL TOKENIZED BALANCE SHEET VAULT - EXITS      */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @dev What's the max amount that can be exited from the vault for 
    /// a cached inputToken
    function _maxExitWithToken(
        TBSState memory $,
        address sharesOwner,
        uint256 feeBps
    ) internal virtual view returns (uint256 maxTokens) {
        if (areExitsPaused()) return 0;

        // If the token balance of the requested token is zero then cannot join
        uint256 _inputTokenBalance = $.inputTokenBalance();
        if (_inputTokenBalance == 0) return 0;

        // Special case for address(0), unlimited
        if (sharesOwner == address(0)) return type(uint256).max;

        uint256 shares = balanceOf(sharesOwner);

        // The max number of tokens is always rounded down
        (shares,) = shares.splitSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_DOWN);

        uint256 maxFromShares = TBSLib.sharesToToken({
            shares: shares,
            tokenBalance: $.inputTokenBalance(),
            totalSupply: $.totalSupply,
            // The final max number of tokens is always rounded down
            rounding: OrigamiMath.Rounding.ROUND_DOWN
        });

        // Return the minimum of the available balance of the requested token in the balance sheet
        // and the derived amount from the available shares
        return maxFromShares < _inputTokenBalance ? maxFromShares : _inputTokenBalance;
    }

    /// @dev Preview an exit for an amount of one of the asset/liability tokens specified in the state cache
    function _previewExitWithToken(
        TBSState memory $,
        uint256 feeBps
    ) internal virtual pure returns (
        uint256 shares,
        uint256 shareFeesTaken,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // When calculating the number of shares/assets/liabilities when EXITING 
        // given an input token amount:
        //   - shares: 
        //       - if input token is an asset: ROUND_UP
        //       - if input token is a liability: ROUND_DOWN
        //   - assets: ROUND_DOWN (the assets which are sent to the recipient)
        //   - liabilities: ROUND_UP (liabilities pulled from the recipient)
        uint256 sharesAfterFees;
        (sharesAfterFees, assets, liabilities) = $.convertInputTokenAmountToSharesAndTokens({
            sharesRounding: $.inputTokenKind == AssetOrLiability.ASSET ? OrigamiMath.Rounding.ROUND_UP : OrigamiMath.Rounding.ROUND_DOWN,
            assetsRounding: OrigamiMath.Rounding.ROUND_DOWN,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_UP
        });

        // Exit fees are taken in 'shares' prior to redeeming to the underlying assets 
        // & liability tokens. So now calculate the total number of shares to burn from
        // the user.
        // Round up the shares needing to be burned, so it's in favour of the vault
        shares = sharesAfterFees.inverseSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_UP);
        unchecked {
            shareFeesTaken = shares - sharesAfterFees;
        }
    }

    /// @dev What's the max amount of shares that can be exited from the vault for an owner
    function _maxExitWithShares(
        address sharesOwner
    ) internal virtual view returns (uint256 maxShares) {
        if (areExitsPaused()) return 0;
        return sharesOwner == address(0)
            ? type(uint256).max // Special case for address(0), unlimited exit
            : balanceOf(sharesOwner);
    }

    /// @dev Preview an exit for an amount of shares
    function _previewExitWithShares(
        TBSState memory $,
        uint256 shares,
        uint256 feeBps
    ) internal virtual pure returns (
        uint256 shareFeesTaken,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        // The `shares` the user receives are rounded down in favour of the vault
        (shares, shareFeesTaken) = shares.splitSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_DOWN);

        // When calculating the number of assets/liabilities when EXITING
        // given a number of shares:
        //   - assets: ROUND_DOWN (assets are sent to the recipient)
        //   - liabilities: ROUND_UP (liabilities are pulled from the caller)
        (assets, liabilities) = $.proportionalTokenAmountsFromShares({
            shares: shares,
            assetsRounding: OrigamiMath.Rounding.ROUND_DOWN,
            liabilitiesRounding: OrigamiMath.Rounding.ROUND_UP
        });
    }

    /// @dev exit for specific assets and liabilities and burn exact shares to the receiver
    /// Can be called on behalf of a sharesOwner as long as allowance has been provided.
    /// Implementations must implement the hook which pulls liabilities from caller and sends 
    /// assets to receiver
    function _exit(
        TBSState memory $,
        address caller,
        address receiver,
        address sharesOwner,
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) internal virtual {
        if (shares == 0) revert CommonEventsAndErrors.ExpectedNonZero();
        if (sharesOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(sharesOwner);
        if (receiver == address(0)) revert CommonEventsAndErrors.InvalidAddress(receiver);
        if (caller != sharesOwner) {
            _spendAllowance(sharesOwner, caller, shares);
        }

        // Call the exit hook prior to burning shares, such that any
        // ERC20 _afterTokenTransfer() hooks the vault may implement (eg hOHM)
        // will use the latest balances
        _exitPreBurnHook($, caller, sharesOwner, receiver, shares, assets, liabilities);

        _burn(sharesOwner, shares);
        $.totalSupply -= shares;

        // If the vault has been fully exited, then reset the maxTotalSupply to zero, as if it were newly created.
        if ($.totalSupply == 0) _maxTotalSupply = 0;
        
        emit Exit(caller, receiver, sharesOwner, assets, liabilities, shares);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       POPULATE STATE                       */
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/

    /// @dev Populate the TBS state cache
    /// For use when a reference input token address/amount isn't required
    function _state() internal virtual view returns (TBSState memory $) {
        $.assetAddresses = assetTokens();
        $.liabilityAddresses = liabilityTokens();
        ($.assetBalances, $.liabilityBalances, $.balanceSheetData) = _balanceSheet();
        $.totalSupply = totalSupply();
    }

    /// @dev Fill the TBS state cache using the provided input token and amount
    function _stateForInputToken(
        address inputTokenAddress,
        uint256 inputTokenAmount
    ) internal view returns (TBSState memory $) {
        $ = _state();
        ($.inputTokenKind, $.inputTokenIndex) = TBSLib.matchToken(inputTokenAddress, $.assetAddresses, $.liabilityAddresses);
        $.inputTokenAmount = $.inputTokenBalance() == 0 ? 0 : inputTokenAmount;
    }
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/access/IOrigamiElevatedAccess.sol)

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
interface IOrigamiElevatedAccess {
    event ExplicitAccessSet(address indexed account, bytes4 indexed fnSelector, bool indexed value);

    event NewOwnerProposed(address indexed oldOwner, address indexed oldProposedOwner, address indexed newProposedOwner);
    event NewOwnerAccepted(address indexed oldOwner, address indexed newOwner);

    struct ExplicitAccess {
        bytes4 fnSelector;
        bool allowed;
    }

    /**
     * @notice The address of the current owner.
     */ 
    function owner() external view returns (address);

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    function explicitFunctionAccess(address contractAddr, bytes4 functionSelector) external view returns (bool);

    /**
     * @notice Revoke ownership. Be very certain before calling this, as no
     * further elevated access can be called.
     */
    function revokeOwnership() external;

    /**
     * @notice Proposes a new Owner.
     * Can only be called by the current owner
     */
    function proposeNewOwner(address account) external;

    /**
     * @notice Caller accepts the role as new Owner.
     * Can only be called by the proposed owner
     */
    function acceptOwner() external;

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external;
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/bundler/IOrigamiBundler.sol)

import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";

/// @notice Struct containing all the data needed to make a call.
/// @notice The call target is `to`, the calldata is `data` with value `value`.
/// @notice If `skipRevert` is true, other planned calls will continue executing even if this call reverts. `skipRevert`
/// will ignore all reverts. Use with caution -- see below for exceptions to this behaviour.
/// @notice If the call will trigger a reenter, the callbackHash should be set to the hash of the reenter bundle data.
struct Call {
    /// @dev The address to call
    address to;

    /// @dev The function selector and calldata, empty is allowed (to send 'native' assets eg ETH)
    bytes data;

    /// @dev Any optional 'native' assets to send to `to`
    uint256 value;

    /// @dev If set to true and the call fails, it will not revert the whole transaction.
    /// There are some exceptions to this where it will always revert regardless of this flag:
    ///   - If the `to` is address(0) it will revert with `InvalidPlugin(to)`
    ///   - If the `to` is not 'approved' in the bundler implementation it will revert with `InvalidPlugin(to)`
    ///   - If there is a reentered call stack where:
    ///       - The inner reentered call reverts and has `skipRevert=false` (so the inner revert bubbles up)
    ///       - The outer call which called reenter has `skipRevert=true`
    ///     In this case the entire transaction will still revert with `MissingExpectedReenter()`
    ///     since the transient `reenterHashT` is not reset to bytes32(0)
    bool skipRevert;

    /// @dev The hash of any expected reentered call as part of this. 
    /// Should be set to `keccak256(abi.encode(Call[] callbackBundle))`
    /// If no callback, set to bytes32(0)
    bytes32 callbackHash;
}

/// @title Origami Bundler
/// @notice Batch-execute a sequence of arbitrary calls atomically
/// @dev It carries specific features to be able to perform actions that require authorizations, and handle callbacks.
/// Credit to Morpho for their bundler3: https://github.com/morpho-org/bundler3/tree/263f75d3662f38133d5fba1b08c7b07847abfb2a 
interface IOrigamiBundler is IERC165 {
    error AlreadyInitiated();
    error EmptyBundle();
    error MissingExpectedReenter();
    error IncorrectReenterHash();
    error InvalidPlugin(address plugin);

    /// @notice The bundler implementation can optionally whitelist which addresses can be
    /// called in each multicall step.
    function isApprovedPlugin(address plugin) external view returns (bool);

    /// @notice Executes a sequence of calls.
    /// @dev 
    ///   - Locks the initiator so that the sender can be identified by other contracts.
    ///   - There are some exceptions to skip reverting if a call's `skipRevert=true`.
    ///     See the natspec within the Call struct definition for details.
    /// @param bundle The ordered array of calldata to execute.
    function multicall(Call[] calldata bundle) external payable;

    /// @notice Executes a sequence of calls.
    /// @dev Useful during callbacks.
    /// @dev Can only be called by the last unreturned callee with known data.
    /// @param bundle The ordered array of calldata to execute.
    function reenter(Call[] calldata bundle) external;

    /****** TRANSIENT VIEWS ******/
    
    /// @notice The initiator of the multicall transaction.
    /// @dev transient
    function initiatorT() external view returns (address);

    /// @notice Hash of the concatenation of the sender and the hash of the calldata of the next call to `reenter`.
    /// @dev transient
    function reenterHashT() external view returns (bytes32);
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later

import { ITokenizedBalanceSheetVault } from "contracts/interfaces/external/tokenizedBalanceSheetVault/ITokenizedBalanceSheetVault.sol";
import { IOrigamiTeleportableToken } from "contracts/interfaces/common/omnichain/IOrigamiTeleportableToken.sol";
import { ITokenPrices } from "contracts/interfaces/common/ITokenPrices.sol";

/**
 * @title Origami Tokenized Balance Sheet Vault
 * @notice A bit more of an opinionated version for our reference implementations
 */
interface IOrigamiTokenizedBalanceSheetVault is 
    ITokenizedBalanceSheetVault,
    IOrigamiTeleportableToken
{
    //============================================================================================//
    //                                           ERRORS                                           //
    //============================================================================================//
    
    /// @dev Attempted to deposit more assets than the max amount for `receiver`.
    error ExceededMaxJoinWithToken(address receiver, address tokenAddress, uint256 tokenAmount, uint256 max);

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

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

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

    /// @dev What kind of fees - either Join or Exit
    enum FeeType {
        JOIN_FEE,
        EXIT_FEE
    }

    /// @dev Either join or exit fees have been applied
    event InKindFees(FeeType feeType, uint256 feeBps, uint256 feeAmount);

    /// @dev Emit when the max total supply has been changed
    event MaxTotalSupplySet(uint256 maxTotalSupply);

    /// @dev May be emitted if the vault sets a TokenPrices contract (used within the UI only)
    event TokenPricesSet(address indexed tokenPrices);

    /// @dev May be emitted if the vault sets a manager contract for delegated logic
    event ManagerSet(address indexed manager);

    //============================================================================================//
    //                                          ADMIN                                             //
    //============================================================================================//

    /// @notice Elevated access seeds the vault, specifying each of the asset amounts, the liability amounts,
    /// and the shares to joinWithShares. This determines the initial share price of each asset and liability.
    /// @param assetAmounts The per token total of assets to seed the vault with
    /// @param liabilityAmounts The per token total of liabilities to seed the vault with
    /// @param sharesToMint The number of shares to mint to the receiver
    /// @param receiver Receives the initial shares and liabilities
    /// @param newMaxTotalSupply The initial maximum total supply cap allowed for this vault
    /// @param balanceSheetData Any encoded state required by the implementation to allocate the total assets 
    ///        and liabilities. For example if the total needs to be split across multiple positions.
    function seed(
        uint256[] calldata assetAmounts,
        uint256[] calldata liabilityAmounts,
        uint256 sharesToMint,
        address receiver,
        uint256 newMaxTotalSupply,
        bytes calldata balanceSheetData
    ) external;

    /// @notice Set the max total supply allowed for this vault
    /// @dev Will revert if the current totalSupply is zero as
    /// `seedDeposit()` needs to be called first
    function setMaxTotalSupply(uint256 maxTotalSupply) external;

    /// @notice Update the current tokens hash based on the assetTokens and liabilityTokens
    /// in the balance sheet
    function updateCurrentTokensHash() external;

    /// @notice Allows the caller to burn their own supply of vault shares.
    function burn(uint256 amount) external;

    /// @notice Set the helper to calculate current off-chain/subgraph integration
    function setTokenPrices(address tokenPrices) external;

    /// @notice Set the Origami delegated manager 
    function setManager(address manager) external;

    //============================================================================================//
    //                                          VIEWS                                             //
    //============================================================================================//

    /**
     * @notice Returns the addresses of the underlying tokens which represent the ASSETS in the Vault's balance sheet
     * @dev
     * - MUST be ERC-20 token contracts.
     * - MUST NOT revert.
     *
     * NOTE: tokens MAY be added or removed over time.
     */
    function assetTokens() external view returns (address[] memory tokens);

    /**
     * @notice Returns the addresses of the underlying tokens which represent the LIABILITIES in the Vault's balance sheet
     * @dev
     * - MUST be ERC-20 token contracts.
     * - MUST NOT revert.
     *
     * NOTE: tokens MAY be added or removed over time.
     */
    function liabilityTokens() external view returns (address[] memory tokens);

    /// @dev To report whether a token is invalid, an asset, or a liability in this balance sheet
    enum AssetOrLiability {
        INVALID,
        ASSET,
        LIABILITY
    }
    
    /// @notice Return whether the input tokenAddress is part of the balance sheet assets or liabilities, and the index
    /// of that address in the respective assetTokens() or liabilityTokens() list
    /// - kind === ASSET if an asset
    /// - kind === LIABILITY if a liability
    /// - kind === INVALID if not an asset or liability (index will be zero in this case)
    function matchToken(address tokenAddress) external view returns (AssetOrLiability kind, uint256 index);

    /// @notice The current joinWithShares or joinWithTokens fee in basis points.
    function joinFeeBps() external view returns (uint256);

    /// @notice The current exitToShares or exitToTokens fee in basis points.
    function exitFeeBps() external view returns (uint256);

    /// @notice The maxiumum total supply of shares for this vault.
    /// @dev may be set up to type(uint256).max
    function maxTotalSupply() external view returns (uint256 shares);

    /// @notice The available shares which can be minted as of now, under the maxTotalSupply
    /// @dev If maxTotalSupply() is type(uint256).max, then this will also be type(uint256).max
    function availableSharesCapacity() external view returns (uint256 shares);

    /// @notice Whether joinWithShares and joinWithAssets are currently paused
    function areJoinsPaused() external view returns (bool);

    /// @notice Whether exitToShares and exitToAssets are currently paused
    function areExitsPaused() external view returns (bool);

    /// @notice The helper contract to retrieve Origami USD prices
    /// @dev Required for off-chain/subgraph integration
    function tokenPrices() external view returns (ITokenPrices);

    /// @notice The Origami contract managing the application of
    /// the deposit tokens into the underlying protocol
    function manager() external view returns (address);
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/ITokenPrices.sol)

/// @title Token Prices
/// @notice A utility contract to pull token prices from on-chain.
/// @dev composable functions (uisng encoded function calldata) to build up price formulas
interface ITokenPrices {
    /// @notice How many decimals places are the token prices reported in
    function decimals() external view returns (uint8);

    /// @notice Retrieve the price for a given token.
    /// @dev If not mapped, or an underlying error occurs, FailedPriceLookup will be thrown.
    /// @dev 0x000...0 is the native chain token (ETH/AVAX/etc)
    function tokenPrice(address token) external view returns (uint256 price);

    /// @notice Token address to function calldata for how to lookup the price for this token
    function priceFnCalldata(address token) external view returns (bytes memory fnCalldata);

    /// @notice Retrieve the price for a list of tokens.
    /// @dev If any aren't mapped, or an underlying error occurs, FailedPriceLookup will be thrown.
    /// @dev Not particularly gas efficient - wouldn't recommend to use on-chain
    function tokenPrices(address[] memory tokens) external view returns (uint256[] memory prices);

    /// @notice The set of all mapped tokens
    function mappedTokenAt(uint256 i) external view returns (address token);

    /// @notice The set of all mapped tokens
    function allMappedTokens() external view returns (address[] memory);

    /// @notice The number of mapped tokens
    function numMappedTokens() external view returns (uint256);
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/omnichain/IOrigamiTeleportableToken.sol)

import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SendParam, OFTReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/standards/oft-evm/interfaces/IOFT.sol";
import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { IOFT } from "@layerzerolabs/lz-evm-oapp-v2/contracts/standards/oft-evm/interfaces/IOFT.sol";

/// @title Origami Teleportable Token
/// @notice An ERC20 token (supporting Permit) which does not require token approval to be spent
///     by the trusted teleporter. 
/// @dev There are intentionally no external mint/burn functions on this token, 
///     the teleporter is expected to be a 'locker', ie escrow the tokens.
interface IOrigamiTeleportableToken is
    IERC20Metadata,
    IERC20Permit,
    IERC165
{
    event TeleporterSet(address indexed teleporter);

    /// @notice Set the trusted address permitted to to bridge this token to another chain.
    function setTeleporter(address newTeleporter) external;

    /// @notice The trusted address permitted to to bridge this token to another chain.
    function teleporter() external view returns (IOFT);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       LAYER ZERO SEND                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice This function is added for improved UX convenience and for users to interact directly 
     * with the vault. The vault relays the call to the trusted token teleporter contract. 
     * @dev Executes the send operation.
     * @param sendParam The parameters for the send operation.
     * @param fee The calculated fee for the send() operation.
     *      - nativeFee: The native fee.
     *      - lzTokenFee: The lzToken fee.
     * @param refundAddress The address to receive any excess funds.
     * @return msgReceipt The receipt for the send operation.
     * @return oftReceipt The OFT receipt information.
     *
     * @dev MessagingReceipt: LayerZero msg receipt
     *  - guid: The unique identifier for the sent message.
     *  - nonce: The nonce of the sent message.
     *  - fee: The LayerZero fee incurred for the message.
     */
    function send(
        SendParam memory sendParam,
        MessagingFee memory fee,
        address refundAddress
    ) external payable returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt);

    /**
     * @notice Provides a quote for the send() operation.
     *  This function is added for improved UX convenience and for users to interact directly with the vault
     *  The vault relays the call to the trusted token teleporter contract. 
     * @param sendParam The parameters for the send() operation.
     * @param payInLzToken Flag indicating whether the caller is paying in the LZ token.
     * @return msgFee The calculated LayerZero messaging fee from the send() operation.
     *
     * @dev MessagingFee: LayerZero msg fee
     *  - nativeFee: The native fee.
     *  - lzTokenFee: The lzToken fee.
     */
    function quoteSend(
        SendParam calldata sendParam,
        bool payInLzToken
    ) external view returns (MessagingFee memory msgFee);
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later

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

/**
 * @title Tokenized 'Balance Sheet' Vault
 * @notice
 *  Shares in this vault represent a proportional slice of the balance sheet of this vault. The balance sheet adheres to the
 *  fundamental accounting equation: equity = assets - liabilities
 *    - ASSETS represent positive value of the vault, and may be composed of zero or more ERC20 tokens.
 *      The balance of each asset token in the balance sheet may be of different amounts.
 *    - LIABILITIES represent debt that this vault owes, and may be composed of zero or more ERC20 tokens.
 *      The balance of each liability token in the balance sheet may be of different amounts.
 *
 *  When a user mints new shares (aka equity):
 *    - They PROVIDE a proportional amount of each ASSET in the balance sheet as of that moment, for that number of shares.
 *    - They RECEIVE a proportional amount of each LIABILITY in the balance sheet as of that moment, for that number of shares.
 *    - The shares representing that proportional slice of the balance sheet equity are minted.
 *
 *  When a user redeems shares:
 *    - They PROVIDE a proportional amount of each LIABILITY in the balance sheet as of that moment, for that number of shares.
 *    - They RECEIVE a proportional amount of each ASSET in the balance sheet as of that moment, for that number of shares.
 *    - Those shares are burned.
 *
 *  The ASSET or LIABILITY amounts on the balance sheet can change over time:
 *    - The balances of the ASSETS may grow or shrink over time (eg yield, rebalancing the weights of the assets)
 *    - The balances of the LIABILITIES may grow or shrink over time (eg borrow cost increasing the debt over time)
 *
 *  The ASSET or LIABILITY token addresses can change over time:
 *    - A new asset may be added to the balance sheet (rolling Pendle PT expiries)
 *    - An asset can be removed from the balance sheet (effectively zero balance - removed for efficiency)
 *    - To ensure the caller joining/exiting is using the required token addresses as of the latest block, a bytes32 hash of
 *      the current asset and liability tokens are passed into the join/exit functions.
 *
 * The interface is inspired by ERC-4626. The intended high level UX is:
 *    - joinWithShares(shares):
 *        Caller specifies the number of shares to mint and the amount of each ASSET (pulled from user) and LIABILITY (sent to user)
 *        token is derived from the existing balance weights of the vault's balance sheet.
 *    - joinWithToken(tokenAddress, amount):
 *        Caller specifies either one of the ASSET addresses (pulled from user) or one of the LIABILITY addresses (sent to user),
 *        and the amount of that token. The number of shares to mint, and the required number of the remaining ASSET (pulled from user) and
 *        LIABILITY (sent to user) tokens are derived from the existing balance weights of the vault's balance sheet.
 *    - exitWithShares(shares):
 *        Caller specifies the number of shares to burn and the amount of each ASSET (sent to user) and LIABILITY (pulled from user)
 *        token is derived from the existing balance weights of the vault's balance sheet.
 *    - exitWithToken(tokenAddress, amount):
 *        Caller specifies either one of the ASSET addresses (sent to user) or one of the LIABILITY addresses (pulled from user),
 *        and the amount of that token. The number of shares to burn, and the required number of the remaining ASSET (sent to user) and
 *        LIABILITY (pulled from user) tokens are derived from the existing balance weights of the vault's balance sheet.
 *
 * The benefits of representing the Balance Sheet tokens 'in kind' include
 *    - No oracles required to convert into a single vault asset (like ERC-4626 would require)
 *    - There is no realisation of exposure (from a vault perspective) from one asset/liability into a single vault asset at a certain point in time.
 *    - The vault is not affected if there is a lack of liquidity to convert the other assets/liabilities into a single vault asset.
 *
 * A drawback is that the UX and integration is obviously tricker.
 *    - In most cases, it's likely that there will be AMM liquidity in order for users to easily buy/sell the vault token, rather than minting redeeming
 *    - More sophisticated can mint/redeem directly providing and receiving all required balance sheet tokens.
 *      This could include arbitrage bots which can ensure the AMM liquidity is pegged to the 'real' vault price.
 *    - 'zaps' can be added where possible for dapps to allocate in to the right assets - eg to provide the best price to users (AMM buy vs direct mint)
 *    - The asset and liability tokens can change over time depending on the specific implementation. Integrators need to be aware of this.
 *
 * As with ERC-4626, there is no slippage/deadline guarantees within this interface enforcing bounds on the sent/received. If required for a particular integration,
 * it can be handled via an intermediate 'router' contract enforcing bounds on the assets/liabilities/shares which are transferred.
 */
interface ITokenizedBalanceSheetVault is IERC20, IERC20Metadata {
    
    /// @dev Emitted during a joinWithToken or joinWithShares
    event Join(
        address indexed sender,
        address indexed owner,
        uint256[] assets,
        uint256[] liabilities,
        uint256 shares
    );

    /// @dev Emitted during a exitWithToken or exitWithShares
    event Exit(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256[] assets,
        uint256[] liabilities,
        uint256 shares
    );

    /// @dev Emitted whenever the `currentTokensHash()` is updated
    event CurrentTokensHashSet(bytes32 indexed hash);

    /// @dev When join/exit functions are called without using the `currentTokensHash()`
    error IncorrectTokensHash();

    /**
     * @notice Returns the addresses of the underlying tokens which represent the ASSETS and LIABILITIES in the Vault's balance sheet
     * @dev
     * - MUST be ERC-20 token contracts.
     * - MUST NOT revert.
     *
     * NOTE: tokens MAY be added or removed over time.
     */
    function tokens() external view returns (address[] memory assetTokens, address[] memory liabilityTokens);

    /**
     * @notice The keccak256 hash of abi encoded `H(assetTokens(), liabilityTokens())`
     * Passed into join/exit functions to ensure the balance sheet tokens has not changed between quote and execution
     * @dev
     * - Implemented as: keccak256(abi.encode(assetTokens(), liabilityTokens()))
     * - MUST not revert.
     */
    function currentTokensHash() external view returns (bytes32); 

    /**
     * @notice Returns the total amount of the ASSETS and LIABILITIES managed by this vault.
     * @dev
     * - `totalAssets` MUST return a list which is the same size and order as `assetTokens()`
     * - `totalLiabilities` MUST return a list which is the same size and order as `liabilityTokens()`
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function balanceSheet() external view returns (uint256[] memory totalAssets, uint256[] memory totalLiabilities);

    /**
     * @notice Returns the amount of shares that the Vault would exchange for the amount of `tokenAddress` provided, in an ideal
     * scenario where all the conditions are met.
     * The address and exact number of tokens of one of the `tokens()` is specified.
     * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
     *  @dev
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `tokens()` then shares, assets and libilities MUST have zero amounts.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertFromToken(
        address tokenAddress,
        uint256 tokenAmount
    ) external view returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

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

    /**
     * @notice Returns the maximum amount of `tokenAddress` that can be joined into the vault for the `receiver`,
     * through a joinWithToken call
     * `tokenAddress` must represent one of the assetTokens or liabilityTokens within `tokens()`
     * @dev
     * - MUST return a limited value if receiver is subject to some limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of `tokenAddress` that may be joined.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `tokens()` then this MUST return 0.
     */
    function maxJoinWithToken(
        address tokenAddress,
        address receiver
    ) external view returns (uint256 maxTokens);

    /**
     * @notice Allows an on-chain or off-chain user to simulate the effects of their joined at the current block, given
     * current on-chain conditions.
     * The address and exact number of tokens of one of the `tokens()` is specified.
     * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
     * @dev
     * - MUST return the `shares` as close to and NO MORE than the exact amount of Vault shares
     *    that would be minted in a joinWithToken call in the same transaction (ie round down).
     * - MUST return the `assets` as close to and NO LESS than the exact amount of tokens
     *    transferred FROM the sender for a joinWithToken call in the same transaction (ie round up).
     * - MUST return the `liabilities` as close to and NO MORE than the exact amount of tokens
     *    transferred TO the receiver for a joinWithToken call in the same transaction (ie round down).
     * - MUST NOT account for join limits like those returned from maxJoinWithToken and should always act as though the
     *    join would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of join fees. Integrators should be aware of the existence of join fees.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `tokens()` then shares, assets and libilities MUST have zero amounts.
     */
    function previewJoinWithToken(
        address tokenAddress,
        uint256 tokenAmount
    ) external view returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
      * @notice Mints Vault shares to receiver by transferring an exact amount of underlying tokens proportional to the current balance sheet.
      * The address and exact number of tokens of one of the `tokens()` is specified.
      * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
      * @dev
      * - MUST emit the Join event
      * - MUST revert if all of assets cannot be joined (due to join limit being reached, slippage, the user not
      *   approving enough underlying tokens to the Vault contract, etc).
      * - MUST revert if `tokensHash` does not equal `currentTokensHash()`
      *
      * NOTE: most implementations will require pre-approval of the Vault with all of the asset tokens
      */
    function joinWithToken(
        address tokenAddress,
        uint256 tokenAmount,
        address receiver,
        bytes32 tokensHash
    ) external returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

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

    /**
     * @notice Allows an on-chain or off-chain user to simulate the effects of their joinWithShares at the current block, given
     * current on-chain conditions.
     * The assets and liabilities are derived from the current balance sheet for that number of shares.
     * @dev
     * - MUST return the `assets` as close to and NO LESS than the exact amount of tokens
     *    transferred FROM the sender for a joinWithShares call in the same transaction (ie round up).
     * - MUST return the `liabilities` as close to and NO MORE than the exact amount of tokens
     *    transferred TO the receiver for a joinWithShares call in the same transaction (ie round down).
     * - MUST NOT account for mint limits like those returned from maxJoinWithShares and should always act as though the
     *    joinWithShares would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of join fees. Integrators should be aware of the existence of join fees.
     * - MUST NOT revert.
     */
    function previewJoinWithShares(
        uint256 shares
    ) external view returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
      * @notice Mints exactly `shares` Vault shares to receiver by joining amount of underlying tokens.
      * The assets and liabilities are derived from the current balance sheet for that number of shares.
      * @dev
      * - MUST emit the Join event
      * - MUST revert if all of shares cannot be minted (due to join limit being reached, slippage, the user not
      *   approving enough underlying tokens to the Vault contract, etc).
      * - MUST revert if `tokensHash` does not equal `currentTokensHash()`
      *
      * NOTE: most implementations will require pre-approval of the Vault with all of the asset tokens
      */
    function joinWithShares(
        uint256 shares,
        address receiver,
        bytes32 tokensHash
    ) external returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
     * @notice Returns the maximum amount of `tokenAddress` that can be withdrawn from the vault given the owner balance in the vault,
     * through a exitWithToken call
     * `tokenAddress` must represent one of the assetTokens or liabilityTokens within `tokens()`
     * @dev
     * - MUST return a limited value if owner is subject to some exit limit or timelock.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `tokens()` then this MUST return 0.
     */
    function maxExitWithToken(
        address tokenAddress,
        address owner
    ) external view returns (uint256 maxTokens);

    /**
     * @notice Allows an on-chain or off-chain user to simulate the effects of their exit at the current block,
     * given current on-chain conditions.
     * The address and exact number of tokens of one of the `tokens()` is specified.
     * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
     * @dev
     * - MUST return the `shares` as close to and NO LESS than the exact amount of Vault shares
     *    that would be burned in a exitWithToken call in the same transaction. (ie round up)
     * - MUST return the `assets` as close to and NO MORE than the exact amount of tokens
     *    transferred TO the sender for a exitWithToken call in the same transaction (ie round down).
     * - MUST return the `liabilities` as close to and NO LESS than the exact amount of tokens
     *    transferred FROM the receiver for a exitWithToken call in the same transaction (ie round up).
     * - MUST NOT account for exit limits like those returned from maxExitWithToken and should always act as though the
     *    exit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of exit fees. Integrators should be aware of the existence of exit fees.
     * - MUST NOT revert.
     * - If `tokenAddress` is not one of the `tokens()` then shares, assets and libilities MUST have zero amounts.
     */
    function previewExitWithToken(
        address tokenAddress,
        uint256 tokenAmount
    ) external view returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
      * @notice Burns shares from owner, sends exactly `tokenAddress` of underlying tokens to receiver.
      * The address and exact number of tokens of one of the `tokens()` is specified.
      * The remaining assets and liabilities are derived from the current balance sheet, along with the number of shares that would represent.
      * @dev
      * - MUST emit the Exit event.
      * - MUST revert if all of assets cannot be withdrawn (due to exit limit being reached, slippage, the owner
      *    not having enough shares, etc).
      * - MUST revert if `tokensHash` does not equal `currentTokensHash()`
      *
      * NOTE: most implementations will require pre-approval of the Vault with all of the liability tokens.
      */
    function exitWithToken(
        address tokenAddress,
        uint256 tokenAmount,
        address receiver,
        address owner,
        bytes32 tokensHash
    ) external returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    );

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

    /**
     * @notice Allows an on-chain or off-chain user to simulate the effects of their redemption at the current block,
     * given current on-chain conditions.
     * The assets and liabilities are derived from the current balance sheet for that number of shares.
     * @dev
     * - MUST return the `assets` as close to and NO MORE than the exact amount of tokens
     *    transferred TO the sender for a exitWithShares call in the same transaction (ie round down).
     * - MUST return the `liabilities` as close to and NO LESS than the exact amount of tokens
     *    transferred FROM the receiver for a exitWithShares call in the same transaction (ie round up).
     * - MUST NOT account for redemption limits like those returned from maxExitWithShares and should always act as though the
     *    redemption would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of exit fees. Integrators should be aware of the existence of exit fees.
     * - MUST NOT revert.
     */
    function previewExitWithShares(
        uint256 shares
    ) external view returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /**
      * @notice Burns shares from owner.
      * The assets and liabilities are derived from the current balance sheet for that number of shares.
      * @dev
      * - MUST emit the Exit event.
      * - MUST revert if all of assets cannot be redeemed (due to exit limit being reached, slippage, the owner
      *    not having enough shares, etc).
      * - MUST revert if `tokensHash` does not equal `currentTokensHash()`
      *
      * NOTE: most implementations will require pre-approval of the Vault with all of the liability tokens.
      */
    function exitWithShares(
        uint256 shares,
        address receiver,
        address owner,
        bytes32 tokensHash
    ) external returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/opal/IOpalAdapterFactory.sol)

interface IOpalAdapterFactory {
    error AlreadyRegistered();
    error NotRegistered();

    event ImplementationAdded(
        address indexed implementation,
        bytes32 indexed implTypeAndVersion
    );

    event ImplementationRemoved(
        address indexed implementation
    );

    event AdapterAdded(
        address newAdapter,
        address indexed implementation,
        bytes32 indexed implTypeAndVersion,
        address indexed manager,
        bytes32 description
    );

    struct ImplementationDetail {
        address implementation;
        string implTypeAndVersion;
    }

    /// @notice Elevated access can register a new implementation
    function addImplementation(address implementation) external;

    /// @notice Elevated access can remove an existing implementation
    function removeImplementation(address implementation) external;

    /**
     * @notice Clone a registered implementation to create a new instance of an adapter
     * @dev Permisionless to clone an existing implementation
     * @param implementation The pre-registered implementation to clone
     * @param manager A designated manager (typically only this is allowed to inititialize
     *        after cloning), added to the immutable args of the clone.
     *        The manager is set as the `bundler` for access rights in the bundler framework.
     * @param description A small string (as bytes32) description for this clone. 
     *        format is left to the caller to decide, but for consistency could (but not
     *        enforced) be structured like:
     *        "[ASSET1.symbol, ASSET2.symbol] / [DEBT1.symbol, DEBT2.symbol]"
     * @param immutableArgsData abi encoded representation of immutable args. Typically this
     *        is packed, and the implementation reads from the relevant runtime slot
     *        The immutable args are packed along with the `manager` (at slot 0x00) and 
     *        `description` (slot 0x14). Meaning `immutableArgsData` starts from slot 0x34
     */
    function create(
        address implementation,
        address manager,
        bytes32 description,
        bytes calldata immutableArgsData
    ) external returns (address);

    /// @notice Check if a given implemenation is registered
    function isRegistered(address implementation) external view returns (bool);

    /// @notice The number of registered implementations
    function numImplementations() external view returns (uint256);

    /**
     * @notice Paginated view of the implementations and their type/version string
     * @dev This can be called sequentially, increasing the `startIndex` each time by the number of items
     * returned in the previous call, until number of items returned is less than `maxItems`
     * Note if an implementation is removed then the order of the items may change.
     */
    function implementationDetailList(
        uint256 startIndex,
        uint256 maxItems
    ) external view returns (ImplementationDetail[] memory items);
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/opal/IOpalManager.sol)

import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IOpalAdapterFactory } from "contracts/interfaces/investments/opal/IOpalAdapterFactory.sol";
import { IOrigamiBundler } from "contracts/interfaces/common/bundler/IOrigamiBundler.sol";
import { IOrigamiTokenizedBalanceSheetVault } from "contracts/interfaces/common/IOrigamiTokenizedBalanceSheetVault.sol";

/**
 * @title Origami Portfolio of Assets and Liabilities (OPAL) Manager
 */
interface IOpalManager is IERC165, IOrigamiBundler {
    event FeeBpsSet(uint16 joinFeeBps, uint16 exitFeeBps);
    event AdapterAdded(address indexed adapter);
    event AdapterRemoved(address indexed adapter);
    event PluginApprovedSet(address indexed plugin, bool value);

    error AdapterStillActive();
    error InvalidAdapter();

    /// @notice Set which plugins (other than adapters) are allowed to be called
    /// as part of bundled multicalls
    function setPluginApproved(address plugin, bool value) external;

    function approvedPlugins(address plugin) external view returns (bool);

    /// @notice Set the join/exit fee, capped by the `MAX_FEE_BPS`
    function setFees(uint16 newJoinFeeBps, uint16 newExitFeeBps) external;

    /**
     * @notice Create a new adapter for a given implementation via the adapterFactory and add it
     * into the aggregated balance sheet.
     * @dev The asset/liability tokens from this new adapter will be added into the aggregated set
     * however it is likely the amounts in the balance sheet of that new adapter will be zero to begin with.
     * @param implementation An adapter implemenation to clone (must be pre-registered in the adapterFactory)
     * @param description A small string (as bytes32) description for this clone. 
     *        format is left to the caller to decide, but for consistency could (but not
     *        enforced) be structured like:
     *        "[ASSET1.symbol, ASSET2.symbol] / [DEBT1.symbol, DEBT2.symbol]"
     * @param immutableArgsData abi encoded representation of immutable args. This must be
     *        encoded in a way the implemenation can decode at runtime.
     * @param immutableArgsData abi encoded representation of initialization args for that implemenation.
     *        This must be encoded in a way the implemenation can decode. adapter.initialize() is
     *        called immediately after creating the new clone of the implementation
     */
    function addAdapter(
        address implementation,
        bytes32 description,
        bytes calldata immutableArgsData,
        bytes calldata initData
    ) external returns (address);

    /**
     * @notice Remove an adapter from the aggregated set.
     * @dev There's no explicit check that the balance sheeet of this outgoing adapter is empty
     * as there may be dust amounts or issues with that particular adapter.
     */
    function removeAdapter(address adapter) external;

    /// @dev Container of asset and liability balances
    struct AssetsAndLiabilities {
        uint256[] assets;
        uint256[] liabilities;
    }

    /// @dev Container of the aggregated and per adapter balance sheet balances
    struct BalanceSheetData {
        /// @dev The aggregated balance sheet data - totalled across all adapters. The index of each matches the `tokens()`
        AssetsAndLiabilities aggregatedBalanceSheet;

        /// @dev The per adapter balance sheet. Abi encoded `AssetsAndLiabilities[]`, where the index of each matches that
        /// underlying adapter's `tokens()`
        /// The sum of these across the adapters equals the respective index for the same token of `combinedAssetBalances` 
        /// and `combinedLiabilityBalances`
        bytes perAdapterBS;
    }

    /**
     * @notice Add equity by allocating the assets and borrowing the liabilities from the underlying adapters.
     * @param assets The total amount of assets to hold or deposit across all adapters, in its native decimals places
     * @param liabilities the total amount of debt across all adapters to send to the receiver, in its native decimals places
     * @param receiver The address to receive the `liabilities`
     * @param bsData The encoded `BalanceSheetData` struct which contains the per adapter balance sheet
     * to work out the proportions
     */
    function join(
        uint256[] calldata assets,
        uint256[] calldata liabilities,
        address receiver,
        BalanceSheetData calldata bsData
    ) external;

    /**
     * @notice Remove equity by repaying `liabilities` and removing `assets` collateral from the underlying adapters.
     * Receiver receives the assets
     * @param assets The total amount of assets across all adapters to send to the receiver, in its native decimals places
     * @param liabilities The total amount of debt to repay across all adapters, in its native decimals places
     * @param receiver The address to receive the `collateralAmount` of `collateralToken`
     * @param bsData The encoded `BalanceSheetData` struct which contains the per adapter balance sheet
     * to work out the proportions
     */
    function exit(
        uint256[] calldata assets,
        uint256[] calldata liabilities,
        address receiver,
        BalanceSheetData calldata bsData
    ) external;

    /**
     * @notice Describe how a set of assets and liabilities will be applied across the set of adapters
     */
    function allocationsAcrossAdapters(
        uint256[] calldata assets,
        uint256[] calldata liabilities
    ) external view returns (AssetsAndLiabilities[] memory);

    /// @notice The Origami vault this is managing
    function vault() external view returns (address);

    /// @notice The combined set of asset tokens across all adapters
    /// @dev Note the size and order of this may change as adapters can be added and removed over time
    function assetTokens() external view returns (address[] memory);

    /// @notice The combined set of liability tokens across all adapters
    /// @dev Note the size and order of this may change as adapters can be added and removed over time
    function liabilityTokens() external view returns (address[] memory);

    /// @notice The combined balances of asset and liability tokens across all adapters
    /// @param totalAssets The combined asset amounts across all adapters. The index correlates to assetTokens()
    /// @param totalLiabilities The combined liability amounts across all adapters. The index correlates to assetTokens()
    /// @param encodedBalanceSheetData The encoded `BalanceSheetData` struct which contains the per adapter balance sheet
    /// to cache and then work out the proportions when allocating to adapters
    function balanceSheet() external view returns (
        uint256[] memory totalAssets,
        uint256[] memory totalLiabilities,
        bytes memory encodedBalanceSheetData
    );

    /// @notice Returns the total amount of the ASSETS and LIABILITIES this vault can join with as of this block
    /// @dev
    /// - `assets` MUST return a list which is the same size and order as `assetTokens()`
    /// - `liabilities` MUST return a list which is the same size and order as `liabilityTokens()`
    /// - SHOULD return zero if paused or entirely capped
    /// - SHOULD return type(uint256).max if unlimited
    /// - MUST NOT revert.
    function maxJoin() external view returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /// @notice Returns the total amount of the ASSETS and LIABILITIES this vault can exit with as of this block
    /// @dev
    /// - `assets` MUST return a list which is the same size and order as `assetTokens()`
    /// - `liabilities` MUST return a list which is the same size and order as `liabilityTokens()`
    /// - SHOULD return zero if paused or entirely capped
    /// - SHOULD return type(uint256).max if unlimited
    /// - MUST NOT revert.
    function maxExit() external view returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    );

    /// @dev Container to represent a given token and the index in the manager level (combined) assetTokens() or liabilityTokens()
    struct AdapterToCombinedIndexMapItem {
        /// @dev erc20 token address for one of the adapter assetTokens() or liabilityTokens()
        /// Must also be in the manager level combined assetTokens() or liabilityTokens()
        address token;

        /// @dev Index in the assetTokens() or liabilityTokens()
        uint256 combinedIndex;
    }

    /// @notice Mapping each of the adapter's token index to the combined manager level token index
    function adapterDetails(address adapter) external view returns (
        AdapterToCombinedIndexMapItem[] memory assetToCombinedIndexMap,
        AdapterToCombinedIndexMapItem[] memory liabilityToCombinedIndexMap
    );

    /// @notice The list of currently active adapters
    function adapters() external view returns (address[] memory);

    /// @notice A list of adapters which contain `token` in either its assetTokens() or liabilityTokens()
    function adaptersWithToken(address token) external view returns (address[] memory adapters);

    /// @notice Whether joinWithShares and joinWithAssets are currently paused
    function areJoinsPaused() external view returns (bool);

    /// @notice Whether exitToShares and exitToAssets are currently paused
    function areExitsPaused() external view returns (bool);

    /// @notice The current joinWithShares or joinWithTokens fee in basis points.
    function joinFeeBps() external view returns (uint16 feeBps);

    /// @notice The current exitToShares or exitToTokens fee in basis points.
    function exitFeeBps() external view returns (uint16 feeBps);

    /// @notice The factory used to create new adapters.
    function adapterFactory() external view returns (IOpalAdapterFactory factory);

    /// @notice The maximum exit fee in basis points: 3.3%
    function MAX_FEE_BPS() external view returns (uint16);

    /// @notice The maximum number of active adapters allowed
    function MAX_ADAPTERS() external view returns (uint256);

    /// @notice The maximum number of active tokens allowed in either the combined assetTokens() OR managerTokens()
    function MAX_TOKENS() external view returns (uint256);

    /// @notice Return whether the tokenAddress is part of the balance sheet assets or liabilities, and the index
    /// of that address in the respective assetTokens() or liabilityTokens() list
    /// - kind === ASSET if an asset
    /// - kind === LIABILITY if a liability
    /// - kind === INVALID if not an asset or liability (index will be zero in this case)
    function matchToken(address tokenAddress) external view returns (IOrigamiTokenizedBalanceSheetVault.AssetOrLiability kind, uint256 index);
}

pragma solidity ^0.8.4;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/opal/IOpalVault.sol)

import { IOrigamiTokenizedBalanceSheetVault } from "contracts/interfaces/common/IOrigamiTokenizedBalanceSheetVault.sol";

/**
 * @title Origami Portfolio of Assets and Liabilities (OPAL) - Tokenized Balance Sheet (TBS) Vault 
 * @notice The logic to aggregate the adapter balance sheets and allocate out to the adapters is delegated to a manager.
 */
interface IOpalVault is IOrigamiTokenizedBalanceSheetVault {
    event PerformanceFeeSet(uint256 fee);
    event PerformanceFeesCollected(address indexed feeCollector, uint256 mintAmount);
    event FeeCollectorSet(address indexed feeCollector);

    /// @notice Set the vault annual performance fee
    /// @dev Represented in basis points
    function setAnnualPerformanceFee(uint48 _annualPerformanceFeeBps) external;

    /// @notice Set the Origami performance fee collector address
    function setFeeCollector(address _feeCollector) external;

    /// @notice Collect the performance fees to the Origami Treasury
    function collectPerformanceFees() external returns (uint256 amount);

    /// @notice The address used to collect the Origami performance fees.
    function feeCollector() external view returns (address);

    /// @notice The annual performance fee which Origami takes from harvested rewards before compounding into reserves.
    /// @dev Represented in basis points
    function annualPerformanceFeeBps() external view returns (uint48);

    /// @notice The last time the performance fee was collected
    function lastPerformanceFeeTime() external view returns (uint48);

    /// @notice The accrued performance fee amount which would be minted as of now, based on the total supply
    function accruedPerformanceFee() external view returns (uint256);

    /// @notice The maximum possible value for the Origami performance fee
    function MAX_PERFORMANCE_FEE_BPS() external view returns (uint16);
}

File 46 of 48 : CommonEventsAndErrors.sol
pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/CommonEventsAndErrors.sol)

/// @notice A collection of common events and errors thrown within the Origami contracts
library CommonEventsAndErrors {
    error InsufficientBalance(address token, uint256 required, uint256 balance);
    error InvalidToken(address token);
    error InvalidParam();
    error InvalidLength();
    error InvalidAddress(address addr);
    error InvalidAmount(address token, uint256 amount);
    error ExpectedNonZero();
    error Slippage(uint256 minAmountExpected, uint256 actualAmount);
    error IsPaused();
    error UnknownExecuteError(bytes returndata);
    error InvalidAccess();
    error BreachedMaxTotalSupply(uint256 totalSupply, uint256 maxTotalSupply);

    event TokenRecovered(address indexed to, address indexed token, uint256 amount);
}

pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/OrigamiMath.sol)

import { mulDiv as prbMulDiv, PRBMath_MulDiv_Overflow } from "@prb/math/src/Common.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @notice Utilities to operate on fixed point math multipliation and division
 * taking rounding into consideration
 */
library OrigamiMath {
    enum Rounding {
        ROUND_DOWN,
        ROUND_UP
    }

    uint256 public constant BASIS_POINTS_DIVISOR = 10_000;
    uint256 public constant WAD_DECIMALS = 18;
    uint256 public constant WAD = 10 ** WAD_DECIMALS;

    function scaleUp(uint256 amount, uint256 scalar) internal pure returns (uint256) {
        // Special case for scalar == 1, as it's common for token amounts to not need
        // scaling if decimal places are the same
        return scalar == 1 ? amount : amount * scalar;
    }

    function scaleDown(
        uint256 amount, 
        uint256 scalar, 
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        // Special case for scalar == 1, as it's common for token amounts to not need
        // scaling if decimal places are the same
        unchecked {
            if (scalar == 1) {
                result = amount;
            } else if (roundingMode == Rounding.ROUND_DOWN) {
                result = amount / scalar;
            } else {
                // ROUND_UP uses the same logic as OZ Math.ceilDiv()
                result = amount == 0 ? 0 : (amount - 1) / scalar + 1;
            }
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision,
     * rounding up
     */
    function mulDiv(
        uint256 x, 
        uint256 y, 
        uint256 denominator,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        result = prbMulDiv(x, y, denominator);
        if (roundingMode == Rounding.ROUND_UP) {
            if (mulmod(x, y, denominator) != 0) {
                if (result < type(uint256).max) {
                    unchecked {
                        result = result + 1;
                    }
                } else {
                    revert PRBMath_MulDiv_Overflow(x, y, denominator);
                }
            }
        }
    }

    function subtractBps(
        uint256 inputAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        uint256 numeratorBps;
        unchecked {
            numeratorBps = BASIS_POINTS_DIVISOR - basisPoints;
        }

        result = basisPoints < BASIS_POINTS_DIVISOR
            ? mulDiv(
                inputAmount,
                numeratorBps, 
                BASIS_POINTS_DIVISOR, 
                roundingMode
            ) : 0;
    }

    function addBps(
        uint256 inputAmount,
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        uint256 numeratorBps = BASIS_POINTS_DIVISOR + basisPoints;

        // Round up for max amounts out expected
        result = mulDiv(
            inputAmount,
            numeratorBps, 
            BASIS_POINTS_DIVISOR, 
            roundingMode
        );
    }

    /**
     * @notice Split the `inputAmount` into two parts based on the `basisPoints` fraction.
     * eg: 3333 BPS (33.3%) can be used to split an input amount of 600 into: (result=400, removed=200).
     * @dev The rounding mode is applied to the `result`
     */
    function splitSubtractBps(
        uint256 inputAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result, uint256 removed) {
        if (basisPoints == 0) return (inputAmount, 0); // gas shortcut for 0

        result = subtractBps(inputAmount, basisPoints, roundingMode);
        unchecked {
            removed = inputAmount - result;
        }
    }

    /**
     * @notice Reverse the fractional amount of an input.
     * eg: For 3333 BPS (33.3%) and the remainder=400, the result is 600
     */
    function inverseSubtractBps(
        uint256 remainderAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        if (basisPoints == 0) return remainderAmount; // gas shortcut for 0
        if (basisPoints >= BASIS_POINTS_DIVISOR) revert CommonEventsAndErrors.InvalidParam();

        uint256 denominatorBps;
        unchecked {
            denominatorBps = BASIS_POINTS_DIVISOR - basisPoints;
        }
        result = mulDiv(
            remainderAmount,
            BASIS_POINTS_DIVISOR, 
            denominatorBps, 
            roundingMode
        );
    }

    /**
     * @notice Calculate the relative difference of a value to a reference
     * @dev `value` and `referenceValue` must have the same precision
     * The denominator is always the referenceValue
     */
    function relativeDifferenceBps(
        uint256 value,
        uint256 referenceValue,
        Rounding roundingMode
    ) internal pure returns (uint256) {
        if (referenceValue == 0) revert CommonEventsAndErrors.InvalidParam();

        uint256 absDelta;
        unchecked {
            absDelta = value < referenceValue
                ? referenceValue - value
                : value - referenceValue;
        }

        return mulDiv(
            absDelta,
            BASIS_POINTS_DIVISOR,
            referenceValue,
            roundingMode
        );
    }
}

pragma solidity ^0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/OrigamiTBSLib.sol)

import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol";
import { IOrigamiTokenizedBalanceSheetVault as ITBSV } from "contracts/interfaces/common/IOrigamiTokenizedBalanceSheetVault.sol";

/// @dev Internal state cache of the balance sheet and input parameters.
/// This should be populated upfront before any actions are performed on the vault, 
/// and intended to immutable after that -- it should not be updated through the lifecycle of the transaction.
/// Motivation: Saves duplicated state and STATICCALL's, as well as circumventing stack-too-deep issues
struct TBSState {
    /// @dev The current list of asset tokens in the TBS
    address[] assetAddresses;

    /// @dev The current list of liability tokens in the TBS
    address[] liabilityAddresses;

    /// @dev The current balances of the assetTokens() in the balance sheet
    uint256[] assetBalances;

    /// @dev The current balances of the liabilityTokens() in the balance sheet
    uint256[] liabilityBalances;

    /// @dev The totalSupply of the vault
    uint256 totalSupply;

    /// @dev If the input token address is an asset/liability or neither
    ITBSV.AssetOrLiability inputTokenKind;

    /// @dev The index of the input token address. Always zero if invalid
    uint256 inputTokenIndex;

    /// @dev The amount of the input token. Set to zero if the token balance of 
    /// the input token address in the balance sheet is zero
    uint256 inputTokenAmount;

    /// @dev Any encoded state required by the implementation to allocate the total assets and liabilities.
    /// For example if the total needs to be split across multiple positions.
    /// May be left empty
    bytes balanceSheetData;
}

/// @dev A library to operate on cached Tokenized Balance Sheet state
library OrigamiTBSLib {
    using OrigamiMath for uint256;

    function _matchAmount(
        ITBSV.AssetOrLiability inputTokenKind,
        uint256 inputTokenIndex,
        uint256[] memory assetAmounts,
        uint256[] memory liabilityAmounts
    ) private pure returns (uint256 amount) {
        if (inputTokenKind == ITBSV.AssetOrLiability.ASSET) {
            return assetAmounts[inputTokenIndex];
        } else if (inputTokenKind == ITBSV.AssetOrLiability.LIABILITY) {
            return liabilityAmounts[inputTokenIndex];
        }

        // If INVALID return 0
    }

    function inputTokenBalance(TBSState memory $) internal pure returns (uint256 balance) {
        return _matchAmount($.inputTokenKind, $.inputTokenIndex, $.assetBalances, $.liabilityBalances);
    }

    /// @dev The available shares which can be minted as of now, under the maxTotalSupply
    /// If maxTotalSupply() is type(uint256).max, then this will also be type(uint256).max
    function availableSharesCapacity(
        uint256 totalSupply_, 
        uint256 maxTotalSupply_
    ) internal pure returns (uint256 shares) {
        if (maxTotalSupply_ == type(uint256).max) return maxTotalSupply_;

        if (totalSupply_ > maxTotalSupply_) {
            shares = 0;
        } else {
            unchecked {
                shares = maxTotalSupply_ - totalSupply_;
            }
        }
    }

    /// @dev Return whether the tokenAddress is part of the balance sheet assets or liabilities, and the index
    /// of that address in the respective assetTokens() or liabilityTokens() list
    /// - kind === ASSET if an asset
    /// - kind === LIABILITY if a liability
    /// - kind === INVALID if not an asset or liability (index will be zero in this case)
    function matchToken(
        address tokenAddress,
        address[] memory assetAddresses,
        address[] memory liabilityAddresses
    ) internal pure returns (ITBSV.AssetOrLiability kind, uint256 index) {
        uint256 i;
        for (; i < assetAddresses.length; ++i) {
            if (assetAddresses[i] == tokenAddress) {
                return (ITBSV.AssetOrLiability.ASSET, i);
            }
        }

        for (i = 0; i < liabilityAddresses.length; ++i) {
            if (liabilityAddresses[i] == tokenAddress) {
                return (ITBSV.AssetOrLiability.LIABILITY, i);
            }
        }

        // Leave uninitialized as ITBSV.AssetOrLiability.INVALID
    }

    /// @dev Convert an amount of one of the asset/liability tokens (specified in the `$` state) into
    /// the full set of shares/assets/liabilities.
    /// Based off the balance of that token in the vault and total supply of shares
    function convertInputTokenAmountToSharesAndTokens(
        TBSState memory $,
        OrigamiMath.Rounding sharesRounding,
        OrigamiMath.Rounding assetsRounding,
        OrigamiMath.Rounding liabilitiesRounding
    ) internal pure returns (
        uint256 shares,
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        shares = tokenToShares($.inputTokenAmount, inputTokenBalance($), $.totalSupply, sharesRounding);

        (assets, liabilities) = proportionalTokenAmountsFromShares({
            shares: shares,
            $: $,
            assetsRounding: assetsRounding,
            liabilitiesRounding: liabilitiesRounding
        });
    }

    /// @dev For each of the asset and liability tokens, calculate the proportional token
    /// amount given an amount of shares and the current total supply
    function proportionalTokenAmountsFromShares(
        TBSState memory $,
        uint256 shares,
        OrigamiMath.Rounding assetsRounding,
        OrigamiMath.Rounding liabilitiesRounding
    ) internal pure returns (
        uint256[] memory assets,
        uint256[] memory liabilities
    ) {
        assets = _proportionalTokenAmountsFromShares($, shares, ITBSV.AssetOrLiability.ASSET, assetsRounding);
        liabilities = _proportionalTokenAmountsFromShares($, shares, ITBSV.AssetOrLiability.LIABILITY, liabilitiesRounding);
    }

    function sharesToToken(
        uint256 shares,
        uint256 tokenBalance,
        uint256 totalSupply,
        OrigamiMath.Rounding rounding
    ) internal pure returns (uint256) {
        return totalSupply == 0
            ? 0
            : shares.mulDiv(tokenBalance, totalSupply, rounding);
    }

    function tokenToShares(
        uint256 tokenAmount,
        uint256 tokenBalance,
        uint256 totalSupply,
        OrigamiMath.Rounding rounding
    ) internal pure returns (uint256) {
        return tokenBalance == 0
            ? 0
            : tokenAmount.mulDiv(totalSupply, tokenBalance, rounding);
    }

    /// @dev For each token in either asset or liability tokens (specified by `forKind`),
    /// calculate the proportional token amount given an amount of shares and the current total supply
    ///   otherTokenAmount = shares * otherTokenBalance / totalSupply
    function _proportionalTokenAmountsFromShares(
        TBSState memory $,
        uint256 shares,
        ITBSV.AssetOrLiability forKind,
        OrigamiMath.Rounding rounding
    ) private pure returns (uint256[] memory amounts) {
        uint256 length = forKind == ITBSV.AssetOrLiability.ASSET 
            ? $.assetBalances.length
            : $.liabilityBalances.length;
        amounts = new uint256[](length);
        for (uint256 i; i < length; ++i) {
            if ($.totalSupply == 0) {
                // denominator is zero
                amounts[i] = 0;
            } else if (i == $.inputTokenIndex && $.inputTokenKind == forKind) {
                // If this if for the specified input token kind and index, use that amount exactly
                // rather than a rounded calculation which could cause some differences
                amounts[i] = $.inputTokenAmount;
            } else {
                // Calculate the amount as `shares * balance[i] / totalSupply`
                amounts[i] = sharesToToken(
                    shares,
                    _tokenBalance($, forKind, i),
                    $.totalSupply,
                    rounding
                );
            }
        }
    }

    /// @dev Get the cached token balance for a given kind and index of token in assets or liabilities
    function _tokenBalance(TBSState memory $, ITBSV.AssetOrLiability forKind, uint256 forIndex) private pure returns (uint256 bal) {
        if (forKind == ITBSV.AssetOrLiability.ASSET && forIndex < $.assetBalances.length) {
            bal = $.assetBalances[forIndex];
        } else if (forKind == ITBSV.AssetOrLiability.LIABILITY && forIndex < $.liabilityBalances.length) {
            bal = $.liabilityBalances[forIndex];
        }

        // Otherwise returns 0
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 9999
  },
  "evmVersion": "cancun",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"initialOwner_","type":"address"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint48","name":"_annualPerformanceFeeBps","type":"uint48"},{"internalType":"address","name":"_feeCollector","type":"address"},{"internalType":"address","name":"tokenPrices_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ExceededMaxExitWithShares","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ExceededMaxExitWithToken","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ExceededMaxJoinWithShares","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ExceededMaxJoinWithToken","type":"error"},{"inputs":[],"name":"ExpectedNonZero","type":"error"},{"inputs":[],"name":"IncorrectTokensHash","type":"error"},{"inputs":[],"name":"InvalidAccess","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidParam","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath_MulDiv_Overflow","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"CurrentTokensHashSet","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"liabilities","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"ExplicitAccessSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeCollector","type":"address"}],"name":"FeeCollectorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IOrigamiTokenizedBalanceSheetVault.FeeType","name":"feeType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"feeBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"InKindFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"liabilities","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Join","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"ManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxTotalSupply","type":"uint256"}],"name":"MaxTotalSupplySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"NewOwnerAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"oldProposedOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newProposedOwner","type":"address"}],"name":"NewOwnerProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"PerformanceFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeCollector","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"PerformanceFeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"teleporter","type":"address"}],"name":"TeleporterSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenPrices","type":"address"}],"name":"TokenPricesSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PERFORMANCE_FEE_BPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accruedPerformanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOwner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"annualPerformanceFeeBps","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"areExitsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"areJoinsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetTokens","outputs":[{"internalType":"address[]","name":"assets","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableSharesCapacity","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balanceSheet","outputs":[{"internalType":"uint256[]","name":"totalAssets","type":"uint256[]"},{"internalType":"uint256[]","name":"totalLiabilities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectPerformanceFees","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertFromShares","outputs":[{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"convertFromToken","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTokensHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exitFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"sharesOwner","type":"address"},{"internalType":"bytes32","name":"tokensHash","type":"bytes32"}],"name":"exitWithShares","outputs":[{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"sharesOwner","type":"address"},{"internalType":"bytes32","name":"tokensHash","type":"bytes32"}],"name":"exitWithToken","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"explicitFunctionAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"joinFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes32","name":"tokensHash","type":"bytes32"}],"name":"joinWithShares","outputs":[{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes32","name":"tokensHash","type":"bytes32"}],"name":"joinWithToken","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastPerformanceFeeTime","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liabilityTokens","outputs":[{"internalType":"address[]","name":"liabilities","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"matchToken","outputs":[{"internalType":"enum IOrigamiTokenizedBalanceSheetVault.AssetOrLiability","name":"kind","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sharesOwner","type":"address"}],"name":"maxExitWithShares","outputs":[{"internalType":"uint256","name":"maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"sharesOwner","type":"address"}],"name":"maxExitWithToken","outputs":[{"internalType":"uint256","name":"maxToken","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxJoinWithShares","outputs":[{"internalType":"uint256","name":"maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"maxJoinWithToken","outputs":[{"internalType":"uint256","name":"maxToken","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewExitWithShares","outputs":[{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"previewExitWithToken","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewJoinWithShares","outputs":[{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"previewJoinWithToken","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilities","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"proposeNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"uint256","name":"amountLD","type":"uint256"},{"internalType":"uint256","name":"minAmountLD","type":"uint256"},{"internalType":"bytes","name":"extraOptions","type":"bytes"},{"internalType":"bytes","name":"composeMsg","type":"bytes"},{"internalType":"bytes","name":"oftCmd","type":"bytes"}],"internalType":"struct SendParam","name":"sendParam","type":"tuple"},{"internalType":"bool","name":"payInLzToken","type":"bool"}],"name":"quoteSend","outputs":[{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"fee","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"liabilityAmounts","type":"uint256[]"},{"internalType":"uint256","name":"sharesToMint","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"newMaxTotalSupply","type":"uint256"},{"internalType":"bytes","name":"balanceSheetData","type":"bytes"}],"name":"seed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"uint256","name":"amountLD","type":"uint256"},{"internalType":"uint256","name":"minAmountLD","type":"uint256"},{"internalType":"bytes","name":"extraOptions","type":"bytes"},{"internalType":"bytes","name":"composeMsg","type":"bytes"},{"internalType":"bytes","name":"oftCmd","type":"bytes"}],"internalType":"struct SendParam","name":"sendParam","type":"tuple"},{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"fee","type":"tuple"},{"internalType":"address","name":"refundAddress","type":"address"}],"name":"send","outputs":[{"components":[{"internalType":"bytes32","name":"guid","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"fee","type":"tuple"}],"internalType":"struct MessagingReceipt","name":"msgReceipt","type":"tuple"},{"components":[{"internalType":"uint256","name":"amountSentLD","type":"uint256"},{"internalType":"uint256","name":"amountReceivedLD","type":"uint256"}],"internalType":"struct OFTReceipt","name":"oftReceipt","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_annualPerformanceFeeBps","type":"uint48"}],"name":"setAnnualPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"allowedCaller","type":"address"},{"components":[{"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"internalType":"bool","name":"allowed","type":"bool"}],"internalType":"struct IOrigamiElevatedAccess.ExplicitAccess[]","name":"access","type":"tuple[]"}],"name":"setExplicitAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newManager","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxTotalSupply_","type":"uint256"}],"name":"setMaxTotalSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newTeleporter","type":"address"}],"name":"setTeleporter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenPrices","type":"address"}],"name":"setTokenPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"teleporter","outputs":[{"internalType":"contract IOFT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPrices","outputs":[{"internalType":"contract ITokenPrices","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokens","outputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address[]","name":"liabilities","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateCurrentTokensHash","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x610160604052348015610010575f5ffd5b5060405161631338038061631383398101604081905261002f9161037d565b85858583828285808380604051806040016040528060018152602001603160f81b8152508686816003908161006491906104b2565b50600461007182826104b2565b50610081915083905060056101dc565b610120526100908160066101dc565b61014052815160208084019190912060e052815190820120610100524660a05261011c60e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b60805250503060c0525061012f8161020e565b50506001600d555050601080546001600160a01b0319166001600160a01b03929092169190911790555050506103e865ffffffffffff8416111561018657604051633494a40d60e21b815260040160405180910390fd5b50601280546001600160a01b03908116600160a01b65ffffffffffff958616026001600160d01b031617600160d01b429590951694909402939093176001600160a01b03191691909216179055506105c4915050565b5f6020835110156101f7576101f083610288565b9050610208565b8161020284826104b2565b5060ff90505b92915050565b6009546001600160a01b03161561023857604051633006171960e21b815260040160405180910390fd5b6001600160a01b03811661026657604051634726455360e11b81525f60048201526024015b60405180910390fd5b600980546001600160a01b0319166001600160a01b0392909216919091179055565b5f5f829050601f815111156102b2578260405163305a27a960e01b815260040161025d919061056c565b80516102bd826105a1565b179392505050565b80516001600160a01b03811681146102db575f5ffd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112610303575f5ffd5b81516001600160401b0381111561031c5761031c6102e0565b604051601f8201601f19908116603f011681016001600160401b038111828210171561034a5761034a6102e0565b604052818152838201602001851015610361575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f5f5f5f5f5f60c08789031215610392575f5ffd5b61039b876102c5565b60208801519096506001600160401b038111156103b6575f5ffd5b6103c289828a016102f4565b604089015190965090506001600160401b038111156103df575f5ffd5b6103eb89828a016102f4565b945050606087015165ffffffffffff81168114610406575f5ffd5b9250610414608088016102c5565b915061042260a088016102c5565b90509295509295509295565b600181811c9082168061044257607f821691505b60208210810361046057634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156104ad57805f5260205f20601f840160051c8101602085101561048b5750805b601f840160051c820191505b818110156104aa575f8155600101610497565b50505b505050565b81516001600160401b038111156104cb576104cb6102e0565b6104df816104d9845461042e565b84610466565b6020601f821160018114610511575f83156104fa5750848201515b5f19600385901b1c1916600184901b1784556104aa565b5f84815260208120601f198516915b828110156105405787850151825560209485019460019092019101610520565b508482101561055d57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80516020808301519190811015610460575f1960209190910360031b1b16919050565b60805160a05160c05160e051610100516101205161014051615cfe6106155f395f61175c01525f61173101525f61340301525f6133db01525f61333601525f61336001525f61338a0152615cfe5ff3fe608060405260043610610434575f3560e01c80638c625c1511610237578063b3ff9f491161013c578063d505accf116100b7578063dd62ed3e11610087578063e96513f61161006d578063e96513f614610c95578063e9cad76614610cb4578063ebbc496514610cc8575f5ffd5b8063dd62ed3e14610c62578063e9607c0114610c81575f5ffd5b8063d505accf14610bcc578063d932916b14610beb578063d9b47d1c14610c0a578063daeccc7914610c29575f5ffd5b8063c28ab6751161010c578063c7c7f5b3116100f2578063c7c7f5b314610b5f578063c83ae6e514610b80578063d0ebdbe714610bad575f5ffd5b8063c28ab67514610b21578063c415b95c14610b40575f5ffd5b8063b3ff9f4914610aa5578063be2f503914610ac4578063bfccf0ec14610ae3578063c1a9759014610b02575f5ffd5b8063a42dce80116101cc578063a9059cbb1161019c578063b1e1fca411610182578063b1e1fca414610a48578063b1f8100d14610a67578063b2317e6b14610a86575f5ffd5b8063a9059cbb14610a15578063af1454d314610a34575f5ffd5b8063a42dce8014610982578063a457c2d7146109a1578063a5c59ba6146109c0578063a7229fd9146109f6575f5ffd5b806395d89b411161020757806395d89b41146109195780639a6b27cf1461092d5780639d63848a14610941578063a3696a0914610963575f5ffd5b80638c625c151461089d5780638da5cb5b146108bc5780638f39a80b146108db578063907ad64a146108fa575f5ffd5b80633644e5151161033d5780634d8fea1f116102d257806370a08231116102a25780637ecebe00116102885780637ecebe001461084257806382d4c5c11461086157806384b0196e14610876575f5ffd5b806370a08231146107ef578063795095cf14610823575f5ffd5b80634d8fea1f1461078b5780634e7e240d1461079f57806357b17a52146107b357806370889165146107c7575f5ffd5b80633f3e4c111161030d5780633f3e4c111461070857806342966c681461072757806342c20b7214610746578063481c6a751461075a575f5ffd5b80633644e5151461068a578063395093511461069e5780633b6f743b146106bd5780633d992aab146106e9575f5ffd5b806320c3627f116103cd5780632ab419501161039d5780632b968958116103835780632b9689581461063a578063313ce56714610650578063359d6ea91461066b575f5ffd5b80632ab41950146106075780632ab4d05214610626575f5ffd5b806320c3627f146105885780632113f244146105a757806322285cf6146105c657806323b872dd146105e8575f5ffd5b80630c14935e116104085780630c14935e146104d15780630e5f8f96146104f357806317b0774a1461054657806318160ddd14610574575f5ffd5b8062274eb31461043857806301ffc9a71461046257806306fdde0314610491578063095ea7b3146104b2575b5f5ffd5b348015610443575f5ffd5b5061044c610cdc565b6040516104599190614ce8565b60405180910390f35b34801561046d575f5ffd5b5061048161047c366004614d2e565b610d66565b6040519015158152602001610459565b34801561049c575f5ffd5b506104a5610dc2565b6040516104599190614d75565b3480156104bd575f5ffd5b506104816104cc366004614d9b565b610e52565b3480156104dc575f5ffd5b506104e5610e69565b604051908152602001610459565b3480156104fe575f5ffd5b5060125461052f907a010000000000000000000000000000000000000000000000000000900465ffffffffffff1681565b60405165ffffffffffff9091168152602001610459565b348015610551575f5ffd5b50610565610560366004614dc5565b610ef8565b60405161045993929190614e3a565b34801561057f575f5ffd5b506002546104e5565b348015610593575f5ffd5b506105656105a2366004614d9b565b611041565b3480156105b2575f5ffd5b506105656105c1366004614d9b565b61106f565b3480156105d1575f5ffd5b506105da611096565b604051610459929190614e6e565b3480156105f3575f5ffd5b50610481610602366004614e92565b6110ab565b348015610612575f5ffd5b506105da610621366004614ed0565b6110d0565b348015610631575f5ffd5b50600e546104e5565b348015610645575f5ffd5b5061064e6110f7565b005b34801561065b575f5ffd5b5060405160128152602001610459565b348015610676575f5ffd5b506104e5610685366004614ee7565b6111b5565b348015610695575f5ffd5b506104e56111c6565b3480156106a9575f5ffd5b506104816106b8366004614d9b565b6111cf565b3480156106c8575f5ffd5b506106dc6106d7366004614f1f565b6111f0565b6040516104599190614f6e565b3480156106f4575f5ffd5b506105da610703366004614f85565b61128a565b348015610713575f5ffd5b5061064e610722366004614ed0565b6113c1565b348015610732575f5ffd5b5061064e610741366004614ed0565b61143f565b348015610751575f5ffd5b5061064e61144c565b348015610765575f5ffd5b506011546001600160a01b03165b6040516001600160a01b039091168152602001610459565b348015610796575f5ffd5b506104e56114c1565b3480156107aa575f5ffd5b506104e56114d2565b3480156107be575f5ffd5b506104e561155f565b3480156107d2575f5ffd5b506107dc6103e881565b60405161ffff9091168152602001610459565b3480156107fa575f5ffd5b506104e5610809366004614ee7565b6001600160a01b03165f9081526020819052604090205490565b34801561082e575f5ffd5b5061056561083d366004614fba565b6115bf565b34801561084d575f5ffd5b506104e561085c366004614ee7565b611707565b34801561086c575f5ffd5b506104e5600f5481565b348015610881575f5ffd5b5061088a611724565b6040516104599796959493929190615011565b3480156108a8575f5ffd5b5061064e6108b736600461509a565b6117c7565b3480156108c7575f5ffd5b50600954610773906001600160a01b031681565b3480156108e6575f5ffd5b506105da6108f5366004614ed0565b6118a8565b348015610905575f5ffd5b50610565610914366004614d9b565b6118c7565b348015610924575f5ffd5b506104a56118e5565b348015610938575f5ffd5b506104816118f4565b34801561094c575f5ffd5b50610955611978565b6040516104599291906150bf565b34801561096e575f5ffd5b5061064e61097d366004614ee7565b611993565b34801561098d575f5ffd5b5061064e61099c366004614ee7565b611a47565b3480156109ac575f5ffd5b506104816109bb366004614d9b565b611afe565b3480156109cb575f5ffd5b5060125461052f9074010000000000000000000000000000000000000000900465ffffffffffff1681565b348015610a01575f5ffd5b5061064e610a10366004614e92565b611b9b565b348015610a20575f5ffd5b50610481610a2f366004614d9b565b611c09565b348015610a3f575f5ffd5b50610481611c16565b348015610a53575f5ffd5b50601054610773906001600160a01b031681565b348015610a72575f5ffd5b5061064e610a81366004614ee7565b611c76565b348015610a91575f5ffd5b506104e5610aa03660046150e3565b611d42565b348015610ab0575f5ffd5b50600c54610773906001600160a01b031681565b348015610acf575f5ffd5b5061064e610ade366004614ee7565b611d8f565b348015610aee575f5ffd5b5061064e610afd36600461510f565b611e46565b348015610b0d575f5ffd5b506104e5610b1c3660046150e3565b611fc6565b348015610b2c575f5ffd5b506104e5610b3b366004614ee7565b611ffd565b348015610b4b575f5ffd5b50601254610773906001600160a01b031681565b610b72610b6d366004615192565b61200f565b6040516104599291906151fb565b348015610b8b575f5ffd5b50610b9f610b9a366004614ee7565b6120f8565b60405161045992919061527a565b348015610bb8575f5ffd5b5061064e610bc7366004614ee7565b61217f565b348015610bd7575f5ffd5b5061064e610be6366004615298565b612245565b348015610bf6575f5ffd5b506105da610c05366004615309565b6123a6565b348015610c15575f5ffd5b506105da610c24366004614ed0565b6124de565b348015610c34575f5ffd5b50610481610c4336600461532d565b600a60209081525f928352604080842090915290825290205460ff1681565b348015610c6d575f5ffd5b506104e5610c7c3660046150e3565b612505565b348015610c8c575f5ffd5b5061044c61254f565b348015610ca0575f5ffd5b5061064e610caf3660046153a8565b6125af565b348015610cbf575f5ffd5b506104e561283e565b348015610cd3575f5ffd5b5061064e612858565b601154604080517e274eb300000000000000000000000000000000000000000000000000000000815290516060926001600160a01b03169162274eb3916004808301925f9291908290030181865afa158015610d3a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610d619190810190615543565b905090565b5f610d708261290b565b80610dbc57507fffffffff0000000000000000000000000000000000000000000000000000000082167f76d3d7dd00000000000000000000000000000000000000000000000000000000145b92915050565b606060038054610dd1906155d8565b80601f0160208091040260200160405190810160405280929190818152602001828054610dfd906155d8565b8015610e485780601f10610e1f57610100808354040283529160200191610e48565b820191905f5260205f20905b815481529060010190602001808311610e2b57829003601f168201915b5050505050905090565b5f33610e5f8185856129b2565b5060019392505050565b6012545f908190610ea0907a010000000000000000000000000000000000000000000000000000900465ffffffffffff1642615650565b9050610ef2610eae60025490565b601254610edc90849074010000000000000000000000000000000000000000900465ffffffffffff16615663565b610eec6127106301e13380615663565b5f612b09565b91505090565b5f606080610f04612ba2565b600f548414610f3f576040517f4c16a56e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610f4a8888612bfb565b90505f610f556114d2565b90505f610f628383612c73565b905080891115610fc4576040517ff0cc4e3b0000000000000000000000000000000000000000000000000000000081526001600160a01b03808a1660048301528b166024820152604481018a9052606481018290526084015b60405180910390fd5b5f610fcf8484612ce4565b92995097509095509050801561101b577f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb78091404275f84836040516110129392919061567a565b60405180910390a15b61102984338b8a8a8a612d3c565b505050506110376001600d55565b9450945094915050565b5f60608061105f6110528686612bfb565b61105a61155f565b612de4565b9298909750919550909350505050565b5f6060806110886110808686612bfb565b5f5f5f612e3f565b919790965090945092505050565b6060806110a1612e7d565b5090939092509050565b5f336110b8858285612f03565b6110c3858585612f7b565b60019150505b9392505050565b6060806110ec6110de613166565b846110e761155f565b6131a8565b909590945092505050565b6110ff6131d8565b600b546001600160a01b031661dead1461115457600b546040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610fbb565b6009546040515f916001600160a01b0316907f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d554908390a3600980547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b5f610dbc6111c1613166565b61323d565b5f610d6161332a565b5f33610e5f8185856111e18383612505565b6111eb91906156a2565b6129b2565b6040805180820182525f8082526020820152600c5491517f3b6f743b00000000000000000000000000000000000000000000000000000000815290916001600160a01b031690633b6f743b9061124c90869086906004016157e1565b6040805180830381865afa158015611266573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110c99190615833565b606080611295612ba2565b600f5483146112d0576040517f4c16a56e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6112da85613453565b90508087111561132f576040517fdc1803de0000000000000000000000000000000000000000000000000000000081526001600160a01b03861660048201526024810188905260448101829052606401610fbb565b5f611338613166565b90505f61134361155f565b90505f611351838b846131a8565b90975095509050801561139b577f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb7809140427600183836040516113929392919061567a565b60405180910390a15b6113aa83338b8b8e8b8b61349c565b505050506113b86001600d55565b94509492505050565b6113c96131d8565b6002545f03611404576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e8190556040518181527f0120f799fc820eabb910038e9cce6e8024add369b4d780181846e300df2844849060200160405180910390a150565b611449338261362f565b50565b5f61145561254f565b61145d610cdc565b60405160200161146e9291906150bf565b60405160208183030381529060405280519060200120905080600f541461144957600f81905560405181907f53686c3e94eb711ffe2ba47b5a14603bb04ba6a04d7b515b7093ba0027ad0009905f90a250565b5f6114ca6131d8565b610d61613796565b601154604080517f4e7e240d00000000000000000000000000000000000000000000000000000000815290515f926001600160a01b031691634e7e240d9160048083019260209291908290030181865afa158015611532573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611556919061584d565b61ffff16905090565b601154604080517f57b17a5200000000000000000000000000000000000000000000000000000000815290515f926001600160a01b0316916357b17a529160048083019260209291908290030181865afa158015611532573d5f5f3e3d5ffd5b5f6060806115cb612ba2565b600f548414611606576040517f4c16a56e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6116118989612bfb565b90505f61161c61155f565b90505f61162a838984613843565b9050808a1115611687576040517f691bf43d0000000000000000000000000000000000000000000000000000000081526001600160a01b03808a1660048301528c166024820152604481018b905260648101829052608401610fbb565b5f6116928484612de4565b9299509750909550905080156116df577f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb7809140427600184836040516116d69392919061567a565b60405180910390a15b6116ee84338c8c8b8b8b61349c565b505050506116fc6001600d55565b955095509592505050565b6001600160a01b0381165f90815260076020526040812054610dbc565b5f606080828080836117577f000000000000000000000000000000000000000000000000000000000000000060056138e9565b6117827f000000000000000000000000000000000000000000000000000000000000000060066138e9565b604080515f808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b6117cf6131d8565b6103e865ffffffffffff82161115611813576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61181b613796565b5060405165ffffffffffff821681527fceb20f7f0b19335681096ee1eaa9bb2a6ef5a9a69ba48b6b488e7b7eff2ef04d9060200160405180910390a16012805465ffffffffffff90921674010000000000000000000000000000000000000000027fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6060806118be6118b6613166565b845f5f613992565b91509150915091565b5f60608061105f6118d88686612bfb565b6118e06114d2565b612ce4565b606060048054610dd1906155d8565b601154604080517f9a6b27cf00000000000000000000000000000000000000000000000000000000815290515f926001600160a01b031691639a6b27cf9160048083019260209291908290030181865afa158015611954573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d61919061586e565b60608061198361254f565b915061198d610cdc565b90509091565b61199b6131d8565b6001600160a01b0381166119e6576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610fbb565b600c80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f7ab5072a2f334db02e20cd6aa963f87434f813ef38fba53eea68704c7445ddb9905f90a250565b611a4f6131d8565b6001600160a01b038116611a91576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081525f6004820152602401610fbb565b6040516001600160a01b038216907f12e1d17016b94668449f97876f4a8d5cc2c19f314db337418894734037cc19d4905f90a2601280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b5f3381611b0b8286612505565b905083811015611b835760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610fbb565b611b9082868684036129b2565b506001949350505050565b611ba36131d8565b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f909683604051611be891815260200190565b60405180910390a3611c046001600160a01b03841683836139bc565b505050565b5f33610e5f818585612f7b565b601154604080517faf1454d300000000000000000000000000000000000000000000000000000000815290515f926001600160a01b03169163af1454d39160048083019260209291908290030181865afa158015611954573d5f5f3e3d5ffd5b611c7e6131d8565b6001600160a01b038116611cc9576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610fbb565b600b546009546040516001600160a01b038085169381169216907f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d8905f90a4600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b5f5f611d4e845f612bfb565b90505f611d5b8285613a65565b90505f19811015611d7c57611d79611d7161155f565b82905f613b50565b90505b611d868282613b72565b95945050505050565b611d976131d8565b6001600160a01b038116611dd9576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081525f6004820152602401610fbb565b6040516001600160a01b038216907f2781e03d8cf8be1845f40e150af1187b0cdb48dccd761a708f5e5b612a865d1d905f90a2601080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b611e4e6131d8565b6001600160a01b038316611e99576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610fbb565b604080518082019091525f80825260208201525f5b82811015611fbf57838382818110611ec857611ec8615889565b905060400201803603810190611ede91906158b6565b915081602001511515825f01517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b0387165f908152600a8352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055600101611eae565b5050505050565b5f5f611fd2845f612bfb565b90505f611fde8261323d565b90505f19811015611d7c57611d79611ff46114d2565b82906001613bb0565b5f610dbc612009613166565b83613a65565b612017614c0b565b6040805180820182525f808252602082015290850135612038333083612f7b565b600c546040517fc7c7f5b30000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063c7c7f5b3903490612087908a908a908a906004016158f5565b60c06040518083038185885af11580156120a3573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906120c89190615933565b805191945092505f906120db9083615650565b905080156120ee576120ee303383612f7b565b5050935093915050565b6011546040517fc83ae6e50000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301525f92839291169063c83ae6e5906024016040805180830381865afa15801561215b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118be91906159bd565b6121876131d8565b6001600160a01b0381166121c9576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081525f6004820152602401610fbb565b6011546001600160a01b0382811691161461223d57601180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa69905f90a25b61144961144c565b834211156122955760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610fbb565b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886122c38c613c10565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f61231d82613c35565b90505f61232c82878787613c7c565b9050896001600160a01b0316816001600160a01b03161461238f5760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610fbb565b61239a8a8a8a6129b2565b50505050505050505050565b6060806123b1612ba2565b600f5483146123ec576040517f4c16a56e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6123f5613166565b90505f6124058260800151613ca2565b90508087111561245a576040517ff35b483d0000000000000000000000000000000000000000000000000000000081526001600160a01b03871660048201526024810188905260448101829052606401610fbb565b5f6124636114d2565b90505f612471848a84613cc4565b9197509550905080156124ba577f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb78091404275f83836040516124b19392919061567a565b60405180910390a15b6124c884338a8c8a8a612d3c565b505050506124d66001600d55565b935093915050565b6060806124fa6124ec613166565b846124f56114d2565b613cc4565b509094909350915050565b600c545f906001600160a01b03838116911614612546576001600160a01b038084165f908152600160209081526040808320938616835292905220546110c9565b505f1992915050565b601154604080517fe9607c0100000000000000000000000000000000000000000000000000000000815290516060926001600160a01b03169163e9607c01916004808301925f9291908290030181865afa158015610d3a573d5f5f3e3d5ffd5b6125b76131d8565b600254156125f1576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6125f9614c4c565b61260161254f565b815261260b610cdc565b81602001819052508989808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050505060408083019190915280516020808a02828101820190935289825290918a918a9182918501908490808284375f920191909152505050506060820152604080516020601f85018190048102820181019092528381529084908490819084018382808284375f9201919091525050505061010082015280515189146126f5576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020810151518714612733576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83861115612786576040517ff35b483d0000000000000000000000000000000000000000000000000000000081526001600160a01b03861660048201526024810187905260448101859052606401610fbb565b600e8490556040518481527f0120f799fc820eabb910038e9cce6e8024add369b4d780181846e300df2844849060200160405180910390a161239a813387898e8e808060200260200160405190810160405280939291908181526020018383602002808284375f81840152601f19601f820116905080830192505050505050508d8d808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d3c92505050565b5f610d6161284b60025490565b600e54613cf9565b613cf9565b600b546001600160a01b0316331461289c576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095460405133916001600160a01b0316907f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d554905f90a3600980547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600b80549091169055565b5f61291582613d1d565b8061296157507fffffffff0000000000000000000000000000000000000000000000000000000082167f8f85dbd200000000000000000000000000000000000000000000000000000000145b80610dbc57507fffffffff0000000000000000000000000000000000000000000000000000000082167fdec81de9000000000000000000000000000000000000000000000000000000001492915050565b6001600160a01b038316612a2d5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b038216612aa95760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b5f612b15858585613ee4565b90506001826001811115612b2b57612b2b61524d565b03612b9a578280612b3e57612b3e6159ec565b84860915612b9a575f19811015612b5757600101612b9a565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610fbb565b949350505050565b6002600d5403612bf45760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610fbb565b6002600d55565b612c03614c4c565b612c0b613166565b9050612c1f83825f01518360200151613fcc565b60c0830181905260a08301826002811115612c3c57612c3c61524d565b6002811115612c4d57612c4d61524d565b8152505050612c5b81614078565b15612c665781612c68565b5f5b60e082015292915050565b5f612c7c611c16565b15612c8857505f610dbc565b612c9183614078565b5f03612c9e57505f610dbc565b5f612cb08460800151612853600e5490565b90505f198103612cc1579050610dbc565b612b9a612cd082856001613bb0565b612cd986614078565b86608001515f614095565b5f80606080612d1c60018760a001516002811115612d0457612d0461524d565b14612d10576001612d12565b5f5b879060015f612e3f565b91955092509050612d2e84865f6140b7565b909790965091945092509050565b825f03612d75576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d838686868686866140e2565b612d8d848461420e565b836001600160a01b0316856001600160a01b03167f8bf99c37e0a09491ed7cb29409f9fcd5a61a01752ab57bbf20d72ce465330dab848487604051612dd493929190615a19565b60405180910390a3505050505050565b5f8060608082612e1d60018860a001516002811115612e0557612e0561524d565b14612e10575f612e13565b60015b88905f6001612e3f565b90945092509050612e3081876001613bb0565b97908803965091945092509050565b5f606080612e5f8760e00151612e5489614078565b8960800151896142cb565b9250612e6d87848787613992565b9398909750929550919350505050565b606080606060115f9054906101000a90046001600160a01b03166001600160a01b03166322285cf66040518163ffffffff1660e01b81526004015f60405180830381865afa158015612ed1573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612ef89190810190615aa9565b925092509250909192565b5f612f0e8484612505565b90505f198114612f755781811015612f685760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610fbb565b612f7584848484036129b2565b50505050565b6001600160a01b038316612ff75760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b0382166130735760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b0383165f90815260208190526040902054818110156131015760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b038481165f81815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3612f75565b61316e614c4c565b61317661254f565b8152613180610cdc565b602082015261318d612e7d565b61010084015260608301526040820152600254608082015290565b5f6060806131b78585856140b7565b90955092506131c986865f6001613992565b93979096509294509192505050565b613205335f357fffffffff00000000000000000000000000000000000000000000000000000000166142de565b61323b576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f5f5f60115f9054906101000a90046001600160a01b03166001600160a01b03166345d67f0e6040518163ffffffff1660e01b81526004015f60405180830381865afa15801561328f573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526132b69190810190615b91565b915091506132cf84604001518386608001515f19614345565b92506132e5846060015182866080015186614345565b92505f19831015613306576133036132fb6114d2565b84905f613b50565b92505b50505f6133168360800151613ca2565b905081811015613324578091505b50919050565b5f306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561338257507f000000000000000000000000000000000000000000000000000000000000000046145b156133ac57507f000000000000000000000000000000000000000000000000000000000000000090565b610d61604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f61345c6118f4565b1561346857505f919050565b6001600160a01b03821615613494576001600160a01b0382165f90815260208190526040902054610dbc565b5f1992915050565b825f036134d5576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038416613520576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610fbb565b6001600160a01b03851661356b576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0386166004820152602401610fbb565b836001600160a01b0316866001600160a01b03161461358f5761358f848785612f03565b61359e878786888787876143cb565b6135a8848461362f565b82876080018181516135ba9190615650565b90525060808701515f036135cd575f600e555b836001600160a01b0316856001600160a01b0316876001600160a01b03167f728a5684b1f77b03e07bd227ddcef96ef31c5268eb61122aabcd651e08f752a185858860405161361e93929190615a19565b60405180910390a450505050505050565b6001600160a01b0382166136ab5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b0382165f90815260208190526040902054818110156137395760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b0383165f818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b5f61379f610e69565b905080156137f7576012546040518281526001600160a01b039091169081907f8ca882445572a0beb49b440f5a329364aa0678559e2511916cf49b557a43cff99060200160405180910390a26137f5818361420e565b505b6012805479ffffffffffffffffffffffffffffffffffffffffffffffffffff167a0100000000000000000000000000000000000000000000000000004265ffffffffffff160217905590565b5f61384c6118f4565b1561385857505f6110c9565b5f61386285614078565b9050805f03613874575f9150506110c9565b6001600160a01b03841661388c575f199150506110c9565b6001600160a01b0384165f9081526020819052604081205490506138b181855f6140b7565b5090505f6138cd826138c289614078565b89608001515f614095565b90508281106138dc57826138de565b805b979650505050505050565b606060ff8314613903576138fc836144d8565b9050610dbc565b81805461390f906155d8565b80601f016020809104026020016040519081016040528092919081815260200182805461393b906155d8565b80156139865780601f1061395d57610100808354040283529160200191613986565b820191905f5260205f20905b81548152906001019060200180831161396957829003601f168201915b50505050509050610dbc565b6060806139a28686600187614515565b91506139b18686600286614515565b905094509492505050565b6040516001600160a01b038316602482015260448101829052611c049084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152614662565b5f5f5f60115f9054906101000a90046001600160a01b03166001600160a01b0316637fd382086040518163ffffffff1660e01b81526004015f60405180830381865afa158015613ab7573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613ade9190810190615b91565b91509150613af785604001518387608001515f19614345565b9250613b0d856060015182876080015186614345565b92505f19831015613b2f57613b2c613b2361155f565b84906001613bb0565b92505b50505f613b3b83613453565b905081811015613b49578091505b5092915050565b5f612710838103908410613b64575f611d86565b611d86858261271086612b09565b5f5f613b7d84614078565b9050805f03613b8f575f915050610dbc565b5f198303613ba05782915050610dbc565b612b9a838286608001515f614095565b5f825f03613bbf5750826110c9565b6127108310613bfa576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83612710039050611d86856127108386612b09565b6001600160a01b0381165f908152600760205260409020805460018101825590613324565b5f610dbc613c4161332a565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b5f5f5f613c8b87878787614748565b91509150613c9881614802565b5095945050505050565b5f613cab611c16565b15613cb757505f919050565b610dbc82612853600e5490565b6060805f80613cd586866001613bb0565b86810392509050613ce9878260015f613992565b9098909750919550909350505050565b5f5f198203613d09575080610dbc565b81831115613d1857505f610dbc565b500390565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167fec3e74c8000000000000000000000000000000000000000000000000000000001480613daf57507fffffffff0000000000000000000000000000000000000000000000000000000082167fa219a02500000000000000000000000000000000000000000000000000000000145b80613dfb57507fffffffff0000000000000000000000000000000000000000000000000000000082167f36372b0700000000000000000000000000000000000000000000000000000000145b80613e4757507fffffffff0000000000000000000000000000000000000000000000000000000082167f9d8ff7da00000000000000000000000000000000000000000000000000000000145b80613e9357507fffffffff0000000000000000000000000000000000000000000000000000000082167f84b0196e00000000000000000000000000000000000000000000000000000000145b80610dbc57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001492915050565b5f80805f19858709858702925082811083820303915050805f03613f1b57838281613f1157613f116159ec565b04925050506110c9565b838110613f65576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610fbb565b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f5f5f5b845181101561401e57856001600160a01b0316858281518110613ff557613ff5615889565b60200260200101516001600160a01b031603614016576001925090506124d6565b600101613fd0565b505f5b835181101561406f57856001600160a01b031684828151811061404657614046615889565b60200260200101516001600160a01b031603614067576002925090506124d6565b600101614021565b50935093915050565b5f610dbc8260a001518360c0015184604001518560600151614966565b5f82156140ad576140a885858585612b09565b611d86565b5f95945050505050565b5f5f835f036140ca57508390505f6124d6565b6140d5858585613b50565b9594869003949350505050565b5f5b825181101561415e576141568660115f9054906101000a90046001600160a01b031685848151811061411857614118615889565b60200260200101518a5f0151858151811061413557614135615889565b60200260200101516001600160a01b03166149cc909392919063ffffffff16565b6001016140e4565b5060408051608081018252878201518183019081526060808a0151908301528152610100880151602082015260115491517f15dd5cce00000000000000000000000000000000000000000000000000000000815290916001600160a01b0316906315dd5cce906141d890869086908a908790600401615bf6565b5f604051808303815f87803b1580156141ef575f5ffd5b505af1158015614201573d5f5f3e3d5ffd5b5050505050505050505050565b6001600160a01b0382166142645760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610fbb565b8060025f82825461427591906156a2565b90915550506001600160a01b0382165f81815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b5f83156140ad576140a885848685612b09565b6009545f906001600160a01b03848116911614806110c95750506001600160a01b03919091165f908152600a602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000009094168352929052205460ff1690565b805f8080805b87518110156143bf5788818151811061436657614366615889565b60200260200101519250825f03156143b75787818151811061438a5761438a615889565b602002602001015191505f198203156143b7576143a98284895f6142cb565b9350848410156143b7578394505b60010161434b565b50505050949350505050565b5f5b81518110156144275761441f8760115f9054906101000a90046001600160a01b031684848151811061440157614401615889565b60200260200101518b60200151858151811061413557614135615889565b6001016143cd565b5060408051608081018252888201518183019081526060808b0151908301528152610100890151602082015260115491517f8ce3c1ad00000000000000000000000000000000000000000000000000000000815290916001600160a01b031690638ce3c1ad906144a190869086908a908790600401615bf6565b5f604051808303815f87803b1580156144b8575f5ffd5b505af11580156144ca573d5f5f3e3d5ffd5b505050505050505050505050565b60605f6144e483614a1d565b6040805160208082528183019092529192505f91906020820181803683375050509182525060208101929092525090565b60605f600184600281111561452c5761452c61524d565b1461453c57856060015151614543565b8560400151515b90508067ffffffffffffffff81111561455e5761455e615499565b604051908082528060200260200182016040528015614587578160200160208202803683370190505b5091505f5b818110156146585786608001515f036145c3575f8382815181106145b2576145b2615889565b602002602001018181525050614650565b8660c00151811480156145fb57508460028111156145e3576145e361524d565b8760a0015160028111156145f9576145f961524d565b145b15614617578660e001518382815181106145b2576145b2615889565b61463186614626898885614a5d565b896080015187614095565b83828151811061464357614643615889565b6020026020010181815250505b60010161458c565b5050949350505050565b5f6146b6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614afe9092919063ffffffff16565b905080515f14806146d65750808060200190518101906146d6919061586e565b611c045760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610fbb565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561477d57505f905060036113b8565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156147ce573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b0381166147f6575f600192509250506113b8565b965f9650945050505050565b5f8160048111156148155761481561524d565b0361481d5750565b60018160048111156148315761483161524d565b0361487e5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610fbb565b60028160048111156148925761489261524d565b036148df5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610fbb565b60038160048111156148f3576148f361524d565b036114495760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b5f600185600281111561497b5761497b61524d565b036149a15782848151811061499257614992615889565b60200260200101519050612b9a565b60028560028111156149b5576149b561524d565b03612b9a5781848151811061499257614992615889565b6040516001600160a01b0380851660248301528316604482015260648101829052612f759085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613a01565b5f60ff8216601f811115610dbc576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6001836002811115614a7257614a7261524d565b148015614a83575083604001515182105b15614aad5783604001518281518110614a9e57614a9e615889565b602002602001015190506110c9565b6002836002811115614ac157614ac161524d565b148015614ad2575083606001515182105b156110c95783606001518281518110614aed57614aed615889565b602002602001015190509392505050565b6060612b9a84845f85855f5f866001600160a01b03168587604051614b239190615cb2565b5f6040518083038185875af1925050503d805f8114614b5d576040519150601f19603f3d011682016040523d82523d5f602084013e614b62565b606091505b50915091506138de8783838760608315614bdc5782515f03614bd5576001600160a01b0385163b614bd55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610fbb565b5081612b9a565b612b9a8383815115614bf15781518083602001fd5b8060405162461bcd60e51b8152600401610fbb9190614d75565b60405180606001604052805f81526020015f67ffffffffffffffff168152602001614c4760405180604001604052805f81526020015f81525090565b905290565b604051806101200160405280606081526020016060815260200160608152602001606081526020015f81526020015f6002811115614c8c57614c8c61524d565b81526020015f81526020015f8152602001606081525090565b5f8151808452602084019350602083015f5b82811015614cde5781516001600160a01b0316865260209586019590910190600101614cb7565b5093949350505050565b602081525f6110c96020830184614ca5565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114614d29575f5ffd5b919050565b5f60208284031215614d3e575f5ffd5b6110c982614cfa565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6110c96020830184614d47565b6001600160a01b0381168114611449575f5ffd5b5f5f60408385031215614dac575f5ffd5b8235614db781614d87565b946020939093013593505050565b5f5f5f5f60808587031215614dd8575f5ffd5b8435614de381614d87565b9350602085013592506040850135614dfa81614d87565b9396929550929360600135925050565b5f8151808452602084019350602083015f5b82811015614cde578151865260209586019590910190600101614e1c565b838152606060208201525f614e526060830185614e0a565b8281036040840152614e648185614e0a565b9695505050505050565b604081525f614e806040830185614e0a565b8281036020840152611d868185614e0a565b5f5f5f60608486031215614ea4575f5ffd5b8335614eaf81614d87565b92506020840135614ebf81614d87565b929592945050506040919091013590565b5f60208284031215614ee0575f5ffd5b5035919050565b5f60208284031215614ef7575f5ffd5b81356110c981614d87565b5f60e08284031215613324575f5ffd5b8015158114611449575f5ffd5b5f5f60408385031215614f30575f5ffd5b823567ffffffffffffffff811115614f46575f5ffd5b614f5285828601614f02565b9250506020830135614f6381614f12565b809150509250929050565b815181526020808301519082015260408101610dbc565b5f5f5f5f60808587031215614f98575f5ffd5b843593506020850135614faa81614d87565b92506040850135614dfa81614d87565b5f5f5f5f5f60a08688031215614fce575f5ffd5b8535614fd981614d87565b9450602086013593506040860135614ff081614d87565b9250606086013561500081614d87565b949793965091946080013592915050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e060208201525f61504b60e0830189614d47565b828103604084015261505d8189614d47565b90508660608401526001600160a01b03861660808401528460a084015282810360c084015261508c8185614e0a565b9a9950505050505050505050565b5f602082840312156150aa575f5ffd5b813565ffffffffffff811681146110c9575f5ffd5b604081525f6150d16040830185614ca5565b8281036020840152611d868185614ca5565b5f5f604083850312156150f4575f5ffd5b82356150ff81614d87565b91506020830135614f6381614d87565b5f5f5f60408486031215615121575f5ffd5b833561512c81614d87565b9250602084013567ffffffffffffffff811115615147575f5ffd5b8401601f81018613615157575f5ffd5b803567ffffffffffffffff81111561516d575f5ffd5b8660208260061b8401011115615181575f5ffd5b939660209190910195509293505050565b5f5f5f83850360808112156151a5575f5ffd5b843567ffffffffffffffff8111156151bb575f5ffd5b6151c787828801614f02565b9450506040601f19820112156151db575f5ffd5b5060208401915060608401356151f081614d87565b809150509250925092565b5f60c0820190508351825267ffffffffffffffff60208501511660208301526040840151615236604084018280518252602090810151910152565b5082516080830152602083015160a08301526110c9565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b604081016003841061528e5761528e61524d565b9281526020015290565b5f5f5f5f5f5f5f60e0888a0312156152ae575f5ffd5b87356152b981614d87565b965060208801356152c981614d87565b95506040880135945060608801359350608088013560ff811681146152ec575f5ffd5b9699959850939692959460a0840135945060c09093013592915050565b5f5f5f6060848603121561531b575f5ffd5b833592506020840135614ebf81614d87565b5f5f6040838503121561533e575f5ffd5b823561534981614d87565b915061535760208401614cfa565b90509250929050565b5f5f83601f840112615370575f5ffd5b50813567ffffffffffffffff811115615387575f5ffd5b6020830191508360208260051b85010111156153a1575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f60c08a8c0312156153c0575f5ffd5b893567ffffffffffffffff8111156153d6575f5ffd5b6153e28c828d01615360565b909a5098505060208a013567ffffffffffffffff811115615401575f5ffd5b61540d8c828d01615360565b90985096505060408a0135945060608a013561542881614d87565b935060808a0135925060a08a013567ffffffffffffffff81111561544a575f5ffd5b8a01601f81018c1361545a575f5ffd5b803567ffffffffffffffff811115615470575f5ffd5b8c6020828401011115615481575f5ffd5b60208201935080925050509295985092959850929598565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040805190810167ffffffffffffffff811182821017156154e9576154e9615499565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561551857615518615499565b604052919050565b5f67ffffffffffffffff82111561553957615539615499565b5060051b60200190565b5f60208284031215615553575f5ffd5b815167ffffffffffffffff811115615569575f5ffd5b8201601f81018413615579575f5ffd5b805161558c61558782615520565b6154ef565b8082825260208201915060208360051b8501019250868311156155ad575f5ffd5b6020840193505b82841015614e645783516155c781614d87565b8252602093840193909101906155b4565b600181811c908216806155ec57607f821691505b602082108103613324577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610dbc57610dbc615623565b8082028115828204841417610dbc57610dbc615623565b606081016002851061568e5761568e61524d565b938152602081019290925260409091015290565b80820180821115610dbc57610dbc615623565b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156e8575f5ffd5b830160208101925035905067ffffffffffffffff811115615707575f5ffd5b8036038213156153a1575f5ffd5b81835281816020850137505f602082840101525f6020601f19601f840116840101905092915050565b5f813563ffffffff81168114615752575f5ffd5b63ffffffff16835260208281013590840152604080830135908401526060808301359084015261578560808301836156b5565b60e0608086015261579a60e086018284615715565b9150506157aa60a08401846156b5565b85830360a08701526157bd838284615715565b925050506157ce60c08401846156b5565b85830360c0870152614e64838284615715565b604081525f6157f3604083018561573e565b905082151560208301529392505050565b5f60408284031215615814575f5ffd5b61581c6154c6565b825181526020928301519281019290925250919050565b5f60408284031215615843575f5ffd5b6110c98383615804565b5f6020828403121561585d575f5ffd5b815161ffff811681146110c9575f5ffd5b5f6020828403121561587e575f5ffd5b81516110c981614f12565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60408284031280156158c7575f5ffd5b506158d06154c6565b6158d983614cfa565b815260208301356158e981614f12565b60208201529392505050565b608081525f615907608083018661573e565b8435602084810191909152909401356040830152506001600160a01b0391909116606090910152919050565b5f5f82840360c0811215615945575f5ffd5b6080811215615952575f5ffd5b506040516060810167ffffffffffffffff8111828210171561597657615976615499565b60405283518152602084015167ffffffffffffffff81168114615997575f5ffd5b60208201526159a98560408601615804565b604082015291506153578460808501615804565b5f5f604083850312156159ce575f5ffd5b8251600381106159dc575f5ffd5b6020939093015192949293505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b606081525f615a2b6060830186614e0a565b8281036020840152615a3d8186614e0a565b915050826040830152949350505050565b5f82601f830112615a5d575f5ffd5b8151615a6b61558782615520565b8082825260208201915060208360051b860101925085831115615a8c575f5ffd5b602085015b83811015613c98578051835260209283019201615a91565b5f5f5f60608486031215615abb575f5ffd5b835167ffffffffffffffff811115615ad1575f5ffd5b615add86828701615a4e565b935050602084015167ffffffffffffffff811115615af9575f5ffd5b615b0586828701615a4e565b925050604084015167ffffffffffffffff811115615b21575f5ffd5b8401601f81018613615b31575f5ffd5b805167ffffffffffffffff811115615b4b57615b4b615499565b615b5e6020601f19601f840116016154ef565b818152876020838501011115615b72575f5ffd5b8160208401602083015e5f602083830101528093505050509250925092565b5f5f60408385031215615ba2575f5ffd5b825167ffffffffffffffff811115615bb8575f5ffd5b615bc485828601615a4e565b925050602083015167ffffffffffffffff811115615be0575f5ffd5b615bec85828601615a4e565b9150509250929050565b608081525f615c086080830187614e0a565b8281036020840152615c1a8187614e0a565b90506001600160a01b038516604084015282810360608401528351604082528051604080840152615c4e6080840182614e0a565b9050602082015191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0838203016060840152615c8b8183614e0a565b91505060208501518282036020840152615ca58282614d47565b9998505050505050505050565b5f82518060208501845e5f92019182525091905056fea264697066735822122035d6c62a12c0924b5973583f3ce2fb75c68124dcf5acaef0508eedb75bb6f60364736f6c634300081c0033000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d8000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000781b4c57100738095222bd92d37b07ed034ab696000000000000000000000000d6c68aac3c46e754ca54a551560ce07cb89dc20b00000000000000000000000000000000000000000000000000000000000000114f50414c2050542d735553446520286129000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6f70616c2d50542d73555344652d610000000000000000000000000000000000

Deployed Bytecode

0x608060405260043610610434575f3560e01c80638c625c1511610237578063b3ff9f491161013c578063d505accf116100b7578063dd62ed3e11610087578063e96513f61161006d578063e96513f614610c95578063e9cad76614610cb4578063ebbc496514610cc8575f5ffd5b8063dd62ed3e14610c62578063e9607c0114610c81575f5ffd5b8063d505accf14610bcc578063d932916b14610beb578063d9b47d1c14610c0a578063daeccc7914610c29575f5ffd5b8063c28ab6751161010c578063c7c7f5b3116100f2578063c7c7f5b314610b5f578063c83ae6e514610b80578063d0ebdbe714610bad575f5ffd5b8063c28ab67514610b21578063c415b95c14610b40575f5ffd5b8063b3ff9f4914610aa5578063be2f503914610ac4578063bfccf0ec14610ae3578063c1a9759014610b02575f5ffd5b8063a42dce80116101cc578063a9059cbb1161019c578063b1e1fca411610182578063b1e1fca414610a48578063b1f8100d14610a67578063b2317e6b14610a86575f5ffd5b8063a9059cbb14610a15578063af1454d314610a34575f5ffd5b8063a42dce8014610982578063a457c2d7146109a1578063a5c59ba6146109c0578063a7229fd9146109f6575f5ffd5b806395d89b411161020757806395d89b41146109195780639a6b27cf1461092d5780639d63848a14610941578063a3696a0914610963575f5ffd5b80638c625c151461089d5780638da5cb5b146108bc5780638f39a80b146108db578063907ad64a146108fa575f5ffd5b80633644e5151161033d5780634d8fea1f116102d257806370a08231116102a25780637ecebe00116102885780637ecebe001461084257806382d4c5c11461086157806384b0196e14610876575f5ffd5b806370a08231146107ef578063795095cf14610823575f5ffd5b80634d8fea1f1461078b5780634e7e240d1461079f57806357b17a52146107b357806370889165146107c7575f5ffd5b80633f3e4c111161030d5780633f3e4c111461070857806342966c681461072757806342c20b7214610746578063481c6a751461075a575f5ffd5b80633644e5151461068a578063395093511461069e5780633b6f743b146106bd5780633d992aab146106e9575f5ffd5b806320c3627f116103cd5780632ab419501161039d5780632b968958116103835780632b9689581461063a578063313ce56714610650578063359d6ea91461066b575f5ffd5b80632ab41950146106075780632ab4d05214610626575f5ffd5b806320c3627f146105885780632113f244146105a757806322285cf6146105c657806323b872dd146105e8575f5ffd5b80630c14935e116104085780630c14935e146104d15780630e5f8f96146104f357806317b0774a1461054657806318160ddd14610574575f5ffd5b8062274eb31461043857806301ffc9a71461046257806306fdde0314610491578063095ea7b3146104b2575b5f5ffd5b348015610443575f5ffd5b5061044c610cdc565b6040516104599190614ce8565b60405180910390f35b34801561046d575f5ffd5b5061048161047c366004614d2e565b610d66565b6040519015158152602001610459565b34801561049c575f5ffd5b506104a5610dc2565b6040516104599190614d75565b3480156104bd575f5ffd5b506104816104cc366004614d9b565b610e52565b3480156104dc575f5ffd5b506104e5610e69565b604051908152602001610459565b3480156104fe575f5ffd5b5060125461052f907a010000000000000000000000000000000000000000000000000000900465ffffffffffff1681565b60405165ffffffffffff9091168152602001610459565b348015610551575f5ffd5b50610565610560366004614dc5565b610ef8565b60405161045993929190614e3a565b34801561057f575f5ffd5b506002546104e5565b348015610593575f5ffd5b506105656105a2366004614d9b565b611041565b3480156105b2575f5ffd5b506105656105c1366004614d9b565b61106f565b3480156105d1575f5ffd5b506105da611096565b604051610459929190614e6e565b3480156105f3575f5ffd5b50610481610602366004614e92565b6110ab565b348015610612575f5ffd5b506105da610621366004614ed0565b6110d0565b348015610631575f5ffd5b50600e546104e5565b348015610645575f5ffd5b5061064e6110f7565b005b34801561065b575f5ffd5b5060405160128152602001610459565b348015610676575f5ffd5b506104e5610685366004614ee7565b6111b5565b348015610695575f5ffd5b506104e56111c6565b3480156106a9575f5ffd5b506104816106b8366004614d9b565b6111cf565b3480156106c8575f5ffd5b506106dc6106d7366004614f1f565b6111f0565b6040516104599190614f6e565b3480156106f4575f5ffd5b506105da610703366004614f85565b61128a565b348015610713575f5ffd5b5061064e610722366004614ed0565b6113c1565b348015610732575f5ffd5b5061064e610741366004614ed0565b61143f565b348015610751575f5ffd5b5061064e61144c565b348015610765575f5ffd5b506011546001600160a01b03165b6040516001600160a01b039091168152602001610459565b348015610796575f5ffd5b506104e56114c1565b3480156107aa575f5ffd5b506104e56114d2565b3480156107be575f5ffd5b506104e561155f565b3480156107d2575f5ffd5b506107dc6103e881565b60405161ffff9091168152602001610459565b3480156107fa575f5ffd5b506104e5610809366004614ee7565b6001600160a01b03165f9081526020819052604090205490565b34801561082e575f5ffd5b5061056561083d366004614fba565b6115bf565b34801561084d575f5ffd5b506104e561085c366004614ee7565b611707565b34801561086c575f5ffd5b506104e5600f5481565b348015610881575f5ffd5b5061088a611724565b6040516104599796959493929190615011565b3480156108a8575f5ffd5b5061064e6108b736600461509a565b6117c7565b3480156108c7575f5ffd5b50600954610773906001600160a01b031681565b3480156108e6575f5ffd5b506105da6108f5366004614ed0565b6118a8565b348015610905575f5ffd5b50610565610914366004614d9b565b6118c7565b348015610924575f5ffd5b506104a56118e5565b348015610938575f5ffd5b506104816118f4565b34801561094c575f5ffd5b50610955611978565b6040516104599291906150bf565b34801561096e575f5ffd5b5061064e61097d366004614ee7565b611993565b34801561098d575f5ffd5b5061064e61099c366004614ee7565b611a47565b3480156109ac575f5ffd5b506104816109bb366004614d9b565b611afe565b3480156109cb575f5ffd5b5060125461052f9074010000000000000000000000000000000000000000900465ffffffffffff1681565b348015610a01575f5ffd5b5061064e610a10366004614e92565b611b9b565b348015610a20575f5ffd5b50610481610a2f366004614d9b565b611c09565b348015610a3f575f5ffd5b50610481611c16565b348015610a53575f5ffd5b50601054610773906001600160a01b031681565b348015610a72575f5ffd5b5061064e610a81366004614ee7565b611c76565b348015610a91575f5ffd5b506104e5610aa03660046150e3565b611d42565b348015610ab0575f5ffd5b50600c54610773906001600160a01b031681565b348015610acf575f5ffd5b5061064e610ade366004614ee7565b611d8f565b348015610aee575f5ffd5b5061064e610afd36600461510f565b611e46565b348015610b0d575f5ffd5b506104e5610b1c3660046150e3565b611fc6565b348015610b2c575f5ffd5b506104e5610b3b366004614ee7565b611ffd565b348015610b4b575f5ffd5b50601254610773906001600160a01b031681565b610b72610b6d366004615192565b61200f565b6040516104599291906151fb565b348015610b8b575f5ffd5b50610b9f610b9a366004614ee7565b6120f8565b60405161045992919061527a565b348015610bb8575f5ffd5b5061064e610bc7366004614ee7565b61217f565b348015610bd7575f5ffd5b5061064e610be6366004615298565b612245565b348015610bf6575f5ffd5b506105da610c05366004615309565b6123a6565b348015610c15575f5ffd5b506105da610c24366004614ed0565b6124de565b348015610c34575f5ffd5b50610481610c4336600461532d565b600a60209081525f928352604080842090915290825290205460ff1681565b348015610c6d575f5ffd5b506104e5610c7c3660046150e3565b612505565b348015610c8c575f5ffd5b5061044c61254f565b348015610ca0575f5ffd5b5061064e610caf3660046153a8565b6125af565b348015610cbf575f5ffd5b506104e561283e565b348015610cd3575f5ffd5b5061064e612858565b601154604080517e274eb300000000000000000000000000000000000000000000000000000000815290516060926001600160a01b03169162274eb3916004808301925f9291908290030181865afa158015610d3a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610d619190810190615543565b905090565b5f610d708261290b565b80610dbc57507fffffffff0000000000000000000000000000000000000000000000000000000082167f76d3d7dd00000000000000000000000000000000000000000000000000000000145b92915050565b606060038054610dd1906155d8565b80601f0160208091040260200160405190810160405280929190818152602001828054610dfd906155d8565b8015610e485780601f10610e1f57610100808354040283529160200191610e48565b820191905f5260205f20905b815481529060010190602001808311610e2b57829003601f168201915b5050505050905090565b5f33610e5f8185856129b2565b5060019392505050565b6012545f908190610ea0907a010000000000000000000000000000000000000000000000000000900465ffffffffffff1642615650565b9050610ef2610eae60025490565b601254610edc90849074010000000000000000000000000000000000000000900465ffffffffffff16615663565b610eec6127106301e13380615663565b5f612b09565b91505090565b5f606080610f04612ba2565b600f548414610f3f576040517f4c16a56e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610f4a8888612bfb565b90505f610f556114d2565b90505f610f628383612c73565b905080891115610fc4576040517ff0cc4e3b0000000000000000000000000000000000000000000000000000000081526001600160a01b03808a1660048301528b166024820152604481018a9052606481018290526084015b60405180910390fd5b5f610fcf8484612ce4565b92995097509095509050801561101b577f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb78091404275f84836040516110129392919061567a565b60405180910390a15b61102984338b8a8a8a612d3c565b505050506110376001600d55565b9450945094915050565b5f60608061105f6110528686612bfb565b61105a61155f565b612de4565b9298909750919550909350505050565b5f6060806110886110808686612bfb565b5f5f5f612e3f565b919790965090945092505050565b6060806110a1612e7d565b5090939092509050565b5f336110b8858285612f03565b6110c3858585612f7b565b60019150505b9392505050565b6060806110ec6110de613166565b846110e761155f565b6131a8565b909590945092505050565b6110ff6131d8565b600b546001600160a01b031661dead1461115457600b546040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610fbb565b6009546040515f916001600160a01b0316907f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d554908390a3600980547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b5f610dbc6111c1613166565b61323d565b5f610d6161332a565b5f33610e5f8185856111e18383612505565b6111eb91906156a2565b6129b2565b6040805180820182525f8082526020820152600c5491517f3b6f743b00000000000000000000000000000000000000000000000000000000815290916001600160a01b031690633b6f743b9061124c90869086906004016157e1565b6040805180830381865afa158015611266573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110c99190615833565b606080611295612ba2565b600f5483146112d0576040517f4c16a56e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6112da85613453565b90508087111561132f576040517fdc1803de0000000000000000000000000000000000000000000000000000000081526001600160a01b03861660048201526024810188905260448101829052606401610fbb565b5f611338613166565b90505f61134361155f565b90505f611351838b846131a8565b90975095509050801561139b577f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb7809140427600183836040516113929392919061567a565b60405180910390a15b6113aa83338b8b8e8b8b61349c565b505050506113b86001600d55565b94509492505050565b6113c96131d8565b6002545f03611404576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e8190556040518181527f0120f799fc820eabb910038e9cce6e8024add369b4d780181846e300df2844849060200160405180910390a150565b611449338261362f565b50565b5f61145561254f565b61145d610cdc565b60405160200161146e9291906150bf565b60405160208183030381529060405280519060200120905080600f541461144957600f81905560405181907f53686c3e94eb711ffe2ba47b5a14603bb04ba6a04d7b515b7093ba0027ad0009905f90a250565b5f6114ca6131d8565b610d61613796565b601154604080517f4e7e240d00000000000000000000000000000000000000000000000000000000815290515f926001600160a01b031691634e7e240d9160048083019260209291908290030181865afa158015611532573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611556919061584d565b61ffff16905090565b601154604080517f57b17a5200000000000000000000000000000000000000000000000000000000815290515f926001600160a01b0316916357b17a529160048083019260209291908290030181865afa158015611532573d5f5f3e3d5ffd5b5f6060806115cb612ba2565b600f548414611606576040517f4c16a56e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6116118989612bfb565b90505f61161c61155f565b90505f61162a838984613843565b9050808a1115611687576040517f691bf43d0000000000000000000000000000000000000000000000000000000081526001600160a01b03808a1660048301528c166024820152604481018b905260648101829052608401610fbb565b5f6116928484612de4565b9299509750909550905080156116df577f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb7809140427600184836040516116d69392919061567a565b60405180910390a15b6116ee84338c8c8b8b8b61349c565b505050506116fc6001600d55565b955095509592505050565b6001600160a01b0381165f90815260076020526040812054610dbc565b5f606080828080836117577f4f50414c2050542d73555344652028612900000000000000000000000000001160056138e9565b6117827f310000000000000000000000000000000000000000000000000000000000000160066138e9565b604080515f808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b6117cf6131d8565b6103e865ffffffffffff82161115611813576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61181b613796565b5060405165ffffffffffff821681527fceb20f7f0b19335681096ee1eaa9bb2a6ef5a9a69ba48b6b488e7b7eff2ef04d9060200160405180910390a16012805465ffffffffffff90921674010000000000000000000000000000000000000000027fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6060806118be6118b6613166565b845f5f613992565b91509150915091565b5f60608061105f6118d88686612bfb565b6118e06114d2565b612ce4565b606060048054610dd1906155d8565b601154604080517f9a6b27cf00000000000000000000000000000000000000000000000000000000815290515f926001600160a01b031691639a6b27cf9160048083019260209291908290030181865afa158015611954573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d61919061586e565b60608061198361254f565b915061198d610cdc565b90509091565b61199b6131d8565b6001600160a01b0381166119e6576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610fbb565b600c80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f7ab5072a2f334db02e20cd6aa963f87434f813ef38fba53eea68704c7445ddb9905f90a250565b611a4f6131d8565b6001600160a01b038116611a91576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081525f6004820152602401610fbb565b6040516001600160a01b038216907f12e1d17016b94668449f97876f4a8d5cc2c19f314db337418894734037cc19d4905f90a2601280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b5f3381611b0b8286612505565b905083811015611b835760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610fbb565b611b9082868684036129b2565b506001949350505050565b611ba36131d8565b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f909683604051611be891815260200190565b60405180910390a3611c046001600160a01b03841683836139bc565b505050565b5f33610e5f818585612f7b565b601154604080517faf1454d300000000000000000000000000000000000000000000000000000000815290515f926001600160a01b03169163af1454d39160048083019260209291908290030181865afa158015611954573d5f5f3e3d5ffd5b611c7e6131d8565b6001600160a01b038116611cc9576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610fbb565b600b546009546040516001600160a01b038085169381169216907f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d8905f90a4600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b5f5f611d4e845f612bfb565b90505f611d5b8285613a65565b90505f19811015611d7c57611d79611d7161155f565b82905f613b50565b90505b611d868282613b72565b95945050505050565b611d976131d8565b6001600160a01b038116611dd9576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081525f6004820152602401610fbb565b6040516001600160a01b038216907f2781e03d8cf8be1845f40e150af1187b0cdb48dccd761a708f5e5b612a865d1d905f90a2601080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b611e4e6131d8565b6001600160a01b038316611e99576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610fbb565b604080518082019091525f80825260208201525f5b82811015611fbf57838382818110611ec857611ec8615889565b905060400201803603810190611ede91906158b6565b915081602001511515825f01517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b0387165f908152600a8352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055600101611eae565b5050505050565b5f5f611fd2845f612bfb565b90505f611fde8261323d565b90505f19811015611d7c57611d79611ff46114d2565b82906001613bb0565b5f610dbc612009613166565b83613a65565b612017614c0b565b6040805180820182525f808252602082015290850135612038333083612f7b565b600c546040517fc7c7f5b30000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063c7c7f5b3903490612087908a908a908a906004016158f5565b60c06040518083038185885af11580156120a3573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906120c89190615933565b805191945092505f906120db9083615650565b905080156120ee576120ee303383612f7b565b5050935093915050565b6011546040517fc83ae6e50000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301525f92839291169063c83ae6e5906024016040805180830381865afa15801561215b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118be91906159bd565b6121876131d8565b6001600160a01b0381166121c9576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081525f6004820152602401610fbb565b6011546001600160a01b0382811691161461223d57601180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f60a0f5b9f9e81e98216071b85826681c796256fe3d1354ecb675580fba64fa69905f90a25b61144961144c565b834211156122955760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610fbb565b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886122c38c613c10565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f61231d82613c35565b90505f61232c82878787613c7c565b9050896001600160a01b0316816001600160a01b03161461238f5760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610fbb565b61239a8a8a8a6129b2565b50505050505050505050565b6060806123b1612ba2565b600f5483146123ec576040517f4c16a56e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6123f5613166565b90505f6124058260800151613ca2565b90508087111561245a576040517ff35b483d0000000000000000000000000000000000000000000000000000000081526001600160a01b03871660048201526024810188905260448101829052606401610fbb565b5f6124636114d2565b90505f612471848a84613cc4565b9197509550905080156124ba577f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb78091404275f83836040516124b19392919061567a565b60405180910390a15b6124c884338a8c8a8a612d3c565b505050506124d66001600d55565b935093915050565b6060806124fa6124ec613166565b846124f56114d2565b613cc4565b509094909350915050565b600c545f906001600160a01b03838116911614612546576001600160a01b038084165f908152600160209081526040808320938616835292905220546110c9565b505f1992915050565b601154604080517fe9607c0100000000000000000000000000000000000000000000000000000000815290516060926001600160a01b03169163e9607c01916004808301925f9291908290030181865afa158015610d3a573d5f5f3e3d5ffd5b6125b76131d8565b600254156125f1576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6125f9614c4c565b61260161254f565b815261260b610cdc565b81602001819052508989808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050505060408083019190915280516020808a02828101820190935289825290918a918a9182918501908490808284375f920191909152505050506060820152604080516020601f85018190048102820181019092528381529084908490819084018382808284375f9201919091525050505061010082015280515189146126f5576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020810151518714612733576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83861115612786576040517ff35b483d0000000000000000000000000000000000000000000000000000000081526001600160a01b03861660048201526024810187905260448101859052606401610fbb565b600e8490556040518481527f0120f799fc820eabb910038e9cce6e8024add369b4d780181846e300df2844849060200160405180910390a161239a813387898e8e808060200260200160405190810160405280939291908181526020018383602002808284375f81840152601f19601f820116905080830192505050505050508d8d808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d3c92505050565b5f610d6161284b60025490565b600e54613cf9565b613cf9565b600b546001600160a01b0316331461289c576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095460405133916001600160a01b0316907f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d554905f90a3600980547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600b80549091169055565b5f61291582613d1d565b8061296157507fffffffff0000000000000000000000000000000000000000000000000000000082167f8f85dbd200000000000000000000000000000000000000000000000000000000145b80610dbc57507fffffffff0000000000000000000000000000000000000000000000000000000082167fdec81de9000000000000000000000000000000000000000000000000000000001492915050565b6001600160a01b038316612a2d5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b038216612aa95760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b5f612b15858585613ee4565b90506001826001811115612b2b57612b2b61524d565b03612b9a578280612b3e57612b3e6159ec565b84860915612b9a575f19811015612b5757600101612b9a565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610fbb565b949350505050565b6002600d5403612bf45760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610fbb565b6002600d55565b612c03614c4c565b612c0b613166565b9050612c1f83825f01518360200151613fcc565b60c0830181905260a08301826002811115612c3c57612c3c61524d565b6002811115612c4d57612c4d61524d565b8152505050612c5b81614078565b15612c665781612c68565b5f5b60e082015292915050565b5f612c7c611c16565b15612c8857505f610dbc565b612c9183614078565b5f03612c9e57505f610dbc565b5f612cb08460800151612853600e5490565b90505f198103612cc1579050610dbc565b612b9a612cd082856001613bb0565b612cd986614078565b86608001515f614095565b5f80606080612d1c60018760a001516002811115612d0457612d0461524d565b14612d10576001612d12565b5f5b879060015f612e3f565b91955092509050612d2e84865f6140b7565b909790965091945092509050565b825f03612d75576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612d838686868686866140e2565b612d8d848461420e565b836001600160a01b0316856001600160a01b03167f8bf99c37e0a09491ed7cb29409f9fcd5a61a01752ab57bbf20d72ce465330dab848487604051612dd493929190615a19565b60405180910390a3505050505050565b5f8060608082612e1d60018860a001516002811115612e0557612e0561524d565b14612e10575f612e13565b60015b88905f6001612e3f565b90945092509050612e3081876001613bb0565b97908803965091945092509050565b5f606080612e5f8760e00151612e5489614078565b8960800151896142cb565b9250612e6d87848787613992565b9398909750929550919350505050565b606080606060115f9054906101000a90046001600160a01b03166001600160a01b03166322285cf66040518163ffffffff1660e01b81526004015f60405180830381865afa158015612ed1573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612ef89190810190615aa9565b925092509250909192565b5f612f0e8484612505565b90505f198114612f755781811015612f685760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610fbb565b612f7584848484036129b2565b50505050565b6001600160a01b038316612ff75760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b0382166130735760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b0383165f90815260208190526040902054818110156131015760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b038481165f81815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3612f75565b61316e614c4c565b61317661254f565b8152613180610cdc565b602082015261318d612e7d565b61010084015260608301526040820152600254608082015290565b5f6060806131b78585856140b7565b90955092506131c986865f6001613992565b93979096509294509192505050565b613205335f357fffffffff00000000000000000000000000000000000000000000000000000000166142de565b61323b576040517fc0185c6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f5f5f60115f9054906101000a90046001600160a01b03166001600160a01b03166345d67f0e6040518163ffffffff1660e01b81526004015f60405180830381865afa15801561328f573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526132b69190810190615b91565b915091506132cf84604001518386608001515f19614345565b92506132e5846060015182866080015186614345565b92505f19831015613306576133036132fb6114d2565b84905f613b50565b92505b50505f6133168360800151613ca2565b905081811015613324578091505b50919050565b5f306001600160a01b037f0000000000000000000000001b3f4ed11cb5f4c3f08c91c5593cbbcd63fa8b231614801561338257507f000000000000000000000000000000000000000000000000000000000000000146145b156133ac57507fc07888033e942cd04c8f1d3aa07dd4c6ee4dc6ecb80ec71351e1dd9e6d2e531a90565b610d61604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f09ac01ac0d79a8d4fd77eed6d5ab029acb8d3e693f538f4961da93fd8894d216918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a08201525f9060c00160405160208183030381529060405280519060200120905090565b5f61345c6118f4565b1561346857505f919050565b6001600160a01b03821615613494576001600160a01b0382165f90815260208190526040902054610dbc565b5f1992915050565b825f036134d5576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038416613520576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610fbb565b6001600160a01b03851661356b576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0386166004820152602401610fbb565b836001600160a01b0316866001600160a01b03161461358f5761358f848785612f03565b61359e878786888787876143cb565b6135a8848461362f565b82876080018181516135ba9190615650565b90525060808701515f036135cd575f600e555b836001600160a01b0316856001600160a01b0316876001600160a01b03167f728a5684b1f77b03e07bd227ddcef96ef31c5268eb61122aabcd651e08f752a185858860405161361e93929190615a19565b60405180910390a450505050505050565b6001600160a01b0382166136ab5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b0382165f90815260208190526040902054818110156137395760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b6001600160a01b0383165f818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b5f61379f610e69565b905080156137f7576012546040518281526001600160a01b039091169081907f8ca882445572a0beb49b440f5a329364aa0678559e2511916cf49b557a43cff99060200160405180910390a26137f5818361420e565b505b6012805479ffffffffffffffffffffffffffffffffffffffffffffffffffff167a0100000000000000000000000000000000000000000000000000004265ffffffffffff160217905590565b5f61384c6118f4565b1561385857505f6110c9565b5f61386285614078565b9050805f03613874575f9150506110c9565b6001600160a01b03841661388c575f199150506110c9565b6001600160a01b0384165f9081526020819052604081205490506138b181855f6140b7565b5090505f6138cd826138c289614078565b89608001515f614095565b90508281106138dc57826138de565b805b979650505050505050565b606060ff8314613903576138fc836144d8565b9050610dbc565b81805461390f906155d8565b80601f016020809104026020016040519081016040528092919081815260200182805461393b906155d8565b80156139865780601f1061395d57610100808354040283529160200191613986565b820191905f5260205f20905b81548152906001019060200180831161396957829003601f168201915b50505050509050610dbc565b6060806139a28686600187614515565b91506139b18686600286614515565b905094509492505050565b6040516001600160a01b038316602482015260448101829052611c049084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152614662565b5f5f5f60115f9054906101000a90046001600160a01b03166001600160a01b0316637fd382086040518163ffffffff1660e01b81526004015f60405180830381865afa158015613ab7573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613ade9190810190615b91565b91509150613af785604001518387608001515f19614345565b9250613b0d856060015182876080015186614345565b92505f19831015613b2f57613b2c613b2361155f565b84906001613bb0565b92505b50505f613b3b83613453565b905081811015613b49578091505b5092915050565b5f612710838103908410613b64575f611d86565b611d86858261271086612b09565b5f5f613b7d84614078565b9050805f03613b8f575f915050610dbc565b5f198303613ba05782915050610dbc565b612b9a838286608001515f614095565b5f825f03613bbf5750826110c9565b6127108310613bfa576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83612710039050611d86856127108386612b09565b6001600160a01b0381165f908152600760205260409020805460018101825590613324565b5f610dbc613c4161332a565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b5f5f5f613c8b87878787614748565b91509150613c9881614802565b5095945050505050565b5f613cab611c16565b15613cb757505f919050565b610dbc82612853600e5490565b6060805f80613cd586866001613bb0565b86810392509050613ce9878260015f613992565b9098909750919550909350505050565b5f5f198203613d09575080610dbc565b81831115613d1857505f610dbc565b500390565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167fec3e74c8000000000000000000000000000000000000000000000000000000001480613daf57507fffffffff0000000000000000000000000000000000000000000000000000000082167fa219a02500000000000000000000000000000000000000000000000000000000145b80613dfb57507fffffffff0000000000000000000000000000000000000000000000000000000082167f36372b0700000000000000000000000000000000000000000000000000000000145b80613e4757507fffffffff0000000000000000000000000000000000000000000000000000000082167f9d8ff7da00000000000000000000000000000000000000000000000000000000145b80613e9357507fffffffff0000000000000000000000000000000000000000000000000000000082167f84b0196e00000000000000000000000000000000000000000000000000000000145b80610dbc57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001492915050565b5f80805f19858709858702925082811083820303915050805f03613f1b57838281613f1157613f116159ec565b04925050506110c9565b838110613f65576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610fbb565b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f5f5f5b845181101561401e57856001600160a01b0316858281518110613ff557613ff5615889565b60200260200101516001600160a01b031603614016576001925090506124d6565b600101613fd0565b505f5b835181101561406f57856001600160a01b031684828151811061404657614046615889565b60200260200101516001600160a01b031603614067576002925090506124d6565b600101614021565b50935093915050565b5f610dbc8260a001518360c0015184604001518560600151614966565b5f82156140ad576140a885858585612b09565b611d86565b5f95945050505050565b5f5f835f036140ca57508390505f6124d6565b6140d5858585613b50565b9594869003949350505050565b5f5b825181101561415e576141568660115f9054906101000a90046001600160a01b031685848151811061411857614118615889565b60200260200101518a5f0151858151811061413557614135615889565b60200260200101516001600160a01b03166149cc909392919063ffffffff16565b6001016140e4565b5060408051608081018252878201518183019081526060808a0151908301528152610100880151602082015260115491517f15dd5cce00000000000000000000000000000000000000000000000000000000815290916001600160a01b0316906315dd5cce906141d890869086908a908790600401615bf6565b5f604051808303815f87803b1580156141ef575f5ffd5b505af1158015614201573d5f5f3e3d5ffd5b5050505050505050505050565b6001600160a01b0382166142645760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610fbb565b8060025f82825461427591906156a2565b90915550506001600160a01b0382165f81815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b5f83156140ad576140a885848685612b09565b6009545f906001600160a01b03848116911614806110c95750506001600160a01b03919091165f908152600a602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000009094168352929052205460ff1690565b805f8080805b87518110156143bf5788818151811061436657614366615889565b60200260200101519250825f03156143b75787818151811061438a5761438a615889565b602002602001015191505f198203156143b7576143a98284895f6142cb565b9350848410156143b7578394505b60010161434b565b50505050949350505050565b5f5b81518110156144275761441f8760115f9054906101000a90046001600160a01b031684848151811061440157614401615889565b60200260200101518b60200151858151811061413557614135615889565b6001016143cd565b5060408051608081018252888201518183019081526060808b0151908301528152610100890151602082015260115491517f8ce3c1ad00000000000000000000000000000000000000000000000000000000815290916001600160a01b031690638ce3c1ad906144a190869086908a908790600401615bf6565b5f604051808303815f87803b1580156144b8575f5ffd5b505af11580156144ca573d5f5f3e3d5ffd5b505050505050505050505050565b60605f6144e483614a1d565b6040805160208082528183019092529192505f91906020820181803683375050509182525060208101929092525090565b60605f600184600281111561452c5761452c61524d565b1461453c57856060015151614543565b8560400151515b90508067ffffffffffffffff81111561455e5761455e615499565b604051908082528060200260200182016040528015614587578160200160208202803683370190505b5091505f5b818110156146585786608001515f036145c3575f8382815181106145b2576145b2615889565b602002602001018181525050614650565b8660c00151811480156145fb57508460028111156145e3576145e361524d565b8760a0015160028111156145f9576145f961524d565b145b15614617578660e001518382815181106145b2576145b2615889565b61463186614626898885614a5d565b896080015187614095565b83828151811061464357614643615889565b6020026020010181815250505b60010161458c565b5050949350505050565b5f6146b6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614afe9092919063ffffffff16565b905080515f14806146d65750808060200190518101906146d6919061586e565b611c045760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610fbb565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561477d57505f905060036113b8565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156147ce573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b0381166147f6575f600192509250506113b8565b965f9650945050505050565b5f8160048111156148155761481561524d565b0361481d5750565b60018160048111156148315761483161524d565b0361487e5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610fbb565b60028160048111156148925761489261524d565b036148df5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610fbb565b60038160048111156148f3576148f361524d565b036114495760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610fbb565b5f600185600281111561497b5761497b61524d565b036149a15782848151811061499257614992615889565b60200260200101519050612b9a565b60028560028111156149b5576149b561524d565b03612b9a5781848151811061499257614992615889565b6040516001600160a01b0380851660248301528316604482015260648101829052612f759085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613a01565b5f60ff8216601f811115610dbc576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6001836002811115614a7257614a7261524d565b148015614a83575083604001515182105b15614aad5783604001518281518110614a9e57614a9e615889565b602002602001015190506110c9565b6002836002811115614ac157614ac161524d565b148015614ad2575083606001515182105b156110c95783606001518281518110614aed57614aed615889565b602002602001015190509392505050565b6060612b9a84845f85855f5f866001600160a01b03168587604051614b239190615cb2565b5f6040518083038185875af1925050503d805f8114614b5d576040519150601f19603f3d011682016040523d82523d5f602084013e614b62565b606091505b50915091506138de8783838760608315614bdc5782515f03614bd5576001600160a01b0385163b614bd55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610fbb565b5081612b9a565b612b9a8383815115614bf15781518083602001fd5b8060405162461bcd60e51b8152600401610fbb9190614d75565b60405180606001604052805f81526020015f67ffffffffffffffff168152602001614c4760405180604001604052805f81526020015f81525090565b905290565b604051806101200160405280606081526020016060815260200160608152602001606081526020015f81526020015f6002811115614c8c57614c8c61524d565b81526020015f81526020015f8152602001606081525090565b5f8151808452602084019350602083015f5b82811015614cde5781516001600160a01b0316865260209586019590910190600101614cb7565b5093949350505050565b602081525f6110c96020830184614ca5565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114614d29575f5ffd5b919050565b5f60208284031215614d3e575f5ffd5b6110c982614cfa565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6110c96020830184614d47565b6001600160a01b0381168114611449575f5ffd5b5f5f60408385031215614dac575f5ffd5b8235614db781614d87565b946020939093013593505050565b5f5f5f5f60808587031215614dd8575f5ffd5b8435614de381614d87565b9350602085013592506040850135614dfa81614d87565b9396929550929360600135925050565b5f8151808452602084019350602083015f5b82811015614cde578151865260209586019590910190600101614e1c565b838152606060208201525f614e526060830185614e0a565b8281036040840152614e648185614e0a565b9695505050505050565b604081525f614e806040830185614e0a565b8281036020840152611d868185614e0a565b5f5f5f60608486031215614ea4575f5ffd5b8335614eaf81614d87565b92506020840135614ebf81614d87565b929592945050506040919091013590565b5f60208284031215614ee0575f5ffd5b5035919050565b5f60208284031215614ef7575f5ffd5b81356110c981614d87565b5f60e08284031215613324575f5ffd5b8015158114611449575f5ffd5b5f5f60408385031215614f30575f5ffd5b823567ffffffffffffffff811115614f46575f5ffd5b614f5285828601614f02565b9250506020830135614f6381614f12565b809150509250929050565b815181526020808301519082015260408101610dbc565b5f5f5f5f60808587031215614f98575f5ffd5b843593506020850135614faa81614d87565b92506040850135614dfa81614d87565b5f5f5f5f5f60a08688031215614fce575f5ffd5b8535614fd981614d87565b9450602086013593506040860135614ff081614d87565b9250606086013561500081614d87565b949793965091946080013592915050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e060208201525f61504b60e0830189614d47565b828103604084015261505d8189614d47565b90508660608401526001600160a01b03861660808401528460a084015282810360c084015261508c8185614e0a565b9a9950505050505050505050565b5f602082840312156150aa575f5ffd5b813565ffffffffffff811681146110c9575f5ffd5b604081525f6150d16040830185614ca5565b8281036020840152611d868185614ca5565b5f5f604083850312156150f4575f5ffd5b82356150ff81614d87565b91506020830135614f6381614d87565b5f5f5f60408486031215615121575f5ffd5b833561512c81614d87565b9250602084013567ffffffffffffffff811115615147575f5ffd5b8401601f81018613615157575f5ffd5b803567ffffffffffffffff81111561516d575f5ffd5b8660208260061b8401011115615181575f5ffd5b939660209190910195509293505050565b5f5f5f83850360808112156151a5575f5ffd5b843567ffffffffffffffff8111156151bb575f5ffd5b6151c787828801614f02565b9450506040601f19820112156151db575f5ffd5b5060208401915060608401356151f081614d87565b809150509250925092565b5f60c0820190508351825267ffffffffffffffff60208501511660208301526040840151615236604084018280518252602090810151910152565b5082516080830152602083015160a08301526110c9565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b604081016003841061528e5761528e61524d565b9281526020015290565b5f5f5f5f5f5f5f60e0888a0312156152ae575f5ffd5b87356152b981614d87565b965060208801356152c981614d87565b95506040880135945060608801359350608088013560ff811681146152ec575f5ffd5b9699959850939692959460a0840135945060c09093013592915050565b5f5f5f6060848603121561531b575f5ffd5b833592506020840135614ebf81614d87565b5f5f6040838503121561533e575f5ffd5b823561534981614d87565b915061535760208401614cfa565b90509250929050565b5f5f83601f840112615370575f5ffd5b50813567ffffffffffffffff811115615387575f5ffd5b6020830191508360208260051b85010111156153a1575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f60c08a8c0312156153c0575f5ffd5b893567ffffffffffffffff8111156153d6575f5ffd5b6153e28c828d01615360565b909a5098505060208a013567ffffffffffffffff811115615401575f5ffd5b61540d8c828d01615360565b90985096505060408a0135945060608a013561542881614d87565b935060808a0135925060a08a013567ffffffffffffffff81111561544a575f5ffd5b8a01601f81018c1361545a575f5ffd5b803567ffffffffffffffff811115615470575f5ffd5b8c6020828401011115615481575f5ffd5b60208201935080925050509295985092959850929598565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040805190810167ffffffffffffffff811182821017156154e9576154e9615499565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561551857615518615499565b604052919050565b5f67ffffffffffffffff82111561553957615539615499565b5060051b60200190565b5f60208284031215615553575f5ffd5b815167ffffffffffffffff811115615569575f5ffd5b8201601f81018413615579575f5ffd5b805161558c61558782615520565b6154ef565b8082825260208201915060208360051b8501019250868311156155ad575f5ffd5b6020840193505b82841015614e645783516155c781614d87565b8252602093840193909101906155b4565b600181811c908216806155ec57607f821691505b602082108103613324577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610dbc57610dbc615623565b8082028115828204841417610dbc57610dbc615623565b606081016002851061568e5761568e61524d565b938152602081019290925260409091015290565b80820180821115610dbc57610dbc615623565b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156e8575f5ffd5b830160208101925035905067ffffffffffffffff811115615707575f5ffd5b8036038213156153a1575f5ffd5b81835281816020850137505f602082840101525f6020601f19601f840116840101905092915050565b5f813563ffffffff81168114615752575f5ffd5b63ffffffff16835260208281013590840152604080830135908401526060808301359084015261578560808301836156b5565b60e0608086015261579a60e086018284615715565b9150506157aa60a08401846156b5565b85830360a08701526157bd838284615715565b925050506157ce60c08401846156b5565b85830360c0870152614e64838284615715565b604081525f6157f3604083018561573e565b905082151560208301529392505050565b5f60408284031215615814575f5ffd5b61581c6154c6565b825181526020928301519281019290925250919050565b5f60408284031215615843575f5ffd5b6110c98383615804565b5f6020828403121561585d575f5ffd5b815161ffff811681146110c9575f5ffd5b5f6020828403121561587e575f5ffd5b81516110c981614f12565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60408284031280156158c7575f5ffd5b506158d06154c6565b6158d983614cfa565b815260208301356158e981614f12565b60208201529392505050565b608081525f615907608083018661573e565b8435602084810191909152909401356040830152506001600160a01b0391909116606090910152919050565b5f5f82840360c0811215615945575f5ffd5b6080811215615952575f5ffd5b506040516060810167ffffffffffffffff8111828210171561597657615976615499565b60405283518152602084015167ffffffffffffffff81168114615997575f5ffd5b60208201526159a98560408601615804565b604082015291506153578460808501615804565b5f5f604083850312156159ce575f5ffd5b8251600381106159dc575f5ffd5b6020939093015192949293505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b606081525f615a2b6060830186614e0a565b8281036020840152615a3d8186614e0a565b915050826040830152949350505050565b5f82601f830112615a5d575f5ffd5b8151615a6b61558782615520565b8082825260208201915060208360051b860101925085831115615a8c575f5ffd5b602085015b83811015613c98578051835260209283019201615a91565b5f5f5f60608486031215615abb575f5ffd5b835167ffffffffffffffff811115615ad1575f5ffd5b615add86828701615a4e565b935050602084015167ffffffffffffffff811115615af9575f5ffd5b615b0586828701615a4e565b925050604084015167ffffffffffffffff811115615b21575f5ffd5b8401601f81018613615b31575f5ffd5b805167ffffffffffffffff811115615b4b57615b4b615499565b615b5e6020601f19601f840116016154ef565b818152876020838501011115615b72575f5ffd5b8160208401602083015e5f602083830101528093505050509250925092565b5f5f60408385031215615ba2575f5ffd5b825167ffffffffffffffff811115615bb8575f5ffd5b615bc485828601615a4e565b925050602083015167ffffffffffffffff811115615be0575f5ffd5b615bec85828601615a4e565b9150509250929050565b608081525f615c086080830187614e0a565b8281036020840152615c1a8187614e0a565b90506001600160a01b038516604084015282810360608401528351604082528051604080840152615c4e6080840182614e0a565b9050602082015191507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0838203016060840152615c8b8183614e0a565b91505060208501518282036020840152615ca58282614d47565b9998505050505050505050565b5f82518060208501845e5f92019182525091905056fea264697066735822122035d6c62a12c0924b5973583f3ce2fb75c68124dcf5acaef0508eedb75bb6f60364736f6c634300081c0033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.