ETH Price: $2,314.16 (-2.83%)
 

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Edit Fees448927302026-04-19 5:00:073 hrs ago1776574807IN
0x35935460...65be6ca13
0 ETH0.000000210.006
Edit Fees448711302026-04-18 17:00:0715 hrs ago1776531607IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Withdraw Saving448708482026-04-18 16:50:4315 hrs ago1776531043IN
0x35935460...65be6ca13
0 ETH0.000000530.007848
Edit Fees448603292026-04-18 11:00:0521 hrs ago1776510005IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees448171302026-04-17 11:00:0745 hrs ago1776423607IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees448063302026-04-17 5:00:072 days ago1776402007IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees447955302026-04-16 23:00:072 days ago1776380407IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees447847312026-04-16 17:00:092 days ago1776358809IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees447739302026-04-16 11:00:072 days ago1776337207IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees447631302026-04-16 5:00:073 days ago1776315607IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees447523302026-04-15 23:00:073 days ago1776294007IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees447415312026-04-15 17:00:093 days ago1776272409IN
0x35935460...65be6ca13
0 ETH0.00000020.00600539
Edit Fees447307302026-04-15 11:00:073 days ago1776250807IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees447199302026-04-15 5:00:074 days ago1776229207IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees447091302026-04-14 23:00:074 days ago1776207607IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Create Saving447032502026-04-14 19:44:074 days ago1776195847IN
0x35935460...65be6ca13
0.00042078 ETH0.000003710.01199
Withdraw Saving446951112026-04-14 15:12:494 days ago1776179569IN
0x35935460...65be6ca13
0 ETH0.000000970.01127
Edit Fees446875302026-04-14 11:00:074 days ago1776164407IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees446767292026-04-14 5:00:055 days ago1776142805IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees446659302026-04-13 23:00:075 days ago1776121207IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees446551332026-04-13 17:00:135 days ago1776099613IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Withdraw Saving446447782026-04-13 11:15:035 days ago1776078903IN
0x35935460...65be6ca13
0 ETH0.000000530.007254
Edit Fees446443302026-04-13 11:00:075 days ago1776078007IN
0x35935460...65be6ca13
0 ETH0.00000020.00601037
Edit Fees446335302026-04-13 5:00:076 days ago1776056407IN
0x35935460...65be6ca13
0 ETH0.00000020.006
Edit Fees446227302026-04-12 23:00:076 days ago1776034807IN
0x35935460...65be6ca13
0 ETH0.00000020.006
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
447032502026-04-14 19:44:074 days ago1776195847
0x35935460...65be6ca13
0.00002103 ETH
441376592026-04-01 17:31:0517 days ago1775064665
0x35935460...65be6ca13
0.0000233 ETH
441327242026-04-01 14:46:3517 days ago1775054795
0x35935460...65be6ca13
0.00002346 ETH
439550392026-03-28 12:03:4521 days ago1774699425
0x35935460...65be6ca13
0.00002514 ETH
438459992026-03-25 23:29:0524 days ago1774481345
0x35935460...65be6ca13
0.000023 ETH
437311002026-03-23 7:39:0727 days ago1774251547
0x35935460...65be6ca13
0.00002425 ETH
432633232026-03-12 11:46:3337 days ago1773315993
0x35935460...65be6ca13
0.00002414 ETH
432631952026-03-12 11:42:1737 days ago1773315737
0x35935460...65be6ca13
0.00002414 ETH
432608062026-03-12 10:22:3937 days ago1773310959
0x35935460...65be6ca13
0.00002471 ETH
432607942026-03-12 10:22:1537 days ago1773310935
0x35935460...65be6ca13
 Contract Creation0 ETH
431720922026-03-10 9:05:3139 days ago1773133531
0x35935460...65be6ca13
0.00451259 ETH
428767672026-03-03 13:01:2146 days ago1772542881
0x35935460...65be6ca13
0.00002557 ETH
428317632026-03-02 12:01:1347 days ago1772452873
0x35935460...65be6ca13
0.00002565 ETH
428316152026-03-02 11:56:1747 days ago1772452577
0x35935460...65be6ca13
0.00002565 ETH
427011902026-02-27 11:28:4750 days ago1772191727
0x35935460...65be6ca13
0.00002523 ETH
426979432026-02-27 9:40:3350 days ago1772185233
0x35935460...65be6ca13
0.00002445 ETH
423934562026-02-20 8:30:5958 days ago1771576259
0x35935460...65be6ca13
0.00002579 ETH
423934182026-02-20 8:29:4358 days ago1771576183
0x35935460...65be6ca13
0.00002579 ETH
423933902026-02-20 8:28:4758 days ago1771576127
0x35935460...65be6ca13
0.00002579 ETH
422224202026-02-16 9:29:4761 days ago1771234187
0x35935460...65be6ca13
0.00002536 ETH
421975942026-02-15 19:42:1562 days ago1771184535
0x35935460...65be6ca13
0.00002492 ETH
421975602026-02-15 19:41:0762 days ago1771184467
0x35935460...65be6ca13
0.00002492 ETH
421975432026-02-15 19:40:3362 days ago1771184433
0x35935460...65be6ca13
 Contract Creation0 ETH
421416062026-02-14 12:35:5963 days ago1771072559
0x35935460...65be6ca13
0.00002553 ETH
420830702026-02-13 4:04:4765 days ago1770955487
0x35935460...65be6ca13
 Contract Creation0 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Bitsave

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ChildContract.sol";
import "./libraries/bitsaveHelperLib.sol";

contract Bitsave {
    // *** Contract parameters ***
    IERC20 public stableCoin;
    IERC20 public csToken;
    address public masterAddress;
    uint256 public rewardPool;
    // *** Fountain ***
    uint256 public fountain;

    // *** Storage ***
    mapping(address => address) addressToUserBS;
    uint256 public userCount;
    // *** Storage requiring house modifiers ***
    uint256 public currentVaultState;
    uint256 public currentTotalValueLocked;

    // *** savings values ***
    uint256 public JoinLimitFee = 0.0001 ether;
    uint256 public SavingFee = 0.0001 ether;
    uint256 public ChildContractGasFee = SavingFee / 20;

    constructor(address _stableCoin, address _csToken) payable {
        stableCoin = IERC20(_stableCoin);
        csToken = IERC20(_csToken);
        masterAddress = msg.sender;
        rewardPool = 0;
        userCount = 0;
        // initial values
        currentVaultState = 14_000_000;
        currentTotalValueLocked = 100_000;
        fountain = msg.value;
    }

    modifier inhouseOnly() {
        if (msg.sender != masterAddress) {
            revert BitsaveHelperLib.MasterCallRequired();
        }
        _;
    }

    modifier registeredOnly(address sender) {
        if (addressToUserBS[sender] == address(0)) {
            revert BitsaveHelperLib.UserNotRegistered();
        }
        _;
    }

    modifier fromABitsaveChildOnly(address childOwnerAddress) {
        address fetchedChildAddress = addressToUserBS[childOwnerAddress];
        if (
            fetchedChildAddress == address(0) // checks that the child contract exists
                    // could be merged into one check but for readability
                || fetchedChildAddress != msg.sender // and that the child contract sent the request
        ) {
            revert BitsaveHelperLib.CallNotFromBitsave();
        }
        _;
    }

    function joinBitsave() public payable returns (address) {
        address ownerAddress = msg.sender;
        address currAddr = addressToUserBS[ownerAddress];
        if (currAddr != address(0)) {
            return currAddr;
        }
        if (msg.value < JoinLimitFee) {
            revert BitsaveHelperLib.AmountNotEnough();
        }
        // deploy child contract for user
        address userBSAddress = address(new ChildBitsave(msg.sender, address(stableCoin)));
        addressToUserBS[ownerAddress] = userBSAddress;
        userCount += 1;
        emit BitsaveHelperLib.JoinedBitsave(ownerAddress);
        return userBSAddress;
    }

    function getUserChildContractAddress() public view returns (address) {
        return addressToUserBS[msg.sender];
    }

    function sendAsOriginalToken(address originalToken, uint256 amount, address ownerAddress)
        public
        payable
        fromABitsaveChildOnly(ownerAddress)
        returns (bool)
    {
        // check amount sent
        // if (amount < poolFee) revert BitsaveHelperLib.AmountNotEnough();
        // retrieve stable coin used from owner address
        return BitsaveHelperLib.retrieveToken(msg.sender, address(stableCoin), amount);
        // convert to original token using crossChainSwap()
        // crossChainSwap(
        //     stableCoin,
        //     originalToken,
        //     amount,
        //     ownerAddress // send to owner address directly
        // );
    }

    /// Edit internal vault data
    function editInternalData(uint256 _newCurrentVaultState, uint256 _newTotalValueLocked, address _newCsToken)
        public
        inhouseOnly
    {
        currentVaultState = _newCurrentVaultState;
        currentTotalValueLocked = _newTotalValueLocked;
        if (_newCsToken != address(0)) {
            csToken = IERC20(_newCsToken);
        }
    }

    /// Edit internal stablecoin data
    function editStableCoin(address _newStableCoin) public inhouseOnly {
        if (_newStableCoin != address(0)) {
            stableCoin = IERC20(_newStableCoin);
        }
    }

    /// Edit internal vault data
    function editFees(uint256 _joinFee, uint256 _savingFee) public inhouseOnly {
        if (_joinFee != 0) {
            JoinLimitFee = _joinFee;
        }
        if (_savingFee != 0) {
            SavingFee = _savingFee;
            ChildContractGasFee = _savingFee / 20;
        }
    }

    function dripFountain() public inhouseOnly {
        // send balance - fountain to masterAddress
        uint256 balance = address(this).balance;
        if (balance > fountain) {
            payable(masterAddress).transfer(balance - fountain);
        }
    }

    function handleNativeSaving(uint256 amount, address tokenToSave, address userChildContractAddress)
        private
        returns (uint256)
    {
        // check if native currency saving
        if (tokenToSave != address(0)) {
            // savingToken = tokenToSave;
            // amountToSave = amount;
            // perform withdrawal respective
            bool tokenHasBeenWithdrawn = BitsaveHelperLib.retrieveToken(msg.sender, tokenToSave, amount);
            if (!tokenHasBeenWithdrawn) {
                revert BitsaveHelperLib.CanNotWithdrawToken("Txn failed");
            }
            // let us know you've removed the savings
            emit BitsaveHelperLib.TokenWithdrawal(msg.sender, address(this), amount);
            // approve child contract withdrawing token
            require(BitsaveHelperLib.approveAmount(userChildContractAddress, amount, tokenToSave), "Savings invalid");
        } else {
            amount = msg.value - SavingFee;
        }
        return amount;
    }

    function createSaving(
        string memory nameOfSaving,
        uint256 maturityTime,
        uint8 penaltyPercentage,
        bool safeMode,
        address tokenToSave, // address 0 for native coin
        uint256 amount // discarded for native token; takes msg.value - SavingFee instead
    ) public payable registeredOnly(msg.sender) {
        if (msg.value < SavingFee) {
            revert BitsaveHelperLib.NotEnoughToPayGasFee();
        }

        if (block.timestamp > maturityTime) {
            revert BitsaveHelperLib.InvalidTime();
        }

        // NOTE: For now, no safeMode since no swap contract
        if (safeMode) {
            revert BitsaveHelperLib.NotSupported("No safe mode yet!");
        }

        // user's child contract address
        address payable userChildContractAddress = getUserChildContractAddress(msg.sender);

        // Handle token sent
        uint256 amountRetrieved = handleNativeSaving(amount, tokenToSave, userChildContractAddress);

        // TODO:  perform conversion for stableCoin
        // functionality for safe mode
        // if (safeMode) {
        //     amountToSave = crossChainSwap(
        //         savingToken,
        //         stableCoin,
        //         amount,
        //         address(this)
        //     );
        //     savingToken = stableCoin;
        // }

        /// send savings request to child contract with a little gas
        // Initialize user's child contract
        ChildBitsave userChildContract = ChildBitsave(userChildContractAddress);

        userChildContract.createSaving{
            value: tokenToSave == address(0) ? ChildContractGasFee + amountRetrieved : ChildContractGasFee
        }(
            nameOfSaving,
            maturityTime,
            block.timestamp, // current time
            penaltyPercentage,
            tokenToSave,
            amountRetrieved,
            safeMode,
            currentVaultState,
            currentTotalValueLocked
        );

        // emit saving created
        emit BitsaveHelperLib.SavingCreated(nameOfSaving, amountRetrieved, tokenToSave);
    }

    ///
    /// INCREMENT SAVING
    ///    the amount to add to saving
    ///
    ///    string nameOfSaving
    ///
    function incrementSaving(string memory nameOfSavings, address tokenToRetrieve, uint256 amount)
        public
        payable
        registeredOnly(msg.sender)
    {
        // initialize userChildContract
        address payable userChildContractAddress = payable(addressToUserBS[msg.sender]);
        ChildBitsave userChildContract = ChildBitsave(userChildContractAddress);

        address savingToken = userChildContract.getSavingTokenId(nameOfSavings);
        bool isNativeToken = savingToken == address(0);
        // todo: perform amount conversion and everything
        uint256 savingPlusAmount = amount;
        // todo: check savings detail by reading the storage of userChildContract
        bool isSafeMode = userChildContract.getSavingMode(nameOfSavings);
        if (isSafeMode) {
            // savingPlusAmount = crossChainSwap(
            //     userChildContract.getSavingTokenId(nameOfSavings),
            //     stableCoin,
            //     savingPlusAmount,
            //     address(this)
            // );
            tokenToRetrieve = address(stableCoin);
        }
        if (!isNativeToken) {
            // approve child contract withdrawing token
            require(
                BitsaveHelperLib.approveAmount(userChildContractAddress, savingPlusAmount, tokenToRetrieve),
                "Savings invalid"
            );
        } else {
            savingPlusAmount = msg.value;
        }

        uint256 amountRetrieved = handleNativeSaving(amount, savingToken, userChildContractAddress);
        // call withdrawSavings

        userChildContract.incrementSaving{
            value: isNativeToken ? ChildContractGasFee + savingPlusAmount : ChildContractGasFee
        }(nameOfSavings, amount, currentVaultState, currentTotalValueLocked);
    }

    /// WITHDRAW savings
    ///
    ///    string nameOfSaving
    ///
    function withdrawSaving(string memory nameOfSavings) public registeredOnly(msg.sender) returns (bool) {
        // initialize user's child userChildContract
        ChildBitsave userChildContract = ChildBitsave(payable(addressToUserBS[msg.sender]));
        // call withdraw savings fn
        userChildContract.withdrawSaving(nameOfSavings);
        return true;
    }

    receive() external payable {}

    // ---------- Private functions ---------------
    function getUserChildContractAddress(address myAddress) internal view returns (address payable) {
        return payable(addressToUserBS[myAddress]);
    }
}

