ETH Price: $1,975.50 (+0.74%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw Perfect244888862026-02-19 5:45:473 days ago1771479947IN
Fluid: fSL19 Token
0 ETH0.000008650.04404729
Withdraw Perfect241675582026-01-05 9:26:4747 days ago1767605207IN
Fluid: fSL19 Token
0 ETH0.000015360.07820216
Withdraw Perfect240901962025-12-25 14:20:3558 days ago1766672435IN
Fluid: fSL19 Token
0 ETH0.000022630.1152161
Withdraw236976282025-10-31 13:27:11113 days ago1761917231IN
Fluid: fSL19 Token
0 ETH0.000344822.2843575
Withdraw Perfect236502132025-10-24 22:10:11120 days ago1761343811IN
Fluid: fSL19 Token
0 ETH0.000330722.10781956
Withdraw235280102025-10-07 19:37:11137 days ago1759865831IN
Fluid: fSL19 Token
0 ETH0.00016961.00976846
Withdraw233477902025-09-12 15:10:23162 days ago1757689823IN
Fluid: fSL19 Token
0 ETH0.000277921.65441536
Withdraw Perfect232817692025-09-03 9:44:35171 days ago1756892675IN
Fluid: fSL19 Token
0 ETH0.000046950.24665412
Withdraw231871622025-08-21 4:46:35185 days ago1755751595IN
Fluid: fSL19 Token
0 ETH0.000034010.20250949
Withdraw Perfect231491652025-08-15 21:35:59190 days ago1755293759IN
Fluid: fSL19 Token
0 ETH0.00008670.45535931
Withdraw Perfect229477892025-07-18 18:09:59218 days ago1752862199IN
Fluid: fSL19 Token
0 ETH0.001196717.38051429
Withdraw229295952025-07-16 5:15:59221 days ago1752642959IN
Fluid: fSL19 Token
0 ETH0.000138220.91583451
Withdraw Perfect228643312025-07-07 2:27:47230 days ago1751855267IN
Fluid: fSL19 Token
0 ETH0.000446622.34522357
Withdraw Perfect228489442025-07-04 22:48:47232 days ago1751669327IN
Fluid: fSL19 Token
0 ETH0.000060670.29229834
Withdraw227619602025-06-22 19:06:59244 days ago1750619219IN
Fluid: fSL19 Token
0 ETH0.000225041.39132909
Withdraw225750442025-05-27 15:34:11270 days ago1748360051IN
Fluid: fSL19 Token
0 ETH0.000953076.31433687
Withdraw225299002025-05-21 7:52:23276 days ago1747813943IN
Fluid: fSL19 Token
0 ETH0.000277891.65374896
Withdraw224784162025-05-14 2:21:47284 days ago1747189307IN
Fluid: fSL19 Token
0 ETH0.000346682.14299118
Deposit Perfect224680282025-05-12 15:15:47285 days ago1747062947IN
Fluid: fSL19 Token
0 ETH0.0035036716.73998576
Withdraw224645162025-05-12 3:23:35286 days ago1747020215IN
Fluid: fSL19 Token
0 ETH0.000254131.51267108
Withdraw Perfect224415542025-05-08 21:44:47289 days ago1746740687IN
Fluid: fSL19 Token
0 ETH0.0032720415.76373887
Withdraw Perfect224372442025-05-08 7:09:11289 days ago1746688151IN
Fluid: fSL19 Token
0 ETH0.00017261.1
Withdraw224252522025-05-06 14:26:59291 days ago1746541619IN
Fluid: fSL19 Token
0 ETH0.000369562.44889249
Deposit224171622025-05-05 11:11:59292 days ago1746443519IN
Fluid: fSL19 Token
0 ETH0.000062950.34309783
Deposit224163112025-05-05 8:20:47292 days ago1746433247IN
Fluid: fSL19 Token
0 ETH0.000092770.52163375
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x61016060217318172025-01-29 17:56:11388 days ago1738173371  Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
FluidSmartLending

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 10000000 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

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

import { IFluidDexT1 } from "../interfaces/iDexT1.sol";
import { FluidDexFactory } from "../factory/main.sol";
import { FluidSmartLendingFactory } from "./factory/main.sol";
import { SafeTransfer } from "../../../libraries/safeTransfer.sol";
import { ErrorTypes } from "../errorTypes.sol";
import { Error } from "../error.sol";
import { DexSlotsLink } from "../../../libraries/dexSlotsLink.sol";
import { DexCalcs } from "../../../libraries/dexCalcs.sol";

abstract contract Constants {
    /// @dev Ignoring leap years
    uint256 internal constant SECONDS_PER_YEAR = 365 days;

    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    address internal constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;

    FluidDexFactory public immutable DEX_FACTORY;

    FluidSmartLendingFactory public immutable SMART_LENDING_FACTORY;

    IFluidDexT1 public immutable DEX;

    address public immutable LIQUIDITY;

    address public immutable TOKEN0;

    address public immutable TOKEN1;

    bool public immutable IS_NATIVE_PAIR;
}

abstract contract Variables is ERC20, Constants {
    // ------------ storage variables from inherited contracts come before vars here --------
    // _________ ERC20 _______________
    // ----------------------- slot 0 ---------------------------
    // mapping(address => uint256) private _balances;

    // ----------------------- slot 1 ---------------------------
    // mapping(address => mapping(address => uint256)) private _allowances;

    // ----------------------- slot 2 ---------------------------
    // uint256 private _totalSupply;

    // ----------------------- slot 3 ---------------------------
    // string private _name;
    // ----------------------- slot 4 ---------------------------
    // string private _symbol;

    // ------------ storage variables ------------------------------------------------------

    // ----------------------- slot 5 ---------------------------
    uint40 public lastTimestamp;
    /// If positive then rewards, if negative then fee.
    /// 1e6 = 100%, 1e4 = 1%, minimum 0.0001% fee or reward.
    int32 public feeOrReward;
    // Starting from 1e18
    // If fees then reduce exchange price
    // If reward then increase exchange price
    uint184 public exchangePrice;

    // ----------------------- slot 6 ---------------------------
    address public rebalancer;

    // ----------------------- slot 7 ---------------------------
    address public dexFromAddress;

    /// @dev status for reentrancy guard
    uint8 internal _status;
}

abstract contract Events {
    /// @dev Emitted when the share to tokens ratio is rebalanced
    /// @param shares_ The number of shares rebalanced
    /// @param token0Amt_ The amount of token0 rebalanced
    /// @param token1Amt_ The amount of token1 rebalanced
    /// @param isWithdraw_ Whether the rebalance is a withdrawal or deposit
    event LogRebalance(uint256 shares_, uint256 token0Amt_, uint256 token1Amt_, bool isWithdraw_);

    /// @dev Emitted when the rebalancer is set
    /// @param rebalancer The new rebalancer
    event LogRebalancerSet(address rebalancer);

    /// @dev Emitted when the fee or reward is set
    /// @param feeOrReward The new fee or reward
    event LogFeeOrRewardSet(int256 feeOrReward);
}

/// @dev ReentrancyGuard based on OpenZeppelin implementation.
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/security/ReentrancyGuard.sol
abstract contract ReentrancyGuard is Variables, Error {
    uint8 internal constant REENTRANCY_NOT_ENTERED = 1;
    uint8 internal constant REENTRANCY_ENTERED = 2;

    constructor() {
        _status = REENTRANCY_NOT_ENTERED;
    }

    /// @dev Prevents a contract from calling itself, directly or indirectly.
    /// See OpenZeppelin implementation for more info
    modifier nonReentrant() {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status != REENTRANCY_NOT_ENTERED) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Reentrancy);
        }

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

        _;

        // storing original value triggers a refund (see https://eips.ethereum.org/EIPS/eip-2200)
        _status = REENTRANCY_NOT_ENTERED;
    }
}