File 2 of 17 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.20;

import {IERC721} from "./IERC721.sol";
import {IERC721Receiver} from "./IERC721Receiver.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    mapping(uint256 tokenId => address) private _owners;

    mapping(address owner => uint256) private _balances;

    mapping(uint256 tokenId => address) private _tokenApprovals;

    mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual returns (uint256) {
        if (owner == address(0)) {
            revert ERC721InvalidOwner(address(0));
        }
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual returns (address) {
        return _requireOwned(tokenId);
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
        _requireOwned(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual {
        _approve(to, tokenId, _msgSender());
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual returns (address) {
        _requireOwned(tokenId);

        return _getApproved(tokenId);
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
        address previousOwner = _update(to, tokenId, _msgSender());
        if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
        transferFrom(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     *
     * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
     * core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
     * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
     * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
     */
    function _getApproved(uint256 tokenId) internal view virtual returns (address) {
        return _tokenApprovals[tokenId];
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
     * particular (ignoring whether it is owned by `owner`).
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
        return
            spender != address(0) &&
            (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
    }

    /**
     * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
     * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
     * the `spender` for the specific `tokenId`.
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
        if (!_isAuthorized(owner, spender, tokenId)) {
            if (owner == address(0)) {
                revert ERC721NonexistentToken(tokenId);
            } else {
                revert ERC721InsufficientApproval(spender, tokenId);
            }
        }
    }

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
     * a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
     *
     * WARNING: Increasing an account's balance using this function tends to be paired with an override of the
     * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
     * remain consistent with one another.
     */
    function _increaseBalance(address account, uint128 value) internal virtual {
        unchecked {
            _balances[account] += value;
        }
    }

    /**
     * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
     * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that
     * `auth` is either the owner of the token, or approved to operate on the token (by the owner).
     *
     * Emits a {Transfer} event.
     *
     * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
        address from = _ownerOf(tokenId);

        // Perform (optional) operator check
        if (auth != address(0)) {
            _checkAuthorized(from, auth, tokenId);
        }

        // Execute the update
        if (from != address(0)) {
            // Clear approval. No need to re-authorize or emit the Approval event
            _approve(address(0), tokenId, address(0), false);

            unchecked {
                _balances[from] -= 1;
            }
        }

        if (to != address(0)) {
            unchecked {
                _balances[to] += 1;
            }
        }

        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        return from;
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner != address(0)) {
            revert ERC721InvalidSender(address(0));
        }
    }

    /**
     * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        _checkOnERC721Received(address(0), to, tokenId, data);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal {
        address previousOwner = _update(address(0), tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        } else if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
     * are aware of the ERC721 standard to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is like {safeTransferFrom} in the sense that it invokes
     * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `tokenId` token must exist and be owned by `from`.
     * - `to` cannot be the zero address.
     * - `from` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId) internal {
        _safeTransfer(from, to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
     * either the owner of the token, or approved to operate on all tokens held by this owner.
     *
     * Emits an {Approval} event.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address to, uint256 tokenId, address auth) internal {
        _approve(to, tokenId, auth, true);
    }

    /**
     * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
     * emitted in the context of transfers.
     */
    function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
        // Avoid reading the owner unless necessary
        if (emitEvent || auth != address(0)) {
            address owner = _requireOwned(tokenId);

            // We do not use _isAuthorized because single-token approvals should not be able to call approve
            if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
                revert ERC721InvalidApprover(auth);
            }

            if (emitEvent) {
                emit Approval(owner, to, tokenId);
            }
        }

        _tokenApprovals[tokenId] = to;
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Requirements:
     * - operator can't be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        if (operator == address(0)) {
            revert ERC721InvalidOperator(operator);
        }
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
     * Returns the owner.
     *
     * Overrides to ownership logic should be done to {_ownerOf}.
     */
    function _requireOwned(uint256 tokenId) internal view returns (address) {
        address owner = _ownerOf(tokenId);
        if (owner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
        return owner;
    }

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
     * recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                if (retval != IERC721Receiver.onERC721Received.selector) {
                    revert ERC721InvalidReceiver(to);
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert ERC721InvalidReceiver(to);
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./libraries/bitsaveHelperLib.sol";
import "./Bitsave.sol";

contract ChildBitsave {
    // *** Contract parameters ***
    address payable public bitsaveAddress;
    IERC20 public stableCoin;
    address public ownerAddress;

    // *** Contract Storage ***
    // total interests gathered; v1 shows points
    uint256 public totalPoints;

    // structure of saving data
    struct SavingDataStruct {
        bool isValid;
        uint256 amount;
        address tokenId;
        uint256 interestAccumulated;
        uint256 startTime;
        uint256 penaltyPercentage;
        uint256 maturityTime;
        bool isSafeMode;
    }

    // mapping of name of saving to individual saving
    mapping(string => SavingDataStruct) public savings;

    struct SavingsNamesObj {
        string[] savingsNames;
    }

    function updatePoints(uint256 newPoint) private {
        totalPoints = totalPoints + newPoint;
    }

    function calculateAndUpdatePoints(
        uint256 savingAmount,
        uint256 endTime,
        uint256 startTime,
        uint256 currentVaultState,
        uint256 currentTotalValueLocked
    ) internal returns (uint256 accumulatedInterest) {
        accumulatedInterest = BitsaveHelperLib.calculateInterestWithBTS(
            savingAmount,
            endTime - startTime, // time interval
            currentVaultState,
            currentTotalValueLocked
        );

        totalPoints = accumulatedInterest;
    }

    SavingsNamesObj private savingsNamesVar;

    constructor(address _ownerAddress, address _stableCoin) payable {
        // save bitsaveAddress first // todo: retrieve correct address
        bitsaveAddress = payable(msg.sender);
        // store owner's address
        ownerAddress = payable(_ownerAddress);
        // store stable coin
        stableCoin = IERC20(payable(_stableCoin));
        // storage
        totalPoints = 0;
    }

    modifier bitsaveOnly() {
        if (msg.sender != bitsaveAddress) {
            revert BitsaveHelperLib.CallNotFromBitsave();
        }
        _;
    }

    function addSavingName(string memory _name) private {
        savingsNamesVar.savingsNames.push(_name);
    }

    // Contract Getters
    function getSavingMode(string memory nameOfSaving) external view returns (bool) {
        return savings[nameOfSaving].isSafeMode;
    }

    function getSavingInterest(string memory nameOfSaving) external view returns (uint256) {
        return savings[nameOfSaving].interestAccumulated;
    }

    function getSavingTokenId(string memory nameOfSaving) external view returns (address) {
        return savings[nameOfSaving].tokenId;
    }

    function getSavingsNames() external view returns (SavingsNamesObj memory) {
        return savingsNamesVar;
    }

    function getSaving(string memory nameOfSaving) public view returns (SavingDataStruct memory) {
        return savings[nameOfSaving];
    }

    // functionality to create savings
    function createSaving(
        string memory name,
        uint256 maturityTime,
        uint256 startTime,
        uint8 penaltyPercentage,
        address tokenId,
        uint256 amountToRetrieve,
        bool isSafeMode,
        uint256 currentVaultState,
        uint256 currentTotalValueLocked
    ) public payable bitsaveOnly returns (uint256) {
        // ensure saving does not exist; !
        if (savings[name].isValid) revert BitsaveHelperLib.InvalidSaving();
        // check if end time valid
        if (maturityTime < startTime) revert BitsaveHelperLib.InvalidTime();
        if (maturityTime < block.timestamp) revert BitsaveHelperLib.InvalidTime();

        uint256 savingsAmount = amountToRetrieve;

        if (isSafeMode) {
            BitsaveHelperLib.retrieveToken(bitsaveAddress, address(stableCoin), amountToRetrieve);
        } else {
            if (tokenId != address(0)) {
                BitsaveHelperLib.retrieveToken(bitsaveAddress, tokenId, amountToRetrieve);
            } else {
                // case native token
                savingsAmount = msg.value;
            }
        }

        uint256 accumulatedInterest =
            calculateAndUpdatePoints(savingsAmount, maturityTime, startTime, currentVaultState, currentTotalValueLocked);

        // store saving to map of savings
        savings[name] = SavingDataStruct({
            amount: savingsAmount,
            maturityTime: maturityTime,
            interestAccumulated: accumulatedInterest,
            startTime: startTime,
            tokenId: tokenId,
            penaltyPercentage: penaltyPercentage,
            isSafeMode: isSafeMode,
            isValid: true
        });

        // addSavingName(name);
        addSavingName(name);

        emit BitsaveHelperLib.SavingCreated(name, amountToRetrieve, tokenId);

        return 1;
    }

    // functionality to add to savings
    function incrementSaving(
        string memory name,
        uint256 savingPlusAmount,
        uint256 currentVaultState,
        uint256 currentTotalValueLocked
    ) public payable bitsaveOnly returns (uint256) {
        // fetch savings data
        SavingDataStruct storage toFundSavings = savings[name];
        if (!toFundSavings.isValid) revert BitsaveHelperLib.InvalidSaving();
        if (block.timestamp > toFundSavings.maturityTime) revert BitsaveHelperLib.InvalidTime();

        bool isNativeToken = toFundSavings.tokenId == address(0);

        // handle retrieving token from contract
        if (toFundSavings.isSafeMode) {
            BitsaveHelperLib.retrieveToken(bitsaveAddress, address(stableCoin), savingPlusAmount);
        } else {
            if (!isNativeToken) {
                BitsaveHelperLib.retrieveToken(bitsaveAddress, toFundSavings.tokenId, savingPlusAmount);
            } else {
                require(msg.value >= savingPlusAmount, "Invalid saving increment value sent");
                savingPlusAmount = msg.value;
            }
        }

        uint256 extraInterest = calculateAndUpdatePoints(
            savingPlusAmount, toFundSavings.maturityTime, block.timestamp, currentVaultState, currentTotalValueLocked
        );

        // calculate new interest
        toFundSavings.interestAccumulated = toFundSavings.interestAccumulated + extraInterest;
        toFundSavings.amount = toFundSavings.amount + savingPlusAmount;

        // save new savings data
        savings[name] = toFundSavings;

        emit BitsaveHelperLib.SavingIncremented(name, savingPlusAmount, toFundSavings.amount, toFundSavings.tokenId);

        return toFundSavings.interestAccumulated;
    }

    function withdrawSaving(string memory name) public payable bitsaveOnly returns (string memory) {
        SavingDataStruct storage toWithdrawSavings = savings[name];
        // check if saving exit
        if (!toWithdrawSavings.isValid) revert BitsaveHelperLib.InvalidSaving();
        uint256 amountToWithdraw = toWithdrawSavings.amount;
        Bitsave bitsave = Bitsave(bitsaveAddress);
        // check if saving is mature
        if (block.timestamp < toWithdrawSavings.maturityTime) {
            // remove penalty from savings
            amountToWithdraw = (toWithdrawSavings.amount * (100 - toWithdrawSavings.penaltyPercentage)) / 100;
        } else {
            // TODO: handle interest point management
            // bitsave.handleUsersInterest(
            //     name,
            //     address(this),
            //     ownerAddress
            // );
        }

        // send the savings amount to withdraw
        address tokenId = toWithdrawSavings.tokenId;
        // function can be abstracted for sending token out
        bool isDelivered = false;
        if (toWithdrawSavings.isSafeMode) {
            // approve withdrawal from parent contract
            BitsaveHelperLib.approveAmount(bitsaveAddress, amountToWithdraw, address(stableCoin));
            // call parent for conversion
            isDelivered = bitsave.sendAsOriginalToken(tokenId, amountToWithdraw, ownerAddress);
        } else {
            if (tokenId == address(0)) {
                (bool sent, bytes memory data) = ownerAddress.call{value: amountToWithdraw}("");
                require(sent, "Couldn't send funds");
                isDelivered = sent;
            } else {
                isDelivered = BitsaveHelperLib.transferToken(toWithdrawSavings.tokenId, ownerAddress, amountToWithdraw);
            }
        }
        // Delete savings; ensure saving is deleted/made invalid
        if (isDelivered) {
            savings[name].isValid = false;

            emit BitsaveHelperLib.SavingWithdrawn(name);

            return "savings withdrawn successfully";
        }

        revert();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "prb-math/contracts/PRBMathUD60x18.sol";

library BitsaveHelperLib {
    using PRBMathUD60x18 for uint256;

    // Constants
    uint256 public constant txnCharge = 0.02 ether;
    // For interest calculation
    uint256 public constant maxSupply = 100_000_000;
    uint256 public constant totalSupply = 15_000_000;
    // just a default of 365 days
    uint256 public constant yearInSeconds = 3600 * 24 * 365;
    uint256 public constant divisor = 1_000 ether;

    // Errors
    error WrongGasContract();
    error NotEnoughToPayGasFee();
    error AmountNotEnough();
    error InvalidTime();
    error UserNotRegistered();
    error InvalidSaving();
    error CanNotWithdrawToken(string);
    error NotSupported(string);
    error MasterCallRequired();
    // child contract specific
    error CallNotFromBitsave();

    // Events
    event JoinedBitsave(address indexed userAddress);
    event SavingCreated(string indexed nameOfSaving, uint256 amount, address token);
    event SavingIncremented(string indexed nameOfSaving, uint256 amountAdded, uint256 totalAmountNow, address token);
    event SavingWithdrawn(string indexed nameOfSaving);
    event TokenWithdrawal(address indexed from, address indexed to, uint256 amount);
    event Received(address indexed, uint256);

    function approveAmount(address toApproveUserAddress, uint256 amountToApprove, address targetToken)
        internal
        returns (bool)
    {
        IERC20 token = IERC20(targetToken);
        return token.approve(toApproveUserAddress, amountToApprove);
    }

    function retrieveToken(address toApproveUserAddress, address targetToken, uint256 amountToWithdraw)
        internal
        returns (bool)
    {
        // first request approval
        require(
            // approveAmount(toApproveUserAddress, amountToWithdraw, targetToken),
            IERC20(targetToken).allowance(toApproveUserAddress, address(this)) >= amountToWithdraw,
            "Token could not be withdrawn"
        );
        return IERC20(targetToken).transferFrom(toApproveUserAddress, address(this), amountToWithdraw);
    }

    // integrate bitsave interest calculator
    function calculateInterest(uint256 amount)
        internal
        // uint256 currBitsPointValue
        pure
        returns (uint256 accumulatedInterest)
    {
        accumulatedInterest = amount / 100;
    }

    function calculateInterestWithBTS(
        // External data
        uint256 principal,
        uint256 timeInterval, // will be converted to years
        // Internal data
        uint256 vaultState,
        uint256 totalValueLocked
    ) internal pure returns (uint256 accumulatedInterest) {
        uint256 crp = ((totalSupply - vaultState).div(vaultState)).mul(100);
        uint256 bsRate = maxSupply.div(crp * totalValueLocked);
        uint256 yearsTaken = timeInterval.div(yearInSeconds);
        accumulatedInterest = ((principal * bsRate * yearsTaken).div(100 * divisor)).toUint();
    }

    function transferToken(address token, address recipient, uint256 amount) internal returns (bool isDelivered) {
        IERC20 Token = IERC20(token);

        // convert address to Byte
        isDelivered = Token.transfer(recipient, amount);

        emit TokenWithdrawal(address(this), recipient, amount);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 7 of 17 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

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

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

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

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @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), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(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) {
        uint256 localValue = value;
        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] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        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 bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

import "./PRBMath.sol";

/// @title PRBMathUD60x18
/// @author Paul Razvan Berg
/// @notice Smart contract library for advanced fixed-point math that works with uint256 numbers considered to have 18
/// trailing decimals. We call this number representation unsigned 60.18-decimal fixed-point, since there can be up to 60
/// digits in the integer part and up to 18 decimals in the fractional part. The numbers are bound by the minimum and the
/// maximum values permitted by the Solidity type uint256.
library PRBMathUD60x18 {
    /// @dev Half the SCALE number.
    uint256 internal constant HALF_SCALE = 5e17;

    /// @dev log2(e) as an unsigned 60.18-decimal fixed-point number.
    uint256 internal constant LOG2_E = 1_442695040888963407;

    /// @dev The maximum value an unsigned 60.18-decimal fixed-point number can have.
    uint256 internal constant MAX_UD60x18 =
        115792089237316195423570985008687907853269984665640564039457_584007913129639935;

    /// @dev The maximum whole value an unsigned 60.18-decimal fixed-point number can have.
    uint256 internal constant MAX_WHOLE_UD60x18 =
        115792089237316195423570985008687907853269984665640564039457_000000000000000000;

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @notice Calculates the arithmetic average of x and y, rounding down.
    /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
    /// @return result The arithmetic average as an unsigned 60.18-decimal fixed-point number.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // The operations can never overflow.
        unchecked {
            // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need
            // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice.
            result = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @notice Yields the least unsigned 60.18 decimal fixed-point number greater than or equal to x.
    ///
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    ///
    /// Requirements:
    /// - x must be less than or equal to MAX_WHOLE_UD60x18.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number to ceil.
    /// @param result The least integer greater than or equal to x, as an unsigned 60.18-decimal fixed-point number.
    function ceil(uint256 x) internal pure returns (uint256 result) {
        if (x > MAX_WHOLE_UD60x18) {
            revert PRBMathUD60x18__CeilOverflow(x);
        }
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "SCALE - remainder" but faster.
            let delta := sub(SCALE, remainder)

            // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster.
            result := add(x, mul(delta, gt(remainder, 0)))
        }
    }

    /// @notice Divides two unsigned 60.18-decimal fixed-point numbers, returning a new unsigned 60.18-decimal fixed-point number.
    ///
    /// @dev Uses mulDiv to enable overflow-safe multiplication and division.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    ///
    /// @param x The numerator as an unsigned 60.18-decimal fixed-point number.
    /// @param y The denominator as an unsigned 60.18-decimal fixed-point number.
    /// @param result The quotient as an unsigned 60.18-decimal fixed-point number.
    function div(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = PRBMath.mulDiv(x, SCALE, y);
    }

    /// @notice Returns Euler's number as an unsigned 60.18-decimal fixed-point number.
    /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant).
    function e() internal pure returns (uint256 result) {
        result = 2_718281828459045235;
    }

    /// @notice Calculates the natural exponent of x.
    ///
    /// @dev Based on the insight that e^x = 2^(x * log2(e)).
    ///
    /// Requirements:
    /// - All from "log2".
    /// - x must be less than 133.084258667509499441.
    ///
    /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp(uint256 x) internal pure returns (uint256 result) {
        // Without this check, the value passed to "exp2" would be greater than 192.
        if (x >= 133_084258667509499441) {
            revert PRBMathUD60x18__ExpInputTooBig(x);
        }

        // Do the fixed-point multiplication inline to save gas.
        unchecked {
            uint256 doubleScaleProduct = x * LOG2_E;
            result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE);
        }
    }

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    ///
    /// @dev See https://ethereum.stackexchange.com/q/79903/24693.
    ///
    /// Requirements:
    /// - x must be 192 or less.
    /// - The result must fit within MAX_UD60x18.
    ///
    /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
        if (x >= 192e18) {
            revert PRBMathUD60x18__Exp2InputTooBig(x);
        }

        unchecked {
            // Convert x to the 192.64-bit fixed-point format.
            uint256 x192x64 = (x << 64) / SCALE;

            // Pass x to the PRBMath.exp2 function, which uses the 192.64-bit fixed-point number representation.
            result = PRBMath.exp2(x192x64);
        }
    }

    /// @notice Yields the greatest unsigned 60.18 decimal fixed-point number less than or equal to x.
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    /// @param x The unsigned 60.18-decimal fixed-point number to floor.
    /// @param result The greatest integer less than or equal to x, as an unsigned 60.18-decimal fixed-point number.
    function floor(uint256 x) internal pure returns (uint256 result) {
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster.
            result := sub(x, mul(remainder, gt(remainder, 0)))
        }
    }

    /// @notice Yields the excess beyond the floor of x.
    /// @dev Based on the odd function definition https://en.wikipedia.org/wiki/Fractional_part.
    /// @param x The unsigned 60.18-decimal fixed-point number to get the fractional part of.
    /// @param result The fractional part of x as an unsigned 60.18-decimal fixed-point number.
    function frac(uint256 x) internal pure returns (uint256 result) {
        assembly {
            result := mod(x, SCALE)
        }
    }

    /// @notice Converts a number from basic integer form to unsigned 60.18-decimal fixed-point representation.
    ///
    /// @dev Requirements:
    /// - x must be less than or equal to MAX_UD60x18 divided by SCALE.
    ///
    /// @param x The basic integer to convert.
    /// @param result The same number in unsigned 60.18-decimal fixed-point representation.
    function fromUint(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__FromUintOverflow(x);
            }
            result = x * SCALE;
        }
    }

    /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down.
    ///
    /// @dev Requirements:
    /// - x * y must fit within MAX_UD60x18, lest it overflows.
    ///
    /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function gm(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        unchecked {
            // Checking for overflow this way is faster than letting Solidity do it.
            uint256 xy = x * y;
            if (xy / x != y) {
                revert PRBMathUD60x18__GmOverflow(x, y);
            }

            // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE
            // during multiplication. See the comments within the "sqrt" function.
            result = PRBMath.sqrt(xy);
        }
    }

    /// @notice Calculates 1 / x, rounding toward zero.
    ///
    /// @dev Requirements:
    /// - x cannot be zero.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the inverse.
    /// @return result The inverse as an unsigned 60.18-decimal fixed-point number.
    function inv(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // 1e36 is SCALE * SCALE.
            result = 1e36 / x;
        }
    }

    /// @notice Calculates the natural logarithm of x.
    ///
    /// @dev Based on the insight that ln(x) = log2(x) / log2(e).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    /// - This doesn't return exactly 1 for 2.718281828459045235, for that we would need more fine-grained precision.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the natural logarithm.
    /// @return result The natural logarithm as an unsigned 60.18-decimal fixed-point number.
    function ln(uint256 x) internal pure returns (uint256 result) {
        // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x)
        // can return is 196205294292027477728.
        unchecked {
            result = (log2(x) * SCALE) / LOG2_E;
        }
    }

    /// @notice Calculates the common logarithm of x.
    ///
    /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common
    /// logarithm based on the insight that log10(x) = log2(x) / log2(10).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the common logarithm.
    /// @return result The common logarithm as an unsigned 60.18-decimal fixed-point number.
    function log10(uint256 x) internal pure returns (uint256 result) {
        if (x < SCALE) {
            revert PRBMathUD60x18__LogInputTooSmall(x);
        }

        // Note that the "mul" in this block is the assembly multiplication operation, not the "mul" function defined
        // in this contract.
        // prettier-ignore
        assembly {
            switch x
            case 1 { result := mul(SCALE, sub(0, 18)) }
            case 10 { result := mul(SCALE, sub(1, 18)) }
            case 100 { result := mul(SCALE, sub(2, 18)) }
            case 1000 { result := mul(SCALE, sub(3, 18)) }
            case 10000 { result := mul(SCALE, sub(4, 18)) }
            case 100000 { result := mul(SCALE, sub(5, 18)) }
            case 1000000 { result := mul(SCALE, sub(6, 18)) }
            case 10000000 { result := mul(SCALE, sub(7, 18)) }
            case 100000000 { result := mul(SCALE, sub(8, 18)) }
            case 1000000000 { result := mul(SCALE, sub(9, 18)) }
            case 10000000000 { result := mul(SCALE, sub(10, 18)) }
            case 100000000000 { result := mul(SCALE, sub(11, 18)) }
            case 1000000000000 { result := mul(SCALE, sub(12, 18)) }
            case 10000000000000 { result := mul(SCALE, sub(13, 18)) }
            case 100000000000000 { result := mul(SCALE, sub(14, 18)) }
            case 1000000000000000 { result := mul(SCALE, sub(15, 18)) }
            case 10000000000000000 { result := mul(SCALE, sub(16, 18)) }
            case 100000000000000000 { result := mul(SCALE, sub(17, 18)) }
            case 1000000000000000000 { result := 0 }
            case 10000000000000000000 { result := SCALE }
            case 100000000000000000000 { result := mul(SCALE, 2) }
            case 1000000000000000000000 { result := mul(SCALE, 3) }
            case 10000000000000000000000 { result := mul(SCALE, 4) }
            case 100000000000000000000000 { result := mul(SCALE, 5) }
            case 1000000000000000000000000 { result := mul(SCALE, 6) }
            case 10000000000000000000000000 { result := mul(SCALE, 7) }
            case 100000000000000000000000000 { result := mul(SCALE, 8) }
            case 1000000000000000000000000000 { result := mul(SCALE, 9) }
            case 10000000000000000000000000000 { result := mul(SCALE, 10) }
            case 100000000000000000000000000000 { result := mul(SCALE, 11) }
            case 1000000000000000000000000000000 { result := mul(SCALE, 12) }
            case 10000000000000000000000000000000 { result := mul(SCALE, 13) }
            case 100000000000000000000000000000000 { result := mul(SCALE, 14) }
            case 1000000000000000000000000000000000 { result := mul(SCALE, 15) }
            case 10000000000000000000000000000000000 { result := mul(SCALE, 16) }
            case 100000000000000000000000000000000000 { result := mul(SCALE, 17) }
            case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) }
            case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) }
            case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) }
            case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) }
            case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) }
            case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) }
            case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) }
            case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) }
            case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) }
            case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) }
            case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) }
            case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) }
            case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) }
            case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) }
            case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) }
            case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) }
            case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) }
            case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) }
            case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) }
            case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) }
            case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) }
            case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) }
            case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) }
            case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) }
            case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) }
            case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) }
            case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) }
            case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) }
            case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) }
            case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) }
            case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) }
            case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) }
            case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 59) }
            default {
                result := MAX_UD60x18
            }
        }

        if (result == MAX_UD60x18) {
            // Do the fixed-point division inline to save gas. The denominator is log2(10).
            unchecked {
                result = (log2(x) * SCALE) / 3_321928094887362347;
            }
        }
    }

    /// @notice Calculates the binary logarithm of x.
    ///
    /// @dev Based on the iterative approximation algorithm.
    /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
    ///
    /// Requirements:
    /// - x must be greater than or equal to SCALE, otherwise the result would be negative.
    ///
    /// Caveats:
    /// - The results are nor perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the binary logarithm.
    /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number.
    function log2(uint256 x) internal pure returns (uint256 result) {
        if (x < SCALE) {
            revert PRBMathUD60x18__LogInputTooSmall(x);
        }
        unchecked {
            // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
            uint256 n = PRBMath.mostSignificantBit(x / SCALE);

            // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number. The operation can't overflow
            // because n is maximum 255 and SCALE is 1e18.
            result = n * SCALE;

            // This is y = x * 2^(-n).
            uint256 y = x >> n;

            // If y = 1, the fractional part is zero.
            if (y == SCALE) {
                return result;
            }

            // Calculate the fractional part via the iterative approximation.
            // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
            for (uint256 delta = HALF_SCALE; delta > 0; delta >>= 1) {
                y = (y * y) / SCALE;

                // Is y^2 > 2 and so in the range [2,4)?
                if (y >= 2 * SCALE) {
                    // Add the 2^(-m) factor to the logarithm.
                    result += delta;

                    // Corresponds to z/2 on Wikipedia.
                    y >>= 1;
                }
            }
        }
    }

    /// @notice Multiplies two unsigned 60.18-decimal fixed-point numbers together, returning a new unsigned 60.18-decimal
    /// fixed-point number.
    /// @dev See the documentation for the "PRBMath.mulDivFixedPoint" function.
    /// @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 product as an unsigned 60.18-decimal fixed-point number.
    function mul(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = PRBMath.mulDivFixedPoint(x, y);
    }

    /// @notice Returns PI as an unsigned 60.18-decimal fixed-point number.
    function pi() internal pure returns (uint256 result) {
        result = 3_141592653589793238;
    }

    /// @notice Raises x to the power of y.
    ///
    /// @dev Based on the insight that x^y = 2^(log2(x) * y).
    ///
    /// Requirements:
    /// - All from "exp2", "log2" and "mul".
    ///
    /// Caveats:
    /// - All from "exp2", "log2" and "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x Number to raise to given power y, as an unsigned 60.18-decimal fixed-point number.
    /// @param y Exponent to raise x to, as an unsigned 60.18-decimal fixed-point number.
    /// @return result x raised to power y, as an unsigned 60.18-decimal fixed-point number.
    function pow(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            result = y == 0 ? SCALE : uint256(0);
        } else {
            result = exp2(mul(log2(x), y));
        }
    }

    /// @notice Raises x (unsigned 60.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
    /// famous algorithm "exponentiation by squaring".
    ///
    /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
    ///
    /// Requirements:
    /// - The result must fit within MAX_UD60x18.
    ///
    /// Caveats:
    /// - All from "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x The base as an unsigned 60.18-decimal fixed-point number.
    /// @param y The exponent as an uint256.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function powu(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // Calculate the first iteration of the loop in advance.
        result = y & 1 > 0 ? x : SCALE;

        // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
        for (y >>= 1; y > 0; y >>= 1) {
            x = PRBMath.mulDivFixedPoint(x, x);

            // Equivalent to "y % 2 == 1" but faster.
            if (y & 1 > 0) {
                result = PRBMath.mulDivFixedPoint(result, x);
            }
        }
    }

    /// @notice Returns 1 as an unsigned 60.18-decimal fixed-point number.
    function scale() internal pure returns (uint256 result) {
        result = SCALE;
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Requirements:
    /// - x must be less than MAX_UD60x18 / SCALE.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the square root.
    /// @return result The result as an unsigned 60.18-decimal fixed-point .
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__SqrtOverflow(x);
            }
            // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two unsigned
            // 60.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root).
            result = PRBMath.sqrt(x * SCALE);
        }
    }

    /// @notice Converts a unsigned 60.18-decimal fixed-point number to basic integer form, rounding down in the process.
    /// @param x The unsigned 60.18-decimal fixed-point number to convert.
    /// @return result The same number in basic integer form.
    function toUint(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            result = x / SCALE;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @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 v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

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

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

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

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

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

    /**
     * @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 towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (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 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

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

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

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

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

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

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

                // Flip twos such that it is 2^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 (unsignedRoundsUp(rounding) && 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
     * towards zero.
     *
     * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

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

pragma solidity ^0.8.20;

/**
 * @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: Unlicense
pragma solidity >=0.8.4;

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivFixedPointOverflow(uint256 prod1);

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator);

/// @notice Emitted when one of the inputs is type(int256).min.
error PRBMath__MulDivSignedInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows int256.
error PRBMath__MulDivSignedOverflow(uint256 rAbs);

/// @notice Emitted when the input is MIN_SD59x18.
error PRBMathSD59x18__AbsInputTooSmall();

/// @notice Emitted when ceiling a number overflows SD59x18.
error PRBMathSD59x18__CeilOverflow(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__DivInputTooSmall();

/// @notice Emitted when one of the intermediary unsigned results overflows SD59x18.
error PRBMathSD59x18__DivOverflow(uint256 rAbs);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathSD59x18__ExpInputTooBig(int256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathSD59x18__Exp2InputTooBig(int256 x);

/// @notice Emitted when flooring a number underflows SD59x18.
error PRBMathSD59x18__FloorUnderflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMathSD59x18__FromIntOverflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMathSD59x18__FromIntUnderflow(int256 x);

/// @notice Emitted when the product of the inputs is negative.
error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y);

/// @notice Emitted when multiplying the inputs overflows SD59x18.
error PRBMathSD59x18__GmOverflow(int256 x, int256 y);

/// @notice Emitted when the input is less than or equal to zero.
error PRBMathSD59x18__LogInputTooSmall(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__MulInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__MulOverflow(uint256 rAbs);

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__PowuOverflow(uint256 rAbs);

/// @notice Emitted when the input is negative.
error PRBMathSD59x18__SqrtNegativeInput(int256 x);

/// @notice Emitted when the calculating the square root overflows SD59x18.
error PRBMathSD59x18__SqrtOverflow(int256 x);

/// @notice Emitted when addition overflows UD60x18.
error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y);

/// @notice Emitted when ceiling a number overflows UD60x18.
error PRBMathUD60x18__CeilOverflow(uint256 x);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathUD60x18__ExpInputTooBig(uint256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathUD60x18__Exp2InputTooBig(uint256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18.
error PRBMathUD60x18__FromUintOverflow(uint256 x);

/// @notice Emitted when multiplying the inputs overflows UD60x18.
error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y);

/// @notice Emitted when the input is less than 1.
error PRBMathUD60x18__LogInputTooSmall(uint256 x);

/// @notice Emitted when the calculating the square root overflows UD60x18.
error PRBMathUD60x18__SqrtOverflow(uint256 x);

/// @notice Emitted when subtraction underflows UD60x18.
error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y);

/// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library
/// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point
/// representation. When it does not, it is explicitly mentioned in the NatSpec documentation.
library PRBMath {
    /// STRUCTS ///

    struct SD59x18 {
        int256 value;
    }

    struct UD60x18 {
        uint256 value;
    }

    /// STORAGE ///

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @dev Largest power of two divisor of SCALE.
    uint256 internal constant SCALE_LPOTD = 262144;

    /// @dev SCALE inverted mod 2^256.
    uint256 internal constant SCALE_INVERSE =
        78156646155174841979727994598816262306175212592076161876661_508869554232690281;

    /// 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.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // Start from 0.5 in the 192.64-bit fixed-point format.
            result = 0x800000000000000000000000000000000000000000000000;

            // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows
            // because the initial result is 2^191 and all magic factors are less than 2^65.
            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 & 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 & 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 & 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 & 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 & 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 & 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 & 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;
            }

            // We're doing two things at the same time:
            //
            //   1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for
            //      the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191
            //      rather than 192.
            //   2. Convert the result to the unsigned 60.18-decimal fixed-point format.
            //
            // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n".
            result *= SCALE;
            result >>= (191 - (x >> 64));
        }
    }

    /// @notice Finds the zero-based index of the first one in the binary representation of x.
    /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
    /// @param x The uint256 number for which to find the index of the most significant bit.
    /// @return msb The index of the most significant bit as an uint256.
    function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) {
        if (x >= 2**128) {
            x >>= 128;
            msb += 128;
        }
        if (x >= 2**64) {
            x >>= 64;
            msb += 64;
        }
        if (x >= 2**32) {
            x >>= 32;
            msb += 32;
        }
        if (x >= 2**16) {
            x >>= 16;
            msb += 16;
        }
        if (x >= 2**8) {
            x >>= 8;
            msb += 8;
        }
        if (x >= 2**4) {
            x >>= 4;
            msb += 4;
        }
        if (x >= 2**2) {
            x >>= 2;
            msb += 2;
        }
        if (x >= 2**1) {
            // No need to shift x any more.
            msb += 1;
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The multiplicand as an uint256.
    /// @param y The multiplier as an uint256.
    /// @param denominator The divisor as an uint256.
    /// @return result The result as an uint256.
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal 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 {
            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 {
                result = prod0 / denominator;
            }
            return result;
        }

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

        ///////////////////////////////////////////////
        // 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.
        unchecked {
            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 lpotdod = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by lpotdod.
                denominator := div(denominator, lpotdod)

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

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

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

            // 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 floor(x*y÷1e18) with full precision.
    ///
    /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the
    /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of
    /// being rounded to 1e-18.  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717.
    ///
    /// Requirements:
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
    /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations:
    ///     1. x * y = type(uint256).max * SCALE
    ///     2. (x * y) % SCALE >= SCALE / 2
    ///
    /// @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.
    function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) {
        uint256 prod0;
        uint256 prod1;
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        if (prod1 >= SCALE) {
            revert PRBMath__MulDivFixedPointOverflow(prod1);
        }

        uint256 remainder;
        uint256 roundUpUnit;
        assembly {
            remainder := mulmod(x, y, SCALE)
            roundUpUnit := gt(remainder, 499999999999999999)
        }

        if (prod1 == 0) {
            unchecked {
                result = (prod0 / SCALE) + roundUpUnit;
                return result;
            }
        }

        assembly {
            result := add(
                mul(
                    or(
                        div(sub(prod0, remainder), SCALE_LPOTD),
                        mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1))
                    ),
                    SCALE_INVERSE
                ),
                roundUpUnit
            )
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately.
    ///
    /// Requirements:
    /// - None of the inputs can be type(int256).min.
    /// - The result must fit within 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.
    function mulDivSigned(
        int256 x,
        int256 y,
        int256 denominator
    ) internal pure returns (int256 result) {
        if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
            revert PRBMath__MulDivSignedInputTooSmall();
        }

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

        // Compute the absolute value of (x*y)÷denominator. The result must fit within int256.
        uint256 rAbs = mulDiv(ax, ay, ad);
        if (rAbs > uint256(type(int256).max)) {
            revert PRBMath__MulDivSignedOverflow(rAbs);
        }

        // Get the signs of x, y and the denominator.
        uint256 sx;
        uint256 sy;
        uint256 sd;
        assembly {
            sx := sgt(x, sub(0, 1))
            sy := sgt(y, sub(0, 1))
            sd := sgt(denominator, sub(0, 1))
        }

        // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs.
        // If yes, the result should be negative.
        result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The uint256 number for which to calculate the square root.
    /// @return result The result as an uint256.
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        // Set the initial guess to the least power of two that is greater than or equal to sqrt(x).
        uint256 xAux = uint256(x);
        result = 1;
        if (xAux >= 0x100000000000000000000000000000000) {
            xAux >>= 128;
            result <<= 64;
        }
        if (xAux >= 0x10000000000000000) {
            xAux >>= 64;
            result <<= 32;
        }
        if (xAux >= 0x100000000) {
            xAux >>= 32;
            result <<= 16;
        }
        if (xAux >= 0x10000) {
            xAux >>= 16;
            result <<= 8;
        }
        if (xAux >= 0x100) {
            xAux >>= 8;
            result <<= 4;
        }
        if (xAux >= 0x10) {
            xAux >>= 4;
            result <<= 2;
        }
        if (xAux >= 0x8) {
            result <<= 1;
        }

        // The operations can never overflow because the result is max 2^127 when it enters this block.
        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; // Seven iterations should be enough
            uint256 roundedDownResult = x / result;
            return result >= roundedDownResult ? roundedDownResult : result;
        }
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "prb-math/=node_modules/prb-math/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_stableCoin","type":"address"},{"internalType":"address","name":"_csToken","type":"address"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"AmountNotEnough","type":"error"},{"inputs":[],"name":"CallNotFromBitsave","type":"error"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"CanNotWithdrawToken","type":"error"},{"inputs":[],"name":"InvalidTime","type":"error"},{"inputs":[],"name":"MasterCallRequired","type":"error"},{"inputs":[],"name":"NotEnoughToPayGasFee","type":"error"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"NotSupported","type":"error"},{"inputs":[],"name":"UserNotRegistered","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"}],"name":"JoinedBitsave","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"nameOfSaving","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"SavingCreated","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":"amount","type":"uint256"}],"name":"TokenWithdrawal","type":"event"},{"inputs":[],"name":"ChildContractGasFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"JoinLimitFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SavingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"nameOfSaving","type":"string"},{"internalType":"uint256","name":"maturityTime","type":"uint256"},{"internalType":"uint8","name":"penaltyPercentage","type":"uint8"},{"internalType":"bool","name":"safeMode","type":"bool"},{"internalType":"address","name":"tokenToSave","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"createSaving","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"csToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTotalValueLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentVaultState","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dripFountain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_joinFee","type":"uint256"},{"internalType":"uint256","name":"_savingFee","type":"uint256"}],"name":"editFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newCurrentVaultState","type":"uint256"},{"internalType":"uint256","name":"_newTotalValueLocked","type":"uint256"},{"internalType":"address","name":"_newCsToken","type":"address"}],"name":"editInternalData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newStableCoin","type":"address"}],"name":"editStableCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fountain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUserChildContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"nameOfSavings","type":"string"},{"internalType":"address","name":"tokenToRetrieve","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"incrementSaving","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"joinBitsave","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"masterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"originalToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"ownerAddress","type":"address"}],"name":"sendAsOriginalToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"stableCoin","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"userCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"nameOfSavings","type":"string"}],"name":"withdrawSaving","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6080604052655af3107a4000600955655af3107a4000600a556014600a5461002791906100a4565b600b5560405162002e5438038062002e5483398101604081905261004a916100e2565b600080546001600160a01b039384166001600160a01b03199182161782556001805493909416928116929092179092556002805490911633179055600381905560065562d59f80600755620186a060085534600455610115565b6000826100c157634e487b7160e01b600052601260045260246000fd5b500490565b80516001600160a01b03811681146100dd57600080fd5b919050565b600080604083850312156100f557600080fd5b6100fe836100c6565b915061010c602084016100c6565b90509250929050565b612d2f80620001256000396000f3fe60806040526004361061012e5760003560e01c806384762b0b116100ab578063d5633e961161006f578063d5633e96146102e9578063de35c372146102fe578063e04605fd14610311578063ea41367214610331578063f24053b31461035c578063fed0b9251461037257600080fd5b806384762b0b1461025d578063992642e5146102735780639986e0e914610293578063bf66bfc7146102b3578063d365a08e146102c957600080fd5b8063372f9101116100f2578063372f9101146101ce5780634c3fa304146101fe5780636405350a1461021457806366666aa9146102275780637679489c1461023d57600080fd5b8063025007391461013a57806307973ccf1461016357806316f94d5d14610179578063174fbaee146101995780631fdc181f146101bb57600080fd5b3661013557005b600080fd5b34801561014657600080fd5b5061015060085481565b6040519081526020015b60405180910390f35b34801561016f57600080fd5b5061015060065481565b610181610388565b6040516001600160a01b03909116815260200161015a565b3480156101a557600080fd5b506101b96101b4366004610e89565b6104aa565b005b6101b96101c9366004610f85565b6104ff565b3480156101da57600080fd5b506101ee6101e9366004610fdf565b610756565b604051901515815260200161015a565b34801561020a57600080fd5b5061015060075481565b6101b9610222366004611022565b610821565b34801561023357600080fd5b5061015060035481565b34801561024957600080fd5b506101b96102583660046110b1565b610a0c565b34801561026957600080fd5b5061015060095481565b34801561027f57600080fd5b50600054610181906001600160a01b031681565b34801561029f57600080fd5b506101b96102ae3660046110ea565b610a71565b3480156102bf57600080fd5b50610150600b5481565b3480156102d557600080fd5b50600254610181906001600160a01b031681565b3480156102f557600080fd5b506101b9610aca565b6101ee61030c36600461110e565b610b49565b34801561031d57600080fd5b50600154610181906001600160a01b031681565b34801561033d57600080fd5b50336000908152600560205260409020546001600160a01b0316610181565b34801561036857600080fd5b5061015060045481565b34801561037e57600080fd5b50610150600a5481565b336000818152600560205260408120549091906001600160a01b031680156103b05792915050565b6009543410156103d3576040516332dba14760e21b815260040160405180910390fd5b6000805460405133916001600160a01b0316906103ef90610e7c565b6001600160a01b03928316815291166020820152604001604051809103906000f080158015610422573d6000803e3d6000fd5b506001600160a01b03848116600090815260056020526040812080546001600160a01b031916928416929092179091556006805492935060019290919061046a90849061115b565b90915550506040516001600160a01b038416907f0c5c0c4d53b63790e32770cf47fd115d706000fd7ab63fbdc8d176c5020d6b4690600090a29392505050565b6002546001600160a01b031633146104d557604051630cd98acd60e21b815260040160405180910390fd5b81156104e15760098290555b80156104fb57600a8190556104f7601482611174565b600b555b5050565b336000818152600560205260409020546001600160a01b031661053557604051632163950f60e01b815260040160405180910390fd5b3360009081526005602052604080822054905163e18f5f6560e01b81526001600160a01b03909116918291829063e18f5f6590610576908a906004016111e6565b602060405180830381865afa158015610593573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b791906111f9565b604051631bb862a560e01b81529091506001600160a01b038083161591879160009190861690631bb862a5906105f1908d906004016111e6565b602060405180830381865afa15801561060e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106329190611216565b90508015610649576000546001600160a01b031698505b826106a15761065986838b610bbe565b61069c5760405162461bcd60e51b815260206004820152600f60248201526e14d85d9a5b99dcc81a5b9d985b1a59608a1b60448201526064015b60405180910390fd5b6106a5565b3491505b60006106b2898689610c3f565b9050856001600160a01b031663dc2379b9856106d057600b546106de565b84600b546106de919061115b565b8d8c6007546008546040518663ffffffff1660e01b81526004016107059493929190611233565b60206040518083038185885af1158015610723573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906107489190611262565b505050505050505050505050565b336000818152600560205260408120549091906001600160a01b031661078f57604051632163950f60e01b815260040160405180910390fd5b336000908152600560205260409081902054905163372f910160e01b81526001600160a01b0390911690819063372f9101906107cf9087906004016111e6565b6000604051808303816000875af11580156107ee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610816919081019061127b565b506001949350505050565b336000818152600560205260409020546001600160a01b031661085757604051632163950f60e01b815260040160405180910390fd5b600a5434101561087a576040516368613cd160e11b815260040160405180910390fd5b8542111561089b576040516337bf561360e11b815260040160405180910390fd5b83156108de5760405163329e2f1560e01b81526020600482015260116024820152704e6f2073616665206d6f6465207965742160781b6044820152606401610693565b336000908152600560205260408120546001600160a01b031690610903848684610c3f565b9050816001600160a01b038082169063436d4a6d9088161561092757600b54610935565b83600b54610935919061115b565b8c8c428d8c898f6007546008546040518b63ffffffff1660e01b8152600401610966999897969594939291906112e9565b60206040518083038185885af1158015610984573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906109a99190611262565b50896040516109b8919061134b565b604080519182900382208483526001600160a01b0389166020840152917f4ad85a69286191006ea05981ae1848070dbf232e5e00ffdda4b857c16097f0d7910160405180910390a250505050505050505050565b6002546001600160a01b03163314610a3757604051630cd98acd60e21b815260040160405180910390fd5b600783905560088290556001600160a01b03811615610a6c57600180546001600160a01b0319166001600160a01b0383161790555b505050565b6002546001600160a01b03163314610a9c57604051630cd98acd60e21b815260040160405180910390fd5b6001600160a01b03811615610ac757600080546001600160a01b0319166001600160a01b0383161790555b50565b6002546001600160a01b03163314610af557604051630cd98acd60e21b815260040160405180910390fd5b6004544790811115610ac7576002546004546001600160a01b03909116906108fc90610b219084611367565b6040518115909202916000818181858888f193505050501580156104fb573d6000803e3d6000fd5b6001600160a01b038082166000908152600560205260408120549091839116801580610b7e57506001600160a01b0381163314155b15610b9c5760405163229e1dbd60e01b815260040160405180910390fd5b600054610bb49033906001600160a01b031687610d38565b9695505050505050565b60405163095ea7b360e01b81526001600160a01b03848116600483015260248201849052600091839182169063095ea7b3906044016020604051808303816000875af1158015610c12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c369190611216565b95945050505050565b60006001600160a01b03831615610d20576000610c5d338587610d38565b905080610c9a5760405163ec86672960e01b815260206004820152600a602482015269151e1b8819985a5b195960b21b6044820152606401610693565b604051858152309033907f42856d0378dde02337bb59ae41747abc77ded8ebdbbc5cbdd1e53693b75549389060200160405180910390a3610cdc838686610bbe565b610d1a5760405162461bcd60e51b815260206004820152600f60248201526e14d85d9a5b99dcc81a5b9d985b1a59608a1b6044820152606401610693565b50610d30565b600a54610d2d9034611367565b93505b509192915050565b604051636eb1769f60e11b81526001600160a01b038481166004830152306024830152600091839185169063dd62ed3e90604401602060405180830381865afa158015610d89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dad9190611262565b1015610dfb5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e20636f756c64206e6f742062652077697468647261776e000000006044820152606401610693565b6040516323b872dd60e01b81526001600160a01b038581166004830152306024830152604482018490528416906323b872dd906064016020604051808303816000875af1158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e749190611216565b949350505050565b61197f8061137b83390190565b60008060408385031215610e9c57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610eea57610eea610eab565b604052919050565b600067ffffffffffffffff821115610f0c57610f0c610eab565b50601f01601f191660200190565b600082601f830112610f2b57600080fd5b8135610f3e610f3982610ef2565b610ec1565b818152846020838601011115610f5357600080fd5b816020850160208301376000918101602001919091529392505050565b6001600160a01b0381168114610ac757600080fd5b600080600060608486031215610f9a57600080fd5b833567ffffffffffffffff811115610fb157600080fd5b610fbd86828701610f1a565b9350506020840135610fce81610f70565b929592945050506040919091013590565b600060208284031215610ff157600080fd5b813567ffffffffffffffff81111561100857600080fd5b610e7484828501610f1a565b8015158114610ac757600080fd5b60008060008060008060c0878903121561103b57600080fd5b863567ffffffffffffffff81111561105257600080fd5b61105e89828a01610f1a565b96505060208701359450604087013560ff8116811461107c57600080fd5b9350606087013561108c81611014565b9250608087013561109c81610f70565b8092505060a087013590509295509295509295565b6000806000606084860312156110c657600080fd5b833592506020840135915060408401356110df81610f70565b809150509250925092565b6000602082840312156110fc57600080fd5b813561110781610f70565b9392505050565b60008060006060848603121561112357600080fd5b833561112e81610f70565b92506020840135915060408401356110df81610f70565b634e487b7160e01b600052601160045260246000fd5b8082018082111561116e5761116e611145565b92915050565b60008261119157634e487b7160e01b600052601260045260246000fd5b500490565b60005b838110156111b1578181015183820152602001611199565b50506000910152565b600081518084526111d2816020860160208601611196565b601f01601f19169290920160200192915050565b60208152600061110760208301846111ba565b60006020828403121561120b57600080fd5b815161110781610f70565b60006020828403121561122857600080fd5b815161110781611014565b60808152600061124660808301876111ba565b6020830195909552506040810192909252606090910152919050565b60006020828403121561127457600080fd5b5051919050565b60006020828403121561128d57600080fd5b815167ffffffffffffffff8111156112a457600080fd5b8201601f810184136112b557600080fd5b80516112c3610f3982610ef2565b8181528560208385010111156112d857600080fd5b610c36826020830160208601611196565b60006101208083526112fd8184018d6111ba565b602084019b909b525050604081019790975260ff9590951660608701526001600160a01b0393909316608086015260a0850191909152151560c084015260e083015261010090910152919050565b6000825161135d818460208701611196565b9190910192915050565b8181038181111561116e5761116e61114556fe60806040526040516200197f3803806200197f83398101604081905261002491610082565b600080546001600160a01b031990811633178255600280546001600160a01b0395861690831617905560018054939094169216919091179091556003556100b5565b80516001600160a01b038116811461007d57600080fd5b919050565b6000806040838503121561009557600080fd5b61009e83610066565b91506100ac60208401610066565b90509250929050565b6118ba80620000c56000396000f3fe6080604052600436106100c25760003560e01c8063585dbffe1161007f578063992642e511610059578063992642e51461031e578063d1ece7901461033e578063dc2379b914610360578063e18f5f651461037357600080fd5b8063585dbffe1461025557806384febb69146102de5780638f84aa09146102fe57600080fd5b806313203929146100c75780631bb862a5146101965780632ae3c90f146101c6578063372f9101146101fe578063436d4a6d1461021e578063567142be1461023f575b600080fd5b3480156100d357600080fd5b506101446100e2366004611432565b8051602081830181018051600480835293830192909401919091209290528154600183015460028401546003850154938501546005860154600687015460079097015460ff9586169794966001600160a01b0390941695939492939192911688565b60408051981515895260208901979097526001600160a01b03909516958701959095526060860192909252608085015260a084015260c0830191909152151560e0820152610100015b60405180910390f35b3480156101a257600080fd5b506101b66101b1366004611432565b610393565b604051901515815260200161018d565b3480156101d257600080fd5b506000546101e6906001600160a01b031681565b6040516001600160a01b03909116815260200161018d565b61021161020c366004611432565b6103c1565b60405161018d91906114b7565b61023161022c3660046114e6565b6106dd565b60405190815260200161018d565b34801561024b57600080fd5b5061023160035481565b34801561026157600080fd5b50610275610270366004611432565b61094f565b60405161018d9190815115158152602080830151908201526040808301516001600160a01b031690820152606080830151908201526080808301519082015260a0808301519082015260c0808301519082015260e0918201511515918101919091526101000190565b3480156102ea57600080fd5b506102316102f9366004611432565b610a35565b34801561030a57600080fd5b506002546101e6906001600160a01b031681565b34801561032a57600080fd5b506001546101e6906001600160a01b031681565b34801561034a57600080fd5b50610353610a60565b60405161018d919061159c565b61023161036e366004611608565b610b50565b34801561037f57600080fd5b506101e661038e366004611432565b610e0c565b60006004826040516103a5919061165c565b9081526040519081900360200190206007015460ff1692915050565b6000546060906001600160a01b031633146103ef5760405163229e1dbd60e01b815260040160405180910390fd5b6000600483604051610401919061165c565b908152604051908190036020019020805490915060ff1661043557604051631ac7a3c960e31b815260040160405180910390fd5b600181015460005460068301546001600160a01b039091169042101561048457606483600501546064610468919061168e565b846001015461047791906116a1565b61048191906116ce565b91505b600283015460078401546001600160a01b039091169060009060ff161561054a576000546001546104c3916001600160a01b0390811691879116610e40565b50600254604051636f1ae1b960e11b81526001600160a01b0384811660048301526024820187905291821660448201529084169063de35c372906064016020604051808303816000875af115801561051f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061054391906116f0565b9050610625565b6001600160a01b0382166106035760025460405160009182916001600160a01b039091169087908381818185875af1925050503d80600081146105a9576040519150601f19603f3d011682016040523d82523d6000602084013e6105ae565b606091505b5091509150816105fb5760405162461bcd60e51b8152602060048201526013602482015272436f756c646e27742073656e642066756e647360681b60448201526064015b60405180910390fd5b509050610625565b6002808601549054610622916001600160a01b03908116911686610ec3565b90505b80156100c257600060048860405161063d919061165c565b908152604051908190036020018120805492151560ff199093169290921790915561066990889061165c565b604051908190038120907f5b059b7fe1fea38d4de595079621498a365a8f1fa13e49af9f1e4b7197ab049a90600090a2505060408051808201909152601e81527f736176696e67732077697468647261776e207375636365737366756c6c790000602082015295945050505050565b919050565b600080546001600160a01b031633146107095760405163229e1dbd60e01b815260040160405180910390fd5b60048a604051610719919061165c565b9081526040519081900360200190205460ff161561074a57604051631ac7a3c960e31b815260040160405180910390fd5b8789101561076b576040516337bf561360e11b815260040160405180910390fd5b4289101561078c576040516337bf561360e11b815260040160405180910390fd5b8484156107b6576000546001546107b0916001600160a01b03908116911688610f92565b506107df565b6001600160a01b038716156107dc576000546107b0906001600160a01b03168888610f92565b50345b60006107ee828c8c88886110d6565b9050604051806101000160405280600115158152602001838152602001896001600160a01b031681526020018281526020018b81526020018a60ff1681526020018c815260200187151581525060048d60405161084b919061165c565b90815260408051602092819003830190208351815460ff19908116911515919091178255928401516001820155908301516002820180546001600160a01b0319166001600160a01b03909216919091179055606083015160038201556080830151600482015560a0830151600582015560c0830151600682015560e090920151600790920180549091169115159190911790556108e78c6110fc565b8b6040516108f5919061165c565b604080519182900382208983526001600160a01b038b166020840152917f4ad85a69286191006ea05981ae1848070dbf232e5e00ffdda4b857c16097f0d7910160405180910390a25060019b9a5050505050505050505050565b6109a46040518061010001604052806000151581526020016000815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000151581525090565b6004826040516109b4919061165c565b90815260408051918290036020908101832061010084018352805460ff9081161515855260018201549285019290925260028101546001600160a01b0316928401929092526003820154606084015260048201546080840152600582015460a0840152600682015460c084015260079091015416151560e082015292915050565b6000600482604051610a47919061165c565b9081526020016040518091039020600301549050919050565b6040805160208101909152606081526040805160058054602081810284018501855283018181529293919284929091849160009085015b82821015610b43578382906000526020600020018054610ab69061170d565b80601f0160208091040260200160405190810160405280929190818152602001828054610ae29061170d565b8015610b2f5780601f10610b0457610100808354040283529160200191610b2f565b820191906000526020600020905b815481529060010190602001808311610b1257829003601f168201915b505050505081526020019060010190610a97565b5050505081525050905090565b600080546001600160a01b03163314610b7c5760405163229e1dbd60e01b815260040160405180910390fd5b6000600486604051610b8e919061165c565b908152604051908190036020019020805490915060ff16610bc257604051631ac7a3c960e31b815260040160405180910390fd5b8060060154421115610be7576040516337bf561360e11b815260040160405180910390fd5b600281015460078201546001600160a01b03909116159060ff1615610c2957600054600154610c23916001600160a01b03908116911688610f92565b50610cad565b80610c4d576000546002830154610c23916001600160a01b03908116911688610f92565b85341015610ca95760405162461bcd60e51b815260206004820152602360248201527f496e76616c696420736176696e6720696e6372656d656e742076616c75652073604482015262195b9d60ea1b60648201526084016105f2565b3495505b6000610cc08784600601544289896110d6565b9050808360030154610cd29190611747565b60038401556001830154610ce7908890611747565b60018401556040518390600490610cff908b9061165c565b9081526040519081900360200181208254815460ff918216151560ff199182161783556001808601549084015560028086015490840180546001600160a01b0319166001600160a01b03909216919091179055600380860154908401556004808601549084015560058086015490840155600680860154908401556007948501549490920180549490911615159390911692909217909155610da290899061165c565b60408051918290038220600186015460028701548b855260208501919091526001600160a01b031683830152905190917ff6b98d8e0d83f8b1d1ff03a2fa36aba2c77ccf20e4ff035c0cf4aa92762e9eec919081900360600190a250506003015495945050505050565b6000600482604051610e1e919061165c565b908152604051908190036020019020600201546001600160a01b031692915050565b60405163095ea7b360e01b81526001600160a01b03848116600483015260248201849052600091839182169063095ea7b3906044016020604051808303816000875af1158015610e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb891906116f0565b9150505b9392505050565b60405163a9059cbb60e01b81526001600160a01b03838116600483015260248201839052600091859182169063a9059cbb906044016020604051808303816000875af1158015610f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3b91906116f0565b9150836001600160a01b0316306001600160a01b03167f42856d0378dde02337bb59ae41747abc77ded8ebdbbc5cbdd1e53693b755493885604051610f8291815260200190565b60405180910390a3509392505050565b604051636eb1769f60e11b81526001600160a01b038481166004830152306024830152600091839185169063dd62ed3e90604401602060405180830381865afa158015610fe3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611007919061175a565b10156110555760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e20636f756c64206e6f742062652077697468647261776e0000000060448201526064016105f2565b6040516323b872dd60e01b81526001600160a01b038581166004830152306024830152604482018490528416906323b872dd906064016020604051808303816000875af11580156110aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ce91906116f0565b949350505050565b60006110ed866110e6868861168e565b858561113c565b60038190559695505050505050565b600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00161113882826117c4565b5050565b600080611161606461115b866111558162e4e1c061168e565b906111da565b906111ef565b9050600061117d61117285846116a1565b6305f5e100906111da565b9050600061118f876301e133806111da565b90506111ce6111c06111ab683635c9adc5dea0000060646116a1565b836111b6868d6116a1565b61115591906116a1565b670de0b6b3a7640000900490565b98975050505050505050565b6000610ebc83670de0b6b3a7640000846111fb565b6000610ebc83836112c8565b60008080600019858709858702925082811083820303915050806000036112355783828161122b5761122b6116b8565b0492505050610ebc565b83811061125f57604051631dcf306360e21b815260048101829052602481018590526044016105f2565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60008080600019848609848602925082811083820303915050670de0b6b3a7640000811061130c5760405163698d9a0160e11b8152600481018290526024016105f2565b600080670de0b6b3a764000086880991506706f05b59d3b1ffff821190508260000361134a5780670de0b6b3a7640000850401945050505050611389565b620400008285030493909111909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac10669020190505b92915050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126113b657600080fd5b813567ffffffffffffffff808211156113d1576113d161138f565b604051601f8301601f19908116603f011681019082821181831017156113f9576113f961138f565b8160405283815286602085880101111561141257600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006020828403121561144457600080fd5b813567ffffffffffffffff81111561145b57600080fd5b6110ce848285016113a5565b60005b8381101561148257818101518382015260200161146a565b50506000910152565b600081518084526114a3816020860160208601611467565b601f01601f19169290920160200192915050565b602081526000610ebc602083018461148b565b80151581146114d857600080fd5b50565b80356106d8816114ca565b60008060008060008060008060006101208a8c03121561150557600080fd5b893567ffffffffffffffff81111561151c57600080fd5b6115288c828d016113a5565b99505060208a0135975060408a0135965060608a013560ff8116811461154d57600080fd5b955060808a01356001600160a01b038116811461156957600080fd5b945060a08a0135935061157e60c08b016114db565b925060e08a013591506101008a013590509295985092959850929598565b60006020808352604083018451828386015281815180845260608701915060608160051b8801019350848301925060005b818110156115fb57605f198886030183526115e985855161148b565b945092850192918501916001016115cd565b5092979650505050505050565b6000806000806080858703121561161e57600080fd5b843567ffffffffffffffff81111561163557600080fd5b611641878288016113a5565b97602087013597506040870135966060013595509350505050565b6000825161166e818460208701611467565b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561138957611389611678565b808202811582820484141761138957611389611678565b634e487b7160e01b600052601260045260246000fd5b6000826116eb57634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561170257600080fd5b8151610ebc816114ca565b600181811c9082168061172157607f821691505b60208210810361174157634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561138957611389611678565b60006020828403121561176c57600080fd5b5051919050565b601f8211156117bf576000816000526020600020601f850160051c8101602086101561179c5750805b601f850160051c820191505b818110156117bb578281556001016117a8565b5050505b505050565b815167ffffffffffffffff8111156117de576117de61138f565b6117f2816117ec845461170d565b84611773565b602080601f831160018114611827576000841561180f5750858301515b600019600386901b1c1916600185901b1785556117bb565b600085815260208120601f198616915b8281101561185657888601518255948401946001909101908401611837565b50858210156118745787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220386aad08a856a308c306b99b1d61fcc3abd04684a11c4dd9ffeb220ba42fc37264736f6c63430008170033a26469706673582212201cc5c789e9e49266ee2aec0aae3da266a355e0b840ed7fecc3f8c28fcf819b8864736f6c6343000817003300000000000000000000000005d032ac25d322df992303dca074ee7392c117b900000000000000000000000005d032ac25d322df992303dca074ee7392c117b9

Deployed Bytecode

0x60806040526004361061012e5760003560e01c806384762b0b116100ab578063d5633e961161006f578063d5633e96146102e9578063de35c372146102fe578063e04605fd14610311578063ea41367214610331578063f24053b31461035c578063fed0b9251461037257600080fd5b806384762b0b1461025d578063992642e5146102735780639986e0e914610293578063bf66bfc7146102b3578063d365a08e146102c957600080fd5b8063372f9101116100f2578063372f9101146101ce5780634c3fa304146101fe5780636405350a1461021457806366666aa9146102275780637679489c1461023d57600080fd5b8063025007391461013a57806307973ccf1461016357806316f94d5d14610179578063174fbaee146101995780631fdc181f146101bb57600080fd5b3661013557005b600080fd5b34801561014657600080fd5b5061015060085481565b6040519081526020015b60405180910390f35b34801561016f57600080fd5b5061015060065481565b610181610388565b6040516001600160a01b03909116815260200161015a565b3480156101a557600080fd5b506101b96101b4366004610e89565b6104aa565b005b6101b96101c9366004610f85565b6104ff565b3480156101da57600080fd5b506101ee6101e9366004610fdf565b610756565b604051901515815260200161015a565b34801561020a57600080fd5b5061015060075481565b6101b9610222366004611022565b610821565b34801561023357600080fd5b5061015060035481565b34801561024957600080fd5b506101b96102583660046110b1565b610a0c565b34801561026957600080fd5b5061015060095481565b34801561027f57600080fd5b50600054610181906001600160a01b031681565b34801561029f57600080fd5b506101b96102ae3660046110ea565b610a71565b3480156102bf57600080fd5b50610150600b5481565b3480156102d557600080fd5b50600254610181906001600160a01b031681565b3480156102f557600080fd5b506101b9610aca565b6101ee61030c36600461110e565b610b49565b34801561031d57600080fd5b50600154610181906001600160a01b031681565b34801561033d57600080fd5b50336000908152600560205260409020546001600160a01b0316610181565b34801561036857600080fd5b5061015060045481565b34801561037e57600080fd5b50610150600a5481565b336000818152600560205260408120549091906001600160a01b031680156103b05792915050565b6009543410156103d3576040516332dba14760e21b815260040160405180910390fd5b6000805460405133916001600160a01b0316906103ef90610e7c565b6001600160a01b03928316815291166020820152604001604051809103906000f080158015610422573d6000803e3d6000fd5b506001600160a01b03848116600090815260056020526040812080546001600160a01b031916928416929092179091556006805492935060019290919061046a90849061115b565b90915550506040516001600160a01b038416907f0c5c0c4d53b63790e32770cf47fd115d706000fd7ab63fbdc8d176c5020d6b4690600090a29392505050565b6002546001600160a01b031633146104d557604051630cd98acd60e21b815260040160405180910390fd5b81156104e15760098290555b80156104fb57600a8190556104f7601482611174565b600b555b5050565b336000818152600560205260409020546001600160a01b031661053557604051632163950f60e01b815260040160405180910390fd5b3360009081526005602052604080822054905163e18f5f6560e01b81526001600160a01b03909116918291829063e18f5f6590610576908a906004016111e6565b602060405180830381865afa158015610593573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b791906111f9565b604051631bb862a560e01b81529091506001600160a01b038083161591879160009190861690631bb862a5906105f1908d906004016111e6565b602060405180830381865afa15801561060e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106329190611216565b90508015610649576000546001600160a01b031698505b826106a15761065986838b610bbe565b61069c5760405162461bcd60e51b815260206004820152600f60248201526e14d85d9a5b99dcc81a5b9d985b1a59608a1b60448201526064015b60405180910390fd5b6106a5565b3491505b60006106b2898689610c3f565b9050856001600160a01b031663dc2379b9856106d057600b546106de565b84600b546106de919061115b565b8d8c6007546008546040518663ffffffff1660e01b81526004016107059493929190611233565b60206040518083038185885af1158015610723573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906107489190611262565b505050505050505050505050565b336000818152600560205260408120549091906001600160a01b031661078f57604051632163950f60e01b815260040160405180910390fd5b336000908152600560205260409081902054905163372f910160e01b81526001600160a01b0390911690819063372f9101906107cf9087906004016111e6565b6000604051808303816000875af11580156107ee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610816919081019061127b565b506001949350505050565b336000818152600560205260409020546001600160a01b031661085757604051632163950f60e01b815260040160405180910390fd5b600a5434101561087a576040516368613cd160e11b815260040160405180910390fd5b8542111561089b576040516337bf561360e11b815260040160405180910390fd5b83156108de5760405163329e2f1560e01b81526020600482015260116024820152704e6f2073616665206d6f6465207965742160781b6044820152606401610693565b336000908152600560205260408120546001600160a01b031690610903848684610c3f565b9050816001600160a01b038082169063436d4a6d9088161561092757600b54610935565b83600b54610935919061115b565b8c8c428d8c898f6007546008546040518b63ffffffff1660e01b8152600401610966999897969594939291906112e9565b60206040518083038185885af1158015610984573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906109a99190611262565b50896040516109b8919061134b565b604080519182900382208483526001600160a01b0389166020840152917f4ad85a69286191006ea05981ae1848070dbf232e5e00ffdda4b857c16097f0d7910160405180910390a250505050505050505050565b6002546001600160a01b03163314610a3757604051630cd98acd60e21b815260040160405180910390fd5b600783905560088290556001600160a01b03811615610a6c57600180546001600160a01b0319166001600160a01b0383161790555b505050565b6002546001600160a01b03163314610a9c57604051630cd98acd60e21b815260040160405180910390fd5b6001600160a01b03811615610ac757600080546001600160a01b0319166001600160a01b0383161790555b50565b6002546001600160a01b03163314610af557604051630cd98acd60e21b815260040160405180910390fd5b6004544790811115610ac7576002546004546001600160a01b03909116906108fc90610b219084611367565b6040518115909202916000818181858888f193505050501580156104fb573d6000803e3d6000fd5b6001600160a01b038082166000908152600560205260408120549091839116801580610b7e57506001600160a01b0381163314155b15610b9c5760405163229e1dbd60e01b815260040160405180910390fd5b600054610bb49033906001600160a01b031687610d38565b9695505050505050565b60405163095ea7b360e01b81526001600160a01b03848116600483015260248201849052600091839182169063095ea7b3906044016020604051808303816000875af1158015610c12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c369190611216565b95945050505050565b60006001600160a01b03831615610d20576000610c5d338587610d38565b905080610c9a5760405163ec86672960e01b815260206004820152600a602482015269151e1b8819985a5b195960b21b6044820152606401610693565b604051858152309033907f42856d0378dde02337bb59ae41747abc77ded8ebdbbc5cbdd1e53693b75549389060200160405180910390a3610cdc838686610bbe565b610d1a5760405162461bcd60e51b815260206004820152600f60248201526e14d85d9a5b99dcc81a5b9d985b1a59608a1b6044820152606401610693565b50610d30565b600a54610d2d9034611367565b93505b509192915050565b604051636eb1769f60e11b81526001600160a01b038481166004830152306024830152600091839185169063dd62ed3e90604401602060405180830381865afa158015610d89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dad9190611262565b1015610dfb5760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e20636f756c64206e6f742062652077697468647261776e000000006044820152606401610693565b6040516323b872dd60e01b81526001600160a01b038581166004830152306024830152604482018490528416906323b872dd906064016020604051808303816000875af1158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e749190611216565b949350505050565b61197f8061137b83390190565b60008060408385031215610e9c57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610eea57610eea610eab565b604052919050565b600067ffffffffffffffff821115610f0c57610f0c610eab565b50601f01601f191660200190565b600082601f830112610f2b57600080fd5b8135610f3e610f3982610ef2565b610ec1565b818152846020838601011115610f5357600080fd5b816020850160208301376000918101602001919091529392505050565b6001600160a01b0381168114610ac757600080fd5b600080600060608486031215610f9a57600080fd5b833567ffffffffffffffff811115610fb157600080fd5b610fbd86828701610f1a565b9350506020840135610fce81610f70565b929592945050506040919091013590565b600060208284031215610ff157600080fd5b813567ffffffffffffffff81111561100857600080fd5b610e7484828501610f1a565b8015158114610ac757600080fd5b60008060008060008060c0878903121561103b57600080fd5b863567ffffffffffffffff81111561105257600080fd5b61105e89828a01610f1a565b96505060208701359450604087013560ff8116811461107c57600080fd5b9350606087013561108c81611014565b9250608087013561109c81610f70565b8092505060a087013590509295509295509295565b6000806000606084860312156110c657600080fd5b833592506020840135915060408401356110df81610f70565b809150509250925092565b6000602082840312156110fc57600080fd5b813561110781610f70565b9392505050565b60008060006060848603121561112357600080fd5b833561112e81610f70565b92506020840135915060408401356110df81610f70565b634e487b7160e01b600052601160045260246000fd5b8082018082111561116e5761116e611145565b92915050565b60008261119157634e487b7160e01b600052601260045260246000fd5b500490565b60005b838110156111b1578181015183820152602001611199565b50506000910152565b600081518084526111d2816020860160208601611196565b601f01601f19169290920160200192915050565b60208152600061110760208301846111ba565b60006020828403121561120b57600080fd5b815161110781610f70565b60006020828403121561122857600080fd5b815161110781611014565b60808152600061124660808301876111ba565b6020830195909552506040810192909252606090910152919050565b60006020828403121561127457600080fd5b5051919050565b60006020828403121561128d57600080fd5b815167ffffffffffffffff8111156112a457600080fd5b8201601f810184136112b557600080fd5b80516112c3610f3982610ef2565b8181528560208385010111156112d857600080fd5b610c36826020830160208601611196565b60006101208083526112fd8184018d6111ba565b602084019b909b525050604081019790975260ff9590951660608701526001600160a01b0393909316608086015260a0850191909152151560c084015260e083015261010090910152919050565b6000825161135d818460208701611196565b9190910192915050565b8181038181111561116e5761116e61114556fe60806040526040516200197f3803806200197f83398101604081905261002491610082565b600080546001600160a01b031990811633178255600280546001600160a01b0395861690831617905560018054939094169216919091179091556003556100b5565b80516001600160a01b038116811461007d57600080fd5b919050565b6000806040838503121561009557600080fd5b61009e83610066565b91506100ac60208401610066565b90509250929050565b6118ba80620000c56000396000f3fe6080604052600436106100c25760003560e01c8063585dbffe1161007f578063992642e511610059578063992642e51461031e578063d1ece7901461033e578063dc2379b914610360578063e18f5f651461037357600080fd5b8063585dbffe1461025557806384febb69146102de5780638f84aa09146102fe57600080fd5b806313203929146100c75780631bb862a5146101965780632ae3c90f146101c6578063372f9101146101fe578063436d4a6d1461021e578063567142be1461023f575b600080fd5b3480156100d357600080fd5b506101446100e2366004611432565b8051602081830181018051600480835293830192909401919091209290528154600183015460028401546003850154938501546005860154600687015460079097015460ff9586169794966001600160a01b0390941695939492939192911688565b60408051981515895260208901979097526001600160a01b03909516958701959095526060860192909252608085015260a084015260c0830191909152151560e0820152610100015b60405180910390f35b3480156101a257600080fd5b506101b66101b1366004611432565b610393565b604051901515815260200161018d565b3480156101d257600080fd5b506000546101e6906001600160a01b031681565b6040516001600160a01b03909116815260200161018d565b61021161020c366004611432565b6103c1565b60405161018d91906114b7565b61023161022c3660046114e6565b6106dd565b60405190815260200161018d565b34801561024b57600080fd5b5061023160035481565b34801561026157600080fd5b50610275610270366004611432565b61094f565b60405161018d9190815115158152602080830151908201526040808301516001600160a01b031690820152606080830151908201526080808301519082015260a0808301519082015260c0808301519082015260e0918201511515918101919091526101000190565b3480156102ea57600080fd5b506102316102f9366004611432565b610a35565b34801561030a57600080fd5b506002546101e6906001600160a01b031681565b34801561032a57600080fd5b506001546101e6906001600160a01b031681565b34801561034a57600080fd5b50610353610a60565b60405161018d919061159c565b61023161036e366004611608565b610b50565b34801561037f57600080fd5b506101e661038e366004611432565b610e0c565b60006004826040516103a5919061165c565b9081526040519081900360200190206007015460ff1692915050565b6000546060906001600160a01b031633146103ef5760405163229e1dbd60e01b815260040160405180910390fd5b6000600483604051610401919061165c565b908152604051908190036020019020805490915060ff1661043557604051631ac7a3c960e31b815260040160405180910390fd5b600181015460005460068301546001600160a01b039091169042101561048457606483600501546064610468919061168e565b846001015461047791906116a1565b61048191906116ce565b91505b600283015460078401546001600160a01b039091169060009060ff161561054a576000546001546104c3916001600160a01b0390811691879116610e40565b50600254604051636f1ae1b960e11b81526001600160a01b0384811660048301526024820187905291821660448201529084169063de35c372906064016020604051808303816000875af115801561051f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061054391906116f0565b9050610625565b6001600160a01b0382166106035760025460405160009182916001600160a01b039091169087908381818185875af1925050503d80600081146105a9576040519150601f19603f3d011682016040523d82523d6000602084013e6105ae565b606091505b5091509150816105fb5760405162461bcd60e51b8152602060048201526013602482015272436f756c646e27742073656e642066756e647360681b60448201526064015b60405180910390fd5b509050610625565b6002808601549054610622916001600160a01b03908116911686610ec3565b90505b80156100c257600060048860405161063d919061165c565b908152604051908190036020018120805492151560ff199093169290921790915561066990889061165c565b604051908190038120907f5b059b7fe1fea38d4de595079621498a365a8f1fa13e49af9f1e4b7197ab049a90600090a2505060408051808201909152601e81527f736176696e67732077697468647261776e207375636365737366756c6c790000602082015295945050505050565b919050565b600080546001600160a01b031633146107095760405163229e1dbd60e01b815260040160405180910390fd5b60048a604051610719919061165c565b9081526040519081900360200190205460ff161561074a57604051631ac7a3c960e31b815260040160405180910390fd5b8789101561076b576040516337bf561360e11b815260040160405180910390fd5b4289101561078c576040516337bf561360e11b815260040160405180910390fd5b8484156107b6576000546001546107b0916001600160a01b03908116911688610f92565b506107df565b6001600160a01b038716156107dc576000546107b0906001600160a01b03168888610f92565b50345b60006107ee828c8c88886110d6565b9050604051806101000160405280600115158152602001838152602001896001600160a01b031681526020018281526020018b81526020018a60ff1681526020018c815260200187151581525060048d60405161084b919061165c565b90815260408051602092819003830190208351815460ff19908116911515919091178255928401516001820155908301516002820180546001600160a01b0319166001600160a01b03909216919091179055606083015160038201556080830151600482015560a0830151600582015560c0830151600682015560e090920151600790920180549091169115159190911790556108e78c6110fc565b8b6040516108f5919061165c565b604080519182900382208983526001600160a01b038b166020840152917f4ad85a69286191006ea05981ae1848070dbf232e5e00ffdda4b857c16097f0d7910160405180910390a25060019b9a5050505050505050505050565b6109a46040518061010001604052806000151581526020016000815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000151581525090565b6004826040516109b4919061165c565b90815260408051918290036020908101832061010084018352805460ff9081161515855260018201549285019290925260028101546001600160a01b0316928401929092526003820154606084015260048201546080840152600582015460a0840152600682015460c084015260079091015416151560e082015292915050565b6000600482604051610a47919061165c565b9081526020016040518091039020600301549050919050565b6040805160208101909152606081526040805160058054602081810284018501855283018181529293919284929091849160009085015b82821015610b43578382906000526020600020018054610ab69061170d565b80601f0160208091040260200160405190810160405280929190818152602001828054610ae29061170d565b8015610b2f5780601f10610b0457610100808354040283529160200191610b2f565b820191906000526020600020905b815481529060010190602001808311610b1257829003601f168201915b505050505081526020019060010190610a97565b5050505081525050905090565b600080546001600160a01b03163314610b7c5760405163229e1dbd60e01b815260040160405180910390fd5b6000600486604051610b8e919061165c565b908152604051908190036020019020805490915060ff16610bc257604051631ac7a3c960e31b815260040160405180910390fd5b8060060154421115610be7576040516337bf561360e11b815260040160405180910390fd5b600281015460078201546001600160a01b03909116159060ff1615610c2957600054600154610c23916001600160a01b03908116911688610f92565b50610cad565b80610c4d576000546002830154610c23916001600160a01b03908116911688610f92565b85341015610ca95760405162461bcd60e51b815260206004820152602360248201527f496e76616c696420736176696e6720696e6372656d656e742076616c75652073604482015262195b9d60ea1b60648201526084016105f2565b3495505b6000610cc08784600601544289896110d6565b9050808360030154610cd29190611747565b60038401556001830154610ce7908890611747565b60018401556040518390600490610cff908b9061165c565b9081526040519081900360200181208254815460ff918216151560ff199182161783556001808601549084015560028086015490840180546001600160a01b0319166001600160a01b03909216919091179055600380860154908401556004808601549084015560058086015490840155600680860154908401556007948501549490920180549490911615159390911692909217909155610da290899061165c565b60408051918290038220600186015460028701548b855260208501919091526001600160a01b031683830152905190917ff6b98d8e0d83f8b1d1ff03a2fa36aba2c77ccf20e4ff035c0cf4aa92762e9eec919081900360600190a250506003015495945050505050565b6000600482604051610e1e919061165c565b908152604051908190036020019020600201546001600160a01b031692915050565b60405163095ea7b360e01b81526001600160a01b03848116600483015260248201849052600091839182169063095ea7b3906044016020604051808303816000875af1158015610e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eb891906116f0565b9150505b9392505050565b60405163a9059cbb60e01b81526001600160a01b03838116600483015260248201839052600091859182169063a9059cbb906044016020604051808303816000875af1158015610f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3b91906116f0565b9150836001600160a01b0316306001600160a01b03167f42856d0378dde02337bb59ae41747abc77ded8ebdbbc5cbdd1e53693b755493885604051610f8291815260200190565b60405180910390a3509392505050565b604051636eb1769f60e11b81526001600160a01b038481166004830152306024830152600091839185169063dd62ed3e90604401602060405180830381865afa158015610fe3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611007919061175a565b10156110555760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e20636f756c64206e6f742062652077697468647261776e0000000060448201526064016105f2565b6040516323b872dd60e01b81526001600160a01b038581166004830152306024830152604482018490528416906323b872dd906064016020604051808303816000875af11580156110aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ce91906116f0565b949350505050565b60006110ed866110e6868861168e565b858561113c565b60038190559695505050505050565b600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00161113882826117c4565b5050565b600080611161606461115b866111558162e4e1c061168e565b906111da565b906111ef565b9050600061117d61117285846116a1565b6305f5e100906111da565b9050600061118f876301e133806111da565b90506111ce6111c06111ab683635c9adc5dea0000060646116a1565b836111b6868d6116a1565b61115591906116a1565b670de0b6b3a7640000900490565b98975050505050505050565b6000610ebc83670de0b6b3a7640000846111fb565b6000610ebc83836112c8565b60008080600019858709858702925082811083820303915050806000036112355783828161122b5761122b6116b8565b0492505050610ebc565b83811061125f57604051631dcf306360e21b815260048101829052602481018590526044016105f2565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60008080600019848609848602925082811083820303915050670de0b6b3a7640000811061130c5760405163698d9a0160e11b8152600481018290526024016105f2565b600080670de0b6b3a764000086880991506706f05b59d3b1ffff821190508260000361134a5780670de0b6b3a7640000850401945050505050611389565b620400008285030493909111909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac10669020190505b92915050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126113b657600080fd5b813567ffffffffffffffff808211156113d1576113d161138f565b604051601f8301601f19908116603f011681019082821181831017156113f9576113f961138f565b8160405283815286602085880101111561141257600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006020828403121561144457600080fd5b813567ffffffffffffffff81111561145b57600080fd5b6110ce848285016113a5565b60005b8381101561148257818101518382015260200161146a565b50506000910152565b600081518084526114a3816020860160208601611467565b601f01601f19169290920160200192915050565b602081526000610ebc602083018461148b565b80151581146114d857600080fd5b50565b80356106d8816114ca565b60008060008060008060008060006101208a8c03121561150557600080fd5b893567ffffffffffffffff81111561151c57600080fd5b6115288c828d016113a5565b99505060208a0135975060408a0135965060608a013560ff8116811461154d57600080fd5b955060808a01356001600160a01b038116811461156957600080fd5b945060a08a0135935061157e60c08b016114db565b925060e08a013591506101008a013590509295985092959850929598565b60006020808352604083018451828386015281815180845260608701915060608160051b8801019350848301925060005b818110156115fb57605f198886030183526115e985855161148b565b945092850192918501916001016115cd565b5092979650505050505050565b6000806000806080858703121561161e57600080fd5b843567ffffffffffffffff81111561163557600080fd5b611641878288016113a5565b97602087013597506040870135966060013595509350505050565b6000825161166e818460208701611467565b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561138957611389611678565b808202811582820484141761138957611389611678565b634e487b7160e01b600052601260045260246000fd5b6000826116eb57634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561170257600080fd5b8151610ebc816114ca565b600181811c9082168061172157607f821691505b60208210810361174157634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561138957611389611678565b60006020828403121561176c57600080fd5b5051919050565b601f8211156117bf576000816000526020600020601f850160051c8101602086101561179c5750805b601f850160051c820191505b818110156117bb578281556001016117a8565b5050505b505050565b815167ffffffffffffffff8111156117de576117de61138f565b6117f2816117ec845461170d565b84611773565b602080601f831160018114611827576000841561180f5750858301515b600019600386901b1c1916600185901b1785556117bb565b600085815260208120601f198616915b8281101561185657888601518255948401946001909101908401611837565b50858210156118745787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220386aad08a856a308c306b99b1d61fcc3abd04684a11c4dd9ffeb220ba42fc37264736f6c63430008170033a26469706673582212201cc5c789e9e49266ee2aec0aae3da266a355e0b840ed7fecc3f8c28fcf819b8864736f6c63430008170033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000005d032ac25d322df992303dca074ee7392c117b900000000000000000000000005d032ac25d322df992303dca074ee7392c117b9

-----Decoded View---------------
Arg [0] : _stableCoin (address): 0x05D032ac25d322df992303dCa074EE7392C117b9
Arg [1] : _csToken (address): 0x05D032ac25d322df992303dCa074EE7392C117b9

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000005d032ac25d322df992303dca074ee7392c117b9
Arg [1] : 00000000000000000000000005d032ac25d322df992303dca074ee7392c117b9


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ 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.