contract FluidSmartLending is ERC20, Variables, Error, ReentrancyGuard, Events {
    /// @dev prefix for token name. constructor appends dex id, e.g. "Fluid Smart Lending 12"
    string private constant TOKEN_NAME_PREFIX = "Fluid Smart Lending ";
    /// @dev prefix for token symbol. constructor appends dex id, e.g. "fSL12"
    string private constant TOKEN_SYMBOL_PREFIX = "fSL";

    /// @dev Validates that an address is not the zero address
    modifier validAddress(address value_) {
        if (value_ == address(0)) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__ZeroAddress);
        }
        _;
    }

    constructor(
        uint256 dexId_,
        address liquidity_,
        address dexFactory_,
        address smartLendingFactory_
    )
        ERC20(
            string(abi.encodePacked(TOKEN_NAME_PREFIX, _toString(dexId_))),
            string(abi.encodePacked(TOKEN_SYMBOL_PREFIX, _toString(dexId_)))
        )
        validAddress(liquidity_)
        validAddress(dexFactory_)
        validAddress(smartLendingFactory_)
    {
        LIQUIDITY = liquidity_;
        DEX_FACTORY = FluidDexFactory(dexFactory_);
        SMART_LENDING_FACTORY = FluidSmartLendingFactory(smartLendingFactory_);
        DEX = IFluidDexT1(DEX_FACTORY.getDexAddress(dexId_));
        IFluidDexT1.ConstantViews memory constants_ = DEX.constantsView();
        TOKEN0 = constants_.token0;
        TOKEN1 = constants_.token1;
        IS_NATIVE_PAIR = (TOKEN0 == ETH_ADDRESS) || (TOKEN1 == ETH_ADDRESS);

        exchangePrice = uint184(1e18);
        feeOrReward = int32(0);
        lastTimestamp = uint40(block.timestamp);

        dexFromAddress = DEAD_ADDRESS;
    }

    modifier setDexFrom() {
        dexFromAddress = msg.sender;
        _;
        dexFromAddress = DEAD_ADDRESS;
    }

    modifier onlyAuth() {
        if (!SMART_LENDING_FACTORY.isSmartLendingAuth(address(this), msg.sender)) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Unauthorized);
        }
        _;
    }

    modifier onlyOwner() {
        if (msg.sender != SMART_LENDING_FACTORY.owner()) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Unauthorized);
        }
        _;
    }

    modifier _updateExchangePrice() {
        bool rewardsOrFeeActive_;
        (exchangePrice, rewardsOrFeeActive_) = getUpdateExchangePrice();
        if (rewardsOrFeeActive_) {
            lastTimestamp = uint40(block.timestamp); // only write to storage if fee or reward is active.
        }
        _;
    }

    /// @notice gets updated exchange price
    function getUpdateExchangePrice() public view returns (uint184 exchangePrice_, bool rewardsOrFeeActive_) {
        int256 feeOrReward_ = feeOrReward;
        exchangePrice_ = exchangePrice;
        if (feeOrReward_ > 0) {
            exchangePrice_ =
                exchangePrice_ +
                uint184(
                    (exchangePrice_ * uint256(feeOrReward_) * (block.timestamp - uint256(lastTimestamp))) /
                        (1e6 * SECONDS_PER_YEAR)
                );
            rewardsOrFeeActive_ = true;
        } else if (feeOrReward_ < 0) {
            exchangePrice_ =
                exchangePrice_ -
                uint184(
                    (exchangePrice_ * uint256(-feeOrReward_) * (block.timestamp - uint256(lastTimestamp))) /
                        (1e6 * SECONDS_PER_YEAR)
                );
            rewardsOrFeeActive_ = true;
        }
    }

    /// @notice triggers updateExchangePrice
    function updateExchangePrice() public _updateExchangePrice {}

    /// @dev Set the fee or reward. Only callable by auths.
    /// @param feeOrReward_ The new fee or reward (1e6 = 100%, 1e4 = 1%, minimum 0.0001% fee or reward). 0 means no fee or reward
    function setFeeOrReward(int256 feeOrReward_) external onlyAuth _updateExchangePrice {
        if (feeOrReward_ > 1e6 || feeOrReward_ < -1e6) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__OutOfRange);
        }
        lastTimestamp = uint40(block.timestamp); // current fee or reward setting is applied until exactly now even if previously 0
        feeOrReward = int32(feeOrReward_);

        emit LogFeeOrRewardSet(feeOrReward_);
    }

    /// @dev Set the rebalancer. Only callable by auths.
    /// @param rebalancer_ The new rebalancer
    function setRebalancer(address rebalancer_) external onlyAuth validAddress(rebalancer_) {
        rebalancer = rebalancer_;

        emit LogRebalancerSet(rebalancer_);
    }

    /// @notice                         Spell allows auths (governance) to do any arbitrary call
    /// @param target_                  Address to which the call needs to be delegated
    /// @param data_                    Data to execute at the delegated address
    function spell(address target_, bytes memory data_) external onlyOwner returns (bytes memory response_) {
        assembly {
            let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
            let size := returndatasize()

            response_ := mload(0x40)
            mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(response_, size)
            returndatacopy(add(response_, 0x20), 0, size)

            switch iszero(succeeded)
            case 1 {
                // throw if delegatecall failed
                returndatacopy(0x00, 0x00, size)
                revert(0x00, size)
            }
        }
    }

    /// @dev Deposit tokens in equal proportion to the current pool ratio
    /// @param shares_ The number of shares to mint
    /// @param maxToken0Deposit_ Maximum amount of token0 to deposit
    /// @param maxToken1Deposit_ Maximum amount of token1 to deposit
    /// @param to_ Recipient of minted tokens. If to_ == address(0) then out tokens will be sent to msg.sender.
    /// @return amount_ Amount of tokens minted
    /// @return token0Amt_ Amount of token0 deposited
    /// @return token1Amt_ Amount of token1 deposited
    function depositPerfect(
        uint256 shares_,
        uint256 maxToken0Deposit_,
        uint256 maxToken1Deposit_,
        address to_
    )
        external
        payable
        setDexFrom
        _updateExchangePrice
        nonReentrant
        returns (uint256 amount_, uint256 token0Amt_, uint256 token1Amt_)
    {
        if (!IS_NATIVE_PAIR) {
            if (msg.value > 0) {
                revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
            }

            (token0Amt_, token1Amt_) = DEX.depositPerfect(
                shares_ + 1, // + 1 rounding up but only minting shares
                maxToken0Deposit_,
                maxToken1Deposit_,
                false
            );
        } else {
            uint256 value_ = TOKEN0 == ETH_ADDRESS ? maxToken0Deposit_ : maxToken1Deposit_;
            if (value_ > msg.value) {
                revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
            }

            uint256 initialEthAmount_ = address(this).balance - msg.value;

            (token0Amt_, token1Amt_) = DEX.depositPerfect{ value: value_ }(
                shares_ + 1, // + 1 rounding up but only minting shares
                maxToken0Deposit_,
                maxToken1Deposit_,
                false
            );

            uint finalEth_ = payable(address(this)).balance;
            if (finalEth_ > initialEthAmount_) {
                unchecked {
                    SafeTransfer.safeTransferNative(msg.sender, finalEth_ - initialEthAmount_); // sending back excess ETH
                }
            }
        }

        to_ = to_ == address(0) ? msg.sender : to_;

        amount_ = (shares_ * 1e18) / exchangePrice;

        _mint(to_, amount_);
    }

    /// @dev This function allows users to deposit tokens in any proportion into the col pool
    /// @param token0Amt_ The amount of token0 to deposit
    /// @param token1Amt_ The amount of token1 to deposit
    /// @param minSharesAmt_ The minimum amount of shares the user expects to receive
    /// @param to_ Recipient of minted tokens. If to_ == address(0) then out tokens will be sent to msg.sender.
    /// @return amount_ The amount of tokens minted for the deposit
    /// @return shares_ The number of dex pool shares deposited
    function deposit(
        uint256 token0Amt_,
        uint256 token1Amt_,
        uint256 minSharesAmt_,
        address to_
    ) external payable setDexFrom _updateExchangePrice nonReentrant returns (uint256 amount_, uint256 shares_) {
        uint256 value_ = !IS_NATIVE_PAIR
            ? 0
            : (TOKEN0 == ETH_ADDRESS)
                ? token0Amt_
                : token1Amt_;

        if (value_ != msg.value) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
        }

        to_ = to_ == address(0) ? msg.sender : to_;

        shares_ = DEX.deposit{ value: value_ }(token0Amt_, token1Amt_, minSharesAmt_, false);

        amount_ = (shares_ * 1e18) / exchangePrice - 1;

        _mint(to_, amount_);
    }

    /// @dev This function allows users to withdraw a perfect amount of collateral liquidity
    /// @param shares_ The number of shares to withdraw. set to type(uint).max to withdraw maximum balance.
    /// @param minToken0Withdraw_ The minimum amount of token0 the user is willing to accept
    /// @param minToken1Withdraw_ The minimum amount of token1 the user is willing to accept
    /// @param to_ Recipient of withdrawn tokens. If to_ == address(0) then out tokens will be sent to msg.sender.
    /// @return amount_ amount_ of shares actually burnt
    /// @return token0Amt_ The amount of token0 withdrawn
    /// @return token1Amt_ The amount of token1 withdrawn
    function withdrawPerfect(
        uint256 shares_,
        uint256 minToken0Withdraw_,
        uint256 minToken1Withdraw_,
        address to_
    ) external _updateExchangePrice nonReentrant returns (uint256 amount_, uint256 token0Amt_, uint256 token1Amt_) {
        if (shares_ == type(uint).max) {
            amount_ = balanceOf(msg.sender);
            shares_ = (amount_ * exchangePrice) / 1e18 - 1;
        } else {
            amount_ = (shares_ * 1e18) / exchangePrice + 1;
        }

        _burn(msg.sender, amount_);

        to_ = to_ == address(0) ? msg.sender : to_;

        if (minToken0Withdraw_ > 0 && minToken1Withdraw_ > 0) {
            (token0Amt_, token1Amt_) = DEX.withdrawPerfect(shares_, minToken0Withdraw_, minToken1Withdraw_, to_);
        } else if (minToken0Withdraw_ > 0 && minToken1Withdraw_ == 0) {
            // withdraw only in token0, token1Amt_ remains 0
            (token0Amt_) = DEX.withdrawPerfectInOneToken(shares_, minToken0Withdraw_, minToken1Withdraw_, to_);
        } else if (minToken0Withdraw_ == 0 && minToken1Withdraw_ > 0) {
            // withdraw only in token1, token0Amt_ remains 0
            (token1Amt_) = DEX.withdrawPerfectInOneToken(token0Amt_, minToken0Withdraw_, minToken1Withdraw_, to_);
        } else {
            // meaning user sent both amounts as == 0
            revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidAmounts);
        }
    }

    /// @dev This function allows users to withdraw tokens in any proportion from the col pool
    /// @param token0Amt_ The amount of token0 to withdraw
    /// @param token1Amt_ The amount of token1 to withdraw
    /// @param maxSharesAmt_ The maximum number of shares the user is willing to burn
    /// @param to_ Recipient of withdrawn tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return amount_ The number of tokens burned for the withdrawal
    /// @return shares_ The number of dex pool shares withdrawn
    function withdraw(
        uint256 token0Amt_,
        uint256 token1Amt_,
        uint256 maxSharesAmt_,
        address to_
    ) external _updateExchangePrice nonReentrant returns (uint256 amount_, uint256 shares_) {
        to_ = to_ == address(0) ? msg.sender : to_;

        shares_ = DEX.withdraw(token0Amt_, token1Amt_, maxSharesAmt_, to_);

        amount_ = (shares_ * 1e18) / exchangePrice + 1;

        _burn(msg.sender, amount_);
    }

    /// @dev Rebalances the share to tokens ratio to balance out rewards and fees
    function rebalance(
        uint256 minOrMaxToken0_,
        uint256 minOrMaxToken1_
    )
        public
        payable
        _updateExchangePrice
        nonReentrant
        returns (uint256 shares_, uint256 token0Amt_, uint256 token1Amt_, bool isWithdraw_)
    {
        if (rebalancer != msg.sender) revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidRebalancer);

        int256 rebalanceDiff_ = rebalanceDiff();

        if (rebalanceDiff_ > 0) {
            // fees (withdraw)
            isWithdraw_ = true;
            if (msg.value > 0) {
                revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
            }
            shares_ = uint256(rebalanceDiff_);
            (token0Amt_, token1Amt_) = DEX.withdrawPerfect(shares_, minOrMaxToken0_, minOrMaxToken1_, msg.sender);
        } else if (rebalanceDiff_ < 0) {
            // rewards (deposit)
            isWithdraw_ = false;

            uint256 initialEthAmount_ = address(this).balance - msg.value;

            uint256 value_ = !IS_NATIVE_PAIR
                ? 0
                : (TOKEN0 == ETH_ADDRESS)
                    ? minOrMaxToken0_
                    : minOrMaxToken1_;

            if (value_ > msg.value) {
                revert FluidSmartLendingError(ErrorTypes.SmartLending__InvalidMsgValue);
            }

            shares_ = uint256(-rebalanceDiff_);

            dexFromAddress = msg.sender;
            (token0Amt_, token1Amt_) = DEX.depositPerfect{ value: value_ }(
                shares_,
                minOrMaxToken0_,
                minOrMaxToken1_,
                false
            );
            dexFromAddress = DEAD_ADDRESS;

            uint finalEth_ = payable(address(this)).balance;
            if (finalEth_ > initialEthAmount_) {
                unchecked {
                    SafeTransfer.safeTransferNative(msg.sender, finalEth_ - initialEthAmount_); // sending back excess ETH
                }
            }
        }

        emit LogRebalance(shares_, token0Amt_, token1Amt_, isWithdraw_);
    }

    /// @dev Returns the difference between the total smart lending shares on the DEX and the total smart lending shares calculated.
    /// A positive value indicates fees to collect, while a negative value indicates rewards to be rebalanced.
    function rebalanceDiff() public view returns (int256) {
        uint256 totalSmartLendingSharesOnDex_ = DEX.readFromStorage(
            DexSlotsLink.calculateMappingStorageSlot(DexSlotsLink.DEX_USER_SUPPLY_MAPPING_SLOT, address(this))
        );
        totalSmartLendingSharesOnDex_ =
            (totalSmartLendingSharesOnDex_ >> DexSlotsLink.BITS_USER_SUPPLY_AMOUNT) &
            DexCalcs.X64;
        totalSmartLendingSharesOnDex_ =
            (totalSmartLendingSharesOnDex_ >> DexCalcs.DEFAULT_EXPONENT_SIZE) <<
            (totalSmartLendingSharesOnDex_ & DexCalcs.DEFAULT_EXPONENT_MASK);

        uint256 totalSmartLendingShares_ = (totalSupply() * exchangePrice) / 1e18;

        return int256(totalSmartLendingSharesOnDex_) - int256(totalSmartLendingShares_);
    }

    /// @notice   dex liquidity callback
    /// @param    token_ The token being transferred
    /// @param    amount_ The amount being transferred
    function dexCallback(address token_, uint256 amount_) external {
        if (msg.sender != address(DEX)) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Unauthorized);
        }
        SafeTransfer.safeTransferFrom(token_, dexFromAddress, LIQUIDITY, amount_);
    }

    /// @dev for excess eth being sent back from dex to here
    receive() external payable {
        if (msg.sender != address(DEX)) {
            revert FluidSmartLendingError(ErrorTypes.SmartLending__Unauthorized);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     * taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol
     */
    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 Converts a `uint256` to its ASCII `string` decimal representation.
     * taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol
     */
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    function _toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = _log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            assembly ("memory-safe") {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                assembly ("memory-safe") {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }
}

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

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

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

        _beforeTokenTransfer(from, to, amount);

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

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice implements calculation of address for contracts deployed through CREATE.
/// Accepts contract deployed from which address & nonce
library AddressCalcs {

    /// @notice                         Computes the address of a contract based
    /// @param deployedFrom_            Address from which the contract was deployed
    /// @param nonce_                   Nonce at which the contract was deployed
    /// @return contract_               Address of deployed contract
    function addressCalc(address deployedFrom_, uint nonce_) internal pure returns (address contract_) {
        // @dev based on https://ethereum.stackexchange.com/a/61413

        // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment
        // hence, nonce of vault deployment starts with 1.
        bytes memory data;
        if (nonce_ == 0x00) {
            return address(0);
        } else if (nonce_ <= 0x7f) {
            data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployedFrom_, uint8(nonce_));
        } else if (nonce_ <= 0xff) {
            data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployedFrom_, bytes1(0x81), uint8(nonce_));
        } else if (nonce_ <= 0xffff) {
            data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployedFrom_, bytes1(0x82), uint16(nonce_));
        } else if (nonce_ <= 0xffffff) {
            data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployedFrom_, bytes1(0x83), uint24(nonce_));
        } else {
            data = abi.encodePacked(bytes1(0xda), bytes1(0x94), deployedFrom_, bytes1(0x84), uint32(nonce_));
        }

        return address(uint160(uint256(keccak256(data))));
    }

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.
/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision
/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can
/// result in significant gas cost reduction due to storage space reduction.
/// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision.
/// @dev roundUp is more like a increase 1, which happens everytime for the same number.
/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number.
library BigMathMinified {
    /// @dev constants to use for `roundUp` input param to increase readability
    bool internal constant ROUND_DOWN = false;
    bool internal constant ROUND_UP = true;

    /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).
    /// e.g.:
    /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]
    /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)
    ///                                     => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000
    ///                                                                        ^-------------------- 51(exponent) -------------- ^
    /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011               (2236301563)
    /// exponent =                                            0011,0011     (51)
    /// bigNumber =   1000,0101,0100,1011,0100,0000,1111,1011,0011,0011     (572493200179)
    ///
    /// @param normal number which needs to be converted into Big Number
    /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))
    /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))
    /// @param roundUp signals if result should be rounded down or up
    /// @return bigNumber converted bigNumber (coefficient << exponent)
    function toBigNumber(
        uint256 normal,
        uint256 coefficientSize,
        uint256 exponentSize,
        bool roundUp
    ) internal pure returns (uint256 bigNumber) {
        assembly {
            let lastBit_
            let number_ := normal
            if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
                number_ := shr(0x80, number_)
                lastBit_ := 0x80
            }
            if gt(number_, 0xFFFFFFFFFFFFFFFF) {
                number_ := shr(0x40, number_)
                lastBit_ := add(lastBit_, 0x40)
            }
            if gt(number_, 0xFFFFFFFF) {
                number_ := shr(0x20, number_)
                lastBit_ := add(lastBit_, 0x20)
            }
            if gt(number_, 0xFFFF) {
                number_ := shr(0x10, number_)
                lastBit_ := add(lastBit_, 0x10)
            }
            if gt(number_, 0xFF) {
                number_ := shr(0x8, number_)
                lastBit_ := add(lastBit_, 0x8)
            }
            if gt(number_, 0xF) {
                number_ := shr(0x4, number_)
                lastBit_ := add(lastBit_, 0x4)
            }
            if gt(number_, 0x3) {
                number_ := shr(0x2, number_)
                lastBit_ := add(lastBit_, 0x2)
            }
            if gt(number_, 0x1) {
                lastBit_ := add(lastBit_, 1)
            }
            if gt(number_, 0) {
                lastBit_ := add(lastBit_, 1)
            }
            if lt(lastBit_, coefficientSize) {
                // for throw exception
                lastBit_ := coefficientSize
            }
            let exponent := sub(lastBit_, coefficientSize)
            let coefficient := shr(exponent, normal)
            if and(roundUp, gt(exponent, 0)) {
                // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number
                coefficient := add(coefficient, 1)
                if eq(shl(coefficientSize, 1), coefficient) {
                    // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits
                    // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.
                    coefficient := shl(sub(coefficientSize, 1), 1)
                    exponent := add(exponent, 1)
                }
            }
            if iszero(lt(exponent, shl(exponentSize, 1))) {
                // if exponent is >= exponentSize, the normal number is too big to fit within
                // BigNumber with too small sizes for coefficient and exponent
                revert(0, 0)
            }
            bigNumber := shl(exponentSize, coefficient)
            bigNumber := add(bigNumber, exponent)
        }
    }

    /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`
    function fromBigNumber(
        uint256 bigNumber,
        uint256 exponentSize,
        uint256 exponentMask
    ) internal pure returns (uint256 normal) {
        assembly {
            let coefficient := shr(exponentSize, bigNumber)
            let exponent := and(bigNumber, exponentMask)
            normal := shl(exponent, coefficient)
        }
    }

    /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).
    /// e.g.
    /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111
    /// lastBit =                   ^---------------------------------   83   ----------------------------------------^
    function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {
        assembly {
            let number_ := normal
            if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
                number_ := shr(0x80, number_)
                lastBit := 0x80
            }
            if gt(number_, 0xFFFFFFFFFFFFFFFF) {
                number_ := shr(0x40, number_)
                lastBit := add(lastBit, 0x40)
            }
            if gt(number_, 0xFFFFFFFF) {
                number_ := shr(0x20, number_)
                lastBit := add(lastBit, 0x20)
            }
            if gt(number_, 0xFFFF) {
                number_ := shr(0x10, number_)
                lastBit := add(lastBit, 0x10)
            }
            if gt(number_, 0xFF) {
                number_ := shr(0x8, number_)
                lastBit := add(lastBit, 0x8)
            }
            if gt(number_, 0xF) {
                number_ := shr(0x4, number_)
                lastBit := add(lastBit, 0x4)
            }
            if gt(number_, 0x3) {
                number_ := shr(0x2, number_)
                lastBit := add(lastBit, 0x2)
            }
            if gt(number_, 0x1) {
                lastBit := add(lastBit, 1)
            }
            if gt(number_, 0) {
                lastBit := add(lastBit, 1)
            }
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { BigMathMinified } from "./bigMathMinified.sol";
import { DexSlotsLink } from "./dexSlotsLink.sol";

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// @DEV ATTENTION: ON ANY CHANGES HERE, MAKE SURE THAT LOGIC IN VAULTS WILL STILL BE VALID.
// SOME CODE THERE ASSUMES DEXCALCS == LIQUIDITYCALCS.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

/// @notice implements calculation methods used for Fluid Dex such as updated withdrawal / borrow limits.
library DexCalcs {
    // constants used for BigMath conversion from and to storage
    uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;
    uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;

    uint256 internal constant FOUR_DECIMALS = 1e4;
    uint256 internal constant X14 = 0x3fff;
    uint256 internal constant X18 = 0x3ffff;
    uint256 internal constant X24 = 0xffffff;
    uint256 internal constant X33 = 0x1ffffffff;
    uint256 internal constant X64 = 0xffffffffffffffff;

    ///////////////////////////////////////////////////////////////////////////
    //////////                      CALC LIMITS                       /////////
    ///////////////////////////////////////////////////////////////////////////

    /// @dev calculates withdrawal limit before an operate execution:
    /// amount of user supply that must stay supplied (not amount that can be withdrawn).
    /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
    /// @param userSupplyData_ user supply data packed uint256 from storage
    /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath
    /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction.
    ///         returned value is in raw for with interest mode, normal amount for interest free mode!
    function calcWithdrawalLimitBeforeOperate(
        uint256 userSupplyData_,
        uint256 userSupply_
    ) internal view returns (uint256 currentWithdrawalLimit_) {
        // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet).
        // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet.
        // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be
        // a deposit anyway. Important is that it would not revert.

        // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit
        // is the fully expanded limit immediately.

        // extract last set withdrawal limit
        uint256 lastWithdrawalLimit_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) &
            X64;
        lastWithdrawalLimit_ =
            (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) <<
            (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK);
        if (lastWithdrawalLimit_ == 0) {
            // withdrawal limit is not activated. Max withdrawal allowed
            return 0;
        }

        uint256 maxWithdrawableLimit_;
        uint256 temp_;
        unchecked {
            // extract max withdrawable percent of user supply and
            // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed
            // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed.

            // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            maxWithdrawableLimit_ =
                (((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) /
                FOUR_DECIMALS;

            // time elapsed since last withdrawal limit was set (in seconds)
            // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before.
            // last timestamp can not be > current timestamp
            temp_ = block.timestamp - ((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33);
        }
        // calculate withdrawable amount of expandPercent that is elapsed of expandDuration.
        // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%.
        // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed.
        temp_ =
            (maxWithdrawableLimit_ * temp_) /
            // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit)
            ((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0
        // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount.
        // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration,
        // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0
        // which will cause minimum (fully expanded) withdrawal limit to be set in lines below.
        unchecked {
            // underflow explicitly checked & handled
            currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0;
            // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion.
            // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
            temp_ = userSupply_ - maxWithdrawableLimit_;
        }
        // if withdrawal limit is decreased below minimum then set minimum
        // (e.g. when more than expandDuration time has elapsed)
        if (temp_ > currentWithdrawalLimit_) {
            currentWithdrawalLimit_ = temp_;
        }
    }

    /// @dev calculates withdrawal limit after an operate execution:
    /// amount of user supply that must stay supplied (not amount that can be withdrawn).
    /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
    /// @param userSupplyData_ user supply data packed uint256 from storage
    /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount
    /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate`
    /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in
    ///                          raw for with interest mode, normal amount for interest free mode!
    function calcWithdrawalLimitAfterOperate(
        uint256 userSupplyData_,
        uint256 userSupply_,
        uint256 newWithdrawalLimit_
    ) internal pure returns (uint256) {
        // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed
        uint256 temp_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        // if user supply is below base limit then max withdrawals are allowed
        if (userSupply_ < temp_) {
            return 0;
        }
        // temp_ => withdrawal limit expandPercent (is in 1e2 decimals)
        temp_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;
        unchecked {
            // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent))
            // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
            temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS);
        }
        // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit.
        // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where
        // increased deposit amount outpaces withrawals.
        if (temp_ > newWithdrawalLimit_) {
            return temp_;
        }
        return newWithdrawalLimit_;
    }

    /// @dev calculates borrow limit before an operate execution:
    /// total amount user borrow can reach (not borrowable amount in current operation).
    /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
    /// @param userBorrowData_ user borrow data packed uint256 from storage
    /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_`
    /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in
    ///                             raw for with interest mode, normal amount for interest free mode!
    function calcBorrowLimitBeforeOperate(
        uint256 userBorrowData_,
        uint256 userBorrow_
    ) internal view returns (uint256 currentBorrowLimit_) {
        // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit.
        // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus
        // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0.

        // temp_ = extract borrow expand percent (is in 1e2 decimals)
        uint256 temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;

        uint256 maxExpansionLimit_;
        uint256 maxExpandedBorrowLimit_;
        unchecked {
            // calculate max expansion limit: Max amount limit can expand to since last interaction
            // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS);

            // calculate max borrow limit: Max point limit can increase to since last interaction
            maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_;
        }

        // currentBorrowLimit_ = extract base borrow limit
        currentBorrowLimit_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
        currentBorrowLimit_ =
            (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) <<
            (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK);

        if (maxExpandedBorrowLimit_ < currentBorrowLimit_) {
            return currentBorrowLimit_;
        }
        // time elapsed since last borrow limit was set (in seconds)
        unchecked {
            // temp_ = timeElapsed_ (last timestamp can not be > current timestamp)
            temp_ = block.timestamp - ((userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last update timestamp
        }

        // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit
        currentBorrowLimit_ =
            // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`.
            // divisor is extract expand duration (after this, full expansion to expandPercentage happened).
            ((maxExpansionLimit_ * temp_) /
                ((userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0
            //  extract last set borrow limit
            BigMathMinified.fromBigNumber(
                (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64,
                DEFAULT_EXPONENT_SIZE,
                DEFAULT_EXPONENT_MASK
            );

        // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion,
        // so set to `maxExpandedBorrowLimit_` in that case.
        // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big)
        if (currentBorrowLimit_ > maxExpandedBorrowLimit_) {
            currentBorrowLimit_ = maxExpandedBorrowLimit_;
        }
        // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        if (currentBorrowLimit_ > temp_) {
            currentBorrowLimit_ = temp_;
        }
    }

    /// @dev calculates borrow limit after an operate execution:
    /// total amount user borrow can reach (not borrowable amount in current operation).
    /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
    /// @param userBorrowData_ user borrow data packed uint256 from storage
    /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount
    /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate`
    /// @return borrowLimit_ updated borrow limit that should be written to storage.
    ///                      returned value is in raw for with interest mode, normal amount for interest free mode!
    function calcBorrowLimitAfterOperate(
        uint256 userBorrowData_,
        uint256 userBorrow_,
        uint256 newBorrowLimit_
    ) internal pure returns (uint256 borrowLimit_) {
        // temp_ = extract borrow expand percent
        uint256 temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals)

        unchecked {
            // borrowLimit_ = calculate maximum borrow limit at full expansion.
            // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS);
        }

        // temp_ = extract base borrow limit
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        if (borrowLimit_ < temp_) {
            // below base limit, borrow limit is always base limit
            return temp_;
        }
        // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        // make sure fully expanded borrow limit is not above hard max borrow limit
        if (borrowLimit_ > temp_) {
            borrowLimit_ = temp_;
        }
        // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit.
        // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)
        if (newBorrowLimit_ > borrowLimit_) {
            return borrowLimit_;
        }
        return newBorrowLimit_;
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice library that helps in reading / working with storage slot data of Fluid Dex.
/// @dev as all data for Fluid Dex is internal, any data must be fetched directly through manual
/// slot reading through this library or, if gas usage is less important, through the FluidDexResolver.
library DexSlotsLink {
    /// @dev storage slot for variables at Dex
    uint256 internal constant DEX_VARIABLES_SLOT = 0;
    /// @dev storage slot for variables2 at Dex
    uint256 internal constant DEX_VARIABLES2_SLOT = 1;
    /// @dev storage slot for total supply shares at Dex
    uint256 internal constant DEX_TOTAL_SUPPLY_SHARES_SLOT = 2;
    /// @dev storage slot for user supply mapping at Dex
    uint256 internal constant DEX_USER_SUPPLY_MAPPING_SLOT = 3;
    /// @dev storage slot for total borrow shares at Dex
    uint256 internal constant DEX_TOTAL_BORROW_SHARES_SLOT = 4;
    /// @dev storage slot for user borrow mapping at Dex
    uint256 internal constant DEX_USER_BORROW_MAPPING_SLOT = 5;
    /// @dev storage slot for oracle mapping at Dex
    uint256 internal constant DEX_ORACLE_MAPPING_SLOT = 6;
    /// @dev storage slot for range and threshold shifts at Dex
    uint256 internal constant DEX_RANGE_THRESHOLD_SHIFTS_SLOT = 7;
    /// @dev storage slot for center price shift at Dex
    uint256 internal constant DEX_CENTER_PRICE_SHIFT_SLOT = 8;

    // --------------------------------
    // @dev stacked uint256 storage slots bits position data for each:

    // UserSupplyData
    uint256 internal constant BITS_USER_SUPPLY_ALLOWED = 0;
    uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;
    uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;
    uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;
    uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;
    uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;
    uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;

    // UserBorrowData
    uint256 internal constant BITS_USER_BORROW_ALLOWED = 0;
    uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;
    uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;
    uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;
    uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;
    uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;
    uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;
    uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;

    // --------------------------------

    /// @notice Calculating the slot ID for Dex contract for single mapping at `slot_` for `key_`
    function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {
        return keccak256(abi.encode(key_, slot_));
    }

    /// @notice Calculating the slot ID for Dex contract for double mapping at `slot_` for `key1_` and `key2_`
    function calculateDoubleMappingStorageSlot(
        uint256 slot_,
        address key1_,
        address key2_
    ) internal pure returns (bytes32) {
        bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));
        return keccak256(abi.encode(key2_, intermediateSlot_));
    }
}

File 10 of 22 : errorTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

library LibsErrorTypes {
    /***********************************|
    |         LiquidityCalcs            | 
    |__________________________________*/

    /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)
    uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;

    /// @notice thrown when rate data is set to a version that is not implemented
    uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;

    /// @notice thrown when the calculated borrow rate turns negative. This should never happen.
    uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003;

    /***********************************|
    |           SafeTransfer            | 
    |__________________________________*/

    /// @notice thrown when safe transfer from for an ERC20 fails
    uint256 internal constant SafeTransfer__TransferFromFailed = 71001;

    /// @notice thrown when safe transfer for an ERC20 fails
    uint256 internal constant SafeTransfer__TransferFailed = 71002;

    /***********************************|
    |           SafeApprove             | 
    |__________________________________*/

    /// @notice thrown when safe approve from for an ERC20 fails
    uint256 internal constant SafeApprove__ApproveFailed = 81001;
}

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.21;

import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol";

/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom
library SafeTransfer {
    uint256 internal constant MAX_NATIVE_TRANSFER_GAS = 20000; // pass max. 20k gas for native transfers

    error FluidSafeTransferError(uint256 errorId_);

    /// @dev Transfer `amount_` of `token_` from `from_` to `to_`, spending the approval given by `from_` to the
    /// calling contract. If `token_` returns no value, non-reverting calls are assumed to be successful.
    /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63
    function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from_" argument.
            mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
            mstore(add(freeMemoryPointer, 68), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.

            success_ := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);
        }
    }

    /// @dev Transfer `amount_` of `token_` to `to_`.
    /// If `token_` returns no value, non-reverting calls are assumed to be successful.
    /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95
    function safeTransfer(address token_, address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
            mstore(add(freeMemoryPointer, 36), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.

            success_ := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
        }
    }

    /// @dev Transfer `amount_` of ` native token to `to_`.
    /// Minimally modified from Solmate SafeTransferLib (Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25
    function safeTransferNative(address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not. Pass limited gas
            success_ := call(MAX_NATIVE_TRANSFER_GAS, to_, amount_, 0, 0, 0, 0)
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice implements a method to read uint256 data from storage at a bytes32 storage slot key.
contract StorageRead {
    function readFromStorage(bytes32 slot_) public view returns (uint256 result_) {
        assembly {
            result_ := sload(slot_) // read value from the storage slot
        }
    }
}

File 13 of 22 : error.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { Structs } from "./poolT1/coreModule/structs.sol";

abstract contract Error {
    error FluidDexError(uint256 errorId_);

    error FluidDexFactoryError(uint256 errorId);

    /// @notice used to simulate swap to find the output amount
    error FluidDexSwapResult(uint256 amountOut);

    error FluidDexPerfectLiquidityOutput(uint256 token0Amt, uint token1Amt);

    error FluidDexSingleTokenOutput(uint256 tokenAmt);

    error FluidDexLiquidityOutput(uint256 shares_);

    error FluidDexPricesAndExchangeRates(Structs.PricesAndExchangePrice pex_);

    error FluidSmartLendingError(uint256 errorId_);

    error FluidSmartLendingFactoryError(uint256 errorId_);
}

File 14 of 22 : errorTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

library ErrorTypes {
    /***********************************|
    |             DexT1                 | 
    |__________________________________*/

    /// @notice thrown at reentrancy
    uint256 internal constant DexT1__AlreadyEntered = 51001;

    uint256 internal constant DexT1__NotAnAuth = 51002;

    uint256 internal constant DexT1__SmartColNotEnabled = 51003;

    uint256 internal constant DexT1__SmartDebtNotEnabled = 51004;

    uint256 internal constant DexT1__PoolNotInitialized = 51005;

    uint256 internal constant DexT1__TokenReservesTooLow = 51006;

    uint256 internal constant DexT1__EthAndAmountInMisMatch = 51007;

    uint256 internal constant DexT1__EthSentForNonNativeSwap = 51008;

    uint256 internal constant DexT1__NoSwapRoute = 51009;

    uint256 internal constant DexT1__NotEnoughAmountOut = 51010;

    uint256 internal constant DexT1__LiquidityLayerTokenUtilizationCapReached = 51011;

    uint256 internal constant DexT1__HookReturnedFalse = 51012;

    // Either user's config are not set or user is paused
    uint256 internal constant DexT1__UserSupplyInNotOn = 51013;

    // Either user's config are not set or user is paused
    uint256 internal constant DexT1__UserDebtInNotOn = 51014;

    // Thrown when contract asks for more token0 or token1 than what user's wants to give on deposit
    uint256 internal constant DexT1__AboveDepositMax = 51015;

    uint256 internal constant DexT1__MsgValueLowOnDepositOrPayback = 51016;

    uint256 internal constant DexT1__WithdrawLimitReached = 51017;

    // Thrown when contract gives less token0 or token1 than what user's wants on withdraw
    uint256 internal constant DexT1__BelowWithdrawMin = 51018;

    uint256 internal constant DexT1__DebtLimitReached = 51019;

    // Thrown when contract gives less token0 or token1 than what user's wants on borrow
    uint256 internal constant DexT1__BelowBorrowMin = 51020;

    // Thrown when contract asks for more token0 or token1 than what user's wants on payback
    uint256 internal constant DexT1__AbovePaybackMax = 51021;

    uint256 internal constant DexT1__InvalidDepositAmts = 51022;

    uint256 internal constant DexT1__DepositAmtsZero = 51023;

    uint256 internal constant DexT1__SharesMintedLess = 51024;

    uint256 internal constant DexT1__WithdrawalNotEnough = 51025;

    uint256 internal constant DexT1__InvalidWithdrawAmts = 51026;

    uint256 internal constant DexT1__WithdrawAmtsZero = 51027;

    uint256 internal constant DexT1__WithdrawExcessSharesBurn = 51028;

    uint256 internal constant DexT1__InvalidBorrowAmts = 51029;

    uint256 internal constant DexT1__BorrowAmtsZero = 51030;

    uint256 internal constant DexT1__BorrowExcessSharesMinted = 51031;

    uint256 internal constant DexT1__PaybackAmtTooHigh = 51032;

    uint256 internal constant DexT1__InvalidPaybackAmts = 51033;

    uint256 internal constant DexT1__PaybackAmtsZero = 51034;

    uint256 internal constant DexT1__PaybackSharedBurnedLess = 51035;

    uint256 internal constant DexT1__NothingToArbitrage = 51036;

    uint256 internal constant DexT1__MsgSenderNotLiquidity = 51037;

    // On liquidity callback reentrancy bit should be on
    uint256 internal constant DexT1__ReentrancyBitShouldBeOn = 51038;

    // Thrown is reentrancy is already on and someone tries to fetch oracle price. Should not be possible to this
    uint256 internal constant DexT1__OraclePriceFetchAlreadyEntered = 51039;

    // Thrown when swap changes the current price by more than 5%
    uint256 internal constant DexT1__OracleUpdateHugeSwapDiff = 51040;

    uint256 internal constant DexT1__Token0ShouldBeSmallerThanToken1 = 51041;

    uint256 internal constant DexT1__OracleMappingOverflow = 51042;

    /// @notice thrown if governance has paused the swapping & arbitrage so only perfect functions are usable
    uint256 internal constant DexT1__SwapAndArbitragePaused = 51043;

    uint256 internal constant DexT1__ExceedsAmountInMax = 51044;

    /// @notice thrown if amount in is too high or too low
    uint256 internal constant DexT1__SwapInLimitingAmounts = 51045;

    /// @notice thrown if amount out is too high or too low
    uint256 internal constant DexT1__SwapOutLimitingAmounts = 51046;

    uint256 internal constant DexT1__MintAmtOverflow = 51047;

    uint256 internal constant DexT1__BurnAmtOverflow = 51048;

    uint256 internal constant DexT1__LimitingAmountsSwapAndNonPerfectActions = 51049;

    uint256 internal constant DexT1__InsufficientOracleData = 51050;

    uint256 internal constant DexT1__SharesAmountInsufficient = 51051;

    uint256 internal constant DexT1__CenterPriceOutOfRange = 51052;

    uint256 internal constant DexT1__DebtReservesTooLow = 51053;

    uint256 internal constant DexT1__SwapAndDepositTooLowOrTooHigh = 51054;

    uint256 internal constant DexT1__WithdrawAndSwapTooLowOrTooHigh = 51055;

    uint256 internal constant DexT1__BorrowAndSwapTooLowOrTooHigh = 51056;

    uint256 internal constant DexT1__SwapAndPaybackTooLowOrTooHigh = 51057;

    uint256 internal constant DexT1__InvalidImplementation = 51058;

    uint256 internal constant DexT1__OnlyDelegateCallAllowed = 51059;

    uint256 internal constant DexT1__IncorrectDataLength = 51060;

    uint256 internal constant DexT1__AmountToSendLessThanAmount = 51061;

    uint256 internal constant DexT1__InvalidCollateralReserves = 51062;

    uint256 internal constant DexT1__InvalidDebtReserves = 51063;

    uint256 internal constant DexT1__SupplySharesOverflow = 51064;

    uint256 internal constant DexT1__BorrowSharesOverflow = 51065;

    uint256 internal constant DexT1__OracleNotActive = 51066;

    /***********************************|
    |            DEX Admin              | 
    |__________________________________*/

    /// @notice thrown when pool is not initialized
    uint256 internal constant DexT1Admin__PoolNotInitialized = 52001;

    uint256 internal constant DexT1Admin__SmartColIsAlreadyOn = 52002;

    uint256 internal constant DexT1Admin__SmartDebtIsAlreadyOn = 52003;

    /// @notice thrown when any of the configs value overflow the maximum limit
    uint256 internal constant DexT1Admin__ConfigOverflow = 52004;

    uint256 internal constant DexT1Admin__AddressNotAContract = 52005;

    uint256 internal constant DexT1Admin__InvalidParams = 52006;

    uint256 internal constant DexT1Admin__UserNotDefined = 52007;

    uint256 internal constant DexT1Admin__OnlyDelegateCallAllowed = 52008;

    uint256 internal constant DexT1Admin__UnexpectedPoolState = 52009;

    /// @notice thrown when trying to pause or unpause but user is already in the target pause state
    uint256 internal constant DexT1Admin__InvalidPauseToggle = 52009;

    /***********************************|
    |            DEX Factory            | 
    |__________________________________*/

    uint256 internal constant DexFactory__InvalidOperation = 53001;
    uint256 internal constant DexFactory__Unauthorized = 53002;
    uint256 internal constant DexFactory__SameTokenNotAllowed = 53003;
    uint256 internal constant DexFactory__TokenConfigNotProper = 53004;
    uint256 internal constant DexFactory__InvalidParams = 53005;
    uint256 internal constant DexFactory__OnlyDelegateCallAllowed = 53006;
    uint256 internal constant DexFactory__InvalidDexAddress = 53007;

    /***********************************|
    |            Smart Lending          | 
    |__________________________________*/

    uint256 internal constant SmartLending__ZeroAddress = 54001;
    uint256 internal constant SmartLending__Unauthorized = 54002;
    uint256 internal constant SmartLending__InvalidMsgValue = 54003;
    uint256 internal constant SmartLending__OutOfRange = 54004;
    uint256 internal constant SmartLending__InvalidRebalancer = 54005;
    uint256 internal constant SmartLending__Reentrancy = 54006;
    uint256 internal constant SmartLending__InvalidAmounts = 54007;

    /***********************************|
    |        Smart Lending Factory       | 
    |__________________________________*/

    uint256 internal constant SmartLendingFactory__ZeroAddress = 55001;
    uint256 internal constant SmartLendingFactory__Unauthorized = 55002;
    uint256 internal constant SmartLendingFactory__AlreadyDeployed = 55003;
    uint256 internal constant SmartLendingFactory__InvalidParams = 55004;
    uint256 internal constant SmartLendingFactory__InvalidOperation = 55005;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { Owned } from "solmate/src/auth/Owned.sol";
import { ErrorTypes } from "../errorTypes.sol";
import { Error } from "../error.sol";
import { AddressCalcs } from "../../../libraries/addressCalcs.sol";
import { StorageRead } from "../../../libraries/storageRead.sol";

abstract contract DexFactoryVariables is Owned, StorageRead, Error {
    /*//////////////////////////////////////////////////////////////
                          STORAGE VARIABLES
    //////////////////////////////////////////////////////////////*/

    // ------------ storage variables from inherited contracts (Owned) come before vars here --------

    // ----------------------- slot 0 ---------------------------
    // address public owner; // from Owned

    // 12 bytes empty

    // ----------------------- slot 1  ---------------------------
    /// @dev deployer can deploy new Dex Pool contract
    /// owner can add/remove deployer.
    /// Owner is deployer by default.
    mapping(address => bool) internal _deployers;

    // ----------------------- slot 2  ---------------------------
    /// @dev global auths can update any dex pool config.
    /// owner can add/remove global auths.
    /// Owner is global auth by default.
    mapping(address => bool) internal _globalAuths;

    // ----------------------- slot 3  ---------------------------
    /// @dev dex auths can update specific dex config.
    /// owner can add/remove dex auths.
    /// Owner is dex auth by default.
    /// dex => auth => add/remove
    mapping(address => mapping(address => bool)) internal _dexAuths;

    // ----------------------- slot 4 ---------------------------
    /// @dev total no of dexes deployed by the factory
    /// only addresses that have deployer role or owner can deploy new dex pool.
    uint256 internal _totalDexes;

    // ----------------------- slot 5 ---------------------------
    /// @dev dex deployment logics for deploying dex pool
    /// These logic contracts hold the deployment logics of specific dexes and are called via .delegatecall inside deployDex().
    /// only addresses that have owner can add/remove new dex deployment logic.
    mapping(address => bool) internal _dexDeploymentLogics;

    /*//////////////////////////////////////////////////////////////
                          CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/
    constructor(address owner_) Owned(owner_) {}
}

abstract contract DexFactoryEvents {
    /// @dev Emitted when a new dex is deployed.
    /// @param dex The address of the newly deployed dex.
    /// @param dexId The id of the newly deployed dex.
    event LogDexDeployed(address indexed dex, uint256 indexed dexId);

    /// @dev Emitted when the deployer is modified by owner.
    /// @param deployer Address whose deployer status is updated.
    /// @param allowed Indicates whether the address is authorized as a deployer or not.
    event LogSetDeployer(address indexed deployer, bool indexed allowed);

    /// @dev Emitted when the globalAuth is modified by owner.
    /// @param globalAuth Address whose globalAuth status is updated.
    /// @param allowed Indicates whether the address is authorized as a deployer or not.
    event LogSetGlobalAuth(address indexed globalAuth, bool indexed allowed);

    /// @dev Emitted when the dexAuth is modified by owner.
    /// @param dexAuth Address whose dexAuth status is updated.
    /// @param allowed Indicates whether the address is authorized as a deployer or not.
    /// @param dex Address of the specific dex related to the authorization change.
    event LogSetDexAuth(address indexed dexAuth, bool indexed allowed, address indexed dex);

    /// @dev Emitted when the dex deployment logic is modified by owner.
    /// @param dexDeploymentLogic The address of the dex deployment logic contract.
    /// @param allowed  Indicates whether the address is authorized as a deployer or not.
    event LogSetDexDeploymentLogic(address indexed dexDeploymentLogic, bool indexed allowed);
}

abstract contract DexFactoryCore is DexFactoryVariables, DexFactoryEvents {
    constructor(address owner_) validAddress(owner_) DexFactoryVariables(owner_) {}

    /// @dev validates that an address is not the zero address
    modifier validAddress(address value_) {
        if (value_ == address(0)) {
            revert FluidDexFactoryError(ErrorTypes.DexFactory__InvalidParams);
        }
        _;
    }
}

/// @dev Implements Dex Factory auth-only callable methods. Owner / auths can set various config values and
/// can define the allow-listed deployers.
abstract contract DexFactoryAuth is DexFactoryCore {
    /// @notice                         Sets an address (`deployer_`) as allowed deployer or not.
    ///                                 This function can only be called by the owner.
    /// @param deployer_                The address to be set as deployer.
    /// @param allowed_                 A boolean indicating whether the specified address is allowed to deploy dexes.
    function setDeployer(address deployer_, bool allowed_) external onlyOwner validAddress(deployer_) {
        _deployers[deployer_] = allowed_;

        emit LogSetDeployer(deployer_, allowed_);
    }

    /// @notice                         Sets an address (`globalAuth_`) as a global authorization or not.
    ///                                 This function can only be called by the owner.
    /// @param globalAuth_              The address to be set as global authorization.
    /// @param allowed_                 A boolean indicating whether the specified address is allowed to update any dex config.
    function setGlobalAuth(address globalAuth_, bool allowed_) external onlyOwner validAddress(globalAuth_) {
        _globalAuths[globalAuth_] = allowed_;

        emit LogSetGlobalAuth(globalAuth_, allowed_);
    }

    /// @notice                         Sets an address (`dexAuth_`) as allowed dex authorization or not for a specific dex (`dex_`).
    ///                                 This function can only be called by the owner.
    /// @param dex_                     The address of the dex for which the authorization is being set.
    /// @param dexAuth_                 The address to be set as dex authorization.
    /// @param allowed_                 A boolean indicating whether the specified address is allowed to update the specific dex config.
    function setDexAuth(address dex_, address dexAuth_, bool allowed_) external onlyOwner validAddress(dexAuth_) {
        _dexAuths[dex_][dexAuth_] = allowed_;

        emit LogSetDexAuth(dexAuth_, allowed_, dex_);
    }

    /// @notice                         Sets an address as allowed dex deployment logic (`deploymentLogic_`) contract or not.
    ///                                 This function can only be called by the owner.
    /// @param deploymentLogic_         The address of the dex deployment logic contract to be set.
    /// @param allowed_                 A boolean indicating whether the specified address is allowed to deploy new type of dex.
    function setDexDeploymentLogic(
        address deploymentLogic_,
        bool allowed_
    ) public onlyOwner validAddress(deploymentLogic_) {
        _dexDeploymentLogics[deploymentLogic_] = allowed_;

        emit LogSetDexDeploymentLogic(deploymentLogic_, allowed_);
    }

    /// @notice                         Spell allows owner aka governance to do any arbitrary call on factory
    /// @param target_                  Address to which the call needs to be delegated
    /// @param data_                    Data to execute at the delegated address
    function spell(address target_, bytes memory data_) external onlyOwner returns (bytes memory response_) {
        assembly {
            let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
            let size := returndatasize()

            response_ := mload(0x40)
            mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(response_, size)
            returndatacopy(add(response_, 0x20), 0, size)

            switch iszero(succeeded)
            case 1 {
                // throw if delegatecall failed
                returndatacopy(0x00, 0x00, size)
                revert(0x00, size)
            }
        }
    }

    /// @notice                         Checks if the provided address (`deployer_`) is authorized as a deployer.
    /// @param deployer_                The address to be checked for deployer authorization.
    /// @return                         Returns `true` if the address is a deployer, otherwise `false`.
    function isDeployer(address deployer_) public view returns (bool) {
        return _deployers[deployer_] || owner == deployer_;
    }

    /// @notice                         Checks if the provided address (`globalAuth_`) has global dex authorization privileges.
    /// @param globalAuth_              The address to be checked for global authorization privileges.
    /// @return                         Returns `true` if the given address has global authorization privileges, otherwise `false`.
    function isGlobalAuth(address globalAuth_) public view returns (bool) {
        return _globalAuths[globalAuth_] || owner == globalAuth_;
    }

    /// @notice                         Checks if the provided address (`dexAuth_`) has dex authorization privileges for the specified dex (`dex_`).
    /// @param dex_                     The address of the dex to check.
    /// @param dexAuth_                 The address to be checked for dex authorization privileges.
    /// @return                         Returns `true` if the given address has dex authorization privileges for the specified dex, otherwise `false`.
    function isDexAuth(address dex_, address dexAuth_) public view returns (bool) {
        return _dexAuths[dex_][dexAuth_] || owner == dexAuth_;
    }

    /// @notice                         Checks if the provided (`dexDeploymentLogic_`) address has authorization for dex deployment.
    /// @param dexDeploymentLogic_      The address of the dex deploy logic to check for authorization privileges.
    /// @return                         Returns `true` if the given address has authorization privileges for dex deployment, otherwise `false`.
    function isDexDeploymentLogic(address dexDeploymentLogic_) public view returns (bool) {
        return _dexDeploymentLogics[dexDeploymentLogic_];
    }
}

/// @dev implements DexFactory deploy dex related methods.
abstract contract DexFactoryDeployment is DexFactoryCore, DexFactoryAuth {
    /// @dev                            Deploys a contract using the CREATE opcode with the provided bytecode (`bytecode_`).
    ///                                 This is an internal function, meant to be used within the contract to facilitate the deployment of other contracts.
    /// @param bytecode_                The bytecode of the contract to be deployed.
    /// @return address_                Returns the address of the deployed contract.
    function _deploy(bytes memory bytecode_) internal returns (address address_) {
        if (bytecode_.length == 0) {
            revert FluidDexError(ErrorTypes.DexFactory__InvalidOperation);
        }
        /// @solidity memory-safe-assembly
        assembly {
            address_ := create(0, add(bytecode_, 0x20), mload(bytecode_))
        }
        if (address_ == address(0)) {
            revert FluidDexError(ErrorTypes.DexFactory__InvalidOperation);
        }
    }

    /// @notice                       Deploys a new dex using the specified deployment logic `dexDeploymentLogic_` and data `dexDeploymentData_`.
    ///                               Only accounts with deployer access or the owner can deploy a new dex.
    /// @param dexDeploymentLogic_    The address of the dex deployment logic contract.
    /// @param dexDeploymentData_     The data to be used for dex deployment.
    /// @return dex_                  Returns the address of the newly deployed dex.
    function deployDex(address dexDeploymentLogic_, bytes calldata dexDeploymentData_) external returns (address dex_) {
        // Revert if msg.sender doesn't have deployer access or is an owner.
        if (!isDeployer(msg.sender)) revert FluidDexError(ErrorTypes.DexFactory__Unauthorized);
        // Revert if dexDeploymentLogic_ is not whitelisted.
        if (!isDexDeploymentLogic(dexDeploymentLogic_)) revert FluidDexError(ErrorTypes.DexFactory__Unauthorized);

        // Dex ID for the new dex and also acts as `nonce` for CREATE
        uint256 dexId_ = ++_totalDexes;

        // compute dex address for dex id.
        dex_ = getDexAddress(dexId_);

        // deploy the dex using dex deployment logic by making .delegatecall
        (bool success_, bytes memory data_) = dexDeploymentLogic_.delegatecall(dexDeploymentData_);

        if (!(success_ && dex_ == _deploy(abi.decode(data_, (bytes))) && isDex(dex_))) {
            revert FluidDexError(ErrorTypes.DexFactory__InvalidDexAddress);
        }

        emit LogDexDeployed(dex_, dexId_);
    }

    /// @notice                       Computes the address of a dex based on its given ID (`dexId_`).
    /// @param dexId_                 The ID of the dex.
    /// @return dex_                  Returns the computed address of the dex.
    function getDexAddress(uint256 dexId_) public view returns (address dex_) {
        return AddressCalcs.addressCalc(address(this), dexId_);
    }

    /// @notice                         Checks if a given address (`dex_`) corresponds to a valid dex.
    /// @param dex_                     The dex address to check.
    /// @return                         Returns `true` if the given address corresponds to a valid dex, otherwise `false`.
    function isDex(address dex_) public view returns (bool) {
        if (dex_.code.length == 0) {
            return false;
        } else {
            // DEX_ID() function signature is 0xf4b9a3fb
            (bool success_, bytes memory data_) = dex_.staticcall(hex"f4b9a3fb");
            return success_ && dex_ == getDexAddress(abi.decode(data_, (uint256)));
        }
    }

    /// @notice                   Returns the total number of dexes deployed by the factory.
    /// @return                   Returns the total number of dexes.
    function totalDexes() external view returns (uint256) {
        return _totalDexes;
    }
}

/// @title Fluid DexFactory
/// @notice creates Fluid dex protocol dexes, which are interacting with Fluid Liquidity to deposit / borrow funds.
/// Dexes are created at a deterministic address, given an incrementing `dexId` (see `getDexAddress()`).
/// Dexes can only be deployed by allow-listed deployer addresses.
/// @dev Note the deployed dexes start out with no config at Liquidity contract.
/// This must be done by Liquidity auths in a separate step, otherwise no deposits will be possible.
/// This contract is not upgradeable. It supports adding new dex deployment logic contracts for new, future dexes.
contract FluidDexFactory is DexFactoryCore, DexFactoryAuth, DexFactoryDeployment {
    constructor(address owner_) DexFactoryCore(owner_) {}
}

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

interface IFluidDexT1 {
    error FluidDexError(uint256 errorId);

    /// @notice used to simulate swap to find the output amount
    error FluidDexSwapResult(uint256 amountOut);

    error FluidDexPerfectLiquidityOutput(uint256 token0Amt, uint token1Amt);

    error FluidDexSingleTokenOutput(uint256 tokenAmt);

    error FluidDexLiquidityOutput(uint256 shares);

    error FluidDexPricesAndExchangeRates(PricesAndExchangePrice pex_);

    /// @notice returns the dex id
    function DEX_ID() external view returns (uint256);

    /// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key.
    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);

    struct Implementations {
        address shift;
        address admin;
        address colOperations;
        address debtOperations;
        address perfectOperationsAndOracle;
    }

    struct ConstantViews {
        uint256 dexId;
        address liquidity;
        address factory;
        Implementations implementations;
        address deployerContract;
        address token0;
        address token1;
        bytes32 supplyToken0Slot;
        bytes32 borrowToken0Slot;
        bytes32 supplyToken1Slot;
        bytes32 borrowToken1Slot;
        bytes32 exchangePriceToken0Slot;
        bytes32 exchangePriceToken1Slot;
        uint256 oracleMapping;
    }

    struct ConstantViews2 {
        uint token0NumeratorPrecision;
        uint token0DenominatorPrecision;
        uint token1NumeratorPrecision;
        uint token1DenominatorPrecision;
    }

    struct PricesAndExchangePrice {
        uint lastStoredPrice; // last stored price in 1e27 decimals
        uint centerPrice; // last stored price in 1e27 decimals
        uint upperRange; // price at upper range in 1e27 decimals
        uint lowerRange; // price at lower range in 1e27 decimals
        uint geometricMean; // geometric mean of upper range & lower range in 1e27 decimals
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct CollateralReserves {
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct DebtReserves {
        uint token0Debt;
        uint token1Debt;
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    function getCollateralReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0SupplyExchangePrice_,
        uint token1SupplyExchangePrice_
    ) external view returns (CollateralReserves memory c_);

    function getDebtReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0BorrowExchangePrice_,
        uint token1BorrowExchangePrice_
    ) external view returns (DebtReserves memory d_);

    // reverts with FluidDexPricesAndExchangeRates(pex_);
    function getPricesAndExchangePrices() external;

    function constantsView() external view returns (ConstantViews memory constantsView_);

    function constantsView2() external view returns (ConstantViews2 memory constantsView2_);

    struct Oracle {
        uint twap1by0; // TWAP price
        uint lowestPrice1by0; // lowest price point
        uint highestPrice1by0; // highest price point
        uint twap0by1; // TWAP price
        uint lowestPrice0by1; // lowest price point
        uint highestPrice0by1; // highest price point
    }

    /// @dev This function allows users to swap a specific amount of input tokens for output tokens
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountIn_ The exact amount of input tokens to swap
    /// @param amountOutMin_ The minimum amount of output tokens the user is willing to accept
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountOut_
    /// @return amountOut_ The amount of output tokens received from the swap
    function swapIn(
        bool swap0to1_,
        uint256 amountIn_,
        uint256 amountOutMin_,
        address to_
    ) external payable returns (uint256 amountOut_);

    /// @dev Swap tokens with perfect amount out
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountOut_ The exact amount of tokens to receive after swap
    /// @param amountInMax_ Maximum amount of tokens to swap in
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountIn_
    /// @return amountIn_ The amount of input tokens used for the swap
    function swapOut(
        bool swap0to1_,
        uint256 amountOut_,
        uint256 amountInMax_,
        address to_
    ) external payable returns (uint256 amountIn_);

    /// @dev Deposit tokens in equal proportion to the current pool ratio
    /// @param shares_ The number of shares to mint
    /// @param maxToken0Deposit_ Maximum amount of token0 to deposit
    /// @param maxToken1Deposit_ Maximum amount of token1 to deposit
    /// @param estimate_ If true, function will revert with estimated deposit amounts without executing the deposit
    /// @return token0Amt_ Amount of token0 deposited
    /// @return token1Amt_ Amount of token1 deposited
    function depositPerfect(
        uint shares_,
        uint maxToken0Deposit_,
        uint maxToken1Deposit_,
        bool estimate_
    ) external payable returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to withdraw a perfect amount of collateral liquidity
    /// @param shares_ The number of shares to withdraw
    /// @param minToken0Withdraw_ The minimum amount of token0 the user is willing to accept
    /// @param minToken1Withdraw_ The minimum amount of token1 the user is willing to accept
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
    /// @return token0Amt_ The amount of token0 withdrawn
    /// @return token1Amt_ The amount of token1 withdrawn
    function withdrawPerfect(
        uint shares_,
        uint minToken0Withdraw_,
        uint minToken1Withdraw_,
        address to_
    ) external returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to borrow tokens in equal proportion to the current debt pool ratio
    /// @param shares_ The number of shares to borrow
    /// @param minToken0Borrow_ Minimum amount of token0 to borrow
    /// @param minToken1Borrow_ Minimum amount of token1 to borrow
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
    /// @return token0Amt_ Amount of token0 borrowed
    /// @return token1Amt_ Amount of token1 borrowed
    function borrowPerfect(
        uint shares_,
        uint minToken0Borrow_,
        uint minToken1Borrow_,
        address to_
    ) external returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to pay back borrowed tokens in equal proportion to the current debt pool ratio
    /// @param shares_ The number of shares to pay back
    /// @param maxToken0Payback_ Maximum amount of token0 to pay back
    /// @param maxToken1Payback_ Maximum amount of token1 to pay back
    /// @param estimate_ If true, function will revert with estimated payback amounts without executing the payback
    /// @return token0Amt_ Amount of token0 paid back
    /// @return token1Amt_ Amount of token1 paid back
    function paybackPerfect(
        uint shares_,
        uint maxToken0Payback_,
        uint maxToken1Payback_,
        bool estimate_
    ) external payable returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to deposit tokens in any proportion into the col pool
    /// @param token0Amt_ The amount of token0 to deposit
    /// @param token1Amt_ The amount of token1 to deposit
    /// @param minSharesAmt_ The minimum amount of shares the user expects to receive
    /// @param estimate_ If true, function will revert with estimated shares without executing the deposit
    /// @return shares_ The amount of shares minted for the deposit
    function deposit(
        uint token0Amt_,
        uint token1Amt_,
        uint minSharesAmt_,
        bool estimate_
    ) external payable returns (uint shares_);

    /// @dev This function allows users to withdraw tokens in any proportion from the col pool
    /// @param token0Amt_ The amount of token0 to withdraw
    /// @param token1Amt_ The amount of token1 to withdraw
    /// @param maxSharesAmt_ The maximum number of shares the user is willing to burn
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return shares_ The number of shares burned for the withdrawal
    function withdraw(
        uint token0Amt_,
        uint token1Amt_,
        uint maxSharesAmt_,
        address to_
    ) external returns (uint shares_);

    /// @dev This function allows users to borrow tokens in any proportion from the debt pool
    /// @param token0Amt_ The amount of token0 to borrow
    /// @param token1Amt_ The amount of token1 to borrow
    /// @param maxSharesAmt_ The maximum amount of shares the user is willing to receive
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return shares_ The amount of borrow shares minted to represent the borrowed amount
    function borrow(
        uint token0Amt_,
        uint token1Amt_,
        uint maxSharesAmt_,
        address to_
    ) external returns (uint shares_);

    /// @dev This function allows users to payback tokens in any proportion to the debt pool
    /// @param token0Amt_ The amount of token0 to payback
    /// @param token1Amt_ The amount of token1 to payback
    /// @param minSharesAmt_ The minimum amount of shares the user expects to burn
    /// @param estimate_ If true, function will revert with estimated shares without executing the payback
    /// @return shares_ The amount of borrow shares burned for the payback
    function payback(
        uint token0Amt_,
        uint token1Amt_,
        uint minSharesAmt_,
        bool estimate_
    ) external payable returns (uint shares_);

    /// @dev This function allows users to withdraw their collateral with perfect shares in one token
    /// @param shares_ The number of shares to burn for withdrawal
    /// @param minToken0_ The minimum amount of token0 the user expects to receive (set to 0 if withdrawing in token1)
    /// @param minToken1_ The minimum amount of token1 the user expects to receive (set to 0 if withdrawing in token0)
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with withdrawAmt_
    /// @return withdrawAmt_ The amount of tokens withdrawn in the chosen token
    function withdrawPerfectInOneToken(
        uint shares_,
        uint minToken0_,
        uint minToken1_,
        address to_
    ) external returns (
        uint withdrawAmt_
    );

    /// @dev This function allows users to payback their debt with perfect shares in one token
    /// @param shares_ The number of shares to burn for payback
    /// @param maxToken0_ The maximum amount of token0 the user is willing to pay (set to 0 if paying back in token1)
    /// @param maxToken1_ The maximum amount of token1 the user is willing to pay (set to 0 if paying back in token0)
    /// @param estimate_ If true, the function will revert with the estimated payback amount without executing the payback
    /// @return paybackAmt_ The amount of tokens paid back in the chosen token
    function paybackPerfectInOneToken(
        uint shares_,
        uint maxToken0_,
        uint maxToken1_,
        bool estimate_
    ) external payable returns (
        uint paybackAmt_
    );

    /// @dev the oracle assumes last set price of pool till the next swap happens.
    /// There's a possibility that during that time some interest is generated hence the last stored price is not the 100% correct price for the whole duration
    /// but the difference due to interest will be super low so this difference is ignored
    /// For example 2 swaps happened 10min (600 seconds) apart and 1 token has 10% higher interest than other.
    /// then that token will accrue about 10% * 600 / secondsInAYear = ~0.0002%
    /// @param secondsAgos_ array of seconds ago for which TWAP is needed. If user sends [10, 30, 60] then twaps_ will return [10-0, 30-10, 60-30]
    /// @return twaps_ twap price, lowest price (aka minima) & highest price (aka maxima) between secondsAgo checkpoints
    /// @return currentPrice_ price of pool after the most recent swap
    function oraclePrice(
        uint[] memory secondsAgos_
    ) external view returns (
        Oracle[] memory twaps_,
        uint currentPrice_
    );
}

File 17 of 22 : structs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Structs {
    struct PricesAndExchangePrice {
        uint lastStoredPrice; // last stored price in 1e27 decimals
        uint centerPrice; // last stored price in 1e27 decimals
        uint upperRange; // price at upper range in 1e27 decimals
        uint lowerRange; // price at lower range in 1e27 decimals
        uint geometricMean; // geometric mean of upper range & lower range in 1e27 decimals
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct ExchangePrices {
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct CollateralReserves {
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct CollateralReservesSwap {
        uint tokenInRealReserves;
        uint tokenOutRealReserves;
        uint tokenInImaginaryReserves;
        uint tokenOutImaginaryReserves;
    }

    struct DebtReserves {
        uint token0Debt;
        uint token1Debt;
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct DebtReservesSwap {
        uint tokenInDebt;
        uint tokenOutDebt;
        uint tokenInRealReserves;
        uint tokenOutRealReserves;
        uint tokenInImaginaryReserves;
        uint tokenOutImaginaryReserves;
    }

    struct SwapInMemory {
        address tokenIn;
        address tokenOut;
        uint256 amtInAdjusted;
        address withdrawTo;
        address borrowTo;
        uint price; // price of pool after swap
        uint fee; // fee of pool
        uint revenueCut; // revenue cut of pool
        bool swap0to1;
        int swapRoutingAmt;
        bytes data; // just added to avoid stack-too-deep error
    }

    struct SwapOutMemory {
        address tokenIn;
        address tokenOut;
        uint256 amtOutAdjusted;
        address withdrawTo;
        address borrowTo;
        uint price; // price of pool after swap
        uint fee;
        uint revenueCut; // revenue cut of pool
        bool swap0to1;
        int swapRoutingAmt;
        bytes data; // just added to avoid stack-too-deep error
        uint msgValue;
    }

    struct DepositColMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0ReservesInitial;
        uint256 token1ReservesInitial;
    }

    struct WithdrawColMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0ReservesInitial;
        uint256 token1ReservesInitial;
        address to;
    }

    struct BorrowDebtMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0DebtInitial;
        uint256 token1DebtInitial;
        address to;
    }

    struct PaybackDebtMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0DebtInitial;
        uint256 token1DebtInitial;
    }

    struct OraclePriceMemory {
        uint lowestPrice1by0;
        uint highestPrice1by0;
        uint oracleSlot;
        uint oracleMap;
        uint oracle;
    }

    struct Oracle {
        uint twap1by0; // TWAP price
        uint lowestPrice1by0; // lowest price point
        uint highestPrice1by0; // highest price point
        uint twap0by1; // TWAP price
        uint lowestPrice0by1; // lowest price point
        uint highestPrice0by1; // highest price point
    }

    struct Implementations {
        address shift;
        address admin;
        address colOperations;
        address debtOperations;
        address perfectOperationsAndSwapOut;
    }

    struct ConstantViews {
        uint256 dexId;
        address liquidity;
        address factory;
        Implementations implementations;
        address deployerContract;
        address token0;
        address token1;
        bytes32 supplyToken0Slot;
        bytes32 borrowToken0Slot;
        bytes32 supplyToken1Slot;
        bytes32 borrowToken1Slot;
        bytes32 exchangePriceToken0Slot;
        bytes32 exchangePriceToken1Slot;
        uint256 oracleMapping;
    }

    struct ConstantViews2 {
        uint token0NumeratorPrecision;
        uint token0DenominatorPrecision;
        uint token1NumeratorPrecision;
        uint token1DenominatorPrecision;
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { Owned } from "solmate/src/auth/Owned.sol";
import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol";
import { CREATE3 } from "solmate/src/utils/CREATE3.sol";
import { ErrorTypes } from "../../errorTypes.sol";
import { Error } from "../../error.sol";

abstract contract Constants {
    address public immutable DEX_FACTORY;
    address public immutable LIQUIDITY;
}

abstract contract Variables is Owned {
    // ------------ storage variables from inherited contracts (Owned) come before vars here --------

    // ----------------------- slot 0 ---------------------------
    // address public owner;

    // 12 bytes empty

    // ----------------------- slot 1  ---------------------------
    /// @dev smart lending auths can update specific configs.
    /// owner can add/remove auths.
    /// Owner is auth by default.
    mapping(address => mapping(address => uint256)) internal _smartLendingAuths;

    // ----------------------- slot 2 ---------------------------
    /// @dev deployers can deploy new smartLendings.
    /// owner can add/remove deployers.
    /// Owner is deployer by default.
    mapping(address => uint256) internal _deployers;

    // ----------------------- slot 3 ---------------------------
    /// @notice list of all created tokens.
    /// @dev Solidity creates an automatic getter only to fetch at a certain position, so explicitly define a getter that returns all.
    address[] public createdTokens;

    // ----------------------- slot 4 ---------------------------

    /// @dev smart lending creation code, accessed via SSTORE2.
    address internal _smartLendingCreationCodePointer;

    /*//////////////////////////////////////////////////////////////
                          CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address owner_) Owned(owner_) {}
}

abstract contract Events {
    /// @dev Emitted when a new smart lending is deployed
    /// @param dexId The ID of the deployed DEX
    /// @param smartLending The address of the deployed smart lending
    event LogSmartLendingDeployed(uint256 dexId, address smartLending);

    /// @dev Emitted when a SmartLending auth is updated
    /// @param smartLending address of SmartLending
    /// @param auth address of auth whose status is being updated
    /// @param allowed updated status of auth
    event LogAuthUpdated(address smartLending, address auth, bool allowed);

    /// @dev Emitted when a deployer is modified by owner
    /// @param deployer address of deployer
    /// @param allowed updated status of deployer
    event LogDeployerUpdated(address deployer, bool allowed);

    /// @dev Emitted when the smart lending creation code is modified by owner
    /// @param creationCodePointer address of the creation code pointer
    event LogSetCreationCode(address creationCodePointer);
}

contract FluidSmartLendingFactory is Constants, Variables, Events, Error {
    /// @dev Validates that an address is not the zero address
    modifier validAddress(address value_) {
        if (value_ == address(0)) {
            revert FluidSmartLendingFactoryError(ErrorTypes.SmartLendingFactory__ZeroAddress);
        }
        _;
    }

    constructor(
        address dexFactory_,
        address liquidity_,
        address owner_
    ) validAddress(dexFactory_) validAddress(liquidity_) validAddress(owner_) Variables(owner_) {
        LIQUIDITY = liquidity_;
        DEX_FACTORY = dexFactory_;
    }

    /// @dev Validates that msg.sender is deployer or owner
    modifier onlyDeployers() {
        if (!isDeployer(msg.sender)) {
            revert FluidSmartLendingFactoryError(ErrorTypes.SmartLendingFactory__Unauthorized);
        }
        _;
    }

    /// @notice List of all created tokens
    function allTokens() public view returns (address[] memory) {
        return createdTokens;
    }

    /// @notice Reads if a certain `auth_` address is an allowed auth for `smartLending_` or not. Owner is auth by default.
    function isSmartLendingAuth(address smartLending_, address auth_) public view returns (bool) {
        return auth_ == owner || _smartLendingAuths[smartLending_][auth_] == 1;
    }

    /// @notice Reads if a certain `deployer_` address is an allowed deployer or not. Owner is deployer by default.
    function isDeployer(address deployer_) public view returns (bool) {
        return deployer_ == owner || _deployers[deployer_] == 1;
    }

    /// @dev Retrieves the creation code for the SmartLending contract
    function smartLendingCreationCode() public view returns (bytes memory) {
        return SSTORE2.read(_smartLendingCreationCodePointer);
    }

    /// @notice Sets an address as allowed deployer or not. Only callable by owner.
    /// @param deployer_ Address to set deployer value for
    /// @param allowed_ Bool flag for whether address is allowed as deployer or not
    function updateDeployer(address deployer_, bool allowed_) external onlyOwner validAddress(deployer_) {
        _deployers[deployer_] = allowed_ ? 1 : 0;

        emit LogDeployerUpdated(deployer_, allowed_);
    }

    /// @notice Updates the authorization status of an address for a SmartLending contract. Only callable by owner.
    /// @param smartLending_ The address of the SmartLending contract
    /// @param auth_ The address to be updated
    /// @param allowed_ The new authorization status
    function updateSmartLendingAuth(
        address smartLending_,
        address auth_,
        bool allowed_
    ) external validAddress(smartLending_) validAddress(auth_) onlyOwner {
        _smartLendingAuths[smartLending_][auth_] = allowed_ ? 1 : 0;

        emit LogAuthUpdated(smartLending_, auth_, allowed_);
    }

    /// @notice Sets the `creationCode_` bytecode for new SmartLending contracts. Only callable by owner.
    /// @param creationCode_ New SmartLending contract creation code.
    function setSmartLendingCreationCode(bytes calldata creationCode_) external onlyOwner {
        if (creationCode_.length == 0) {
            revert FluidSmartLendingFactoryError(ErrorTypes.SmartLendingFactory__InvalidParams);
        }

        // write creation code to SSTORE2 pointer and set in mapping
        address creationCodePointer_ = SSTORE2.write(creationCode_);
        _smartLendingCreationCodePointer = creationCodePointer_;

        emit LogSetCreationCode(creationCodePointer_);
    }

    /// @notice Spell allows owner aka governance to do any arbitrary call on factory
    /// @param target_ Address to which the call needs to be delegated
    /// @param data_ Data to execute at the delegated address
    function spell(address target_, bytes memory data_) external onlyOwner returns (bytes memory response_) {
        assembly {
            let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
            let size := returndatasize()

            response_ := mload(0x40)
            mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(response_, size)
            returndatacopy(add(response_, 0x20), 0, size)

            switch iszero(succeeded)
            case 1 {
                // throw if delegatecall failed
                returndatacopy(0x00, 0x00, size)
                revert(0x00, size)
            }
        }
    }

    /// @notice Deploys a new SmartLending contract. Only callable by deployers.
    /// @param dexId_ The ID of the DEX for which the smart lending wrapper is being deployed
    /// @return smartLending_ The newly deployed SmartLending contract
    function deploy(uint256 dexId_) public onlyDeployers returns (address smartLending_) {
        if (getSmartLendingAddress(dexId_).code.length != 0) {
            revert FluidSmartLendingFactoryError(ErrorTypes.SmartLendingFactory__AlreadyDeployed);
        }

        // Use CREATE3 for deterministic deployments. Unfortunately it has 55k gas overhead
        smartLending_ = CREATE3.deploy(
            _getSalt(dexId_),
            abi.encodePacked(
                SSTORE2.read(_smartLendingCreationCodePointer), // creation code
                abi.encode(dexId_, LIQUIDITY, DEX_FACTORY, address(this)) // constructor params
            ),
            0
        );

        createdTokens.push(smartLending_); // Add the created token to the allTokens array

        emit LogSmartLendingDeployed(dexId_, smartLending_);
    }

    /// @notice Computes the address of a SmartLending contract based on a given dexId.
    /// @param dexId_ The ID of the DEX for which the SmartLending contract address is being computed.
    /// @return The computed address of the SmartLending contract.
    function getSmartLendingAddress(uint256 dexId_) public view returns (address) {
        return CREATE3.getDeployed(_getSalt(dexId_));
    }

    /// @notice Returns the total number of SmartLending contracts deployed by the factory.
    /// @return The total number of SmartLending contracts deployed.
    function totalSmartLendings() external view returns (uint256) {
        return createdTokens.length;
    }

    /// @notice                         Checks if a given address (`smartLending_`) corresponds to a valid smart lending.
    /// @param smartLending_            The smart lending address to check.
    /// @return                         Returns `true` if the given address corresponds to a valid smart lending, otherwise `false`.
    function isSmartLending(address smartLending_) public view returns (bool) {
        if (smartLending_.code.length == 0) {
            return false;
        } else {
            // DEX() function signature is 0x80935aa9
            (bool success_, bytes memory data_) = smartLending_.staticcall(hex"80935aa9");
            address dex_ = abi.decode(data_, (address));
            // DEX_ID() function signature is 0xf4b9a3fb
            (success_, data_) = dex_.staticcall(hex"f4b9a3fb");
            return success_ && smartLending_ == getSmartLendingAddress(abi.decode(data_, (uint256)));
        }
    }

    /// @dev unique deployment salt for the smart lending
    function _getSalt(uint256 dexId_) internal pure returns (bytes32) {
        return keccak256(abi.encode(dexId_));
    }

    /// @dev Deploys a contract using the CREATE opcode with the provided bytecode (`bytecode_`).
    /// This is an internal function, meant to be used within the contract to facilitate the deployment of other contracts.
    /// @param bytecode_ The bytecode of the contract to be deployed.
    /// @return address_ Returns the address of the deployed contract.
    function _deploy(bytes memory bytecode_) internal returns (address address_) {
        if (bytecode_.length == 0) {
            revert FluidDexError(ErrorTypes.SmartLendingFactory__InvalidOperation);
        }
        /// @solidity memory-safe-assembly
        assembly {
            address_ := create(0, add(bytecode_, 0x20), mload(bytecode_))
        }
        if (address_ == address(0)) {
            revert FluidDexError(ErrorTypes.SmartLendingFactory__InvalidOperation);
        }
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Library for converting between addresses and bytes32 values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol)
library Bytes32AddressLib {
    function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) {
        return address(uint160(uint256(bytesValue)));
    }

    function fillLast12Bytes(address addressValue) internal pure returns (bytes32) {
        return bytes32(bytes20(addressValue));
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

/// @notice Deploy to deterministic addresses without an initcode factor.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/CREATE3.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol)
library CREATE3 {
    using Bytes32AddressLib for bytes32;

    //--------------------------------------------------------------------------------//
    // Opcode     | Opcode + Arguments    | Description      | Stack View             //
    //--------------------------------------------------------------------------------//
    // 0x36       |  0x36                 | CALLDATASIZE     | size                   //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 size                 //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 0 size               //
    // 0x37       |  0x37                 | CALLDATACOPY     |                        //
    // 0x36       |  0x36                 | CALLDATASIZE     | size                   //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 size                 //
    // 0x34       |  0x34                 | CALLVALUE        | value 0 size           //
    // 0xf0       |  0xf0                 | CREATE           | newContract            //
    //--------------------------------------------------------------------------------//
    // Opcode     | Opcode + Arguments    | Description      | Stack View             //
    //--------------------------------------------------------------------------------//
    // 0x67       |  0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode   | bytecode               //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 bytecode             //
    // 0x52       |  0x52                 | MSTORE           |                        //
    // 0x60       |  0x6008               | PUSH1 08         | 8                      //
    // 0x60       |  0x6018               | PUSH1 18         | 24 8                   //
    // 0xf3       |  0xf3                 | RETURN           |                        //
    //--------------------------------------------------------------------------------//
    bytes internal constant PROXY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3";

    bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE);

    function deploy(
        bytes32 salt,
        bytes memory creationCode,
        uint256 value
    ) internal returns (address deployed) {
        bytes memory proxyChildBytecode = PROXY_BYTECODE;

        address proxy;
        /// @solidity memory-safe-assembly
        assembly {
            // Deploy a new contract with our pre-made bytecode via CREATE2.
            // We start 32 bytes into the code to avoid copying the byte length.
            proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt)
        }
        require(proxy != address(0), "DEPLOYMENT_FAILED");

        deployed = getDeployed(salt);
        (bool success, ) = proxy.call{value: value}(creationCode);
        require(success && deployed.code.length != 0, "INITIALIZATION_FAILED");
    }

    function getDeployed(bytes32 salt) internal view returns (address) {
        address proxy = keccak256(
            abi.encodePacked(
                // Prefix:
                bytes1(0xFF),
                // Creator:
                address(this),
                // Salt:
                salt,
                // Bytecode hash:
                PROXY_BYTECODE_HASH
            )
        ).fromLast20Bytes();

        return
            keccak256(
                abi.encodePacked(
                    // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01)
                    // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)
                    hex"d6_94",
                    proxy,
                    hex"01" // Nonce of the proxy contract (1)
                )
            ).fromLast20Bytes();
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
    uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.

    /*//////////////////////////////////////////////////////////////
                               WRITE LOGIC
    //////////////////////////////////////////////////////////////*/

    function write(bytes memory data) internal returns (address pointer) {
        // Prefix the bytecode with a STOP opcode to ensure it cannot be called.
        bytes memory runtimeCode = abi.encodePacked(hex"00", data);

        bytes memory creationCode = abi.encodePacked(
            //---------------------------------------------------------------------------------------------------------------//
            // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
            //---------------------------------------------------------------------------------------------------------------//
            // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
            // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
            // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
            // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
            // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
            // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
            // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
            // 0xf3    |  0xf3               | RETURN       |                                                                //
            //---------------------------------------------------------------------------------------------------------------//
            hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes.
            runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
        );

        /// @solidity memory-safe-assembly
        assembly {
            // Deploy a new contract with the generated creation code.
            // We start 32 bytes into the code to avoid copying the byte length.
            pointer := create(0, add(creationCode, 32), mload(creationCode))
        }

        require(pointer != address(0), "DEPLOYMENT_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                               READ LOGIC
    //////////////////////////////////////////////////////////////*/

    function read(address pointer) internal view returns (bytes memory) {
        return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
    }

    function read(address pointer, uint256 start) internal view returns (bytes memory) {
        start += DATA_OFFSET;

        return readBytecode(pointer, start, pointer.code.length - start);
    }

    function read(
        address pointer,
        uint256 start,
        uint256 end
    ) internal view returns (bytes memory) {
        start += DATA_OFFSET;
        end += DATA_OFFSET;

        require(pointer.code.length >= end, "OUT_OF_BOUNDS");

        return readBytecode(pointer, start, end - start);
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function readBytecode(
        address pointer,
        uint256 start,
        uint256 size
    ) private view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            data := mload(0x40)

            // Update the free memory pointer to prevent overriding our data.
            // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
            // Adding 31 to size and running the result through the logic above ensures
            // the memory pointer remains word-aligned, following the Solidity convention.
            mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))

            // Store the size of the data in the first 32 byte chunk of free memory.
            mstore(data, size)

            // Copy the code into memory right after the 32 bytes we used to store the size.
            extcodecopy(pointer, add(data, 32), start, size)
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000000
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"uint256","name":"dexId_","type":"uint256"},{"internalType":"address","name":"liquidity_","type":"address"},{"internalType":"address","name":"dexFactory_","type":"address"},{"internalType":"address","name":"smartLendingFactory_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidDexError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId","type":"uint256"}],"name":"FluidDexFactoryError","type":"error"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"FluidDexLiquidityOutput","type":"error"},{"inputs":[{"internalType":"uint256","name":"token0Amt","type":"uint256"},{"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"FluidDexPerfectLiquidityOutput","type":"error"},{"inputs":[{"components":[{"internalType":"uint256","name":"lastStoredPrice","type":"uint256"},{"internalType":"uint256","name":"centerPrice","type":"uint256"},{"internalType":"uint256","name":"upperRange","type":"uint256"},{"internalType":"uint256","name":"lowerRange","type":"uint256"},{"internalType":"uint256","name":"geometricMean","type":"uint256"},{"internalType":"uint256","name":"supplyToken0ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"borrowToken0ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"supplyToken1ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"borrowToken1ExchangePrice","type":"uint256"}],"internalType":"struct Structs.PricesAndExchangePrice","name":"pex_","type":"tuple"}],"name":"FluidDexPricesAndExchangeRates","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenAmt","type":"uint256"}],"name":"FluidDexSingleTokenOutput","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"FluidDexSwapResult","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSafeTransferError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSmartLendingError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSmartLendingFactoryError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"feeOrReward","type":"int256"}],"name":"LogFeeOrRewardSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isWithdraw_","type":"bool"}],"name":"LogRebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rebalancer","type":"address"}],"name":"LogRebalancerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEX","outputs":[{"internalType":"contract IFluidDexT1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEX_FACTORY","outputs":[{"internalType":"contract FluidDexFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IS_NATIVE_PAIR","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDITY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SMART_LENDING_FACTORY","outputs":[{"internalType":"contract FluidSmartLendingFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"minSharesAmt_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"maxToken0Deposit_","type":"uint256"},{"internalType":"uint256","name":"maxToken1Deposit_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"depositPerfect","outputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"dexCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dexFromAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangePrice","outputs":[{"internalType":"uint184","name":"","type":"uint184"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeOrReward","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUpdateExchangePrice","outputs":[{"internalType":"uint184","name":"exchangePrice_","type":"uint184"},{"internalType":"bool","name":"rewardsOrFeeActive_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastTimestamp","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"minOrMaxToken0_","type":"uint256"},{"internalType":"uint256","name":"minOrMaxToken1_","type":"uint256"}],"name":"rebalance","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"bool","name":"isWithdraw_","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"rebalanceDiff","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebalancer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"feeOrReward_","type":"int256"}],"name":"setFeeOrReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rebalancer_","type":"address"}],"name":"setRebalancer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target_","type":"address"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"spell","outputs":[{"internalType":"bytes","name":"response_","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateExchangePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"maxSharesAmt_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"minToken0Withdraw_","type":"uint256"},{"internalType":"uint256","name":"minToken1Withdraw_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"withdrawPerfect","outputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101606040523480156200001257600080fd5b50604051620041f1380380620041f18339810160408190526200003591620004d8565b60408051808201909152601481527f466c75696420536d617274204c656e64696e67200000000000000000000000006020820152620000748562000338565b604051602001620000879291906200055e565b60408051601f1981840301815282820190915260038252621994d360ea1b602083015290620000b68662000338565b604051602001620000c99291906200055e565b60408051601f198184030181529190526003620000e7838262000624565b506004620000f6828262000624565b50506007805460ff60a01b1916600160a01b17905550826001600160a01b0381166200013e57604051633407ab4b60e01b815261d2f160048201526024015b60405180910390fd5b826001600160a01b0381166200016c57604051633407ab4b60e01b815261d2f1600482015260240162000135565b826001600160a01b0381166200019a57604051633407ab4b60e01b815261d2f1600482015260240162000135565b6001600160a01b0386811660e052858116608081905290851660a052604051630971b35560e11b8152600481018990526312e366aa90602401602060405180830381865afa158015620001f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002179190620006f0565b6001600160a01b031660c081905260408051635bbc8df960e11b815290516000929163b7791bf2916004808301926102409291908290030181865afa15801562000265573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200028b9190620007df565b60a08101516001600160a01b0390811661010081905260c08301519091166101205290915073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1480620002f15750610120516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b151561014052505050504264ffffffffff16700de0b6b3a7640000000000000000000000176005555050600780546001600160a01b03191661dead17905550620008d59050565b606060006200034783620003d1565b60010190506000816001600160401b038111156200036957620003696200057f565b6040519080825280601f01601f19166020018201604052801562000394576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846200039e57509392505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083106200041b577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef8100000000831062000448576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106200046757662386f26fc10000830492506010015b6305f5e100831062000480576305f5e100830492506008015b61271083106200049557612710830492506004015b60648310620004a8576064830492506002015b600a8310620004b5576001015b92915050565b80516001600160a01b0381168114620004d357600080fd5b919050565b60008060008060808587031215620004ef57600080fd5b845193506200050160208601620004bb565b92506200051160408601620004bb565b91506200052160608601620004bb565b905092959194509250565b6000815160005b818110156200054f576020818501810151868301520162000533565b50600093019283525090919050565b6000620005776200057083866200052c565b846200052c565b949350505050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620005aa57607f821691505b602082108103620005cb57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200061f57600081815260208120601f850160051c81016020861015620005fa5750805b601f850160051c820191505b818110156200061b5782815560010162000606565b5050505b505050565b81516001600160401b038111156200064057620006406200057f565b620006588162000651845462000595565b84620005d1565b602080601f831160018114620006905760008415620006775750858301515b600019600386901b1c1916600185901b1785556200061b565b600085815260208120601f198616915b82811015620006c157888601518255948401946001909101908401620006a0565b5085821015620006e05787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000602082840312156200070357600080fd5b6200070e82620004bb565b9392505050565b6040516101c081016001600160401b03811182821017156200073b576200073b6200057f565b60405290565b600060a082840312156200075457600080fd5b60405160a081016001600160401b03811182821017156200077957620007796200057f565b6040529050806200078a83620004bb565b81526200079a60208401620004bb565b6020820152620007ad60408401620004bb565b6040820152620007c060608401620004bb565b6060820152620007d360808401620004bb565b60808201525092915050565b60006102408284031215620007f357600080fd5b620007fd62000715565b825181526200080f60208401620004bb565b60208201526200082260408401620004bb565b604082015262000836846060850162000741565b60608201526101006200084b818501620004bb565b608083015261012062000860818601620004bb565b60a084015261014062000875818701620004bb565b60c08501526101608087015160e086015261018080880151858701526101a0945084880151848701526101c0880151838701526101e088015182870152610200880151818701525050505061022084015181830152508091505092915050565b60805160a05160c05160e051610100516101205161014051613827620009ca6000396000818161081701528181610a37015281816123b901526127a5015260006105100152600081816104bc01528181610b79015281816123df01526127cb0152600081816104170152611ab7015260008181610254015281816105a701528181610aad01528181610c350152818161113101528181611214015281816112f50152818161192501528181611a3b01528181611fe001528181612325015281816124f701526128e80152600081816108b6015281816114f2015281816117490152611bd20152600061062501526138276000f3fe6080604052600436106102375760003560e01c806380935aa911610138578063c7acb01f116100b0578063ddd59b9f1161007f578063e47a882d11610064578063e47a882d1461086e578063f53eddcc146108a4578063fad3cc4b146108d857600080fd5b8063ddd59b9f14610805578063df8879b81461083957600080fd5b8063c7acb01f14610719578063c89c74ea14610739578063d331bef71461077d578063dd62ed3e146107b257600080fd5b806397b87b4a11610107578063a457c2d7116100ec578063a457c2d7146106ac578063a9059cbb146106cc578063bd4dbda0146106ec57600080fd5b806397b87b4a146106135780639e65741e1461064757600080fd5b806380935aa91461059557806392eb1656146105c95780639410ae88146105de57806395d89b41146105fe57600080fd5b8063313ce567116101cb578063443ec74d1161019a5780635ee04d781161017f5780635ee04d78146104fe5780636cfd15531461053257806370a082311461055257600080fd5b8063443ec74d146104aa5780634fa1aa8a146104de57600080fd5b8063313ce5671461043957806335f0df981461045557806339509351146104755780633bfaa7e31461049557600080fd5b806318160ddd1161020757806318160ddd1461039257806319d8ac61146103b157806323b872dd146103e55780632861c7d11461040557600080fd5b8062c3cdae146102bb57806301d22ccd146102ee57806306fdde0314610340578063095ea7b31461036257600080fd5b366102b6573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146102b4576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024015b60405180910390fd5b005b600080fd5b6102ce6102c93660046132ee565b6108eb565b604080519384526020840192909252908201526060015b60405180910390f35b3480156102fa57600080fd5b5060065461031b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102e5565b34801561034c57600080fd5b50610355610dd8565b6040516102e59190613393565b34801561036e57600080fd5b5061038261037d3660046133ad565b610e6a565b60405190151581526020016102e5565b34801561039e57600080fd5b506002545b6040519081526020016102e5565b3480156103bd57600080fd5b506005546103cf9064ffffffffff1681565b60405164ffffffffff90911681526020016102e5565b3480156103f157600080fd5b506103826104003660046133d9565b610e84565b34801561041157600080fd5b5061031b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561044557600080fd5b50604051601281526020016102e5565b34801561046157600080fd5b506102ce6104703660046132ee565b610ea8565b34801561048157600080fd5b506103826104903660046133ad565b6113ec565b3480156104a157600080fd5b506102b4611438565b3480156104b657600080fd5b5061031b7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104ea57600080fd5b506102b46104f936600461341a565b6114be565b34801561050a57600080fd5b5061031b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561053e57600080fd5b506102b461054d366004613433565b611715565b34801561055e57600080fd5b506103a361056d366004613433565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b3480156105a157600080fd5b5061031b7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105d557600080fd5b506103a36118c8565b3480156105ea57600080fd5b506102b46105f93660046133ad565b611a23565b34801561060a57600080fd5b50610355611ae0565b34801561061f57600080fd5b5061031b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561065357600080fd5b50600554610684906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1681565b60405176ffffffffffffffffffffffffffffffffffffffffffffff90911681526020016102e5565b3480156106b857600080fd5b506103826106c73660046133ad565b611aef565b3480156106d857600080fd5b506103826106e73660046133ad565b611bc0565b3480156106f857600080fd5b5060075461031b9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561072557600080fd5b5061035561073436600461347f565b611bce565b34801561074557600080fd5b5061074e611d11565b6040805176ffffffffffffffffffffffffffffffffffffffffffffff90931683529015156020830152016102e5565b34801561078957600080fd5b5061079d6107983660046132ee565b611e3f565b604080519283526020830191909152016102e5565b3480156107be57600080fd5b506103a36107cd366004613561565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b34801561081157600080fd5b506103827f000000000000000000000000000000000000000000000000000000000000000081565b61084c61084736600461359a565b6120f9565b60408051948552602085019390935291830152151560608201526080016102e5565b34801561087a57600080fd5b506005546108919065010000000000900460030b81565b60405160039190910b81526020016102e5565b3480156108b057600080fd5b5061031b7f000000000000000000000000000000000000000000000000000000000000000081565b61079d6108e63660046132ee565b612658565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556000808080610922611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561099b57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff166001146109f6576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740200000000000000000000000000000000000000001790557f0000000000000000000000000000000000000000000000000000000000000000610b75573415610a96576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016634d9036de610add8a60016135eb565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152602481018a9052604481018990526000606482015260840160408051808303816000875af1158015610b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6b91906135fe565b9093509150610d13565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610bce5786610bd0565b875b905034811115610c10576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b6000610c1c3447613622565b905073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016634d9036de83610c668d60016135eb565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526004810191909152602481018d9052604481018c905260006064820152608401604080518083038185885af1158015610ccf573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610cf491906135fe565b9095509350303181811115610d0f57610d0f33838303612a04565b5050505b73ffffffffffffffffffffffffffffffffffffffff851615610d355784610d37565b335b6005549095506901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff16610d7589670de0b6b3a7640000613635565b610d7f919061364c565b9350610d8b8585612a55565b50600780547fffffffffffffffffffffff000000000000000000000000000000000000000000167401000000000000000000000000000000000000dead1790559196909550909350915050565b606060038054610de790613687565b80601f0160208091040260200160405190810160405280929190818152602001828054610e1390613687565b8015610e605780601f10610e3557610100808354040283529160200191610e60565b820191906000526020600020905b815481529060010190602001808311610e4357829003601f168201915b5050505050905090565b600033610e78818585612b48565b60019150505b92915050565b600033610e92858285612cfb565b610e9d858585612dd2565b506001949350505050565b600080600080610eb6611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff9093169290921790915590508015610f2f57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114610f8a576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740200000000000000000000000000000000000000001790556001880161103f5733600090815260208190526040902054600554909450600190670de0b6b3a764000090611024906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1687613635565b61102e919061364c565b6110389190613622565b9750611092565b6005546901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661107a89670de0b6b3a7640000613635565b611084919061364c565b61108f9060016135eb565b93505b61109c3385613041565b73ffffffffffffffffffffffffffffffffffffffff8516156110be57846110c0565b335b94506000871180156110d25750600086115b156111a7576040517f35f0df9800000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f000000000000000000000000000000000000000000000000000000000000000016906335f0df989060840160408051808303816000875af1158015611179573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119d91906135fe565b909350915061139f565b6000871180156111b5575085155b15611288576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f00000000000000000000000000000000000000000000000000000000000000001690634c89bfd4906084016020604051808303816000875af115801561125d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061128191906136da565b925061139f565b861580156112965750600086115b15611369576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101849052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f00000000000000000000000000000000000000000000000000000000000000001690634c89bfd4906084016020604051808303816000875af115801561133e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136291906136da565b915061139f565b6040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f760048201526024016102ab565b50600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790559196909550909350915050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152812054909190610e7890829086906114339087906135eb565b612b48565b6000611442611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff90931692909217909155905080156114bb57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b50565b6040517f73f156420000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906373f1564290604401602060405180830381865afa15801561154e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157291906136f3565b6115ac576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b60006115b6611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561162f57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b620f424082138061165f57507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0bdc082125b1561169a576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f460048201526024016102ab565b600580544264ffffffffff167fffffffffffffffffffffffffffffffffffffffffffffff000000000000000000909116176501000000000063ffffffff8516021790556040518281527f7f11439341687b064c9a44aed86bf97e498b897829329ebebee243a8aeab35bb906020015b60405180910390a15050565b6040517f73f156420000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906373f1564290604401602060405180830381865afa1580156117a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c991906136f3565b611803576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b8073ffffffffffffffffffffffffffffffffffffffff8116611855576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f160048201526024016102ab565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040519081527f336a7807b53df027467559d6ed5ba292805d0e7df83ec3fd0df46e81675dbc5e90602001611709565b6040805130602080830191909152600382840152825180830384018152606083019384905280519101207fb5c736e400000000000000000000000000000000000000000000000000000000909252606481019190915260009081907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e490608401602060405180830381865afa158015611981573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a591906136da565b600554600254600983901c66ffffffffffffff1660019390931c60ff169290921b9250600091670de0b6b3a7640000916901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1690611a069190613635565b611a10919061364c565b9050611a1c8183613715565b9250505090565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611a96576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b600754611adc90839073ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000084613205565b5050565b606060048054610de790613687565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015611bb3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084016102ab565b610e9d8286868403612b48565b600033610e78818585612dd2565b60607f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5f919061373c565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611cc7576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e811560018103611d0857816000803e816000fd5b50505092915050565b60055476ffffffffffffffffffffffffffffffffffffffffffffff69010000000000000000008204169060009065010000000000900460030b81811315611dc257611d636301e13380620f4240613635565b600554611d779064ffffffffff1642613622565b611d9a8376ffffffffffffffffffffffffffffffffffffffffffffff8716613635565b611da49190613635565b611dae919061364c565b611db89084613759565b9360019350915050565b6000811215611e3a57611ddc6301e13380620f4240613635565b600554611df09064ffffffffff1642613622565b611df983613789565b611e1c9076ffffffffffffffffffffffffffffffffffffffffffffff8716613635565b611e269190613635565b611e30919061364c565b611db890846137c1565b509091565b6000806000611e4c611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff9093169290921790915590508015611ec557600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114611f20576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167402000000000000000000000000000000000000000017905573ffffffffffffffffffffffffffffffffffffffff841615611f815783611f83565b335b6040517fd331bef700000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff80831660648301529195507f00000000000000000000000000000000000000000000000000000000000000009091169063d331bef7906084016020604051808303816000875af115801561202b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204f91906136da565b6005549092506901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661208d83670de0b6b3a7640000613635565b612097919061364c565b6120a29060016135eb565b92506120ae3384613041565b50600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055909590945092505050565b6000806000806000612109611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561218257600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff166001146121dd576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b60078054740200000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90911617905560065473ffffffffffffffffffffffffffffffffffffffff163314612273576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f560048201526024016102ab565b600061227d6118c8565b9050600081131561239b576001925034156122c8576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b6040517f35f0df98000000000000000000000000000000000000000000000000000000008152600481018290526024810189905260448101889052336064820152909550859073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906335f0df989060840160408051808303816000875af115801561236d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239191906135fe565b90955093506125c5565b60008112156125c55760009250826123b33447613622565b905060007f00000000000000000000000000000000000000000000000000000000000000001561243a577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14612434578861243d565b8961243d565b60005b90503481111561247d576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b61248683613789565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556040517f4d9036de00000000000000000000000000000000000000000000000000000000815260048101829052602481018c9052604481018b9052600060648201529098507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690634d9036de908390608401604080518083038185885af1158015612555573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061257a91906135fe565b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905590975095503031828111156125c1576125c133848303612a04565b5050505b604080518781526020810187905290810185905283151560608201527f074a87ec182f91973b93d344745a07f667c7edd70584cb334add5392a31e173a9060800160405180910390a15050600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905592959194509250565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556000808061268e611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561270757600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114612762576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167402000000000000000000000000000000000000000017905560007f000000000000000000000000000000000000000000000000000000000000000015612826577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146128205786612829565b87612829565b60005b9050348114612868576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b73ffffffffffffffffffffffffffffffffffffffff85161561288a578461288c565b335b6040517fe980e1eb000000000000000000000000000000000000000000000000000000008152600481018a905260248101899052604481018890526000606482015290955073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063e980e1eb90839060840160206040518083038185885af1158015612932573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061295791906136da565b6005549093506001906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661299885670de0b6b3a7640000613635565b6129a2919061364c565b6129ac9190613622565b93506129b88585612a55565b5050600780547fffffffffffffffffffffff000000000000000000000000000000000000000000167401000000000000000000000000000000000000dead179055909590945092505050565b60008060008060008587614e20f1905080612a50576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016102ab565b505050565b73ffffffffffffffffffffffffffffffffffffffff8216612ad2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016102ab565b8060026000828254612ae491906135eb565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8316612bea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff8216612c8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114612dcc5781811015612dbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016102ab565b612dcc8484848403612b48565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316612e75576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f647265737300000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff8216612f18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f657373000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015612fce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e6365000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3612dcc565b73ffffffffffffffffffffffffffffffffffffffff82166130e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f730000000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff82166000908152602081905260409020548181101561319a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f636500000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806132c5576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155960048201526024016102ab565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146114bb57600080fd5b6000806000806080858703121561330457600080fd5b8435935060208501359250604085013591506060850135613324816132cc565b939692955090935050565b6000815180845260005b8181101561335557602081850181015186830182015201613339565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006133a6602083018461332f565b9392505050565b600080604083850312156133c057600080fd5b82356133cb816132cc565b946020939093013593505050565b6000806000606084860312156133ee57600080fd5b83356133f9816132cc565b92506020840135613409816132cc565b929592945050506040919091013590565b60006020828403121561342c57600080fd5b5035919050565b60006020828403121561344557600080fd5b81356133a6816132cc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561349257600080fd5b823561349d816132cc565b9150602083013567ffffffffffffffff808211156134ba57600080fd5b818501915085601f8301126134ce57600080fd5b8135818111156134e0576134e0613450565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561352657613526613450565b8160405282815288602084870101111561353f57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000806040838503121561357457600080fd5b823561357f816132cc565b9150602083013561358f816132cc565b809150509250929050565b600080604083850312156135ad57600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610e7e57610e7e6135bc565b6000806040838503121561361157600080fd5b505080516020909101519092909150565b81810381811115610e7e57610e7e6135bc565b8082028115828204841417610e7e57610e7e6135bc565b600082613682577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600181811c9082168061369b57607f821691505b6020821081036136d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156136ec57600080fd5b5051919050565b60006020828403121561370557600080fd5b815180151581146133a657600080fd5b8181036000831280158383131683831282161715613735576137356135bc565b5092915050565b60006020828403121561374e57600080fd5b81516133a6816132cc565b76ffffffffffffffffffffffffffffffffffffffffffffff818116838216019080821115613735576137356135bc565b60007f800000000000000000000000000000000000000000000000000000000000000082036137ba576137ba6135bc565b5060000390565b76ffffffffffffffffffffffffffffffffffffffffffffff828116828216039080821115613735576137356135bc56fea26469706673582212206410ba81500eb21339eab51a3a55a2ed3d77da8f866e624ec0d97928ccb6284d64736f6c63430008150033000000000000000000000000000000000000000000000000000000000000001300000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49700000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c19085000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b

Deployed Bytecode

0x6080604052600436106102375760003560e01c806380935aa911610138578063c7acb01f116100b0578063ddd59b9f1161007f578063e47a882d11610064578063e47a882d1461086e578063f53eddcc146108a4578063fad3cc4b146108d857600080fd5b8063ddd59b9f14610805578063df8879b81461083957600080fd5b8063c7acb01f14610719578063c89c74ea14610739578063d331bef71461077d578063dd62ed3e146107b257600080fd5b806397b87b4a11610107578063a457c2d7116100ec578063a457c2d7146106ac578063a9059cbb146106cc578063bd4dbda0146106ec57600080fd5b806397b87b4a146106135780639e65741e1461064757600080fd5b806380935aa91461059557806392eb1656146105c95780639410ae88146105de57806395d89b41146105fe57600080fd5b8063313ce567116101cb578063443ec74d1161019a5780635ee04d781161017f5780635ee04d78146104fe5780636cfd15531461053257806370a082311461055257600080fd5b8063443ec74d146104aa5780634fa1aa8a146104de57600080fd5b8063313ce5671461043957806335f0df981461045557806339509351146104755780633bfaa7e31461049557600080fd5b806318160ddd1161020757806318160ddd1461039257806319d8ac61146103b157806323b872dd146103e55780632861c7d11461040557600080fd5b8062c3cdae146102bb57806301d22ccd146102ee57806306fdde0314610340578063095ea7b31461036257600080fd5b366102b6573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed16146102b4576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024015b60405180910390fd5b005b600080fd5b6102ce6102c93660046132ee565b6108eb565b604080519384526020840192909252908201526060015b60405180910390f35b3480156102fa57600080fd5b5060065461031b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102e5565b34801561034c57600080fd5b50610355610dd8565b6040516102e59190613393565b34801561036e57600080fd5b5061038261037d3660046133ad565b610e6a565b60405190151581526020016102e5565b34801561039e57600080fd5b506002545b6040519081526020016102e5565b3480156103bd57600080fd5b506005546103cf9064ffffffffff1681565b60405164ffffffffff90911681526020016102e5565b3480156103f157600080fd5b506103826104003660046133d9565b610e84565b34801561041157600080fd5b5061031b7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49781565b34801561044557600080fd5b50604051601281526020016102e5565b34801561046157600080fd5b506102ce6104703660046132ee565b610ea8565b34801561048157600080fd5b506103826104903660046133ad565b6113ec565b3480156104a157600080fd5b506102b4611438565b3480156104b657600080fd5b5061031b7f00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa13881565b3480156104ea57600080fd5b506102b46104f936600461341a565b6114be565b34801561050a57600080fd5b5061031b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b34801561053e57600080fd5b506102b461054d366004613433565b611715565b34801561055e57600080fd5b506103a361056d366004613433565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b3480156105a157600080fd5b5061031b7f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed81565b3480156105d557600080fd5b506103a36118c8565b3480156105ea57600080fd5b506102b46105f93660046133ad565b611a23565b34801561060a57600080fd5b50610355611ae0565b34801561061f57600080fd5b5061031b7f00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c1908581565b34801561065357600080fd5b50600554610684906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1681565b60405176ffffffffffffffffffffffffffffffffffffffffffffff90911681526020016102e5565b3480156106b857600080fd5b506103826106c73660046133ad565b611aef565b3480156106d857600080fd5b506103826106e73660046133ad565b611bc0565b3480156106f857600080fd5b5060075461031b9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561072557600080fd5b5061035561073436600461347f565b611bce565b34801561074557600080fd5b5061074e611d11565b6040805176ffffffffffffffffffffffffffffffffffffffffffffff90931683529015156020830152016102e5565b34801561078957600080fd5b5061079d6107983660046132ee565b611e3f565b604080519283526020830191909152016102e5565b3480156107be57600080fd5b506103a36107cd366004613561565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b34801561081157600080fd5b506103827f000000000000000000000000000000000000000000000000000000000000000081565b61084c61084736600461359a565b6120f9565b60408051948552602085019390935291830152151560608201526080016102e5565b34801561087a57600080fd5b506005546108919065010000000000900460030b81565b60405160039190910b81526020016102e5565b3480156108b057600080fd5b5061031b7f000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b81565b61079d6108e63660046132ee565b612658565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556000808080610922611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561099b57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff166001146109f6576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740200000000000000000000000000000000000000001790557f0000000000000000000000000000000000000000000000000000000000000000610b75573415610a96576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed16634d9036de610add8a60016135eb565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526004810191909152602481018a9052604481018990526000606482015260840160408051808303816000875af1158015610b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6b91906135fe565b9093509150610d13565b60007f00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa13873ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610bce5786610bd0565b875b905034811115610c10576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b6000610c1c3447613622565b905073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed16634d9036de83610c668d60016135eb565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526004810191909152602481018d9052604481018c905260006064820152608401604080518083038185885af1158015610ccf573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610cf491906135fe565b9095509350303181811115610d0f57610d0f33838303612a04565b5050505b73ffffffffffffffffffffffffffffffffffffffff851615610d355784610d37565b335b6005549095506901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff16610d7589670de0b6b3a7640000613635565b610d7f919061364c565b9350610d8b8585612a55565b50600780547fffffffffffffffffffffff000000000000000000000000000000000000000000167401000000000000000000000000000000000000dead1790559196909550909350915050565b606060038054610de790613687565b80601f0160208091040260200160405190810160405280929190818152602001828054610e1390613687565b8015610e605780601f10610e3557610100808354040283529160200191610e60565b820191906000526020600020905b815481529060010190602001808311610e4357829003601f168201915b5050505050905090565b600033610e78818585612b48565b60019150505b92915050565b600033610e92858285612cfb565b610e9d858585612dd2565b506001949350505050565b600080600080610eb6611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff9093169290921790915590508015610f2f57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114610f8a576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740200000000000000000000000000000000000000001790556001880161103f5733600090815260208190526040902054600554909450600190670de0b6b3a764000090611024906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1687613635565b61102e919061364c565b6110389190613622565b9750611092565b6005546901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661107a89670de0b6b3a7640000613635565b611084919061364c565b61108f9060016135eb565b93505b61109c3385613041565b73ffffffffffffffffffffffffffffffffffffffff8516156110be57846110c0565b335b94506000871180156110d25750600086115b156111a7576040517f35f0df9800000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed16906335f0df989060840160408051808303816000875af1158015611179573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061119d91906135fe565b909350915061139f565b6000871180156111b5575085155b15611288576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed1690634c89bfd4906084016020604051808303816000875af115801561125d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061128191906136da565b925061139f565b861580156112965750600086115b15611369576040517f4c89bfd400000000000000000000000000000000000000000000000000000000815260048101849052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff86811660648301527f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed1690634c89bfd4906084016020604051808303816000875af115801561133e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136291906136da565b915061139f565b6040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f760048201526024016102ab565b50600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790559196909550909350915050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152812054909190610e7890829086906114339087906135eb565b612b48565b6000611442611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff90931692909217909155905080156114bb57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b50565b6040517f73f156420000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b73ffffffffffffffffffffffffffffffffffffffff16906373f1564290604401602060405180830381865afa15801561154e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157291906136f3565b6115ac576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b60006115b6611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561162f57600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b620f424082138061165f57507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0bdc082125b1561169a576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f460048201526024016102ab565b600580544264ffffffffff167fffffffffffffffffffffffffffffffffffffffffffffff000000000000000000909116176501000000000063ffffffff8516021790556040518281527f7f11439341687b064c9a44aed86bf97e498b897829329ebebee243a8aeab35bb906020015b60405180910390a15050565b6040517f73f156420000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b73ffffffffffffffffffffffffffffffffffffffff16906373f1564290604401602060405180830381865afa1580156117a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c991906136f3565b611803576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b8073ffffffffffffffffffffffffffffffffffffffff8116611855576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f160048201526024016102ab565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040519081527f336a7807b53df027467559d6ed5ba292805d0e7df83ec3fd0df46e81675dbc5e90602001611709565b6040805130602080830191909152600382840152825180830384018152606083019384905280519101207fb5c736e400000000000000000000000000000000000000000000000000000000909252606481019190915260009081907f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed73ffffffffffffffffffffffffffffffffffffffff169063b5c736e490608401602060405180830381865afa158015611981573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a591906136da565b600554600254600983901c66ffffffffffffff1660019390931c60ff169290921b9250600091670de0b6b3a7640000916901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1690611a069190613635565b611a10919061364c565b9050611a1c8183613715565b9250505090565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed1614611a96576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b600754611adc90839073ffffffffffffffffffffffffffffffffffffffff167f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49784613205565b5050565b606060048054610de790613687565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015611bb3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084016102ab565b610e9d8286868403612b48565b600033610e78818585612dd2565b60607f000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5f919061373c565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611cc7576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f260048201526024016102ab565b600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e811560018103611d0857816000803e816000fd5b50505092915050565b60055476ffffffffffffffffffffffffffffffffffffffffffffff69010000000000000000008204169060009065010000000000900460030b81811315611dc257611d636301e13380620f4240613635565b600554611d779064ffffffffff1642613622565b611d9a8376ffffffffffffffffffffffffffffffffffffffffffffff8716613635565b611da49190613635565b611dae919061364c565b611db89084613759565b9360019350915050565b6000811215611e3a57611ddc6301e13380620f4240613635565b600554611df09064ffffffffff1642613622565b611df983613789565b611e1c9076ffffffffffffffffffffffffffffffffffffffffffffff8716613635565b611e269190613635565b611e30919061364c565b611db890846137c1565b509091565b6000806000611e4c611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff9093169290921790915590508015611ec557600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114611f20576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167402000000000000000000000000000000000000000017905573ffffffffffffffffffffffffffffffffffffffff841615611f815783611f83565b335b6040517fd331bef700000000000000000000000000000000000000000000000000000000815260048101899052602481018890526044810187905273ffffffffffffffffffffffffffffffffffffffff80831660648301529195507f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed9091169063d331bef7906084016020604051808303816000875af115801561202b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204f91906136da565b6005549092506901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661208d83670de0b6b3a7640000613635565b612097919061364c565b6120a29060016135eb565b92506120ae3384613041565b50600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055909590945092505050565b6000806000806000612109611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561218257600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff166001146121dd576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b60078054740200000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90911617905560065473ffffffffffffffffffffffffffffffffffffffff163314612273576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f560048201526024016102ab565b600061227d6118c8565b9050600081131561239b576001925034156122c8576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b6040517f35f0df98000000000000000000000000000000000000000000000000000000008152600481018290526024810189905260448101889052336064820152909550859073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed16906335f0df989060840160408051808303816000875af115801561236d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239191906135fe565b90955093506125c5565b60008112156125c55760009250826123b33447613622565b905060007f00000000000000000000000000000000000000000000000000000000000000001561243a577f00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa13873ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14612434578861243d565b8961243d565b60005b90503481111561247d576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b61248683613789565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556040517f4d9036de00000000000000000000000000000000000000000000000000000000815260048101829052602481018c9052604481018b9052600060648201529098507f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed73ffffffffffffffffffffffffffffffffffffffff1690634d9036de908390608401604080518083038185885af1158015612555573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061257a91906135fe565b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905590975095503031828111156125c1576125c133848303612a04565b5050505b604080518781526020810187905290810185905283151560608201527f074a87ec182f91973b93d344745a07f667c7edd70584cb334add5392a31e173a9060800160405180910390a15050600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905592959194509250565b600780547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556000808061268e611d11565b6005805476ffffffffffffffffffffffffffffffffffffffffffffff90931669010000000000000000000268ffffffffffffffffff909316929092179091559050801561270757600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161790555b60075474010000000000000000000000000000000000000000900460ff16600114612762576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f660048201526024016102ab565b600780547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167402000000000000000000000000000000000000000017905560007f000000000000000000000000000000000000000000000000000000000000000015612826577f00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa13873ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146128205786612829565b87612829565b60005b9050348114612868576040517f3407ab4b00000000000000000000000000000000000000000000000000000000815261d2f360048201526024016102ab565b73ffffffffffffffffffffffffffffffffffffffff85161561288a578461288c565b335b6040517fe980e1eb000000000000000000000000000000000000000000000000000000008152600481018a905260248101899052604481018890526000606482015290955073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e8c831687ce8c9d015eb10b430d1a54faa0cb4ed169063e980e1eb90839060840160206040518083038185885af1158015612932573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061295791906136da565b6005549093506001906901000000000000000000900476ffffffffffffffffffffffffffffffffffffffffffffff1661299885670de0b6b3a7640000613635565b6129a2919061364c565b6129ac9190613622565b93506129b88585612a55565b5050600780547fffffffffffffffffffffff000000000000000000000000000000000000000000167401000000000000000000000000000000000000dead179055909590945092505050565b60008060008060008587614e20f1905080612a50576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155a60048201526024016102ab565b505050565b73ffffffffffffffffffffffffffffffffffffffff8216612ad2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016102ab565b8060026000828254612ae491906135eb565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8316612bea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff8216612c8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114612dcc5781811015612dbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016102ab565b612dcc8484848403612b48565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316612e75576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f647265737300000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff8216612f18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f657373000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015612fce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e6365000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3612dcc565b73ffffffffffffffffffffffffffffffffffffffff82166130e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f730000000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff82166000908152602081905260409020548181101561319a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f636500000000000000000000000000000000000000000000000000000000000060648201526084016102ab565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806132c5576040517fdee51a8a0000000000000000000000000000000000000000000000000000000081526201155960048201526024016102ab565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146114bb57600080fd5b6000806000806080858703121561330457600080fd5b8435935060208501359250604085013591506060850135613324816132cc565b939692955090935050565b6000815180845260005b8181101561335557602081850181015186830182015201613339565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006133a6602083018461332f565b9392505050565b600080604083850312156133c057600080fd5b82356133cb816132cc565b946020939093013593505050565b6000806000606084860312156133ee57600080fd5b83356133f9816132cc565b92506020840135613409816132cc565b929592945050506040919091013590565b60006020828403121561342c57600080fd5b5035919050565b60006020828403121561344557600080fd5b81356133a6816132cc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561349257600080fd5b823561349d816132cc565b9150602083013567ffffffffffffffff808211156134ba57600080fd5b818501915085601f8301126134ce57600080fd5b8135818111156134e0576134e0613450565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561352657613526613450565b8160405282815288602084870101111561353f57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000806040838503121561357457600080fd5b823561357f816132cc565b9150602083013561358f816132cc565b809150509250929050565b600080604083850312156135ad57600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610e7e57610e7e6135bc565b6000806040838503121561361157600080fd5b505080516020909101519092909150565b81810381811115610e7e57610e7e6135bc565b8082028115828204841417610e7e57610e7e6135bc565b600082613682577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600181811c9082168061369b57607f821691505b6020821081036136d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156136ec57600080fd5b5051919050565b60006020828403121561370557600080fd5b815180151581146133a657600080fd5b8181036000831280158383131683831282161715613735576137356135bc565b5092915050565b60006020828403121561374e57600080fd5b81516133a6816132cc565b76ffffffffffffffffffffffffffffffffffffffffffffff818116838216019080821115613735576137356135bc565b60007f800000000000000000000000000000000000000000000000000000000000000082036137ba576137ba6135bc565b5060000390565b76ffffffffffffffffffffffffffffffffffffffffffffff828116828216039080821115613735576137356135bc56fea26469706673582212206410ba81500eb21339eab51a3a55a2ed3d77da8f866e624ec0d97928ccb6284d64736f6c63430008150033

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

000000000000000000000000000000000000000000000000000000000000001300000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49700000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c19085000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b

-----Decoded View---------------
Arg [0] : dexId_ (uint256): 19
Arg [1] : liquidity_ (address): 0x52Aa899454998Be5b000Ad077a46Bbe360F4e497
Arg [2] : dexFactory_ (address): 0x91716C4EDA1Fb55e84Bf8b4c7085f84285c19085
Arg [3] : smartLendingFactory_ (address): 0xe57227C7d5900165344b190fc7aa580bceb53B9B

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [1] : 00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497
Arg [2] : 00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c19085
Arg [3] : 000000000000000000000000e57227c7d5900165344b190fc7aa580bceb53b9b


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.